3 /*!*************************************************************
7 * Copyright (c) 2007, Parakey Inc.
8 * Released under BSD license.
9 * More information: http://getfirebug.com/firebuglite
11 **************************************************************/
14 * CSS selectors powered by:
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/
22 /** @namespace describe lib */
24 // FIXME: xxxpedro if we use "var FBL = {}" the FBL won't appear in the DOM Panel in IE
27 ( /** @scope s_lib @this FBL */ function() {
28 // ************************************************************************************************
30 // ************************************************************************************************
33 var productionDir = "http://getfirebug.com/releases/lite/";
34 var bookmarkletVersion = 4;
36 // ************************************************************************************************
38 var reNotWhitespace = /[^\s]/;
39 var reSplitFile = /:\/{1,3}(.*?)\/([^\/]*?)\/?($|\?.*)/;
42 this.reJavascript = /\s*javascript:\s*(.*)/;
43 this.reChrome = /chrome:\/\/([^\/]*)\//;
44 this.reFile = /file:\/\/([^\/]*)\//;
47 // ************************************************************************************************
50 var userAgent = navigator.userAgent.toLowerCase();
51 this.isFirefox = /firefox/.test(userAgent);
52 this.isOpera = /opera/.test(userAgent);
53 this.isSafari = /webkit/.test(userAgent);
54 this.isIE = /msie/.test(userAgent) && !/opera/.test(userAgent);
55 this.isIE6 = /msie 6/i.test(navigator.appVersion);
56 this.browserVersion = (userAgent.match( /.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/ ) || [0,'0'])[1];
57 this.isIElt8 = this.isIE && (this.browserVersion-0 < 8);
59 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
62 this.pixelsPerInch = null;
65 // ************************************************************************************************
70 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
72 this.ns = function(fn)
75 namespaces.push(fn, ns);
81 this.initialize = function()
83 // Firebug Lite is already running in persistent mode so we just quit
84 if (window.firebug && firebug.firebuglite || window.console && console.firebuglite)
87 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
88 // initialize environment
90 // point the FBTrace object to the local variable
92 FBTrace = FBL.FBTrace;
94 FBTrace = FBL.FBTrace = {};
96 // check if the actual window is a persisted chrome context
97 var isChromeContext = window.Firebug && typeof window.Firebug.SharedEnv == "object";
99 // chrome context of the persistent application
102 // TODO: xxxpedro persist - make a better synchronization
103 sharedEnv = window.Firebug.SharedEnv;
104 delete window.Firebug.SharedEnv;
107 FBL.Env.isChromeContext = true;
108 FBTrace.messageQueue = FBL.Env.traceMessageQueue;
110 // non-persistent application
113 FBL.NS = document.documentElement.namespaceURI;
114 FBL.Env.browser = window;
115 FBL.Env.destroy = destroyEnvironment;
117 if (document.documentElement.getAttribute("debug") == "true")
118 FBL.Env.Options.startOpened = true;
120 // find the URL location of the loaded application
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 || {});
131 typeof FBL.Env.browser.console == "object" &&
132 FBL.Env.browser.console.firebug &&
133 FBL.Env.Options.disableWhenFirebugActive)
137 // exposes the FBL to the global namespace when in debug mode
138 if (FBL.Env.isDebugMode)
140 FBL.Env.browser.FBL = FBL;
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;
148 this.noFixedPosition = this.isIE6 || this.isIEQuiksMode;
150 // after creating/synchronizing the environment, initialize the FBTrace module
151 if (FBL.Env.Options.enableTrace) FBTrace.initialize();
153 if (FBTrace.DBG_INITIALIZE && isChromeContext) FBTrace.sysout("FBL.initialize - persistent application", "initialize chrome context");
155 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
156 // initialize namespaces
158 if (FBTrace.DBG_INITIALIZE) FBTrace.sysout("FBL.initialize", namespaces.length/2+" namespaces BEGIN");
160 for (var i = 0; i < namespaces.length; i += 2)
162 var fn = namespaces[i];
163 var ns = namespaces[i+1];
167 if (FBTrace.DBG_INITIALIZE) {
168 FBTrace.sysout("FBL.initialize", namespaces.length/2+" namespaces END");
169 FBTrace.sysout("FBL waitForDocument", "waiting document load");
172 FBL.Ajax.initialize();
174 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
175 // finish environment initialization
176 FBL.Firebug.loadPrefs();
178 if (FBL.Env.Options.enablePersistent)
180 // TODO: xxxpedro persist - make a better synchronization
183 FBL.FirebugChrome.clone(FBL.Env.FirebugChrome);
187 FBL.Env.FirebugChrome = FBL.FirebugChrome;
188 FBL.Env.traceMessageQueue = FBTrace.messageQueue;
192 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
193 // wait document load
198 var waitForDocument = function waitForDocument()
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];
206 calculatePixelsPerInch(doc, body);
210 setTimeout(waitForDocument, 50);
213 var onDocumentLoad = function onDocumentLoad()
215 if (FBTrace.DBG_INITIALIZE) FBTrace.sysout("FBL onDocumentLoad", "document loaded");
217 // fix IE6 problem with cache of background images, causing a lot of flickering
219 fixIE6BackgroundImageCache();
221 // chrome context of the persistent application
222 if (FBL.Env.Options.enablePersistent && FBL.Env.isChromeContext)
224 // finally, start the application in the chrome context
225 FBL.Firebug.initialize();
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)
235 // non-persistent application
238 FBL.FirebugChrome.create();
242 // ************************************************************************************************
249 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
250 // Env Options (will be transported to Firebug options)
255 saveWindowPosition: false,
256 saveCommandLineHistory: false,
259 startInNewWindow: false,
260 showIconWhenHidden: true,
262 overrideConsole: true,
263 ignoreFirebugElements: true,
264 disableWhenFirebugActive: true,
266 disableXHRListener: false,
267 disableResourceFetching: false,
270 enablePersistent: false
274 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
288 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
290 isDevelopmentMode: false,
292 isChromeContext: false,
294 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
300 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
302 var destroyEnvironment = function destroyEnvironment()
304 setTimeout(function()
310 // ************************************************************************************************
313 var findLocation = function findLocation()
315 var reFirebugFile = /(firebug-lite(?:-\w+)?(?:\.js|\.jgz))(?:#(.+))?$/;
316 var reGetFirebugSite = /(?:http|https):\/\/getfirebug.com\//;
317 var isGetFirebugSite;
319 var rePath = /^(.*\/)/;
320 var reProtocol = /^\w+:\/\//;
324 // Firebug Lite 1.3.0 bookmarklet identification
325 var script = doc.getElementById("FirebugLite");
328 var hasSrcAttribute = true;
330 // If the script was loaded via bookmarklet, we already have the script tag
333 scriptSrc = script.src;
334 file = reFirebugFile.exec(scriptSrc);
336 var version = script.getAttribute("FirebugLite");
337 var number = version ? parseInt(version) : 0;
339 if (!version || !number || number < bookmarkletVersion)
341 FBL.Env.bookmarkletOutdated = true;
344 // otherwise we must search for the correct script tag
347 for(var i=0, s=doc.getElementsByTagName("script"), si; si=s[i]; i++)
350 if ( si.nodeName.toLowerCase() == "script" )
352 if (file = reFirebugFile.exec(si.getAttribute("firebugSrc")))
354 scriptSrc = si.getAttribute("firebugSrc");
355 hasSrcAttribute = false;
357 else if (file = reFirebugFile.exec(si.src))
370 // mark the script tag to be ignored by Firebug Lite
372 script.firebugIgnore = true;
376 var fileName = file[1];
377 var fileOptions = file[2];
380 if (reProtocol.test(scriptSrc)) {
381 path = rePath.exec(scriptSrc)[1];
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];
396 var j = backDir[1].length/3;
399 path = reLastDir.exec(path)[1];
404 else if(src.indexOf("/") != -1)
407 if(/^\.\/./.test(src))
409 path += src.substring(2);
412 else if(/^\/./.test(src))
414 var domain = /^(\w+:\/\/[^\/]+)/.exec(path);
415 path = domain[1] + src;
426 FBL.Env.isChromeExtension = script && script.getAttribute("extension") == "Chrome";
427 if (FBL.Env.isChromeExtension)
429 path = productionDir;
430 FBL.Env.bookmarkletOutdated = false;
431 script = {innerHTML: "{showIconWhenHidden:false}"};
434 isGetFirebugSite = reGetFirebugSite.test(path);
436 if (isGetFirebugSite && path.indexOf("/releases/lite/") == -1)
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/");
445 var m = path && path.match(/([^\/]+)\/$/) || null;
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
458 // detecting development and debug modes via file name
459 if (fileName == "firebug-lite-dev.js")
461 Env.isDevelopmentMode = true;
462 Env.isDebugMode = true;
464 else if (fileName == "firebug-lite-debug.js")
466 Env.isDebugMode = true;
469 // process the <html debug="true">
470 if (Env.browser.document.documentElement.getAttribute("debug") == "true")
472 Env.Options.startOpened = true;
475 // process the Script URL Options
478 var options = fileOptions.split(",");
480 for (var i = 0, length = options.length; i < length; i++)
482 var option = options[i];
485 if (option.indexOf("=") != -1)
487 var parts = option.split("=");
489 value = eval(unescape(parts[1]));
499 Env.isDebugMode = !!value;
501 else if (name in Env.Options)
503 Env.Options[name] = value;
512 // process the Script JSON Options
515 var innerOptions = FBL.trim(script.innerHTML);
518 var innerOptionsObject = eval("(" + innerOptions + ")");
520 for (var name in innerOptionsObject)
522 var value = innerOptionsObject[name];
526 Env.isDebugMode = !!value;
528 else if (name in Env.Options)
530 Env.Options[name] = value;
540 if (!Env.Options.saveCookies)
541 FBL.Store.remove("FirebugLite");
543 // process the Debug Mode
546 Env.Options.startOpened = true;
547 Env.Options.enableTrace = true;
548 Env.Options.disableWhenFirebugActive = false;
551 var loc = Env.Location;
552 var isProductionRelease = path.indexOf(productionDir) != -1;
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;
562 throw new Error("Firebug Error: Library path not found");
566 // ************************************************************************************************
569 this.bind = function() // fn, thisObject, args => thisObject.fn(args, arguments);
571 var args = cloneArray(arguments), fn = args.shift(), object = args.shift();
572 return function() { return fn.apply(object, arrayInsert(cloneArray(args), 0, arguments)); };
575 this.bindFixed = function() // fn, thisObject, args => thisObject.fn(args);
577 var args = cloneArray(arguments), fn = args.shift(), object = args.shift();
578 return function() { return fn.apply(object, args); };
581 this.extend = function(l, r)
591 this.descend = function(prototypeParent, childProperties)
593 function protoSetter() {};
594 protoSetter.prototype = prototypeParent;
595 var newOb = new protoSetter();
596 for (var n in childProperties)
597 newOb[n] = childProperties[n];
601 this.append = function(l, r)
609 this.keys = function(map) // At least sometimes the keys will be on user-level window objects
614 for (var name in map) // enumeration is safe
615 keys.push(name); // name is string, safe
619 // Sometimes we get exceptions trying to iterate properties
622 return keys; // return is safe
625 this.values = function(map)
630 for (var name in map)
634 values.push(map[name]);
638 // Sometimes we get exceptions trying to access properties
639 if (FBTrace.DBG_ERRORS)
640 FBTrace.sysout("lib.values FAILED ", exc);
647 // Sometimes we get exceptions trying to iterate properties
648 if (FBTrace.DBG_ERRORS)
649 FBTrace.sysout("lib.values FAILED ", exc);
655 this.remove = function(list, item)
657 for (var i = 0; i < list.length; ++i)
667 this.sliceArray = function(array, index)
670 for (var i = index; i < array.length; ++i)
671 slice.push(array[i]);
676 function cloneArray(array, fn)
681 for (var i = 0; i < array.length; ++i)
682 newArray.push(fn(array[i]));
684 for (var i = 0; i < array.length; ++i)
685 newArray.push(array[i]);
690 function extendArray(array, array2)
693 newArray.push.apply(newArray, array);
694 newArray.push.apply(newArray, array2);
698 this.extendArray = extendArray;
699 this.cloneArray = cloneArray;
701 function arrayInsert(array, index, other)
703 for (var i = 0; i < other.length; ++i)
704 array.splice(i+index, 0, other[i]);
709 // ************************************************************************************************
711 this.createStyleSheet = function(doc, url)
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);
723 //style.innerHTML = this.getResource(url);
727 this.addStyleSheet = function(doc, style)
729 var heads = doc.getElementsByTagName("head");
731 heads[0].appendChild(style);
733 doc.documentElement.appendChild(style);
736 this.appendStylesheet = function(doc, uri)
738 // Make sure the stylesheet is not appended twice.
739 if (this.$(uri, doc))
742 var styleSheet = this.createStyleSheet(doc, uri);
743 styleSheet.setAttribute("id", uri);
744 this.addStyleSheet(doc, styleSheet);
747 this.addScript = function(doc, id, src)
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;
755 element.innerHTML = src;
756 if (doc.documentElement)
757 doc.documentElement.appendChild(element);
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);
768 // ************************************************************************************************
770 this.getStyle = this.isIE ?
773 return el.currentStyle[name] || el.style[name] || undefined;
778 return el.ownerDocument.defaultView.getComputedStyle(el,null)[name]
779 || el.style[name] || undefined;
783 // ************************************************************************************************
784 // Whitespace and Entity conversions
786 var entityConversionLists = this.entityConversionLists = {
789 '\t' : '\u200c\u2192',
790 '\n' : '\u200c\u00b6',
791 '\r' : '\u200c\u00ac',
799 '\u200c\u2192' : '\t',
800 '\u200c\u00b6' : '\n',
801 '\u200c\u00ac' : '\r',
807 var normal = entityConversionLists.normal,
808 reverse = entityConversionLists.reverse;
810 function addEntityMapToList(ccode, entity)
812 var lists = Array.prototype.slice.call(arguments, 2),
814 ch = String.fromCharCode(ccode);
815 for (var i = 0; i < len; i++)
818 normal[list]=normal[list] || {};
819 normal[list][ch] = '&' + entity + ';';
820 reverse[list]=reverse[list] || {};
821 reverse[list]['&' + entity + ';'] = ch;
825 var e = addEntityMapToList,
826 white = 'whitespace',
832 e(0x0022, 'quot', attr, css);
833 e(0x0026, 'amp', attr, text, css);
834 e(0x0027, 'apos', css);
835 e(0x003c, 'lt', attr, text, css);
836 e(0x003e, 'gt', attr, text, css);
837 e(0xa9, 'copy', text, editor);
838 e(0xae, 'reg', text, editor);
839 e(0x2122, 'trade', text, editor);
841 // See http://en.wikipedia.org/wiki/Dash
842 e(0x2012, '#8210', attr, text, editor); // figure dash
843 e(0x2013, 'ndash', attr, text, editor); // en dash
844 e(0x2014, 'mdash', attr, text, editor); // em dash
845 e(0x2015, '#8213', attr, text, editor); // horizontal bar
847 e(0x00a0, 'nbsp', attr, text, white, editor);
848 e(0x2002, 'ensp', attr, text, white, editor);
849 e(0x2003, 'emsp', attr, text, white, editor);
850 e(0x2009, 'thinsp', attr, text, white, editor);
851 e(0x200c, 'zwnj', attr, text, white, editor);
852 e(0x200d, 'zwj', attr, text, white, editor);
853 e(0x200e, 'lrm', attr, text, white, editor);
854 e(0x200f, 'rlm', attr, text, white, editor);
855 e(0x200b, '#8203', attr, text, white, editor); // zero-width space (ZWSP)
857 //************************************************************************************************
860 var entityConversionRegexes = {
865 var escapeEntitiesRegEx = {
866 normal : function(list)
869 for ( var ch in list)
873 return new RegExp('([' + chars.join('') + '])', 'gm');
875 reverse : function(list)
878 for ( var ch in list)
882 return new RegExp('(' + chars.join('|') + ')', 'gm');
886 function getEscapeRegexp(direction, lists)
889 var groups = [].concat(lists);
890 for (i = 0; i < groups.length; i++)
892 name += groups[i].group;
894 re = entityConversionRegexes[direction][name];
898 if (groups.length > 1)
900 for ( var i = 0; i < groups.length; i++)
902 var aList = entityConversionLists[direction][groups[i].group];
903 for ( var item in aList)
904 list[item] = aList[item];
906 } else if (groups.length==1)
908 list = entityConversionLists[direction][groups[0].group]; // faster for special case
910 list = {}; // perhaps should print out an error here?
912 re = entityConversionRegexes[direction][name] = escapeEntitiesRegEx[direction](list);
917 function createSimpleEscape(name, direction)
919 return function(value)
921 var list = entityConversionLists[direction][name];
922 return String(value).replace(
923 getEscapeRegexp(direction, {
935 function escapeGroupsForEntities(str, lists)
937 lists = [].concat(lists);
938 var re = getEscapeRegexp('normal', lists),
939 split = String(str).split(re),
942 cur, r, i, ri = 0, l, list, last = '';
949 for (i = 0; i < len; i++)
954 for (l = 0; l < lists.length; l++)
957 r = entityConversionLists.normal[list.group][cur];
958 // if (cur == ' ' && list.group == 'whitespace' && last == ' ') // only show for runs of more than one space
964 'class' : list['class'],
965 'extra' : list.extra[cur] ? list['class']
966 + list.extra[cur] : ''
983 this.escapeGroupsForEntities = escapeGroupsForEntities;
986 function unescapeEntities(str, lists)
988 var re = getEscapeRegexp('reverse', lists),
989 split = String(str).split(re),
992 cur, r, i, ri = 0, l, list;
995 lists = [].concat(lists);
996 for (i = 0; i < len; i++)
1001 for (l = 0; l < lists.length; l++)
1004 r = entityConversionLists.reverse[list.group][cur];
1015 return results.join('') || '';
1019 // ************************************************************************************************
1022 var escapeForTextNode = this.escapeForTextNode = createSimpleEscape('text', 'normal');
1023 var escapeForHtmlEditor = this.escapeForHtmlEditor = createSimpleEscape('editor', 'normal');
1024 var escapeForElementAttribute = this.escapeForElementAttribute = createSimpleEscape('attributes', 'normal');
1025 var escapeForCss = this.escapeForCss = createSimpleEscape('css', 'normal');
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);
1033 var escapeForSourceLine = this.escapeForSourceLine = createSimpleEscape('text', 'normal');
1035 var unescapeWhitespace = createSimpleEscape('whitespace', 'reverse');
1037 this.unescapeForTextNode = function(str)
1039 if (Firebug.showTextNodesWithWhitespace)
1040 str = unescapeWhitespace(str);
1041 if (!Firebug.showTextNodesWithEntities)
1042 str = escapeForElementAttribute(str);
1046 this.escapeNewLines = function(value)
1048 return value.replace(/\r/g, "\\r").replace(/\n/g, "\\n");
1051 this.stripNewLines = function(value)
1053 return typeof(value) == "string" ? value.replace(/[\r\n]/g, " ") : value;
1056 this.escapeJS = function(value)
1058 return value.replace(/\r/g, "\\r").replace(/\n/g, "\\n").replace('"', '\\"', "g");
1061 function escapeHTMLAttribute(value)
1063 function replaceChars(ch)
1076 var apos = "'", quot = """, around = '"';
1077 if( value.indexOf('"') == -1 ) {
1080 } else if( value.indexOf("'") == -1 ) {
1084 return around + (String(value).replace(/[&'"]/g, replaceChars)) + around;
1088 function escapeHTML(value)
1090 function replaceChars(ch)
1107 return String(value).replace(/[<>&"']/g, replaceChars);
1110 this.escapeHTML = escapeHTML;
1112 this.cropString = function(text, limit)
1119 var halfLimit = limit / 2;
1121 if (text.length > limit)
1122 return this.escapeNewLines(text.substr(0, halfLimit) + "..." + text.substr(text.length-halfLimit));
1124 return this.escapeNewLines(text);
1127 this.isWhitespace = function(text)
1129 return !reNotWhitespace.exec(text);
1132 this.splitLines = function(text)
1134 var reSplitLines2 = /.*(:?\r\n|\n|\r)?/mg;
1138 lines = text.match(reSplitLines2);
1143 lines = str.match(reSplitLines2);
1150 // ************************************************************************************************
1152 this.safeToString = function(ob)
1158 // FIXME: xxxpedro this is failing in IE for the global "external" object
1163 FBTrace.sysout("Lib.safeToString() failed for ", ob);
1170 if (ob && "toString" in ob && typeof ob.toString == "function")
1171 return ob.toString();
1175 // xxxpedro it is not safe to use ob+""?
1177 ///return "[an object with no toString() function]";
1181 // ************************************************************************************************
1183 this.hasProperties = function(ob)
1187 for (var name in ob)
1193 // ************************************************************************************************
1196 var reTrim = /^\s+|\s+$/g;
1197 this.trim = function(s)
1199 return s.replace(reTrim, "");
1203 // ************************************************************************************************
1206 this.emptyFn = function(){};
1210 // ************************************************************************************************
1213 this.isVisible = function(elt)
1216 if (elt instanceof XULElement)
1218 //FBTrace.sysout("isVisible elt.offsetWidth: "+elt.offsetWidth+" offsetHeight:"+ elt.offsetHeight+" localName:"+ elt.localName+" nameSpace:"+elt.nameSpaceURI+"\n");
1219 return (!elt.hidden && !elt.collapsed);
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" );
1230 this.collapse = function(elt, collapsed)
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.
1239 this.setClass(elt, "collapsed");
1241 this.removeClass(elt, "collapsed");
1244 elt.setAttribute("collapsed", collapsed ? "true" : "false");
1247 this.obscure = function(elt, obscured)
1250 this.setClass(elt, "obscured");
1252 this.removeClass(elt, "obscured");
1255 this.hide = function(elt, hidden)
1257 elt.style.visibility = hidden ? "hidden" : "visible";
1260 this.clearNode = function(node)
1262 var nodeName = " " + node.nodeName.toLowerCase() + " ";
1263 var ignoreTags = " table tbody thead tfoot th tr td ";
1265 // IE can't use innerHTML of table elements
1266 if (this.isIE && ignoreTags.indexOf(nodeName) != -1)
1267 this.eraseNode(node);
1269 node.innerHTML = "";
1272 this.eraseNode = function(node)
1274 while (node.lastChild)
1275 node.removeChild(node.lastChild);
1278 // ************************************************************************************************
1281 this.iterateWindows = function(win, handler)
1283 if (!win || !win.document)
1288 if (win == top || !win.frames) return; // XXXjjb hack for chromeBug
1290 for (var i = 0; i < win.frames.length; ++i)
1292 var subWin = win.frames[i];
1294 this.iterateWindows(subWin, handler);
1298 this.getRootWindow = function(win)
1300 for (; win; win = win.parent)
1302 if (!win.parent || win == win.parent || !this.instanceOf(win.parent, "Window"))
1308 // ************************************************************************************************
1311 this.getClientOffset = function(elt)
1313 var addOffset = function addOffset(elt, coords, view)
1315 var p = elt.offsetParent;
1317 ///var style = isIE ? elt.currentStyle : view.getComputedStyle(elt, "");
1318 var chrome = Firebug.chrome;
1321 ///coords.x += elt.offsetLeft + parseInt(style.borderLeftWidth);
1322 coords.x += elt.offsetLeft + chrome.getMeasurementInPixels(elt, "borderLeft");
1324 ///coords.y += elt.offsetTop + parseInt(style.borderTopWidth);
1325 coords.y += elt.offsetTop + chrome.getMeasurementInPixels(elt, "borderTop");
1329 if (p.nodeType == 1)
1330 addOffset(p, coords, view);
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);
1343 var isIE = this.isIE;
1344 var coords = {x: 0, y: 0};
1347 var view = isIE ? elt.ownerDocument.parentWindow : elt.ownerDocument.defaultView;
1348 addOffset(elt, coords, view);
1354 this.getViewOffset = function(elt, singleFrame)
1356 function addOffset(elt, coords, view)
1358 var p = elt.offsetParent;
1359 coords.x += elt.offsetLeft - (p ? p.scrollLeft : 0);
1360 coords.y += elt.offsetTop - (p ? p.scrollTop : 0);
1364 if (p.nodeType == 1)
1366 var parentStyle = view.getComputedStyle(p, "");
1367 if (parentStyle.position != "static")
1369 coords.x += parseInt(parentStyle.borderLeftWidth);
1370 coords.y += parseInt(parentStyle.borderTopWidth);
1372 if (p.localName == "TABLE")
1374 coords.x += parseInt(parentStyle.paddingLeft);
1375 coords.y += parseInt(parentStyle.paddingTop);
1377 else if (p.localName == "BODY")
1379 var style = view.getComputedStyle(elt, "");
1380 coords.x += parseInt(style.marginLeft);
1381 coords.y += parseInt(style.marginTop);
1384 else if (p.localName == "BODY")
1386 coords.x += parseInt(parentStyle.borderLeftWidth);
1387 coords.y += parseInt(parentStyle.borderTopWidth);
1390 var parent = elt.parentNode;
1393 coords.x -= parent.scrollLeft;
1394 coords.y -= parent.scrollTop;
1395 parent = parent.parentNode;
1397 addOffset(p, coords, view);
1402 if (elt.localName == "BODY")
1404 var style = view.getComputedStyle(elt, "");
1405 coords.x += parseInt(style.borderLeftWidth);
1406 coords.y += parseInt(style.borderTopWidth);
1408 var htmlStyle = view.getComputedStyle(elt.parentNode, "");
1409 coords.x -= parseInt(htmlStyle.paddingLeft);
1410 coords.y -= parseInt(htmlStyle.paddingTop);
1414 coords.x += elt.scrollLeft;
1416 coords.y += elt.scrollTop;
1418 var win = elt.ownerDocument.defaultView;
1419 if (win && (!singleFrame && win.frameElement))
1420 addOffset(win.frameElement, coords, win);
1425 var coords = {x: 0, y: 0};
1427 addOffset(elt, coords, elt.ownerDocument.defaultView);
1432 this.getLTRBWH = function(elt)
1435 dims = {"left": 0, "top": 0, "right": 0, "bottom": 0, "width": 0, "height": 0};
1439 bcrect = elt.getBoundingClientRect();
1440 dims.left = bcrect.left;
1441 dims.top = bcrect.top;
1442 dims.right = bcrect.right;
1443 dims.bottom = bcrect.bottom;
1447 dims.width = bcrect.width;
1448 dims.height = bcrect.height;
1452 dims.width = dims.right - dims.left;
1453 dims.height = dims.bottom - dims.top;
1459 this.applyBodyOffsets = function(elt, clientRect)
1461 var od = elt.ownerDocument;
1465 var style = od.defaultView.getComputedStyle(od.body, null);
1467 var pos = style.getPropertyValue('position');
1468 if(pos === 'absolute' || pos === 'relative')
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;
1477 var offsetX = borderLeft + paddingLeft + marginLeft;
1478 var offsetY = borderTop + paddingTop + marginTop;
1480 clientRect.left -= offsetX;
1481 clientRect.top -= offsetY;
1482 clientRect.right -= offsetX;
1483 clientRect.bottom -= offsetY;
1489 this.getOffsetSize = function(elt)
1491 return {width: elt.offsetWidth, height: elt.offsetHeight};
1494 this.getOverflowParent = function(element)
1496 for (var scrollParent = element.parentNode; scrollParent; scrollParent = scrollParent.offsetParent)
1498 if (scrollParent.scrollHeight > scrollParent.offsetHeight)
1499 return scrollParent;
1503 this.isScrolledToBottom = function(element)
1505 var onBottom = (element.scrollTop + element.offsetHeight) == element.scrollHeight;
1506 if (FBTrace.DBG_CONSOLE)
1507 FBTrace.sysout("isScrolledToBottom offsetHeight: "+element.offsetHeight +" onBottom:"+onBottom);
1511 this.scrollToBottom = function(element)
1513 element.scrollTop = element.scrollHeight;
1515 if (FBTrace.DBG_CONSOLE)
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);
1522 return (element.scrollTop == element.scrollHeight);
1525 this.move = function(element, x, y)
1527 element.style.left = x + "px";
1528 element.style.top = y + "px";
1531 this.resize = function(element, w, h)
1533 element.style.width = w + "px";
1534 element.style.height = h + "px";
1537 this.linesIntoCenterView = function(element, scrollBox) // {before: int, after: int}
1540 scrollBox = this.getOverflowParent(element);
1545 var offset = this.getClientOffset(element);
1547 var topSpace = offset.y - scrollBox.scrollTop;
1548 var bottomSpace = (scrollBox.scrollTop + scrollBox.clientHeight)
1549 - (offset.y + element.offsetHeight);
1551 if (topSpace < 0 || bottomSpace < 0)
1553 var split = (scrollBox.clientHeight/2);
1554 var centerY = offset.y - split;
1555 scrollBox.scrollTop = centerY;
1557 bottomSpace = split - element.offsetHeight;
1560 return {before: Math.round((topSpace/element.offsetHeight) + 0.5),
1561 after: Math.round((bottomSpace/element.offsetHeight) + 0.5) };
1564 this.scrollIntoCenterView = function(element, scrollBox, notX, notY)
1570 scrollBox = this.getOverflowParent(element);
1575 var offset = this.getClientOffset(element);
1579 var topSpace = offset.y - scrollBox.scrollTop;
1580 var bottomSpace = (scrollBox.scrollTop + scrollBox.clientHeight)
1581 - (offset.y + element.offsetHeight);
1583 if (topSpace < 0 || bottomSpace < 0)
1585 var centerY = offset.y - (scrollBox.clientHeight/2);
1586 scrollBox.scrollTop = centerY;
1592 var leftSpace = offset.x - scrollBox.scrollLeft;
1593 var rightSpace = (scrollBox.scrollLeft + scrollBox.clientWidth)
1594 - (offset.x + element.clientWidth);
1596 if (leftSpace < 0 || rightSpace < 0)
1598 var centerX = offset.x - (scrollBox.clientWidth/2);
1599 scrollBox.scrollLeft = centerX;
1602 if (FBTrace.DBG_SOURCEFILES)
1603 FBTrace.sysout("lib.scrollIntoCenterView ","Element:"+element.innerHTML);
1607 // ************************************************************************************************
1610 var cssKeywordMap = null;
1611 var cssPropNames = null;
1612 var cssColorNames = null;
1613 var imageRules = null;
1615 this.getCSSKeywordsByProperty = function(propName)
1621 for (var name in this.cssInfo)
1625 var types = this.cssInfo[name];
1626 for (var i = 0; i < types.length; ++i)
1628 var keywords = this.cssKeywords[types[i]];
1630 list.push.apply(list, keywords);
1633 cssKeywordMap[name] = list;
1637 return propName in cssKeywordMap ? cssKeywordMap[propName] : [];
1640 this.getCSSPropertyNames = function()
1646 for (var name in this.cssInfo)
1647 cssPropNames.push(name);
1650 return cssPropNames;
1653 this.isColorKeyword = function(keyword)
1655 if (keyword == "transparent")
1662 var colors = this.cssKeywords["color"];
1663 for (var i = 0; i < colors.length; ++i)
1664 cssColorNames.push(colors[i].toLowerCase());
1666 var systemColors = this.cssKeywords["systemColor"];
1667 for (var i = 0; i < systemColors.length; ++i)
1668 cssColorNames.push(systemColors[i].toLowerCase());
1671 return cssColorNames.indexOf ? // Array.indexOf is not available in IE
1672 cssColorNames.indexOf(keyword.toLowerCase()) != -1 :
1673 (" " + cssColorNames.join(" ") + " ").indexOf(" " + keyword.toLowerCase() + " ") != -1;
1676 this.isImageRule = function(rule)
1682 for (var i in this.cssInfo)
1684 var r = i.toLowerCase();
1685 var suffix = "image";
1686 if (r.match(suffix + "$") == suffix || r == "background")
1691 return imageRules.indexOf ? // Array.indexOf is not available in IE
1692 imageRules.indexOf(rule.toLowerCase()) != -1 :
1693 (" " + imageRules.join(" ") + " ").indexOf(" " + rule.toLowerCase() + " ") != -1;
1696 this.copyTextStyles = function(fromNode, toNode, style)
1698 var view = this.isIE ?
1699 fromNode.ownerDocument.parentWindow :
1700 fromNode.ownerDocument.defaultView;
1705 style = this.isIE ? fromNode.currentStyle : view.getComputedStyle(fromNode, "");
1707 toNode.style.fontFamily = style.fontFamily;
1709 // TODO: xxxpedro need to create a FBL.getComputedStyle() because IE
1710 // returns wrong computed styles for inherited properties (like font-*)
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;
1721 this.copyBoxStyles = function(fromNode, toNode, style)
1723 var view = this.isIE ?
1724 fromNode.ownerDocument.parentWindow :
1725 fromNode.ownerDocument.defaultView;
1730 style = this.isIE ? fromNode.currentStyle : view.getComputedStyle(fromNode, "");
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;
1745 this.readBoxStyles = function(style)
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",
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);
1765 this.getBoxFromStyles = function(style, element)
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);
1775 this.getElementCSSSelector = function(element)
1777 var label = element.localName.toLowerCase();
1779 label += "#" + element.id;
1780 if (element.hasAttribute("class"))
1781 label += "." + element.getAttribute("class").split(" ")[0];
1786 this.getURLForStyleSheet= function(styleSheet)
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);
1792 this.getDocumentForStyleSheet = function(styleSheet)
1794 while (styleSheet.parentStyleSheet && !styleSheet.ownerNode)
1796 styleSheet = styleSheet.parentStyleSheet;
1798 if (styleSheet.ownerNode)
1799 return styleSheet.ownerNode.ownerDocument;
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.
1806 this.getInstanceForStyleSheet = function(styleSheet, ownerDocument)
1808 // System URLs are always unique (or at least we are making this assumption)
1809 if (FBL.isSystemStyleSheet(styleSheet))
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);
1817 styleSheets = ownerDocument.styleSheets,
1818 href = styleSheet.href;
1819 for (var i = 0; i < styleSheets.length; i++)
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)
1825 if (curSheet.href == href)
1831 // ************************************************************************************************
1832 // HTML and XML Serialization
1835 var getElementType = this.getElementType = function(node)
1837 if (isElementXUL(node))
1839 else if (isElementSVG(node))
1841 else if (isElementMathML(node))
1843 else if (isElementXHTML(node))
1845 else if (isElementHTML(node))
1849 var getElementSimpleType = this.getElementSimpleType = function(node)
1851 if (isElementSVG(node))
1853 else if (isElementMathML(node))
1859 var isElementHTML = this.isElementHTML = function(node)
1861 return node.nodeName == node.nodeName.toUpperCase();
1864 var isElementXHTML = this.isElementXHTML = function(node)
1866 return node.nodeName == node.nodeName.toLowerCase();
1869 var isElementMathML = this.isElementMathML = function(node)
1871 return node.namespaceURI == 'http://www.w3.org/1998/Math/MathML';
1874 var isElementSVG = this.isElementSVG = function(node)
1876 return node.namespaceURI == 'http://www.w3.org/2000/svg';
1879 var isElementXUL = this.isElementXUL = function(node)
1881 return node instanceof XULElement;
1884 this.isSelfClosing = function(element)
1886 if (isElementSVG(element) || isElementMathML(element))
1888 var tag = element.localName.toLowerCase();
1889 return (this.selfClosingTags.hasOwnProperty(tag));
1892 this.getElementHTML = function(element)
1895 function toHTML(elt)
1897 if (elt.nodeType == Node.ELEMENT_NODE)
1899 if (unwrapObject(elt).firebugIgnore)
1902 html.push('<', elt.nodeName.toLowerCase());
1904 for (var i = 0; i < elt.attributes.length; ++i)
1906 var attr = elt.attributes[i];
1908 // Hide attributes set by Firebug
1909 if (attr.localName.indexOf("firebug-") == 0)
1913 if (attr.localName.indexOf("-moz-math") == 0)
1915 // just hide for now
1919 html.push(' ', attr.nodeName, '="', escapeForElementAttribute(attr.nodeValue),'"');
1927 for (var child = element.firstChild; child; child = child.nextSibling)
1928 pureText=pureText && (child.nodeType == Node.TEXT_NODE);
1931 html.push(escapeForHtmlEditor(elt.textContent));
1933 for (var child = elt.firstChild; child; child = child.nextSibling)
1937 html.push('</', elt.nodeName.toLowerCase(), '>');
1939 else if (isElementSVG(elt) || isElementMathML(elt))
1943 else if (self.isSelfClosing(elt))
1945 html.push((isElementXHTML(elt))?'/>':'>');
1949 html.push('></', elt.nodeName.toLowerCase(), '>');
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, '-->');
1962 return html.join("");
1965 this.getElementXML = function(element)
1969 if (elt.nodeType == Node.ELEMENT_NODE)
1971 if (unwrapObject(elt).firebugIgnore)
1974 xml.push('<', elt.nodeName.toLowerCase());
1976 for (var i = 0; i < elt.attributes.length; ++i)
1978 var attr = elt.attributes[i];
1980 // Hide attributes set by Firebug
1981 if (attr.localName.indexOf("firebug-") == 0)
1985 if (attr.localName.indexOf("-moz-math") == 0)
1987 // just hide for now
1991 xml.push(' ', attr.nodeName, '="', escapeForElementAttribute(attr.nodeValue),'"');
1998 for (var child = elt.firstChild; child; child = child.nextSibling)
2001 xml.push('</', elt.nodeName.toLowerCase(), '>');
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, '-->');
2016 return xml.join("");
2020 // ************************************************************************************************
2023 this.hasClass = function(node, name) // className, className, ...
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;
2031 if (!node || node.nodeType != 1)
2035 for (var i=1; i<arguments.length; ++i)
2037 var name = arguments[i];
2038 var re = new RegExp("(^|\\s)"+name+"($|\\s)");
2039 if (!re.exec(node.className))
2047 this.old_hasClass = function(node, name) // className, className, ...
2049 if (!node || node.nodeType != 1)
2053 for (var i=1; i<arguments.length; ++i)
2055 var name = arguments[i];
2056 var re = new RegExp("(^|\\s)"+name+"($|\\s)");
2057 if (!re.exec(node.className))
2065 this.setClass = function(node, name)
2067 if (node && (' '+node.className+' ').indexOf(' '+name+' ') == -1)
2068 ///if (node && !this.hasClass(node, name))
2069 node.className += " " + name;
2072 this.getClassValue = function(node, name)
2074 var re = new RegExp(name+"-([^ ]+)");
2075 var m = re.exec(node.className);
2076 return m ? m[1] : "";
2079 this.removeClass = function(node, name)
2081 if (node && node.className)
2083 var index = node.className.indexOf(name);
2086 var size = name.length;
2087 node.className = node.className.substr(0,index-1) + node.className.substr(index+size);
2092 this.toggleClass = function(elt, name)
2094 if ((' '+elt.className+' ').indexOf(' '+name+' ') != -1)
2095 ///if (this.hasClass(elt, name))
2096 this.removeClass(elt, name);
2098 this.setClass(elt, name);
2101 this.setClassTimed = function(elt, name, context, timeout)
2106 if (elt.__setClassTimeout)
2107 context.clearTimeout(elt.__setClassTimeout);
2109 this.setClass(elt, name);
2111 elt.__setClassTimeout = context.setTimeout(function()
2113 delete elt.__setClassTimeout;
2115 FBL.removeClass(elt, name);
2119 this.cancelClassTimed = function(elt, name, context)
2121 if (elt.__setClassTimeout)
2123 FBL.removeClass(elt, name);
2124 context.clearTimeout(elt.__setClassTimeout);
2125 delete elt.__setClassTimeout;
2130 // ************************************************************************************************
2133 this.$ = function(id, doc)
2136 return doc.getElementById(id);
2139 return FBL.Firebug.chrome.document.getElementById(id);
2143 this.$$ = function(selector, doc)
2145 if (doc || !FBL.Firebug.chrome)
2146 return FBL.Firebug.Selector(selector, doc);
2149 return FBL.Firebug.Selector(selector, FBL.Firebug.chrome.document);
2153 this.getChildByClass = function(node) // ,classname, classname, classname...
2155 for (var i = 1; i < arguments.length; ++i)
2157 var className = arguments[i];
2158 var child = node.firstChild;
2160 for (; child; child = child.nextSibling)
2162 if (this.hasClass(child, className))
2173 this.getAncestorByClass = function(node, className)
2175 for (var parent = node; parent; parent = parent.parentNode)
2177 if (this.hasClass(parent, className))
2185 this.getElementsByClass = function(node, className)
2189 for (var child = node.firstChild; child; child = child.nextSibling)
2191 if (this.hasClass(child, className))
2198 this.getElementByClass = function(node, className) // className, className, ...
2200 var args = cloneArray(arguments); args.splice(0, 1);
2201 for (var child = node.firstChild; child; child = child.nextSibling)
2203 var args1 = cloneArray(args); args1.unshift(child);
2204 if (FBL.hasClass.apply(null, args1))
2208 var found = FBL.getElementByClass.apply(null, args1);
2217 this.isAncestor = function(node, potentialAncestor)
2219 for (var parent = node; parent; parent = parent.parentNode)
2221 if (parent == potentialAncestor)
2228 this.getNextElement = function(node)
2230 while (node && node.nodeType != 1)
2231 node = node.nextSibling;
2236 this.getPreviousElement = function(node)
2238 while (node && node.nodeType != 1)
2239 node = node.previousSibling;
2244 this.getBody = function(doc)
2249 var body = doc.getElementsByTagName("body")[0];
2253 return doc.firstChild; // For non-HTML docs
2256 this.findNextDown = function(node, criteria)
2261 for (var child = node.firstChild; child; child = child.nextSibling)
2263 if (criteria(child))
2266 var next = this.findNextDown(child, criteria);
2272 this.findPreviousUp = function(node, criteria)
2277 for (var child = node.lastChild; child; child = child.previousSibling)
2279 var next = this.findPreviousUp(child, criteria);
2283 if (criteria(child))
2288 this.findNext = function(node, criteria, upOnly, maxRoot)
2295 var next = this.findNextDown(node, criteria);
2300 for (var sib = node.nextSibling; sib; sib = sib.nextSibling)
2305 var next = this.findNextDown(sib, criteria);
2310 if (node.parentNode && node.parentNode != maxRoot)
2311 return this.findNext(node.parentNode, criteria, true);
2314 this.findPrevious = function(node, criteria, downOnly, maxRoot)
2319 for (var sib = node.previousSibling; sib; sib = sib.previousSibling)
2321 var prev = this.findPreviousUp(sib, criteria);
2331 var next = this.findPreviousUp(node, criteria);
2336 if (node.parentNode && node.parentNode != maxRoot)
2338 if (criteria(node.parentNode))
2339 return node.parentNode;
2341 return this.findPrevious(node.parentNode, criteria, true);
2345 this.getNextByClass = function(root, state)
2347 var iter = function iter(node) { return node.nodeType == 1 && FBL.hasClass(node, state); };
2348 return this.findNext(root, iter);
2351 this.getPreviousByClass = function(root, state)
2353 var iter = function iter(node) { return node.nodeType == 1 && FBL.hasClass(node, state); };
2354 return this.findPrevious(root, iter);
2357 this.isElement = function(o)
2360 return o && this.instanceOf(o, "Element");
2368 // ************************************************************************************************
2371 // TODO: xxxpedro use doc fragments in Context API
2372 var appendFragment = null;
2374 this.appendInnerHTML = function(element, html, referenceElement)
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;
2380 var doc = element.ownerDocument;
2382 // doc.createRange not available in IE
2383 if (doc.createRange)
2385 var range = doc.createRange(); // a helper object
2386 range.selectNodeContents(element); // the environment to interpret the html
2388 var fragment = range.createContextualFragment(html); // parse
2389 var firstChild = fragment.firstChild;
2390 element.insertBefore(fragment, referenceElement);
2394 if (!appendFragment || appendFragment.ownerDocument != doc)
2395 appendFragment = doc.createDocumentFragment();
2397 var div = doc.createElement("div");
2398 div.innerHTML = html;
2400 var firstChild = div.firstChild;
2401 while (div.firstChild)
2402 appendFragment.appendChild(div.firstChild);
2404 element.insertBefore(appendFragment, referenceElement);
2413 // ************************************************************************************************
2416 this.createElement = function(tagName, properties)
2418 properties = properties || {};
2419 var doc = properties.document || FBL.Firebug.chrome.document;
2421 var element = doc.createElement(tagName);
2423 for(var name in properties)
2425 if (name != "document")
2427 element[name] = properties[name];
2434 this.createGlobalElement = function(tagName, properties)
2436 properties = properties || {};
2437 var doc = FBL.Env.browser.document;
2439 var element = this.NS && doc.createElementNS ?
2440 doc.createElementNS(FBL.NS, tagName) :
2441 doc.createElement(tagName);
2443 for(var name in properties)
2445 var propname = name;
2446 if (FBL.isIE && name == "class") propname = "className";
2448 if (name != "document")
2450 element.setAttribute(propname, properties[name]);
2457 //************************************************************************************************
2459 this.safeGetWindowLocation = function(window)
2466 return "(window.closed)";
2467 if ("location" in window)
2468 return window.location+"";
2470 return "(no window.location)";
2473 return "(no context.window)";
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+")";
2484 // ************************************************************************************************
2487 this.isLeftClick = function(event)
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);
2495 this.isMiddleClick = function(event)
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);
2503 this.isRightClick = function(event)
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);
2511 this.noKeyModifiers = function(event)
2513 return !event.ctrlKey && !event.shiftKey && !event.altKey && !event.metaKey;
2516 this.isControlClick = function(event)
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);
2524 this.isShiftClick = function(event)
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);
2532 this.isControl = function(event)
2534 return (event.metaKey || event.ctrlKey) && !event.shiftKey && !event.altKey;
2537 this.isAlt = function(event)
2539 return event.altKey && !event.ctrlKey && !event.shiftKey && !event.metaKey;
2542 this.isAltClick = function(event)
2544 return (this.isIE && event.type != "click" && event.type != "dblclick" ?
2545 event.button == 1 : // IE "click" and "dblclick" button model
2546 event.button == 0) &&
2550 this.isControlShift = function(event)
2552 return (event.metaKey || event.ctrlKey) && event.shiftKey && !event.altKey;
2555 this.isShift = function(event)
2557 return event.shiftKey && !event.metaKey && !event.ctrlKey && !event.altKey;
2560 this.addEvent = function(object, name, handler, useCapture)
2562 if (object.addEventListener)
2563 object.addEventListener(name, handler, useCapture);
2565 object.attachEvent("on"+name, handler);
2568 this.removeEvent = function(object, name, handler, useCapture)
2572 if (object.removeEventListener)
2573 object.removeEventListener(name, handler, useCapture);
2575 object.detachEvent("on"+name, handler);
2579 if (FBTrace.DBG_ERRORS)
2580 FBTrace.sysout("FBL.removeEvent error: ", object, name);
2584 this.cancelEvent = function(e, preventDefault)
2590 if (e.preventDefault)
2593 e.returnValue = false;
2596 if (e.stopPropagation)
2597 e.stopPropagation();
2599 e.cancelBubble = true;
2602 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2604 this.addGlobalEvent = function(name, handler)
2606 var doc = this.Firebug.browser.document;
2607 var frames = this.Firebug.browser.window.frames;
2609 this.addEvent(doc, name, handler);
2611 if (this.Firebug.chrome.type == "popup")
2612 this.addEvent(this.Firebug.chrome.document, name, handler);
2614 for (var i = 0, frame; frame = frames[i]; i++)
2618 this.addEvent(frame.document, name, handler);
2622 // Avoid acess denied
2627 this.removeGlobalEvent = function(name, handler)
2629 var doc = this.Firebug.browser.document;
2630 var frames = this.Firebug.browser.window.frames;
2632 this.removeEvent(doc, name, handler);
2634 if (this.Firebug.chrome.type == "popup")
2635 this.removeEvent(this.Firebug.chrome.document, name, handler);
2637 for (var i = 0, frame; frame = frames[i]; i++)
2641 this.removeEvent(frame.document, name, handler);
2645 // Avoid acess denied
2650 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2652 this.dispatch = function(listeners, name, args)
2654 if (!listeners) return;
2658 if (typeof listeners.length != "undefined")
2660 if (FBTrace.DBG_DISPATCH) FBTrace.sysout("FBL.dispatch", name+" to "+listeners.length+" listeners");
2662 for (var i = 0; i < listeners.length; ++i)
2664 var listener = listeners[i];
2665 if ( listener[name] )
2666 listener[name].apply(listener, args);
2671 if (FBTrace.DBG_DISPATCH) FBTrace.sysout("FBL.dispatch", name+" to listeners of an object");
2673 for (var prop in listeners)
2675 var listener = listeners[prop];
2676 if ( listener[name] )
2677 listener[name].apply(listener, args);
2683 if (FBTrace.DBG_ERRORS)
2685 FBTrace.sysout(" Exception in lib.dispatch "+ name, exc);
2686 //FBTrace.dumpProperties(" Exception in lib.dispatch listener", listener);
2692 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2694 var disableTextSelectionHandler = function(event)
2696 FBL.cancelEvent(event, true);
2701 this.disableTextSelection = function(e)
2703 if (typeof e.onselectstart != "undefined") // IE
2704 this.addEvent(e, "selectstart", disableTextSelectionHandler);
2708 e.style.cssText = "user-select: none; -khtml-user-select: none; -moz-user-select: none;";
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);
2716 e.style.cursor = "default";
2719 this.restoreTextSelection = function(e)
2721 if (typeof e.onselectstart != "undefined") // IE
2722 this.removeEvent(e, "selectstart", disableTextSelectionHandler);
2726 e.style.cssText = "cursor: default;";
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);
2735 // ************************************************************************************************
2780 "DOMSubtreeModified",
2783 "DOMNodeRemovedFromDocument",
2784 "DOMNodeInsertedIntoDocument",
2786 "DOMCharacterDataModified" ],
2794 "overflowchanged" ],
2812 this.getEventFamily = function(eventType)
2818 for (var family in eventTypes)
2820 var types = eventTypes[family];
2821 for (var i = 0; i < types.length; ++i)
2822 this.families[types[i]] = family;
2826 return this.families[eventType];
2830 // ************************************************************************************************
2833 this.getFileName = function(url)
2835 var split = this.splitURLBase(url);
2839 this.splitURLBase = function(url)
2841 if (this.isDataURL(url))
2842 return this.splitDataURL(url);
2843 return this.splitURLTrue(url);
2846 this.splitDataURL = function(url)
2848 var mark = url.indexOf(':', 3);
2850 return false; // the first 5 chars must be 'data:'
2852 var point = url.indexOf(',', mark+1);
2854 return false; // syntax error
2856 var props = { encodedContent: url.substr(point+1) };
2858 var metadataBuffer = url.substr(mark+1, point);
2859 var metadata = metadataBuffer.split(';');
2860 for (var i = 0; i < metadata.length; i++)
2862 var nv = metadata[i].split('=');
2864 props[nv[0]] = nv[1];
2867 // Additional Firebug-specific properties
2868 if (props.hasOwnProperty('fileName'))
2870 var caller_URL = decodeURIComponent(props['fileName']);
2871 var caller_split = this.splitURLTrue(caller_URL);
2873 if (props.hasOwnProperty('baseLineNumber')) // this means it's probably an eval()
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;
2882 props['name'] = caller_split.name;
2883 props['path'] = caller_split.path;
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*$/, "");
2897 this.splitURLTrue = function(url)
2899 var m = reSplitFile.exec(url);
2901 return {name: url, path: url};
2903 return {path: m[1], name: m[1]};
2905 return {path: m[1], name: m[2]+m[3]};
2908 this.getFileExtension = function(url)
2913 // Remove query string from the URL if any.
2914 var queryString = url.indexOf("?");
2915 if (queryString != -1)
2916 url = url.substr(0, queryString);
2918 // Now get the file extension.
2919 var lastDot = url.lastIndexOf(".");
2920 return url.substr(lastDot+1);
2923 this.isSystemURL = function(url)
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:")
2930 else if (url.substr(0, 16) == "chrome://firebug")
2932 else if (url == "XPCSafeJSObjectWrapper.cpp")
2934 else if (url.substr(0, 6) == "about:")
2936 else if (url.indexOf("firebug-service.js") != -1)
2942 this.isSystemPage = function(win)
2946 var doc = win.document;
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"))
2957 return FBL.isSystemURL(win.location.href);
2961 // Sometimes documents just aren't ready to be manipulated here, but don't let that
2963 ERROR("tabWatcher.isSystemPage document not ready:"+ exc);
2968 this.isSystemStyleSheet = function(sheet)
2970 var href = sheet && sheet.href;
2971 return href && FBL.isSystemURL(href);
2974 this.getURIHost = function(uri)
2989 this.isLocalURL = function(url)
2991 if (url.substr(0, 5) == "file:")
2993 else if (url.substr(0, 8) == "wyciwyg:")
2999 this.isDataURL = function(url)
3001 return (url && url.substr(0,5) == "data:");
3004 this.getLocalPath = function(url)
3006 if (this.isLocalURL(url))
3008 var fileHandler = ioService.getProtocolHandler("file").QueryInterface(Ci.nsIFileProtocolHandler);
3009 var file = fileHandler.getFileFromURLSpec(url);
3014 this.getURLFromLocalFile = function(file)
3016 var fileHandler = ioService.getProtocolHandler("file").QueryInterface(Ci.nsIFileProtocolHandler);
3017 var URL = fileHandler.getURLSpecFromFile(file);
3021 this.getDataURLForContent = function(content, url)
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);
3030 this.getDomain = function(url)
3032 var m = /[^:]+:\/{1,3}([^\/]+)/.exec(url);
3033 return m ? m[1] : "";
3036 this.getURLPath = function(url)
3038 var m = /[^:]+:\/{1,3}[^\/]+(\/.*?)$/.exec(url);
3039 return m ? m[1] : "";
3042 this.getPrettyDomain = function(url)
3044 var m = /[^:]+:\/{1,3}(www\.)?([^\/]+)/.exec(url);
3045 return m ? m[2] : "";
3048 this.absoluteURL = function(url, baseURL)
3050 return this.absoluteURLWithDots(url, baseURL).replace("/./", "/", "g");
3053 this.absoluteURLWithDots = function(url, baseURL)
3056 return baseURL + url;
3058 var reURL = /(([^:]+:)\/{1,2}[^\/]*)(.*?)$/;
3059 var m = reURL.exec(url);
3063 var m = reURL.exec(baseURL);
3069 if (url.substr(0, 2) == "//")
3071 else if (url[0] == "/")
3075 else if (tail[tail.length-1] == "/")
3076 return baseURL + url;
3079 var parts = tail.split("/");
3080 return head + parts.slice(0, parts.length-1).join("/") + "/" + url;
3084 this.normalizeURL = function(url) // this gets called a lot, any performance improvement welcome
3088 // Replace one or more characters that are not forward-slash followed by /.., by space.
3089 if (url.length < 255) // guard against monsters.
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)
3100 var m = reChromeCase.exec(url); // 1 is package name, 2 is path
3103 url = "chrome://"+m[1].toLowerCase()+"/"+m[2];
3110 this.denormalizeURL = function(url)
3112 return url.replace(/file:\/\/\//g, "file:/");
3115 this.parseURLParams = function(url)
3117 var q = url ? url.indexOf("?") : -1;
3121 var search = url.substr(q+1);
3122 var h = search.lastIndexOf("#");
3124 search = search.substr(0, h);
3129 return this.parseURLEncodedText(search);
3132 this.parseURLEncodedText = function(text)
3134 var maxValueLength = 25000;
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, " ");
3142 var args = text.split("&");
3143 for (var i = 0; i < args.length; ++i)
3146 var parts = args[i].split("=");
3147 if (parts.length == 2)
3149 if (parts[1].length > maxValueLength)
3150 parts[1] = this.$STR("LargeData");
3152 params.push({name: decodeURIComponent(parts[0]), value: decodeURIComponent(parts[1])});
3155 params.push({name: decodeURIComponent(parts[0]), value: ""});
3159 if (FBTrace.DBG_ERRORS)
3161 FBTrace.sysout("parseURLEncodedText EXCEPTION ", e);
3162 FBTrace.sysout("parseURLEncodedText EXCEPTION URI", args[i]);
3167 params.sort(function(a, b) { return a.name <= b.name ? -1 : 1; });
3172 // TODO: xxxpedro lib. why loops in domplate are requiring array in parameters
3173 // as in response/request headers and get/post parameters in Net module?
3174 this.parseURLParamsArray = function(url)
3176 var q = url ? url.indexOf("?") : -1;
3180 var search = url.substr(q+1);
3181 var h = search.lastIndexOf("#");
3183 search = search.substr(0, h);
3188 return this.parseURLEncodedTextArray(search);
3191 this.parseURLEncodedTextArray = function(text)
3193 var maxValueLength = 25000;
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, " ");
3201 var args = text.split("&");
3202 for (var i = 0; i < args.length; ++i)
3205 var parts = args[i].split("=");
3206 if (parts.length == 2)
3208 if (parts[1].length > maxValueLength)
3209 parts[1] = this.$STR("LargeData");
3211 params.push({name: decodeURIComponent(parts[0]), value: [decodeURIComponent(parts[1])]});
3214 params.push({name: decodeURIComponent(parts[0]), value: [""]});
3218 if (FBTrace.DBG_ERRORS)
3220 FBTrace.sysout("parseURLEncodedText EXCEPTION ", e);
3221 FBTrace.sysout("parseURLEncodedText EXCEPTION URI", args[i]);
3226 params.sort(function(a, b) { return a.name <= b.name ? -1 : 1; });
3231 this.reEncodeURL = function(file, text)
3233 var lines = text.split("\n");
3234 var params = this.parseURLEncodedText(lines[lines.length-1]);
3237 for (var i = 0; i < params.length; ++i)
3238 args.push(encodeURIComponent(params[i].name)+"="+encodeURIComponent(params[i].value));
3240 var url = file.href;
3241 url += (url.indexOf("?") == -1 ? "?" : "&") + args.join("&");
3246 this.getResource = function(aURL)
3250 var channel=ioService.newChannel(aURL,null,null);
3251 var input=channel.open();
3252 return FBL.readFromStream(input);
3256 if (FBTrace.DBG_ERRORS)
3257 FBTrace.sysout("lib.getResource FAILS for "+aURL, e);
3261 this.parseJSONString = function(jsonString, originURL)
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);
3269 jsonString = matches[1];
3271 if (jsonString[0] == "\\" && jsonString[1] == "n")
3272 jsonString = jsonString.substr(2);
3274 if (jsonString[jsonString.length-2] == "\\" && jsonString[jsonString.length-1] == "n")
3275 jsonString = jsonString.substr(0, jsonString.length-2);
3278 if (jsonString.indexOf("&&&START&&&"))
3280 regex = new RegExp(/&&&START&&& (.+) &&&END&&&/);
3281 matches = regex.exec(jsonString);
3283 jsonString = matches[1];
3286 // throw on the extra parentheses
3287 jsonString = "(" + jsonString + ")";
3289 ///var s = Components.utils.Sandbox(originURL);
3290 var jsonObject = null;
3294 ///jsonObject = Components.utils.evalInSandbox(jsonString, s);
3296 //jsonObject = Firebug.context.eval(jsonString);
3297 jsonObject = Firebug.context.evaluate(jsonString, null, null, function(){return null;});
3302 if (e.message.indexOf("is not defined"))
3304 var parts = e.message.split(" ");
3305 s[parts[0]] = function(str){ return str; };
3307 jsonObject = Components.utils.evalInSandbox(jsonString, s);
3309 if (FBTrace.DBG_ERRORS || FBTrace.DBG_JSONVIEWER)
3310 FBTrace.sysout("jsonviewer.parseJSON EXCEPTION", e);
3316 if (FBTrace.DBG_ERRORS || FBTrace.DBG_JSONVIEWER)
3317 FBTrace.sysout("jsonviewer.parseJSON EXCEPTION", e);
3325 // ************************************************************************************************
3327 this.objectToString = function(object)
3339 // ************************************************************************************************
3340 // Input Caret Position
3342 this.setSelectionRange = function(input, start, length)
3344 if (input.createTextRange)
3346 var range = input.createTextRange();
3347 range.moveStart("character", start);
3348 range.moveEnd("character", length - input.value.length);
3351 else if (input.setSelectionRange)
3353 input.setSelectionRange(start, length);
3358 // ************************************************************************************************
3359 // Input Selection Start / Caret Position
3361 this.getInputSelectionStart = function(input)
3363 if (document.selection)
3365 var range = input.ownerDocument.selection.createRange();
3366 var text = range.text;
3368 //console.log("range", range.text);
3370 // if there is a selection, find the start position
3373 return input.value.indexOf(text);
3375 // if there is no selection, find the caret position
3378 range.moveStart("character", -input.value.length);
3380 return range.text.length;
3383 else if (typeof input.selectionStart != "undefined")
3384 return input.selectionStart;
3389 // ************************************************************************************************
3392 function onOperaTabBlur(e)
3394 if (this.lastKey == 9)
3398 function onOperaTabKeyDown(e)
3400 this.lastKey = e.keyCode;
3403 function onOperaTabFocus(e)
3405 this.lastKey = null;
3408 this.fixOperaTabKey = function(el)
3410 el.onfocus = onOperaTabFocus;
3411 el.onblur = onOperaTabBlur;
3412 el.onkeydown = onOperaTabKeyDown;
3415 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
3417 this.Property = function(object, name)
3419 this.object = object;
3422 this.getObject = function()
3424 return object[name];
3428 this.ErrorCopy = function(message)
3430 this.message = message;
3433 function EventCopy(event)
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)
3440 this[name] = event[name];
3445 this.EventCopy = EventCopy;
3448 // ************************************************************************************************
3451 var toString = Object.prototype.toString;
3452 var reFunction = /^\s*function(\s+[\w_$][\w\d_$]*)?\s*\(/;
3454 this.isArray = function(object) {
3455 return toString.call(object) === '[object Array]';
3458 this.isFunction = function(object) {
3459 if (!object) return false;
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);
3469 FBTrace.sysout("Lib.isFunction() failed for ", object);
3475 // ************************************************************************************************
3476 // Instance Checking
3478 this.instanceOf = function(object, className)
3480 if (!object || typeof object != "object")
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)
3487 // find the correct window of the object
3488 var win = object.ownerDocument.defaultView || object.ownerDocument.parentWindow;
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])
3498 // If the object doesn't have the ownerDocument property, we'll try to look at
3499 // the current context's window
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];
3510 // get the duck type model from the cache
3511 var cache = instanceCheckMap[className];
3515 // starts the hacky duck type detection
3519 var type = typeof obj;
3520 obj = type == "object" ? obj : [obj];
3522 for(var name in obj)
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))
3529 var value = obj[name];
3531 if( n == "property" && !(value in object) ||
3532 n == "method" && !this.isFunction(object[value]) ||
3533 n == "value" && (""+object[name]).toLowerCase() != (""+value).toLowerCase() )
3541 var instanceCheckMap =
3545 // property: ["window", "document"],
3546 // method: "setTimeout",
3547 // value: {nodeType: 1}
3552 property: ["window", "document"],
3553 method: "setTimeout"
3558 property: ["body", "cookie"],
3559 method: "getElementById"
3564 property: "ownerDocument",
3565 method: "appendChild"
3570 property: "tagName",
3571 value: {nodeType: 1}
3576 property: ["hostname", "protocol"],
3592 property: "hreflang",
3637 property: ["selectorText", "style"]
3643 // ************************************************************************************************
3650 - IE does not have window.Node, window.Element, etc
3651 - for (var name in Node.prototype) return nothing on FF
3656 var domMemberMap2 = {};
3658 var domMemberMap2Sandbox = null;
3660 var getDomMemberMap2 = function(name)
3662 if (!domMemberMap2Sandbox)
3664 var doc = Firebug.chrome.document;
3665 var frame = doc.createElement("iframe");
3667 frame.id = "FirebugSandbox";
3668 frame.style.display = "none";
3669 frame.src = "about:blank";
3671 doc.body.appendChild(frame);
3673 domMemberMap2Sandbox = frame.window || frame.contentWindow;
3678 //var object = domMemberMap2Sandbox[name];
3679 //object = object.prototype || object;
3683 if (name == "Window")
3684 object = domMemberMap2Sandbox.window;
3686 else if (name == "Document")
3687 object = domMemberMap2Sandbox.document;
3689 else if (name == "HTMLScriptElement")
3690 object = domMemberMap2Sandbox.document.createElement("script");
3692 else if (name == "HTMLAnchorElement")
3693 object = domMemberMap2Sandbox.document.createElement("a");
3695 else if (name.indexOf("Element") != -1)
3697 object = domMemberMap2Sandbox.document.createElement("div");
3702 //object = object.prototype || object;
3704 //props = 'addEventListener,document,location,navigator,window'.split(',');
3706 for (var n in object)
3712 return extendArray(props, domMemberMap[name]);
3715 // xxxpedro experimental get DOM members
3716 this.getDOMMembers = function(object)
3718 if (!domMemberCache)
3720 FBL.domMemberCache = domMemberCache = {};
3722 for (var name in domMemberMap)
3724 var builtins = getDomMemberMap2(name);
3725 var cache = domMemberCache[name] = {};
3728 if (name.indexOf("Element") != -1)
3730 this.append(cache, this.getDOMMembers("Node"));
3731 this.append(cache, this.getDOMMembers("Element"));
3735 for (var i = 0; i < builtins.length; ++i)
3736 cache[builtins[i]] = i;
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; }
3791 if (FBTrace.DBG_ERRORS)
3792 FBTrace.sysout("lib.getDOMMembers FAILED ", E);
3800 this.getDOMMembers = function(object)
3802 if (!domMemberCache)
3804 domMemberCache = {};
3806 for (var name in domMemberMap)
3808 var builtins = domMemberMap[name];
3809 var cache = domMemberCache[name] = {};
3811 for (var i = 0; i < builtins.length; ++i)
3812 cache[builtins[i]] = i;
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; }
3872 this.isDOMMember = function(object, propName)
3874 var members = this.getDOMMembers(object);
3875 return members && propName in members;
3878 var domMemberCache = null;
3879 var domMemberMap = {};
3881 domMemberMap.Window =
3943 "removeEventListener",
3949 "enableExternalCapture",
3950 "disableExternalCapture",
3982 "GeckoActiveXObject",
3983 "applicationCache" // FF3
3986 domMemberMap.Location =
4031 "removeEventListener",
4038 "compareDocumentPosition",
4041 "lookupNamespaceURI",
4044 "isDefaultNamespace",
4053 domMemberMap.Document = extendArray(domMemberMap.Node,
4068 "strictErrorChecking",
4090 "preferredStylesheetSet",
4100 "removeEventListener",
4109 "execCommandShowHelp",
4110 "getElementsByName",
4112 "queryCommandEnabled",
4113 "queryCommandIndeterm",
4114 "queryCommandState",
4115 "queryCommandSupported",
4117 "queryCommandValue",
4125 "compareDocumentPosition",
4127 "createAttributeNS",
4128 "createCDATASection",
4130 "createDocumentFragment",
4133 "createEntityReference",
4137 "createNodeIterator",
4138 "createProcessingInstruction",
4146 "getAnonymousElementByAttribute",
4147 "getAnonymousNodes",
4154 "getElementsByTagName",
4155 "getElementsByTagNameNS",
4160 "isDefaultNamespace",
4165 "loadBindingDocument",
4166 "lookupNamespaceURI",
4169 "normalizeDocument",
4175 domMemberMap.Element = extendArray(domMemberMap.Node,
4197 "removeEventListener",
4206 "compareDocumentPosition",
4207 "getElementsByTagName",
4208 "getElementsByTagNameNS",
4212 "getAttributeNodeNS",
4216 "setAttributeNodeNS",
4218 "removeAttributeNS",
4219 "removeAttributeNode",
4224 "lookupNamespaceURI",
4227 "isDefaultNamespace",
4236 domMemberMap.SVGElement = extendArray(domMemberMap.Element,
4249 "farthestViewportElement",
4250 "nearestViewportElement",
4255 "getTransformToElement",
4256 "getPresentationAttribute",
4257 "preserveAspectRatio"
4260 domMemberMap.SVGSVGElement = extendArray(domMemberMap.Element,
4274 "pixelUnitToMillimeterX",
4275 "pixelUnitToMillimeterY",
4276 "screenPixelToMillimeterX",
4277 "screenPixelToMillimeterY",
4284 "farthestViewportElement",
4285 "nearestViewportElement",
4286 "contentScriptType",
4292 "getTransformToElement",
4294 "getIntersectionList",
4295 "getViewboxToViewportTransform",
4296 "getPresentationAttribute",
4299 "checkIntersection",
4307 "createSVGTransform",
4308 "createSVGTransformFromMatrix",
4310 "preserveAspectRatio",
4314 "unsuspendRedrawAll",
4322 domMemberMap.HTMLImageElement = extendArray(domMemberMap.Element,
4343 domMemberMap.HTMLAnchorElement = extendArray(domMemberMap.Element,
4366 domMemberMap.HTMLIFrameElement = extendArray(domMemberMap.Element,
4381 domMemberMap.HTMLTableElement = extendArray(domMemberMap.Element,
4407 domMemberMap.HTMLTableRowElement = extendArray(domMemberMap.Element,
4421 domMemberMap.HTMLTableCellElement = extendArray(domMemberMap.Element,
4440 domMemberMap.HTMLScriptElement = extendArray(domMemberMap.Element,
4445 domMemberMap.HTMLButtonElement = extendArray(domMemberMap.Element,
4457 domMemberMap.HTMLInputElement = extendArray(domMemberMap.Element,
4485 domMemberMap.HTMLFormElement = extendArray(domMemberMap.Element,
4506 domMemberMap.HTMLBodyElement = extendArray(domMemberMap.Element,
4516 domMemberMap.HTMLHtmlElement = extendArray(domMemberMap.Element,
4521 domMemberMap.Text = extendArray(domMemberMap.Node,
4534 domMemberMap.Attr = extendArray(domMemberMap.Node,
4542 domMemberMap.Event =
4548 "explicitOriginalTarget",
4582 "getPreventDefault",
4593 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
4595 this.domConstantMap =
4598 "ATTRIBUTE_NODE": 1,
4600 "CDATA_SECTION_NODE": 1,
4601 "ENTITY_REFERENCE_NODE": 1,
4603 "PROCESSING_INSTRUCTION_NODE": 1,
4606 "DOCUMENT_TYPE_NODE": 1,
4607 "DOCUMENT_FRAGMENT_NODE": 1,
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,
4622 "FONT_FACE_RULE": 1,
4625 "CAPTURING_PHASE": 1,
4627 "BUBBLING_PHASE": 1,
4629 "SCROLL_PAGE_UP": 1,
4630 "SCROLL_PAGE_DOWN": 1,
4670 "DOM_VK_PAGE_UP": 1,
4671 "DOM_VK_PAGE_DOWN": 1,
4678 "DOM_VK_BACK_SPACE": 1,
4683 "DOM_VK_CONTROL": 1,
4686 "DOM_VK_CAPS_LOCK": 1,
4691 "DOM_VK_PRINTSCREEN": 1,
4704 "DOM_VK_SEMICOLON": 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,
4745 "DOM_VK_SEPARATOR": 1,
4746 "DOM_VK_SUBTRACT": 1,
4747 "DOM_VK_DECIMAL": 1,
4773 "DOM_VK_NUM_LOCK": 1,
4774 "DOM_VK_SCROLL_LOCK": 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,
4785 "SVG_ZOOMANDPAN_DISABLE": 1,
4786 "SVG_ZOOMANDPAN_MAGNIFY": 1,
4787 "SVG_ZOOMANDPAN_UNKNOWN": 1
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"],
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"],
4823 "caption-side": ["captionSide"],
4824 "clear": ["clear", "none"],
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"],
4834 "float": ["float", "none"],
4835 "font": ["fontStyle", "fontVariant", "fontWeight", "fontFamily"],
4837 "font-family": ["fontFamily"],
4838 "font-size": ["fontSize"],
4839 "font-size-adjust": [],
4841 "font-style": ["fontStyle"],
4842 "font-variant": ["fontVariant"],
4843 "font-weight": ["fontWeight"],
4847 "letter-spacing": [],
4850 "list-style": ["listStyleType", "listStylePosition", "none"],
4851 "list-style-image": ["none"],
4852 "list-style-position": ["listStylePosition"],
4853 "list-style-type": ["listStyleType", "none"],
4858 "margin-bottom": [],
4861 "marker-offset": ["auto"],
4862 "min-height": ["none"],
4863 "max-height": ["none"],
4864 "min-width": ["none"],
4865 "max-width": ["none"],
4867 "outline": ["borderStyle", "color", "systemColor", "none"],
4868 "outline-color": ["color", "systemColor"],
4869 "outline-style": ["borderStyle"],
4870 "outline-width": [],
4872 "overflow": ["overflow", "auto"],
4873 "overflow-x": ["overflow", "auto"],
4874 "overflow-y": ["overflow", "auto"],
4878 "padding-right": [],
4879 "padding-bottom": [],
4882 "position": ["position"],
4885 "table-layout": ["tableLayout", "auto"],
4886 "text-align": ["textAlign"],
4887 "text-decoration": ["textDecoration", "none"],
4890 "text-transform": ["textTransform", "none"],
4893 "vertical-align": ["verticalAlign"],
4894 "white-space": ["whiteSpace"],
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"],
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": [],
4925 "-moz-column-count": [],
4926 "-moz-column-gap": [],
4927 "-moz-column-width": [],
4928 "-moz-image-region": []
4931 this.inheritedStyleNames =
4933 "border-collapse": 1,
4934 "border-spacing": 1,
4943 "font-size-adjust": 1,
4948 "letter-spacing": 1,
4951 "list-style-image": 1,
4952 "list-style-position": 1,
4953 "list-style-type": 1,
4956 "text-decoration": 1,
4959 "text-transform": 1,
4971 "checkbox-container",
4978 "menulist-textfield",
4986 "scrollbarbutton-down",
4987 "scrollbarbutton-left",
4988 "scrollbarbutton-right",
4989 "scrollbarbutton-up",
4990 "scrollbartrack-horizontal",
4991 "scrollbartrack-vertical",
5003 "treeheadersortarrow",
5027 "InactiveCaptionText",
5036 "ThreeDLightShadow",
5044 "-moz-visitedhyperlinktext",
5045 "-moz-use-text-color"
5117 "LightGoldenRodYellow",
5139 "MediumSpringGreen",
5323 "-moz-scrollbars-horizontal",
5324 "-moz-scrollbars-none",
5325 "-moz-scrollbars-vertical"
5334 "decimal-leading-zero",
5353 "listStylePosition":
5443 "table-column-group",
5444 "table-header-group",
5445 "table-footer-group",
5456 "-moz-inline-block",
5459 "-moz-inline-stack",
5460 "-moz-inline-table",
5598 this.nonEditableTags =
5606 this.innerEditableTags =
5612 this.selfClosingTags =
5613 { // End tags for void elements are forbidden http://wiki.whatwg.org/wiki/HTML_vs._XHTML
5627 var invisibleTags = this.invisibleTags =
5665 if (typeof KeyEvent == "undefined") {
5669 DOM_VK_BACK_SPACE: 8,
5678 DOM_VK_CAPS_LOCK: 20,
5682 DOM_VK_PAGE_DOWN: 34,
5689 DOM_VK_PRINTSCREEN: 44,
5702 DOM_VK_SEMICOLON: 59,
5730 DOM_VK_CONTEXT_MENU: 93,
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,
5743 DOM_VK_SEPARATOR: 108,
5744 DOM_VK_SUBTRACT: 109,
5745 DOM_VK_DECIMAL: 110,
5771 DOM_VK_NUM_LOCK: 144,
5772 DOM_VK_SCROLL_LOCK: 145,
5776 DOM_VK_BACK_QUOTE: 192,
5777 DOM_VK_OPEN_BRACKET: 219,
5778 DOM_VK_BACK_SLASH: 220,
5779 DOM_VK_CLOSE_BRACKET: 221,
5786 // ************************************************************************************************
5797 states: ["Uninitialized","Loading","Loaded","Interactive","Complete"],
5799 initialize: function()
5801 this.transport = FBL.getNativeXHRObject();
5804 getXHRObject: function()
5809 xhrObj = new XMLHttpRequest();
5814 "MSXML2.XMLHTTP.5.0", "MSXML2.XMLHTTP.4.0",
5815 "MSXML2.XMLHTTP.3.0", "MSXML2.XMLHTTP", "Microsoft.XMLHTTP"
5818 for ( var i=0; i < progid.length; ++i ) {
5821 xhrObj = new ActiveXObject(progid[i]);
5838 * Create a AJAX 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
5855 request: function(options)
5864 contentType: "application/x-www-form-urlencoded"
5869 this.requests.push(o);
5871 var s = this.getState();
5872 if (s == "Uninitialized" || s == "Complete" || s == "Loaded")
5876 serialize: function(data)
5878 var r = [""], rl = 0;
5880 if (typeof data == "string") r[rl++] = data;
5882 else if (data.innerHTML && data.elements) {
5883 for (var i=0,el,l=(el=data.elements).length; i < l; i++)
5885 r[rl++] = encodeURIComponent(el[i].name);
5887 r[rl++] = encodeURIComponent(el[i].value);
5892 for(var param in data) {
5893 r[rl++] = encodeURIComponent(param);
5895 r[rl++] = encodeURIComponent(data[param]);
5899 return r.join("").replace(/&$/, "");
5902 sendRequest: function()
5904 var t = FBL.Ajax.transport, r = FBL.Ajax.requests.shift(), data;
5907 t.open(r.type, r.url, r.async);
5909 //setRequestHeaders();
5911 // indicates that it is a XHR request to the server
5912 t.setRequestHeader("X-Requested-With", "XMLHttpRequest");
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);
5919 // onreadystatechange handler
5920 t.onreadystatechange = function()
5922 FBL.Ajax.onStateChange(r);
5930 * Handles the state change
5932 onStateChange: function(options)
5934 var fn, o = options, t = this.transport;
5935 var state = this.getState(t);
5937 if (fn = o["on" + state]) fn(this.getResponse(o), o);
5939 if (state == "Complete")
5941 var success = t.status == 200, response = this.getResponse(o);
5943 if (fn = o["onUpdate"])
5946 if (fn = o["on" + (success ? "Success" : "Failure")])
5949 t.onreadystatechange = FBL.emptyFn;
5951 if (this.requests.length > 0)
5952 setTimeout(this.sendRequest, 10);
5957 * gets the appropriate response value according the type
5959 getResponse: function(options)
5961 var t = this.transport, type = options.dataType;
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 + ")");
5971 * returns the current state of the XHR object
5973 getState: function()
5975 return this.states[this.transport.readyState];
5981 // ************************************************************************************************
5982 // Cookie, from http://www.quirksmode.org/js/cookies.html
5984 this.createCookie = function(name,value,days)
5986 if ('cookie' in document)
5990 var date = new Date();
5991 date.setTime(date.getTime()+(days*24*60*60*1000));
5992 var expires = "; expires="+date.toGMTString();
5997 document.cookie = name+"="+value+expires+"; path=/";
6001 this.readCookie = function (name)
6003 if ('cookie' in document)
6005 var nameEQ = name + "=";
6006 var ca = document.cookie.split(';');
6008 for(var i=0; i < ca.length; 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);
6019 this.removeCookie = function(name)
6021 this.createCookie(name, "", -1);
6025 // ************************************************************************************************
6026 // http://www.mister-pixel.com/#Content__state=is_that_simple
6027 var fixIE6BackgroundImageCache = function(doc)
6029 doc = doc || document;
6032 doc.execCommand("BackgroundImageCache", false, true);
6040 // ************************************************************************************************
6041 // calculatePixelsPerInch
6043 var resetStyle = "margin:0; padding:0; border:0; position:absolute; overflow:hidden; display:block;";
6045 var calculatePixelsPerInch = function calculatePixelsPerInch(doc, body)
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);
6051 FBL.pixelsPerInch = {
6052 x: inch.offsetWidth,
6053 y: inch.offsetHeight
6056 body.removeChild(inch);
6060 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6062 this.SourceLink = function(url, line, type, object, instance)
6065 this.instance = instance;
6068 this.object = object;
6071 this.SourceLink.prototype =
6073 toString: function()
6077 toJSON: function() // until 3.1...
6079 return "{\"href\":\""+this.href+"\", "+
6080 (this.line?("\"line\":"+this.line+","):"")+
6081 (this.type?(" \"type\":\""+this.type+"\","):"")+
6087 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6089 this.SourceText = function(lines, owner)
6095 this.SourceText.getLineAsHTML = function(lineNo)
6097 return escapeForSourceLine(this.lines[lineNo-1]);
6101 // ************************************************************************************************
6104 /* See license.txt for terms of usage */
6106 FBL.ns( /** @scope s_i18n */ function() { with (FBL) {
6107 // ************************************************************************************************
6109 // TODO: xxxpedro localization
6112 "NoMembersWarning": "There are no properties to show for this object.",
6114 "EmptyStyleSheet": "There are no rules in this stylesheet.",
6115 "EmptyElementCSS": "This element has no style rules.",
6116 "AccessRestricted": "Access to restricted URI denied.",
6118 "net.label.Parameters": "Parameters",
6119 "net.label.Source": "Source",
6120 "URLParameters": "Params",
6122 "EditStyle": "Edit Element Style...",
6123 "NewRule": "New Rule...",
6125 "NewProp": "New Property...",
6126 "EditProp": 'Edit "%s"',
6127 "DeleteProp": 'Delete "%s"',
6128 "DisableProp": 'Disable "%s"'
6131 // ************************************************************************************************
6133 FBL.$STR = function(name)
6135 return oSTR.hasOwnProperty(name) ? oSTR[name] : name;
6138 FBL.$STRF = function(name, args)
6140 if (!oSTR.hasOwnProperty(name)) return name;
6142 var format = oSTR[name];
6145 var parts = parseFormat(format);
6146 var trialIndex = objIndex;
6149 for (var i= 0; i < parts.length; i++)
6151 var part = parts[i];
6152 if (part && typeof(part) == "object")
6154 if (++trialIndex > objects.length) // then too few parameters for format, assume unformatted.
6166 for (var i = 0; i < parts.length; ++i)
6168 var part = parts[i];
6169 if (part && typeof(part) == "object")
6171 result.push(""+args.shift());
6177 return result.join("");
6180 // ************************************************************************************************
6182 var parseFormat = function parseFormat(format)
6185 if (format.length <= 0)
6188 var reg = /((^%|.%)(\d+)?(\.)([a-zA-Z]))|((^%|.%)([a-zA-Z]))/;
6189 for (var m = reg.exec(format); m; m = reg.exec(format))
6191 if (m[0].substr(0, 2) == "%%")
6193 parts.push(format.substr(0, m.index));
6194 parts.push(m[0].substr(1));
6198 var type = m[8] ? m[8] : m[5];
6199 var precision = m[3] ? parseInt(m[3]) : (m[4] == "." ? -1 : 0);
6205 rep = FirebugReps.Text;
6210 rep = FirebugReps.Number;
6217 parts.push(format.substr(0, m[0][0] == "%" ? m.index : m.index+1));
6218 parts.push({rep: rep, precision: precision, type: ("%" + type)});
6221 format = format.substr(m.index+m[0].length);
6228 // ************************************************************************************************
6231 /* See license.txt for terms of usage */
6233 FBL.ns( /** @scope s_firebug */ function() { with (FBL) {
6234 // ************************************************************************************************
6236 // ************************************************************************************************
6239 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6243 var panelTypes = [];
6244 var panelTypeMap = {};
6247 var parentPanelMap = {};
6250 // ************************************************************************************************
6254 * @namespace describe Firebug
6255 * @exports FBL.Firebug as Firebug
6259 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6260 version: "Firebug Lite 1.4.0",
6261 revision: "$Revision$",
6263 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6265 panelTypes: panelTypes,
6266 panelTypeMap: panelTypeMap,
6269 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6272 initialize: function()
6274 if (FBTrace.DBG_INITIALIZE) FBTrace.sysout("Firebug.initialize", "initializing application");
6276 Firebug.browser = new Context(Env.browser);
6277 Firebug.context = Firebug.browser;
6279 Firebug.loadPrefs();
6280 Firebug.context.persistedState.isOpen = false;
6282 // Document must be cached before chrome initialization
6285 if (Firebug.Inspector && Firebug.Inspector.create)
6286 Firebug.Inspector.create();
6288 if (FBL.CssAnalyzer && FBL.CssAnalyzer.processAllStyleSheets)
6289 FBL.CssAnalyzer.processAllStyleSheets(Firebug.browser.document);
6291 FirebugChrome.initialize();
6293 dispatch(modules, "initialize", []);
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");
6304 var onLoad = Env.onLoad;
6307 setTimeout(onLoad, 200);
6311 shutdown: function()
6313 if (Firebug.saveCookies)
6314 Firebug.savePrefs();
6316 if (Firebug.Inspector)
6317 Firebug.Inspector.destroy();
6319 dispatch(modules, "shutdown", []);
6321 var chromeMap = FirebugChrome.chromeMap;
6323 for (var name in chromeMap)
6325 if (chromeMap.hasOwnProperty(name))
6329 chromeMap[name].destroy();
6333 if (FBTrace.DBG_ERRORS) FBTrace.sysout("chrome.destroy() failed to: " + name);
6338 Firebug.Lite.Cache.Element.clear();
6339 Firebug.Lite.Cache.StyleSheet.clear();
6341 Firebug.browser = null;
6342 Firebug.context = null;
6345 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6348 registerModule: function()
6350 modules.push.apply(modules, arguments);
6352 if (FBTrace.DBG_INITIALIZE) FBTrace.sysout("Firebug.registerModule");
6355 registerPanel: function()
6357 panelTypes.push.apply(panelTypes, arguments);
6359 for (var i = 0, panelType; panelType = arguments[i]; ++i)
6361 panelTypeMap[panelType.prototype.name] = arguments[i];
6363 if (panelType.prototype.parentPanel)
6364 parentPanelMap[panelType.prototype.parentPanel] = 1;
6367 if (FBTrace.DBG_INITIALIZE)
6368 for (var i = 0; i < arguments.length; ++i)
6369 FBTrace.sysout("Firebug.registerPanel", arguments[i].prototype.name);
6372 registerRep: function()
6374 reps.push.apply(reps, arguments);
6377 unregisterRep: function()
6379 for (var i = 0; i < arguments.length; ++i)
6380 remove(reps, arguments[i]);
6383 setDefaultReps: function(funcRep, rep)
6385 FBL.defaultRep = rep;
6386 FBL.defaultFuncRep = funcRep;
6389 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6392 getRep: function(object)
6394 var type = typeof object;
6395 if (isIE && isFunction(object))
6398 for (var i = 0; i < reps.length; ++i)
6403 if (rep.supportsObject(object, type))
6405 if (FBTrace.DBG_DOM)
6406 FBTrace.sysout("getRep type: "+type+" object: "+object, rep);
6412 if (FBTrace.DBG_ERRORS)
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
6422 return (type == 'function') ? defaultFuncRep : defaultRep;
6425 getRepObject: function(node)
6428 for (var child = node; child; child = child.parentNode)
6430 if (hasClass(child, "repTarget"))
6433 if (child.repObject)
6435 if (!target && hasClass(child, "repIgnore"))
6438 return child.repObject;
6443 getRepNode: function(node)
6445 for (var child = node; child; child = child.parentNode)
6447 if (child.repObject)
6452 getElementByRepObject: function(element, object)
6454 for (var child = element.firstChild; child; child = child.nextSibling)
6456 if (child.repObject == object)
6461 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6464 getPref: function(name)
6466 return Firebug[name];
6469 setPref: function(name, value)
6471 Firebug[name] = value;
6473 Firebug.savePrefs();
6476 setPrefs: function(prefs)
6478 for (var name in prefs)
6480 if (prefs.hasOwnProperty(name))
6481 Firebug[name] = prefs[name];
6484 Firebug.savePrefs();
6487 restorePrefs: function()
6489 var Options = Env.DefaultOptions;
6491 for (var name in Options)
6493 Firebug[name] = Options[name];
6497 loadPrefs: function()
6499 this.restorePrefs();
6501 var prefs = Store.get("FirebugLite") || {};
6502 var options = prefs.options;
6503 var persistedState = prefs.persistedState || FBL.defaultPersistedState;
6505 for (var name in options)
6507 if (options.hasOwnProperty(name))
6508 Firebug[name] = options[name];
6511 if (Firebug.context && persistedState)
6512 Firebug.context.persistedState = persistedState;
6515 savePrefs: function()
6521 var EnvOptions = Env.Options;
6522 var options = prefs.options;
6523 for (var name in EnvOptions)
6525 if (EnvOptions.hasOwnProperty(name))
6527 options[name] = Firebug[name];
6531 var persistedState = Firebug.context.persistedState;
6532 if (!persistedState)
6534 persistedState = Firebug.context.persistedState = FBL.defaultPersistedState;
6537 prefs.persistedState = persistedState;
6539 Store.set("FirebugLite", prefs);
6542 erasePrefs: function()
6544 Store.remove("FirebugLite");
6545 this.restorePrefs();
6549 Firebug.restorePrefs();
6551 // xxxpedro should we remove this?
6552 window.Firebug = FBL.Firebug;
6554 if (!Env.Options.enablePersistent ||
6555 Env.Options.enablePersistent && Env.isChromeContext ||
6557 Env.browser.window.Firebug = FBL.Firebug;
6560 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6563 FBL.cacheDocument = function cacheDocument()
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++)
6574 // ************************************************************************************************
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
6585 Firebug.Listener = function()
6587 // The array is created when the first listeners is added.
6588 // It can't be created here since derived objects would share
6590 this.fbListeners = null;
6593 Firebug.Listener.prototype =
6595 addListener: function(listener)
6597 if (!this.fbListeners)
6598 this.fbListeners = []; // delay the creation until the objects are created so 'this' causes new array for each module
6600 this.fbListeners.push(listener);
6603 removeListener: function(listener)
6605 remove(this.fbListeners, listener); // if this.fbListeners is null, remove is being called with no add
6609 // ************************************************************************************************
6612 // ************************************************************************************************
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
6621 Firebug.Module = extend(new Firebug.Listener(),
6622 /** @extend Firebug.Module */
6625 * Called when the window is opened.
6627 initialize: function()
6632 * Called when the window is closed.
6634 shutdown: function()
6638 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6641 * Called when a new context is created but before the page is loaded.
6643 initContext: function(context)
6648 * Called after a context is detached to a separate window;
6650 reattachContext: function(browser, context)
6655 * Called when a context is destroyed. Module may store info on persistedState for reloaded pages.
6657 destroyContext: function(context, persistedState)
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)
6668 * Called after a context's page gets DOMContentLoaded
6670 loadedContext: function(context)
6674 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6676 showPanel: function(browser, panel)
6680 showSidePanel: function(browser, panel)
6684 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6686 updateOption: function(name, value)
6690 getObjectByURL: function(context, url)
6695 // ************************************************************************************************
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.
6706 title: "Hello World!",
6710 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6713 hasCommandLine: false,
6714 hasStatusBar: false,
6715 hasToolButtons: false,
6717 // Pre-rendered panels are those included in the skin file (firebug.html)
6718 isPreRendered: false,
6719 innerHTMLSync: false
6722 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6723 // To be used by external extensions
6731 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6735 sidePanelNode: null,
6736 statusBarNode: null,
6737 toolButtonsNode: null,
6741 sidePanelBarBoxNode: null,
6742 sidePanelBarNode: null,
6744 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6748 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6753 statusSeparator: "<",
6755 create: function(context, doc)
6757 this.hasSidePanel = parentPanelMap.hasOwnProperty(this.name);
6759 this.panelBarNode = $("fbPanelBar1");
6760 this.sidePanelBarBoxNode = $("fbPanelBar2");
6762 if (this.hasSidePanel)
6764 this.sidePanelBar = extend({}, PanelBar);
6765 this.sidePanelBar.create(this);
6768 var options = this.options = extend(Firebug.Panel.options, this.options);
6769 var panelId = "fb" + this.name;
6771 if (options.isPreRendered)
6773 this.panelNode = $(panelId);
6775 this.tabNode = $(panelId + "Tab");
6776 this.tabNode.style.display = "block";
6778 if (options.hasToolButtons)
6780 this.toolButtonsNode = $(panelId + "Buttons");
6783 if (options.hasStatusBar)
6785 this.statusBarBox = $("fbStatusBarBox");
6786 this.statusBarNode = $(panelId + "StatusBar");
6791 var containerSufix = this.parentPanel ? "2" : "1";
6793 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6795 var panelNode = this.panelNode = createElement("div", {
6797 className: "fbPanel"
6800 $("fbPanel" + containerSufix).appendChild(panelNode);
6802 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6804 var tabHTML = '<span class="fbTabL"></span><span class="fbTabText">' +
6805 this.title + '</span><span class="fbTabR"></span>';
6807 var tabNode = this.tabNode = createElement("a", {
6808 id: panelId + "Tab",
6809 className: "fbTab fbHover",
6815 tabNode.href = "javascript:void(0)";
6818 var panelBarNode = this.parentPanel ?
6819 Firebug.chrome.getPanel(this.parentPanel).sidePanelBarNode :
6822 panelBarNode.appendChild(tabNode);
6823 tabNode.style.display = "block";
6825 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6826 // create ToolButtons
6827 if (options.hasToolButtons)
6829 this.toolButtonsNode = createElement("span", {
6830 id: panelId + "Buttons",
6831 className: "fbToolbarButtons"
6834 $("fbToolbarButtons").appendChild(this.toolButtonsNode);
6837 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6839 if (options.hasStatusBar)
6841 this.statusBarBox = $("fbStatusBarBox");
6843 this.statusBarNode = createElement("span", {
6844 id: panelId + "StatusBar",
6845 className: "fbToolbarButtons fbStatusBar"
6848 this.statusBarBox.appendChild(this.statusBarNode);
6851 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6855 this.containerNode = this.panelNode.parentNode;
6857 if (FBTrace.DBG_INITIALIZE) FBTrace.sysout("Firebug.Panel.create", this.name);
6859 // xxxpedro contextMenu
6860 this.onContextMenu = bind(this.onContextMenu, this);
6863 this.context = context;
6864 this.document = doc;
6866 this.panelNode = doc.createElement("div");
6867 this.panelNode.ownerPanel = this;
6869 setClass(this.panelNode, "panelNode panelNode-"+this.name+" contextUID="+context.uid);
6870 doc.body.appendChild(this.panelNode);
6872 if (FBTrace.DBG_INITIALIZE)
6873 FBTrace.sysout("firebug.initialize panelNode for "+this.name+"\n");
6875 this.initializeNode(this.panelNode);
6879 destroy: function(state) // Panel may store info on state
6881 if (FBTrace.DBG_INITIALIZE) FBTrace.sysout("Firebug.Panel.destroy", this.name);
6883 if (this.hasSidePanel)
6885 this.sidePanelBar.destroy();
6886 this.sidePanelBar = null;
6889 this.options = null;
6891 this.parentPanel = null;
6893 this.tabNode = null;
6894 this.panelNode = null;
6895 this.containerNode = null;
6897 this.toolButtonsNode = null;
6898 this.statusBarBox = null;
6899 this.statusBarNode = null;
6901 //if (this.panelNode)
6902 // delete this.panelNode.ownerPanel;
6904 //this.destroyNode();
6907 initialize: function()
6909 if (FBTrace.DBG_INITIALIZE) FBTrace.sysout("Firebug.Panel.initialize", this.name);
6911 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6912 if (this.hasSidePanel)
6914 this.sidePanelBar.initialize();
6917 var options = this.options = extend(Firebug.Panel.options, this.options);
6918 var panelId = "fb" + this.name;
6920 this.panelNode = $(panelId);
6922 this.tabNode = $(panelId + "Tab");
6923 this.tabNode.style.display = "block";
6925 if (options.hasStatusBar)
6927 this.statusBarBox = $("fbStatusBarBox");
6928 this.statusBarNode = $(panelId + "StatusBar");
6931 if (options.hasToolButtons)
6933 this.toolButtonsNode = $(panelId + "Buttons");
6936 this.containerNode = this.panelNode.parentNode;
6938 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6939 // restore persistent state
6940 this.containerNode.scrollTop = this.lastScrollTop;
6942 // xxxpedro contextMenu
6943 addEvent(this.containerNode, "contextmenu", this.onContextMenu);
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;
6952 Firebug.showInfoTips = true;
6953 if (Firebug.InfoTip)
6954 Firebug.InfoTip.initializeBrowser(Firebug.chrome);
6957 shutdown: function()
6959 if (FBTrace.DBG_INITIALIZE) FBTrace.sysout("Firebug.Panel.shutdown", this.name);
6961 /// TODO: xxxpedro infoTip Hack
6962 if (Firebug.InfoTip)
6963 Firebug.InfoTip.uninitializeBrowser(Firebug.chrome);
6965 if (Firebug.chrome.largeCommandLineVisible)
6966 Firebug.chrome.hideLargeCommandLine();
6968 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6969 if (this.hasSidePanel)
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();
6978 // store persistent state
6979 this.lastScrollTop = this.containerNode.scrollTop;
6981 // xxxpedro contextMenu
6982 removeEvent(this.containerNode, "contextmenu", this.onContextMenu);
6985 detach: function(oldChrome, newChrome)
6987 if (oldChrome && oldChrome.selectedPanel && oldChrome.selectedPanel.name == this.name)
6988 this.lastScrollTop = oldChrome.selectedPanel.containerNode.scrollTop;
6991 reattach: function(doc)
6993 if (this.options.innerHTMLSync)
6994 this.synchronizeUI();
6997 synchronizeUI: function()
6999 this.containerNode.scrollTop = this.lastScrollTop || 0;
7002 show: function(state)
7004 var options = this.options;
7006 if (options.hasStatusBar)
7008 this.statusBarBox.style.display = "inline";
7009 this.statusBarNode.style.display = "inline";
7012 if (options.hasToolButtons)
7014 this.toolButtonsNode.style.display = "inline";
7017 this.panelNode.style.display = "block";
7019 this.visible = true;
7021 if (!this.parentPanel)
7022 Firebug.chrome.layout(this);
7025 hide: function(state)
7027 var options = this.options;
7029 if (options.hasStatusBar)
7031 this.statusBarBox.style.display = "none";
7032 this.statusBarNode.style.display = "none";
7035 if (options.hasToolButtons)
7037 this.toolButtonsNode.style.display = "none";
7040 this.panelNode.style.display = "none";
7042 this.visible = false;
7045 watchWindow: function(win)
7049 unwatchWindow: function(win)
7053 updateOption: function(name, value)
7057 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
7062 showToolbarButtons: function(buttonsId, show)
7066 if (!this.context.browser) // XXXjjb this is bug. Somehow the panel context is not FirebugContext.
7068 if (FBTrace.DBG_ERRORS)
7069 FBTrace.sysout("firebug.Panel showToolbarButtons this.context has no browser, this:", this);
7073 var buttons = this.context.browser.chrome.$(buttonsId);
7075 collapse(buttons, show ? "false" : "true");
7079 if (FBTrace.DBG_ERRORS)
7081 FBTrace.dumpProperties("firebug.Panel showToolbarButtons FAILS", exc);
7082 if (!this.context.browser)FBTrace.dumpStack("firebug.Panel showToolbarButtons no browser");
7087 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
7090 * Returns a number indicating the view's ability to inspect the object.
7092 * Zero means not supported, and higher numbers indicate specificity.
7094 supportsObject: function(object)
7099 hasObject: function(object) // beyond type testing, is this object selectable?
7104 select: function(object, forceUpdate)
7107 object = this.getDefaultSelection(this.context);
7109 if(FBTrace.DBG_PANELS)
7110 FBTrace.sysout("firebug.select "+this.name+" forceUpdate: "+forceUpdate+" "+object+((object==this.selection)?"==":"!=")+this.selection);
7112 if (forceUpdate || object != this.selection)
7114 this.selection = object;
7115 this.updateSelection(object);
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
7125 updateSelection: function(object)
7129 markChange: function(skipSelf)
7131 if (this.dependents)
7135 for (var i = 0; i < this.dependents.length; ++i)
7137 var panelName = this.dependents[i];
7138 if (panelName != this.name)
7139 this.context.invalidatePanels(panelName);
7143 this.context.invalidatePanels.apply(this.context, this.dependents);
7147 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
7149 startInspecting: function()
7153 stopInspecting: function(object, cancelled)
7157 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
7159 search: function(text, reverse)
7164 * Retrieves the search options that this modules supports.
7165 * This is used by the search UI to present the proper options.
7167 getSearchOptionsMenuItems: function()
7170 Firebug.Search.searchOptionMenu("search.Case Sensitive", "searchCaseSensitive")
7175 * Navigates to the next document whose match parameter returns true.
7177 navigateToNextDocument: function(match, reverse)
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.
7184 function compare(a, b) {
7185 var locA = self.getObjectDescription(a);
7186 var locB = self.getObjectDescription(b);
7187 if(locA.path > locB.path)
7189 if(locA.path < locB.path)
7191 if(locA.name > locB.name)
7193 if(locA.name < locB.name)
7197 var allLocs = this.getLocationList().sort(compare);
7198 for (var curPos = 0; curPos < allLocs.length && allLocs[curPos] != this.location; curPos++);
7200 function transformIndex(index) {
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;
7206 return (curPos + index + 1) % allLocs.length;
7210 for (var next = 0; next < allLocs.length - 1; next++)
7212 var object = allLocs[transformIndex(next)];
7216 this.navigate(object);
7222 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
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()
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.
7239 getContextMenuItems: function(object, target)
7244 getBreakOnMenuItems: function()
7249 getEditor: function(target, value)
7253 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
7255 getDefaultSelection: function()
7260 browseObject: function(object)
7264 getPopupObject: function(target)
7266 return Firebug.getRepObject(target);
7269 getTooltipObject: function(target)
7271 return Firebug.getRepObject(target);
7274 showInfoTip: function(infoTip, x, y)
7279 getObjectPath: function(object)
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()
7292 getDefaultLocation: function()
7297 getObjectLocation: function(object)
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)
7306 var url = this.getObjectLocation(object);
7307 return FBL.splitURLBase(url);
7311 * UI signal that a tab needs attention, eg Script panel is currently stopped on a breakpoint
7312 * @param: show boolean, true turns on.
7314 highlight: function(show)
7316 var tab = this.getTab();
7321 tab.setAttribute("highlight", "true");
7323 tab.removeAttribute("highlight");
7328 var chrome = Firebug.chrome;
7330 var tab = chrome.$("fbPanelBar2").getTab(this.name);
7332 tab = chrome.$("fbPanelBar1").getTab(this.name);
7336 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
7337 // Support for Break On Next
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|.
7345 breakOnNext: function(armed)
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.
7353 shouldBreakOnNext: function()
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.
7363 getBreakOnNextTooltip: function(enabled)
7368 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
7370 // xxxpedro contextMenu
7371 onContextMenu: function(event)
7373 if (!this.getContextMenuItems)
7376 cancelEvent(event, true);
7378 var target = event.target || event.srcElement;
7380 var menu = this.getContextMenuItems(this.selection, target);
7384 var contextMenu = new Menu(
7386 id: "fbPanelContextMenu",
7391 contextMenu.show(event.clientX, event.clientY);
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);
7403 if (typeof window.mozInnerScreenY != "undefined")
7405 screenY = window.mozInnerScreenY;
7408 else if (typeof window.innerHeight != "undefined")
7410 screenY = window.outerHeight - window.innerHeight;
7413 else if (typeof window.screenTop != "undefined")
7415 screenY = window.screenTop;
7418 contextMenu.show(event.screenX-box.left, event.screenY-screenY-box.top);
7422 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
7426 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
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>
7438 Firebug.MeasureBox =
7440 startMeasuring: function(target)
7442 if (!this.measureBox)
7444 this.measureBox = target.ownerDocument.createElement("span");
7445 this.measureBox.className = "measureBox";
7448 copyTextStyles(target, this.measureBox);
7449 target.ownerDocument.body.appendChild(this.measureBox);
7452 getMeasuringElement: function()
7454 return this.measureBox;
7457 measureText: function(value)
7459 this.measureBox.innerHTML = value ? escapeForSourceLine(value) : "m";
7460 return {width: this.measureBox.offsetWidth, height: this.measureBox.offsetHeight-1};
7463 measureInputText: function(value)
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};
7472 getBox: function(target)
7474 var style = this.measureBox.ownerDocument.defaultView.getComputedStyle(this.measureBox, "");
7475 var box = getBoxFromStyles(style, this.measureBox);
7479 stopMeasuring: function()
7481 this.measureBox.parentNode.removeChild(this.measureBox);
7486 // ************************************************************************************************
7487 if (FBL.domplate) Firebug.Rep = domplate(
7492 supportsObject: function(object, type)
7497 inspectObject: function(object, context)
7499 Firebug.chrome.select(object);
7502 browseObject: function(object, context)
7506 persistObject: function(object, context)
7510 getRealObject: function(object, context)
7515 getTitle: function(object)
7517 var label = safeToString(object);
7519 var re = /\[object (.*?)\]/;
7520 var m = re.exec(label);
7522 ///return m ? m[1] : label;
7524 // if the label is in the "[object TYPE]" format return its type
7529 // if it is IE we need to handle some special cases
7531 // safeToString() fails to recognize some objects in IE
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")
7547 getTooltip: function(object)
7552 getContextMenuItems: function(object, target, context)
7557 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
7558 // Convenience for domplates
7565 cropString: function(text)
7567 return cropString(text);
7570 cropMultipleLines: function(text, limit)
7572 return cropMultipleLines(text, limit);
7575 toLowerCase: function(text)
7577 return text ? text.toLowerCase() : text;
7582 return n == 1 ? "" : "s";
7586 // ************************************************************************************************
7589 // ************************************************************************************************
7592 /* See license.txt for terms of usage */
7594 FBL.ns( /** @scope s_gui */ function() { with (FBL) {
7595 // ************************************************************************************************
7597 // ************************************************************************************************
7604 controllerContext: null,
7606 initialize: function(context)
7608 this.controllers = [];
7609 this.controllerContext = context || Firebug.chrome;
7612 shutdown: function()
7614 this.removeControllers();
7616 //this.controllers = null;
7617 //this.controllerContext = null;
7620 addController: function()
7622 for (var i=0, arg; arg=arguments[i]; i++)
7624 // If the first argument is a string, make a selector query
7625 // within the controller node context
7626 if (typeof arg[0] == "string")
7628 arg[0] = $$(arg[0], this.controllerContext);
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
7638 this.controllers.push(arg);
7639 addEvent.apply(this, arg);
7643 removeController: function()
7645 for (var i=0, arg; arg=arguments[i]; i++)
7647 for (var j=0, c; c=this.controllers[j]; j++)
7649 if (arg[0] == c[0] && arg[1] == c[1] && arg[2] == c[3])
7650 removeEvent.apply(this, c);
7655 removeControllers: function()
7657 for (var i=0, c; c=this.controllers[i]; i++)
7659 removeEvent.apply(this, c);
7665 // ************************************************************************************************
7671 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
7675 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
7677 selectedPanel: null,
7678 parentPanelName: null,
7680 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
7682 create: function(ownerPanel)
7685 this.ownerPanel = ownerPanel;
7689 ownerPanel.sidePanelBarNode = createElement("span");
7690 ownerPanel.sidePanelBarNode.style.display = "none";
7691 ownerPanel.sidePanelBarBoxNode.appendChild(ownerPanel.sidePanelBarNode);
7694 var panels = Firebug.panelTypes;
7695 for (var i=0, p; p=panels[i]; i++)
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)
7703 this.addPanel(p.prototype.name);
7710 PanelBar.shutdown.call(this);
7712 for (var name in this.panelMap)
7714 this.removePanel(name);
7716 var panel = this.panelMap[name];
7719 this.panelMap[name] = null;
7720 delete this.panelMap[name];
7723 this.panelMap = null;
7724 this.ownerPanel = null;
7727 initialize: function()
7729 if (this.ownerPanel)
7730 this.ownerPanel.sidePanelBarNode.style.display = "inline";
7732 for(var name in this.panelMap)
7734 (function(self, name){
7736 // tab click handler
7737 var onTabClick = function onTabClick()
7739 self.selectPanel(name);
7743 Firebug.chrome.addController([self.panelMap[name].tabNode, "mousedown", onTabClick]);
7749 shutdown: function()
7751 var selectedPanel = this.selectedPanel;
7755 removeClass(selectedPanel.tabNode, "fbSelectedTab");
7756 selectedPanel.hide();
7757 selectedPanel.shutdown();
7760 if (this.ownerPanel)
7761 this.ownerPanel.sidePanelBarNode.style.display = "none";
7763 this.selectedPanel = null;
7766 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
7768 addPanel: function(panelName, parentPanel)
7770 var PanelType = Firebug.panelTypeMap[panelName];
7771 var panel = this.panelMap[panelName] = new PanelType();
7776 removePanel: function(panelName)
7778 var panel = this.panelMap[panelName];
7779 if (panel.hasOwnProperty(panelName))
7783 selectPanel: function(panelName)
7785 var selectedPanel = this.selectedPanel;
7786 var panel = this.panelMap[panelName];
7788 if (panel && selectedPanel != panel)
7792 removeClass(selectedPanel.tabNode, "fbSelectedTab");
7793 selectedPanel.shutdown();
7794 selectedPanel.hide();
7797 if (!panel.parentPanel)
7798 Firebug.context.persistedState.selectedPanelName = panelName;
7800 this.selectedPanel = panel;
7802 setClass(panel.tabNode, "fbSelectedTab");
7808 getPanel: function(panelName)
7810 var panel = this.panelMap[panelName];
7817 //************************************************************************************************
7827 * options.pressedClassName
7834 * @extends FBL.Controller
7838 FBL.Button = function(options)
7840 options = options || {};
7842 append(this, options);
7844 this.state = "unpressed";
7845 this.display = "unpressed";
7849 this.container = this.element.parentNode;
7853 this.shouldDestroy = true;
7855 this.container = this.owner.getPanel().toolButtonsNode;
7857 this.element = createElement("a", {
7858 className: this.baseClassName + " " + this.className + " fbHover",
7859 innerHTML: this.caption
7863 this.element.title = this.title;
7865 this.container.appendChild(this.element);
7869 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
7871 Button.prototype = extend(Controller,
7872 /**@extend FBL.Button.prototype*/
7878 className: "", // custom class
7879 baseClassName: "fbButton", // control class
7880 pressedClassName: "fbBtnPressed", // control pressed class
7893 // only remove if it is a dynamically generated button (not pre-rendered)
7894 if (this.shouldDestroy)
7895 this.container.removeChild(this.element);
7897 this.element = null;
7898 this.container = null;
7902 initialize: function()
7904 Controller.initialize.apply(this);
7906 var element = this.element;
7908 this.addController([element, "mousedown", this.handlePress]);
7910 if (this.type == "normal")
7912 [element, "mouseup", this.handleUnpress],
7913 [element, "mouseout", this.handleUnpress],
7914 [element, "click", this.handleClick]
7918 shutdown: function()
7920 Controller.shutdown.apply(this);
7925 this.changeState("unpressed");
7928 changeState: function(state)
7931 this.changeDisplay(state);
7934 changeDisplay: function(display)
7936 if (display != this.display)
7938 if (display == "pressed")
7940 setClass(this.element, this.pressedClassName);
7942 else if (display == "unpressed")
7944 removeClass(this.element, this.pressedClassName);
7946 this.display = display;
7950 handlePress: function(event)
7952 cancelEvent(event, true);
7954 if (this.type == "normal")
7956 this.changeDisplay("pressed");
7957 this.beforeClick = true;
7959 else if (this.type == "toggle")
7961 if (this.state == "pressed")
7963 this.changeState("unpressed");
7966 this.onUnpress.apply(this.owner, arguments);
7970 this.changeState("pressed");
7973 this.onPress.apply(this.owner, arguments);
7977 this.onClick.apply(this.owner, arguments);
7983 handleUnpress: function(event)
7985 cancelEvent(event, true);
7987 if (this.beforeClick)
7988 this.changeDisplay("unpressed");
7993 handleClick: function(event)
7995 cancelEvent(event, true);
7997 if (this.type == "normal")
8000 this.onClick.apply(this.owner);
8002 this.changeState("unpressed");
8005 this.beforeClick = false;
8011 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
8015 * @extends FBL.Button
8017 FBL.IconButton = function()
8019 Button.apply(this, arguments);
8022 IconButton.prototype = extend(Button.prototype,
8023 /**@extend FBL.IconButton.prototype*/
8025 baseClassName: "fbIconButton",
8026 pressedClassName: "fbIconPressed"
8030 //************************************************************************************************
8033 var menuItemProps = {"class": "$item.className", type: "$item.type", value: "$item.value",
8034 _command: "$item.command"};
8037 menuItemProps.href = "javascript:void(0)";
8039 // Allow GUI to be loaded even when Domplate module is not installed.
8041 var MenuPlate = domplate(Firebug.Rep,
8044 DIV({"class": "fbMenu fbShadow"},
8045 DIV({"class": "fbMenuContent fbShadowContent"},
8046 FOR("item", "$object.items|memberIterator",
8047 TAG("$item.tag", {item: "$item"})
8058 A(extend(menuItemProps, {checked : "$item.checked"}),
8064 A(extend(menuItemProps, {selected : "$item.selected"}),
8070 A(extend(menuItemProps, {child: "$item.child"}),
8077 SPAN({"class": "fbMenuShortcutKey"},
8083 SPAN({"class": "fbMenuSeparator"}),
8085 memberIterator: function(items)
8089 for (var i=0, length=items.length; i<length; i++)
8091 var item = items[i];
8093 // separator representation
8094 if (typeof item == "string" && item.indexOf("-") == 0)
8096 result.push({tag: this.separatorTag});
8100 item = extend(item, {});
8102 item.type = item.type || "";
8103 item.value = item.value || "";
8105 var type = item.type;
8107 // default item representation
8108 item.tag = this.itemTag;
8110 var className = item.className || "";
8112 className += "fbMenuOption fbHover ";
8114 // specific representations
8115 if (type == "checkbox")
8117 className += "fbMenuCheckBox ";
8118 item.tag = this.checkBoxTag;
8120 else if (type == "radiobutton")
8122 className += "fbMenuRadioButton ";
8123 item.tag = this.radioButtonTag;
8125 else if (type == "group")
8127 className += "fbMenuGroup ";
8128 item.tag = this.groupTag;
8130 else if (type == "shortcut")
8132 className += "fbMenuShortcut ";
8133 item.tag = this.shortcutTag;
8137 className += "fbMenuChecked ";
8138 else if (item.selected)
8139 className += "fbMenuRadioSelected ";
8142 className += "fbMenuDisabled ";
8144 item.className = className;
8146 item.label = $STR(item.label);
8155 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
8175 * @extends FBL.Controller
8178 FBL.Menu = function(options)
8180 // if element is not pre-rendered, we must render it now
8181 if (!options.element)
8183 if (options.getItems)
8184 options.items = options.getItems();
8186 options.element = MenuPlate.tag.append(
8188 getElementByClass(Firebug.chrome.document, "fbBody"),
8193 // extend itself with the provided options
8194 append(this, options);
8196 if (typeof this.element == "string")
8198 this.id = this.element;
8199 this.element = $(this.id);
8203 this.element.id = this.id;
8206 this.element.firebugIgnore = true;
8207 this.elementStyle = this.element.style;
8209 this.isVisible = false;
8211 this.handleMouseDown = bind(this.handleMouseDown, this);
8212 this.handleMouseOver = bind(this.handleMouseOver, this);
8213 this.handleMouseOut = bind(this.handleMouseOut, this);
8215 this.handleWindowMouseDown = bind(this.handleWindowMouseDown, this);
8218 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
8222 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
8224 Menu.prototype = extend(Controller,
8225 /**@extend FBL.Menu.prototype*/
8229 //if (this.element) console.log("destroy", this.element.id);
8233 // if it is a childMenu, remove its reference from the parentMenu
8234 if (this.parentMenu)
8235 this.parentMenu.childMenu = null;
8237 // remove the element from the document
8238 this.element.parentNode.removeChild(this.element);
8241 this.element = null;
8242 this.elementStyle = null;
8243 this.parentMenu = null;
8244 this.parentTarget = null;
8247 initialize: function()
8249 Controller.initialize.call(this);
8252 [this.element, "mousedown", this.handleMouseDown],
8253 [this.element, "mouseover", this.handleMouseOver]
8257 shutdown: function()
8259 Controller.shutdown.call(this);
8262 show: function(x, y)
8266 if (this.isVisible) return;
8268 //console.log("show", this.element.id);
8273 if (this.parentMenu)
8275 var oldChildMenu = this.parentMenu.childMenu;
8276 if (oldChildMenu && oldChildMenu != this)
8278 oldChildMenu.destroy();
8281 this.parentMenu.childMenu = this;
8284 addEvent(Firebug.chrome.document, "mousedown", this.handleWindowMouseDown);
8286 this.elementStyle.display = "block";
8287 this.elementStyle.visibility = "hidden";
8289 var size = Firebug.chrome.getSize();
8291 x = Math.min(x, size.width - this.element.clientWidth - 10);
8294 y = Math.min(y, size.height - this.element.clientHeight - 10);
8297 this.elementStyle.left = x + "px";
8298 this.elementStyle.top = y + "px";
8300 this.elementStyle.visibility = "visible";
8302 this.isVisible = true;
8304 if (isFunction(this.onShow))
8305 this.onShow.apply(this, arguments);
8310 this.clearHideTimeout();
8311 this.clearShowChildTimeout();
8313 if (!this.isVisible) return;
8315 //console.log("hide", this.element.id);
8317 this.elementStyle.display = "none";
8321 this.childMenu.destroy();
8322 this.childMenu = null;
8325 if(this.parentTarget)
8326 removeClass(this.parentTarget, "fbMenuGroupSelected");
8328 this.isVisible = false;
8332 if (isFunction(this.onHide))
8333 this.onHide.apply(this, arguments);
8336 showChildMenu: function(target)
8338 var id = target.getAttribute("child");
8341 var target = target;
8343 this.showChildTimeout = Firebug.chrome.window.setTimeout(function(){
8345 //if (!parent.isVisible) return;
8347 var box = Firebug.chrome.getElementBox(target);
8349 var childMenuObject = menuMap.hasOwnProperty(id) ?
8350 menuMap[id] : {element: $(id)};
8352 var childMenu = new Menu(extend(childMenuObject,
8355 parentTarget: target
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");
8365 clearHideTimeout: function()
8367 if (this.hideTimeout)
8369 Firebug.chrome.window.clearTimeout(this.hideTimeout);
8370 delete this.hideTimeout;
8374 clearShowChildTimeout: function()
8376 if(this.showChildTimeout)
8378 Firebug.chrome.window.clearTimeout(this.showChildTimeout);
8379 this.showChildTimeout = null;
8383 handleMouseDown: function(event)
8385 cancelEvent(event, true);
8387 var topParent = this;
8388 while (topParent.parentMenu)
8389 topParent = topParent.parentMenu;
8391 var target = event.target || event.srcElement;
8393 target = getAncestorByClass(target, "fbMenuOption");
8395 if(!target || hasClass(target, "fbMenuGroup"))
8398 if (target && !hasClass(target, "fbMenuDisabled"))
8400 var type = target.getAttribute("type");
8402 if (type == "checkbox")
8404 var checked = target.getAttribute("checked");
8405 var value = target.getAttribute("value");
8406 var wasChecked = hasClass(target, "fbMenuChecked");
8410 removeClass(target, "fbMenuChecked");
8411 target.setAttribute("checked", "");
8415 setClass(target, "fbMenuChecked");
8416 target.setAttribute("checked", "true");
8419 if (isFunction(this.onCheck))
8420 this.onCheck.call(this, target, value, !wasChecked);
8423 if (type == "radiobutton")
8425 var selectedRadios = getElementsByClass(target.parentNode, "fbMenuRadioSelected");
8427 var group = target.getAttribute("group");
8429 for (var i = 0, length = selectedRadios.length; i < length; i++)
8431 radio = selectedRadios[i];
8433 if (radio.getAttribute("group") == group)
8435 removeClass(radio, "fbMenuRadioSelected");
8436 radio.setAttribute("selected", "");
8440 setClass(target, "fbMenuRadioSelected");
8441 target.setAttribute("selected", "true");
8446 // target.command can be a function or a string.
8447 var cmd = target.command;
8449 // If it is a function it will be used as the handler
8450 if (isFunction(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];
8457 var closeMenu = true;
8460 closeMenu = handler.call(this, target) !== false;
8469 handleWindowMouseDown: function(event)
8471 //console.log("handleWindowMouseDown");
8473 var target = event.target || event.srcElement;
8475 target = getAncestorByClass(target, "fbMenu");
8479 removeEvent(Firebug.chrome.document, "mousedown", this.handleWindowMouseDown);
8484 handleMouseOver: function(event)
8486 //console.log("handleMouseOver", this.element.id);
8488 this.clearHideTimeout();
8489 this.clearShowChildTimeout();
8491 var target = event.target || event.srcElement;
8493 target = getAncestorByClass(target, "fbMenuOption");
8498 var childMenu = this.childMenu;
8501 removeClass(childMenu.parentTarget, "fbMenuGroupSelected");
8503 if (childMenu.parentTarget != target && childMenu.isVisible)
8505 childMenu.clearHideTimeout();
8506 childMenu.hideTimeout = Firebug.chrome.window.setTimeout(function(){
8507 childMenu.destroy();
8512 if(hasClass(target, "fbMenuGroup"))
8514 this.showChildMenu(target);
8519 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
8522 /**@extend FBL.Menu*/
8524 register: function(object)
8526 menuMap[object.id] = object;
8529 check: function(element)
8531 setClass(element, "fbMenuChecked");
8532 element.setAttribute("checked", "true");
8535 uncheck: function(element)
8537 removeClass(element, "fbMenuChecked");
8538 element.setAttribute("checked", "");
8541 disable: function(element)
8543 setClass(element, "fbMenuDisabled");
8546 enable: function(element)
8548 removeClass(element, "fbMenuDisabled");
8553 //************************************************************************************************
8557 function StatusBar(){};
8559 StatusBar.prototype = extend(Controller, {
8563 // ************************************************************************************************
8566 // ************************************************************************************************
8569 /* See license.txt for terms of usage */
8571 FBL.ns( /**@scope s_context*/ function() { with (FBL) {
8572 // ************************************************************************************************
8574 // ************************************************************************************************
8577 var refreshDelay = 300;
8579 // Opera and some versions of webkit returns the wrong value of document.elementFromPoint()
8580 // function, without taking into account the scroll position. Safari 4 (webkit/531.21.8)
8581 // still have this issue. Google Chrome 4 (webkit/532.5) does not. So, we're assuming this
8582 // issue was fixed in the 532 version
8583 var shouldFixElementFromPoint = isOpera || isSafari && browserVersion < "532";
8585 var evalError = "___firebug_evaluation_error___";
8588 var resetStyle = "margin:0; padding:0; border:0; position:absolute; overflow:hidden; display:block;";
8589 var offscreenStyle = resetStyle + "top:-1234px; left:-1234px;";
8592 // ************************************************************************************************
8596 FBL.Context = function(win)
8598 this.window = win.window;
8599 this.document = win.document;
8601 this.browser = Env.browser;
8603 // Some windows in IE, like iframe, doesn't have the eval() method
8604 if (isIE && !this.window.eval)
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");
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 }" +
8621 FBL.Context.prototype =
8623 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
8624 // partial-port of Firebug tabContext.js
8629 setTimeout: function(fn, delay)
8631 var win = this.window;
8633 if (win.setTimeout == this.setTimeout)
8634 throw new Error("setTimeout recursion");
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);
8643 this.timeouts[timeout] = 1;
8648 clearTimeout: function(timeout)
8650 clearTimeout(timeout);
8653 delete this.timeouts[timeout];
8656 setInterval: function(fn, delay)
8658 var win = this.window;
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);
8664 if (!this.intervals)
8665 this.intervals = {};
8667 this.intervals[timeout] = 1;
8672 clearInterval: function(timeout)
8674 clearInterval(timeout);
8677 delete this.intervals[timeout];
8680 invalidatePanels: function()
8682 if (!this.invalidPanels)
8683 this.invalidPanels = {};
8685 for (var i = 0; i < arguments.length; ++i)
8687 var panelName = arguments[i];
8689 // avoid error. need to create a better getPanel() function as explained below
8690 if (!Firebug.chrome || !Firebug.chrome.selectedPanel)
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) :
8701 if (panel && !panel.noRefresh)
8702 this.invalidPanels[panelName] = 1;
8705 if (this.refreshTimeout)
8707 this.clearTimeout(this.refreshTimeout);
8708 delete this.refreshTimeout;
8711 this.refreshTimeout = this.setTimeout(bindFixed(function()
8715 for (var panelName in this.invalidPanels)
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) :
8727 if (panel.visible && !panel.editing)
8730 panel.needsRefresh = true;
8732 // If the panel is being edited, we'll keep trying to
8733 // refresh it until editing is done
8735 invalids.push(panelName);
8739 delete this.invalidPanels;
8740 delete this.refreshTimeout;
8742 // Keep looping until every tab is valid
8743 if (invalids.length)
8744 this.invalidatePanels.apply(this, invalids);
8745 }, this), refreshDelay);
8749 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
8750 // Evalutation Method
8753 * Evaluates an expression in the current context window.
8755 * @param {String} expr expression to be evaluated
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"
8764 * @param {String} api string indicating the global location
8765 * of the object that will be used as the
8766 * api of the evaluation.
8768 * @param {Function} errorHandler(message) error handler to be called
8769 * if the evaluation fails.
8771 evaluate: function(expr, context, api, errorHandler)
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";
8777 var isObjectLiteral = trim(expr).indexOf("{") == 0,
8781 // if the context is the "window" object, we don't need a closure
8782 if (context == "window")
8784 // If it is an object literal, then wrap the expression with parenthesis so we can
8785 // capture the return value
8786 if (isObjectLiteral)
8789 "with("+api+"){ ("+expr+") }" :
8795 "with("+api+"){ "+expr+" }" :
8802 // with API and context, no return value
8803 "(function(arguments){ with(" + api + "){ " +
8805 " } }).call(" + context + ",undefined)"
8807 // with context only, no return value
8808 "(function(arguments){ " +
8810 " }).call(" + context + ",undefined)";
8813 result = this.eval(cmd);
8815 if (result && result[evalError])
8817 var msg = result.name ? (result.name + ": ") : "";
8818 msg += result.message || result;
8821 result = errorHandler(msg);
8830 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
8833 getWindowSize: function()
8835 var width=0, height=0, el;
8837 if (typeof this.window.innerWidth == "number")
8839 width = this.window.innerWidth;
8840 height = this.window.innerHeight;
8842 else if ((el=this.document.documentElement) && (el.clientHeight || el.clientWidth))
8844 width = el.clientWidth;
8845 height = el.clientHeight;
8847 else if ((el=this.document.body) && (el.clientHeight || el.clientWidth))
8849 width = el.clientWidth;
8850 height = el.clientHeight;
8853 return {width: width, height: height};
8856 getWindowScrollSize: function()
8858 var width=0, height=0, el;
8860 // first try the document.documentElement scroll size
8861 if (!isIEQuiksMode && (el=this.document.documentElement) &&
8862 (el.scrollHeight || el.scrollWidth))
8864 width = el.scrollWidth;
8865 height = el.scrollHeight;
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))
8874 width = el.scrollWidth;
8875 height = el.scrollHeight;
8878 return {width: width, height: height};
8881 getWindowScrollPosition: function()
8883 var top=0, left=0, el;
8885 if(typeof this.window.pageYOffset == "number")
8887 top = this.window.pageYOffset;
8888 left = this.window.pageXOffset;
8890 else if((el=this.document.body) && (el.scrollTop || el.scrollLeft))
8893 left = el.scrollLeft;
8895 else if((el=this.document.documentElement) && (el.scrollTop || el.scrollLeft))
8898 left = el.scrollLeft;
8901 return {top:top, left:left};
8905 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
8908 getElementFromPoint: function(x, y)
8910 if (shouldFixElementFromPoint)
8912 var scroll = this.getWindowScrollPosition();
8913 return this.document.elementFromPoint(x + scroll.left, y + scroll.top);
8916 return this.document.elementFromPoint(x, y);
8919 getElementPosition: function(el)
8926 left += el.offsetLeft;
8927 top += el.offsetTop;
8929 while (el = el.offsetParent);
8931 return {left:left, top:top};
8934 getElementBox: function(el)
8938 if (el.getBoundingClientRect)
8940 var rect = el.getBoundingClientRect();
8942 // fix IE problem with offset when not in fullscreen mode
8943 var offset = isIE ? this.document.body.clientTop || this.document.documentElement.clientTop: 0;
8945 var scroll = this.getWindowScrollPosition();
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);
8954 var position = this.getElementPosition(el);
8956 result.top = position.top;
8957 result.left = position.left;
8958 result.height = el.offsetHeight;
8959 result.width = el.offsetWidth;
8966 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
8967 // Measurement Methods
8969 getMeasurement: function(el, name)
8971 var result = {value: 0, unit: "px"};
8973 var cssValue = this.getStyle(el, name);
8975 if (!cssValue) return result;
8976 if (cssValue.toLowerCase() == "auto") return result;
8978 var reMeasure = /(\d+\.?\d*)(.*)/;
8979 var m = cssValue.match(reMeasure);
8983 result.value = m[1]-0;
8984 result.unit = m[2].toLowerCase();
8990 getMeasurementInPixels: function(el, name)
8992 if (!el) return null;
8994 var m = this.getMeasurement(el, name);
8995 var value = m.value;
9001 else if (unit == "pt")
9002 return this.pointsToPixels(name, value);
9004 else if (unit == "em")
9005 return this.emToPixels(el, value);
9007 else if (unit == "%")
9008 return this.percentToPixels(el, value);
9010 else if (unit == "ex")
9011 return this.exToPixels(el, value);
9013 // TODO: add other units. Maybe create a better general way
9014 // to calculate measurements in different units.
9017 getMeasurementBox1: function(el, name)
9019 var sufixes = ["Top", "Left", "Bottom", "Right"];
9022 for(var i=0, sufix; sufix=sufixes[i]; i++)
9023 result[i] = Math.round(this.getMeasurementInPixels(el, name + sufix));
9025 return {top:result[0], left:result[1], bottom:result[2], right:result[3]};
9028 getMeasurementBox: function(el, name)
9031 var sufixes = name == "border" ?
9032 ["TopWidth", "LeftWidth", "BottomWidth", "RightWidth"] :
9033 ["Top", "Left", "Bottom", "Right"];
9037 var propName, cssValue;
9038 var autoMargin = null;
9040 for(var i=0, sufix; sufix=sufixes[i]; i++)
9042 propName = name + sufix;
9044 cssValue = el.currentStyle[propName] || el.style[propName];
9046 if (cssValue == "auto")
9049 autoMargin = this.getCSSAutoMarginBox(el);
9051 result[i] = autoMargin[sufix.toLowerCase()];
9054 result[i] = this.getMeasurementInPixels(el, propName);
9061 for(var i=0, sufix; sufix=sufixes[i]; i++)
9062 result[i] = this.getMeasurementInPixels(el, name + sufix);
9065 return {top:result[0], left:result[1], bottom:result[2], right:result[3]};
9068 getCSSAutoMarginBox: function(el)
9070 if (isIE && " meta title input script link a ".indexOf(" "+el.nodeName.toLowerCase()+" ") != -1)
9071 return {top:0, left:0, bottom:0, right:0};
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};
9079 if (false && isIEStantandMode)
9081 var scrollSize = Firebug.browser.getWindowScrollSize();
9082 offsetTop = scrollSize.height;
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;";
9089 var clone = el.cloneNode(false);
9090 var text = this.document.createTextNode(" ");
9091 clone.appendChild(text);
9093 box.appendChild(clone);
9095 this.document.body.appendChild(box);
9097 var marginTop = clone.offsetTop - box.offsetTop - 1;
9098 var marginBottom = box.offsetHeight - clone.offsetHeight - 2 - marginTop;
9100 var marginLeft = clone.offsetLeft - box.offsetLeft - 1;
9101 var marginRight = box.offsetWidth - clone.offsetWidth - 2 - marginLeft;
9103 this.document.body.removeChild(box);
9105 return {top:marginTop+offsetTop, left:marginLeft, bottom:marginBottom-offsetTop, right:marginRight};
9108 getFontSizeInPixels: function(el)
9110 var size = this.getMeasurement(el, "fontSize");
9112 if (size.unit == "px") return size.value;
9114 // get font size, the dirty way
9115 var computeDirtyFontSize = function(el, calibration)
9117 var div = this.document.createElement("div");
9118 var divStyle = offscreenStyle;
9121 divStyle += " font-size:"+calibration+"px;";
9123 div.style.cssText = divStyle;
9124 div.innerHTML = "A";
9125 el.appendChild(div);
9127 var value = div.offsetHeight;
9128 el.removeChild(div);
9133 var calibrationBase = 200;
9134 var calibrationValue = computeDirtyFontSize(el, calibrationBase);
9135 var rate = calibrationBase / calibrationValue;
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;
9142 var value = computeDirtyFontSize(el);
9144 return value * rate;
9148 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9151 pointsToPixels: function(name, value, returnFloat)
9153 var axis = /Top$|Bottom$/.test(name) ? "y" : "x";
9155 var result = value * pixelsPerInch[axis] / 72;
9157 return returnFloat ? result : Math.round(result);
9160 emToPixels: function(el, value)
9162 if (!el) return null;
9164 var fontSize = this.getFontSizeInPixels(el);
9166 return Math.round(value * fontSize);
9169 exToPixels: function(el, value)
9171 if (!el) return null;
9173 // get ex value, the dirty way
9174 var div = this.document.createElement("div");
9175 div.style.cssText = offscreenStyle + "width:"+value + "ex;";
9177 el.appendChild(div);
9178 var value = div.offsetWidth;
9179 el.removeChild(div);
9184 percentToPixels: function(el, value)
9186 if (!el) return null;
9188 // get % value, the dirty way
9189 var div = this.document.createElement("div");
9190 div.style.cssText = offscreenStyle + "width:"+value + "%;";
9192 el.appendChild(div);
9193 var value = div.offsetWidth;
9194 el.removeChild(div);
9199 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9201 getStyle: isIE ? function(el, name)
9203 return el.currentStyle[name] || el.style[name] || undefined;
9205 : function(el, name)
9207 return this.document.defaultView.getComputedStyle(el,null)[name]
9208 || el.style[name] || undefined;
9214 // ************************************************************************************************
9217 /* See license.txt for terms of usage */
9219 FBL.ns( /**@scope ns-chrome*/ function() { with (FBL) {
9220 // ************************************************************************************************
9222 // ************************************************************************************************
9225 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9228 var WindowDefaultOptions =
9232 //height: 350 // obsolete
9235 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9236 // Instantiated objects
9240 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9241 // Interface Elements Cache
9256 fbPanelBar2BoxStyle,
9273 fbLargeCommandButtons,
9275 //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9276 // Cached size values
9281 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9283 chromeRedrawSkipRate = isIE ? 75 : isOpera ? 80 : 75,
9285 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9287 lastSelectedPanelName,
9289 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9291 focusCommandLineState = 0,
9292 lastFocusedPanelName,
9294 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9296 lastHSplitterMouseMove = 0,
9297 onHSplitterMouseMoveBuffer = null,
9298 onHSplitterMouseMoveTimer = null,
9300 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9302 lastVSplitterMouseMove = 0;
9304 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9307 // ************************************************************************************************
9310 FBL.defaultPersistedState =
9312 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9315 sidePanelWidth: 350,
9317 selectedPanelName: "Console",
9318 selectedHTMLElementId: null,
9320 htmlSelectionStack: []
9321 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9327 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9331 //sidePanelWidth: 350,
9333 //selectedPanelName: "Console",
9334 //selectedHTMLElementId: null,
9338 htmlSelectionStack: [],
9340 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9344 if (FBTrace.DBG_INITIALIZE) FBTrace.sysout("FirebugChrome.create", "creating chrome window");
9346 createChromeWindow();
9349 initialize: function()
9351 if (FBTrace.DBG_INITIALIZE) FBTrace.sysout("FirebugChrome.initialize", "initializing chrome window");
9353 if (Env.chrome.type == "frame" || Env.chrome.type == "div")
9354 ChromeMini.create(Env.chrome);
9356 var chrome = Firebug.chrome = new Chrome(Env.chrome);
9357 FirebugChrome.chromeMap[chrome.type] = chrome;
9359 addGlobalEvent("keydown", onGlobalKeyDown);
9361 if (Env.Options.enablePersistent && chrome.type == "popup")
9363 // TODO: xxxpedro persist - revise chrome synchronization when in persistent mode
9364 var frame = FirebugChrome.chromeMap.frame;
9368 //chrome.reattach(frame, chrome);
9369 //TODO: xxxpedro persist synchronize?
9370 chrome.initialize();
9374 clone: function(FBChrome)
9376 for (var name in FBChrome)
9378 var prop = FBChrome[name];
9379 if (FBChrome.hasOwnProperty(name) && !isFunction(prop))
9389 // ************************************************************************************************
9390 // Chrome Window Creation
9392 var createChromeWindow = function(options)
9394 options = extend(WindowDefaultOptions, options || {});
9396 //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
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;
9406 context = options.context || Env.browser,
9408 type = chrome.type = Env.Options.enablePersistent ?
9412 isChromeFrame = type == "frame",
9414 useLocalSkin = Env.useLocalSkin,
9416 url = useLocalSkin ?
9420 // document.body not available in XML+XSL documents in Firefox
9421 body = context.document.getElementsByTagName("body")[0],
9423 formatNode = function(node)
9425 if (!Env.isDebugMode)
9427 node.firebugIgnore = true;
9430 var browserWinSize = browserContext.getWindowSize();
9431 var height = persistedState.height || 300;
9433 height = Math.min(browserWinSize.height, height);
9434 height = Math.max(200, height);
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";
9445 // avoid flickering during chrome rendering
9447 // node.style.display = "none";
9450 createChromeDiv = function()
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.");
9454 var node = chrome.node = createGlobalElement("div"),
9455 style = createGlobalElement("style"),
9457 css = FirebugChrome.Skin.CSS
9459 .replace(/;/g, " !important;")
9460 .replace(/!important\s!important/g, "!important")
9461 .replace(/display\s*:\s*(\w+)\s*!important;/g, "display:$1;")*/,
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
9467 // adjust some remaining styles
9468 ".fbBody #fbHSplitter{position:absolute !important;} .fbBody #fbHTML span{line-height:14px;} .fbBody .lineNo div{line-height:inherit !important;}";
9472 // IE7 CSS bug (FbChrome table bigger than its parent div)
9473 rules += ".fbBody table.fbChrome{position: static !important;}";
9476 style.type = "text/css";
9478 if (style.styleSheet)
9479 style.styleSheet.cssText = rules;
9481 style.appendChild(context.document.createTextNode(rules));
9483 document.getElementsByTagName("head")[0].appendChild(style);
9485 node.className = "fbBody";
9486 node.style.overflow = "hidden";
9487 node.innerHTML = getChromeDivTemplate();
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";
9501 body.appendChild(node);
9503 chrome.window = window;
9504 chrome.document = document;
9505 onChromeLoad(chrome);
9508 //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9512 //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9513 // create the Chrome as a "div" (windowless mode)
9520 //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9521 // cretate the Chrome as an "iframe"
9522 else if (isChromeFrame)
9524 // Create the Chrome Frame
9525 var node = chrome.node = createGlobalElement("iframe");
9526 node.setAttribute("src", url);
9527 node.setAttribute("frameBorder", "0");
9531 body.appendChild(node);
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;
9540 //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9541 // create the Chrome as a "popup"
9544 var height = persistedState.popupHeight || 300;
9545 var browserWinSize = browserContext.getWindowSize();
9547 var browserWinLeft = typeof browserWin.screenX == "number" ?
9548 browserWin.screenX : browserWin.screenLeft;
9550 var popupLeft = typeof persistedState.popupLeft == "number" ?
9551 persistedState.popupLeft : browserWinLeft;
9553 var browserWinTop = typeof browserWin.screenY == "number" ?
9554 browserWin.screenY : browserWin.screenTop;
9556 var popupTop = typeof persistedState.popupTop == "number" ?
9557 persistedState.popupTop :
9561 browserWinTop + browserWinSize.height - height,
9562 // Google Chrome bug
9563 screen.availHeight - height - 61
9567 var popupWidth = typeof persistedState.popupWidth == "number" ?
9568 persistedState.popupWidth :
9572 browserWinSize.width,
9573 // Opera opens popup in a new tab if it's too big!
9574 screen.availWidth-10
9578 var popupHeight = typeof persistedState.popupHeight == "number" ?
9579 persistedState.popupHeight : 300;
9582 "true,top=", popupTop,
9583 ",left=", popupLeft,
9584 ",height=", popupHeight,
9585 ",width=", popupWidth,
9589 node = chrome.node = context.window.open(
9603 alert("Firebug Error: Firebug popup was blocked.");
9609 alert("Firebug Error: Firebug popup was blocked.");
9614 //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9615 // Inject the interface HTML if it is not using the local skin
9619 var tpl = getChromeTemplate(!isChromeFrame),
9620 doc = isChromeFrame ? node.contentWindow.document : node.document;
9626 //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9627 // Wait the Window to be loaded
9631 waitDelay = useLocalSkin ? isChromeFrame ? 200 : 300 : 100,
9633 waitForWindow = function()
9635 if ( // Frame loaded... OR
9636 isChromeFrame && (win=node.contentWindow) &&
9637 node.contentWindow.document.getElementById("fbCommandLine") ||
9640 !isChromeFrame && (win=node.window) && node.document &&
9641 node.document.getElementById("fbCommandLine") )
9643 chrome.window = win.window;
9644 chrome.document = win.document;
9646 // Prevent getting the wrong chrome height in FF when opening a popup
9647 setTimeout(function(){
9648 onChromeLoad(chrome);
9649 }, useLocalSkin ? 200 : 0);
9652 setTimeout(waitForWindow, waitDelay);
9659 var msg = e.message || e;
9661 if (/access/i.test(msg))
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".
9668 body.removeChild(node);
9669 else if(type == "popup")
9672 // Load the GUI in a "windowless mode"
9677 alert("Firebug Error: Firebug GUI could not be created.");
9682 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9684 var onChromeLoad = function onChromeLoad(chrome)
9686 Env.chrome = chrome;
9688 if (FBTrace.DBG_INITIALIZE) FBTrace.sysout("Chrome onChromeLoad", "chrome window loaded");
9690 if (Env.Options.enablePersistent)
9692 // TODO: xxxpedro persist - make better chrome synchronization when in persistent mode
9693 Env.FirebugChrome = FirebugChrome;
9695 chrome.window.Firebug = chrome.window.Firebug || {};
9696 chrome.window.Firebug.SharedEnv = Env;
9698 if (Env.isDevelopmentMode)
9700 Env.browser.window.FBDev.loadChromeApplication(chrome);
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);
9712 if (chrome.type == "frame" || chrome.type == "div")
9714 // initialize the chrome application
9715 setTimeout(function(){
9716 FBL.Firebug.initialize();
9719 else if (chrome.type == "popup")
9721 var oldChrome = FirebugChrome.chromeMap.frame;
9723 var newChrome = new Chrome(chrome);
9725 // TODO: xxxpedro sync detach reattach attach
9726 dispatch(newChrome.panelMap, "detach", [oldChrome, newChrome]);
9728 newChrome.reattach(oldChrome, newChrome);
9733 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9735 var getChromeDivTemplate = function()
9737 return FirebugChrome.Skin.HTML;
9740 var getChromeTemplate = function(isPopup)
9742 var tpl = FirebugChrome.Skin;
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;
9750 r[++i] = '</title><link href="';
9751 r[++i] = Env.Location.skinDir + 'firebug.css';
9752 r[++i] = '" rel="stylesheet" type="text/css" />';
9755 r[++i] = '</title><style>html,body{margin:0;padding:0;overflow:hidden;}';
9757 r[++i] = '</style>';
9760 r[++i] = '</head><body class="fbBody' + (isPopup ? ' FirebugPopup' : '') + '">';
9762 r[++i] = '</body></html>';
9768 // ************************************************************************************************
9772 var Chrome = function Chrome(chrome)
9774 var type = chrome.type;
9775 var Base = type == "frame" || type == "div" ? ChromeFrameBase : ChromePopupBase;
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
9781 FirebugChrome.chromeMap[type] = this;
9782 Firebug.chrome = this;
9783 Env.chrome = chrome.window;
9785 this.commandLineVisible = false;
9786 this.sidePanelVisible = false;
9793 // ************************************************************************************************
9798 * @extends FBL.Controller
9799 * @extends FBL.PanelBar
9801 var ChromeBase = {};
9802 append(ChromeBase, Controller);
9803 append(ChromeBase, PanelBar);
9805 /**@extend ns-chrome-ChromeBase*/
9807 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9808 // inherited properties
9810 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9811 // inherited from createChrome function
9816 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9817 // inherited from Context.prototype
9822 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9825 sidePanelVisible: false,
9826 commandLineVisible: false,
9827 largeCommandLineVisible: false,
9829 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9830 // object properties
9832 inspectButton: null,
9834 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9838 PanelBar.create.call(this);
9840 if (Firebug.Inspector)
9841 this.inspectButton = new Button({
9843 element: $("fbChrome_btInspect"),
9844 owner: Firebug.Inspector,
9846 onPress: Firebug.Inspector.startInspecting,
9847 onUnpress: Firebug.Inspector.stopInspecting
9853 if(Firebug.Inspector)
9854 this.inspectButton.destroy();
9856 PanelBar.destroy.call(this);
9861 testMenu: function()
9863 var firebugMenu = new Menu(
9865 id: "fbFirebugMenu",
9870 label: "Open Firebug",
9872 key: isFirefox ? "Shift+F12" : "F12",
9874 command: "toggleChrome"
9877 label: "Open Firebug in New Window",
9879 key: isFirefox ? "Ctrl+Shift+F12" : "Ctrl+F12",
9880 command: "openPopup"
9883 label: "Inspect Element",
9885 key: "Ctrl+Shift+C",
9886 command: "toggleInspect"
9889 label: "Command Line",
9891 key: "Ctrl+Shift+L",
9892 command: "focusCommandLine"
9898 child: "fbFirebugOptionsMenu"
9902 label: "Firebug Lite Website...",
9903 command: "visitWebsite"
9906 label: "Discussion Group...",
9907 command: "visitDiscussionGroup"
9910 label: "Issue Tracker...",
9911 command: "visitIssueTracker"
9917 iconButton.restore();
9920 toggleChrome: function()
9922 Firebug.chrome.toggle();
9925 openPopup: function()
9927 Firebug.chrome.toggle(true, true);
9930 toggleInspect: function()
9932 Firebug.Inspector.toggleInspect();
9935 focusCommandLine: function()
9937 Firebug.chrome.focusCommandLine();
9940 visitWebsite: function()
9942 this.visit("http://getfirebug.com/lite.html");
9945 visitDiscussionGroup: function()
9947 this.visit("http://groups.google.com/group/firebug");
9950 visitIssueTracker: function()
9952 this.visit("http://code.google.com/p/fbug/issues/list");
9955 visit: function(url)
9963 var firebugOptionsMenu =
9965 id: "fbFirebugOptionsMenu",
9967 getItems: function()
9969 var cookiesDisabled = !Firebug.saveCookies;
9973 label: "Start Opened",
9975 value: "startOpened",
9976 checked: Firebug.startOpened,
9977 disabled: cookiesDisabled
9980 label: "Start in New Window",
9982 value: "startInNewWindow",
9983 checked: Firebug.startInNewWindow,
9984 disabled: cookiesDisabled
9987 label: "Show Icon When Hidden",
9989 value: "showIconWhenHidden",
9990 checked: Firebug.showIconWhenHidden,
9991 disabled: cookiesDisabled
9994 label: "Override Console Object",
9996 value: "overrideConsole",
9997 checked: Firebug.overrideConsole,
9998 disabled: cookiesDisabled
10001 label: "Ignore Firebug Elements",
10003 value: "ignoreFirebugElements",
10004 checked: Firebug.ignoreFirebugElements,
10005 disabled: cookiesDisabled
10008 label: "Disable When Firebug Active",
10010 value: "disableWhenFirebugActive",
10011 checked: Firebug.disableWhenFirebugActive,
10012 disabled: cookiesDisabled
10015 label: "Disable XHR Listener",
10017 value: "disableXHRListener",
10018 checked: Firebug.disableXHRListener,
10019 disabled: cookiesDisabled
10022 label: "Disable Resource Fetching",
10024 value: "disableResourceFetching",
10025 checked: Firebug.disableResourceFetching,
10026 disabled: cookiesDisabled
10029 label: "Enable Trace Mode",
10031 value: "enableTrace",
10032 checked: Firebug.enableTrace,
10033 disabled: cookiesDisabled
10036 label: "Enable Persistent Mode (experimental)",
10038 value: "enablePersistent",
10039 checked: Firebug.enablePersistent,
10040 disabled: cookiesDisabled
10044 label: "Reset All Firebug Options",
10045 command: "restorePrefs",
10046 disabled: cookiesDisabled
10051 onCheck: function(target, value, checked)
10053 Firebug.setPref(value, checked);
10056 restorePrefs: function(target)
10058 Firebug.erasePrefs();
10061 this.updateMenu(target);
10064 updateMenu: function(target)
10066 var options = getElementsByClass(target.parentNode, "fbMenuOption");
10068 var firstOption = options[0];
10069 var enabled = Firebug.saveCookies;
10071 Menu.check(firstOption);
10073 Menu.uncheck(firstOption);
10076 Menu.check(options[0]);
10078 Menu.uncheck(options[0]);
10080 for (var i = 1, length = options.length; i < length; i++)
10082 var option = options[i];
10084 var value = option.getAttribute("value");
10085 var pref = Firebug[value];
10088 Menu.check(option);
10090 Menu.uncheck(option);
10093 Menu.enable(option);
10095 Menu.disable(option);
10100 Menu.register(firebugOptionsMenu);
10102 var menu = firebugMenu;
10104 var testMenuClick = function(event)
10106 //console.log("testMenuClick");
10107 cancelEvent(event, true);
10109 var target = event.target || event.srcElement;
10111 if (menu.isVisible)
10115 var offsetLeft = isIE6 ? 1 : -4, // IE6 problem with fixed position
10117 chrome = Firebug.chrome,
10119 box = chrome.getElementBox(target),
10121 offset = chrome.type == "div" ?
10122 chrome.getElementPosition(chrome.node) :
10126 box.left + offsetLeft - offset.left,
10127 box.top + box.height -5 - offset.top
10134 var iconButton = new IconButton({
10136 element: $("fbFirebugButton"),
10138 onClick: testMenuClick
10141 iconButton.initialize();
10143 //addEvent($("fbToolbarIcon"), "click", testMenuClick);
10146 initialize: function()
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");
10155 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10156 if (Firebug.Console)
10157 Firebug.Console.flush();
10160 FBTrace.flush(Firebug.Trace);
10162 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10163 if (FBTrace.DBG_INITIALIZE) FBTrace.sysout("Firebug.chrome.initialize", "initializing chrome application");
10165 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10166 // initialize inherited classes
10167 Controller.initialize.call(this);
10168 PanelBar.initialize.call(this);
10170 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10171 // create the interface elements cache
10173 fbTop = $("fbTop");
10174 fbContent = $("fbContent");
10175 fbContentStyle = fbContent.style;
10176 fbBottom = $("fbBottom");
10177 fbBtnInspect = $("fbBtnInspect");
10179 fbToolbar = $("fbToolbar");
10181 fbPanelBox1 = $("fbPanelBox1");
10182 fbPanelBox1Style = fbPanelBox1.style;
10183 fbPanelBox2 = $("fbPanelBox2");
10184 fbPanelBox2Style = fbPanelBox2.style;
10185 fbPanelBar2Box = $("fbPanelBar2Box");
10186 fbPanelBar2BoxStyle = fbPanelBar2Box.style;
10188 fbHSplitter = $("fbHSplitter");
10189 fbVSplitter = $("fbVSplitter");
10190 fbVSplitterStyle = fbVSplitter.style;
10192 fbPanel1 = $("fbPanel1");
10193 fbPanel1Style = fbPanel1.style;
10194 fbPanel2 = $("fbPanel2");
10195 fbPanel2Style = fbPanel2.style;
10197 fbConsole = $("fbConsole");
10198 fbConsoleStyle = fbConsole.style;
10199 fbHTML = $("fbHTML");
10201 fbCommandLine = $("fbCommandLine");
10202 fbLargeCommandLine = $("fbLargeCommandLine");
10203 fbLargeCommandButtons = $("fbLargeCommandButtons");
10205 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10206 // static values cache
10207 topHeight = fbTop.offsetHeight;
10208 topPartialHeight = fbToolbar.offsetHeight;
10210 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10212 disableTextSelection($("fbToolbar"));
10213 disableTextSelection($("fbPanelBarBox"));
10214 disableTextSelection($("fbPanelBar1"));
10215 disableTextSelection($("fbPanelBar2"));
10217 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10218 // Add the "javascript:void(0)" href attributes used to make the hover effect in IE6
10219 if (isIE6 && Firebug.Selector)
10221 // TODO: xxxpedro change to getElementsByClass
10222 var as = $$(".fbHover");
10223 for (var i=0, a; a=as[i]; i++)
10225 a.setAttribute("href", "javascript:void(0)");
10229 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10230 // initialize all panels
10232 var panelMap = Firebug.panelTypes;
10233 for (var i=0, p; p=panelMap[i]; i++)
10235 if (!p.parentPanel)
10237 this.addPanel(p.prototype.name);
10242 // ************************************************************************************************
10243 // ************************************************************************************************
10244 // ************************************************************************************************
10245 // ************************************************************************************************
10247 if(Firebug.Inspector)
10248 this.inspectButton.initialize();
10250 // ************************************************************************************************
10251 // ************************************************************************************************
10252 // ************************************************************************************************
10253 // ************************************************************************************************
10255 this.addController(
10256 [$("fbLargeCommandLineIcon"), "click", this.showLargeCommandLine]
10259 // ************************************************************************************************
10261 // Select the first registered panel
10264 setTimeout(function(){
10265 self.selectPanel(Firebug.context.persistedState.selectedPanelName);
10267 if (Firebug.context.persistedState.selectedPanelName == "Console" && Firebug.CommandLine)
10268 Firebug.chrome.focusCommandLine();
10271 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10281 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10282 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10283 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10284 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10285 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10286 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10288 var onPanelMouseDown = function onPanelMouseDown(event)
10290 //console.log("onPanelMouseDown", event.target || event.srcElement, event);
10292 var target = event.target || event.srcElement;
10294 if (FBL.isLeftClick(event))
10296 var editable = FBL.getAncestorByClass(target, "editable");
10298 // if an editable element has been clicked then start editing
10301 Firebug.Editor.startEditing(editable);
10302 FBL.cancelEvent(event);
10304 // if any other element has been clicked then stop editing
10307 if (!hasClass(target, "textEditorInner"))
10308 Firebug.Editor.stopEditing();
10311 else if (FBL.isMiddleClick(event) && Firebug.getRepNode(target))
10313 // Prevent auto-scroll when middle-clicking a rep object
10314 FBL.cancelEvent(event);
10318 Firebug.getElementPanel = function(element)
10320 var panelNode = getAncestorByClass(element, "fbPanel");
10321 var id = panelNode.id.substr(2);
10323 var panel = Firebug.chrome.panelMap[id];
10327 if (Firebug.chrome.selectedPanel.sidePanelBar)
10328 panel = Firebug.chrome.selectedPanel.sidePanelBar.panelMap[id];
10336 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10338 // TODO: xxxpedro port to Firebug
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)
10346 for (var keyCode in onKeyCodeListenersMap)
10348 var listeners = onKeyCodeListenersMap[keyCode];
10350 for (var i = 0, listener; listener = listeners[i]; i++)
10352 var filter = listener.filter || FBL.noKeyModifiers;
10354 if (event.keyCode == keyCode && (!filter || filter(event)))
10356 listener.listener();
10357 FBL.cancelEvent(event, true);
10364 addEvent(Firebug.chrome.document, "keydown", onKeyCodeListen);
10367 * @name keyCodeListen
10368 * @memberOf FBL.FirebugChrome
10370 Firebug.chrome.keyCodeListen = function(key, filter, listener, capture)
10372 var keyCode = KeyEvent["DOM_VK_"+key];
10374 if (!onKeyCodeListenersMap[keyCode])
10375 onKeyCodeListenersMap[keyCode] = [];
10377 onKeyCodeListenersMap[keyCode].push({
10387 * @memberOf FBL.FirebugChrome
10389 Firebug.chrome.keyIgnore = function(keyCode)
10391 onKeyCodeListenersMap[keyCode] = null;
10392 delete onKeyCodeListenersMap[keyCode];
10395 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10398 // move to shutdown
10399 //removeEvent(Firebug.chrome.document, "keydown", listener[0]);
10403 Firebug.chrome.keyCodeListen = function(key, filter, listener, capture)
10406 filter = FBL.noKeyModifiers;
10408 var keyCode = KeyEvent["DOM_VK_"+key];
10410 var fn = function fn(event)
10412 if (event.keyCode == keyCode && (!filter || filter(event)))
10415 FBL.cancelEvent(event, true);
10420 addEvent(Firebug.chrome.document, "keydown", fn);
10422 return [fn, capture];
10425 Firebug.chrome.keyIgnore = function(listener)
10427 removeEvent(Firebug.chrome.document, "keydown", listener[0]);
10432 this.addController(
10433 [fbPanel1, "mousedown", onPanelMouseDown],
10434 [fbPanel2, "mousedown", onPanelMouseDown]
10437 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10438 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10439 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10440 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10441 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10442 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10445 // menus can be used without domplate
10452 setTimeout(function(){
10454 FBL.Ajax.request({url: "../content/firebug/boot.js"});
10455 FBL.Ajax.request({url: "../content/firebug/boot.js.invalid"});
10461 shutdown: function()
10463 // ************************************************************************************************
10464 // ************************************************************************************************
10465 // ************************************************************************************************
10466 // ************************************************************************************************
10468 if(Firebug.Inspector)
10469 this.inspectButton.shutdown();
10471 // ************************************************************************************************
10472 // ************************************************************************************************
10473 // ************************************************************************************************
10474 // ************************************************************************************************
10476 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10478 // remove disableTextSelection event handlers
10479 restoreTextSelection($("fbToolbar"));
10480 restoreTextSelection($("fbPanelBarBox"));
10481 restoreTextSelection($("fbPanelBar1"));
10482 restoreTextSelection($("fbPanelBar2"));
10484 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10485 // shutdown inherited classes
10486 Controller.shutdown.call(this);
10487 PanelBar.shutdown.call(this);
10489 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10490 // Remove the interface elements cache (this must happen after calling
10491 // the shutdown method of all dependent components to avoid errors)
10495 fbContentStyle = null;
10497 fbBtnInspect = null;
10501 fbPanelBox1 = null;
10502 fbPanelBox1Style = null;
10503 fbPanelBox2 = null;
10504 fbPanelBox2Style = null;
10505 fbPanelBar2Box = null;
10506 fbPanelBar2BoxStyle = null;
10508 fbHSplitter = null;
10509 fbVSplitter = null;
10510 fbVSplitterStyle = null;
10513 fbPanel1Style = null;
10517 fbConsoleStyle = null;
10520 fbCommandLine = null;
10521 fbLargeCommandLine = null;
10522 fbLargeCommandButtons = null;
10524 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10525 // static values cache
10528 topPartialHeight = null;
10531 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10533 toggle: function(forceOpen, popup)
10541 if (isOpera && Firebug.chrome.type == "popup" && Firebug.chrome.node.closed)
10543 var frame = FirebugChrome.chromeMap.frame;
10546 FirebugChrome.chromeMap.popup = null;
10553 // If the context is a popup, ignores the toggle process
10554 if (Firebug.chrome.type == "popup") return;
10556 var shouldOpen = forceOpen || !Firebug.context.persistedState.isOpen;
10565 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10569 if(!FirebugChrome.chromeMap.popup)
10572 createChromeWindow({type: "popup"});
10576 reattach: function(oldChrome, newChrome)
10578 Firebug.browser.window.Firebug = Firebug;
10580 // chrome synchronization
10581 var newPanelMap = newChrome.panelMap;
10582 var oldPanelMap = oldChrome.panelMap;
10585 for(var name in newPanelMap)
10587 // TODO: xxxpedro innerHTML
10588 panel = newPanelMap[name];
10589 if (panel.options.innerHTMLSync)
10590 panel.panelNode.innerHTML = oldPanelMap[name].panelNode.innerHTML;
10593 Firebug.chrome = newChrome;
10595 // TODO: xxxpedro sync detach reattach attach
10596 //dispatch(Firebug.chrome.panelMap, "detach", [oldChrome, newChrome]);
10598 if (newChrome.type == "popup")
10600 newChrome.initialize();
10601 //dispatch(Firebug.modules, "initialize", []);
10605 // TODO: xxxpedro only needed in persistent
10606 // should use FirebugChrome.clone, but popup FBChrome
10608 Firebug.context.persistedState.selectedPanelName = oldChrome.selectedPanel.name;
10611 dispatch(newPanelMap, "reattach", [oldChrome, newChrome]);
10614 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10618 var size = this.getSize();
10620 // Height related values
10621 var commandLineHeight = Firebug.chrome.commandLineVisible ? fbCommandLine.offsetHeight : 0,
10623 y = Math.max(size.height /* chrome height */, topHeight),
10625 heightValue = Math.max(y - topHeight - commandLineHeight /* fixed height */, 0),
10627 height = heightValue + "px",
10629 // Width related values
10630 sideWidthValue = Firebug.chrome.sidePanelVisible ? Firebug.context.persistedState.sidePanelWidth : 0,
10632 width = Math.max(size.width /* chrome width */ - sideWidthValue, 0) + "px";
10634 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10635 // Height related rendering
10636 fbPanelBox1Style.height = height;
10637 fbPanel1Style.height = height;
10639 if (isIE || isOpera)
10641 // Fix IE and Opera problems with auto resizing the verticall splitter
10642 fbVSplitterStyle.height = Math.max(y - topPartialHeight - commandLineHeight, 0) + "px";
10644 //xxxpedro FF2 only?
10646 else if (isFirefox)
10648 // Fix Firefox problem with table rows with 100% height (fit height)
10649 fbContentStyle.maxHeight = Math.max(y - fixedHeight, 0)+ "px";
10652 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10653 // Width related rendering
10654 fbPanelBox1Style.width = width;
10655 fbPanel1Style.width = width;
10657 // SidePanel rendering
10658 if (Firebug.chrome.sidePanelVisible)
10660 sideWidthValue = Math.max(sideWidthValue - 6, 0);
10662 var sideWidth = sideWidthValue + "px";
10664 fbPanelBox2Style.width = sideWidth;
10666 fbVSplitterStyle.right = sideWidth;
10668 if (Firebug.chrome.largeCommandLineVisible)
10670 fbLargeCommandLine = $("fbLargeCommandLine");
10672 fbLargeCommandLine.style.height = heightValue - 4 + "px";
10673 fbLargeCommandLine.style.width = sideWidthValue - 2 + "px";
10675 fbLargeCommandButtons = $("fbLargeCommandButtons");
10676 fbLargeCommandButtons.style.width = sideWidth;
10680 fbPanel2Style.height = height;
10681 fbPanel2Style.width = sideWidth;
10683 fbPanelBar2BoxStyle.width = sideWidth;
10688 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10690 getSize: function()
10692 return this.type == "div" ?
10694 height: this.node.offsetHeight,
10695 width: this.node.offsetWidth
10698 this.getWindowSize();
10705 // avoid partial resize when maximizing window
10706 setTimeout(function(){
10709 if (noFixedPosition && (self.type == "frame" || self.type == "div"))
10710 self.fixIEPosition();
10714 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10716 layout: function(panel)
10718 if (FBTrace.DBG_CHROME) FBTrace.sysout("Chrome.layout", "");
10720 var options = panel.options;
10722 changeCommandLineVisibility(options.hasCommandLine);
10723 changeSidePanelVisibility(panel.hasSidePanel);
10725 Firebug.chrome.draw();
10728 showLargeCommandLine: function(hideToggleIcon)
10730 var chrome = Firebug.chrome;
10732 if (!chrome.largeCommandLineVisible)
10734 chrome.largeCommandLineVisible = true;
10736 if (chrome.selectedPanel.options.hasCommandLine)
10738 if (Firebug.CommandLine)
10739 Firebug.CommandLine.blur();
10741 changeCommandLineVisibility(false);
10744 changeSidePanelVisibility(true);
10746 fbLargeCommandLine.style.display = "block";
10747 fbLargeCommandButtons.style.display = "block";
10749 fbPanel2Style.display = "none";
10750 fbPanelBar2BoxStyle.display = "none";
10754 fbLargeCommandLine.focus();
10756 if (Firebug.CommandLine)
10757 Firebug.CommandLine.setMultiLine(true);
10761 hideLargeCommandLine: function()
10763 if (Firebug.chrome.largeCommandLineVisible)
10765 Firebug.chrome.largeCommandLineVisible = false;
10767 if (Firebug.CommandLine)
10768 Firebug.CommandLine.setMultiLine(false);
10770 fbLargeCommandLine.blur();
10772 fbPanel2Style.display = "block";
10773 fbPanelBar2BoxStyle.display = "block";
10775 fbLargeCommandLine.style.display = "none";
10776 fbLargeCommandButtons.style.display = "none";
10778 changeSidePanelVisibility(false);
10780 if (Firebug.chrome.selectedPanel.options.hasCommandLine)
10781 changeCommandLineVisibility(true);
10783 Firebug.chrome.draw();
10788 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10790 focusCommandLine: function()
10792 var selectedPanelName = this.selectedPanel.name, panelToSelect;
10794 if (focusCommandLineState == 0 || selectedPanelName != "Console")
10796 focusCommandLineState = 0;
10797 lastFocusedPanelName = selectedPanelName;
10799 panelToSelect = "Console";
10801 if (focusCommandLineState == 1)
10803 panelToSelect = lastFocusedPanelName;
10806 this.selectPanel(panelToSelect);
10810 if (Firebug.CommandLine)
10812 if (panelToSelect == "Console")
10813 Firebug.CommandLine.focus();
10815 Firebug.CommandLine.blur();
10820 //TODO: xxxpedro trace error
10823 focusCommandLineState = ++focusCommandLineState % 2;
10828 // ************************************************************************************************
10833 * @extends ns-chrome-ChromeBase
10835 var ChromeFrameBase = extend(ChromeBase,
10836 /**@extend ns-chrome-ChromeFrameBase*/
10840 ChromeBase.create.call(this);
10842 // restore display for the anti-flicker trick
10844 this.node.style.display = "block";
10846 if (Env.Options.startInNewWindow)
10849 this.toggle(true, true);
10853 if (Env.Options.startOpened)
10859 destroy: function()
10861 var size = Firebug.chrome.getWindowSize();
10863 Firebug.context.persistedState.height = size.height;
10865 if (Firebug.saveCookies)
10866 Firebug.savePrefs();
10868 removeGlobalEvent("keydown", onGlobalKeyDown);
10870 ChromeBase.destroy.call(this);
10872 this.document = null;
10873 delete this.document;
10875 this.window = null;
10876 delete this.window;
10878 this.node.parentNode.removeChild(this.node);
10883 initialize: function()
10885 //FBTrace.sysout("Frame", "initialize();")
10886 ChromeBase.initialize.call(this);
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]
10895 if (!Env.Options.enablePersistent)
10896 this.addController([Firebug.browser.window, "unload", Firebug.shutdown]);
10898 if (noFixedPosition)
10900 this.addController(
10901 [Firebug.browser.window, "scroll", this.fixIEPosition]
10905 fbVSplitter.onmousedown = onVSplitterMouseDown;
10906 fbHSplitter.onmousedown = onHSplitterMouseDown;
10908 this.isInitialized = true;
10911 shutdown: function()
10913 fbVSplitter.onmousedown = null;
10914 fbHSplitter.onmousedown = null;
10916 ChromeBase.shutdown.apply(this);
10918 this.isInitialized = false;
10921 reattach: function()
10923 var frame = FirebugChrome.chromeMap.frame;
10925 ChromeBase.reattach(FirebugChrome.chromeMap.popup, this);
10930 if (!Firebug.context.persistedState.isOpen)
10932 Firebug.context.persistedState.isOpen = true;
10934 if (Env.isChromeExtension)
10935 localStorage.setItem("Firebug", "1,1");
10937 var node = this.node;
10939 node.style.visibility = "hidden"; // Avoid flickering
10941 if (Firebug.showIconWhenHidden)
10943 if (ChromeMini.isInitialized)
10945 ChromeMini.shutdown();
10950 node.style.display = "block";
10952 var main = $("fbChrome");
10954 // IE6 throws an error when setting this property! why?
10955 //main.style.display = "table";
10956 main.style.display = "";
10959 /// TODO: xxxpedro FOUC
10960 node.style.visibility = "visible";
10961 setTimeout(function(){
10962 ///node.style.visibility = "visible";
10964 //dispatch(Firebug.modules, "initialize", []);
10967 if (noFixedPosition)
10968 self.fixIEPosition();
10978 if (Firebug.context.persistedState.isOpen)
10980 if (this.isInitialized)
10982 //dispatch(Firebug.modules, "shutdown", []);
10986 Firebug.context.persistedState.isOpen = false;
10988 if (Env.isChromeExtension)
10989 localStorage.setItem("Firebug", "1,0");
10991 var node = this.node;
10993 if (Firebug.showIconWhenHidden)
10995 node.style.visibility = "hidden"; // Avoid flickering
10997 // TODO: xxxpedro - persist IE fixed?
10998 var main = $("fbChrome", FirebugChrome.chromeMap.frame.document);
10999 main.style.display = "none";
11001 ChromeMini.initialize();
11003 node.style.visibility = "visible";
11006 node.style.display = "none";
11010 deactivate: function()
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)
11016 localStorage.removeItem("Firebug");
11017 Firebug.GoogleChrome.dispatch("FB_deactivate");
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();
11026 Firebug.shutdown();
11030 fixIEPosition: function()
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;
11036 var size = Firebug.browser.getWindowSize();
11037 var scroll = Firebug.browser.getWindowScrollPosition();
11038 var maxHeight = size.height;
11039 var height = this.node.offsetHeight;
11041 var bodyStyle = doc.body.currentStyle;
11043 this.node.style.top = maxHeight - height + scroll.top + "px";
11045 if ((this.type == "frame" || this.type == "div") &&
11046 (bodyStyle.marginLeft || bodyStyle.marginRight))
11048 this.node.style.width = size.width + "px";
11051 if (fbVSplitterStyle)
11052 fbVSplitterStyle.right = Firebug.context.persistedState.sidePanelWidth + "px";
11060 // ************************************************************************************************
11065 * @extends FBL.Controller
11067 var ChromeMini = extend(Controller,
11068 /**@extend ns-chrome-ChromeMini*/
11070 create: function(chrome)
11072 append(this, chrome);
11073 this.type = "mini";
11076 initialize: function()
11078 Controller.initialize.apply(this);
11080 var doc = FirebugChrome.chromeMap.frame.document;
11082 var mini = $("fbMiniChrome", doc);
11083 mini.style.display = "block";
11085 var miniIcon = $("fbMiniIcon", doc);
11086 var width = miniIcon.offsetWidth + 10;
11087 miniIcon.title = "Open " + Firebug.version;
11089 var errors = $("fbMiniErrors", doc);
11090 if (errors.offsetWidth)
11091 width += errors.offsetWidth + 10;
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;
11099 if (this.node.nodeName.toLowerCase() == "iframe")
11101 node.setAttribute("allowTransparency", "true");
11102 this.document.body.style.backgroundColor = "transparent";
11105 node.style.background = "transparent";
11107 if (noFixedPosition)
11108 this.fixIEPosition();
11110 this.addController(
11111 [$("fbMiniIcon", doc), "click", onMiniIconClick]
11114 if (noFixedPosition)
11116 this.addController(
11117 [Firebug.browser.window, "scroll", this.fixIEPosition]
11121 this.isInitialized = true;
11124 shutdown: function()
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 = "";
11132 if (this.node.nodeName.toLowerCase() == "iframe")
11134 node.setAttribute("allowTransparency", "false");
11135 this.document.body.style.backgroundColor = "#fff";
11138 node.style.background = "#fff";
11140 if (noFixedPosition)
11141 this.fixIEPosition();
11143 var doc = FirebugChrome.chromeMap.frame.document;
11145 var mini = $("fbMiniChrome", doc);
11146 mini.style.display = "none";
11148 Controller.shutdown.apply(this);
11150 this.isInitialized = false;
11158 fixIEPosition: ChromeFrameBase.fixIEPosition
11163 // ************************************************************************************************
11168 * @extends ns-chrome-ChromeBase
11170 var ChromePopupBase = extend(ChromeBase,
11171 /**@extend ns-chrome-ChromePopupBase*/
11174 initialize: function()
11176 setClass(this.document.body, "FirebugPopup");
11178 ChromeBase.initialize.call(this);
11180 this.addController(
11181 [Firebug.chrome.window, "resize", this.resize],
11182 [Firebug.chrome.window, "unload", this.destroy]
11183 //[Firebug.chrome.window, "beforeunload", this.destroy]
11186 if (Env.Options.enablePersistent)
11188 this.persist = bind(this.persist, this);
11189 addEvent(Firebug.browser.window, "unload", this.persist);
11192 this.addController(
11193 [Firebug.browser.window, "unload", this.close]
11196 fbVSplitter.onmousedown = onVSplitterMouseDown;
11199 destroy: function()
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();
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;
11211 if (Firebug.saveCookies)
11212 Firebug.savePrefs();
11214 // TODO: xxxpedro sync detach reattach attach
11215 var frame = FirebugChrome.chromeMap.frame;
11219 dispatch(frame.panelMap, "detach", [this, frame]);
11221 frame.reattach(this, frame);
11224 if (Env.Options.enablePersistent)
11226 removeEvent(Firebug.browser.window, "unload", this.persist);
11229 ChromeBase.destroy.apply(this);
11231 FirebugChrome.chromeMap.popup = null;
11236 persist: function()
11238 persistTimeStart = new Date().getTime();
11240 removeEvent(Firebug.browser.window, "unload", this.persist);
11242 Firebug.Inspector.destroy();
11243 Firebug.browser.window.FirebugOldBrowser = true;
11245 var persistTimeStart = new Date().getTime();
11247 var waitMainWindow = function()
11253 if (window.opener && !window.opener.FirebugOldBrowser && (doc = window.opener.document)/* &&
11254 doc.documentElement && (head = doc.documentElement.firstChild)*/)
11259 // exposes the FBL to the global namespace when in debug mode
11260 if (Env.isDebugMode)
11265 window.Firebug = Firebug;
11266 window.opener.Firebug = Firebug;
11268 Env.browser = window.opener;
11269 Firebug.browser = Firebug.context = new Context(Env.browser);
11270 Firebug.loadPrefs();
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;
11279 var chrome = Firebug.chrome;
11280 addEvent(Firebug.browser.window, "unload", chrome.persist);
11282 FBL.cacheDocument();
11283 Firebug.Inspector.create();
11285 Firebug.Console.logFormatted(
11286 ["Firebug could not capture console calls during " +
11287 persistDelay + "ms"],
11292 setTimeout(function(){
11293 var htmlPanel = chrome.getPanel("HTML");
11294 htmlPanel.createUI();
11300 alert("persist error: " + (pE.message || pE));
11306 window.setTimeout(waitMainWindow, 0);
11325 //************************************************************************************************
11328 var changeCommandLineVisibility = function changeCommandLineVisibility(visibility)
11330 var last = Firebug.chrome.commandLineVisible;
11331 var visible = Firebug.chrome.commandLineVisible =
11332 typeof visibility == "boolean" ? visibility : !Firebug.chrome.commandLineVisible;
11334 if (visible != last)
11338 fbBottom.className = "";
11340 if (Firebug.CommandLine)
11341 Firebug.CommandLine.activate();
11345 if (Firebug.CommandLine)
11346 Firebug.CommandLine.deactivate();
11348 fbBottom.className = "hide";
11353 var changeSidePanelVisibility = function changeSidePanelVisibility(visibility)
11355 var last = Firebug.chrome.sidePanelVisible;
11356 Firebug.chrome.sidePanelVisible =
11357 typeof visibility == "boolean" ? visibility : !Firebug.chrome.sidePanelVisible;
11359 if (Firebug.chrome.sidePanelVisible != last)
11361 fbPanelBox2.className = Firebug.chrome.sidePanelVisible ? "" : "hide";
11362 fbPanelBar2Box.className = Firebug.chrome.sidePanelVisible ? "" : "hide";
11367 // ************************************************************************************************
11370 var onGlobalKeyDown = function onGlobalKeyDown(event)
11372 var keyCode = event.keyCode;
11373 var shiftKey = event.shiftKey;
11374 var ctrlKey = event.ctrlKey;
11376 if (keyCode == 123 /* F12 */ && (!isFirefox && !shiftKey || shiftKey && isFirefox))
11378 Firebug.chrome.toggle(false, ctrlKey);
11379 cancelEvent(event, true);
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)
11385 Firebug.GoogleChrome.dispatch("FB_enableIcon");
11388 else if (keyCode == 67 /* C */ && ctrlKey && shiftKey)
11390 Firebug.Inspector.toggleInspect();
11391 cancelEvent(event, true);
11393 else if (keyCode == 76 /* L */ && ctrlKey && shiftKey)
11395 Firebug.chrome.focusCommandLine();
11396 cancelEvent(event, true);
11400 var onMiniIconClick = function onMiniIconClick(event)
11402 Firebug.chrome.toggle(false, event.ctrlKey);
11403 cancelEvent(event, true);
11407 // ************************************************************************************************
11408 // Horizontal Splitter Handling
11410 var onHSplitterMouseDown = function onHSplitterMouseDown(event)
11412 addGlobalEvent("mousemove", onHSplitterMouseMove);
11413 addGlobalEvent("mouseup", onHSplitterMouseUp);
11416 addEvent(Firebug.browser.document.documentElement, "mouseleave", onHSplitterMouseUp);
11418 fbHSplitter.className = "fbOnMovingHSplitter";
11423 var onHSplitterMouseMove = function onHSplitterMouseMove(event)
11425 cancelEvent(event, true);
11427 var clientY = event.clientY;
11429 ? event.srcElement.ownerDocument.parentWindow
11430 : event.target.defaultView || event.target.ownerDocument && event.target.ownerDocument.defaultView;
11435 if (win != win.parent)
11437 var frameElement = win.frameElement;
11440 var framePos = Firebug.browser.getElementPosition(frameElement).top;
11441 clientY += framePos;
11443 if (frameElement.style.position != "fixed")
11444 clientY -= Firebug.browser.getWindowScrollPosition().top;
11448 if (isOpera && isQuiksMode && win.frameElement.id == "FirebugUI")
11450 clientY = Firebug.browser.getWindowSize().height - win.frameElement.offsetHeight + clientY;
11455 typeof win.FBL != "undefined" ? "no-Chrome" : "Chrome",
11456 //win.frameElement.id,
11461 onHSplitterMouseMoveBuffer = clientY; // buffer
11463 if (new Date().getTime() - lastHSplitterMouseMove > chromeRedrawSkipRate) // frame skipping
11465 lastHSplitterMouseMove = new Date().getTime();
11466 handleHSplitterMouseMove();
11469 if (!onHSplitterMouseMoveTimer)
11470 onHSplitterMouseMoveTimer = setTimeout(handleHSplitterMouseMove, chromeRedrawSkipRate);
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);
11479 var handleHSplitterMouseMove = function()
11481 if (onHSplitterMouseMoveTimer)
11483 clearTimeout(onHSplitterMouseMoveTimer);
11484 onHSplitterMouseMoveTimer = null;
11487 var clientY = onHSplitterMouseMoveBuffer;
11489 var windowSize = Firebug.browser.getWindowSize();
11490 var scrollSize = Firebug.browser.getWindowScrollSize();
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;
11497 var scrollbarSize = !isIE && (scrollSize.width > windowSize.width) ? 17 : 0;
11499 //var height = !isOpera ? chromeNode.offsetTop + chromeNode.clientHeight : windowSize.height;
11500 var height = windowSize.height;
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);
11506 Firebug.context.persistedState.height = chromeHeight;
11507 chromeNode.style.height = chromeHeight + "px";
11509 if (noFixedPosition)
11510 Firebug.chrome.fixIEPosition();
11512 Firebug.chrome.draw();
11515 var onHSplitterMouseUp = function onHSplitterMouseUp(event)
11517 removeGlobalEvent("mousemove", onHSplitterMouseMove);
11518 removeGlobalEvent("mouseup", onHSplitterMouseUp);
11521 removeEvent(Firebug.browser.document.documentElement, "mouseleave", onHSplitterMouseUp);
11523 fbHSplitter.className = "";
11525 Firebug.chrome.draw();
11527 // avoid text selection in IE when returning to the document
11528 // after the mouse leaves the document during the resizing
11533 // ************************************************************************************************
11534 // Vertical Splitter Handling
11536 var onVSplitterMouseDown = function onVSplitterMouseDown(event)
11538 addGlobalEvent("mousemove", onVSplitterMouseMove);
11539 addGlobalEvent("mouseup", onVSplitterMouseUp);
11544 var onVSplitterMouseMove = function onVSplitterMouseMove(event)
11546 if (new Date().getTime() - lastVSplitterMouseMove > chromeRedrawSkipRate) // frame skipping
11548 var target = event.target || event.srcElement;
11549 if (target && target.ownerDocument) // avoid error when cursor reaches out of the chrome
11551 var clientX = event.clientX;
11552 var win = document.all
11553 ? event.srcElement.ownerDocument.parentWindow
11554 : event.target.ownerDocument.defaultView;
11556 if (win != win.parent)
11557 clientX += win.frameElement ? win.frameElement.offsetLeft : 0;
11559 var size = Firebug.chrome.getSize();
11560 var x = Math.max(size.width - clientX + 3, 6);
11562 Firebug.context.persistedState.sidePanelWidth = x;
11563 Firebug.chrome.draw();
11566 lastVSplitterMouseMove = new Date().getTime();
11569 cancelEvent(event, true);
11573 var onVSplitterMouseUp = function onVSplitterMouseUp(event)
11575 removeGlobalEvent("mousemove", onVSplitterMouseMove);
11576 removeGlobalEvent("mouseup", onVSplitterMouseUp);
11578 Firebug.chrome.draw();
11582 // ************************************************************************************************
11585 /* See license.txt for terms of usage */
11587 FBL.ns(function() { with (FBL) {
11588 // ************************************************************************************************
11594 // ************************************************************************************************
11598 /* See license.txt for terms of usage */
11600 FBL.ns(function() { with (FBL) {
11601 // ************************************************************************************************
11603 Firebug.Lite.Cache =
11605 ID: "firebug-" + new Date().getTime()
11608 // ************************************************************************************************
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
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.
11620 var createCache = function()
11625 var CID = Firebug.Lite.Cache.ID;
11627 // better detection
11628 var supportsDeleteExpando = !document.all;
11630 var cacheFunction = function(element)
11632 return cacheAPI.set(element);
11639 return map.hasOwnProperty(key) ?
11644 set: function(element)
11646 var id = getValidatedKey(element);
11654 if (!map.hasOwnProperty(id))
11663 unset: function(element)
11665 var id = getValidatedKey(element);
11669 if (supportsDeleteExpando)
11671 delete element[CID];
11673 else if (element.removeAttribute)
11675 element.removeAttribute(CID);
11683 key: function(element)
11685 return getValidatedKey(element);
11688 has: function(element)
11690 var id = getValidatedKey(element);
11691 return id && map.hasOwnProperty(id);
11694 each: function(callback)
11696 for (var key in map)
11698 if (map.hasOwnProperty(key))
11700 callback(key, map[key]);
11705 data: function(element, name, value)
11710 if (!name) return null;
11712 var id = cacheAPI.set(element);
11714 return data[id][name] = value;
11719 var id = cacheAPI.key(element);
11721 return data.hasOwnProperty(id) && data[id].hasOwnProperty(name) ?
11729 for (var id in map)
11731 var element = map[id];
11732 cacheAPI.unset(element);
11737 var getValidatedKey = function(element)
11739 var id = element[CID];
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.
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
11752 // remove the problematic property
11753 element.removeAttribute(CID);
11761 FBL.append(cacheFunction, cacheAPI);
11763 return cacheFunction;
11766 // ************************************************************************************************
11768 // TODO: xxxpedro : check if we need really this on FBL scope
11769 Firebug.Lite.Cache.StyleSheet = createCache();
11770 Firebug.Lite.Cache.Element = createCache();
11773 Firebug.Lite.Cache.Event = createCache();
11776 // ************************************************************************************************
11780 /* See license.txt for terms of usage */
11782 FBL.ns(function() { with (FBL) {
11783 // ************************************************************************************************
11785 // ************************************************************************************************
11786 var sourceMap = {};
11788 // ************************************************************************************************
11789 Firebug.Lite.Proxy =
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).
11799 load: function(url)
11801 var resourceDomain = getDomain(url);
11802 var isLocalResource =
11803 // empty domain means local URL
11805 // same domain means local too
11806 resourceDomain == Firebug.context.window.location.host; // TODO: xxxpedro context
11808 return isLocalResource ? fetchResource(url) : fetchProxyResource(url);
11812 * Load a resource using JSONP technique.
11814 loadJSONP: function(url, callback)
11816 var script = createGlobalElement("script"),
11817 doc = Firebug.context.document,
11819 uid = "" + new Date().getTime(),
11820 callbackName = "callback=Firebug.Lite.Proxy._callbacks." + uid,
11822 jsonpURL = url.indexOf("?") != -1 ?
11823 url + "&" + callbackName :
11824 url + "?" + callbackName;
11826 Firebug.Lite.Proxy._callbacks[uid] = function(data)
11831 script.parentNode.removeChild(script);
11832 delete Firebug.Lite.Proxy._callbacks[uid];
11835 script.src = jsonpURL;
11837 if (doc.documentElement)
11838 doc.documentElement.appendChild(script);
11842 * Load a resource using YQL (not reliable).
11844 YQL: function(url, callback)
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";
11849 this.loadJSONP(yql, function(data)
11851 var source = data.results[0];
11853 // clean up YQL bogus elements
11854 var match = /<body>\s+<p>([\s\S]+)<\/p>\s+<\/body>$/.exec(source);
11858 console.log(source);
11863 // ************************************************************************************************
11865 Firebug.Lite.Proxy.fetchResourceDisabledMessage =
11866 "/* Firebug Lite resource fetching is disabled.\n" +
11867 "To enabled it set the Firebug Lite option \"disableResourceFetching\" to \"false\".\n" +
11868 "More info at http://getfirebug.com/firebuglite#Options */";
11870 var fetchResource = function(url)
11872 if (Firebug.disableResourceFetching)
11874 var source = sourceMap[url] = Firebug.Lite.Proxy.fetchResourceDisabledMessage;
11878 if (sourceMap.hasOwnProperty(url))
11879 return sourceMap[url];
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);
11886 var source = sourceMap[url] = xhr.responseText;
11890 var fetchProxyResource = function(url)
11892 if (sourceMap.hasOwnProperty(url))
11893 return sourceMap[url];
11895 var proxyURL = Env.Location.baseDir + "plugin/proxy/proxy.php?url=" + encodeURIComponent(url);
11896 var response = fetchResource(proxyURL);
11900 var data = eval("(" + response + ")");
11904 return "ERROR: Firebug Lite Proxy plugin returned an invalid response.";
11907 var source = data ? data.contents : "";
11912 // ************************************************************************************************
11916 /* See license.txt for terms of usage */
11918 FBL.ns(function() { with (FBL) {
11919 // ************************************************************************************************
11921 Firebug.Lite.Style =
11925 // ************************************************************************************************
11929 /* See license.txt for terms of usage */
11931 FBL.ns(function() { with (FBL) {
11932 // ************************************************************************************************
11934 Firebug.Lite.Script = function(window)
11936 this.fileName = null;
11937 this.isValid = null;
11938 this.baseLineNumber = null;
11939 this.lineExtent = null;
11942 this.functionName = null;
11943 this.functionSource = null;
11946 Firebug.Lite.Script.prototype =
11948 isLineExecutable: function(){},
11949 pcToLine: function(){},
11950 lineToPc: function(){},
11952 toString: function()
11954 return "Firebug.Lite.Script";
11958 // ************************************************************************************************
11962 /* See license.txt for terms of usage */
11964 FBL.ns(function() { with (FBL) {
11965 // ************************************************************************************************
11968 Firebug.Lite.Browser = function(window)
11970 this.contentWindow = window;
11971 this.contentDocument = window.document;
11974 spec: window.location.href
11978 Firebug.Lite.Browser.prototype =
11980 toString: function()
11982 return "Firebug.Lite.Browser";
11987 // ************************************************************************************************
11991 /* See license.txt for terms of usage */
11994 http://www.JSON.org/json2.js
11999 NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
12001 See http://www.JSON.org/js.html
12004 This code should be minified before deployment.
12005 See http://javascript.crockford.com/jsmin.html
12007 USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
12011 This file creates a global JSON object containing two methods: stringify
12014 JSON.stringify(value, replacer, space)
12015 value any JavaScript value, usually an object or array.
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.
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 ' '),
12026 it contains the characters used to indent at each level.
12028 This method produces a JSON text from a JavaScript value.
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
12038 For example, this would serialize Dates as ISO strings.
12040 Date.prototype.toJSON = function (key) {
12042 // Format integers to have at least two digits.
12043 return n < 10 ? '0' + n : n;
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';
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.
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
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.
12071 The optional space parameter produces a stringification of the
12072 value that is filled with line breaks and indentation to make it
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.
12081 text = JSON.stringify(['e', {pluribus: 'unum'}]);
12082 // text is '["e",{"pluribus":"unum"}]'
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]'
12088 text = JSON.stringify([new Date()], function (key, value) {
12089 return this[key] instanceof Date ?
12090 'Date(' + this[key] + ')' : value;
12092 // text is '["Date(---current time---)"]'
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.
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.
12107 // Parse the text. Values that look like ISO date strings will
12108 // be converted to Date objects.
12110 myData = JSON.parse(text, function (key, value) {
12112 if (typeof value === 'string') {
12114 /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
12116 return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
12123 myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
12125 if (typeof value === 'string' &&
12126 value.slice(0, 5) === 'Date(' &&
12127 value.slice(-1) === ')') {
12128 d = new Date(value.slice(5, -1));
12137 This is a reference implementation. You are free to copy, modify, or
12141 /*jslint evil: true, strict: false */
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
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.
12154 // ************************************************************************************************
12156 var JSON = window.JSON || {};
12158 // ************************************************************************************************
12163 // Format integers to have at least two digits.
12164 return n < 10 ? '0' + n : n;
12167 if (typeof Date.prototype.toJSON !== 'function') {
12169 Date.prototype.toJSON = function (key) {
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;
12180 String.prototype.toJSON =
12181 Number.prototype.toJSON =
12182 Boolean.prototype.toJSON = function (key) {
12183 return this.valueOf();
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,
12191 meta = { // table of character substitutions
12203 function quote(string) {
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
12210 escapable.lastIndex = 0;
12211 return escapable.test(string) ?
12212 '"' + string.replace(escapable, function (a) {
12214 return typeof c === 'string' ? c :
12215 '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
12217 '"' + string + '"';
12221 function str(key, holder) {
12223 // Produce a string from holder[key].
12225 var i, // The loop counter.
12226 k, // The member key.
12227 v, // The member value.
12231 value = holder[key];
12233 // If the value has a toJSON method, call it to obtain a replacement value.
12235 if (value && typeof value === 'object' &&
12236 typeof value.toJSON === 'function') {
12237 value = value.toJSON(key);
12240 // If we were called with a replacer function, then call the replacer to
12241 // obtain a replacement value.
12243 if (typeof rep === 'function') {
12244 value = rep.call(holder, key, value);
12247 // What happens next depends on the value's type.
12249 switch (typeof value) {
12251 return quote(value);
12255 // JSON numbers must be finite. Encode non-finite numbers as null.
12257 return isFinite(value) ? String(value) : 'null';
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.
12266 return String(value);
12268 // If the type is 'object', we might be dealing with an object or an array or
12273 // Due to a specification blunder in ECMAScript, typeof null is 'object',
12274 // so watch out for that case.
12280 // Make an array to hold the partial results of stringifying this object value.
12285 // Is the value an array?
12287 if (Object.prototype.toString.apply(value) === '[object Array]') {
12289 // The value is an array. Stringify every element. Use null as a placeholder
12290 // for non-JSON values.
12292 length = value.length;
12293 for (i = 0; i < length; i += 1) {
12294 partial[i] = str(i, value) || 'null';
12297 // Join all of the elements together, separated with commas, and wrap them in
12300 v = partial.length === 0 ? '[]' :
12301 gap ? '[\n' + gap +
12302 partial.join(',\n' + gap) + '\n' +
12304 '[' + partial.join(',') + ']';
12309 // If the replacer is an array, use it to select the members to be stringified.
12311 if (rep && typeof rep === 'object') {
12312 length = rep.length;
12313 for (i = 0; i < length; i += 1) {
12315 if (typeof k === 'string') {
12318 partial.push(quote(k) + (gap ? ': ' : ':') + v);
12324 // Otherwise, iterate through all of the keys in the object.
12327 if (Object.hasOwnProperty.call(value, k)) {
12330 partial.push(quote(k) + (gap ? ': ' : ':') + v);
12336 // Join all of the member texts together, separated with commas,
12337 // and wrap them in braces.
12339 v = partial.length === 0 ? '{}' :
12340 gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
12341 mind + '}' : '{' + partial.join(',') + '}';
12347 // If the JSON object does not yet have a stringify method, give it one.
12349 if (typeof JSON.stringify !== 'function') {
12350 JSON.stringify = function (value, replacer, space) {
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.
12362 // If the space parameter is a number, make an indent string containing that
12365 if (typeof space === 'number') {
12366 for (i = 0; i < space; i += 1) {
12370 // If the space parameter is a string, it will be used as the indent string.
12372 } else if (typeof space === 'string') {
12376 // If there is a replacer, it must be a function or an array.
12377 // Otherwise, throw an error.
12380 if (replacer && typeof replacer !== 'function' &&
12381 (typeof replacer !== 'object' ||
12382 typeof replacer.length !== 'number')) {
12383 throw new Error('JSON.stringify');
12386 // Make a fake root object containing our value under the key of ''.
12387 // Return the result of stringifying the value.
12389 return str('', {'': value});
12394 // If the JSON object does not yet have a parse method, give it one.
12396 if (typeof JSON.parse !== 'function') {
12397 JSON.parse = function (text, reviver) {
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.
12404 function walk(holder, key) {
12406 // The walk method is used to recursively walk the resulting structure so
12407 // that modifications can be made.
12409 var k, v, value = holder[key];
12410 if (value && typeof value === 'object') {
12412 if (Object.hasOwnProperty.call(value, k)) {
12413 v = walk(value, k);
12414 if (v !== undefined) {
12422 return reviver.call(holder, key, value);
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.
12430 text = String(text);
12432 if (cx.test(text)) {
12433 text = text.replace(cx, function (a) {
12435 ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
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.
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.
12452 if (/^[\],:{}\s]*$/.
12453 test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
12454 replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
12455 replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
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.
12462 j = eval('(' + text + ')');
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.
12467 return typeof reviver === 'function' ?
12468 walk({'': j}, '') : j;
12471 // If the text is not JSON parseable, then a SyntaxError is thrown.
12473 throw new SyntaxError('JSON.parse');
12477 // ************************************************************************************************
12482 // ************************************************************************************************
12485 /* See license.txt for terms of usage */
12488 // ************************************************************************************************
12490 /* Copyright (c) 2010-2011 Marcus Westin
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:
12499 * The above copyright notice and this permission notice shall be included in
12500 * all copies or substantial portions of the Software.
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
12511 var store = (function(){
12514 doc = win.document,
12515 localStorageName = 'localStorage',
12516 globalStorageName = 'globalStorage',
12517 namespace = '__firebug__storejs__',
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 = {} }
12532 api.serialize = function(value) {
12533 return JSON.stringify(value)
12535 api.deserialize = function(value) {
12536 if (typeof value != 'string') { return undefined }
12537 return JSON.parse(value)
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 }
12548 function isGlobalStorageNameSupported() {
12549 try { return (globalStorageName in win && win[globalStorageName] && win[globalStorageName][win.location.hostname]) }
12550 catch(err) { return false }
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() }
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] } }
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)
12585 api.set = withIEStorage(function(storage, key, val) {
12586 storage.setAttribute(key, api.serialize(val))
12587 storage.save(localStorageName)
12589 api.get = withIEStorage(function(storage, key) {
12590 return api.deserialize(storage.getAttribute(key))
12592 api.remove = withIEStorage(function(storage, key) {
12593 storage.removeAttribute(key)
12594 storage.save(localStorageName)
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)
12602 storage.save(localStorageName)
12607 api.set(namespace, namespace)
12608 if (api.get(namespace) != namespace) { api.disabled = true }
12609 api.remove(namespace)
12611 api.disabled = true
12617 if (typeof module != 'undefined') { module.exports = store }
12620 // ************************************************************************************************
12625 // ************************************************************************************************
12628 /* See license.txt for terms of usage */
12630 FBL.ns( /**@scope s_selector*/ function() { with (FBL) {
12631 // ************************************************************************************************
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/
12640 var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
12642 toString = Object.prototype.toString,
12643 hasDuplicate = false,
12644 baseHasDuplicate = true;
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;
12656 * @name Firebug.Selector
12661 * @exports Sizzle as Firebug.Selector
12663 var Sizzle = function(selector, context, results, seed) {
12664 results = results || [];
12665 var origContext = context = context || document;
12667 if ( context.nodeType !== 1 && context.nodeType !== 9 ) {
12671 if ( !selector || typeof selector !== "string" ) {
12675 var parts = [], m, set, checkSet, check, mode, extra, prune = true, contextXML = isXML(context),
12678 // Reset the position of the chunker regexp (start from head)
12679 while ( (chunker.exec(""), m = chunker.exec(soFar)) !== null ) {
12682 parts.push( m[1] );
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 );
12694 set = Expr.relative[ parts[0] ] ?
12696 Sizzle( parts.shift(), context );
12698 while ( parts.length ) {
12699 selector = parts.shift();
12701 if ( Expr.relative[ selector ] )
12702 selector += parts.shift();
12704 set = posProcess( selector, set );
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];
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;
12722 if ( parts.length > 0 ) {
12723 checkSet = makeArray(set);
12728 while ( parts.length ) {
12729 var cur = parts.pop(), pop = cur;
12731 if ( !Expr.relative[ cur ] ) {
12737 if ( pop == null ) {
12741 Expr.relative[ cur ]( checkSet, pop, contextXML );
12744 checkSet = parts = [];
12753 throw "Syntax error, unrecognized expression: " + (cur || selector);
12756 if ( toString.call(checkSet) === "[object Array]" ) {
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] );
12766 for ( var i = 0; checkSet[i] != null; i++ ) {
12767 if ( checkSet[i] && checkSet[i].nodeType === 1 ) {
12768 results.push( set[i] );
12773 makeArray( checkSet, results );
12777 Sizzle( extra, origContext, results, seed );
12778 Sizzle.uniqueSort( results );
12784 Sizzle.uniqueSort = function(results){
12786 hasDuplicate = baseHasDuplicate;
12787 results.sort(sortOrder);
12789 if ( hasDuplicate ) {
12790 for ( var i = 1; i < results.length; i++ ) {
12791 if ( results[i] === results[i-1] ) {
12792 results.splice(i--, 1);
12801 Sizzle.matches = function(expr, set){
12802 return Sizzle(expr, null, null, set);
12805 Sizzle.find = function(expr, context, isXML){
12812 for ( var i = 0, l = Expr.order.length; i < l; i++ ) {
12813 var type = Expr.order[i], match;
12815 if ( (match = Expr.leftMatch[ type ].exec( expr )) ) {
12816 var left = match[1];
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 ], "" );
12831 set = context.getElementsByTagName("*");
12834 return {set: set, expr: expr};
12837 Sizzle.filter = function(expr, set, inplace, not){
12838 var old = expr, result = [], curLoop = set, match, anyFound,
12839 isXMLFilter = set && set[0] && isXML(set[0]);
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;
12847 if ( curLoop == result ) {
12851 if ( Expr.preFilter[ type ] ) {
12852 match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );
12855 anyFound = found = true;
12856 } else if ( match === true ) {
12862 for ( var i = 0; (item = curLoop[i]) != null; i++ ) {
12864 found = filter( item, match, i, curLoop );
12865 var pass = not ^ !!found;
12867 if ( inplace && found != null ) {
12871 curLoop[i] = false;
12873 } else if ( pass ) {
12874 result.push( item );
12881 if ( found !== undefined ) {
12886 expr = expr.replace( Expr.match[ type ], "" );
12897 // Improper expression
12898 if ( expr == old ) {
12899 if ( anyFound == null ) {
12900 throw "Syntax error, unrecognized expression: " + expr;
12913 var Expr = Sizzle.selectors = {
12914 order: [ "ID", "NAME", "TAG" ],
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\))?/
12927 "class": "className",
12931 href: function(elem){
12932 return elem.getAttribute("href");
12936 "+": function(checkSet, part, isXML){
12937 var isPartStr = typeof part === "string",
12938 isTag = isPartStr && !/\W/.test(part),
12939 isPartStrNotTag = isPartStr && !isTag;
12941 if ( isTag && !isXML ) {
12942 part = part.toUpperCase();
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 ) {}
12949 checkSet[i] = isPartStrNotTag || elem && elem.nodeName === part ?
12955 if ( isPartStrNotTag ) {
12956 Sizzle.filter( part, checkSet, true );
12959 ">": function(checkSet, part, isXML){
12960 var isPartStr = typeof part === "string";
12962 if ( isPartStr && !/\W/.test(part) ) {
12963 part = isXML ? part : part.toUpperCase();
12965 for ( var i = 0, l = checkSet.length; i < l; i++ ) {
12966 var elem = checkSet[i];
12968 var parent = elem.parentNode;
12969 checkSet[i] = parent.nodeName === part ? parent : false;
12973 for ( var i = 0, l = checkSet.length; i < l; i++ ) {
12974 var elem = checkSet[i];
12976 checkSet[i] = isPartStr ?
12978 elem.parentNode === part;
12983 Sizzle.filter( part, checkSet, true );
12987 "": function(checkSet, part, isXML){
12988 var doneName = done++, checkFn = dirCheck;
12990 if ( !/\W/.test(part) ) {
12991 var nodeCheck = part = isXML ? part : part.toUpperCase();
12992 checkFn = dirNodeCheck;
12995 checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML);
12997 "~": function(checkSet, part, isXML){
12998 var doneName = done++, checkFn = dirCheck;
13000 if ( typeof part === "string" && !/\W/.test(part) ) {
13001 var nodeCheck = part = isXML ? part : part.toUpperCase();
13002 checkFn = dirNodeCheck;
13005 checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML);
13009 ID: function(match, context, isXML){
13010 if ( typeof context.getElementById !== "undefined" && !isXML ) {
13011 var m = context.getElementById(match[1]);
13012 return m ? [m] : [];
13015 NAME: function(match, context, isXML){
13016 if ( typeof context.getElementsByName !== "undefined" ) {
13017 var ret = [], results = context.getElementsByName(match[1]);
13019 for ( var i = 0, l = results.length; i < l; i++ ) {
13020 if ( results[i].getAttribute("name") === match[1] ) {
13021 ret.push( results[i] );
13025 return ret.length === 0 ? null : ret;
13028 TAG: function(match, context){
13029 return context.getElementsByTagName(match[1]);
13033 CLASS: function(match, curLoop, inplace, result, not, isXML){
13034 match = " " + match[1].replace(/\\/g, "") + " ";
13040 for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {
13042 if ( not ^ (elem.className && (" " + elem.className + " ").indexOf(match) >= 0) ) {
13044 result.push( elem );
13045 } else if ( inplace ) {
13046 curLoop[i] = false;
13053 ID: function(match){
13054 return match[1].replace(/\\/g, "");
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();
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]);
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;
13072 // TODO: Move to normal caching system
13077 ATTR: function(match, curLoop, inplace, result, not, isXML){
13078 var name = match[1].replace(/\\/g, "");
13080 if ( !isXML && Expr.attrMap[name] ) {
13081 match[1] = Expr.attrMap[name];
13084 if ( match[2] === "~=" ) {
13085 match[4] = " " + match[4] + " ";
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);
13096 var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);
13098 result.push.apply( result, ret );
13102 } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {
13108 POS: function(match){
13109 match.unshift( true );
13114 enabled: function(elem){
13115 return elem.disabled === false && elem.type !== "hidden";
13117 disabled: function(elem){
13118 return elem.disabled === true;
13120 checked: function(elem){
13121 return elem.checked === true;
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;
13129 parent: function(elem){
13130 return !!elem.firstChild;
13132 empty: function(elem){
13133 return !elem.firstChild;
13135 has: function(elem, i, match){
13136 return !!Sizzle( match[3], elem ).length;
13138 header: function(elem){
13139 return /h\d/i.test( elem.nodeName );
13141 text: function(elem){
13142 return "text" === elem.type;
13144 radio: function(elem){
13145 return "radio" === elem.type;
13147 checkbox: function(elem){
13148 return "checkbox" === elem.type;
13150 file: function(elem){
13151 return "file" === elem.type;
13153 password: function(elem){
13154 return "password" === elem.type;
13156 submit: function(elem){
13157 return "submit" === elem.type;
13159 image: function(elem){
13160 return "image" === elem.type;
13162 reset: function(elem){
13163 return "reset" === elem.type;
13165 button: function(elem){
13166 return "button" === elem.type || elem.nodeName.toUpperCase() === "BUTTON";
13168 input: function(elem){
13169 return /input|select|textarea|button/i.test(elem.nodeName);
13173 first: function(elem, i){
13176 last: function(elem, i, match, array){
13177 return i === array.length - 1;
13179 even: function(elem, i){
13180 return i % 2 === 0;
13182 odd: function(elem, i){
13183 return i % 2 === 1;
13185 lt: function(elem, i, match){
13186 return i < match[3] - 0;
13188 gt: function(elem, i, match){
13189 return i > match[3] - 0;
13191 nth: function(elem, i, match){
13192 return match[3] - 0 == i;
13194 eq: function(elem, i, match){
13195 return match[3] - 0 == i;
13199 PSEUDO: function(elem, match, i, array){
13200 var name = match[1], filter = Expr.filters[ name ];
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];
13209 for ( var i = 0, l = not.length; i < l; i++ ) {
13210 if ( not[i] === elem ) {
13218 CHILD: function(elem, match){
13219 var type = match[1], node = elem;
13223 while ( (node = node.previousSibling) ) {
13224 if ( node.nodeType === 1 ) return false;
13226 if ( type == 'first') return true;
13229 while ( (node = node.nextSibling) ) {
13230 if ( node.nodeType === 1 ) return false;
13234 var first = match[2], last = match[3];
13236 if ( first == 1 && last == 0 ) {
13240 var doneName = match[0],
13241 parent = elem.parentNode;
13243 if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) {
13245 for ( node = parent.firstChild; node; node = node.nextSibling ) {
13246 if ( node.nodeType === 1 ) {
13247 node.nodeIndex = ++count;
13250 parent.sizcache = doneName;
13253 var diff = elem.nodeIndex - last;
13254 if ( first == 0 ) {
13257 return ( diff % first == 0 && diff / first >= 0 );
13261 ID: function(elem, match){
13262 return elem.nodeType === 1 && elem.getAttribute("id") === match;
13264 TAG: function(elem, match){
13265 return (match === "*" && elem.nodeType === 1) || elem.nodeName === match;
13267 CLASS: function(elem, match){
13268 return (" " + (elem.className || elem.getAttribute("class")) + " ")
13269 .indexOf( match ) > -1;
13271 ATTR: function(elem, match){
13272 var name = match[1],
13273 result = Expr.attrHandle[ name ] ?
13274 Expr.attrHandle[ name ]( elem ) :
13275 elem[ name ] != null ?
13277 elem.getAttribute( name ),
13278 value = result + "",
13282 return result == null ?
13287 value.indexOf(check) >= 0 :
13289 (" " + value + " ").indexOf(check) >= 0 :
13291 value && result !== false :
13295 value.indexOf(check) === 0 :
13297 value.substr(value.length - check.length) === check :
13299 value === check || value.substr(0, check.length + 1) === check + "-" :
13302 POS: function(elem, match, i, array){
13303 var name = match[2], filter = Expr.setFilters[ name ];
13306 return filter( elem, i, match, array );
13312 var origPOS = Expr.match.POS;
13314 for ( var type in Expr.match ) {
13315 Expr.match[ type ] = new RegExp( Expr.match[ type ].source + /(?![^\[]*\])(?![^\(]*\))/.source );
13316 Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source );
13319 var makeArray = function(array, results) {
13320 array = Array.prototype.slice.call( array, 0 );
13323 results.push.apply( results, array );
13330 // Perform a simple check to determine if the browser is capable of
13331 // converting a NodeList to an array using builtin methods.
13333 Array.prototype.slice.call( document.documentElement.childNodes, 0 );
13335 // Provide a fallback method if it does not work
13337 makeArray = function(array, results) {
13338 var ret = results || [];
13340 if ( toString.call(array) === "[object Array]" ) {
13341 Array.prototype.push.apply( ret, array );
13343 if ( typeof array.length === "number" ) {
13344 for ( var i = 0, l = array.length; i < l; i++ ) {
13345 ret.push( array[i] );
13348 for ( var i = 0; array[i]; i++ ) {
13349 ret.push( array[i] );
13360 if ( document.documentElement.compareDocumentPosition ) {
13361 sortOrder = function( a, b ) {
13362 if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) {
13364 hasDuplicate = true;
13369 var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;
13371 hasDuplicate = true;
13375 } else if ( "sourceIndex" in document.documentElement ) {
13376 sortOrder = function( a, b ) {
13377 if ( !a.sourceIndex || !b.sourceIndex ) {
13379 hasDuplicate = true;
13384 var ret = a.sourceIndex - b.sourceIndex;
13386 hasDuplicate = true;
13390 } else if ( document.createRange ) {
13391 sortOrder = function( a, b ) {
13392 if ( !a.ownerDocument || !b.ownerDocument ) {
13394 hasDuplicate = true;
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);
13406 hasDuplicate = true;
13412 // Check to see if the browser returns elements by name when
13413 // querying by getElementById (and provide a workaround)
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 + "'/>";
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 );
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 : [];
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;
13440 root.removeChild( form );
13441 root = form = null; // release memory in IE
13445 // Check to see if the browser returns only elements
13446 // when doing getElementsByTagName("*")
13448 // Create a fake element
13449 var div = document.createElement("div");
13450 div.appendChild( document.createComment("") );
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]);
13457 // Filter out possible comments
13458 if ( match[1] === "*" ) {
13461 for ( var i = 0; results[i]; i++ ) {
13462 if ( results[i].nodeType === 1 ) {
13463 tmp.push( results[i] );
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);
13483 div = null; // release memory in IE
13486 if ( document.querySelectorAll ) (function(){
13487 var oldSizzle = Sizzle, div = document.createElement("div");
13488 div.innerHTML = "<p class='TEST'></p>";
13490 // Safari can't handle uppercase or unicode characters when
13492 if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
13496 Sizzle = function(query, context, extra, seed){
13497 context = context || document;
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) ) {
13503 return makeArray( context.querySelectorAll(query), extra );
13507 return oldSizzle(query, context, extra, seed);
13510 for ( var prop in oldSizzle ) {
13511 Sizzle[ prop ] = oldSizzle[ prop ];
13514 div = null; // release memory in IE
13517 if ( document.getElementsByClassName && document.documentElement.getElementsByClassName ) (function(){
13518 var div = document.createElement("div");
13519 div.innerHTML = "<div class='test e'></div><div class='test'></div>";
13521 // Opera can't find a second classname (in 9.6)
13522 if ( div.getElementsByClassName("e").length === 0 )
13525 // Safari caches class attributes, doesn't catch changes (in 3.2)
13526 div.lastChild.className = "e";
13528 if ( div.getElementsByClassName("e").length === 1 )
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]);
13538 div = null; // release memory in IE
13541 function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
13542 var sibDir = dir == "previousSibling" && !isXML;
13543 for ( var i = 0, l = checkSet.length; i < l; i++ ) {
13544 var elem = checkSet[i];
13546 if ( sibDir && elem.nodeType === 1 ){
13547 elem.sizcache = doneName;
13554 if ( elem.sizcache === doneName ) {
13555 match = checkSet[elem.sizset];
13559 if ( elem.nodeType === 1 && !isXML ){
13560 elem.sizcache = doneName;
13564 if ( elem.nodeName === cur ) {
13572 checkSet[i] = match;
13577 function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
13578 var sibDir = dir == "previousSibling" && !isXML;
13579 for ( var i = 0, l = checkSet.length; i < l; i++ ) {
13580 var elem = checkSet[i];
13582 if ( sibDir && elem.nodeType === 1 ) {
13583 elem.sizcache = doneName;
13590 if ( elem.sizcache === doneName ) {
13591 match = checkSet[elem.sizset];
13595 if ( elem.nodeType === 1 ) {
13597 elem.sizcache = doneName;
13600 if ( typeof cur !== "string" ) {
13601 if ( elem === cur ) {
13606 } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {
13615 checkSet[i] = match;
13620 var contains = document.compareDocumentPosition ? function(a, b){
13621 return a.compareDocumentPosition(b) & 16;
13622 } : function(a, b){
13623 return a !== b && (a.contains ? a.contains(b) : true);
13626 var isXML = function(elem){
13627 return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" ||
13628 !!elem.ownerDocument && elem.ownerDocument.documentElement.nodeName !== "HTML";
13631 var posProcess = function(selector, context){
13632 var tmpSet = [], later = "", match,
13633 root = context.nodeType ? [context] : context;
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 )) ) {
13639 selector = selector.replace( Expr.match.PSEUDO, "" );
13642 selector = Expr.relative[selector] ? selector + "*" : selector;
13644 for ( var i = 0, l = root.length; i < l; i++ ) {
13645 Sizzle( selector, root[i], tmpSet );
13648 return Sizzle.filter( later, tmpSet );
13653 Firebug.Selector = Sizzle;
13657 // ************************************************************************************************
13660 /* See license.txt for terms of usage */
13662 FBL.ns(function() { with (FBL) {
13663 // ************************************************************************************************
13665 // ************************************************************************************************
13666 // Inspector Module
13668 var ElementCache = Firebug.Lite.Cache.Element;
13670 var inspectorTS, inspectorTimer, isInspecting;
13672 Firebug.Inspector =
13676 offlineFragment = Env.browser.document.createDocumentFragment();
13678 createBoxModelInspector();
13679 createOutlineInspector();
13682 destroy: function()
13684 destroyBoxModelInspector();
13685 destroyOutlineInspector();
13687 offlineFragment = null;
13690 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
13691 // Inspect functions
13693 toggleInspect: function()
13697 this.stopInspecting();
13701 Firebug.chrome.inspectButton.changeState("pressed");
13702 this.startInspecting();
13706 startInspecting: function()
13708 isInspecting = true;
13710 Firebug.chrome.selectPanel("HTML");
13712 createInspectorFrame();
13714 var size = Firebug.browser.getWindowScrollSize();
13716 fbInspectFrame.style.width = size.width + "px";
13717 fbInspectFrame.style.height = size.height + "px";
13719 //addEvent(Firebug.browser.document.documentElement, "mousemove", Firebug.Inspector.onInspectingBody);
13721 addEvent(fbInspectFrame, "mousemove", Firebug.Inspector.onInspecting);
13722 addEvent(fbInspectFrame, "mousedown", Firebug.Inspector.onInspectingClick);
13725 stopInspecting: function()
13727 isInspecting = false;
13729 if (outlineVisible) this.hideOutline();
13730 removeEvent(fbInspectFrame, "mousemove", Firebug.Inspector.onInspecting);
13731 removeEvent(fbInspectFrame, "mousedown", Firebug.Inspector.onInspectingClick);
13733 destroyInspectorFrame();
13735 Firebug.chrome.inspectButton.restore();
13737 if (Firebug.chrome.type == "popup")
13738 Firebug.chrome.node.focus();
13741 onInspectingClick: function(e)
13743 fbInspectFrame.style.display = "none";
13744 var targ = Firebug.browser.getElementFromPoint(e.clientX, e.clientY);
13745 fbInspectFrame.style.display = "block";
13747 // Avoid inspecting the outline, and the FirebugUI
13749 if (id && /^fbOutline\w$/.test(id)) return;
13750 if (id == "FirebugUI") return;
13752 // Avoid looking at text nodes in Opera
13753 while (targ.nodeType != 1) targ = targ.parentNode;
13755 //Firebug.Console.log(targ);
13756 Firebug.Inspector.stopInspecting();
13759 onInspecting: function(e)
13761 if (new Date().getTime() - lastInspecting > 30)
13763 fbInspectFrame.style.display = "none";
13764 var targ = Firebug.browser.getElementFromPoint(e.clientX, e.clientY);
13765 fbInspectFrame.style.display = "block";
13767 // Avoid inspecting the outline, and the FirebugUI
13769 if (id && /^fbOutline\w$/.test(id)) return;
13770 if (id == "FirebugUI") return;
13772 // Avoid looking at text nodes in Opera
13773 while (targ.nodeType != 1) targ = targ.parentNode;
13775 if (targ.nodeName.toLowerCase() == "body") return;
13777 //Firebug.Console.log(e.clientX, e.clientY, targ);
13778 Firebug.Inspector.drawOutline(targ);
13780 if (ElementCache(targ))
13782 var target = ""+ElementCache.key(targ);
13783 var lazySelect = function()
13785 inspectorTS = new Date().getTime();
13788 Firebug.HTML.selectTreeNode(""+ElementCache.key(targ));
13791 if (inspectorTimer)
13793 clearTimeout(inspectorTimer);
13794 inspectorTimer = null;
13797 if (new Date().getTime() - inspectorTS > 200)
13798 setTimeout(lazySelect, 0);
13800 inspectorTimer = setTimeout(lazySelect, 300);
13803 lastInspecting = new Date().getTime();
13807 // TODO: xxxpedro remove this?
13808 onInspectingBody: function(e)
13810 if (new Date().getTime() - lastInspecting > 30)
13812 var targ = e.target;
13814 // Avoid inspecting the outline, and the FirebugUI
13816 if (id && /^fbOutline\w$/.test(id)) return;
13817 if (id == "FirebugUI") return;
13819 // Avoid looking at text nodes in Opera
13820 while (targ.nodeType != 1) targ = targ.parentNode;
13822 if (targ.nodeName.toLowerCase() == "body") return;
13824 //Firebug.Console.log(e.clientX, e.clientY, targ);
13825 Firebug.Inspector.drawOutline(targ);
13827 if (ElementCache.has(targ))
13828 FBL.Firebug.HTML.selectTreeNode(""+ElementCache.key(targ));
13830 lastInspecting = new Date().getTime();
13843 drawOutline: function(el)
13846 var scrollbarSize = 17;
13848 var windowSize = Firebug.browser.getWindowSize();
13849 var scrollSize = Firebug.browser.getWindowScrollSize();
13850 var scrollPosition = Firebug.browser.getWindowScrollPosition();
13852 var box = Firebug.browser.getElementBox(el);
13855 var left = box.left;
13856 var height = box.height;
13857 var width = box.width;
13859 var freeHorizontalSpace = scrollPosition.left + windowSize.width - left - width -
13860 (!isIE && scrollSize.height > windowSize.height ? // is *vertical* scrollbar visible
13861 scrollbarSize : 0);
13863 var freeVerticalSpace = scrollPosition.top + windowSize.height - top - height -
13864 (!isIE && scrollSize.width > windowSize.width ? // is *horizontal* scrollbar visible
13865 scrollbarSize : 0);
13867 var numVerticalBorders = freeVerticalSpace > 0 ? 2 : 1;
13869 var o = outlineElements;
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";
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()
13884 style = o.fbOutlineB.style;
13885 if (freeVerticalSpace > 0)
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?
13894 style.top = -2*border + "px";
13895 style.left = -2*border + "px";
13896 style.width = border + "px";
13897 //style.height = border + "px";
13900 style = o.fbOutlineR.style;
13901 if (freeHorizontalSpace > 0)
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";
13910 style.top = -2*border + "px";
13911 style.left = -2*border + "px";
13912 style.height = border + "px";
13913 style.width = border + "px";
13916 if (!outlineVisible) this.showOutline();
13919 hideOutline: function()
13921 if (!outlineVisible) return;
13923 for (var name in outline)
13924 offlineFragment.appendChild(outlineElements[name]);
13926 outlineVisible = false;
13929 showOutline: function()
13931 if (outlineVisible) return;
13933 if (boxModelVisible) this.hideBoxModel();
13935 for (var name in outline)
13936 Firebug.browser.document.getElementsByTagName("body")[0].appendChild(outlineElements[name]);
13938 outlineVisible = true;
13941 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
13944 drawBoxModel: function(el)
13946 // avoid error when the element is not attached a document
13947 if (!el || !el.parentNode)
13950 var box = Firebug.browser.getElementBox(el);
13952 var windowSize = Firebug.browser.getWindowSize();
13953 var scrollPosition = Firebug.browser.getWindowScrollPosition();
13955 // element may be occluded by the chrome, when in frame mode
13956 var offsetHeight = Firebug.chrome.type == "frame" ? Firebug.context.persistedState.height : 0;
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 )
13966 var left = box.left;
13967 var height = box.height;
13968 var width = box.width;
13970 var margin = Firebug.browser.getMeasurementBox(el, "margin");
13971 var padding = Firebug.browser.getMeasurementBox(el, "padding");
13972 var border = Firebug.browser.getMeasurementBox(el, "border");
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";
13979 boxBorderStyle.top = margin.top + "px";
13980 boxBorderStyle.left = margin.left + "px";
13981 boxBorderStyle.height = height + "px";
13982 boxBorderStyle.width = width + "px";
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";
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";
13994 if (!boxModelVisible) this.showBoxModel();
13997 hideBoxModel: function()
13999 if (!boxModelVisible) return;
14001 offlineFragment.appendChild(boxModel);
14002 boxModelVisible = false;
14005 showBoxModel: function()
14007 if (boxModelVisible) return;
14009 if (outlineVisible) this.hideOutline();
14011 Firebug.browser.document.getElementsByTagName("body")[0].appendChild(boxModel);
14012 boxModelVisible = true;
14017 // ************************************************************************************************
14018 // Inspector Internals
14021 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
14022 // Shared variables
14026 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
14027 // Internal variables
14029 var offlineFragment = null;
14031 var boxModelVisible = false;
14033 var boxModel, boxModelStyle,
14034 boxMargin, boxMarginStyle,
14035 boxBorder, boxBorderStyle,
14036 boxPadding, boxPaddingStyle,
14037 boxContent, boxContentStyle;
14039 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
14041 var resetStyle = "margin:0; padding:0; border:0; position:absolute; overflow:hidden; display:block;";
14042 var offscreenStyle = resetStyle + "top:-1234px; left:-1234px;";
14044 var inspectStyle = resetStyle + "z-index: 2147483500;";
14045 var inspectFrameStyle = resetStyle + "z-index: 2147483550; top:0; left:0; background:url(" +
14046 Env.Location.skinDir + "pixel_transparent.gif);";
14048 //if (Env.Options.enableTrace) inspectFrameStyle = resetStyle + "z-index: 2147483550; top: 0; left: 0; background: #ff0; opacity: 0.05; _filter: alpha(opacity=5);";
14050 var inspectModelOpacity = isIE ? "filter:alpha(opacity=80);" : "opacity:0.8;";
14051 var inspectModelStyle = inspectStyle + inspectModelOpacity;
14052 var inspectMarginStyle = inspectStyle + "background: #EDFF64; height:100%; width:100%;";
14053 var inspectBorderStyle = inspectStyle + "background: #666;";
14054 var inspectPaddingStyle = inspectStyle + "background: SlateBlue;";
14055 var inspectContentStyle = inspectStyle + "background: SkyBlue;";
14058 var outlineStyle = {
14059 fbHorizontalLine: "background: #3875D7;height: 2px;",
14060 fbVerticalLine: "background: #3875D7;width: 2px;"
14063 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
14065 var lastInspecting = 0;
14066 var fbInspectFrame = null;
14069 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
14071 var outlineVisible = false;
14072 var outlineElements = {};
14074 "fbOutlineT": "fbHorizontalLine",
14075 "fbOutlineL": "fbVerticalLine",
14076 "fbOutlineB": "fbHorizontalLine",
14077 "fbOutlineR": "fbVerticalLine"
14081 var getInspectingTarget = function()
14086 // ************************************************************************************************
14089 var createInspectorFrame = function createInspectorFrame()
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);
14098 var destroyInspectorFrame = function destroyInspectorFrame()
14100 if (fbInspectFrame)
14102 Firebug.browser.document.getElementsByTagName("body")[0].removeChild(fbInspectFrame);
14103 fbInspectFrame = null;
14107 var createOutlineInspector = function createOutlineInspector()
14109 for (var name in outline)
14111 var el = outlineElements[name] = createGlobalElement("div");
14113 el.firebugIgnore = true;
14114 el.style.cssText = inspectStyle + outlineStyle[outline[name]];
14115 offlineFragment.appendChild(el);
14119 var destroyOutlineInspector = function destroyOutlineInspector()
14121 for (var name in outline)
14123 var el = outlineElements[name];
14124 el.parentNode.removeChild(el);
14128 var createBoxModelInspector = function createBoxModelInspector()
14130 boxModel = createGlobalElement("div");
14131 boxModel.id = "fbBoxModel";
14132 boxModel.firebugIgnore = true;
14133 boxModelStyle = boxModel.style;
14134 boxModelStyle.cssText = inspectModelStyle;
14136 boxMargin = createGlobalElement("div");
14137 boxMargin.id = "fbBoxMargin";
14138 boxMarginStyle = boxMargin.style;
14139 boxMarginStyle.cssText = inspectMarginStyle;
14140 boxModel.appendChild(boxMargin);
14142 boxBorder = createGlobalElement("div");
14143 boxBorder.id = "fbBoxBorder";
14144 boxBorderStyle = boxBorder.style;
14145 boxBorderStyle.cssText = inspectBorderStyle;
14146 boxModel.appendChild(boxBorder);
14148 boxPadding = createGlobalElement("div");
14149 boxPadding.id = "fbBoxPadding";
14150 boxPaddingStyle = boxPadding.style;
14151 boxPaddingStyle.cssText = inspectPaddingStyle;
14152 boxModel.appendChild(boxPadding);
14154 boxContent = createGlobalElement("div");
14155 boxContent.id = "fbBoxContent";
14156 boxContentStyle = boxContent.style;
14157 boxContentStyle.cssText = inspectContentStyle;
14158 boxModel.appendChild(boxContent);
14160 offlineFragment.appendChild(boxModel);
14163 var destroyBoxModelInspector = function destroyBoxModelInspector()
14165 boxModel.parentNode.removeChild(boxModel);
14168 // ************************************************************************************************
14174 // ************************************************************************************************
14178 // FIXED - eval return
14179 // FIXED - addEventListener problem in IE
14180 // FIXED doc.createRange?
14182 // class reserved word
14183 // test all honza examples in IE6 and IE7
14186 /* See license.txt for terms of usage */
14188 ( /** @scope s_domplate */ function() {
14190 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
14193 FBL.DomplateTag = function DomplateTag(tagName)
14195 this.tagName = tagName;
14200 * @extends FBL.DomplateTag
14202 FBL.DomplateEmbed = function DomplateEmbed()
14208 * @extends FBL.DomplateTag
14210 FBL.DomplateLoop = function DomplateLoop()
14214 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
14216 var DomplateTag = FBL.DomplateTag;
14217 var DomplateEmbed = FBL.DomplateEmbed;
14218 var DomplateLoop = FBL.DomplateLoop;
14220 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
14224 FBL.domplate = function()
14227 for (var i = 0; i < arguments.length; ++i)
14228 lastSubject = lastSubject ? copyObject(lastSubject, arguments[i]) : arguments[i];
14230 for (var name in lastSubject)
14232 var val = lastSubject[name];
14234 val.tag.subject = lastSubject;
14237 return lastSubject;
14240 var domplate = FBL.domplate;
14242 FBL.domplate.context = function(context, fn)
14244 var lastContext = domplate.lastContext;
14245 domplate.topContext = context;
14247 domplate.topContext = lastContext;
14250 FBL.TAG = function()
14252 var embed = new DomplateEmbed();
14253 return embed.merge(arguments);
14256 FBL.FOR = function()
14258 var loop = new DomplateLoop();
14259 return loop.merge(arguments);
14262 FBL.DomplateTag.prototype =
14264 merge: function(args, oldTag)
14267 this.tagName = oldTag.tagName;
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) : [];
14278 var attrs = args.length ? args[0] : null;
14279 var hasAttrs = typeof(attrs) == "object" && !isTag(attrs);
14281 this.children = [];
14283 if (domplate.topContext)
14284 this.context = domplate.topContext;
14287 parseChildren(args, hasAttrs ? 1 : 0, this.vars, this.children);
14290 this.parseAttrs(attrs);
14292 return creator(this, DomplateTag);
14295 parseAttrs: function(args)
14297 for (var name in args)
14299 var val = parseValue(args[name]);
14300 readPartNames(val, this.vars);
14302 if (name.indexOf("on") == 0)
14304 var eventName = name.substr(2);
14305 if (!this.listeners)
14306 this.listeners = [];
14307 this.listeners.push(eventName, val);
14309 else if (name.indexOf("_") == 0)
14311 var propName = name.substr(1);
14314 this.props[propName] = val;
14316 else if (name.indexOf("$") == 0)
14318 var className = name.substr(1);
14321 this.classes[className] = val;
14325 if (name == "class" && this.attrs.hasOwnProperty(name) )
14326 this.attrs[name] += " " + val;
14328 this.attrs[name] = val;
14333 compile: function()
14335 if (this.renderMarkup)
14338 this.compileMarkup();
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);
14346 compileMarkup: function()
14348 this.markupArgs = [];
14349 var topBlock = [], topOuts = [], blocks = [], info = {args: this.markupArgs, argIndex: 0};
14351 this.generateMarkup(topBlock, topOuts, blocks, info);
14352 this.addCode(topBlock, topOuts, blocks);
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(') {');
14360 fnBlock.push('with (this) {');
14362 fnBlock.push('with (__context__) {');
14363 fnBlock.push('with (__in__) {');
14365 fnBlock.push.apply(fnBlock, blocks);
14372 fnBlock.push('}})');
14374 function __link__(tag, code, outputs, args)
14376 if (!tag || !tag.tag)
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);
14387 outputs.push(tagOutputs);
14390 function __escape__(value)
14392 function replaceChars(ch)
14409 return String(value).replace(/[<>&"']/g, replaceChars);
14412 function __loop__(iter, outputs, fn)
14415 outputs.push(iterOuts);
14417 if (iter instanceof Array)
14418 iter = new ArrayIterator(iter);
14424 var value = iter.next();
14425 var itemOuts = [0,0];
14426 iterOuts.push(itemOuts);
14427 fn.apply(this, [value, itemOuts]);
14432 if (exc != StopIteration)
14437 var js = fnBlock.join("");
14440 this.renderMarkup = r;
14443 getVarNames: function(args)
14446 args.push.apply(args, this.vars);
14448 for (var i = 0; i < this.children.length; ++i)
14450 var child = this.children[i];
14452 child.tag.getVarNames(args);
14453 else if (child instanceof Parts)
14455 for (var i = 0; i < child.parts.length; ++i)
14457 if (child.parts[i] instanceof Variable)
14459 var name = child.parts[i].name;
14460 var names = name.split(".");
14461 args.push(names[0]);
14468 generateMarkup: function(topBlock, topOuts, blocks, info)
14470 topBlock.push(',"<', this.tagName, '"');
14472 for (var name in this.attrs)
14474 if (name != "class")
14476 var val = this.attrs[name];
14477 topBlock.push(', " ', name, '=\\""');
14478 addParts(val, ',', topBlock, info, true);
14479 topBlock.push(', "\\""');
14483 if (this.listeners)
14485 for (var i = 0; i < this.listeners.length; i += 2)
14486 readPartNames(this.listeners[i+1], topOuts);
14491 for (var name in this.props)
14492 readPartNames(this.props[name], topOuts);
14495 if ( this.attrs.hasOwnProperty("class") || this.classes)
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)
14503 topBlock.push(', (');
14504 addParts(this.classes[name], '', topBlock, info);
14505 topBlock.push(' ? "', name, '" + " " : "")');
14507 topBlock.push(', "\\""');
14509 topBlock.push(',">"');
14511 this.generateChildMarkup(topBlock, topOuts, blocks, info);
14512 topBlock.push(',"</', this.tagName, '>"');
14515 generateChildMarkup: function(topBlock, topOuts, blocks, info)
14517 for (var i = 0; i < this.children.length; ++i)
14519 var child = this.children[i];
14521 child.tag.generateMarkup(topBlock, topOuts, blocks, info);
14523 addParts(child, ',', topBlock, info, true);
14527 addCode: function(topBlock, topOuts, blocks)
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);
14537 addLocals: function(blocks)
14540 this.getVarNames(varNames);
14543 for (var i = 0; i < varNames.length; ++i)
14545 var name = varNames[i];
14546 if ( map.hasOwnProperty(name) )
14550 var names = name.split(".");
14551 blocks.push('var ', names[0] + ' = ' + '__in__.' + names[0] + ';');
14555 compileDOM: function()
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);
14566 var fnBlock = ['r=(function (root, context, o'];
14568 for (var i = 0; i < path.staticIndex; ++i)
14569 fnBlock.push(', ', 's'+i);
14571 for (var i = 0; i < path.renderIndex; ++i)
14572 fnBlock.push(', ', 'd'+i);
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;');
14581 fnBlock.push('with (this) {');
14583 fnBlock.push('with (context) {');
14585 fnBlock.push(blocks.join(""));
14592 fnBlock.push('return ', nodeCount, ';');
14593 fnBlock.push('})');
14595 function __bind__(object, fn)
14597 return function(event) { return fn.apply(object, [event]); };
14600 function __link__(node, tag, args)
14602 if (!tag || !tag.tag)
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);
14615 function __loop__(iter, fn)
14618 for (var i = 0; i < iter.length; ++i)
14621 iter[i][1] = nodeCount;
14622 nodeCount += fn.apply(this, iter[i]);
14623 //if (FBTrace.DBG_DOM) FBTrace.sysout("nodeCount", nodeCount);
14628 function __path__(parent, offset)
14630 //if (FBTrace.DBG_DOM) FBTrace.sysout("domplate __path__ offset: "+ offset+"\n");
14633 for (var i = 2; i < arguments.length; ++i)
14635 var index = arguments[i];
14640 parent = parent.parentNode;
14642 parent = parent.childNodes[index];
14645 //if (FBTrace.DBG_DOM) FBTrace.sysout("domplate: "+arguments[2]+", root: "+ root+", parent: "+ parent+"\n");
14649 var js = fnBlock.join("");
14650 //if (FBTrace.DBG_DOM) FBTrace.sysout(js.replace(/(\;|\{)/g, "$1\n"));
14653 this.renderDOM = r;
14656 generateDOM: function(path, blocks, args)
14658 if (this.listeners || this.props)
14659 this.generateNodePath(path, blocks);
14661 if (this.listeners)
14663 for (var i = 0; i < this.listeners.length; i += 2)
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);');
14674 for (var name in this.props)
14676 var val = this.props[name];
14677 var arg = generateArg(val, path, args);
14678 blocks.push('node.', name, ' = ', arg, ';');
14682 this.generateChildDOM(path, blocks, args);
14686 generateNodePath: function(path, blocks)
14688 blocks.push("var node = __path__(root, o");
14689 for (var i = 0; i < path.length; ++i)
14690 blocks.push(",", path[i]);
14694 generateChildDOM: function(path, blocks, args)
14697 for (var i = 0; i < this.children.length; ++i)
14699 var child = this.children[i];
14701 path[path.length-1] += '+' + child.tag.generateDOM(path, blocks, args);
14703 path[path.length-1] += '+1';
14709 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
14711 FBL.DomplateEmbed.prototype = copyObject(FBL.DomplateTag.prototype,
14712 /** @lends FBL.DomplateEmbed.prototype */
14714 merge: function(args, oldTag)
14716 this.value = oldTag ? oldTag.value : parseValue(args[0]);
14717 this.attrs = oldTag ? oldTag.attrs : {};
14718 this.vars = oldTag ? copyArray(oldTag.vars) : [];
14720 var attrs = args[1];
14721 for (var name in attrs)
14723 var val = parseValue(attrs[name]);
14724 this.attrs[name] = val;
14725 readPartNames(val, this.vars);
14728 return creator(this, DomplateEmbed);
14731 getVarNames: function(names)
14733 if (this.value instanceof Parts)
14734 names.push(this.value.parts[0].name);
14737 names.push.apply(names, this.vars);
14740 generateMarkup: function(topBlock, topOuts, blocks, info)
14742 this.addCode(topBlock, topOuts, blocks);
14744 blocks.push('__link__(');
14745 addParts(this.value, '', blocks, info);
14746 blocks.push(', __code__, __out__, {');
14748 var lastName = null;
14749 for (var name in this.attrs)
14755 var val = this.attrs[name];
14756 blocks.push('"', name, '":');
14757 addParts(val, '', blocks, info);
14760 blocks.push('});');
14761 //this.generateChildMarkup(topBlock, topOuts, blocks, info);
14764 generateDOM: function(path, blocks, args)
14766 var embedName = 'e'+path.embedIndex++;
14768 this.generateNodePath(path, blocks);
14770 var valueName = 'd' + path.renderIndex++;
14771 var argsName = 'd' + path.renderIndex++;
14772 blocks.push(embedName + ' = __link__(node, ', valueName, ', ', argsName, ');');
14778 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
14780 FBL.DomplateLoop.prototype = copyObject(FBL.DomplateTag.prototype,
14781 /** @lends FBL.DomplateLoop.prototype */
14783 merge: function(args, oldTag)
14785 this.varName = oldTag ? oldTag.varName : args[0];
14786 this.iter = oldTag ? oldTag.iter : parseValue(args[1]);
14789 this.children = oldTag ? copyArray(oldTag.children) : [];
14791 var offset = Math.min(args.length, 2);
14792 parseChildren(args, offset, this.vars, this.children);
14794 return creator(this, DomplateLoop);
14797 getVarNames: function(names)
14799 if (this.iter instanceof Parts)
14800 names.push(this.iter.parts[0].name);
14802 DomplateTag.prototype.getVarNames.apply(this, [names]);
14805 generateMarkup: function(topBlock, topOuts, blocks, info)
14807 this.addCode(topBlock, topOuts, blocks);
14810 if (this.iter instanceof Parts)
14812 var part = this.iter.parts[0];
14813 iterName = part.name;
14817 for (var i = 0; i < part.format.length; ++i)
14818 iterName = part.format[i] + "(" + iterName + ")";
14822 iterName = this.iter;
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('}]);');
14830 generateDOM: function(path, blocks, args)
14832 var iterName = 'd'+path.renderIndex++;
14833 var counterName = 'i'+path.loopIndex;
14834 var loopName = 'l'+path.loopIndex++;
14839 var preIndex = path.renderIndex;
14840 path.renderIndex = 0;
14844 var subBlocks = [];
14845 var basePath = path[path.length-1];
14846 for (var i = 0; i < this.children.length; ++i)
14848 path[path.length-1] = basePath+'+'+loopName+'+'+nodeCount;
14850 var child = this.children[i];
14852 nodeCount += '+' + child.tag.generateDOM(path, subBlocks, args);
14857 path[path.length-1] = basePath+'+'+loopName;
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('}]);');
14867 path.renderIndex = preIndex;
14873 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
14876 function Variable(name, format)
14879 this.format = format;
14883 function Parts(parts)
14885 this.parts = parts;
14888 // ************************************************************************************************
14890 function parseParts(str)
14892 var re = /\$([_A-Za-z][_A-Za-z0-9.|]*)/g;
14897 while (m = re.exec(str))
14899 var pre = str.substr(index, (re.lastIndex-m[0].length)-index);
14903 var expr = m[1].split("|");
14904 parts.push(new Variable(expr[0], expr.slice(1)));
14905 index = re.lastIndex;
14911 var post = str.substr(index);
14915 return new Parts(parts);
14918 function parseValue(val)
14920 return typeof(val) == 'string' ? parseParts(val) : val;
14923 function parseChildren(args, offset, vars, children)
14925 for (var i = offset; i < args.length; ++i)
14927 var val = parseValue(args[i]);
14928 children.push(val);
14929 readPartNames(val, vars);
14933 function readPartNames(val, vars)
14935 if (val instanceof Parts)
14937 for (var i = 0; i < val.parts.length; ++i)
14939 var part = val.parts[i];
14940 if (part instanceof Variable)
14941 vars.push(part.name);
14946 function generateArg(val, path, args)
14948 if (val instanceof Parts)
14951 for (var i = 0; i < val.parts.length; ++i)
14953 var part = val.parts[i];
14954 if (part instanceof Variable)
14956 var varName = 'd'+path.renderIndex++;
14959 for (var j = 0; j < part.format.length; ++j)
14960 varName = part.format[j] + '(' + varName + ')';
14963 vals.push(varName);
14966 vals.push('"'+part.replace(/"/g, '\\"')+'"');
14969 return vals.join('+');
14974 return 's' + path.staticIndex++;
14978 function addParts(val, delim, block, info, escapeIt)
14981 if (val instanceof Parts)
14983 for (var i = 0; i < val.parts.length; ++i)
14985 var part = val.parts[i];
14986 if (part instanceof Variable)
14988 var partName = part.name;
14991 for (var j = 0; j < part.format.length; ++j)
14992 partName = part.format[j] + "(" + partName + ")";
14996 vals.push("__escape__(" + partName + ")");
14998 vals.push(partName);
15001 vals.push('"'+ part + '"');
15004 else if (isTag(val))
15006 info.args.push(val);
15007 vals.push('s'+info.argIndex++);
15010 vals.push('"'+ val + '"');
15012 var parts = vals.join(delim);
15014 block.push(delim, parts);
15017 function isTag(obj)
15019 return (typeof(obj) == "function" || obj instanceof Function) && !!obj.tag;
15022 function creator(tag, cons)
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);");
15032 extend(fn, Renderer);
15037 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
15039 function copyArray(oldArray)
15043 for (var i = 0; i < oldArray.length; ++i)
15044 ary.push(oldArray[i]);
15048 function copyObject(l, r)
15056 function extend(l, r)
15062 function addEvent(object, name, handler)
15065 object.attachEvent("on"+name, handler);
15067 object.addEventListener(name, handler, false);
15070 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
15073 function ArrayIterator(array)
15077 this.next = function()
15079 if (++index >= array.length)
15080 throw StopIteration;
15082 return array[index];
15087 function StopIteration() {}
15089 FBL.$break = function()
15091 throw StopIteration;
15094 // ************************************************************************************************
15099 renderHTML: function(args, outputs, self)
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("");
15108 insertRows: function(args, before, self)
15110 this.tag.compile();
15113 var html = this.renderHTML(args, outputs, self);
15115 var doc = before.ownerDocument;
15116 var div = doc.createElement("div");
15117 div.innerHTML = "<table><tbody>"+html+"</tbody></table>";
15119 var tbody = div.firstChild.firstChild;
15120 var parent = before.tagName == "TR" ? before.parentNode : before;
15121 var after = before.tagName == "TR" ? before.nextSibling : null;
15123 var firstRow = tbody.firstChild, lastRow;
15124 while (tbody.firstChild)
15126 lastRow = tbody.firstChild;
15128 parent.insertBefore(lastRow, after);
15130 parent.appendChild(lastRow);
15134 if (before.tagName == "TR")
15136 var node = firstRow.parentNode.firstChild;
15137 for (; node && node != firstRow; node = node.nextSibling)
15141 var domArgs = [firstRow, this.tag.context, offset];
15142 domArgs.push.apply(domArgs, this.tag.domArgs);
15143 domArgs.push.apply(domArgs, outputs);
15145 this.tag.renderDOM.apply(self ? self : this.tag.subject, domArgs);
15146 return [firstRow, lastRow];
15149 insertBefore: function(args, before, self)
15151 return this.insertNode(args, before.ownerDocument, before, false, self);
15154 insertAfter: function(args, after, self)
15156 return this.insertNode(args, after.ownerDocument, after, true, self);
15159 insertNode: function(args, doc, element, isAfter, self)
15164 this.tag.compile();
15167 var html = this.renderHTML(args, outputs, self);
15169 //if (FBTrace.DBG_DOM)
15170 // FBTrace.sysout("domplate.insertNode html: "+html+"\n");
15172 var doc = element.ownerDocument;
15173 if (!womb || womb.ownerDocument != doc)
15174 womb = doc.createElement("div");
15176 womb.innerHTML = html;
15178 var root = womb.firstChild;
15181 while (womb.firstChild)
15182 if (element.nextSibling)
15183 element.parentNode.insertBefore(womb.firstChild, element.nextSibling);
15185 element.parentNode.appendChild(womb.firstChild);
15189 while (womb.lastChild)
15190 element.parentNode.insertBefore(womb.lastChild, element);
15193 var domArgs = [root, this.tag.context, 0];
15194 domArgs.push.apply(domArgs, this.tag.domArgs);
15195 domArgs.push.apply(domArgs, outputs);
15197 //if (FBTrace.DBG_DOM)
15198 // FBTrace.sysout("domplate.insertNode domArgs:", domArgs);
15199 this.tag.renderDOM.apply(self ? self : this.tag.subject, domArgs);
15206 insertAfter: function(args, before, self)
15208 this.tag.compile();
15211 var html = this.renderHTML(args, outputs, self);
15213 var doc = before.ownerDocument;
15214 if (!womb || womb.ownerDocument != doc)
15215 womb = doc.createElement("div");
15217 womb.innerHTML = html;
15219 var root = womb.firstChild;
15220 while (womb.firstChild)
15221 if (before.nextSibling)
15222 before.parentNode.insertBefore(womb.firstChild, before.nextSibling);
15224 before.parentNode.appendChild(womb.firstChild);
15226 var domArgs = [root, this.tag.context, 0];
15227 domArgs.push.apply(domArgs, this.tag.domArgs);
15228 domArgs.push.apply(domArgs, outputs);
15230 this.tag.renderDOM.apply(self ? self : (this.tag.subject ? this.tag.subject : null),
15237 replace: function(args, parent, self)
15239 this.tag.compile();
15242 var html = this.renderHTML(args, outputs, self);
15245 if (parent.nodeType == 1)
15247 parent.innerHTML = html;
15248 root = parent.firstChild;
15252 if (!parent || parent.nodeType != 9)
15255 if (!womb || womb.ownerDocument != parent)
15256 womb = parent.createElement("div");
15257 womb.innerHTML = html;
15259 root = womb.firstChild;
15260 //womb.removeChild(root);
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);
15271 append: function(args, parent, self)
15273 this.tag.compile();
15276 var html = this.renderHTML(args, outputs, self);
15277 //if (FBTrace.DBG_DOM) FBTrace.sysout("domplate.append html: "+html+"\n");
15279 if (!womb || womb.ownerDocument != parent.ownerDocument)
15280 womb = parent.ownerDocument.createElement("div");
15281 womb.innerHTML = html;
15283 // TODO: xxxpedro domplate port to Firebug
15284 var root = womb.firstChild;
15285 while (womb.firstChild)
15286 parent.appendChild(womb.firstChild);
15288 // clearing element reference to avoid reference error in IE8 when switching contexts
15291 var domArgs = [root, this.tag.context, 0];
15292 domArgs.push.apply(domArgs, this.tag.domArgs);
15293 domArgs.push.apply(domArgs, outputs);
15295 //if (FBTrace.DBG_DOM) FBTrace.dumpProperties("domplate append domArgs:", domArgs);
15296 this.tag.renderDOM.apply(self ? self : this.tag.subject, domArgs);
15302 // ************************************************************************************************
15304 function defineTags()
15306 for (var i = 0; i < arguments.length; ++i)
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;
15312 var fnName = tagName.toUpperCase();
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"
15326 /* See license.txt for terms of usage */
15328 var FirebugReps = FBL.ns(function() { with (FBL) {
15331 // ************************************************************************************************
15334 var OBJECTBOX = this.OBJECTBOX =
15335 SPAN({"class": "objectBox objectBox-$className"});
15337 var OBJECTBLOCK = this.OBJECTBLOCK =
15338 DIV({"class": "objectBox objectBox-$className"});
15340 var OBJECTLINK = this.OBJECTLINK = isIE6 ? // IE6 object link representation
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"
15350 : // Other browsers
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"
15361 // ************************************************************************************************
15363 this.Undefined = domplate(Firebug.Rep,
15365 tag: OBJECTBOX("undefined"),
15367 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
15369 className: "undefined",
15371 supportsObject: function(object, type)
15373 return type == "undefined";
15377 // ************************************************************************************************
15379 this.Null = domplate(Firebug.Rep,
15381 tag: OBJECTBOX("null"),
15383 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
15387 supportsObject: function(object, type)
15389 return object == null;
15393 // ************************************************************************************************
15395 this.Nada = domplate(Firebug.Rep,
15399 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
15404 // ************************************************************************************************
15406 this.Number = domplate(Firebug.Rep,
15408 tag: OBJECTBOX("$object"),
15410 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
15412 className: "number",
15414 supportsObject: function(object, type)
15416 return type == "boolean" || type == "number";
15420 // ************************************************************************************************
15422 this.String = domplate(Firebug.Rep,
15424 tag: OBJECTBOX(""$object""),
15426 shortTag: OBJECTBOX(""$object|cropString""),
15428 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
15430 className: "string",
15432 supportsObject: function(object, type)
15434 return type == "string";
15438 // ************************************************************************************************
15440 this.Text = domplate(Firebug.Rep,
15442 tag: OBJECTBOX("$object"),
15444 shortTag: OBJECTBOX("$object|cropString"),
15446 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
15451 // ************************************************************************************************
15453 this.Caption = domplate(Firebug.Rep,
15455 tag: SPAN({"class": "caption"}, "$object")
15458 // ************************************************************************************************
15460 this.Warning = domplate(Firebug.Rep,
15462 tag: DIV({"class": "warning focusRow", role : 'listitem'}, "$object|STR")
15465 // ************************************************************************************************
15467 this.Func = domplate(Firebug.Rep,
15470 OBJECTLINK("$object|summarizeFunction"),
15472 summarizeFunction: function(fn)
15474 var fnRegex = /function ([^(]+\([^)]*\)) \{/;
15475 var fnText = safeToString(fn);
15477 var m = fnRegex.exec(fnText);
15478 return m ? m[1] : "function()";
15481 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
15483 copySource: function(fn)
15485 copyToClipboard(safeToString(fn));
15488 monitor: function(fn, script, monitored)
15491 Firebug.Debugger.unmonitorScript(fn, script, "monitor");
15493 Firebug.Debugger.monitorScript(fn, script, "monitor");
15496 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
15498 className: "function",
15500 supportsObject: function(object, type)
15502 return isFunction(object);
15505 inspectObject: function(fn, context)
15507 var sourceLink = findSourceForFunction(fn, context);
15509 Firebug.chrome.select(sourceLink);
15510 if (FBTrace.DBG_FUNCTION_NAME)
15511 FBTrace.sysout("reps.function.inspectObject selected sourceLink is ", sourceLink);
15514 getTooltip: function(fn, context)
15516 var script = findScriptForFunctionInContext(context, fn);
15518 return $STRF("Line", [normalizeURL(script.fileName), script.baseLineNumber]);
15521 return fn.toString();
15524 getTitle: function(fn, context)
15526 var name = fn.name ? fn.name : "function";
15527 return name + "()";
15530 getContextMenuItems: function(fn, target, context, script)
15533 script = findScriptForFunctionInContext(context, fn);
15537 var scriptInfo = getSourceFileAndLineByScript(context, script);
15538 var monitored = scriptInfo ? fbs.isMonitored(scriptInfo.sourceFile.href, scriptInfo.lineNo) : false;
15540 var name = script ? getFunctionName(script, context) : fn.name;
15542 {label: "CopySource", command: bindFixed(this.copySource, this, fn) },
15544 {label: $STRF("ShowCallsInConsole", [name]), nol10n: true,
15545 type: "checkbox", checked: monitored,
15546 command: bindFixed(this.monitor, this, fn, script, monitored) }
15551 // ************************************************************************************************
15553 this.jsdScript = domplate(Firebug.Rep,
15555 copySource: function(script)
15557 var fn = script.functionObject.getWrappedValue();
15558 return FirebugReps.Func.copySource(fn);
15561 monitor: function(fn, script, monitored)
15563 fn = script.functionObject.getWrappedValue();
15564 return FirebugReps.Func.monitor(fn, script, monitored);
15567 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
15569 className: "jsdScript",
15570 inspectable: false,
15572 supportsObject: function(object, type)
15574 return object instanceof jsdIScript;
15577 inspectObject: function(script, context)
15579 var sourceLink = getSourceLinkForScript(script, context);
15581 Firebug.chrome.select(sourceLink);
15584 getRealObject: function(script, context)
15589 getTooltip: function(script)
15591 return $STRF("jsdIScript", [script.tag]);
15594 getTitle: function(script, context)
15596 var fn = script.functionObject.getWrappedValue();
15597 return FirebugReps.Func.getTitle(fn, context);
15600 getContextMenuItems: function(script, target, context)
15602 var fn = script.functionObject.getWrappedValue();
15604 var scriptInfo = getSourceFileAndLineByScript(context, script);
15605 var monitored = scriptInfo ? fbs.isMonitored(scriptInfo.sourceFile.href, scriptInfo.lineNo) : false;
15607 var name = getFunctionName(script, context);
15610 {label: "CopySource", command: bindFixed(this.copySource, this, script) },
15612 {label: $STRF("ShowCallsInConsole", [name]), nol10n: true,
15613 type: "checkbox", checked: monitored,
15614 command: bindFixed(this.monitor, this, fn, script, monitored) }
15619 //************************************************************************************************
15621 this.Obj = domplate(Firebug.Rep,
15625 SPAN({"class": "objectTitle"}, "$object|getTitle "),
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")
15635 SPAN({"class": "objectRightBrace"}, "}")
15640 SPAN({"class": "objectProp-number"}, "$object"),
15643 SPAN({"class": "objectProp-string"}, ""$object""),
15646 SPAN({"class": "objectProp-object"}, "$object"),
15648 propIterator: function (object)
15650 ///Firebug.ObjectShortIteratorMax;
15651 var maxLength = 55; // default max length for long representation
15659 var numProperties = 0;
15660 var numPropertiesShown = 0;
15661 var maxLengthReached = false;
15667 "boolean": this.propNumberTag,
15668 "number": this.propNumberTag,
15669 "string": this.propStringTag,
15670 "object": this.propObjectTag
15675 var title = Firebug.Rep.getTitle(object);
15676 length += title.length;
15678 for (var name in object)
15683 value = object[name];
15690 var type = typeof(value);
15691 if (type == "boolean" ||
15692 type == "number" ||
15693 (type == "string" && value) ||
15694 (type == "object" && value && value.toString))
15696 var tag = propRepsMap[type];
15698 var value = (type == "object") ?
15699 Firebug.getRep(value).getTitle(value) :
15702 length += name.length + value.length + 4;
15704 if (length <= maxLength)
15714 numPropertiesShown++;
15717 maxLengthReached = true;
15723 if (maxLengthReached && numProperties > numPropertiesShown)
15727 if (numProperties > numPropertiesShown)
15730 object: "...", //xxxHonza localization
15731 tag: FirebugReps.Caption.tag,
15737 else if (props.length > 0)
15739 props[props.length-1].delim = '';
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
15752 fb_1_6_propIterator: function (object, max)
15759 var len = 0, count = 0;
15763 for (var name in object)
15768 value = object[name];
15775 var t = typeof(value);
15776 if (t == "boolean" || t == "number" || (t == "string" && value)
15777 || (t == "object" && value && value.toString))
15779 var rep = Firebug.getRep(value);
15780 var tag = rep.shortTag || rep.tag;
15783 value = rep.getTitle(value);
15784 tag = rep.titleTag;
15788 props.push({tag: tag, name: name, object: value, equal: "=", delim: ", "});
15795 props[Math.max(1,max-1)] = {
15796 object: "more...", //xxxHonza localization
15797 tag: FirebugReps.Caption.tag,
15803 else if (props.length > 0)
15805 props[props.length-1].delim = '';
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
15819 propIterator: function (object)
15829 for (var name in object)
15834 val = object[name];
15841 var t = typeof val;
15842 if (t == "boolean" || t == "number" || (t == "string" && val)
15843 || (t == "object" && !isFunction(val) && val && val.toString))
15845 var title = (t == "object")
15846 ? Firebug.getRep(val).getTitle(val)
15849 len += name.length + title.length + 1;
15851 props.push({name: name, value: title});
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
15869 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
15871 className: "object",
15873 supportsObject: function(object, type)
15880 // ************************************************************************************************
15882 this.Arr = domplate(Firebug.Rep,
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")
15891 SPAN({"class": "arrayRightBracket", role : "presentation"}, "]")
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")
15901 // TODO: xxxpedro - confirm this on Firebug
15902 //FOR("prop", "$object|shortPropIterator",
15904 // SPAN({"class": "objectPropValue"}, "$prop.value|cropString")
15906 SPAN({"class": "arrayRightBracket"}, "]")
15909 arrayIterator: function(array)
15912 for (var i = 0; i < array.length; ++i)
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 ? "" : ", ");
15919 items.push({object: value, tag: tag, delim: delim});
15925 shortArrayIterator: function(array)
15928 for (var i = 0; i < array.length && i < 3; ++i)
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 ? "" : ", ");
15935 items.push({object: value, tag: tag, delim: delim});
15938 if (array.length > 3)
15939 items.push({object: (array.length-3) + " more...", tag: FirebugReps.Caption.tag, delim: ""});
15944 shortPropIterator: this.Obj.propIterator,
15946 getItemIndex: function(child)
15948 var arrayIndex = 0;
15949 for (child = child.previousSibling; child; child = child.previousSibling)
15951 if (child.repObject)
15957 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
15959 className: "array",
15961 supportsObject: function(object)
15963 return this.isArray(object);
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) {
15972 else if (isIE && !isFunction(obj) && typeof obj == "object" && isFinite(obj.length) && obj.nodeType != 8)
15974 else if (isFinite(obj.length) && isFunction(obj.splice))
15976 else if (isFinite(obj.length) && isFunction(obj.callee)) // arguments
15978 else if (instanceOf(obj, "HTMLCollection"))
15980 else if (instanceOf(obj, "NodeList"))
15987 if (FBTrace.DBG_ERRORS)
15989 FBTrace.sysout("isArray FAILS:", exc); /* Something weird: without the try/catch, OOM, with no exception?? */
15990 FBTrace.sysout("isArray Fails on obj", obj);
15996 // END Yahoo BSD SOURCE See license below.
15998 getTitle: function(object, context)
16000 return "[" + object.length + "]";
16004 // ************************************************************************************************
16006 this.Property = domplate(Firebug.Rep,
16008 supportsObject: function(object)
16010 return object instanceof Property;
16013 getRealObject: function(prop, context)
16015 return prop.object[prop.name];
16018 getTitle: function(prop, context)
16024 // ************************************************************************************************
16026 this.NetFile = domplate(this.Obj,
16028 supportsObject: function(object)
16030 return object instanceof Firebug.NetFile;
16033 browseObject: function(file, context)
16035 openNewTab(file.href);
16039 getRealObject: function(file, context)
16045 // ************************************************************************************************
16047 this.Except = domplate(Firebug.Rep,
16050 OBJECTBOX({_repObject: "$object"}, "$object.message"),
16052 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
16054 className: "exception",
16056 supportsObject: function(object)
16058 return object instanceof ErrorCopy;
16063 // ************************************************************************************************
16065 this.Element = domplate(Firebug.Rep,
16070 SPAN({"class": "nodeTag"}, "$object.nodeName|toLowerCase"),
16071 FOR("attr", "$object|attrIterator",
16072 " $attr.nodeName="", SPAN({"class": "nodeValue"}, "$attr.nodeValue"), """
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")
16087 getVisible: function(elt)
16089 return isVisible(elt) ? "" : "selectorHidden";
16092 getSelectorTag: function(elt)
16094 return elt.nodeName.toLowerCase();
16097 getSelectorId: function(elt)
16099 return elt.id ? "#" + elt.id : "";
16102 getSelectorClass: function(elt)
16104 return elt.className ? "." + elt.className.split(" ")[0] : "";
16107 getValue: function(elt)
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)
16118 else if (elt instanceof HTMLFormElement)
16119 value = getFileName(elt.action);
16120 else if (elt instanceof HTMLScriptElement)
16121 value = getFileName(elt.src);
16123 return value ? " " + cropString(value, 20) : "";
16126 attrIterator: function(elt)
16129 var idAttr, classAttr;
16130 if (elt.attributes)
16132 for (var i = 0; i < elt.attributes.length; ++i)
16134 var attr = elt.attributes[i];
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)
16139 else if (attr.nodeName == "id")
16141 else if (attr.nodeName == "class")
16143 else if (attr.nodeName == "style")
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()+":"})
16157 attrs.splice(0, 0, classAttr);
16159 attrs.splice(0, 0, idAttr);
16164 shortAttrIterator: function(elt)
16167 if (elt.attributes)
16169 for (var i = 0; i < elt.attributes.length; ++i)
16171 var attr = elt.attributes[i];
16172 if (attr.nodeName == "id" || attr.nodeName == "class")
16180 getHidden: function(elt)
16182 return isVisible(elt) ? "" : "nodeHidden";
16185 getXPath: function(elt)
16187 return getElementTreeXPath(elt);
16190 // TODO: xxxpedro remove this?
16191 getNodeText: function(element)
16193 var text = element.textContent;
16194 if (Firebug.showFullTextNodes)
16197 return cropString(text, 50);
16201 getNodeTextGroups: function(element)
16203 var text = element.textContent;
16204 if (!Firebug.showFullTextNodes)
16206 text=cropString(text,50);
16209 var escapeGroups=[];
16211 if (Firebug.showTextNodesWithWhitespace)
16212 escapeGroups.push({
16213 'group': 'whitespace',
16214 'class': 'nodeWhiteSpace',
16221 if (Firebug.showTextNodesWithEntities)
16222 escapeGroups.push({
16224 'class':'nodeTextEntity',
16228 if (escapeGroups.length)
16229 return escapeGroupsForEntities(text, escapeGroups);
16231 return [{str:text,'class':'',extra:''}];
16234 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
16236 copyHTML: function(elt)
16238 var html = getElementXML(elt);
16239 copyToClipboard(html);
16242 copyInnerHTML: function(elt)
16244 copyToClipboard(elt.innerHTML);
16247 copyXPath: function(elt)
16249 var xpath = getElementXPath(elt);
16250 copyToClipboard(xpath);
16253 persistor: function(context, xpath)
16256 ? getElementsByXPath(context.window.document, xpath)
16259 return elts && elts.length ? elts[0] : null;
16262 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
16264 className: "element",
16266 supportsObject: function(object)
16268 //return object instanceof Element || object.nodeType == 1 && typeof object.nodeName == "string";
16269 return instanceOf(object, "Element");
16272 browseObject: function(elt, context)
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);
16287 persistObject: function(elt, context)
16289 var xpath = getElementXPath(elt);
16291 return bind(this.persistor, top, xpath);
16294 getTitle: function(element, context)
16296 return getElementCSSSelector(element);
16299 getTooltip: function(elt)
16301 return this.getXPath(elt);
16304 getContextMenuItems: function(elt, target, context)
16306 var monitored = areEventsMonitored(elt, null, context);
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) },
16313 {label: "ShowEventsInConsole", type: "checkbox", checked: monitored,
16314 command: bindFixed(toggleMonitorEvents, FBL, elt, null, monitored, context) },
16316 {label: "ScrollIntoView", command: bindFixed(elt.scrollIntoView, elt) }
16321 // ************************************************************************************************
16323 this.TextNode = domplate(Firebug.Rep,
16328 SPAN({"class": "nodeTag"}, "TextNode"),
16329 " textContent="", SPAN({"class": "nodeValue"}, "$object.textContent|cropString"), """,
16333 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
16335 className: "textNode",
16337 supportsObject: function(object)
16339 return object instanceof Text;
16343 // ************************************************************************************************
16345 this.Document = domplate(Firebug.Rep,
16348 OBJECTLINK("Document ", SPAN({"class": "objectPropValue"}, "$object|getLocation")),
16350 getLocation: function(doc)
16352 return doc.location ? getFileName(doc.location.href) : "";
16355 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
16357 className: "object",
16359 supportsObject: function(object)
16361 //return object instanceof Document || object instanceof XMLDocument;
16362 return instanceOf(object, "Document");
16365 browseObject: function(doc, context)
16367 openNewTab(doc.location.href);
16371 persistObject: function(doc, context)
16373 return this.persistor;
16376 persistor: function(context)
16378 return context.window.document;
16381 getTitle: function(win, context)
16386 getTooltip: function(doc)
16388 return doc.location.href;
16392 // ************************************************************************************************
16394 this.StyleSheet = domplate(Firebug.Rep,
16397 OBJECTLINK("StyleSheet ", SPAN({"class": "objectPropValue"}, "$object|getLocation")),
16399 getLocation: function(styleSheet)
16401 return getFileName(styleSheet.href);
16404 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
16406 copyURL: function(styleSheet)
16408 copyToClipboard(styleSheet.href);
16411 openInTab: function(styleSheet)
16413 openNewTab(styleSheet.href);
16416 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
16418 className: "object",
16420 supportsObject: function(object)
16422 //return object instanceof CSSStyleSheet;
16423 return instanceOf(object, "CSSStyleSheet");
16426 browseObject: function(styleSheet, context)
16428 openNewTab(styleSheet.href);
16432 persistObject: function(styleSheet, context)
16434 return bind(this.persistor, top, styleSheet.href);
16437 getTooltip: function(styleSheet)
16439 return styleSheet.href;
16442 getContextMenuItems: function(styleSheet, target, context)
16445 {label: "CopyLocation", command: bindFixed(this.copyURL, this, styleSheet) },
16447 {label: "OpenInTab", command: bindFixed(this.openInTab, this, styleSheet) }
16451 persistor: function(context, href)
16453 return getStyleSheetByHref(href, context);
16457 // ************************************************************************************************
16459 this.Window = domplate(Firebug.Rep,
16462 OBJECTLINK("Window ", SPAN({"class": "objectPropValue"}, "$object|getLocation")),
16464 getLocation: function(win)
16468 return (win && win.location && !win.closed) ? getFileName(win.location.href) : "";
16472 if (FBTrace.DBG_ERRORS)
16473 FBTrace.sysout("reps.Window window closed?");
16477 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
16479 className: "object",
16481 supportsObject: function(object)
16483 return instanceOf(object, "Window");
16486 browseObject: function(win, context)
16488 openNewTab(win.location.href);
16492 persistObject: function(win, context)
16494 return this.persistor;
16497 persistor: function(context)
16499 return context.window;
16502 getTitle: function(win, context)
16507 getTooltip: function(win)
16509 if (win && !win.closed)
16510 return win.location.href;
16514 // ************************************************************************************************
16516 this.Event = domplate(Firebug.Rep,
16518 tag: TAG("$copyEventTag", {object: "$object|copyEvent"}),
16521 OBJECTLINK("$object|summarizeEvent"),
16523 summarizeEvent: function(event)
16525 var info = [event.type, ' '];
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);
16533 return info.join("");
16536 copyEvent: function(event)
16538 return new EventCopy(event);
16541 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
16543 className: "object",
16545 supportsObject: function(object)
16547 //return object instanceof Event || object instanceof EventCopy;
16548 return instanceOf(object, "Event") || instanceOf(object, "EventCopy");
16551 getTitle: function(event, context)
16553 return "Event " + event.type;
16557 // ************************************************************************************************
16559 this.SourceLink = domplate(Firebug.Rep,
16562 OBJECTLINK({$collapsed: "$object|hideSourceLink"}, "$object|getSourceLinkTitle"),
16564 hideSourceLink: function(sourceLink)
16566 return sourceLink ? sourceLink.href.indexOf("XPCSafeJSObjectWrapper") != -1 : true;
16569 getSourceLinkTitle: function(sourceLink)
16576 var fileName = getFileName(sourceLink.href);
16577 fileName = decodeURIComponent(fileName);
16578 fileName = cropString(fileName, 17);
16582 if (FBTrace.DBG_ERRORS)
16583 FBTrace.sysout("reps.getSourceLinkTitle decodeURIComponent fails for \'"+fileName+"\': "+exc, exc);
16586 return typeof sourceLink.line == "number" ?
16587 fileName + " (line " + sourceLink.line + ")" :
16591 //return $STRF("Line", [fileName, sourceLink.line]);
16594 copyLink: function(sourceLink)
16596 copyToClipboard(sourceLink.href);
16599 openInTab: function(sourceLink)
16601 openNewTab(sourceLink.href);
16604 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
16606 className: "sourceLink",
16608 supportsObject: function(object)
16610 return object instanceof SourceLink;
16613 getTooltip: function(sourceLink)
16615 return decodeURI(sourceLink.href);
16618 inspectObject: function(sourceLink, context)
16620 if (sourceLink.type == "js")
16622 var scriptFile = getSourceFileByHref(sourceLink.href, context);
16624 return Firebug.chrome.select(sourceLink);
16626 else if (sourceLink.type == "css")
16628 // If an object is defined, treat it as the highest priority for
16630 if (sourceLink.object) {
16631 Firebug.chrome.select(sourceLink.object);
16635 var stylesheet = getStyleSheetByHref(sourceLink.href, context);
16638 var ownerNode = stylesheet.ownerNode;
16641 Firebug.chrome.select(sourceLink, "html");
16645 var panel = context.getPanel("stylesheet");
16646 if (panel && panel.getRuleByLine(stylesheet, sourceLink.line))
16647 return Firebug.chrome.select(sourceLink);
16651 // Fallback is to just open the view-source window on the file
16652 viewSource(sourceLink.href, sourceLink.line);
16655 browseObject: function(sourceLink, context)
16657 openNewTab(sourceLink.href);
16661 getContextMenuItems: function(sourceLink, target, context)
16664 {label: "CopyLocation", command: bindFixed(this.copyLink, this, sourceLink) },
16666 {label: "OpenInTab", command: bindFixed(this.openInTab, this, sourceLink) }
16671 // ************************************************************************************************
16673 this.SourceFile = domplate(this.SourceLink,
16676 OBJECTLINK({$collapsed: "$object|hideSourceLink"}, "$object|getSourceLinkTitle"),
16678 persistor: function(context, href)
16680 return getSourceFileByHref(href, context);
16683 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
16685 className: "sourceFile",
16687 supportsObject: function(object)
16689 return object instanceof SourceFile;
16692 persistObject: function(sourceFile)
16694 return bind(this.persistor, top, sourceFile.href);
16697 browseObject: function(sourceLink, context)
16701 getTooltip: function(sourceFile)
16703 return sourceFile.href;
16707 // ************************************************************************************************
16709 this.StackFrame = domplate(Firebug.Rep, // XXXjjb Since the repObject is fn the stack does not have correct line numbers
16713 A({"class": "objectLink objectLink-function focusRow a11yFocus", _repObject: "$object.fn"}, "$object|getCallName"),
16715 FOR("arg", "$object|argIterator",
16716 TAG("$arg.tag", {object: "$arg.value"}),
16717 SPAN({"class": "arrayComma"}, "$arg.delim")
16720 SPAN({"class": "objectLink-sourceLink objectLink"}, "$object|getSourceLinkTitle")
16723 getCallName: function(frame)
16725 //TODO: xxxpedro reps StackFrame
16726 return frame.name || "anonymous";
16728 //return getFunctionName(frame.script, frame.context);
16731 getSourceLinkTitle: function(frame)
16733 //TODO: xxxpedro reps StackFrame
16734 var fileName = cropString(getFileName(frame.href), 20);
16735 return fileName + (frame.lineNo ? " (line " + frame.lineNo + ")" : "");
16737 var fileName = cropString(getFileName(frame.href), 17);
16738 return $STRF("Line", [fileName, frame.lineNo]);
16741 argIterator: function(frame)
16748 for (var i = 0; i < frame.args.length; ++i)
16750 var arg = frame.args[i];
16755 var rep = Firebug.getRep(arg.value);
16756 var tag = rep.shortTag ? rep.shortTag : rep.tag;
16758 var delim = (i == frame.args.length-1 ? "" : ", ");
16760 items.push({name: arg.name, value: arg.value, tag: tag, delim: delim});
16766 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
16768 className: "stackFrame",
16770 supportsObject: function(object)
16772 return object instanceof StackFrame;
16775 inspectObject: function(stackFrame, context)
16777 var sourceLink = new SourceLink(stackFrame.href, stackFrame.lineNo, "js");
16778 Firebug.chrome.select(sourceLink);
16781 getTooltip: function(stackFrame, context)
16783 return $STRF("Line", [stackFrame.href, stackFrame.lineNo]);
16788 // ************************************************************************************************
16790 this.StackTrace = domplate(Firebug.Rep,
16793 FOR("frame", "$object.frames focusRow",
16794 TAG(this.StackFrame.tag, {object: "$frame"})
16797 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
16799 className: "stackTrace",
16801 supportsObject: function(object)
16803 return object instanceof StackTrace;
16807 // ************************************************************************************************
16809 this.jsdStackFrame = domplate(Firebug.Rep,
16811 inspectable: false,
16813 supportsObject: function(object)
16815 return (object instanceof jsdIStackFrame) && (object.isValid);
16818 getTitle: function(frame, context)
16820 if (!frame.isValid) return "(invalid frame)"; // XXXjjb avoid frame.script == null
16821 return getFunctionName(frame.script, context);
16824 getTooltip: function(frame, context)
16826 if (!frame.isValid) return "(invalid frame)"; // XXXjjb avoid frame.script == null
16827 var sourceInfo = FBL.getSourceFileAndLineByScript(context, frame.script, frame);
16829 return $STRF("Line", [sourceInfo.sourceFile.href, sourceInfo.lineNo]);
16831 return $STRF("Line", [frame.script.fileName, frame.line]);
16834 getContextMenuItems: function(frame, target, context)
16836 var fn = frame.script.functionObject.getWrappedValue();
16837 return FirebugReps.Func.getContextMenuItems(fn, target, context, frame.script);
16841 // ************************************************************************************************
16843 this.ErrorMessage = domplate(Firebug.Rep,
16847 $hasTwisty: "$object|hasStackTrace",
16848 $hasBreakSwitch: "$object|hasBreakSwitch",
16849 $breakForError: "$object|hasErrorBreak",
16850 _repObject: "$object",
16851 _stackTrace: "$object|getLastErrorStackTrace",
16852 onclick: "$onToggleError"},
16854 DIV({"class": "errorTitle a11yFocus", role : 'checkbox', 'aria-checked' : 'false'},
16855 "$object.message|getMessage"
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")
16862 TAG(this.SourceLink.tag, {object: "$object|getSourceLink"})
16865 getLastErrorStackTrace: function(error)
16867 return error.trace;
16870 hasStackTrace: function(error)
16872 var url = error.href.toString();
16873 var fromCommandLine = (url.indexOf("XPCSafeJSObjectWrapper") != -1);
16874 return !fromCommandLine && error.trace;
16877 hasBreakSwitch: function(error)
16879 return error.href && error.lineNo > 0;
16882 hasErrorBreak: function(error)
16884 return fbs.hasErrorBreakpoint(error.href, error.lineNo);
16887 getMessage: function(message)
16889 var re = /\[Exception... "(.*?)" nsresult:/;
16890 var m = re.exec(message);
16891 return m ? m[1] : message;
16894 getLine: function(error)
16896 if (error.category == "js")
16899 return cropString(error.source, 80);
16900 else if (error.href && error.href.indexOf("XPCSafeJSObjectWrapper") == -1)
16901 return cropString(error.getSourceLine(), 80);
16905 getSourceLink: function(error)
16907 var ext = error.category == "css" ? "css" : "js";
16908 return error.lineNo ? new SourceLink(error.href, error.lineNo, ext) : null;
16911 getSourceType: function(error)
16913 // Errors occurring inside of HTML event handlers look like "foo.html (line 1)"
16914 // so let's try to skip those
16917 else if (error.lineNo == 1 && getFileExtension(error.href) != "js")
16919 else if (error.category == "css")
16921 else if (!error.href || !error.lineNo)
16927 onToggleError: function(event)
16929 var target = event.currentTarget;
16930 if (hasClass(event.target, "errorBreak"))
16932 this.breakOnThisError(target.repObject);
16934 else if (hasClass(event.target, "errorSource"))
16936 var panel = Firebug.getElementPanel(event.target);
16937 this.inspectObject(target.repObject, panel.context);
16939 else if (hasClass(event.target, "errorTitle"))
16941 var traceBox = target.childNodes[1];
16942 toggleClass(target, "opened");
16943 event.target.setAttribute('aria-checked', hasClass(target, "opened"));
16944 if (hasClass(target, "opened"))
16946 if (target.stackTrace)
16947 var node = FirebugReps.StackTrace.tag.append({object: target.stackTrace}, traceBox);
16948 if (Firebug.A11yModel.enabled)
16950 var panel = Firebug.getElementPanel(event.target);
16951 dispatch([Firebug.A11yModel], "onLogRowContentCreated", [panel , traceBox]);
16955 clearNode(traceBox);
16959 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
16961 copyError: function(error)
16964 this.getMessage(error.message),
16966 "Line " + error.lineNo
16968 copyToClipboard(message.join("\n"));
16971 breakOnThisError: function(error)
16973 if (this.hasErrorBreak(error))
16974 Firebug.Debugger.clearErrorBreakpoint(error.href, error.lineNo);
16976 Firebug.Debugger.setErrorBreakpoint(error.href, error.lineNo);
16979 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
16981 className: "errorMessage",
16982 inspectable: false,
16984 supportsObject: function(object)
16986 return object instanceof ErrorMessage;
16989 inspectObject: function(error, context)
16991 var sourceLink = this.getSourceLink(error);
16992 FirebugReps.SourceLink.inspectObject(sourceLink, context);
16995 getContextMenuItems: function(error, target, context)
16997 var breakOnThisError = this.hasErrorBreak(error);
17000 {label: "CopyError", command: bindFixed(this.copyError, this, error) }
17003 if (error.category == "css")
17007 {label: "BreakOnThisError", type: "checkbox", checked: breakOnThisError,
17008 command: bindFixed(this.breakOnThisError, this, error) },
17010 optionMenu("BreakOnAllErrors", "breakOnErrors")
17018 // ************************************************************************************************
17020 this.Assert = domplate(Firebug.Rep,
17024 DIV({"class": "errorTitle"}),
17025 DIV({"class": "assertDescription"})
17028 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
17030 className: "assert",
17032 inspectObject: function(error, context)
17034 var sourceLink = this.getSourceLink(error);
17035 Firebug.chrome.select(sourceLink);
17038 getContextMenuItems: function(error, target, context)
17040 var breakOnThisError = this.hasErrorBreak(error);
17043 {label: "CopyError", command: bindFixed(this.copyError, this, error) },
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) }
17053 // ************************************************************************************************
17055 this.SourceText = domplate(Firebug.Rep,
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")
17067 lineIterator: function(sourceText)
17069 var maxLineNoChars = (sourceText.lines.length + "").length;
17072 for (var i = 0; i < sourceText.lines.length; ++i)
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;
17079 list.push({lineNo: lineNo, text: sourceText.lines[i]});
17085 getHTML: function(sourceText)
17087 return getSourceLineRange(sourceText, 1, sourceText.lines.length);
17091 //************************************************************************************************
17092 this.nsIDOMHistory = domplate(Firebug.Rep,
17094 tag:OBJECTBOX({onclick: "$showHistory"},
17095 OBJECTLINK("$object|summarizeHistory")
17098 className: "nsIDOMHistory",
17100 summarizeHistory: function(history)
17104 var items = history.length;
17105 return items + " history entries";
17109 return "object does not support history (nsIDOMHistory)";
17113 showHistory: function(history)
17117 var items = history.length; // if this throws, then unsupported
17118 Firebug.chrome.select(history);
17125 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
17127 supportsObject: function(object, type)
17129 return (object instanceof Ci.nsIDOMHistory);
17133 // ************************************************************************************************
17134 this.ApplicationCache = domplate(Firebug.Rep,
17136 tag:OBJECTBOX({onclick: "$showApplicationCache"},
17137 OBJECTLINK("$object|summarizeCache")
17140 summarizeCache: function(applicationCache)
17144 return applicationCache.length + " items in offline cache";
17148 return "https://bugzilla.mozilla.org/show_bug.cgi?id=422264";
17152 showApplicationCache: function(event)
17154 openNewTab("https://bugzilla.mozilla.org/show_bug.cgi?id=422264");
17157 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
17159 className: "applicationCache",
17161 supportsObject: function(object, type)
17163 if (Ci.nsIDOMOfflineResourceList)
17164 return (object instanceof Ci.nsIDOMOfflineResourceList);
17169 this.Storage = domplate(Firebug.Rep,
17171 tag: OBJECTBOX({onclick: "$show"}, OBJECTLINK("$object|summarize")),
17173 summarize: function(storage)
17175 return storage.length +" items in Storage";
17177 show: function(storage)
17179 openNewTab("http://dev.w3.org/html5/webstorage/#storage-0");
17181 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
17183 className: "Storage",
17185 supportsObject: function(object, type)
17187 return (object instanceof Storage);
17192 // ************************************************************************************************
17193 Firebug.registerRep(
17194 //this.nsIDOMHistory, // make this early to avoid exceptions
17200 //this.ApplicationCache, // must come before Arr (array) else exceptions.
17201 //this.ErrorMessage,
17211 //this.jsdStackFrame,
17219 Firebug.setDefaultReps(this.Func, this.Obj);
17223 // ************************************************************************************************
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.
17228 Software License Agreement (BSD License)
17230 Copyright (c) 2006, Yahoo! Inc.
17231 All rights reserved.
17233 Redistribution and use of this software in source and binary forms, with or without modification, are
17234 permitted provided that the following conditions are met:
17236 * Redistributions of source code must retain the above
17237 copyright notice, this list of conditions and the
17238 following disclaimer.
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.
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.
17250 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
17251 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
17252 PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
17253 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
17254 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
17255 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
17256 TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
17257 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
17262 /* See license.txt for terms of usage */
17264 FBL.ns(function() { with (FBL) {
17266 // ************************************************************************************************
17269 var saveTimeout = 400;
17270 var pageAmount = 10;
17272 // ************************************************************************************************
17275 var currentTarget = null;
17276 var currentGroup = null;
17277 var currentPanel = null;
17278 var currentEditor = null;
17280 var defaultEditor = null;
17282 var originalClassName = null;
17284 var originalValue = null;
17285 var defaultValue = null;
17286 var previousValue = null;
17288 var invalidEditor = false;
17289 var ignoreNextInput = false;
17291 // ************************************************************************************************
17293 Firebug.Editor = extend(Firebug.Module,
17295 supportsStopEvent: true,
17297 dispatchName: "editor",
17300 startEditing: function(target, value, editor)
17302 this.stopEditing();
17304 if (hasClass(target, "insertBefore") || hasClass(target, "insertAfter"))
17307 var panel = Firebug.getElementPanel(target);
17308 if (!panel.editable)
17311 if (FBTrace.DBG_EDITOR)
17312 FBTrace.sysout("editor.startEditing " + value, target);
17314 defaultValue = target.getAttribute("defaultValue");
17315 if (value == undefined)
17317 var textContent = isIE ? "innerText" : "textContent";
17318 value = target[textContent];
17319 if (value == defaultValue)
17323 originalValue = previousValue = value;
17325 invalidEditor = false;
17326 currentTarget = target;
17327 currentPanel = panel;
17328 currentGroup = getAncestorByClass(target, "editGroup");
17330 currentPanel.editing = true;
17332 var panelEditor = currentPanel.getEditor(target, value);
17333 currentEditor = editor ? editor : panelEditor;
17334 if (!currentEditor)
17335 currentEditor = getDefaultEditor(currentPanel);
17337 var inlineParent = getInlineParent(target);
17338 var targetSize = getOffsetSize(inlineParent);
17340 setClass(panel.panelNode, "editing");
17341 setClass(target, "editing");
17343 setClass(currentGroup, "editing");
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);
17353 stopEditing: function(cancel)
17355 if (!currentTarget)
17358 if (FBTrace.DBG_EDITOR)
17359 FBTrace.sysout("editor.stopEditing cancel:" + cancel+" saveTimeout: "+this.saveTimeout);
17361 clearTimeout(this.saveTimeout);
17362 delete this.saveTimeout;
17364 this.detachListeners(currentEditor, currentPanel.context);
17366 removeClass(currentPanel.panelNode, "editing");
17367 removeClass(currentTarget, "editing");
17369 removeClass(currentGroup, "editing");
17371 var value = currentEditor.getValue();
17372 if (value == defaultValue)
17375 var removeGroup = currentEditor.endEditing(currentTarget, value, cancel);
17381 //dispatch([Firebug.A11yModel], 'onInlineEditorClose', [currentPanel, currentTarget, removeGroup && !originalValue]);
17382 if (value != originalValue)
17383 this.saveEditAndNotifyListeners(currentTarget, originalValue, previousValue);
17385 if (removeGroup && !originalValue && currentGroup)
17386 currentGroup.parentNode.removeChild(currentGroup);
17390 this.saveEditAndNotifyListeners(currentTarget, null, previousValue);
17392 if (removeGroup && currentGroup)
17393 currentGroup.parentNode.removeChild(currentGroup);
17400 //throw exc.message;
17404 currentEditor.hide();
17405 currentPanel.editing = false;
17407 //dispatch(this.fbListeners, "onStopEdit", [currentPanel, currentEditor, currentTarget]);
17408 //if (FBTrace.DBG_EDITOR)
17409 // FBTrace.sysout("Editor stop panel "+currentPanel.name);
17411 currentTarget = null;
17412 currentGroup = null;
17413 currentPanel = null;
17414 currentEditor = null;
17415 originalValue = null;
17416 invalidEditor = false;
17421 cancelEditing: function()
17423 return this.stopEditing(true);
17426 update: function(saveNow)
17428 if (this.saveTimeout)
17429 clearTimeout(this.saveTimeout);
17431 invalidEditor = true;
17433 currentEditor.layout();
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);
17446 save: function(value)
17448 if (!invalidEditor)
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"));
17457 this.saveEditAndNotifyListeners(currentTarget, value, previousValue);
17459 previousValue = value;
17460 invalidEditor = false;
17464 if (FBTrace.DBG_ERRORS)
17465 FBTrace.sysout("editor.save FAILS "+exc, exc);
17469 saveEditAndNotifyListeners: function(currentTarget, value, previousValue)
17471 currentEditor.saveEdit(currentTarget, value, previousValue);
17472 //dispatch(this.fbListeners, "onSaveEdit", [currentPanel, currentEditor, currentTarget, value, previousValue]);
17475 setEditTarget: function(element)
17479 dispatch([Firebug.A11yModel], 'onInlineEditorClose', [currentPanel, currentTarget, true]);
17480 this.stopEditing();
17482 else if (hasClass(element, "insertBefore"))
17483 this.insertRow(element, "before");
17484 else if (hasClass(element, "insertAfter"))
17485 this.insertRow(element, "after");
17487 this.startEditing(element);
17490 tabNextEditor: function()
17492 if (!currentTarget)
17495 var value = currentEditor.getValue();
17496 var nextEditable = currentTarget;
17499 nextEditable = !value && currentGroup
17500 ? getNextOutsider(nextEditable, currentGroup)
17501 : getNextByClass(nextEditable, "editable");
17503 while (nextEditable && !nextEditable.offsetHeight);
17505 this.setEditTarget(nextEditable);
17508 tabPreviousEditor: function()
17510 if (!currentTarget)
17513 var value = currentEditor.getValue();
17514 var prevEditable = currentTarget;
17517 prevEditable = !value && currentGroup
17518 ? getPreviousOutsider(prevEditable, currentGroup)
17519 : getPreviousByClass(prevEditable, "editable");
17521 while (prevEditable && !prevEditable.offsetHeight);
17523 this.setEditTarget(prevEditable);
17526 insertRow: function(relative, insertWhere)
17529 relative || getAncestorByClass(currentTarget, "editGroup") || currentTarget;
17530 var value = this.stopEditing();
17532 currentPanel = Firebug.getElementPanel(group);
17534 currentEditor = currentPanel.getEditor(group, value);
17535 if (!currentEditor)
17536 currentEditor = getDefaultEditor(currentPanel);
17538 currentGroup = currentEditor.insertNewRow(group, insertWhere);
17542 var editable = hasClass(currentGroup, "editable")
17544 : getNextByClass(currentGroup, "editable");
17547 this.setEditTarget(editable);
17550 insertRowForObject: function(relative)
17552 var container = getAncestorByClass(relative, "insertInto");
17555 relative = getChildByClass(container, "insertBefore");
17557 this.insertRow(relative, "before");
17561 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
17563 attachListeners: function(editor, context)
17566 currentTarget.ownerDocument.parentWindow :
17567 currentTarget.ownerDocument.defaultView;
17569 addEvent(win, "resize", this.onResize);
17570 addEvent(win, "blur", this.onBlur);
17572 var chrome = Firebug.chrome;
17575 chrome.keyCodeListen("ESCAPE", null, bind(this.cancelEditing, this))
17578 if (editor.arrowCompletion)
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))
17588 if (currentEditor.tabNavigation)
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))
17597 else if (currentEditor.multiLine)
17599 this.listeners.push(
17600 chrome.keyCodeListen("TAB", null, insertTab)
17605 this.listeners.push(
17606 chrome.keyCodeListen("RETURN", null, bindFixed(this.stopEditing, this))
17609 if (currentEditor.tabCompletion)
17611 this.listeners.push(
17612 chrome.keyCodeListen("TAB", null, bind(editor.completeValue, editor, 1)),
17613 chrome.keyCodeListen("TAB", isShift, bind(editor.completeValue, editor, -1))
17619 detachListeners: function(editor, context)
17621 if (!this.listeners)
17625 currentTarget.ownerDocument.parentWindow :
17626 currentTarget.ownerDocument.defaultView;
17628 removeEvent(win, "resize", this.onResize);
17629 removeEvent(win, "blur", this.onBlur);
17631 var chrome = Firebug.chrome;
17634 for (var i = 0; i < this.listeners.length; ++i)
17635 chrome.keyIgnore(this.listeners[i]);
17638 delete this.listeners;
17641 onResize: function(event)
17643 currentEditor.layout(true);
17646 onBlur: function(event)
17648 if (currentEditor.enterOnBlur && isAncestor(event.target, currentEditor.box))
17649 this.stopEditing();
17652 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
17655 initialize: function()
17657 Firebug.Module.initialize.apply(this, arguments);
17659 this.onResize = bindFixed(this.onResize, this);
17660 this.onBlur = bind(this.onBlur, this);
17663 disable: function()
17665 this.stopEditing();
17668 showContext: function(browser, context)
17670 this.stopEditing();
17673 showPanel: function(browser, panel)
17675 this.stopEditing();
17679 // ************************************************************************************************
17682 Firebug.BaseEditor = extend(Firebug.MeasureBox,
17684 getValue: function()
17688 setValue: function(value)
17692 show: function(target, panel, value, textSize, targetSize)
17700 layout: function(forceAll)
17704 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
17705 // Support for context menus within inline editors.
17707 getContextMenuItems: function(target)
17710 items.push({label: "Cut", commandID: "cmd_cut"});
17711 items.push({label: "Copy", commandID: "cmd_copy"});
17712 items.push({label: "Paste", commandID: "cmd_paste"});
17716 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
17717 // Editor Module listeners will get "onBeginEditing" just before this call
17719 beginEditing: function(target, value)
17723 // Editor Module listeners will get "onSaveEdit" just after this call
17724 saveEdit: function(target, value, previousValue)
17728 endEditing: function(target, value, cancel)
17730 // Remove empty groups by default
17734 insertNewRow: function(target, insertWhere)
17739 // ************************************************************************************************
17742 // basic inline editor attributes
17743 var inlineEditorAttributes = {
17744 "class": "textEditorInner",
17747 spellcheck: "false",
17749 onkeypress: "$onKeyPress",
17751 onoverflow: "$onOverflow",
17752 oncontextmenu: "$onContextMenu"
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
17762 inlineEditorAttributes.onpropertychange = "$onInput";
17763 inlineEditorAttributes.onkeydown = "$onKeyDown";
17765 // for other browsers we use the oninput event
17768 inlineEditorAttributes.oninput = "$onInput";
17771 Firebug.InlineEditor = function(doc)
17773 this.initializeInline(doc);
17776 Firebug.InlineEditor.prototype = domplate(Firebug.BaseEditor,
17783 DIV({"class": "inlineEditor"},
17784 DIV({"class": "textEditorTop1"},
17785 DIV({"class": "textEditorTop2"})
17787 DIV({"class": "textEditorInner1"},
17788 DIV({"class": "textEditorInner2"},
17790 inlineEditorAttributes
17794 DIV({"class": "textEditorBottom1"},
17795 DIV({"class": "textEditorBottom2"})
17800 INPUT({"class": "textEditorInner", type: "text",
17801 /*oninput: "$onInput",*/ onkeypress: "$onKeyPress", onoverflow: "$onOverflow"}
17805 IMG({"class": "inlineExpander", src: "blank.gif"}),
17807 initialize: function()
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;
17819 destroy: function()
17821 this.destroyInput();
17824 initializeInline: function(doc)
17826 if (FBTrace.DBG_EDITOR)
17827 FBTrace.sysout("Firebug.InlineEditor initializeInline()");
17829 //this.box = this.tag.replace({}, doc, this);
17830 this.box = this.tag.append({}, doc.body, this);
17832 //this.input = this.box.childNodes[1].firstChild.firstChild; // XXXjjb childNode[1] required
17833 this.input = this.box.getElementsByTagName("input")[0];
17837 this.input.style.top = "-8px";
17840 this.expander = this.expanderTag.replace({}, doc, this);
17844 destroyInput: function()
17846 // XXXjoe Need to remove input/keypress handlers to avoid leaks
17849 getValue: function()
17851 return this.input.value;
17854 setValue: function(value)
17856 // It's only a one-line editor, so new lines shouldn't be allowed
17857 return this.input.value = stripNewLines(value);
17860 show: function(target, panel, value, targetSize)
17862 //dispatch([Firebug.A11yModel], "onInlineEditorShow", [panel, this]);
17863 this.target = target;
17864 this.panel = panel;
17866 this.targetSize = targetSize;
17868 // TODO: xxxpedro editor
17869 //this.targetOffset = getClientOffset(target);
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 = ".";
17880 // Get the position of the target element (that is about to be edited)
17881 this.targetOffset =
17883 x: target.offsetLeft,
17884 y: target.offsetTop
17887 // Restore the original innerHTML value of the empty element
17888 if (isEmptyElement)
17889 target.innerHTML = innerHTML;
17891 this.originalClassName = this.box.className;
17893 var classNames = target.className.split(" ");
17894 for (var i = 0; i < classNames.length; ++i)
17895 setClass(this.box, "editor-" + classNames[i]);
17897 // Make the editor match the target's font style
17898 copyTextStyles(target, this.box);
17900 this.setValue(value);
17902 if (this.fixedWidth)
17903 this.updateLayout(true);
17906 this.startMeasuring(target);
17907 this.textSize = this.measureInputText(value);
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"))
17913 var yDiff = this.textSize.height - this.shadowExpand;
17915 // IE6 height offset
17919 parent.style.height = yDiff + "px";
17920 parent.parentNode.style.height = yDiff + "px";
17923 this.updateLayout(true);
17926 this.getAutoCompleter().reset();
17929 panel.panelNode.appendChild(this.box);
17931 target.offsetParent.appendChild(this.box);
17933 //console.log(target);
17934 //this.input.select(); // it's called bellow, with setTimeout
17938 // reset input style
17939 this.input.style.fontFamily = "Monospace";
17940 this.input.style.fontSize = "11px";
17943 // Insert the "expander" to cover the target element with white space
17944 if (!this.fixedWidth)
17946 copyBoxStyles(target, this.expander);
17948 target.parentNode.replaceChild(this.expander, target);
17949 collapse(target, true);
17950 this.expander.parentNode.insertBefore(target, this.expander);
17954 //scrollIntoCenterView(this.box, null, true);
17956 // Display the editor after change its size and position to avoid flickering
17957 this.box.style.display = "block";
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
17962 setTimeout(function(){
17963 self.input.focus();
17964 self.input.select();
17970 this.box.className = this.originalClassName;
17972 if (!this.fixedWidth)
17974 this.stopMeasuring();
17976 collapse(this.target, false);
17978 if (this.expander.parentNode)
17979 this.expander.parentNode.removeChild(this.expander);
17982 if (this.box.parentNode)
17984 ///setSelectionRange(this.input, 0, 0);
17987 this.box.parentNode.removeChild(this.box);
17990 delete this.target;
17994 layout: function(forceAll)
17996 if (!this.fixedWidth)
17997 this.textSize = this.measureInputText(this.input.value);
18000 this.targetOffset = getClientOffset(this.expander);
18002 this.updateLayout(false, forceAll);
18005 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
18007 beginEditing: function(target, value)
18011 saveEdit: function(target, value, previousValue)
18015 endEditing: function(target, value, cancel)
18017 // Remove empty groups by default
18021 insertNewRow: function(target, insertWhere)
18025 advanceToNext: function(target, charCode)
18030 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
18032 getAutoCompleteRange: function(value, offset)
18036 getAutoCompleteList: function(preExpr, expr, postExpr)
18040 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
18042 getAutoCompleter: function()
18044 if (!this.autoCompleter)
18046 this.autoCompleter = new Firebug.AutoCompleter(null,
18047 bind(this.getAutoCompleteRange, this), bind(this.getAutoCompleteList, this),
18051 return this.autoCompleter;
18054 completeValue: function(amt)
18056 //console.log("completeValue");
18058 var selectRangeCallback = this.getAutoCompleter().complete(currentPanel.context, this.input, true, amt < 0);
18060 if (selectRangeCallback)
18062 Firebug.Editor.update(true);
18064 // We need to select the editor text after calling update in Safari/Chrome,
18065 // otherwise the text won't be selected
18067 setTimeout(selectRangeCallback,0);
18069 selectRangeCallback();
18072 this.incrementValue(amt);
18075 incrementValue: function(amt)
18077 var value = this.input.value;
18079 // TODO: xxxpedro editor
18081 var start = getInputSelectionStart(this.input), end = start;
18083 var start = this.input.selectionStart, end = this.input.selectionEnd;
18086 var range = this.getAutoCompleteRange(value, start);
18087 if (!range || range.type != "int")
18088 range = {start: 0, end: value.length-1};
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);
18094 // See if the value is an integer, and if so increment it
18095 var intValue = parseInt(expr);
18096 if (!!intValue || intValue == 0)
18098 var m = /\d+/.exec(expr);
18099 var digitPost = expr.substr(m.index+m[0].length);
18101 var completion = intValue-amt;
18102 this.input.value = preExpr + completion + digitPost + postExpr;
18104 setSelectionRange(this.input, start, end);
18106 Firebug.Editor.update(true);
18114 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
18116 onKeyPress: function(event)
18118 //console.log("onKeyPress", event);
18119 if (event.keyCode == 27 && !this.completeAsYouType)
18121 var reverted = this.getAutoCompleter().revert(this.input);
18123 cancelEvent(event);
18125 else if (event.charCode && this.advanceToNext(this.target, event.charCode))
18127 Firebug.Editor.tabNextEditor();
18128 cancelEvent(event);
18132 if (this.numeric && event.charCode && (event.charCode < 48 || event.charCode > 57)
18133 && event.charCode != 45 && event.charCode != 46)
18134 FBL.cancelEvent(event);
18137 // If the user backspaces, don't autocomplete after the upcoming input event
18138 this.ignoreNextInput = event.keyCode == 8;
18143 onOverflow: function()
18145 this.updateLayout(false, false, 3);
18148 onKeyDown: function(event)
18150 //console.log("onKeyDown", event.keyCode);
18151 if (event.keyCode > 46 || event.keyCode == 32 || event.keyCode == 8)
18153 this.keyDownPressed = true;
18157 onInput: function(event)
18161 // skip not relevant onpropertychange calls on IE
18164 if (event.propertyName != "value" || !isVisible(this.input) || !this.keyDownPressed)
18167 this.keyDownPressed = false;
18170 //console.log("onInput", event);
18173 var selectRangeCallback;
18175 if (this.ignoreNextInput)
18177 this.ignoreNextInput = false;
18178 this.getAutoCompleter().reset();
18180 else if (this.completeAsYouType)
18181 selectRangeCallback = this.getAutoCompleter().complete(currentPanel.context, this.input, false);
18183 this.getAutoCompleter().reset();
18185 Firebug.Editor.update();
18187 if (selectRangeCallback)
18189 // We need to select the editor text after calling update in Safari/Chrome,
18190 // otherwise the text won't be selected
18192 setTimeout(selectRangeCallback,0);
18194 selectRangeCallback();
18198 onContextMenu: function(event)
18200 cancelEvent(event);
18202 var popup = $("fbInlineEditorPopup");
18203 FBL.eraseNode(popup);
18205 var target = event.target || event.srcElement;
18206 var menu = this.getContextMenuItems(target);
18209 for (var i = 0; i < menu.length; ++i)
18210 FBL.createMenuItem(popup, menu[i]);
18213 if (!popup.firstChild)
18216 popup.openPopupAtScreen(event.screenX, event.screenY, true);
18220 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
18222 updateLayout: function(initial, forceAll, extraWidth)
18224 if (this.fixedWidth)
18226 this.box.style.left = (this.targetOffset.x) + "px";
18227 this.box.style.top = (this.targetOffset.y) + "px";
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";
18236 if (initial || forceAll)
18238 this.box.style.left = this.targetOffset.x + "px";
18239 this.box.style.top = this.targetOffset.y + "px";
18242 var approxTextWidth = this.textSize.width;
18243 var maxWidth = (currentPanel.panelNode.scrollWidth - this.targetOffset.x)
18244 - this.outerMargin;
18246 var wrapped = initial
18247 ? this.noWrap && this.targetSize.height > this.textSize.height+3
18248 : this.noWrap && approxTextWidth > maxWidth;
18253 this.target.currentStyle :
18254 this.target.ownerDocument.defaultView.getComputedStyle(this.target, "");
18256 targetMargin = parseInt(style.marginLeft) + parseInt(style.marginRight);
18258 // Make the width fit the remaining x-space from the offset to the far right
18259 approxTextWidth = maxWidth - targetMargin;
18261 this.input.style.width = "100%";
18262 this.box.style.width = approxTextWidth + "px";
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;
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
18273 charWidth *= extraWidth;
18275 var inputWidth = approxTextWidth + charWidth;
18283 this.box.style.width = (inputWidth + xDiff) + "px";
18286 this.box.style.width = "auto";
18291 var xDiff = isIE ? 13: this.box.scrollWidth - this.input.offsetWidth;
18292 this.box.style.width = (inputWidth + xDiff) + "px";
18295 this.input.style.width = inputWidth + "px";
18298 this.expander.style.width = approxTextWidth + "px";
18299 this.expander.style.height = Math.max(this.textSize.height-3,0) + "px";
18303 scrollIntoCenterView(this.box, null, true);
18307 // ************************************************************************************************
18310 Firebug.AutoCompleter = function(getExprOffset, getRange, evaluator, selectMode, caseSensitive)
18312 var candidates = null;
18313 var originalValue = null;
18314 var originalOffset = -1;
18315 var lastExpr = null;
18316 var lastOffset = -1;
18317 var exprOffset = 0;
18319 var preParsed = null;
18320 var preExpr = null;
18321 var postExpr = null;
18323 this.revert = function(textBox)
18325 if (originalOffset != -1)
18327 textBox.value = originalValue;
18329 setSelectionRange(textBox, originalOffset, originalOffset);
18341 this.reset = function()
18344 originalValue = null;
18345 originalOffset = -1;
18351 this.complete = function(context, textBox, cycle, reverse)
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;
18358 //var offset = textBox.selectionStart;
18359 var offset = getInputSelectionStart(textBox);
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++;
18365 if (!selectMode && originalOffset != -1)
18366 offset = originalOffset;
18368 if (!candidates || !cycle || offset != lastOffset)
18370 originalOffset = offset;
18371 originalValue = value;
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);
18378 // Find the part of the string that is being completed
18379 var range = getRange ? getRange(parsed, offset-parseStart, context) : null;
18381 range = {start: 0, end: parsed.length-1 };
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;
18392 else if (lastExpr && lastExpr.indexOf(expr) != 0)
18396 else if (lastExpr && lastExpr.length >= expr.length)
18405 lastOffset = offset;
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)
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;
18424 // We can't complete unless we are at the ridge edge
18429 var values = evaluator(preExpr, expr, postExpr, context);
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
18441 for (var i = 0; i < values.length; ++i)
18443 var name = values[i];
18444 if (name.indexOf && name.indexOf(expr) == 0)
18445 candidates.push(name);
18450 var lowerExpr = caseSensitive ? expr : expr.toLowerCase();
18451 for (var i = 0; i < values.length; ++i)
18453 var name = values[i];
18454 if (name.indexOf && name.toLowerCase().indexOf(lowerExpr) == 0)
18455 candidates.push(name);
18459 lastIndex = reverse ? candidates.length-1 : 0;
18461 else if (searchExpr)
18463 var searchIndex = -1;
18465 // Find the first instance of searchExpr in the values list. We
18466 // will then complete the string that is found
18469 searchIndex = values.indexOf(expr);
18473 var lowerExpr = searchExpr.toLowerCase();
18474 for (var i = 0; i < values.length; ++i)
18476 var name = values[i];
18477 if (name && name.toLowerCase().indexOf(lowerExpr) == 0)
18485 // Nothing found, so there's nothing to complete to
18486 if (searchIndex == -1)
18487 return this.reset();
18490 candidates = cloneArray(values);
18491 lastIndex = searchIndex;
18497 for (var i = 0; i < values.length; ++i)
18499 if (values[i].substr)
18500 candidates.push(values[i]);
18509 lastIndex += reverse ? -1 : 1;
18512 if (!candidates.length)
18515 if (lastIndex >= candidates.length)
18517 else if (lastIndex < 0)
18518 lastIndex = candidates.length-1;
18520 var completion = candidates[lastIndex];
18521 var preCompletion = expr.substr(0, offset-exprOffset);
18522 var postCompletion = completion.substr(offset-exprOffset);
18524 textBox.value = preParsed + preExpr + preCompletion + postCompletion + postExpr;
18525 var offsetEnd = preParsed.length + preExpr.length + completion.length;
18527 // TODO: xxxpedro remove the following commented code, if the lib.setSelectionRange()
18528 // is working well.
18530 if (textBox.setSelectionRange)
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(){
18537 textBox.setSelectionRange(offset, offsetEnd);
18539 textBox.setSelectionRange(offsetEnd, offsetEnd);
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)
18548 setTimeout(function(){
18550 setSelectionRange(textBox, offset, offsetEnd);
18552 setSelectionRange(textBox, offsetEnd, offsetEnd);
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).
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).
18566 //console.log("autocomplete ", textBox, offset, offsetEnd);
18569 setSelectionRange(textBox, offset, offsetEnd);
18571 setSelectionRange(textBox, offsetEnd, offsetEnd);
18577 // ************************************************************************************************
18580 var getDefaultEditor = function getDefaultEditor(panel)
18582 if (!defaultEditor)
18584 var doc = panel.document;
18585 defaultEditor = new Firebug.InlineEditor(doc);
18588 return defaultEditor;
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.
18598 var getOutsider = function getOutsider(element, group, stepper)
18600 var parentGroup = getAncestorByClass(group.parentNode, "editGroup");
18604 next = stepper(next || element);
18606 while (isAncestor(next, group) || isGroupInsert(next, parentGroup));
18611 var isGroupInsert = function isGroupInsert(next, group)
18613 return (!group || isAncestor(next, group))
18614 && (hasClass(next, "insertBefore") || hasClass(next, "insertAfter"));
18617 var getNextOutsider = function getNextOutsider(element, group)
18619 return getOutsider(element, group, bind(getNextByClass, FBL, "editable"));
18622 var getPreviousOutsider = function getPreviousOutsider(element, group)
18624 return getOutsider(element, group, bind(getPreviousByClass, FBL, "editable"));
18627 var getInlineParent = function getInlineParent(element)
18629 var lastInline = element;
18630 for (; element; element = element.parentNode)
18632 //var s = element.ownerDocument.defaultView.getComputedStyle(element, "");
18634 element.currentStyle :
18635 element.ownerDocument.defaultView.getComputedStyle(element, "");
18637 if (s.display != "inline")
18640 lastInline = element;
18645 var insertTab = function insertTab()
18647 insertTextIntoElement(currentEditor.input, Firebug.Editor.tabCharacter);
18650 // ************************************************************************************************
18652 Firebug.registerModule(Firebug.Editor);
18654 // ************************************************************************************************
18659 /* See license.txt for terms of usage */
18661 FBL.ns(function() { with (FBL) {
18662 // ************************************************************************************************
18664 if (Env.Options.disableXHRListener)
18667 // ************************************************************************************************
18670 var XHRSpy = function()
18672 this.requestHeaders = [];
18673 this.responseHeaders = [];
18690 responseText: null,
18692 requestHeaders: null,
18693 responseHeaders: null,
18695 sourceLink: null, // {href:"file.html", line: 22}
18703 // ************************************************************************************************
18704 // XMLHttpRequestWrapper
18706 var XMLHttpRequestWrapper = function(activeXObject)
18708 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
18709 // XMLHttpRequestWrapper internal variables
18711 var xhrRequest = typeof activeXObject != "undefined" ?
18713 new _XMLHttpRequest(),
18715 spy = new XHRSpy(),
18723 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
18724 // XMLHttpRequestWrapper internal methods
18726 var updateSelfPropertiesIgnore = {
18729 getAllResponseHeaders: 1,
18731 getResponseHeader: 1,
18732 mozBackgroundRequest: 1,
18734 onreadystatechange: 1,
18737 setRequestHeader: 1
18740 var updateSelfProperties = function()
18742 if (supportsXHRIterator)
18744 for (var propName in xhrRequest)
18746 if (propName in updateSelfPropertiesIgnore)
18751 var propValue = xhrRequest[propName];
18753 if (propValue && !isFunction(propValue))
18754 self[propName] = propValue;
18758 //console.log(propName, E.message);
18764 // will fail to read these xhrRequest properties if the request is not completed
18765 if (xhrRequest.readyState == 4)
18767 self.status = xhrRequest.status;
18768 self.statusText = xhrRequest.statusText;
18769 self.responseText = xhrRequest.responseText;
18770 self.responseXML = xhrRequest.responseXML;
18775 var updateXHRPropertiesIgnore = {
18777 onreadystatechange: 1,
18787 var updateXHRProperties = function()
18789 for (var propName in self)
18791 if (propName in updateXHRPropertiesIgnore)
18796 var propValue = self[propName];
18798 if (propValue && !xhrRequest[propName])
18800 xhrRequest[propName] = propValue;
18805 //console.log(propName, E.message);
18810 var logXHR = function()
18812 var row = Firebug.Console.log(spy, null, "spy", Firebug.Spy.XHR);
18816 setClass(row, "loading");
18821 var finishXHR = function()
18823 var duration = new Date().getTime() - reqStartTS;
18824 var success = xhrRequest.status == 200;
18826 var responseHeadersText = xhrRequest.getAllResponseHeaders();
18827 var responses = responseHeadersText ? responseHeadersText.split(/[\n\r]/) : [];
18828 var reHeader = /^(\S+):\s*(.*)/;
18830 for (var i=0, l=responses.length; i<l; i++)
18832 var text = responses[i];
18833 var match = text.match(reHeader);
18837 var name = match[1];
18838 var value = match[2];
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;
18846 if (name == "Last Modified")
18848 if (!spy.cacheEntry)
18849 spy.cacheEntry = [];
18851 spy.cacheEntry.push({
18858 spy.responseHeaders.push({
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,
18875 setTimeout(function(){
18877 spy.responseText = xhrRequest.responseText;
18879 // update row information to avoid "ethernal spinning gif" bug in IE
18880 row = row || spy.logRow;
18882 // if chrome document is not loaded, there will be no row yet, so just ignore
18885 // update the XHR representation data
18886 handleRequestStatus(success, status, time);
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;
18899 updateSelfProperties();
18902 var handleStateChange = function()
18904 //Firebug.Console.log(["onreadystatechange", xhrRequest.readyState, xhrRequest.readyState == 4 && xhrRequest.status]);
18906 self.readyState = xhrRequest.readyState;
18908 if (xhrRequest.readyState == 4)
18912 xhrRequest.onreadystatechange = function(){};
18915 //Firebug.Console.log(spy.url + ": " + xhrRequest.readyState);
18917 self.onreadystatechange();
18920 // update the XHR representation data
18921 var handleRequestStatus = function(success, status, time)
18923 var row = spy.logRow;
18924 FBL.removeClass(row, "loading");
18927 FBL.setClass(row, "error");
18929 var item = FBL.$$(".spyStatus", row)[0];
18930 item.innerHTML = status;
18934 var item = FBL.$$(".spyTime", row)[0];
18935 item.innerHTML = time + "ms";
18939 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
18940 // XMLHttpRequestWrapper public properties and handlers
18942 this.readyState = 0;
18944 this.onreadystatechange = function(){};
18946 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
18947 // XMLHttpRequestWrapper public methods
18949 this.open = function(method, url, async, user, password)
18951 //Firebug.Console.log("xhrRequest open");
18953 updateSelfProperties();
18956 spy = new XHRSpy();
18958 spy.method = method;
18962 spy.xhrRequest = xhrRequest;
18963 spy.urlParams = parseURLParamsArray(url);
18967 // xhrRequest.open.apply may not be available in IE
18969 xhrRequest.open.apply(xhrRequest, arguments);
18971 xhrRequest.open(method, url, async, user, password);
18977 xhrRequest.onreadystatechange = handleStateChange;
18981 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
18983 this.send = function(data)
18985 //Firebug.Console.log("xhrRequest send");
18988 reqStartTS = new Date().getTime();
18990 updateXHRProperties();
18994 xhrRequest.send(data);
18998 // TODO: xxxpedro XHR throws or not?
19007 self.readyState = xhrRequest.readyState;
19009 // sometimes an error happens when calling finishXHR()
19010 // Issue 3422: Firebug Lite breaks Google Instant Search
19022 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
19024 this.setRequestHeader = function(header, value)
19026 spy.requestHeaders.push({name: [header], value: [value]});
19027 return xhrRequest.setRequestHeader(header, value);
19031 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
19033 this.abort = function()
19035 xhrRequest.abort();
19036 updateSelfProperties();
19037 handleRequestStatus(false, "Aborted");
19040 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
19042 this.getResponseHeader = function(header)
19044 return xhrRequest.getResponseHeader(header);
19047 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
19049 this.getAllResponseHeaders = function()
19051 return xhrRequest.getAllResponseHeaders();
19055 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
19056 // Clone XHR object
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 &&
19063 typeof xhrRequest.open.apply != "undefined";
19065 var numberOfXHRProperties = 0;
19066 for (var propName in xhrRequest)
19068 numberOfXHRProperties++;
19070 if (propName in updateSelfPropertiesIgnore)
19075 var propValue = xhrRequest[propName];
19077 if (isFunction(propValue))
19079 if (typeof self[propName] == "undefined")
19081 this[propName] = (function(name, xhr){
19083 return supportsApply ?
19084 // if the browser supports apply
19087 return xhr[name].apply(xhr, arguments);
19090 function(a,b,c,d,e)
19092 return xhr[name](a,b,c,d,e);
19095 })(propName, xhrRequest);
19099 this[propName] = propValue;
19103 //console.log(propName, E.message);
19107 // IE6 does not support for (var prop in XHR)
19108 var supportsXHRIterator = numberOfXHRProperties > 0;
19115 // ************************************************************************************************
19116 // ActiveXObject Wrapper (IE6 only)
19118 var _ActiveXObject;
19119 var isIE6 = /msie 6/i.test(navigator.appVersion);
19123 _ActiveXObject = window.ActiveXObject;
19125 var xhrObjects = " MSXML2.XMLHTTP.5.0 MSXML2.XMLHTTP.4.0 MSXML2.XMLHTTP.3.0 MSXML2.XMLHTTP Microsoft.XMLHTTP ";
19127 window.ActiveXObject = function(name)
19133 var activeXObject = new _ActiveXObject(name);
19143 if (xhrObjects.indexOf(" " + name + " ") != -1)
19144 return new XMLHttpRequestWrapper(activeXObject);
19146 return activeXObject;
19149 throw error.message;
19154 // ************************************************************************************************
19156 // Register the XMLHttpRequestWrapper for non-IE6 browsers
19159 var _XMLHttpRequest = XMLHttpRequest;
19160 window.XMLHttpRequest = function()
19162 return new XMLHttpRequestWrapper();
19166 //************************************************************************************************
19168 FBL.getNativeXHRObject = function()
19170 var xhrObj = false;
19173 xhrObj = new _XMLHttpRequest();
19178 "MSXML2.XMLHTTP.5.0", "MSXML2.XMLHTTP.4.0",
19179 "MSXML2.XMLHTTP.3.0", "MSXML2.XMLHTTP", "Microsoft.XMLHTTP"
19182 for ( var i=0; i < progid.length; ++i ) {
19185 xhrObj = new _ActiveXObject(progid[i]);
19200 // ************************************************************************************************
19204 /* See license.txt for terms of usage */
19206 FBL.ns(function() { with (FBL) {
19207 // ************************************************************************************************
19209 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
19211 var reIgnore = /about:|javascript:|resource:|chrome:|jar:/;
19212 var layoutInterval = 300;
19213 var indentWidth = 18;
19215 var cacheSession = null;
19216 var contexts = new Array();
19217 var panelName = "net";
19218 var maxQueueRequests = 500;
19219 //var panelBar1 = $("fbPanelBar1"); // chrome not available at startup
19220 var activeRequests = [];
19222 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
19224 var mimeExtensionMap =
19226 "txt": "text/plain",
19227 "html": "text/html",
19228 "htm": "text/html",
19229 "xhtml": "text/html",
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"
19243 var fileCategories =
19256 var textFileCategories =
19265 var binaryFileCategories =
19271 var mimeCategoryMap =
19273 "text/plain": "txt",
19274 "application/octet-stream": "bin",
19275 "text/html": "html",
19276 "text/xml": "html",
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"
19290 var binaryCategoryMap =
19296 // ************************************************************************************************
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
19304 Firebug.NetMonitor = extend(Firebug.ActivableModule,
19306 dispatchName: "netMonitor",
19308 clear: function(context)
19310 // The user pressed a Clear button so, remove content of the panel...
19311 var panel = context.getPanel(panelName, true);
19316 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
19319 initialize: function()
19323 this.panelName = panelName;
19325 Firebug.ActivableModule.initialize.apply(this, arguments);
19327 if (Firebug.TraceModule)
19328 Firebug.TraceModule.addListener(this.TraceListener);
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();
19335 Firebug.Debugger.addListener(this.DebuggerListener);
19338 shutdown: function()
19342 prefs.removeObserver(Firebug.prefDomain, this, false);
19343 if (Firebug.TraceModule)
19344 Firebug.TraceModule.removeListener(this.TraceListener);
19346 NetHttpObserver.unregisterObserver();
19347 NetHttpActivityObserver.unregisterObserver();
19349 Firebug.Debugger.removeListener(this.DebuggerListener);
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.
19358 Firebug.NetMonitor.NetInfoBody = domplate(Firebug.Rep, new Firebug.Listener(),
19361 DIV({"class": "netInfoBody", _repObject: "$file"},
19362 TAG("$infoTabs", {file: "$file"}),
19363 TAG("$infoBodies", {file: "$file"})
19367 DIV({"class": "netInfoTabs focusRow subFocusRow", "role": "tablist"},
19368 A({"class": "netInfoParamsTab netInfoTab a11yFocus", onclick: "$onClickTab", "role": "tab",
19370 $collapsed: "$file|hideParams"},
19371 $STR("URLParameters")
19373 A({"class": "netInfoHeadersTab netInfoTab a11yFocus", onclick: "$onClickTab", "role": "tab",
19377 A({"class": "netInfoPostTab netInfoTab a11yFocus", onclick: "$onClickTab", "role": "tab",
19379 $collapsed: "$file|hidePost"},
19382 A({"class": "netInfoPutTab netInfoTab a11yFocus", onclick: "$onClickTab", "role": "tab",
19384 $collapsed: "$file|hidePut"},
19387 A({"class": "netInfoResponseTab netInfoTab a11yFocus", onclick: "$onClickTab", "role": "tab",
19389 $collapsed: "$file|hideResponse"},
19392 A({"class": "netInfoCacheTab netInfoTab a11yFocus", onclick: "$onClickTab", "role": "tab",
19394 $collapsed: "$file|hideCache"},
19397 A({"class": "netInfoHtmlTab netInfoTab a11yFocus", onclick: "$onClickTab", "role": "tab",
19399 $collapsed: "$file|hideHtml"},
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")})
19417 DIV({"class": "netInfoHtmlText netInfoText", "role": "tabpanel"},
19418 IFRAME({"class": "netInfoHtmlPreview", "role": "document"})
19423 FOR("param", "$headers",
19424 TR({"role": "listitem"},
19425 TD({"class": "netInfoParamName", "role": "presentation"},
19426 TAG("$param|getNameTag", {param: "$param"})
19428 TD({"class": "netInfoParamValue", "role": "list", "aria-label": "$param.name"},
19429 FOR("line", "$param|getParamValueIterator",
19430 CODE({"class": "focusRow subFocusRow", "role": "listitem"}, "$line")
19437 A({"class": "netInfo$tabId\\Tab netInfoTab", onclick: "$onClickTab", view: "$tabId", "role": "tab"},
19442 DIV({"class": "netInfo$tabId\\Text netInfoText", "role": "tabpanel"}),
19444 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
19447 SPAN("$param|getParamName"),
19449 nameWithTooltipTag:
19450 SPAN({title: "$param.name"}, "$param|getParamName"),
19452 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
19454 getNameTag: function(param)
19456 return (this.getParamName(param) == param.name) ? this.nameTag : this.nameWithTooltipTag;
19459 getParamName: function(param)
19462 var name = param.name;
19463 if (name.length > limit)
19464 name = name.substr(0, limit) + "...";
19468 getParamTitle: function(param)
19471 var name = param.name;
19472 if (name.length > limit)
19477 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
19479 hideParams: function(file)
19481 return !file.urlParams || !file.urlParams.length;
19484 hidePost: function(file)
19486 return file.method.toUpperCase() != "POST";
19489 hidePut: function(file)
19491 return file.method.toUpperCase() != "PUT";
19494 hideResponse: function(file)
19497 //return file.category in binaryFileCategories;
19500 hideCache: function(file)
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";
19507 hideHtml: function(file)
19509 return (file.mimeType != "text/html") && (file.mimeType != "application/xhtml+xml");
19512 onClickTab: function(event)
19514 this.selectTab(event.currentTarget || event.srcElement);
19517 getParamValueIterator: function(param)
19519 // TODO: xxxpedro console2
19520 return param.value;
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);
19530 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
19532 appendTab: function(netInfoBox, tabId, tabTitle)
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]);
19542 selectTabByName: function(netInfoBox, tabName)
19544 var tab = getChildByClass(netInfoBox, "netInfoTabs", "netInfo"+tabName+"Tab");
19546 this.selectTab(tab);
19549 selectTab: function(tab)
19551 var view = tab.getAttribute("view");
19553 var netInfoBox = getAncestorByClass(tab, "netInfoBody");
19555 var selectedTab = netInfoBox.selectedTab;
19559 //netInfoBox.selectedText.removeAttribute("selected");
19560 removeClass(netInfoBox.selectedText, "netInfoTextSelected");
19562 removeClass(selectedTab, "netInfoTabSelected");
19563 //selectedTab.removeAttribute("selected");
19564 selectedTab.setAttribute("aria-selected", "false");
19567 var textBodyName = "netInfo" + view + "Text";
19569 selectedTab = netInfoBox.selectedTab = tab;
19571 netInfoBox.selectedText = $$("."+textBodyName, netInfoBox)[0];
19572 //netInfoBox.selectedText = netInfoBox.getElementsByClassName(textBodyName).item(0);
19574 //netInfoBox.selectedText.setAttribute("selected", "true");
19575 setClass(netInfoBox.selectedText, "netInfoTextSelected");
19577 setClass(selectedTab, "netInfoTabSelected");
19578 selectedTab.setAttribute("selected", "true");
19579 selectedTab.setAttribute("aria-selected", "true");
19581 var file = Firebug.getRepObject(netInfoBox);
19583 //var context = Firebug.getElementPanel(netInfoBox).context;
19584 var context = Firebug.chrome;
19586 this.updateInfo(netInfoBox, file, context);
19589 updateInfo: function(netInfoBox, file, context)
19591 if (FBTrace.DBG_NET)
19592 FBTrace.sysout("net.updateInfo; file", file);
19596 if (FBTrace.DBG_NET || FBTrace.DBG_ERRORS)
19597 FBTrace.sysout("net.updateInfo; ERROR netInfo == null " + file.href, file);
19601 var tab = netInfoBox.selectedTab;
19603 if (hasClass(tab, "netInfoParamsTab"))
19605 if (file.urlParams && !netInfoBox.urlParamsPresented)
19607 netInfoBox.urlParamsPresented = true;
19608 this.insertHeaderRows(netInfoBox, file.urlParams, "Params");
19612 else if (hasClass(tab, "netInfoHeadersTab"))
19614 var headersText = $$(".netInfoHeadersText", netInfoBox)[0];
19615 //var headersText = netInfoBox.getElementsByClassName("netInfoHeadersText").item(0);
19617 if (file.responseHeaders && !netInfoBox.responseHeadersPresented)
19619 netInfoBox.responseHeadersPresented = true;
19620 NetInfoHeaders.renderHeaders(headersText, file.responseHeaders, "ResponseHeaders");
19623 if (file.requestHeaders && !netInfoBox.requestHeadersPresented)
19625 netInfoBox.requestHeadersPresented = true;
19626 NetInfoHeaders.renderHeaders(headersText, file.requestHeaders, "RequestHeaders");
19630 else if (hasClass(tab, "netInfoPostTab"))
19632 if (!netInfoBox.postPresented)
19634 netInfoBox.postPresented = true;
19635 //var postText = netInfoBox.getElementsByClassName("netInfoPostText").item(0);
19636 var postText = $$(".netInfoPostText", netInfoBox)[0];
19637 NetInfoPostData.render(context, postText, file);
19641 else if (hasClass(tab, "netInfoPutTab"))
19643 if (!netInfoBox.putPresented)
19645 netInfoBox.putPresented = true;
19646 //var putText = netInfoBox.getElementsByClassName("netInfoPutText").item(0);
19647 var putText = $$(".netInfoPutText", netInfoBox)[0];
19648 NetInfoPostData.render(context, putText, file);
19652 else if (hasClass(tab, "netInfoResponseTab") && file.loaded && !netInfoBox.responsePresented)
19654 ///var responseTextBox = netInfoBox.getElementsByClassName("netInfoResponseText").item(0);
19655 var responseTextBox = $$(".netInfoResponseText", netInfoBox)[0];
19656 if (file.category == "image")
19658 netInfoBox.responsePresented = true;
19660 var responseImage = netInfoBox.ownerDocument.createElement("img");
19661 responseImage.src = file.href;
19663 clearNode(responseTextBox);
19664 responseTextBox.appendChild(responseImage, responseTextBox);
19666 else ///if (!(binaryCategoryMap.hasOwnProperty(file.category)))
19668 this.setResponseText(file, netInfoBox, responseTextBox, context);
19672 else if (hasClass(tab, "netInfoCacheTab") && file.loaded && !netInfoBox.cachePresented)
19674 var responseTextBox = netInfoBox.getElementsByClassName("netInfoCacheText").item(0);
19675 if (file.cacheEntry) {
19676 netInfoBox.cachePresented = true;
19677 this.insertHeaderRows(netInfoBox, file.cacheEntry, "Cache");
19681 else if (hasClass(tab, "netInfoHtmlTab") && file.loaded && !netInfoBox.htmlPresented)
19683 netInfoBox.htmlPresented = true;
19685 var text = Utils.getResponseText(file, context);
19687 ///var iframe = netInfoBox.getElementsByClassName("netInfoHtmlPreview").item(0);
19688 var iframe = $$(".netInfoHtmlPreview", netInfoBox)[0];
19690 ///iframe.contentWindow.document.body.innerHTML = text;
19692 // TODO: xxxpedro net - remove scripts
19693 var reScript = /<script(.|\s)*?\/script>/gi;
19695 text = text.replace(reScript, "");
19697 iframe.contentWindow.document.write(text);
19698 iframe.contentWindow.document.close();
19701 // Notify listeners about update so, content of custom tabs can be updated.
19702 dispatch(NetInfoBody.fbListeners, "updateTabBody", [netInfoBox, file, context]);
19705 setResponseText: function(file, netInfoBox, responseTextBox, context)
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.
19714 responseTextBox.style.whiteSpace = "nowrap";
19717 typeof responseTextBox.textContent != "undefined" ?
19720 ] = file.responseText;
19723 //**********************************************
19724 //**********************************************
19725 //**********************************************
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;
19732 text = text.substr(0, limit) + "...";
19734 // Insert the response into the UI.
19736 insertWrappedText(text, responseTextBox);
19738 insertWrappedText("", responseTextBox);
19740 // Append a message informing the user that the response isn't fully displayed.
19744 text: $STR("net.responseSizeLimitMessage"),
19745 onClickLink: function() {
19746 var panel = context.getPanel("net", true);
19747 panel.openResponseInTab(file);
19750 Firebug.NetMonitor.ResponseSizeLimit.append(object, responseTextBox);
19753 netInfoBox.responsePresented = true;
19755 if (FBTrace.DBG_NET)
19756 FBTrace.sysout("net.setResponseText; response text updated");
19759 insertHeaderRows: function(netInfoBox, headers, tableName, rowName)
19761 if (!headers.length)
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");
19768 tbody = headersTable.firstChild;
19769 var titleRow = getChildByClass(tbody, "netInfo" + rowName + "Title");
19771 this.headerDataTag.insertRows({headers: headers}, titleRow ? titleRow : tbody);
19772 removeClass(titleRow, "collapsed");
19776 var NetInfoBody = Firebug.NetMonitor.NetInfoBody;
19778 // ************************************************************************************************
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.
19784 Firebug.NetMonitor.NetInfoHeaders = domplate(Firebug.Rep, //new Firebug.Listener(),
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")
19795 TABLE({cellpadding: 0, cellspacing: 0},
19796 TBODY({"class": "netInfoResponseHeadersBody", "role": "list",
19797 "aria-label": $STR("ResponseHeaders")})
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")
19806 TABLE({cellpadding: 0, cellspacing: 0},
19807 TBODY({"class": "netInfoRequestHeadersBody", "role": "list",
19808 "aria-label": $STR("RequestHeaders")})
19813 TR({"role": "presentation"},
19814 TD({colspan: 2, "role": "presentation"},
19815 PRE({"class": "source"})
19819 onViewSource: function(event)
19821 var target = event.target;
19822 var requestHeaders = (target.rowName == "RequestHeaders");
19824 var netInfoBox = getAncestorByClass(target, "netInfoBody");
19825 var file = netInfoBox.repObject;
19827 if (target.sourceDisplayed)
19829 var headers = requestHeaders ? file.requestHeaders : file.responseHeaders;
19830 this.insertHeaderRows(netInfoBox, headers, target.rowName);
19831 target.innerHTML = $STR("net.headers.view source");
19835 var source = requestHeaders ? file.requestHeadersText : file.responseHeadersText;
19836 this.insertSource(netInfoBox, source, target.rowName);
19837 target.innerHTML = $STR("net.headers.pretty print");
19840 target.sourceDisplayed = !target.sourceDisplayed;
19842 cancelEvent(event);
19845 insertSource: function(netInfoBox, source, rowName)
19847 // This breaks copy to clipboard.
19849 // source = source.replace(/\r\n/gm, "<span style='color:lightgray'>\\r\\n</span>\r\n");
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;
19859 insertHeaderRows: function(netInfoBox, headers, rowName)
19861 var headersTable = $$(".netInfoHeadersTable", netInfoBox)[0];
19862 var tbody = $$(".netInfo" + rowName + "Body", headersTable)[0];
19864 //var headersTable = netInfoBox.getElementsByClassName("netInfoHeadersTable").item(0);
19865 //var tbody = headersTable.getElementsByClassName("netInfo" + rowName + "Body").item(0);
19869 if (!headers.length)
19872 NetInfoBody.headerDataTag.insertRows({headers: headers}, tbody);
19874 var titleRow = getChildByClass(headersTable, "netInfo" + rowName + "Title");
19875 removeClass(titleRow, "collapsed");
19878 init: function(parent)
19880 var rootNode = this.tag.append({}, parent);
19882 var netInfoBox = getAncestorByClass(parent, "netInfoBody");
19883 var file = netInfoBox.repObject;
19887 viewSource = $$(".request", rootNode)[0];
19888 //viewSource = rootNode.getElementsByClassName("netHeadersViewSource request").item(0);
19889 if (file.requestHeadersText)
19890 removeClass(viewSource, "collapsed");
19892 viewSource = $$(".response", rootNode)[0];
19893 //viewSource = rootNode.getElementsByClassName("netHeadersViewSource response").item(0);
19894 if (file.responseHeadersText)
19895 removeClass(viewSource, "collapsed");
19898 renderHeaders: function(parent, headers, rowName)
19900 if (!parent.firstChild)
19903 this.insertHeaderRows(parent, headers, rowName);
19907 var NetInfoHeaders = Firebug.NetMonitor.NetInfoHeaders;
19909 // ************************************************************************************************
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.
19915 Firebug.NetMonitor.NetInfoPostData = domplate(Firebug.Rep, /*new Firebug.Listener(),*/
19917 // application/x-www-form-urlencoded
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"
19934 // multipart/form-data
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"
19951 // application/json
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")
19965 TD({"class": "netInfoPostJSONBody"})
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")
19982 TD({"class": "netInfoPostXMLBody"})
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")
20001 TR({"role": "presentation"},
20002 TD({colspan: 2, "role": "presentation"},
20003 FOR("line", "$param|getParamValueIterator",
20004 CODE({"class":"focusRow subFocusRow" , "role": "listitem"},"$line")
20009 getParamValueIterator: function(param)
20011 return NetInfoBody.getParamValueIterator(param);
20014 render: function(context, parentNode, file)
20017 var spy = getAncestorByClass(parentNode, "spyHead");
20018 var spyObject = spy.repObject;
20019 var data = spyObject.data;
20021 ///var contentType = Utils.findHeader(file.requestHeaders, "content-type");
20022 var contentType = file.mimeType;
20024 ///var text = Utils.getPostText(file, context, true);
20025 ///if (text == undefined)
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)
20033 ///var lines = text.split("\n");
20034 ///var params = parseURLEncodedText(lines[lines.length-1]);
20035 var params = parseURLEncodedTextArray(data);
20037 this.insertParameters(parentNode, params);
20040 ///if (Utils.isMultiPartRequest(file, context))
20042 /// var data = this.parseMultiPartText(file, context);
20044 /// this.insertParts(parentNode, data);
20047 // moved to the top
20048 ///var contentType = Utils.findHeader(file.requestHeaders, "content-type");
20050 ///if (Firebug.JSONViewerModel.isJSON(contentType))
20055 if (Firebug.JSONViewerModel.isJSON(contentType, data))
20056 ///this.insertJSON(parentNode, file, context);
20057 this.insertJSON(parentNode, jsonData, context);
20059 ///if (Firebug.XMLViewerModel.isXML(contentType))
20060 /// this.insertXML(parentNode, file, context);
20062 ///var postText = Utils.getPostText(file, context);
20063 ///postText = Utils.formatPostText(postText);
20064 var postText = data;
20066 this.insertSource(parentNode, postText);
20069 insertParameters: function(parentNode, params)
20071 if (!params || !params.length)
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);
20079 var tbody = paramTable.getElementsByTagName("tbody")[0];
20081 NetInfoBody.headerDataTag.insertRows({headers: params}, row);
20084 insertParts: function(parentNode, data)
20086 if (!data.params || !data.params.length)
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);
20094 NetInfoBody.headerDataTag.insertRows({headers: data.params}, row);
20097 insertJSON: function(parentNode, file, context)
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);
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];
20114 Firebug.DOMPanel.DirTable.tag.replace(
20115 {object: data, toggles: this.toggles}, jsonBody);
20118 insertXML: function(parentNode, file, context)
20120 var text = Utils.getPostText(file, context);
20122 var jsonTable = this.xmlTable.append(null, parentNode);
20123 ///var jsonBody = jsonTable.getElementsByClassName("netInfoPostXMLBody").item(0);
20124 var jsonBody = $$(".netInfoPostXMLBody", jsonTable)[0];
20126 Firebug.XMLViewerModel.insertXML(jsonBody, text);
20129 insertSource: function(parentNode, text)
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);
20136 var param = {value: [text]};
20137 this.sourceBodyTag.insertRows({param: param}, row);
20140 parseMultiPartText: function(file, context)
20142 var text = Utils.getPostText(file, context);
20143 if (text == undefined)
20146 FBTrace.sysout("net.parseMultiPartText; boundary: ", text);
20148 var boundary = text.match(/\s*boundary=\s*(.*)/)[1];
20150 var divider = "\r\n\r\n";
20151 var bodyStart = text.indexOf(divider);
20152 var body = text.substr(bodyStart + divider.length);
20155 postData.mimeType = "multipart/form-data";
20156 postData.params = [];
20158 var parts = body.split("--" + boundary);
20159 for (var i=0; i<parts.length; i++)
20161 var part = parts[i].split(divider);
20162 if (part.length != 2)
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])
20176 var NetInfoPostData = Firebug.NetMonitor.NetInfoPostData;
20178 // ************************************************************************************************
20181 // TODO: xxxpedro net i18n
20182 var $STRP = function(a){return a;};
20184 Firebug.NetMonitor.NetLimit = domplate(Firebug.Rep,
20190 TABLE({width: "100%", cellpadding: 0, cellspacing: 0},
20196 TR({"class": "netRow netLimitRow", $collapsed: "$isCollapsed"},
20197 TD({"class": "netCol netLimitCol", colspan: 6},
20198 TABLE({cellpadding: 0, cellspacing: 0},
20202 SPAN({"class": "netLimitLabel"},
20203 $STRP("plural.Limit_Exceeded", [0])
20206 TD({style: "width:100%"}),
20208 BUTTON({"class": "netLimitButton", title: "$limitPrefsTitle",
20209 onclick: "$onPreferences"},
20220 isCollapsed: function()
20222 return this.collapsed;
20225 onPreferences: function(event)
20227 openNewTab("about:config");
20230 updateCounter: function(row)
20232 removeClass(row, "collapsed");
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]);
20239 createTable: function(parent, limitInfo)
20241 var table = this.tableTag.replace({}, parent);
20242 var row = this.createRow(table.firstChild.firstChild, limitInfo);
20243 return [table, row];
20246 createRow: function(parent, limitInfo)
20248 var row = this.limitTag.insertRows(limitInfo, parent, this)[0];
20249 row.limitInfo = limitInfo;
20254 observe: function(subject, topic, data)
20256 // We're observing preferences only.
20257 if (topic != "nsPref:changed")
20260 if (data.indexOf("net.logLimit") != -1)
20261 this.updateMaxLimit();
20264 updateMaxLimit: function()
20266 var value = Firebug.getPref(Firebug.prefDomain, "net.logLimit");
20267 maxQueueRequests = value ? value : maxQueueRequests;
20271 var NetLimit = Firebug.NetMonitor.NetLimit;
20273 // ************************************************************************************************
20275 Firebug.NetMonitor.ResponseSizeLimit = domplate(Firebug.Rep,
20278 DIV({"class": "netInfoResponseSizeLimit"},
20279 SPAN("$object.beforeLink"),
20280 A({"class": "objectLink", onclick: "$onClickLink"},
20283 SPAN("$object.afterLink")
20286 reLink: /^(.*)<a>(.*)<\/a>(.*$)/,
20287 append: function(obj, parent)
20289 var m = obj.text.match(this.reLink);
20290 return this.tag.append({onClickLink: obj.onClickLink,
20299 // ************************************************************************************************
20300 // ************************************************************************************************
20302 Firebug.NetMonitor.Utils =
20304 findHeader: function(headers, name)
20309 name = name.toLowerCase();
20310 for (var i = 0; i < headers.length; ++i)
20312 var headerName = headers[i].name.toLowerCase();
20313 if (headerName == name)
20314 return headers[i].value;
20318 formatPostText: function(text)
20320 if (text instanceof XMLDocument)
20321 return getElementXML(text.documentElement);
20326 getPostText: function(file, context, noLimit)
20328 if (!file.postText)
20330 file.postText = readPostTextFromRequest(file.request, context);
20332 if (!file.postText && context)
20333 file.postText = readPostTextFromPage(file.href, context);
20336 if (!file.postText)
20337 return file.postText;
20339 var limit = Firebug.netDisplayedPostBodyLimit;
20340 if (file.postText.length > limit && !noLimit)
20342 return cropString(file.postText, limit,
20343 "\n\n... " + $STR("net.postDataSizeLimitMessage") + " ...\n\n");
20346 return file.postText;
20349 getResponseText: function(file, context)
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);
20356 isURLEncodedRequest: function(file, context)
20358 var text = Utils.getPostText(file, context);
20359 if (text && text.toLowerCase().indexOf("content-type: application/x-www-form-urlencoded") == 0)
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)
20371 isMultiPartRequest: function(file, context)
20373 var text = Utils.getPostText(file, context);
20374 if (text && text.toLowerCase().indexOf("content-type: multipart/form-data") == 0)
20379 getMimeType: function(mimeType, uri)
20381 if (!mimeType || !(mimeCategoryMap.hasOwnProperty(mimeType)))
20383 var ext = getFileExtension(uri);
20388 var extMimeType = mimeExtensionMap[ext.toLowerCase()];
20389 return extMimeType ? extMimeType : mimeType;
20396 getDateFromSeconds: function(s)
20398 var d = new Date();
20403 getHttpHeaders: function(request, file)
20407 var http = QI(request, Ci.nsIHttpChannel);
20408 file.status = request.responseStatus;
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);
20415 if (!file.responseHeaders && Firebug.collectHttpHeaders)
20417 var requestHeaders = [], responseHeaders = [];
20419 http.visitRequestHeaders({
20420 visitHeader: function(name, value)
20422 requestHeaders.push({name: name, value: value});
20425 http.visitResponseHeaders({
20426 visitHeader: function(name, value)
20428 responseHeaders.push({name: name, value: value});
20432 file.requestHeaders = requestHeaders;
20433 file.responseHeaders = responseHeaders;
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);
20445 isXHR: function(request)
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));
20454 return (xhrRequest != null);
20463 getFileCategory: function(file)
20467 if (FBTrace.DBG_NET)
20468 FBTrace.sysout("net.getFileCategory; current: " + file.category + " for: " + file.href, file);
20469 return file.category;
20474 if (FBTrace.DBG_NET)
20475 FBTrace.sysout("net.getFileCategory; XHR for: " + file.href, file);
20476 return file.category = "xhr";
20479 if (!file.mimeType)
20481 var ext = getFileExtension(file.href);
20483 file.mimeType = mimeExtensionMap[ext.toLowerCase()];
20486 /*if (FBTrace.DBG_NET)
20487 FBTrace.sysout("net.getFileCategory; " + mimeCategoryMap[file.mimeType] +
20488 ", mimeType: " + file.mimeType + " for: " + file.href, file);*/
20490 if (!file.mimeType)
20493 // Solve cases when charset is also specified, eg "text/html; charset=UTF-8".
20494 var mimeType = file.mimeType;
20496 mimeType = mimeType.split(";")[0];
20498 return (file.category = mimeCategoryMap[mimeType]);
20502 var Utils = Firebug.NetMonitor.Utils;
20504 // ************************************************************************************************
20506 //Firebug.registerRep(Firebug.NetMonitor.NetRequestTable);
20507 //Firebug.registerActivableModule(Firebug.NetMonitor);
20508 //Firebug.registerPanel(NetPanel);
20510 Firebug.registerModule(Firebug.NetMonitor);
20511 //Firebug.registerRep(Firebug.NetMonitor.BreakpointRep);
20513 // ************************************************************************************************
20517 /* See license.txt for terms of usage */
20519 FBL.ns(function() { with (FBL) {
20521 // ************************************************************************************************
20524 //const Cc = Components.classes;
20525 //const Ci = Components.interfaces;
20527 // List of contexts with XHR spy attached.
20530 // ************************************************************************************************
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
20539 * The module is responsible for attaching/detaching a HTTP Observers when Firebug is
20540 * activated/deactivated for a site.
20542 Firebug.Spy = extend(Firebug.Module,
20543 /** @lends Firebug.Spy */
20545 dispatchName: "spy",
20547 initialize: function()
20549 if (Firebug.TraceModule)
20550 Firebug.TraceModule.addListener(this.TraceListener);
20552 Firebug.Module.initialize.apply(this, arguments);
20555 shutdown: function()
20557 Firebug.Module.shutdown.apply(this, arguments);
20559 if (Firebug.TraceModule)
20560 Firebug.TraceModule.removeListener(this.TraceListener);
20563 initContext: function(context)
20565 context.spies = [];
20567 if (Firebug.showXMLHttpRequests && Firebug.Console.isAlwaysEnabled())
20568 this.attachObserver(context, context.window);
20570 if (FBTrace.DBG_SPY)
20571 FBTrace.sysout("spy.initContext " + contexts.length + " ", context.getName());
20574 destroyContext: function(context)
20576 // For any spies that are in progress, remove our listeners so that they don't leak
20577 this.detachObserver(context, null);
20579 if (FBTrace.DBG_SPY && context.spies.length)
20580 FBTrace.sysout("spy.destroyContext; ERROR There are leaking Spies ("
20581 + context.spies.length + ") " + context.getName());
20583 delete context.spies;
20585 if (FBTrace.DBG_SPY)
20586 FBTrace.sysout("spy.destroyContext " + contexts.length + " ", context.getName());
20589 watchWindow: function(context, win)
20591 if (Firebug.showXMLHttpRequests && Firebug.Console.isAlwaysEnabled())
20592 this.attachObserver(context, win);
20595 unwatchWindow: function(context, win)
20599 // This make sure that the existing context is properly removed from "contexts" array.
20600 this.detachObserver(context, win);
20604 // Get exceptions here sometimes, so let's just ignore them
20605 // since the window is going away anyhow
20610 updateOption: function(name, value)
20612 // XXXjjb Honza, if Console.isEnabled(context) false, then this can't be called,
20613 // but somehow seems not correct
20614 if (name == "showXMLHttpRequests")
20616 var tach = value ? this.attachObserver : this.detachObserver;
20617 for (var i = 0; i < TabWatcher.contexts.length; ++i)
20619 var context = TabWatcher.contexts[i];
20620 iterateWindows(context.window, function(win)
20622 tach.apply(this, [context, win]);
20628 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
20629 // Attaching Spy to XHR requests.
20632 * Returns false if Spy should not be attached to XHRs executed by the specified window.
20634 skipSpy: function(win)
20639 // Don't attach spy to chrome.
20640 var uri = safeGetWindowLocation(win);
20641 if (uri && (uri.indexOf("about:") == 0 || uri.indexOf("chrome:") == 0))
20645 attachObserver: function(context, win)
20647 if (Firebug.Spy.skipSpy(win))
20650 for (var i=0; i<contexts.length; ++i)
20652 if ((contexts[i].context == context) && (contexts[i].win == win))
20656 // Register HTTP observers only once.
20657 if (contexts.length == 0)
20659 httpObserver.addObserver(SpyHttpObserver, "firebug-http-event", false);
20660 SpyHttpActivityObserver.registerObserver();
20663 contexts.push({context: context, win: win});
20665 if (FBTrace.DBG_SPY)
20666 FBTrace.sysout("spy.attachObserver (HTTP) " + contexts.length + " ", context.getName());
20669 detachObserver: function(context, win)
20671 for (var i=0; i<contexts.length; ++i)
20673 if (contexts[i].context == context)
20675 if (win && (contexts[i].win != win))
20678 contexts.splice(i, 1);
20680 // If no context is using spy, remvove the (only one) HTTP observer.
20681 if (contexts.length == 0)
20683 httpObserver.removeObserver(SpyHttpObserver, "firebug-http-event");
20684 SpyHttpActivityObserver.unregisterObserver();
20687 if (FBTrace.DBG_SPY)
20688 FBTrace.sysout("spy.detachObserver (HTTP) " + contexts.length + " ",
20689 context.getName());
20696 * Return XHR object that is associated with specified request <i>nsIHttpChannel</i>.
20697 * Returns null if the request doesn't represent XHR.
20699 getXHR: function(request)
20701 // Does also query-interface for nsIHttpChannel.
20702 if (!(request instanceof Ci.nsIHttpChannel))
20707 var callbacks = request.notificationCallbacks;
20708 return (callbacks ? callbacks.getInterface(Ci.nsIXMLHttpRequest) : null);
20712 if (exc.name == "NS_NOINTERFACE")
20714 if (FBTrace.DBG_SPY)
20715 FBTrace.sysout("spy.getXHR; Request is not nsIXMLHttpRequest: " +
20716 safeGetRequestName(request));
20728 // ************************************************************************************************
20731 function getSpyForXHR(request, xhrRequest, context, noCreate)
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++)
20740 spy = context.spies[i];
20741 if (spy.request == request)
20748 spy = new Firebug.Spy.XMLHttpRequestSpy(request, xhrRequest, context);
20749 context.spies.push(spy);
20751 var name = request.URI.asciiSpec;
20752 var origName = request.originalURI.asciiSpec;
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)
20759 if (FBTrace.DBG_SPY)
20760 FBTrace.sysout("spy.getSpyForXHR; New spy object created (" +
20761 (name == origName ? "new XHR" : "redirected XHR") + ") for: " + name, spy);
20767 // ************************************************************************************************
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.)
20775 Firebug.Spy.XMLHttpRequestSpy = function(request, xhrRequest, context)
20777 this.request = request;
20778 this.xhrRequest = xhrRequest;
20779 this.context = context;
20780 this.responseText = "";
20782 // For compatibility with the Net templates.
20785 // Support for activity-observer
20786 this.transactionStarted = false;
20787 this.transactionClosed = false;
20791 //Firebug.Spy.XMLHttpRequestSpy.prototype =
20792 /** @lends Firebug.Spy.XMLHttpRequestSpy */
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); };
20803 // xxxHonza: #502959 is still failing on Fx 3.5
20804 // Use activity distributor to identify 3.6
20805 if (SpyHttpActivityObserver.getActivityDistributor())
20807 this.onreadystatechange = this.xhrRequest.onreadystatechange;
20808 this.xhrRequest.onreadystatechange = this.onReadyStateChange;
20811 this.xhrRequest.addEventListener("load", this.onLoad, false);
20812 this.xhrRequest.addEventListener("error", this.onError, false);
20813 this.xhrRequest.addEventListener("abort", this.onAbort, false);
20815 // xxxHonza: should be removed from FB 3.6
20816 if (!SpyHttpActivityObserver.getActivityDistributor())
20817 this.context.sourceCache.addListener(this);
20822 // Bubble out if already detached.
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.
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)
20839 if (FBTrace.DBG_SPY)
20840 FBTrace.sysout("spy.detach; " + this.href);
20842 // Remove itself from the list of active spies.
20843 remove(this.context.spies, this);
20845 if (this.onreadystatechange)
20846 this.xhrRequest.onreadystatechange = this.onreadystatechange;
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) {}
20852 this.onreadystatechange = null;
20853 this.onLoad = null;
20854 this.onError = null;
20855 this.onAbort = null;
20857 // xxxHonza: shouuld be removed from FB 1.6
20858 if (!SpyHttpActivityObserver.getActivityDistributor())
20859 this.context.sourceCache.removeListener(this);
20864 return this.xhrRequest.channel ? this.xhrRequest.channel.name : this.href;
20868 onStopRequest: function(context, request, responseText)
20873 if (request == this.request)
20874 this.responseText = responseText;
20878 // ************************************************************************************************
20880 function onHTTPSpyReadyStateChange(spy, event)
20882 if (FBTrace.DBG_SPY)
20883 FBTrace.sysout("spy.onHTTPSpyReadyStateChange " + spy.xhrRequest.readyState +
20884 " (multipart: " + spy.xhrRequest.multipart + ")");
20886 // Remember just in case spy is detached (readyState == 4).
20887 var originalHandler = spy.onreadystatechange;
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)
20893 var netInfoBox = getChildByClass(spy.logRow, "spyHead", "netInfoBody");
20896 netInfoBox.htmlPresented = false;
20897 netInfoBox.responsePresented = false;
20901 // If the request is loading update the end time.
20902 if (spy.xhrRequest.readyState == 3)
20904 spy.responseTime = spy.endTime - spy.sendTime;
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)
20912 // Cumulate response so, multipart response content is properly displayed.
20913 if (SpyHttpActivityObserver.getActivityDistributor())
20914 spy.responseText += spy.xhrRequest.responseText;
20917 // xxxHonza: remove from FB 1.6
20918 if (!spy.responseText)
20919 spy.responseText = spy.xhrRequest.responseText;
20922 // The XHR is loaded now (used also by the activity observer).
20926 updateHttpSpyInfo(spy);
20928 // Notify Net pane about a request beeing loaded.
20929 // xxxHonza: I don't think this is necessary.
20930 var netProgress = spy.context.netProgress;
20932 netProgress.post(netProgress.stopFile, [spy.request, spy.endTime, spy.postText, spy.responseText]);
20934 // Notify registered listeners about finish of the XHR.
20935 dispatch(Firebug.Spy.fbListeners, "onLoad", [spy.context, spy]);
20938 // Pass the event to the original page handler.
20939 callPageHandler(spy, event, originalHandler);
20942 function onHTTPSpyLoad(spy)
20944 if (FBTrace.DBG_SPY)
20945 FBTrace.sysout("spy.onHTTPSpyLoad: " + spy.href, spy);
20947 // Detach must be done in onLoad (not in onreadystatechange) otherwise
20948 // onAbort would not be handled.
20951 // xxxHonza: Still needed for Fx 3.5 (#502959)
20952 if (!SpyHttpActivityObserver.getActivityDistributor())
20953 onHTTPSpyReadyStateChange(spy, null);
20956 function onHTTPSpyError(spy)
20958 if (FBTrace.DBG_SPY)
20959 FBTrace.sysout("spy.onHTTPSpyError; " + spy.href, spy);
20966 removeClass(spy.logRow, "loading");
20967 setClass(spy.logRow, "error");
20971 function onHTTPSpyAbort(spy)
20973 if (FBTrace.DBG_SPY)
20974 FBTrace.sysout("spy.onHTTPSpyAbort: " + spy.href, spy);
20981 removeClass(spy.logRow, "loading");
20982 setClass(spy.logRow, "error");
20985 spy.statusText = "Aborted";
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;
20992 netProgress.post(netProgress.abortFile, [spy.request, spy.endTime, spy.postText, spy.responseText]);
20996 // ************************************************************************************************
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}.
21003 Firebug.Spy.XHR = domplate(Firebug.Rep,
21004 /** @lends Firebug.Spy.XHR */
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"
21017 DIV({"class": "spyFullTitle spyTitle"},
21018 "$object|getFullUri"
21021 TD({"class": "spyCol"},
21022 DIV({"class": "spyStatus"}, "$object|getStatus")
21024 TD({"class": "spyCol"},
21025 SPAN({"class": "spyIcon"})
21027 TD({"class": "spyCol"},
21028 SPAN({"class": "spyTime"})
21030 TD({"class": "spyCol"},
21031 TAG(FirebugReps.SourceLink.tag, {object: "$object.sourceLink"})
21038 getCaption: function(spy)
21040 return spy.method.toUpperCase() + " " + cropString(spy.getURL(), 100);
21043 getFullUri: function(spy)
21045 return spy.method.toUpperCase() + " " + spy.getURL();
21048 getStatus: function(spy)
21051 if (spy.statusCode)
21052 text += spy.statusCode + " ";
21054 if (spy.statusText)
21055 return text += spy.statusText;
21060 onToggleBody: function(event)
21062 var target = event.currentTarget || event.srcElement;
21063 var logRow = getAncestorByClass(target, "logRow-spy");
21065 if (isLeftClick(event))
21067 toggleClass(logRow, "opened");
21069 var spy = getChildByClass(logRow, "spyHead").repObject;
21070 var spyHeadTable = getAncestorByClass(target, "spyHeadTable");
21072 if (hasClass(logRow, "opened"))
21074 updateHttpSpyInfo(spy, logRow);
21076 spyHeadTable.setAttribute('aria-expanded', 'true');
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');
21088 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
21090 copyURL: function(spy)
21092 copyToClipboard(spy.getURL());
21095 copyParams: function(spy)
21097 var text = spy.postText;
21101 var url = reEncodeURL(spy, text, true);
21102 copyToClipboard(url);
21105 copyResponse: function(spy)
21107 copyToClipboard(spy.responseText);
21110 openInTab: function(spy)
21112 openNewTab(spy.getURL(), spy.postText);
21115 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
21117 supportsObject: function(object)
21119 // TODO: xxxpedro spy xhr
21122 return object instanceof Firebug.Spy.XMLHttpRequestSpy;
21125 browseObject: function(spy, context)
21127 var url = spy.getURL();
21132 getRealObject: function(spy, context)
21134 return spy.xhrRequest;
21137 getContextMenuItems: function(spy)
21140 {label: "CopyLocation", command: bindFixed(this.copyURL, this, spy) }
21146 {label: "CopyLocationParameters", command: bindFixed(this.copyParams, this, spy) }
21151 {label: "CopyResponse", command: bindFixed(this.copyResponse, this, spy) },
21153 {label: "OpenInTab", command: bindFixed(this.openInTab, this, spy) }
21160 // ************************************************************************************************
21162 function updateTime(spy)
21164 var timeBox = spy.logRow.getElementsByClassName("spyTime").item(0);
21165 if (spy.responseTime)
21166 timeBox.textContent = " " + formatTime(spy.responseTime);
21169 function updateLogRow(spy)
21173 var statusBox = spy.logRow.getElementsByClassName("spyStatus").item(0);
21174 statusBox.textContent = Firebug.Spy.XHR.getStatus(spy);
21176 removeClass(spy.logRow, "loading");
21177 setClass(spy.logRow, "loaded");
21181 var errorRange = Math.floor(spy.xhrRequest.status/100);
21182 if (errorRange == 4 || errorRange == 5)
21183 setClass(spy.logRow, "error");
21190 var updateHttpSpyInfo = function updateHttpSpyInfo(spy, logRow)
21192 if (!spy.logRow && logRow)
21193 spy.logRow = logRow;
21195 if (!spy.logRow || !hasClass(spy.logRow, "opened"))
21199 //spy.params = parseURLParams(spy.href+"");
21200 spy.params = parseURLParams(spy.href+"");
21202 if (!spy.requestHeaders)
21203 spy.requestHeaders = getRequestHeaders(spy);
21205 if (!spy.responseHeaders && spy.loaded)
21206 spy.responseHeaders = getResponseHeaders(spy);
21208 var template = Firebug.NetMonitor.NetInfoBody;
21209 var netInfoBox = getChildByClass(spy.logRow, "spyHead", "netInfoBody");
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");
21219 template.updateInfo(netInfoBox, spy, spy.context);
21225 // ************************************************************************************************
21227 function getRequestHeaders(spy)
21231 var channel = spy.xhrRequest.channel;
21232 if (channel instanceof Ci.nsIHttpChannel)
21234 channel.visitRequestHeaders({
21235 visitHeader: function(name, value)
21237 headers.push({name: name, value: value});
21245 function getResponseHeaders(spy)
21251 var channel = spy.xhrRequest.channel;
21252 if (channel instanceof Ci.nsIHttpChannel)
21254 channel.visitResponseHeaders({
21255 visitHeader: function(name, value)
21257 headers.push({name: name, value: value});
21264 if (FBTrace.DBG_SPY || FBTrace.DBG_ERRORS)
21265 FBTrace.sysout("spy.getResponseHeaders; EXCEPTION " +
21266 safeGetRequestName(spy.request), exc);
21272 // ************************************************************************************************
21275 Firebug.registerModule(Firebug.Spy);
21276 //Firebug.registerRep(Firebug.Spy.XHR);
21278 // ************************************************************************************************
21282 /* See license.txt for terms of usage */
21284 FBL.ns(function() { with (FBL) {
21286 // ************************************************************************************************
21288 // List of JSON content types.
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"
21294 "text/javascript": 1,
21295 "text/x-javascript": 1,
21298 "application/json": 1,
21299 "application/x-json": 1,
21300 "application/javascript": 1,
21301 "application/x-javascript": 1,
21302 "application/json-rpc": 1
21305 // ************************************************************************************************
21306 // Model implementation
21308 Firebug.JSONViewerModel = extend(Firebug.Module,
21310 dispatchName: "jsonViewer",
21311 initialize: function()
21313 Firebug.NetMonitor.NetInfoBody.addListener(this);
21315 // Used by Firebug.DOMPanel.DirTable domplate.
21319 shutdown: function()
21321 Firebug.NetMonitor.NetInfoBody.removeListener(this);
21324 initTabBody: function(infoBox, file)
21326 if (FBTrace.DBG_JSONVIEWER)
21327 FBTrace.sysout("jsonviewer.initTabBody", infoBox);
21329 // Let listeners to parse the JSON.
21330 dispatch(this.fbListeners, "onParseJSON", [file]);
21332 // The JSON is still no there, try to parse most common cases.
21333 if (!file.jsonObject)
21335 ///if (this.isJSON(safeGetContentType(file.request), file.responseText))
21336 if (this.isJSON(file.mimeType, file.responseText))
21337 file.jsonObject = this.parseJSON(file);
21340 // The jsonObject is created so, the JSON tab can be displayed.
21341 if (file.jsonObject && hasProperties(file.jsonObject))
21343 Firebug.NetMonitor.NetInfoBody.appendTab(infoBox, "JSON",
21344 ///$STR("jsonviewer.tab.JSON"));
21347 if (FBTrace.DBG_JSONVIEWER)
21348 FBTrace.sysout("jsonviewer.initTabBody; JSON object available " +
21349 (typeof(file.jsonObject) != "undefined"), file.jsonObject);
21353 isJSON: function(contentType, data)
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).
21362 ///var responseText = data ? trimLeft(data) : null;
21363 ///if (responseText && responseText.indexOf("{") == 0)
21365 var responseText = data ? trim(data) : null;
21366 if (responseText && responseText.indexOf("{") == 0)
21372 contentType = contentType.split(";")[0];
21373 contentType = trim(contentType);
21374 return contentTypes[contentType];
21377 // Update listener for TabView
21378 updateTabBody: function(infoBox, file, context)
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)
21386 tabBody.updated = true;
21388 if (file.jsonObject) {
21389 Firebug.DOMPanel.DirTable.tag.replace(
21390 {object: file.jsonObject, toggles: this.toggles}, tabBody);
21394 parseJSON: function(file)
21396 var jsonString = new String(file.responseText);
21397 ///return parseJSONString(jsonString, "http://" + file.request.originalURI.host);
21398 return parseJSONString(jsonString);
21402 // ************************************************************************************************
21405 Firebug.registerModule(Firebug.JSONViewerModel);
21407 // ************************************************************************************************
21411 /* See license.txt for terms of usage */
21413 FBL.ns(function() { with (FBL) {
21415 // ************************************************************************************************
21418 // List of XML related content types.
21419 var xmlContentTypes =
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"
21431 // ************************************************************************************************
21432 // Model implementation
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.
21439 Firebug.XMLViewerModel = extend(Firebug.Module,
21441 dispatchName: "xmlViewer",
21443 initialize: function()
21445 ///Firebug.ActivableModule.initialize.apply(this, arguments);
21446 Firebug.Module.initialize.apply(this, arguments);
21447 Firebug.NetMonitor.NetInfoBody.addListener(this);
21450 shutdown: function()
21452 ///Firebug.ActivableModule.shutdown.apply(this, arguments);
21453 Firebug.Module.shutdown.apply(this, arguments);
21454 Firebug.NetMonitor.NetInfoBody.removeListener(this);
21458 * Check response's content-type and if it's a XML, create a new tab with XML preview.
21460 initTabBody: function(infoBox, file)
21462 if (FBTrace.DBG_XMLVIEWER)
21463 FBTrace.sysout("xmlviewer.initTabBody", infoBox);
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))
21469 Firebug.NetMonitor.NetInfoBody.appendTab(infoBox, "XML",
21470 ///$STR("xmlviewer.tab.XML"));
21473 if (FBTrace.DBG_XMLVIEWER)
21474 FBTrace.sysout("xmlviewer.initTabBody; XML response available");
21478 isXML: function(contentType)
21483 // Look if the response is XML based.
21484 for (var i=0; i<xmlContentTypes.length; i++)
21486 if (contentType.indexOf(xmlContentTypes[i]) == 0)
21494 * Parse XML response and render pretty printed preview.
21496 updateTabBody: function(infoBox, file, context)
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)
21504 tabBody.updated = true;
21506 this.insertXML(tabBody, Firebug.NetMonitor.Utils.getResponseText(file, context));
21509 insertXML: function(parentNode, text)
21511 var xmlText = text.replace(/^\s*<?.+?>\s*/, "");
21513 var div = parentNode.ownerDocument.createElement("div");
21514 div.innerHTML = xmlText;
21516 var root = div.getElementsByTagName("*")[0];
21519 var parser = CCIN("@mozilla.org/xmlextras/domparser;1", "nsIDOMParser");
21520 var doc = parser.parseFromString(text, "text/xml");
21521 var root = doc.documentElement;
21524 var nsURI = "http://www.mozilla.org/newlayout/xml/parsererror.xml";
21525 if (root.namespaceURI == nsURI && root.nodeName == "parsererror")
21527 this.ParseError.tag.replace({error: {
21528 message: root.firstChild.nodeValue,
21529 source: root.lastChild.textContent
21535 if (FBTrace.DBG_XMLVIEWER)
21536 FBTrace.sysout("xmlviewer.updateTabBody; XML response parsed", doc);
21538 // Override getHidden in these templates. The parsed XML documen is
21539 // hidden, but we want to display it using 'visible' styling.
21542 Firebug.HTMLPanel.CompleteElement,
21543 Firebug.HTMLPanel.Element,
21544 Firebug.HTMLPanel.TextElement,
21545 Firebug.HTMLPanel.EmptyElement,
21546 Firebug.HTMLPanel.XEmptyElement,
21549 var originals = [];
21550 for (var i=0; i<templates.length; i++)
21552 originals[i] = templates[i].getHidden;
21553 templates[i].getHidden = function() {
21559 // Generate XML preview.
21560 ///Firebug.HTMLPanel.CompleteElement.tag.replace({object: doc.documentElement}, parentNode);
21562 // TODO: xxxpedro html3
21563 ///Firebug.HTMLPanel.CompleteElement.tag.replace({object: root}, parentNode);
21565 Firebug.Reps.appendNode(root, html);
21566 parentNode.innerHTML = html.join("");
21570 for (var i=0; i<originals.length; i++)
21571 templates[i].getHidden = originals[i];/**/
21575 // ************************************************************************************************
21579 * @domplate Represents a template for displaying XML parser errors. Used by
21580 * <code>Firebug.XMLViewerModel</code>.
21582 Firebug.XMLViewerModel.ParseError = domplate(Firebug.Rep,
21585 DIV({"class": "xmlInfoError"},
21586 DIV({"class": "xmlInfoErrorMsg"}, "$error.message"),
21587 PRE({"class": "xmlInfoErrorSource"}, "$error|getSource")
21590 getSource: function(error)
21592 var parts = error.source.split("\n");
21593 if (parts.length != 2)
21594 return error.source;
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);
21603 if (parts[0].length > 80)
21604 parts[0] = parts[0].substr(0, 80) + "...";
21606 return parts.join("\n");
21610 // ************************************************************************************************
21613 Firebug.registerModule(Firebug.XMLViewerModel);
21618 /* See license.txt for terms of usage */
21620 // next-generation Console Panel (will override consoje.js)
21621 FBL.ns(function() { with (FBL) {
21622 // ************************************************************************************************
21624 // ************************************************************************************************
21628 const Cc = Components.classes;
21629 const Ci = Components.interfaces;
21630 const nsIPrefBranch2 = Ci.nsIPrefBranch2;
21631 const PrefService = Cc["@mozilla.org/preferences-service;1"];
21632 const prefs = PrefService.getService(nsIPrefBranch2);
21636 // new offline message handler
21639 r = Firebug.getRep(o);
21641 r.tag.tag.compile();
21644 html = r.tag.renderHTML({object:o}, outputs);
21647 // finish rendering the template (the DOM part)
21648 target = $("build");
21649 target.innerHTML = html;
21650 root = target.firstChild;
21652 domArgs = [root, r.tag.context, 0];
21653 domArgs.push.apply(domArgs, r.tag.domArgs);
21654 domArgs.push.apply(domArgs, outputs);
21655 r.tag.tag.renderDOM.apply(self ? self : r.tag.subject, domArgs);
21659 var consoleQueue = [];
21660 var lastHighlightedObject;
21661 var FirebugContext = Env.browser;
21663 // ************************************************************************************************
21665 var maxQueueRequests = 500;
21667 // ************************************************************************************************
21669 Firebug.ConsoleBase =
21671 log: function(object, context, className, rep, noThrottle, sourceLink)
21673 //dispatch(this.fbListeners,"log",[context, object, className, sourceLink]);
21674 return this.logRow(appendObject, object, context, className, rep, sourceLink, noThrottle);
21677 logFormatted: function(objects, context, className, noThrottle, sourceLink)
21679 //dispatch(this.fbListeners,"logFormatted",[context, objects, className, sourceLink]);
21680 return this.logRow(appendFormatted, objects, context, className, null, sourceLink, noThrottle);
21683 openGroup: function(objects, context, className, rep, noThrottle, sourceLink, noPush)
21685 return this.logRow(appendOpenGroup, objects, context, className, rep, sourceLink, noThrottle);
21688 closeGroup: function(context, noThrottle)
21690 return this.logRow(appendCloseGroup, null, context, null, null, null, noThrottle, true);
21693 logRow: function(appender, objects, context, className, rep, sourceLink, noThrottle, noRow)
21695 // TODO: xxxpedro console console2
21696 noThrottle = true; // xxxpedro forced because there is no TabContext yet
21699 context = FirebugContext;
21701 if (FBTrace.DBG_ERRORS && !context)
21702 FBTrace.sysout("Console.logRow has no context, skipping objects", objects);
21707 if (noThrottle || !context)
21709 var panel = this.getPanel(context);
21712 var row = panel.append(appender, objects, className, rep, sourceLink, noRow);
21713 var container = panel.panelNode;
21715 // TODO: xxxpedro what is this? console console2
21717 var template = Firebug.NetMonitor.NetLimit;
21719 while (container.childNodes.length > maxQueueRequests + 1)
21721 clearDomplate(container.firstChild.nextSibling);
21722 container.removeChild(container.firstChild.nextSibling);
21723 panel.limit.limitInfo.totalCount++;
21724 template.updateCounter(panel.limit);
21726 dispatch([Firebug.A11yModel], "onLogRowCreated", [panel , row]);
21732 consoleQueue.push([appender, objects, context, className, rep, sourceLink, noThrottle, noRow]);
21737 if (!context.throttle)
21739 //FBTrace.sysout("console.logRow has not context.throttle! ");
21742 var args = [appender, objects, context, className, rep, sourceLink, true, noRow];
21743 context.throttle(this.logRow, this, args);
21747 appendFormatted: function(args, row, context)
21750 context = FirebugContext;
21752 var panel = this.getPanel(context);
21753 panel.appendFormatted(args, row);
21756 clear: function(context)
21759 //context = FirebugContext;
21760 context = Firebug.context;
21764 Firebug.Errors.clear(context);
21767 var panel = this.getPanel(context, true);
21774 // Override to direct output to your panel
21775 getPanel: function(context, noCreate)
21777 //return context.getPanel("console", noCreate);
21778 // TODO: xxxpedro console console2
21779 return Firebug.chrome ? Firebug.chrome.getPanel("Console") : null;
21784 // ************************************************************************************************
21787 //var ActivableConsole = extend(Firebug.ActivableModule, Firebug.ConsoleBase);
21788 var ActivableConsole = extend(Firebug.ConsoleBase,
21790 isAlwaysEnabled: function()
21796 Firebug.Console = Firebug.Console = extend(ActivableConsole,
21797 //Firebug.Console = extend(ActivableConsole,
21799 dispatchName: "console",
21803 Firebug.Console.logFormatted(arguments, Firebug.browser, "error");
21808 dispatch(this.fbListeners,"flush",[]);
21810 for (var i=0, length=consoleQueue.length; i<length; i++)
21812 var args = consoleQueue[i];
21813 this.logRow.apply(this, args);
21817 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
21820 showPanel: function(browser, panel)
21824 getFirebugConsoleElement: function(context, win)
21826 var element = win.document.getElementById("_firebugConsole");
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
21833 if (context.stopped)
21834 Firebug.Console.injector.evaluateConsoleScript(context); // todo evaluate consoleForcer on stack
21836 var r = Firebug.CommandLine.evaluateInWebPage(elementForcer, context, win);
21838 if (FBTrace.DBG_CONSOLE)
21839 FBTrace.sysout("getFirebugConsoleElement forcing element result "+r, r);
21841 var element = win.document.getElementById("_firebugConsole");
21842 if (!element) // elementForce fails
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);
21852 isReadyElsePreparing: function(context, win) // this is the only code that should call injector.attachIfNeeded
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));
21860 return this.injector.attachIfNeeded(context, win);
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);
21875 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
21876 // extends ActivableModule
21878 initialize: function()
21880 this.panelName = "console";
21883 //Firebug.ActivableModule.initialize.apply(this, arguments);
21884 //Firebug.Debugger.addListener(this);
21889 if (Firebug.Console.isAlwaysEnabled())
21890 this.watchForErrors();
21893 disable: function()
21895 if (Firebug.Console.isAlwaysEnabled())
21896 this.unwatchForErrors();
21899 initContext: function(context, persistedState)
21901 Firebug.ActivableModule.initContext.apply(this, arguments);
21902 context.consoleReloadWarning = true; // mark as need to warn.
21905 loadedContext: function(context)
21907 for (var url in context.sourceFileMap)
21908 return; // if there are any sourceFiles, then do nothing
21910 // else we saw no JS, so the reload warning it not needed.
21911 this.clearReloadWarning(context);
21914 clearReloadWarning: function(context) // remove the warning about reloading.
21916 if (context.consoleReloadWarning)
21918 var panel = context.getPanel(this.panelName);
21919 panel.clearReloadWarning();
21920 delete context.consoleReloadWarning;
21924 togglePersist: function(context)
21926 var panel = context.getPanel(this.panelName);
21927 panel.persistContent = panel.persistContent ? false : true;
21928 Firebug.chrome.setGlobalAttribute("cmd_togglePersistConsole", "checked", panel.persistContent);
21931 showContext: function(browser, context)
21933 Firebug.chrome.setGlobalAttribute("cmd_clearConsole", "disabled", !context);
21935 Firebug.ActivableModule.showContext.apply(this, arguments);
21938 destroyContext: function(context, persistedState)
21940 Firebug.Console.injector.detachConsole(context, context.window); // TODO iterate windows?
21943 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
21945 onPanelEnable: function(panelName)
21947 if (panelName != this.panelName) // we don't care about other panels
21950 if (FBTrace.DBG_CONSOLE)
21951 FBTrace.sysout("console.onPanelEnable**************");
21953 this.watchForErrors();
21954 Firebug.Debugger.addDependentModule(this); // we inject the console during JS compiles so we need jsd
21957 onPanelDisable: function(panelName)
21959 if (panelName != this.panelName) // we don't care about other panels
21962 if (FBTrace.DBG_CONSOLE)
21963 FBTrace.sysout("console.onPanelDisable**************");
21965 Firebug.Debugger.removeDependentModule(this); // we inject the console during JS compiles so we need jsd
21966 this.unwatchForErrors();
21968 // Make sure possible errors coming from the page and displayed in the Firefox
21969 // status bar are removed.
21973 onSuspendFirebug: function()
21975 if (FBTrace.DBG_CONSOLE)
21976 FBTrace.sysout("console.onSuspendFirebug\n");
21977 if (Firebug.Console.isAlwaysEnabled())
21978 this.unwatchForErrors();
21981 onResumeFirebug: function()
21983 if (FBTrace.DBG_CONSOLE)
21984 FBTrace.sysout("console.onResumeFirebug\n");
21985 if (Firebug.Console.isAlwaysEnabled())
21986 this.watchForErrors();
21989 watchForErrors: function()
21991 Firebug.Errors.checkEnabled();
21992 $('fbStatusIcon').setAttribute("console", "on");
21995 unwatchForErrors: function()
21997 Firebug.Errors.checkEnabled();
21998 $('fbStatusIcon').removeAttribute("console");
22001 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
22002 // Firebug.Debugger listener
22004 onMonitorScript: function(context, frame)
22006 Firebug.Console.log(frame, context);
22009 onFunctionCall: function(context, frame, depth, calling)
22012 Firebug.Console.openGroup([frame, "depth:"+depth], context);
22014 Firebug.Console.closeGroup(context);
22017 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
22019 logRow: function(appender, objects, context, className, rep, sourceLink, noThrottle, noRow)
22022 context = FirebugContext;
22024 if (FBTrace.DBG_WINDOWS && !context) FBTrace.sysout("Console.logRow: no context \n");
22026 if (this.isAlwaysEnabled())
22027 return Firebug.ConsoleBase.logRow.apply(this, arguments);
22031 Firebug.ConsoleListener =
22033 log: function(context, object, className, sourceLink)
22037 logFormatted: function(context, objects, className, sourceLink)
22042 // ************************************************************************************************
22044 Firebug.ConsolePanel = function () {} // XXjjb attach Firebug so this panel can be extended.
22047 //Firebug.ConsolePanel.prototype = extend(Firebug.ActivablePanel,
22048 Firebug.ConsolePanel.prototype = extend(Firebug.Panel,
22050 wasScrolledToBottom: false,
22056 append: function(appender, objects, className, rep, sourceLink, noRow)
22058 var container = this.getTopContainer();
22062 appender.apply(this, [objects]);
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);
22072 var row = this.createRow("logRow", className);
22073 appender.apply(this, [objects, row, rep]);
22076 FirebugReps.SourceLink.tag.append({object: sourceLink}, row);
22078 container.appendChild(row);
22080 this.filterLogRow(row, this.wasScrolledToBottom);
22082 if (this.wasScrolledToBottom)
22083 scrollToBottom(this.panelNode);
22091 if (this.panelNode)
22093 if (FBTrace.DBG_CONSOLE)
22094 FBTrace.sysout("ConsolePanel.clear");
22095 clearNode(this.panelNode);
22096 this.insertLogLimit(this.context);
22100 insertLogLimit: function()
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");
22109 limitPrefsTitle: $STRF("LimitPrefsTitle", [Firebug.prefDomain+".console.logLimit"])
22112 //TODO: xxxpedro console net limit!?
22114 var netLimitRep = Firebug.NetMonitor.NetLimit;
22115 var nodes = netLimitRep.createTable(row, limitInfo);
22117 this.limit = nodes[1];
22119 var container = this.panelNode;
22120 container.insertBefore(nodes[0], container.firstChild);
22123 insertReloadWarning: function()
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");
22129 clearReloadWarning: function()
22131 if (this.warningRow)
22133 this.warningRow.parentNode.removeChild(this.warningRow);
22134 delete this.warningRow;
22138 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
22140 appendObject: function(object, row, rep)
22143 rep = Firebug.getRep(object);
22144 return rep.tag.append({object: object}, row);
22147 appendFormatted: function(objects, row, rep)
22149 if (!objects || !objects.length)
22152 function logText(text, row)
22154 var node = row.ownerDocument.createTextNode(text);
22155 row.appendChild(node);
22158 var format = objects[0];
22161 if (typeof(format) != "string")
22168 if (objects.length === 1) // then we have only a string...
22170 if (format.length < 1) { // ...and it has no characters.
22171 logText("(an empty string)", row);
22177 var parts = parseFormat(format);
22178 var trialIndex = objIndex;
22179 for (var i= 0; i < parts.length; i++)
22181 var part = parts[i];
22182 if (part && typeof(part) == "object")
22184 if (++trialIndex > objects.length) // then too few parameters for format, assume unformatted.
22194 for (var i = 0; i < parts.length; ++i)
22196 var part = parts[i];
22197 if (part && typeof(part) == "object")
22199 var object = objects[++objIndex];
22200 if (typeof(object) != "undefined")
22201 this.appendObject(object, row, part.rep);
22203 this.appendObject(part.type, row, FirebugReps.Text);
22206 FirebugReps.Text.tag.append({object: part}, row);
22209 for (var i = objIndex+1; i < objects.length; ++i)
22212 var object = objects[i];
22213 if (typeof(object) == "string")
22214 FirebugReps.Text.tag.append({object: object}, row);
22216 this.appendObject(object, row);
22220 appendOpenGroup: function(objects, row, rep)
22225 setClass(row, "logGroup");
22226 setClass(row, "opened");
22228 var innerRow = this.createRow("logRow");
22229 setClass(innerRow, "logGroupLabel");
22231 rep.tag.replace({"objects": objects}, innerRow);
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);
22241 addEvent(innerRow, "mousedown", function(event)
22243 if (isLeftClick(event))
22245 //console.log(event.currentTarget == event.target);
22247 var target = event.target || event.srcElement;
22249 target = getAncestorByClass(target, "logGroupLabel");
22251 var groupRow = target.parentNode;
22253 if (hasClass(groupRow, "opened"))
22255 removeClass(groupRow, "opened");
22256 target.setAttribute('aria-expanded', 'false');
22260 setClass(groupRow, "opened");
22261 target.setAttribute('aria-expanded', 'true');
22267 appendCloseGroup: function(object, row, rep)
22273 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
22274 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
22275 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
22276 // TODO: xxxpedro console2
22277 onMouseMove: function(event)
22279 if (!Firebug.Inspector) return;
22281 var target = event.srcElement || event.target;
22283 var object = getAncestorByClass(target, "objectLink-element");
22284 object = object ? object.repObject : null;
22286 if(object && instanceOf(object, "Element") && object.nodeType == 1)
22288 if(object != lastHighlightedObject)
22290 Firebug.Inspector.drawBoxModel(object);
22291 object = lastHighlightedObject;
22295 Firebug.Inspector.hideBoxModel();
22299 onMouseDown: function(event)
22301 var target = event.srcElement || event.target;
22303 var object = getAncestorByClass(target, "objectLink");
22304 var repObject = object ? object.repObject : null;
22311 if (hasClass(object, "objectLink-object"))
22313 Firebug.chrome.selectPanel("DOM");
22314 Firebug.chrome.getPanel("DOM").select(repObject, true);
22316 else if (hasClass(object, "objectLink-element"))
22318 Firebug.chrome.selectPanel("HTML");
22319 Firebug.chrome.getPanel("HTML").select(repObject, true);
22323 if(object && instanceOf(object, "Element") && object.nodeType == 1)
22325 if(object != lastHighlightedObject)
22327 Firebug.Inspector.drawBoxModel(object);
22328 object = lastHighlightedObject;
22332 Firebug.Inspector.hideBoxModel();
22336 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
22337 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
22338 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
22340 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
22345 //searchable: true,
22351 hasCommandLine: true,
22352 hasToolButtons: true,
22353 isPreRendered: true
22358 Firebug.Panel.create.apply(this, arguments);
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);
22365 this.clearButton = new Button({
22366 element: $("fbConsole_btClear"),
22367 owner: Firebug.Console,
22368 onClick: Firebug.Console.clear
22372 initialize: function()
22374 Firebug.Panel.initialize.apply(this, arguments); // loads persisted content
22375 //Firebug.ActivablePanel.initialize.apply(this, arguments); // loads persisted content
22377 if (!this.persistedContent && Firebug.Console.isAlwaysEnabled())
22379 this.insertLogLimit(this.context);
22381 // Initialize log limit and listen for changes.
22382 this.updateMaxLimit();
22384 if (this.context.consoleReloadWarning) // we have not yet injected the console
22385 this.insertReloadWarning();
22388 //Firebug.Console.injector.install(Firebug.browser.window);
22390 addEvent(this.panelNode, "mouseover", this.onMouseMove);
22391 addEvent(this.panelNode, "mousedown", this.onMouseDown);
22393 this.clearButton.initialize();
22395 //consolex.trace();
22396 //TODO: xxxpedro remove this
22398 Firebug.Console.openGroup(["asd"], null, "group", null, false);
22399 Firebug.Console.log("asd");
22400 Firebug.Console.log("asd");
22401 Firebug.Console.log("asd");
22404 //TODO: xxxpedro preferences prefs
22405 //prefs.addObserver(Firebug.prefDomain, this, false);
22408 initializeNode : function()
22410 //dispatch([Firebug.A11yModel], 'onInitializeNode', [this]);
22411 if (FBTrace.DBG_CONSOLE)
22413 this.onScroller = bind(this.onScroll, this);
22414 addEvent(this.panelNode, "scroll", this.onScroller);
22417 this.onResizer = bind(this.onResize, this);
22418 this.resizeEventTarget = Firebug.chrome.$('fbContentBox');
22419 addEvent(this.resizeEventTarget, "resize", this.onResizer);
22422 destroyNode : function()
22424 //dispatch([Firebug.A11yModel], 'onDestroyNode', [this]);
22425 if (this.onScroller)
22426 removeEvent(this.panelNode, "scroll", this.onScroller);
22428 //removeEvent(this.resizeEventTarget, "resize", this.onResizer);
22431 shutdown: function()
22433 //TODO: xxxpedro console console2
22434 this.clearButton.shutdown();
22436 removeEvent(this.panelNode, "mousemove", this.onMouseMove);
22437 removeEvent(this.panelNode, "mousedown", this.onMouseDown);
22439 this.destroyNode();
22441 Firebug.Panel.shutdown.apply(this, arguments);
22443 //TODO: xxxpedro preferences prefs
22444 //prefs.removeObserver(Firebug.prefDomain, this, false);
22447 ishow: function(state)
22449 if (FBTrace.DBG_CONSOLE)
22450 FBTrace.sysout("Console.panel show; " + this.context.getName(), state);
22452 var enabled = Firebug.Console.isAlwaysEnabled();
22455 Firebug.Console.disabledPanelPage.hide(this);
22456 this.showCommandLine(true);
22457 this.showToolbarButtons("fbConsoleButtons", true);
22458 Firebug.chrome.setGlobalAttribute("cmd_togglePersistConsole", "checked", this.persistContent);
22460 if (state && state.wasScrolledToBottom)
22462 this.wasScrolledToBottom = state.wasScrolledToBottom;
22463 delete state.wasScrolledToBottom;
22466 if (this.wasScrolledToBottom)
22467 scrollToBottom(this.panelNode);
22469 if (FBTrace.DBG_CONSOLE)
22470 FBTrace.sysout("console.show ------------------ wasScrolledToBottom: " +
22471 this.wasScrolledToBottom + ", " + this.context.getName());
22476 Firebug.Console.disabledPanelPage.show(this);
22480 ihide: function(state)
22482 if (FBTrace.DBG_CONSOLE)
22483 FBTrace.sysout("Console.panel hide; " + this.context.getName(), state);
22485 this.showToolbarButtons("fbConsoleButtons", false);
22486 this.showCommandLine(false);
22488 if (FBTrace.DBG_CONSOLE)
22489 FBTrace.sysout("console.hide ------------------ wasScrolledToBottom: " +
22490 this.wasScrolledToBottom + ", " + this.context.getName());
22493 destroy: function(state)
22495 if (this.panelNode.offsetHeight)
22496 this.wasScrolledToBottom = isScrolledToBottom(this.panelNode);
22499 state.wasScrolledToBottom = this.wasScrolledToBottom;
22501 if (FBTrace.DBG_CONSOLE)
22502 FBTrace.sysout("console.destroy ------------------ wasScrolledToBottom: " +
22503 this.wasScrolledToBottom + ", " + this.context.getName());
22506 shouldBreakOnNext: function()
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");
22514 getBreakOnNextTooltip: function(enabled)
22516 return (enabled ? $STR("console.Disable Break On All Errors") :
22517 $STR("console.Break On All Errors"));
22520 enablePanel: function(module)
22522 if (FBTrace.DBG_CONSOLE)
22523 FBTrace.sysout("console.ConsolePanel.enablePanel; " + this.context.getName());
22525 Firebug.ActivablePanel.enablePanel.apply(this, arguments);
22527 this.showCommandLine(true);
22529 if (this.wasScrolledToBottom)
22530 scrollToBottom(this.panelNode);
22533 disablePanel: function(module)
22535 if (FBTrace.DBG_CONSOLE)
22536 FBTrace.sysout("console.ConsolePanel.disablePanel; " + this.context.getName());
22538 Firebug.ActivablePanel.disablePanel.apply(this, arguments);
22540 this.showCommandLine(false);
22543 getOptionsMenuItems: function()
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(),
22558 optionMenu("LargeCommandLine", "largeCommandLine")
22562 getShowStackTraceMenuItem: function()
22564 var menuItem = serviceOptionMenu("ShowStackTrace", "showStackTrace");
22565 if (FirebugContext && !Firebug.Debugger.isAlwaysEnabled())
22566 menuItem.disabled = true;
22570 getStrictOptionMenuItem: function()
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) };
22579 getBreakOnMenuItems: function()
22581 //xxxHonza: no BON options for now.
22583 optionMenu("console.option.Persist Break On Error", "persistBreakOnError")
22588 search: function(text)
22593 // Make previously visible nodes invisible again
22596 for (var i in this.matchSet)
22597 removeClass(this.matchSet[i], "matched");
22600 this.matchSet = [];
22602 function findRow(node) { return getAncestorByClass(node, "logRow"); }
22603 var search = new TextSearch(this.panelNode, findRow);
22605 var logRow = search.find(text);
22608 dispatch([Firebug.A11yModel], 'onConsoleSearchMatchFound', [this, text, []]);
22611 for (; logRow; logRow = search.findNext())
22613 setClass(logRow, "matched");
22614 this.matchSet.push(logRow);
22616 dispatch([Firebug.A11yModel], 'onConsoleSearchMatchFound', [this, text, this.matchSet]);
22620 breakOnNext: function(breaking)
22622 Firebug.setPref(Firebug.servicePrefDomain, "breakOnErrors", breaking);
22625 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
22628 createRow: function(rowName, className)
22630 var elt = this.document.createElement("div");
22631 elt.className = rowName + (className ? " " + rowName + "-" + className : "");
22635 getTopContainer: function()
22637 if (this.groups && this.groups.length)
22638 return this.groups[this.groups.length-1];
22640 return this.panelNode;
22643 filterLogRow: function(logRow, scrolledToBottom)
22645 if (this.searchText)
22647 setClass(logRow, "matching");
22648 setClass(logRow, "matched");
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()
22654 if (this.searchFilter(this.searchText, logRow))
22655 this.matchSet.push(logRow);
22657 removeClass(logRow, "matched");
22659 removeClass(logRow, "matching");
22661 if (scrolledToBottom)
22662 scrollToBottom(this.panelNode);
22667 searchFilter: function(text, logRow)
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);
22674 var startPt = this.document.createRange();
22675 startPt.setStartBefore(logRow);
22677 var endPt = this.document.createRange();
22678 endPt.setStartAfter(logRow);
22680 return finder.Find(text, searchRange, startPt, endPt) != null;
22684 observe: function(subject, topic, data)
22686 // We're observing preferences only.
22687 if (topic != "nsPref:changed")
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();
22697 updateMaxLimit: function()
22700 //TODO: xxxpedro preferences log limit?
22701 //var value = Firebug.getPref(Firebug.prefDomain, "console.logLimit");
22702 maxQueueRequests = value ? value : maxQueueRequests;
22705 showCommandLine: function(shouldShow)
22707 //TODO: xxxpedro show command line important
22712 collapse(Firebug.chrome.$("fbCommandBox"), false);
22713 Firebug.CommandLine.setMultiLine(Firebug.largeCommandLine, Firebug.chrome);
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);
22724 onScroll: function(event)
22726 // Update the scroll position flag if the position changes.
22727 this.wasScrolledToBottom = FBL.isScrolledToBottom(this.panelNode);
22729 if (FBTrace.DBG_CONSOLE)
22730 FBTrace.sysout("console.onScroll ------------------ wasScrolledToBottom: " +
22731 this.wasScrolledToBottom + ", wasScrolledToBottom: " +
22732 this.context.getName(), event);
22735 onResize: function(event)
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);
22743 if (this.wasScrolledToBottom)
22744 scrollToBottom(this.panelNode);
22748 // ************************************************************************************************
22750 function parseFormat(format)
22753 if (format.length <= 0)
22756 var reg = /((^%|.%)(\d+)?(\.)([a-zA-Z]))|((^%|.%)([a-zA-Z]))/;
22757 for (var m = reg.exec(format); m; m = reg.exec(format))
22759 if (m[0].substr(0, 2) == "%%")
22761 parts.push(format.substr(0, m.index));
22762 parts.push(m[0].substr(1));
22766 var type = m[8] ? m[8] : m[5];
22767 var precision = m[3] ? parseInt(m[3]) : (m[4] == "." ? -1 : 0);
22773 rep = FirebugReps.Text;
22778 rep = FirebugReps.Number;
22785 parts.push(format.substr(0, m[0][0] == "%" ? m.index : m.index+1));
22786 parts.push({rep: rep, precision: precision, type: ("%" + type)});
22789 format = format.substr(m.index+m[0].length);
22792 parts.push(format);
22796 // ************************************************************************************************
22798 var appendObject = Firebug.ConsolePanel.prototype.appendObject;
22799 var appendFormatted = Firebug.ConsolePanel.prototype.appendFormatted;
22800 var appendOpenGroup = Firebug.ConsolePanel.prototype.appendOpenGroup;
22801 var appendCloseGroup = Firebug.ConsolePanel.prototype.appendCloseGroup;
22803 // ************************************************************************************************
22805 //Firebug.registerActivableModule(Firebug.Console);
22806 Firebug.registerModule(Firebug.Console);
22807 Firebug.registerPanel(Firebug.ConsolePanel);
22809 // ************************************************************************************************
22813 /* See license.txt for terms of usage */
22815 FBL.ns(function() { with (FBL) {
22817 // ************************************************************************************************
22820 //const Cc = Components.classes;
22821 //const Ci = Components.interfaces;
22823 var frameCounters = {};
22824 var traceRecursion = 0;
22826 Firebug.Console.injector =
22828 install: function(context)
22830 var win = context.window;
22832 var consoleHandler = new FirebugConsoleHandler(context, win);
22858 var Handler = function(name)
22860 var c = consoleHandler;
22861 var f = consoleHandler[name];
22862 return function(){return f.apply(c,arguments);};
22865 var installer = function(c)
22867 for (var i=0, l=properties.length; i<l; i++)
22869 var name = properties[i];
22870 c[name] = new Handler(name);
22871 c.firebuglite = Firebug.version;
22879 if (Env.Options.overrideConsole)
22880 sandbox = new win.Function("arguments.callee.install(window.console={})");
22882 // if there's a console object and overrideConsole is false we should just quit
22889 // try overriding the console object
22890 sandbox = new win.Function("arguments.callee.install(window.console={})");
22894 // if something goes wrong create the firebug object instead
22895 sandbox = new win.Function("arguments.callee.install(window.firebug={})");
22899 sandbox.install = installer;
22903 isAttached: function(context, win)
22905 if (win.wrappedJSObject)
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));
22915 if (FBTrace.DBG_CONSOLE)
22916 FBTrace.sysout("Console.isAttached? to win "+win.location+" fnc:"+win._getFirebugConsoleElement);
22917 return (win._getFirebugConsoleElement ? true : false);
22921 attachIfNeeded: function(context, win)
22923 if (FBTrace.DBG_CONSOLE)
22924 FBTrace.sysout("Console.attachIfNeeded has win "+(win? ((win.wrappedJSObject?"YES":"NO")+" wrappedJSObject"):"null") );
22926 if (this.isAttached(context, win))
22929 if (FBTrace.DBG_CONSOLE)
22930 FBTrace.sysout("Console.attachIfNeeded found isAttached false ");
22932 this.attachConsoleInjector(context, win);
22933 this.addConsoleListener(context, win);
22935 Firebug.Console.clearReloadWarning(context);
22937 var attached = this.isAttached(context, win);
22939 dispatch(Firebug.Console.fbListeners, "onConsoleInjected", [context, win]);
22944 attachConsoleInjector: function(context, win)
22946 var consoleInjection = this.getConsoleInjectionScript(); // Do it all here.
22948 if (FBTrace.DBG_CONSOLE)
22949 FBTrace.sysout("attachConsoleInjector evaluating in "+win.location, consoleInjection);
22951 Firebug.CommandLine.evaluateInWebPage(consoleInjection, context, win);
22953 if (FBTrace.DBG_CONSOLE)
22954 FBTrace.sysout("attachConsoleInjector evaluation completed for "+win.location);
22957 getConsoleInjectionScript: function() {
22958 if (!this.consoleInjectionScript)
22961 script += "window.__defineGetter__('console', function() {\n";
22962 script += " return (window._firebug ? window._firebug : window.loadFirebugConsole()); })\n\n";
22964 script += "window.loadFirebugConsole = function() {\n";
22965 script += "window._firebug = new _FirebugConsole();";
22967 if (FBTrace.DBG_CONSOLE)
22968 script += " window.dump('loadFirebugConsole '+window.location+'\\n');\n";
22970 script += " return window._firebug };\n";
22972 var theFirebugConsoleScript = getResource("chrome://firebug/content/consoleInjected.js");
22973 script += theFirebugConsoleScript;
22976 this.consoleInjectionScript = script;
22978 return this.consoleInjectionScript;
22981 forceConsoleCompilationInPage: function(context, win)
22985 if (FBTrace.DBG_CONSOLE)
22986 FBTrace.sysout("no win in forceConsoleCompilationInPage!");
22990 var consoleForcer = "window.loadFirebugConsole();";
22992 if (context.stopped)
22993 Firebug.Console.injector.evaluateConsoleScript(context); // todo evaluate consoleForcer on stack
22995 Firebug.CommandLine.evaluateInWebPage(consoleForcer, context, win);
22997 if (FBTrace.DBG_CONSOLE)
22998 FBTrace.sysout("forceConsoleCompilationInPage "+win.location, consoleForcer);
23001 evaluateConsoleScript: function(context)
23003 var scriptSource = this.getConsoleInjectionScript(); // TODO XXXjjb this should be getConsoleInjectionScript
23004 Firebug.Debugger.evaluate(scriptSource, context);
23007 addConsoleListener: function(context, win)
23009 if (!context.activeConsoleHandlers) // then we have not been this way before
23010 context.activeConsoleHandlers = [];
23012 { // we've been this way before...
23013 for (var i=0; i<context.activeConsoleHandlers.length; i++)
23015 if (context.activeConsoleHandlers[i].window == win)
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);
23025 // We need the element to attach our event listener.
23026 var element = Firebug.Console.getFirebugConsoleElement(context, win);
23028 element.setAttribute("FirebugVersion", Firebug.version); // Initialize Firebug version.
23032 var handler = new FirebugConsoleHandler(context, win);
23033 handler.attachTo(element);
23035 context.activeConsoleHandlers.push(handler);
23037 if (FBTrace.DBG_CONSOLE)
23038 FBTrace.sysout("consoleInjector addConsoleListener attached handler("+handler.handler_name+") to _firebugConsole in : "+win.location+"\n");
23042 detachConsole: function(context, win)
23044 if (win && win.document)
23046 var element = win.document.getElementById("_firebugConsole");
23048 element.parentNode.removeChild(element);
23053 var total_handlers = 0;
23054 var FirebugConsoleHandler = function FirebugConsoleHandler(context, win)
23058 this.attachTo = function(element)
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
23066 this.detach = function()
23068 this.element.removeEventListener('firebugAppendConsole', this.boundHandler, true);
23071 this.handler_name = ++total_handlers;
23072 this.handleEvent = function(event)
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))
23078 if (FBTrace.DBG_CONSOLE)
23079 FBTrace.sysout("FirebugConsoleHandler", this);
23081 var methodName = event.target.getAttribute("methodName");
23082 Firebug.Console.log($STRF("console.MethodNotSupported", [methodName]));
23086 this.firebuglite = Firebug.version;
23088 this.init = function()
23090 var consoleElement = win.document.getElementById('_firebugConsole');
23091 consoleElement.setAttribute("FirebugVersion", Firebug.version);
23094 this.log = function()
23096 logFormatted(arguments, "log");
23099 this.debug = function()
23101 logFormatted(arguments, "debug", true);
23104 this.info = function()
23106 logFormatted(arguments, "info", true);
23109 this.warn = function()
23111 logFormatted(arguments, "warn", true);
23114 this.error = function()
23116 //TODO: xxxpedro console error
23117 //if (arguments.length == 1)
23119 // logAssert("error", arguments); // add more info based on stack trace
23123 //Firebug.Errors.increaseCount(context);
23124 logFormatted(arguments, "error", true); // user already added info
23128 this.exception = function()
23130 logAssert("error", arguments);
23133 this.assert = function(x)
23138 for (var i = 1; i < arguments.length; i++)
23139 rest.push(arguments[i]);
23140 logAssert("assert", rest);
23144 this.dir = function(o)
23146 Firebug.Console.log(o, context, "dir", Firebug.DOMPanel.DirTable);
23149 this.dirxml = function(o)
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;
23158 Firebug.Console.log(o, context, "dirxml", Firebug.HTMLPanel.SoloElement);
23161 this.group = function()
23164 //var sourceLink = getStackLink();
23165 var sourceLink = null;
23166 Firebug.Console.openGroup(arguments, null, "group", null, false, sourceLink);
23169 this.groupEnd = function()
23171 Firebug.Console.closeGroup(context);
23174 this.groupCollapsed = function()
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");
23182 this.profile = function(title)
23184 logFormatted(["console.profile() not supported."], "warn", true);
23186 //Firebug.Profiler.startProfiling(context, title);
23189 this.profileEnd = function()
23191 logFormatted(["console.profile() not supported."], "warn", true);
23193 //Firebug.Profiler.stopProfiling(context);
23196 this.count = function(key)
23198 // TODO: xxxpedro console2: is there a better way to find a unique ID for the coun() call?
23200 //var frameId = FBL.getStackFrameId();
23203 if (!frameCounters)
23204 frameCounters = {};
23206 if (key != undefined)
23209 var frameCounter = frameCounters[frameId];
23212 var logRow = logFormatted(["0"], null, true, true);
23214 frameCounter = {logRow: logRow, count: 1};
23215 frameCounters[frameId] = frameCounter;
23218 ++frameCounter.count;
23220 var label = key == undefined
23221 ? frameCounter.count
23222 : key + " " + frameCounter.count;
23224 frameCounter.logRow.firstChild.firstChild.nodeValue = label;
23228 this.trace = function()
23230 var getFuncName = function getFuncName (f)
23232 if (f.getName instanceof Function)
23234 return f.getName();
23236 if (f.name) // in FireFox, Function objects have a name property...
23241 var name = f.toString().match(/function\s*([_$\w\d]*)/)[1];
23242 return name || "anonymous";
23245 var wasVisited = function(fn)
23247 for (var i=0, l=frames.length; i<l; i++)
23249 if (frames[i].fn == fn)
23260 if (traceRecursion > 1)
23268 for (var fn = arguments.callee.caller.caller; fn; fn = fn.caller)
23270 if (wasVisited(fn)) break;
23274 for (var i = 0, l = fn.arguments.length; i < l; ++i)
23276 args.push({value: fn.arguments[i]});
23279 frames.push({fn: fn, name: getFuncName(fn), args: args});
23283 // ****************************************************************************************
23294 result.stack || // Firefox / Google Chrome
23295 result.stacktrace || // Opera
23298 stack = stack.replace(/\n\r|\r\n/g, "\n"); // normalize line breaks
23299 var items = stack.split(/[\n\r]/);
23301 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
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):\/\/.*)$/;
23309 var reChromeStackItemName = /\s*\($/;
23310 var reChromeStackItemValue = /^(.+)\:(\d+\:\d+)\)?$/;
23313 for (var i=4, length=items.length; i<length; i++, framePos++)
23315 var frame = frames[framePos];
23316 var item = items[i];
23317 var match = item.match(reChromeStackItem);
23319 //Firebug.Console.log("["+ framePos +"]--------------------------");
23320 //Firebug.Console.log(item);
23321 //Firebug.Console.log("................");
23325 var name = match[1];
23328 name = name.replace(reChromeStackItemName, "");
23332 //Firebug.Console.log("name: "+name);
23334 var value = match[2].match(reChromeStackItemValue);
23337 frame.href = value[1];
23338 frame.lineNo = value[2];
23340 //Firebug.Console.log("url: "+value[1]);
23341 //Firebug.Console.log("line: "+value[2]);
23344 // Firebug.Console.log(match[2]);
23351 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
23352 else if (FBL.isFirefox)
23355 var reFirefoxStackItem = /^(.*)@(.*)$/;
23356 var reFirefoxStackItemValue = /^(.+)\:(\d+)$/;
23359 for (var i=2, length=items.length; i<length; i++, framePos++)
23361 var frame = frames[framePos] || {};
23362 var item = items[i];
23363 var match = item.match(reFirefoxStackItem);
23367 var name = match[1];
23369 //Firebug.Console.logFormatted("name: "+name);
23371 var value = match[2].match(reFirefoxStackItemValue);
23374 frame.href = value[1];
23375 frame.lineNo = value[2];
23377 //Firebug.Console.log("href: "+ value[1]);
23378 //Firebug.Console.log("line: " + value[2]);
23381 // Firebug.Console.logFormatted([match[2]]);
23387 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
23389 else if (FBL.isOpera)
23392 var reOperaStackItem = /^\s\s(?:\.\.\.\s\s)?Line\s(\d+)\sof\s(.+)$/;
23393 var reOperaStackItemValue = /^linked\sscript\s(.+)$/;
23395 for (var i=0, length=items.length; i<length; i+=2)
23397 var item = items[i];
23399 var match = item.match(reOperaStackItem);
23403 //Firebug.Console.log(match[1]);
23405 var value = match[2].match(reOperaStackItemValue);
23409 //Firebug.Console.log(value[1]);
23412 // Firebug.Console.log(match[2]);
23414 //Firebug.Console.log("--------------------------");
23421 //console.log(stack);
23422 //console.dir(frames);
23423 Firebug.Console.log({frames: frames}, context, "stackTrace", FirebugReps.StackTrace);
23428 this.trace_ok = function()
23430 var getFuncName = function getFuncName (f)
23432 if (f.getName instanceof Function)
23433 return f.getName();
23434 if (f.name) // in FireFox, Function objects have a name property...
23437 var name = f.toString().match(/function\s*([_$\w\d]*)/)[1];
23438 return name || "anonymous";
23441 var wasVisited = function(fn)
23443 for (var i=0, l=frames.length; i<l; i++)
23445 if (frames[i].fn == fn)
23454 for (var fn = arguments.callee.caller; fn; fn = fn.caller)
23456 if (wasVisited(fn)) break;
23460 for (var i = 0, l = fn.arguments.length; i < l; ++i)
23462 args.push({value: fn.arguments[i]});
23465 frames.push({fn: fn, name: getFuncName(fn), args: args});
23468 Firebug.Console.log({frames: frames}, context, "stackTrace", FirebugReps.StackTrace);
23471 this.clear = function()
23473 Firebug.Console.clear(context);
23476 this.time = function(name, reset)
23481 var time = new Date().getTime();
23483 if (!this.timeCounters)
23484 this.timeCounters = {};
23486 var key = "KEY"+name.toString();
23488 if (!reset && this.timeCounters[key])
23491 this.timeCounters[key] = time;
23494 this.timeEnd = function(name)
23496 var time = new Date().getTime();
23498 if (!this.timeCounters)
23501 var key = "KEY"+name.toString();
23503 var timeCounter = this.timeCounters[key];
23506 var diff = time - timeCounter;
23507 var label = name + ": " + diff + "ms";
23511 delete this.timeCounters[key];
23516 // These functions are over-ridden by commandLine
23517 this.evaluated = function(result, context)
23519 if (FBTrace.DBG_CONSOLE)
23520 FBTrace.sysout("consoleInjector.FirebugConsoleHandler evalutated default called", result);
23522 Firebug.Console.log(result, context);
23524 this.evaluateError = function(result, context)
23526 Firebug.Console.log(result, context, "errorMessage");
23529 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
23531 function logFormatted(args, className, linkToSource, noThrottle)
23533 var sourceLink = linkToSource ? getStackLink() : null;
23534 return Firebug.Console.logFormatted(args, context, className, noThrottle, sourceLink);
23537 function logAssert(category, args)
23539 Firebug.Errors.increaseCount(context);
23541 if (!args || !args.length || args.length == 0)
23542 var msg = [FBL.$STR("Assertion")];
23546 if (Firebug.errorStackTrace)
23548 var trace = Firebug.errorStackTrace;
23549 delete Firebug.errorStackTrace;
23550 if (FBTrace.DBG_CONSOLE)
23551 FBTrace.sysout("logAssert trace from errorStackTrace", trace);
23553 else if (msg.stack)
23555 var trace = parseToStackTrace(msg.stack);
23556 if (FBTrace.DBG_CONSOLE)
23557 FBTrace.sysout("logAssert trace from msg.stack", trace);
23561 var trace = getJSDUserStack();
23562 if (FBTrace.DBG_CONSOLE)
23563 FBTrace.sysout("logAssert trace from getJSDUserStack", trace);
23566 var errorObject = new FBL.ErrorMessage(msg, (msg.fileName?msg.fileName:win.location), (msg.lineNumber?msg.lineNumber:0), "", category, context, trace);
23569 if (trace && trace.frames && trace.frames[0])
23570 errorObject.correctWithStackTrace(trace);
23572 errorObject.resetSource();
23574 var objects = errorObject;
23575 if (args.length > 1)
23577 objects = [errorObject];
23578 for (var i = 1; i < args.length; i++)
23579 objects.push(args[i]);
23582 var row = Firebug.Console.log(objects, context, "errorMessage", null, true); // noThrottle
23583 row.scrollIntoView();
23586 function getComponentsStackDump()
23588 // Starting with our stack, walk back to the user-level code
23589 var frame = Components.stack;
23590 var userURL = win.location.href.toString();
23592 if (FBTrace.DBG_CONSOLE)
23593 FBTrace.sysout("consoleInjector.getComponentsStackDump initial stack for userURL "+userURL, frame);
23595 // Drop frames until we get into user code.
23596 while (frame && FBL.isSystemURL(frame.filename) )
23597 frame = frame.caller;
23599 // Drop two more frames, the injected console function and firebugAppendConsole()
23601 frame = frame.caller;
23603 frame = frame.caller;
23605 if (FBTrace.DBG_CONSOLE)
23606 FBTrace.sysout("consoleInjector.getComponentsStackDump final stack for userURL "+userURL, frame);
23611 function getStackLink()
23613 // TODO: xxxpedro console2
23615 //return FBL.getFrameSourceLink(getComponentsStackDump());
23618 function getJSDUserStack()
23620 var trace = FBL.getCurrentStackTrace(context);
23622 var frames = trace ? trace.frames : null;
23623 if (frames && (frames.length > 0) )
23625 var oldest = frames.length - 1; // 6 - 1 = 5
23626 for (var i = 0; i < frames.length; i++)
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
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
23638 return "Firebug failed to get stack trace with any frames";
23642 // ************************************************************************************************
23643 // Register console namespace
23645 FBL.registerConsole = function()
23647 var win = Env.browser.window;
23648 Firebug.Console.injector.install(win);
23656 /* See license.txt for terms of usage */
23658 FBL.ns(function() { with (FBL) {
23659 // ************************************************************************************************
23662 // ************************************************************************************************
23665 var commandPrefix = ">>>";
23666 var reOpenBracket = /[\[\(\{]/;
23667 var reCloseBracket = /[\]\)\}]/;
23669 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
23671 var commandHistory = [];
23672 var commandPointer = -1;
23674 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
23676 var isAutoCompleting = null;
23677 var autoCompletePrefix = null;
23678 var autoCompleteExpr = null;
23679 var autoCompleteBuffer = null;
23680 var autoCompletePosition = null;
23682 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
23684 var fbCommandLine = null;
23685 var fbLargeCommandLine = null;
23686 var fbLargeCommandButtons = null;
23688 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
23700 "getElementsByTagName"
23704 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
23706 var _stack = function(command)
23708 Firebug.context.persistedState.commandHistory.push(command);
23709 Firebug.context.persistedState.commandPointer =
23710 Firebug.context.persistedState.commandHistory.length;
23713 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
23715 // ************************************************************************************************
23718 Firebug.CommandLine = extend(Firebug.Module,
23720 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
23723 isMultiLine: false,
23726 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
23728 initialize: function(doc)
23730 this.clear = bind(this.clear, this);
23731 this.enter = bind(this.enter, this);
23733 this.onError = bind(this.onError, this);
23734 this.onKeyDown = bind(this.onKeyDown, this);
23735 this.onMultiLineKeyDown = bind(this.onMultiLineKeyDown, this);
23737 addEvent(Firebug.browser.window, "error", this.onError);
23738 addEvent(Firebug.chrome.window, "error", this.onError);
23741 shutdown: function(doc)
23745 removeEvent(Firebug.browser.window, "error", this.onError);
23746 removeEvent(Firebug.chrome.window, "error", this.onError);
23749 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
23751 activate: function(multiLine, hideToggleIcon, onRun)
23753 defineCommandLineAPI();
23755 Firebug.context.persistedState.commandHistory =
23756 Firebug.context.persistedState.commandHistory || [];
23758 Firebug.context.persistedState.commandPointer =
23759 Firebug.context.persistedState.commandPointer || -1;
23763 if (this.isMultiLine == multiLine) return;
23768 fbCommandLine = $("fbCommandLine");
23769 fbLargeCommandLine = $("fbLargeCommandLine");
23770 fbLargeCommandButtons = $("fbLargeCommandButtons");
23774 onRun = onRun || this.enter;
23776 this.isMultiLine = true;
23778 this.element = fbLargeCommandLine;
23780 addEvent(this.element, "keydown", this.onMultiLineKeyDown);
23782 addEvent($("fbSmallCommandLineIcon"), "click", Firebug.chrome.hideLargeCommandLine);
23784 this.runButton = new Button({
23785 element: $("fbCommand_btRun"),
23786 owner: Firebug.CommandLine,
23790 this.runButton.initialize();
23792 this.clearButton = new Button({
23793 element: $("fbCommand_btClear"),
23794 owner: Firebug.CommandLine,
23795 onClick: this.clear
23798 this.clearButton.initialize();
23802 this.isMultiLine = false;
23803 this.element = fbCommandLine;
23805 if (!fbCommandLine)
23808 addEvent(this.element, "keydown", this.onKeyDown);
23811 //Firebug.Console.log("activate", this.element);
23814 fixOperaTabKey(this.element);
23817 this.element.value = this.lastValue;
23819 this.isActive = true;
23822 deactivate: function()
23824 if (!this.isActive) return;
23826 //Firebug.Console.log("deactivate", this.element);
23828 this.isActive = false;
23830 this.lastValue = this.element.value;
23832 if (this.isMultiLine)
23834 removeEvent(this.element, "keydown", this.onMultiLineKeyDown);
23836 removeEvent($("fbSmallCommandLineIcon"), "click", Firebug.chrome.hideLargeCommandLine);
23838 this.runButton.destroy();
23839 this.clearButton.destroy();
23843 removeEvent(this.element, "keydown", this.onKeyDown);
23846 this.element = null;
23847 delete this.element;
23849 fbCommandLine = null;
23850 fbLargeCommandLine = null;
23851 fbLargeCommandButtons = null;
23854 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
23858 this.element.focus();
23863 this.element.blur();
23866 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
23870 this.element.value = "";
23873 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
23875 evaluate: function(expr)
23877 // TODO: need to register the API in console.firebug.commandLineAPI
23878 var api = "Firebug.CommandLine.API";
23880 var result = Firebug.context.evaluate(expr, "window", api, Firebug.Console.error);
23885 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
23889 var command = this.element.value;
23891 if (!command) return;
23895 Firebug.Console.log(commandPrefix + " " + stripNewLines(command),
23896 Firebug.browser, "command", FirebugReps.Text);
23898 var result = this.evaluate(command);
23900 Firebug.Console.log(result);
23903 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
23905 prevCommand: function()
23907 if (Firebug.context.persistedState.commandPointer > 0 &&
23908 Firebug.context.persistedState.commandHistory.length > 0)
23910 this.element.value = Firebug.context.persistedState.commandHistory
23911 [--Firebug.context.persistedState.commandPointer];
23915 nextCommand: function()
23917 var element = this.element;
23919 var limit = Firebug.context.persistedState.commandHistory.length -1;
23920 var i = Firebug.context.persistedState.commandPointer;
23923 element.value = Firebug.context.persistedState.commandHistory
23924 [++Firebug.context.persistedState.commandPointer];
23926 else if (i == limit)
23928 ++Firebug.context.persistedState.commandPointer;
23929 element.value = "";
23933 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
23935 autocomplete: function(reverse)
23937 var element = this.element;
23939 var command = element.value;
23940 var offset = getExpressionOffset(command);
23942 var valBegin = offset ? command.substr(0, offset) : "";
23943 var val = command.substr(offset);
23945 var buffer, obj, objName, commandBegin, result, prefix;
23947 // if it is the beginning of the completion
23948 if(!isAutoCompleting)
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);
23958 if (r[1] || r[2] || r[3])
23960 commandBegin = r[1] || "";
23961 objName = r[2] || "";
23962 prefix = r[3] || "";
23964 else if (val == "")
23966 commandBegin = objName = prefix = "";
23970 isAutoCompleting = true;
23972 // find base object
23978 objName = objName.replace(/\.$/, "");
23980 var n = objName.split(".");
23981 var target = window, o;
23983 for (var i=0, ni; ni = n[i]; i++)
23985 if (o = target[ni])
24000 autoCompletePrefix = prefix;
24001 autoCompleteExpr = valBegin + commandBegin + (objName ? objName + "." : "");
24002 autoCompletePosition = -1;
24004 buffer = autoCompleteBuffer = isIE ?
24005 _completion[objName || "window"] || [] : [];
24011 // if it is the continuation of the last completion
24013 buffer = autoCompleteBuffer;
24017 prefix = autoCompletePrefix;
24019 var diff = reverse ? -1 : 1;
24021 for(var i=autoCompletePosition+diff, l=buffer.length, bi; i>=0 && i<l; i+=diff)
24025 if (bi.indexOf(prefix) == 0)
24027 autoCompletePosition = i;
24035 element.value = autoCompleteExpr + result;
24038 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
24040 setMultiLine: function(multiLine)
24042 if (multiLine == this.isMultiLine) return;
24044 this.activate(multiLine);
24047 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
24049 onError: function(msg, href, lineNo)
24053 var lastSlash = href.lastIndexOf("/");
24054 var fileName = lastSlash == -1 ? href : href.substr(lastSlash+1);
24056 '<span class="errorMessage">', msg, '</span>',
24057 '<div class="objectBox-sourceLink">', fileName, ' (line ', lineNo, ')</div>'
24060 // TODO: xxxpedro ajust to Console2
24061 //Firebug.Console.writeRow(html, "error");
24064 onKeyDown: function(e)
24068 var code = e.keyCode;
24070 /*tab, shift, control, alt*/
24071 if (code != 9 && code != 16 && code != 17 && code != 18)
24073 isAutoCompleting = false;
24076 if (code == 13 /* enter */)
24081 else if (code == 27 /* ESC */)
24083 setTimeout(this.clear, 0);
24085 else if (code == 38 /* up */)
24087 this.prevCommand();
24089 else if (code == 40 /* down */)
24091 this.nextCommand();
24093 else if (code == 9 /* tab */)
24095 this.autocomplete(e.shiftKey);
24100 cancelEvent(e, true);
24104 onMultiLineKeyDown: function(e)
24108 var code = e.keyCode;
24110 if (code == 13 /* enter */ && e.ctrlKey)
24117 Firebug.registerModule(Firebug.CommandLine);
24120 // ************************************************************************************************
24123 function getExpressionOffset(command)
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...
24129 var bracketCount = 0;
24131 var start = command.length-1;
24132 for (; start >= 0; --start)
24134 var c = command[start];
24135 if ((c == "," || c == ";" || c == " ") && !bracketCount)
24137 if (reOpenBracket.test(c))
24144 else if (reCloseBracket.test(c))
24151 // ************************************************************************************************
24154 var CommandLineAPI =
24158 return Firebug.browser.document.getElementById(id);
24161 $$: function(selector, context)
24163 context = context || Firebug.browser.document;
24164 return Firebug.Selector ?
24165 Firebug.Selector(selector, context) :
24166 Firebug.Console.error("Firebug.Selector module not loaded.");
24175 Firebug.Console.log(o, Firebug.context, "dir", Firebug.DOMPanel.DirTable);
24178 dirxml: function(o)
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;
24187 Firebug.Console.log(o, Firebug.context, "dirxml", Firebug.HTMLPanel.SoloElement);
24191 // ************************************************************************************************
24193 var defineCommandLineAPI = function defineCommandLineAPI()
24195 Firebug.CommandLine.API = {};
24196 for (var m in CommandLineAPI)
24197 if (!Env.browser.window[m])
24198 Firebug.CommandLine.API[m] = CommandLineAPI[m];
24200 var stack = FirebugChrome.htmlSelectionStack;
24203 Firebug.CommandLine.API.$0 = stack[0];
24204 Firebug.CommandLine.API.$1 = stack[1];
24208 // ************************************************************************************************
24211 /* See license.txt for terms of usage */
24213 FBL.ns(function() { with (FBL) {
24214 // ************************************************************************************************
24216 // ************************************************************************************************
24219 var ElementCache = Firebug.Lite.Cache.Element;
24220 var cacheID = Firebug.Lite.Cache.ID;
24222 var ignoreHTMLProps =
24224 // ignores the attributes injected by Sizzle, otherwise it will
24225 // be visible on IE (when enumerating element.attributes)
24230 if (Firebug.ignoreFirebugElements)
24231 // ignores also the cache property injected by firebug
24232 ignoreHTMLProps[cacheID] = 1;
24235 // ************************************************************************************************
24238 Firebug.HTML = extend(Firebug.Module,
24240 appendTreeNode: function(nodeArray, html)
24242 var reTrim = /^\s+|\s+$/g;
24244 if (!nodeArray.length) nodeArray = [nodeArray];
24246 for (var n=0, node; node=nodeArray[n]; n++)
24248 if (node.nodeType == 1)
24250 if (Firebug.ignoreFirebugElements && node.firebugIgnore) continue;
24252 var uid = ElementCache(node);
24253 var child = node.childNodes;
24254 var childLength = child.length;
24256 var nodeName = node.nodeName.toLowerCase();
24258 var nodeVisible = isVisible(node);
24260 var hasSingleTextChild = childLength == 1 && node.firstChild.nodeType == 3 &&
24261 nodeName != "script" && nodeName != "style";
24263 var nodeControl = !hasSingleTextChild && childLength > 0 ?
24264 ('<div class="nodeControl"></div>') : '';
24266 // FIXME xxxpedro remove this
24267 //var isIE = false;
24269 if(isIE && nodeControl)
24270 html.push(nodeControl);
24272 if (typeof uid != 'undefined')
24274 '<div class="objectBox-element" ',
24277 !isIE && nodeControl ? nodeControl: "",
24281 '" class="nodeBox',
24282 nodeVisible ? "" : " nodeHidden",
24283 '"><<span class="nodeTag">', nodeName, '</span>'
24287 '<div class="objectBox-element"><span class="nodeBox',
24288 nodeVisible ? "" : " nodeHidden",
24289 '"><<span class="nodeTag">',
24290 nodeName, '</span>'
24293 for (var i = 0; i < node.attributes.length; ++i)
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))
24305 var name = attr.nodeName.toLowerCase();
24306 var value = name == "style" ? formatStyles(node.style.cssText) : attr.nodeValue;
24308 html.push(' <span class="nodeName">', name,
24309 '</span>="<span class="nodeValue">', escapeHTML(value),
24314 // source code nodes
24315 if (nodeName == 'script' || nodeName == 'style')
24319 var src = node.innerHTML+'\n';
24322 var src = '\n'+node.innerHTML+'\n';
24325 var match = src.match(/\n/g);
24326 var num = match ? match.length : 0;
24327 var s = [], sl = 0;
24329 for(var c=1; c<num; c++){
24330 s[sl++] = '<div line="'+c+'">' + c + '</div>';
24333 html.push('></div><div class="nodeGroup"><div class="nodeChildren"><div class="lineNo">',
24335 '</div><pre class="nodeCode">',
24338 '</div><div class="objectBox-element"></<span class="nodeTag">',
24340 '</span>></div>',
24347 // Just a single text node child
24348 if (hasSingleTextChild)
24350 var value = child[0].nodeValue.replace(reTrim, '');
24354 '><span class="nodeText">',
24356 '</span></<span class="nodeTag">',
24358 '</span>></span></div>'
24362 html.push('/></span></div>'); // blank text, print as childless node
24365 else if (childLength > 0)
24367 html.push('></span></div>');
24370 html.push('/></span></div>');
24373 else if (node.nodeType == 3)
24375 if ( node.parentNode && ( node.parentNode.nodeName.toLowerCase() == "script" ||
24376 node.parentNode.nodeName.toLowerCase() == "style" ) )
24378 var value = node.nodeValue.replace(reTrim, '');
24381 var src = value+'\n';
24384 var src = '\n'+value+'\n';
24387 var match = src.match(/\n/g);
24388 var num = match ? match.length : 0;
24389 var s = [], sl = 0;
24391 for(var c=1; c<num; c++){
24392 s[sl++] = '<div line="'+c+'">' + c + '</div>';
24395 html.push('<div class="lineNo">',
24397 '</div><pre class="sourceCode">',
24405 var value = node.nodeValue.replace(reTrim, '');
24407 html.push('<div class="nodeText">', escapeHTML(value),'</div>');
24413 appendTreeChildren: function(treeNode)
24415 var doc = Firebug.chrome.document;
24416 var uid = treeNode.id;
24417 var parentNode = ElementCache.get(uid);
24419 if (parentNode.childNodes.length == 0) return;
24421 var treeNext = treeNode.nextSibling;
24422 var treeParent = treeNode.parentNode;
24424 // FIXME xxxpedro remove this
24425 //var isIE = false;
24426 var control = isIE ? treeNode.previousSibling : treeNode.firstChild;
24427 control.className = 'nodeControl nodeMaximized';
24430 var children = doc.createElement("div");
24431 children.className = "nodeChildren";
24432 this.appendTreeNode(parentNode.childNodes, html);
24433 children.innerHTML = html.join("");
24435 treeParent.insertBefore(children, treeNext);
24437 var closeElement = doc.createElement("div");
24438 closeElement.className = "objectBox-element";
24439 closeElement.innerHTML = '</<span class="nodeTag">' +
24440 parentNode.nodeName.toLowerCase() + '></span>';
24442 treeParent.insertBefore(closeElement, treeNext);
24446 removeTreeChildren: function(treeNode)
24448 var children = treeNode.nextSibling;
24449 var closeTag = children.nextSibling;
24451 // FIXME xxxpedro remove this
24452 //var isIE = false;
24453 var control = isIE ? treeNode.previousSibling : treeNode.firstChild;
24454 control.className = 'nodeControl';
24456 children.parentNode.removeChild(children);
24457 closeTag.parentNode.removeChild(closeTag);
24460 isTreeNodeVisible: function(id)
24465 select: function(el)
24467 var id = el && ElementCache(el);
24469 this.selectTreeNode(id);
24472 selectTreeNode: function(id)
24475 var node, stack = [];
24476 while(id && !this.isTreeNodeVisible(id))
24480 var node = ElementCache.get(id).parentNode;
24483 id = ElementCache(node);
24490 while(stack.length > 0)
24495 if (stack.length > 0 && ElementCache.get(id).childNodes.length > 0)
24496 this.appendTreeChildren(node);
24499 selectElement(node);
24503 fbPanel1.scrollTop = Math.round(node.offsetTop - fbPanel1.clientHeight/2);
24508 Firebug.registerModule(Firebug.HTML);
24510 // ************************************************************************************************
24513 function HTMLPanel(){};
24515 HTMLPanel.prototype = extend(Firebug.Panel,
24521 hasSidePanel: true,
24522 //hasToolButtons: true,
24523 isPreRendered: !Firebug.flexChromeEnabled /* FIXME xxxpedro chromenew */,
24524 innerHTMLSync: true
24527 create: function(){
24528 Firebug.Panel.create.apply(this, arguments);
24530 this.panelNode.style.padding = "4px 3px 1px 15px";
24531 this.panelNode.style.minWidth = "500px";
24533 if (Env.Options.enablePersistent || Firebug.chrome.type != "popup")
24536 if(this.sidePanelBar && !this.sidePanelBar.selectedPanel)
24538 this.sidePanelBar.selectPanel("css");
24542 destroy: function()
24544 selectedElement = null;
24547 selectedSidePanelTS = null;
24548 selectedSidePanelTimer = null;
24550 Firebug.Panel.destroy.apply(this, arguments);
24553 createUI: function()
24555 var rootNode = Firebug.browser.document.documentElement;
24557 Firebug.HTML.appendTreeNode(rootNode, html);
24559 this.panelNode.innerHTML = html.join("");
24562 initialize: function()
24564 Firebug.Panel.initialize.apply(this, arguments);
24565 addEvent(this.panelNode, 'click', Firebug.HTML.onTreeClick);
24567 fbPanel1 = $("fbPanel1");
24569 if(!selectedElement)
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);
24577 Firebug.HTML.selectTreeNode(Firebug.context.persistedState.selectedHTMLElementId);
24581 addEvent(fbPanel1, 'mousemove', Firebug.HTML.onListMouseMove);
24582 addEvent($("fbContent"), 'mouseout', Firebug.HTML.onListMouseMove);
24583 addEvent(Firebug.chrome.node, 'mouseout', Firebug.HTML.onListMouseMove);
24586 shutdown: function()
24589 removeEvent(fbPanel1, 'mousemove', Firebug.HTML.onListMouseMove);
24590 removeEvent($("fbContent"), 'mouseout', Firebug.HTML.onListMouseMove);
24591 removeEvent(Firebug.chrome.node, 'mouseout', Firebug.HTML.onListMouseMove);
24593 removeEvent(this.panelNode, 'click', Firebug.HTML.onTreeClick);
24597 Firebug.Panel.shutdown.apply(this, arguments);
24600 reattach: function()
24602 // TODO: panel reattach
24603 if(Firebug.context.persistedState.selectedHTMLElementId)
24604 Firebug.HTML.selectTreeNode(Firebug.context.persistedState.selectedHTMLElementId);
24607 updateSelection: function(object)
24609 var id = ElementCache(object);
24613 Firebug.HTML.selectTreeNode(id);
24618 Firebug.registerPanel(HTMLPanel);
24620 // ************************************************************************************************
24622 var formatStyles = function(styles)
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
24631 // ************************************************************************************************
24633 var selectedElement = null;
24634 var fbPanel1 = null;
24636 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
24637 var selectedSidePanelTS, selectedSidePanelTimer;
24639 var selectElement= function selectElement(e)
24641 if (e != selectedElement)
24643 if (selectedElement)
24644 selectedElement.className = "objectBox-element";
24646 e.className = e.className + " selectedElement";
24649 e.style.MozBorderRadius = "2px";
24651 else if (FBL.isSafari)
24652 e.style.WebkitBorderRadius = "2px";
24654 e.style.borderRadius = "2px";
24656 selectedElement = e;
24658 Firebug.context.persistedState.selectedHTMLElementId = e.id;
24660 var target = ElementCache.get(e.id);
24661 var sidePanelBar = Firebug.chrome.getPanel("HTML").sidePanelBar;
24662 var selectedSidePanel = sidePanelBar ? sidePanelBar.selectedPanel : null;
24664 var stack = FirebugChrome.htmlSelectionStack;
24666 stack.unshift(target);
24668 if (stack.length > 2)
24671 var lazySelect = function()
24673 selectedSidePanelTS = new Date().getTime();
24675 if (selectedSidePanel)
24676 selectedSidePanel.select(target, true);
24679 if (selectedSidePanelTimer)
24681 clearTimeout(selectedSidePanelTimer);
24682 selectedSidePanelTimer = null;
24685 if (new Date().getTime() - selectedSidePanelTS > 100)
24686 setTimeout(lazySelect, 0);
24688 selectedSidePanelTimer = setTimeout(lazySelect, 150);
24693 // ************************************************************************************************
24694 // *** TODO: REFACTOR **************************************************************************
24695 // ************************************************************************************************
24696 Firebug.HTML.onTreeClick = function (e)
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;
24707 if (targ.className.indexOf('nodeControl') != -1 || targ.className == 'nodeTag')
24709 // FIXME xxxpedro remove this
24710 //var isIE = false;
24712 if(targ.className == 'nodeTag')
24714 var control = isIE ? (targ.parentNode.previousSibling || targ) :
24715 (targ.parentNode.previousSibling || targ);
24717 selectElement(targ.parentNode.parentNode);
24719 if (control.className.indexOf('nodeControl') == -1)
24725 FBL.cancelEvent(e);
24727 var treeNode = isIE ? control.nextSibling : control.parentNode;
24729 //FBL.Firebug.Console.log(treeNode);
24731 if (control.className.indexOf(' nodeMaximized') != -1) {
24732 FBL.Firebug.HTML.removeTreeChildren(treeNode);
24734 FBL.Firebug.HTML.appendTreeChildren(treeNode);
24737 else if (targ.className == 'nodeValue' || targ.className == 'nodeName')
24740 var input = FBL.Firebug.chrome.document.getElementById('treeInput');
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;
24752 function onListMouseOut(e)
24754 e = e || event || window;
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;
24762 if (hasClass(targ, "fbPanel")) {
24763 FBL.Firebug.Inspector.hideBoxModel();
24764 hoverElement = null;
24768 var hoverElement = null;
24769 var hoverElementTS = 0;
24771 Firebug.HTML.onListMouseMove = function onListMouseMove(e)
24775 e = e || event || window;
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;
24784 while (targ && !found) {
24785 if (!/\snodeBox\s|\sobjectBox-selector\s/.test(" " + targ.className + " "))
24786 targ = targ.parentNode;
24793 FBL.Firebug.Inspector.hideBoxModel();
24794 hoverElement = null;
24799 if (typeof targ.attributes[cacheID] == 'undefined') return;
24801 var uid = targ.attributes[cacheID];
24805 if (typeof targ.attributes[cacheID] == 'undefined') return;
24807 var uid = targ.attributes[cacheID];
24810 var el = ElementCache.get(uid.value);
24812 var nodeName = el.nodeName.toLowerCase();
24814 if (FBL.isIE && " meta title script link ".indexOf(" "+nodeName+" ") != -1)
24817 if (!/\snodeBox\s|\sobjectBox-selector\s/.test(" " + targ.className + " ")) return;
24819 if (el.id == "FirebugUI" || " html head body br script link iframe ".indexOf(" "+nodeName+" ") != -1) {
24820 FBL.Firebug.Inspector.hideBoxModel();
24821 hoverElement = null;
24825 if ((new Date().getTime() - hoverElementTS > 40) && hoverElement != el) {
24826 hoverElementTS = new Date().getTime();
24828 FBL.Firebug.Inspector.drawBoxModel(el);
24837 // ************************************************************************************************
24841 appendText: function(object, html)
24843 html.push(escapeHTML(objectToString(object)));
24846 appendNull: function(object, html)
24848 html.push('<span class="objectBox-null">', escapeHTML(objectToString(object)), '</span>');
24851 appendString: function(object, html)
24853 html.push('<span class="objectBox-string">"', escapeHTML(objectToString(object)),
24857 appendInteger: function(object, html)
24859 html.push('<span class="objectBox-number">', escapeHTML(objectToString(object)), '</span>');
24862 appendFloat: function(object, html)
24864 html.push('<span class="objectBox-number">', escapeHTML(objectToString(object)), '</span>');
24867 appendFunction: function(object, html)
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>');
24875 appendObject: function(object, html)
24878 var rep = Firebug.getRep(object);
24881 rep.tag.tag.compile();
24883 var str = rep.tag.renderHTML({object: object}, outputs);
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")
24905 if (typeof object.length != "undefined")
24906 this.appendArray(object, html);
24908 this.appendObjectFormatted(object, html);
24911 this.appendText(object, html);
24919 appendObjectFormatted: function(object, html)
24921 var text = objectToString(object);
24922 var reObject = /\[object (.*?)\]/;
24924 var m = reObject.exec(text);
24925 html.push('<span class="objectBox-object">', m ? m[1] : text, '</span>');
24928 appendSelector: function(object, html)
24930 var uid = ElementCache(object);
24931 var uidString = uid ? [cacheID, '="', uid, '"'].join("") : "";
24933 html.push('<span class="objectBox-selector"', uidString, '>');
24935 html.push('<span class="selectorTag">', escapeHTML(object.nodeName.toLowerCase()), '</span>');
24937 html.push('<span class="selectorId">#', escapeHTML(object.id), '</span>');
24938 if (object.className)
24939 html.push('<span class="selectorClass">.', escapeHTML(object.className), '</span>');
24941 html.push('</span>');
24944 appendNode: function(node, html)
24946 if (node.nodeType == 1)
24948 var uid = ElementCache(node);
24949 var uidString = uid ? [cacheID, '="', uid, '"'].join("") : "";
24952 '<div class="objectBox-element"', uidString, '">',
24953 '<span ', cacheID, '="', uid, '" class="nodeBox">',
24954 '<<span class="nodeTag">', node.nodeName.toLowerCase(), '</span>');
24956 for (var i = 0; i < node.attributes.length; ++i)
24958 var attr = node.attributes[i];
24959 if (!attr.specified || attr.nodeName == cacheID)
24962 var name = attr.nodeName.toLowerCase();
24963 var value = name == "style" ? node.style.cssText : attr.nodeValue;
24965 html.push(' <span class="nodeName">', name,
24966 '</span>="<span class="nodeValue">', escapeHTML(value),
24970 if (node.firstChild)
24972 html.push('></div><div class="nodeChildren">');
24974 for (var child = node.firstChild; child; child = child.nextSibling)
24975 this.appendNode(child, html);
24977 html.push('</div><div class="objectBox-element"></<span class="nodeTag">',
24978 node.nodeName.toLowerCase(), '></span></span></div>');
24981 html.push('/></span></div>');
24983 else if (node.nodeType == 3)
24985 var value = trim(node.nodeValue);
24987 html.push('<div class="nodeText">', escapeHTML(value),'</div>');
24991 appendArray: function(object, html)
24993 html.push('<span class="objectBox-array"><b>[</b> ');
24995 for (var i = 0, l = object.length, obj; i < l; ++i)
24997 this.appendObject(object[i], html);
25003 html.push(' <b>]</b></span>');
25010 // ************************************************************************************************
25013 /* See license.txt for terms of usage */
25018 Firebug.chrome.currentPanel = Firebug.chrome.selectedPanel;
25019 Firebug.showInfoTips = true;
25020 Firebug.InfoTip.initializeBrowser(Firebug.chrome);
25024 FBL.ns(function() { with (FBL) {
25026 // ************************************************************************************************
25029 var maxWidth = 100, maxHeight = 80;
25030 var infoTipMargin = 10;
25031 var infoTipWindowPadding = 25;
25033 // ************************************************************************************************
25035 Firebug.InfoTip = extend(Firebug.Module,
25037 dispatchName: "infoTip",
25040 infoTipTag: DIV({"class": "infoTip"}),
25043 DIV({style: "background: $rgbValue; width: 100px; height: 40px"}, " "),
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"})
25053 onLoadImage: function(event)
25055 var img = event.currentTarget || event.srcElement;
25056 ///var bgImg = img.nextSibling;
25058 /// return; // Sometimes gets called after element is dead
25060 ///var caption = bgImg.nextSibling;
25061 var innerBox = img.parentNode;
25063 /// TODO: xxxpedro infoTip hack
25064 var caption = getElementByClass(innerBox, "infoTipCaption");
25065 var bgImg = getElementByClass(innerBox, "infoTipBgImage");
25067 return; // Sometimes gets called after element is dead
25069 // TODO: xxxpedro infoTip IE and timing issue
25070 // TODO: use offline document to avoid flickering
25072 removeClass(innerBox, "infoTipLoading");
25074 var updateInfoTip = function(){
25076 var w = img.naturalWidth || img.width || 10,
25077 h = img.naturalHeight || img.height || 10;
25079 var repeat = img.getAttribute("repeat");
25081 if (repeat == "repeat-x" || (w == 1 && h > 1))
25083 collapse(img, true);
25084 collapse(bgImg, false);
25085 bgImg.style.background = "url(" + img.src + ") repeat-x";
25086 bgImg.style.width = maxWidth + "px";
25088 bgImg.style.height = maxHeight + "px";
25090 bgImg.style.height = h + "px";
25092 else if (repeat == "repeat-y" || (h == 1 && w > 1))
25094 collapse(img, true);
25095 collapse(bgImg, false);
25096 bgImg.style.background = "url(" + img.src + ") repeat-y";
25097 bgImg.style.height = maxHeight + "px";
25099 bgImg.style.width = maxWidth + "px";
25101 bgImg.style.width = w + "px";
25103 else if (repeat == "repeat" || (w == 1 && h == 1))
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";
25113 if (w > maxWidth || h > maxHeight)
25117 img.style.width = maxWidth + "px";
25118 img.style.height = Math.round((h / w) * maxWidth) + "px";
25122 img.style.width = Math.round((w / h) * maxHeight) + "px";
25123 img.style.height = maxHeight + "px";
25128 //caption.innerHTML = $STRF("Dimensions", [w, h]);
25129 caption.innerHTML = $STRF(w + " x " + h);
25135 setTimeout(updateInfoTip, 0);
25139 removeClass(innerBox, "infoTipLoading");
25146 /// onLoadImage original
25147 onLoadImage: function(event)
25149 var img = event.currentTarget;
25150 var bgImg = img.nextSibling;
25152 return; // Sometimes gets called after element is dead
25154 var caption = bgImg.nextSibling;
25155 var innerBox = img.parentNode;
25157 var w = img.naturalWidth, h = img.naturalHeight;
25158 var repeat = img.getAttribute("repeat");
25160 if (repeat == "repeat-x" || (w == 1 && h > 1))
25162 collapse(img, true);
25163 collapse(bgImg, false);
25164 bgImg.style.background = "url(" + img.src + ") repeat-x";
25165 bgImg.style.width = maxWidth + "px";
25167 bgImg.style.height = maxHeight + "px";
25169 bgImg.style.height = h + "px";
25171 else if (repeat == "repeat-y" || (h == 1 && w > 1))
25173 collapse(img, true);
25174 collapse(bgImg, false);
25175 bgImg.style.background = "url(" + img.src + ") repeat-y";
25176 bgImg.style.height = maxHeight + "px";
25178 bgImg.style.width = maxWidth + "px";
25180 bgImg.style.width = w + "px";
25182 else if (repeat == "repeat" || (w == 1 && h == 1))
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";
25192 if (w > maxWidth || h > maxHeight)
25196 img.style.width = maxWidth + "px";
25197 img.style.height = Math.round((h / w) * maxWidth) + "px";
25201 img.style.width = Math.round((w / h) * maxHeight) + "px";
25202 img.style.height = maxHeight + "px";
25207 caption.innerHTML = $STRF("Dimensions", [w, h]);
25209 removeClass(innerBox, "infoTipLoading");
25215 initializeBrowser: function(browser)
25217 browser.onInfoTipMouseOut = bind(this.onMouseOut, this, browser);
25218 browser.onInfoTipMouseMove = bind(this.onMouseMove, this, browser);
25220 ///var doc = browser.contentDocument;
25221 var doc = browser.document;
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);
25232 return browser.infoTip = this.tags.infoTipTag.append({}, getBody(doc));
25235 uninitializeBrowser: function(browser)
25237 if (browser.infoTip)
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);
25248 browser.infoTip.parentNode.removeChild(browser.infoTip);
25249 delete browser.infoTip;
25250 delete browser.onInfoTipMouseMove;
25254 showInfoTip: function(infoTip, panel, target, x, y, rangeParent, rangeOffset)
25256 if (!Firebug.showInfoTips)
25259 var scrollParent = getOverflowParent(target);
25260 var scrollX = x + (scrollParent ? scrollParent.scrollLeft : 0);
25262 if (panel.showInfoTip(infoTip, target, scrollX, y, rangeParent, rangeOffset))
25264 var htmlElt = infoTip.ownerDocument.documentElement;
25265 var panelWidth = htmlElt.clientWidth;
25266 var panelHeight = htmlElt.clientHeight;
25268 if (x+infoTip.offsetWidth+infoTipMargin > panelWidth)
25270 infoTip.style.left = Math.max(0, panelWidth-(infoTip.offsetWidth+infoTipMargin)) + "px";
25271 infoTip.style.right = "auto";
25275 infoTip.style.left = (x+infoTipMargin) + "px";
25276 infoTip.style.right = "auto";
25279 if (y+infoTip.offsetHeight+infoTipMargin > panelHeight)
25281 infoTip.style.top = Math.max(0, panelHeight-(infoTip.offsetHeight+infoTipMargin)) + "px";
25282 infoTip.style.bottom = "auto";
25286 infoTip.style.top = (y+infoTipMargin) + "px";
25287 infoTip.style.bottom = "auto";
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);
25298 infoTip.setAttribute("active", "true");
25301 this.hideInfoTip(infoTip);
25304 hideInfoTip: function(infoTip)
25307 infoTip.removeAttribute("active");
25310 onMouseOut: function(event, browser)
25312 if (!event.relatedTarget)
25313 this.hideInfoTip(browser.infoTip);
25316 onMouseMove: function(event, browser)
25318 // Ignore if the mouse is moving over the existing info tip.
25319 if (getAncestorByClass(event.target, "infoTip"))
25322 if (browser.currentPanel)
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);
25328 this.hideInfoTip(browser.infoTip);
25331 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
25333 populateColorInfoTip: function(infoTip, color)
25335 this.tags.colorTag.replace({rgbValue: color}, infoTip);
25339 populateImageInfoTip: function(infoTip, url, repeat)
25342 repeat = "no-repeat";
25344 this.tags.imgTag.replace({urlValue: url, repeat: repeat}, infoTip);
25349 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
25352 disable: function()
25354 // XXXjoe For each browser, call uninitializeBrowser
25357 showPanel: function(browser, panel)
25361 var infoTip = panel.panelBrowser.infoTip;
25363 infoTip = this.initializeBrowser(panel.panelBrowser);
25364 this.hideInfoTip(infoTip);
25369 showSidePanel: function(browser, panel)
25371 this.showPanel(browser, panel);
25375 // ************************************************************************************************
25377 Firebug.registerModule(Firebug.InfoTip);
25379 // ************************************************************************************************
25384 /* See license.txt for terms of usage */
25386 FBL.ns(function() { with (FBL) {
25387 // ************************************************************************************************
25389 var CssParser = null;
25391 // ************************************************************************************************
25393 // Simple CSS stylesheet parser from:
25394 // https://github.com/sergeche/webkit-css
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
25401 CssParser = (function(){
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
25408 function rule(start, body_start, end) {
25411 body_start: body_start || 0,
25417 /** @type {rule[]} */
25420 addChild: function(start, body_start, end) {
25421 var r = rule(start, body_start, end);
25423 this.children.push(r);
25427 * Returns last child element
25430 lastChild: function() {
25431 return this.children[this.children.length - 1];
25437 * Replaces all occurances of substring defined by regexp
25438 * @param {String} str
25439 * @return {RegExp} re
25442 function removeAll(str, re) {
25444 while (m = str.match(re)) {
25445 str = str.substring(m[0].length);
25452 * Trims whitespace from the beginning and the end of string
25453 * @param {String} str
25456 function trim(str) {
25457 return str.replace(/^\s+|\s+$/g, '');
25461 * Normalizes CSS rules selector
25462 * @param {String} selector
25464 function normalizeSelector(selector) {
25466 selector = selector.replace(/[\n\r]/g, ' ');
25468 selector = trim(selector);
25470 // remove spaces after commas
25471 selector = selector.replace(/\s*,\s*/g, ',');
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
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;
25489 // remove newlines for better regexp matching
25490 rule_start = rule_start.replace(/[\n\r]/g, ' ');
25492 // remove @import rules
25493 // rule_start = removeAll(rule_start, /^\s*@import\s*url\((['"])?.+?\1?\)\;?/g);
25496 rule_start = removeAll(rule_start, /^\s*\/\*.*?\*\/[\s\t]*/);
25498 // remove whitespace
25499 rule_start = rule_start.replace(/^[\s\t]+/, '');
25501 r.start += (cur_len - rule_start.length);
25502 r.selector = normalizeSelector(rule_start);
25509 * Saves all lise starting indexes for faster search
25510 * @param {String} text CSS stylesheet
25511 * @return {Number[]}
25513 function saveLineIndexes(text) {
25520 ch = text.charAt(i);
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
25528 result.push(i + 1);
25538 * Saves line number for parsed rules
25539 * @param {String} text CSS stylesheet
25540 * @param {rule} rule_node Rule node
25543 function saveLineNumbers(text, rule_node, line_indexes, startLine) {
25544 preprocessRules(text, rule_node);
25546 startLine = startLine || 0;
25548 // remember lines start indexes, preserving line ending characters
25550 var line_indexes = saveLineIndexes(text);
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;
25564 saveLineNumbers(text, r, line_indexes);
25572 * Parses text as CSS stylesheet, remembring each rule position inside
25574 * @param {String} text CSS stylesheet to parse
25576 read: function(text, startLine) {
25577 var rule_start = [],
25578 rule_body_start = [],
25587 stack.last = function() {
25588 return this[this.length - 1];
25591 function hasStr(pos, substr) {
25592 return text.substr(pos, substr.length) == substr;
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) : '';
25599 if (!rule_start.length)
25600 rule_start.push(i);
25605 if (hasStr(i, '@import')) {
25606 var m = text.substr(i).match(/^@import\s*url\((['"])?.+?\1?\)\;?/);
25608 cur_parent.addChild(i, i + 7, i + m[0].length);
25616 // xxxpedro allowing comment inside comment
25617 if (!in_comment && ch2 == '*') { // comment start
25623 if (ch2 == '/') { // comment end
25630 rule_body_start.push(i);
25632 cur_parent = cur_parent.addChild(rule_start.pop());
25633 stack.push(cur_parent);
25638 // found the end of the rule
25640 /** @type {rule} */
25641 var last_rule = stack.pop();
25643 last_rule.body_start = rule_body_start.pop();
25645 cur_parent = last_rule.parent || root;
25652 return saveLineNumbers(text, root, null, startLine);
25655 normalizeSelector: normalizeSelector,
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
25663 * @return {rule[]|null} Array of matched rules, sorted by priority (most
25666 findBySelector: function(rule_node, selector, source) {
25667 var selector = normalizeSelector(selector),
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) {
25680 if (result.length) {
25690 // ************************************************************************************************
25692 FBL.CssParser = CssParser;
25694 // ************************************************************************************************
25697 /* See license.txt for terms of usage */
25699 FBL.ns(function() { with (FBL) {
25701 // ************************************************************************************************
25702 // StyleSheet Parser
25704 var CssAnalyzer = {};
25706 // ************************************************************************************************
25709 var CSSRuleMap = {};
25710 var ElementCSSRulesMap = {};
25712 var internalStyleSheetIndex = -1;
25714 var reSelectorTag = /(^|\s)(?:\w+)/g;
25715 var reSelectorClass = /\.[\w\d_-]+/g;
25716 var reSelectorId = /#[\w\d_-]+/g;
25718 var globalCSSRuleIndex;
25720 var processAllStyleSheetsTimeout = null;
25722 var externalStyleSheetURLs = [];
25724 var ElementCache = Firebug.Lite.Cache.Element;
25725 var StyleSheetCache = Firebug.Lite.Cache.StyleSheet;
25727 //************************************************************************************************
25728 // CSS Analyzer templates
25730 CssAnalyzer.externalStyleSheetWarning = domplate(Firebug.Rep,
25733 DIV({"class": "warning focusRow", style: "font-weight:normal;", role: 'listitem'},
25734 SPAN("$object|STR"),
25735 A({"href": "$href", target:"_blank"}, "$link|STR")
25739 // ************************************************************************************************
25740 // CSS Analyzer methods
25742 CssAnalyzer.processAllStyleSheets = function(doc, styleSheetIterator)
25746 processAllStyleSheets(doc, styleSheetIterator);
25750 // TODO: FBTrace condition
25751 FBTrace.sysout("CssAnalyzer.processAllStyleSheets fails: ", e);
25758 * @returns {String[]} Array of IDs of CSS Rules
25760 CssAnalyzer.getElementCSSRules = function(element)
25764 return getElementCSSRules(element);
25768 // TODO: FBTrace condition
25769 FBTrace.sysout("CssAnalyzer.getElementCSSRules fails: ", e);
25773 CssAnalyzer.getRuleData = function(ruleId)
25775 return CSSRuleMap[ruleId];
25778 // TODO: do we need this?
25779 CssAnalyzer.getRuleLine = function()
25783 CssAnalyzer.hasExternalStyleSheet = function()
25785 return externalStyleSheetURLs.length > 0;
25788 CssAnalyzer.parseStyleSheet = function(href)
25790 var sourceData = extractSourceData(href);
25791 var parsedObj = CssParser.read(sourceData.source, sourceData.startLine);
25792 var parsedRules = parsedObj.children;
25794 // See: Issue 4776: [Firebug lite] CSS Media Types
25796 // Ignore all special selectors like @media and @page
25797 for(var i=0; i < parsedRules.length; )
25799 if (parsedRules[i].selector.indexOf("@") != -1)
25801 parsedRules.splice(i, 1);
25807 return parsedRules;
25810 //************************************************************************************************
25812 //************************************************************************************************
25814 // ************************************************************************************************
25815 // StyleSheet processing
25817 var processAllStyleSheets = function(doc, styleSheetIterator)
25819 styleSheetIterator = styleSheetIterator || processStyleSheet;
25821 globalCSSRuleIndex = -1;
25823 var styleSheets = doc.styleSheets;
25824 var importedStyleSheets = [];
25826 if (FBTrace.DBG_CSS)
25827 var start = new Date().getTime();
25829 for(var i=0, length=styleSheets.length; i<length; i++)
25833 var styleSheet = styleSheets[i];
25835 if ("firebugIgnore" in styleSheet) continue;
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;
25845 externalStyleSheetURLs.push(styleSheet.href);
25846 styleSheet.restricted = true;
25847 var ssid = StyleSheetCache(styleSheet);
25849 /// TODO: xxxpedro external css
25850 //loadExternalStylesheet(doc, styleSheetIterator, styleSheet);
25853 // process internal and external styleSheets
25854 styleSheetIterator(doc, styleSheet);
25856 var importedStyleSheet, importedRules;
25858 // process imported styleSheets in IE
25861 var imports = styleSheet.imports;
25863 for(var j=0, importsLength=imports.length; j<importsLength; j++)
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;
25875 externalStyleSheetURLs.push(styleSheet.href);
25876 importedStyleSheet.restricted = true;
25877 var ssid = StyleSheetCache(importedStyleSheet);
25880 styleSheetIterator(doc, importedStyleSheet);
25883 // process imported styleSheets in other browsers
25886 for(var j=0, rulesLength=rules.length; j<rulesLength; j++)
25890 var rule = rules[j];
25892 importedStyleSheet = rule.styleSheet;
25894 if (importedStyleSheet)
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;
25906 externalStyleSheetURLs.push(styleSheet.href);
25907 importedStyleSheet.restricted = true;
25908 var ssid = StyleSheetCache(importedStyleSheet);
25911 styleSheetIterator(doc, importedStyleSheet);
25916 if (FBTrace.DBG_CSS)
25918 FBTrace.sysout("FBL.processAllStyleSheets", "all stylesheet rules processed in " + (new Date().getTime() - start) + "ms");
25922 // ************************************************************************************************
25924 var processStyleSheet = function(doc, styleSheet)
25926 if (styleSheet.restricted)
25929 var rules = isIE ? styleSheet.rules : styleSheet.cssRules;
25931 var ssid = StyleSheetCache(styleSheet);
25933 var href = styleSheet.href;
25935 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
25937 var shouldParseCSS = typeof CssParser != "undefined" && !Firebug.disableResourceFetching;
25938 if (shouldParseCSS)
25942 var parsedRules = CssAnalyzer.parseStyleSheet(href);
25946 if (FBTrace.DBG_ERRORS) FBTrace.sysout("processStyleSheet FAILS", e.message || e);
25947 shouldParseCSS = false;
25951 var parsedRulesIndex = 0;
25953 var dontSupportGroupedRules = isIE && browserVersion < 9;
25958 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
25960 for (var i=0, length=rules.length; i<length; i++)
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 || "";
25969 // See: Issue 4776: [Firebug lite] CSS Media Types
25971 // Ignore all special selectors like @media and @page
25972 if (!selector || selector.indexOf("@") != -1)
25976 selector = selector.replace(reSelectorTag, function(s){return s.toLowerCase();});
25978 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
25980 if (shouldParseCSS)
25982 var parsedRule = parsedRules[parsedRulesIndex];
25983 var parsedSelector = parsedRule.selector;
25985 if (dontSupportGroupedRules && parsedSelector.indexOf(",") != -1 && group.length == 0)
25986 group = parsedSelector.split(",");
25988 if (dontSupportGroupedRules && group.length > 0)
25990 groupItem = group.shift();
25992 if (CssParser.normalizeSelector(selector) == groupItem)
25993 lineNo = parsedRule.line;
25995 if (group.length == 0)
25996 parsedRulesIndex++;
25998 else if (CssParser.normalizeSelector(selector) == parsedRule.selector)
26000 lineNo = parsedRule.line;
26001 parsedRulesIndex++;
26004 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
26008 styleSheetId: ssid,
26009 styleSheetIndex: i,
26010 order: ++globalCSSRuleIndex,
26012 // See: Issue 4777: [Firebug lite] Specificity of CSS Rules
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
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)
26027 selector: selector,
26028 cssText: rule.style ? rule.style.cssText : rule.cssText ? rule.cssText : ""
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.
26035 // Maybe add a "refresh" button?
26036 var elements = Firebug.Selector(selector, doc);
26038 for (var j=0, elementsLength=elements.length; j<elementsLength; j++)
26040 var element = elements[j];
26041 var eid = ElementCache(element);
26043 if (!ElementCSSRulesMap[eid])
26044 ElementCSSRulesMap[eid] = [];
26046 ElementCSSRulesMap[eid].push(rid);
26049 //console.log(selector, elements);
26053 // ************************************************************************************************
26054 // External StyleSheet Loader
26056 var loadExternalStylesheet = function(doc, styleSheetIterator, styleSheet)
26058 var url = styleSheet.href;
26059 styleSheet.firebugIgnore = true;
26061 var source = Firebug.Lite.Proxy.load(url);
26063 // TODO: check for null and error responses
26066 //var reMultiComment = /(\/\*([^\*]|\*(?!\/))*\*\/)/g;
26067 //source = source.replace(reMultiComment, "");
26069 // convert relative addresses to absolute ones
26070 source = source.replace(/url\(([^\)]+)\)/g, function(a,name){
26072 var hasDomain = /\w+:\/\/./.test(name);
26076 name = name.replace(/^(["'])(.+)\1$/, "$2");
26077 var first = name.charAt(0);
26079 // relative path, based on root
26082 // TODO: xxxpedro move to lib or Firebug.Lite.something
26084 var m = /^([^:]+:\/{1,3}[^\/]+)/.exec(url);
26087 "url(" + m[1] + name + ")" :
26088 "url(" + name + ")";
26090 // relative path, based on current location
26093 // TODO: xxxpedro move to lib or Firebug.Lite.something
26095 var path = url.replace(/[^\/]+\.[\w\d]+(\?.+|#.+)?$/g, "");
26097 path = path + name;
26099 var reBack = /[^\/]+\/\.\.\//;
26100 while(reBack.test(path))
26102 path = path.replace(reBack, "");
26105 //console.log("url(" + path + ")");
26107 return "url(" + path + ")";
26111 // if it is an absolute path, there is nothing to do
26115 var oldStyle = styleSheet.ownerNode;
26117 if (!oldStyle) return;
26119 if (!oldStyle.parentNode) return;
26121 var style = createGlobalElement("style");
26122 style.setAttribute("charset","utf-8");
26123 style.setAttribute("type", "text/css");
26124 style.innerHTML = source;
26127 oldStyle.parentNode.insertBefore(style, oldStyle.nextSibling);
26128 oldStyle.parentNode.removeChild(oldStyle);
26130 doc.styleSheets[doc.styleSheets.length-1].externalURL = url;
26132 console.log(url, "call " + externalStyleSheetURLs.length, source);
26134 externalStyleSheetURLs.pop();
26136 if (processAllStyleSheetsTimeout)
26138 clearTimeout(processAllStyleSheetsTimeout);
26141 processAllStyleSheetsTimeout = setTimeout(function(){
26142 console.log("processing");
26143 FBL.processAllStyleSheets(doc, styleSheetIterator);
26144 processAllStyleSheetsTimeout = null;
26149 //************************************************************************************************
26150 // getElementCSSRules
26152 var getElementCSSRules = function(element)
26154 var eid = ElementCache(element);
26155 var rules = ElementCSSRulesMap[eid];
26157 if (!rules) return;
26159 var arr = [element];
26160 var Selector = Firebug.Selector;
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++)
26170 rule = CSSRuleMap[ruleId];
26172 // check if it is a grouped selector
26173 if (rule.selector.indexOf(",") != -1)
26175 var selectors = rule.selector.split(",");
26176 var maxSpecificity = -1;
26177 var sel, spec, mostSpecificSelector;
26179 // loop over all selectors in the group
26180 for (var j, len = selectors.length; j < len; j++)
26182 sel = selectors[j];
26184 // find if the selector matches the element
26185 if (Selector.matches(sel, arr).length == 1)
26187 spec = getCSSRuleSpecificity(sel);
26189 // find the most specific selector that macthes the element
26190 if (spec > maxSpecificity)
26192 maxSpecificity = spec;
26193 mostSpecificSelector = sel;
26198 rule.specificity = maxSpecificity;
26202 rules.sort(sortElementRules);
26203 //rules.sort(solveRulesTied);
26208 // ************************************************************************************************
26209 // Rule Specificity
26211 var sortElementRules = function(a, b)
26213 var ruleA = CSSRuleMap[a];
26214 var ruleB = CSSRuleMap[b];
26216 var specificityA = ruleA.specificity;
26217 var specificityB = ruleB.specificity;
26219 if (specificityA > specificityB)
26222 else if (specificityA < specificityB)
26226 return ruleA.order > ruleB.order ? 1 : -1;
26229 var solveRulesTied = function(a, b)
26231 var ruleA = CSSRuleMap[a];
26232 var ruleB = CSSRuleMap[b];
26234 if (ruleA.specificity == ruleB.specificity)
26235 return ruleA.order > ruleB.order ? 1 : -1;
26240 var getCSSRuleSpecificity = function(selector)
26242 var match = selector.match(reSelectorTag);
26243 var tagCount = match ? match.length : 0;
26245 match = selector.match(reSelectorClass);
26246 var classCount = match ? match.length : 0;
26248 match = selector.match(reSelectorId);
26249 var idCount = match ? match.length : 0;
26251 return tagCount + 10*classCount + 100*idCount;
26254 // ************************************************************************************************
26257 var extractSourceData = function(href)
26267 sourceData.source = Firebug.Lite.Proxy.load(href);
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
26275 var ssIndex = ++internalStyleSheetIndex;
26276 var reStyleTag = /\<\s*style.*\>/gi;
26277 var reEndStyleTag = /\<\/\s*style.*\>/gi;
26279 var source = Firebug.Lite.Proxy.load(Env.browser.location.href);
26280 source = source.replace(/\n\r|\r\n/g, "\n"); // normalize line breaks
26286 var matchStyleTag = source.match(reStyleTag);
26287 var i0 = source.indexOf(matchStyleTag[0]) + matchStyleTag[0].length;
26289 for (var i=0; i < i0; i++)
26291 if (source.charAt(i) == "\n")
26295 source = source.substr(i0);
26299 while (index <= ssIndex);
26301 var matchEndStyleTag = source.match(reEndStyleTag);
26302 var i1 = source.indexOf(matchEndStyleTag[0]);
26304 var extractedSource = source.substr(0, i1);
26306 sourceData.source = extractedSource;
26307 sourceData.startLine = startLine;
26313 // ************************************************************************************************
26316 FBL.CssAnalyzer = CssAnalyzer;
26318 // ************************************************************************************************
26322 /* See license.txt for terms of usage */
26327 // ************************************************************************************************
26331 * Gets an XPath for an element which describes its hierarchical location.
26333 this.getElementXPath = function(element)
26337 if (element && element.id)
26338 return '//*[@id="' + element.id + '"]';
26340 return this.getElementTreeXPath(element);
26344 // xxxpedro: trying to detect the mysterious error:
26345 // Security error" code: "1000
26350 this.getElementTreeXPath = function(element)
26354 for (; element && element.nodeType == 1; element = element.parentNode)
26357 var nodeName = element.nodeName;
26359 for (var sibling = element.previousSibling; sibling; sibling = sibling.previousSibling)
26361 if (sibling.nodeType != 1) continue;
26363 if (sibling.nodeName == nodeName)
26367 var tagName = element.nodeName.toLowerCase();
26368 var pathIndex = (index ? "[" + (index+1) + "]" : "");
26369 paths.splice(0, 0, tagName + pathIndex);
26372 return paths.length ? "/" + paths.join("/") : null;
26375 this.getElementsByXPath = function(doc, xpath)
26380 var result = doc.evaluate(xpath, doc, null, XPathResult.ANY_TYPE, null);
26381 for (var item = result.iterateNext(); item; item = result.iterateNext())
26386 // Invalid xpath expressions make their way here sometimes. If that happens,
26387 // we still want to return an empty set without an exception.
26393 this.getRuleMatchingElements = function(rule, doc)
26395 var css = rule.selectorText;
26396 var xpath = this.cssToXPath(css);
26397 return this.getElementsByXPath(doc, xpath);
26406 FBL.ns(function() { with (FBL) {
26408 // ************************************************************************************************
26409 // ************************************************************************************************
26410 // ************************************************************************************************
26411 // ************************************************************************************************
26412 // ************************************************************************************************
26414 var toCamelCase = function toCamelCase(s)
26416 return s.replace(reSelectorCase, toCamelCaseReplaceFn);
26419 var toSelectorCase = function toSelectorCase(s)
26421 return s.replace(reCamelCase, "-$1").toLowerCase();
26425 var reCamelCase = /([A-Z])/g;
26426 var reSelectorCase = /\-(.)/g;
26427 var toCamelCaseReplaceFn = function toCamelCaseReplaceFn(m,g)
26429 return g.toUpperCase();
26432 // ************************************************************************************************
26434 var ElementCache = Firebug.Lite.Cache.Element;
26435 var StyleSheetCache = Firebug.Lite.Cache.StyleSheet;
26437 // ************************************************************************************************
26438 // ************************************************************************************************
26439 // ************************************************************************************************
26440 // ************************************************************************************************
26441 // ************************************************************************************************
26442 // ************************************************************************************************
26445 // ************************************************************************************************
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;
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;
26460 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
26461 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
26462 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
26463 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
26464 Firebug.SourceBoxPanel = Firebug.Panel;
26466 var reSelectorTag = /(^|\s)(?:\w+)/g;
26468 var domUtils = null;
26470 var textContent = isIE ? "innerText" : "textContent";
26471 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
26472 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
26473 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
26474 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
26476 var CSSDomplateBase = {
26477 isEditable: function(rule)
26479 return !rule.isSystemSheet;
26481 isSelectorEditable: function(rule)
26483 return rule.isSelectorEditable && this.isEditable(rule);
26487 var CSSPropTag = domplate(CSSDomplateBase, {
26488 tag: DIV({"class": "cssProp focusRow", $disabledStyle: "$prop.disabled",
26489 $editGroup: "$rule|isEditable",
26490 $cssOverridden: "$prop.overridden", role : "option"},
26491 A({"class": "cssPropDisable"}, " "),
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"}, ";")
26500 TAG("$rule.tag", {rule: "$rule"});
26502 var CSSImportRuleTag = domplate({
26503 tag: DIV({"class": "cssRule insertInto focusRow importRule", _repObject: "$rule.rule"},
26505 A({"class": "objectLink", _repObject: "$rule.rule.styleSheet"}, "$rule.rule.href"),
26510 var CSSStyleRuleTag = domplate(CSSDomplateBase, {
26511 tag: DIV({"class": "cssRule insertInto",
26512 $cssEditableRule: "$rule|isEditable",
26513 $editGroup: "$rule|isSelectorEditable",
26514 _repObject: "$rule.rule",
26515 "ruleId": "$rule.id", role : 'presentation'},
26516 DIV({"class": "cssHead focusRow", role : 'listitem'},
26517 SPAN({"class": "cssSelector", $editable: "$rule|isSelectorEditable"}, "$rule.selector"), " {"
26519 DIV({role : 'group'},
26520 DIV({"class": "cssPropertyListBox", role : 'listbox'},
26521 FOR("prop", "$rule.props",
26522 TAG(CSSPropTag.tag, {rule: "$rule", prop: "$prop"})
26526 DIV({"class": "editable insertBefore", role:"presentation"}, "}")
26530 var reSplitCSS = /(url\("?[^"\)]+?"?\))|(rgb\(.*?\))|(#[\dA-Fa-f]+)|(-?\d+(\.\d+)?(%|[a-z]{1,2})?)|([^,\s]+)|"(.*?)"/;
26532 var reURL = /url\("?([^"\)]+)?"?\)/;
26534 var reRepeat = /no-repeat|repeat-x|repeat-y|repeat/;
26536 //const sothinkInstalled = !!$("swfcatcherKey_sidebar");
26537 var sothinkInstalled = false;
26560 "background-color",
26561 "background-image",
26562 "background-repeat",
26563 "background-position",
26564 "background-attachment",
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"
26618 "overflow-x", // http://www.w3.org/TR/2002/WD-css3-box-20021024/#overflow
26630 "list-style-image",
26631 "list-style-position",
26641 var styleGroupTitles =
26644 background: "Background",
26650 Firebug.CSSModule = extend(Firebug.Module,
26652 freeEdit: function(styleSheet, value)
26654 if (!styleSheet.editStyleSheet)
26656 var ownerNode = getStyleSheetOwnerNode(styleSheet);
26657 styleSheet.disabled = true;
26659 var url = CCSV("@mozilla.org/network/standard-url;1", Components.interfaces.nsIURL);
26660 url.spec = styleSheet.href;
26662 var editStyleSheet = ownerNode.ownerDocument.createElementNS(
26663 "http://www.w3.org/1999/xhtml",
26665 unwrapObject(editStyleSheet).firebugIgnore = true;
26666 editStyleSheet.setAttribute("type", "text/css");
26667 editStyleSheet.setAttributeNS(
26668 "http://www.w3.org/XML/1998/namespace",
26671 if (ownerNode.hasAttribute("media"))
26673 editStyleSheet.setAttribute("media", ownerNode.getAttribute("media"));
26676 // Insert the edited stylesheet directly after the old one to ensure the styles
26677 // cascade properly.
26678 ownerNode.parentNode.insertBefore(editStyleSheet, ownerNode.nextSibling);
26680 styleSheet.editStyleSheet = editStyleSheet;
26683 styleSheet.editStyleSheet.innerHTML = value;
26684 if (FBTrace.DBG_CSS)
26685 FBTrace.sysout("css.saveEdit styleSheet.href:"+styleSheet.href+" got innerHTML:"+value+"\n");
26687 dispatch(this.fbListeners, "onCSSFreeEdit", [styleSheet, value]);
26690 insertRule: function(styleSheet, cssText, ruleIndex)
26692 if (FBTrace.DBG_CSS) FBTrace.sysout("Insert: " + ruleIndex + " " + cssText);
26693 var insertIndex = styleSheet.insertRule(cssText, ruleIndex);
26695 dispatch(this.fbListeners, "onCSSInsertRule", [styleSheet, cssText, ruleIndex]);
26697 return insertIndex;
26700 deleteRule: function(styleSheet, ruleIndex)
26702 if (FBTrace.DBG_CSS) FBTrace.sysout("deleteRule: " + ruleIndex + " " + styleSheet.cssRules.length, styleSheet.cssRules);
26703 dispatch(this.fbListeners, "onCSSDeleteRule", [styleSheet, ruleIndex]);
26705 styleSheet.deleteRule(ruleIndex);
26708 setProperty: function(rule, propName, propValue, propPriority)
26710 var style = rule.style || rule;
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;
26717 if (style.getPropertyValue)
26719 var prevValue = style.getPropertyValue(propName);
26720 var prevPriority = style.getPropertyPriority(propName);
26722 // XXXjoe Gecko bug workaround: Just changing priority doesn't have any effect
26723 // unless we remove the property first
26724 style.removeProperty(propName);
26726 style.setProperty(propName, propValue, propPriority);
26731 // TODO: xxxpedro parse CSS rule to find property priority in IE?
26732 //console.log(propName, propValue);
26733 style[toCamelCase(propName)] = propValue;
26737 dispatch(this.fbListeners, "onCSSSetProperty", [style, propName, propValue, propPriority, prevValue, prevPriority, rule, baseText]);
26741 removeProperty: function(rule, propName, parent)
26743 var style = rule.style || rule;
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;
26749 if (style.getPropertyValue)
26752 var prevValue = style.getPropertyValue(propName);
26753 var prevPriority = style.getPropertyPriority(propName);
26755 style.removeProperty(propName);
26759 style[toCamelCase(propName)] = "";
26763 dispatch(this.fbListeners, "onCSSRemoveProperty", [style, propName, prevValue, prevPriority, rule, baseText]);
26767 cleanupSheets: function(doc, context)
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.
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);
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++)
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()));
26799 if (FBTrace.DBG_ERRORS)
26800 FBTrace.sysout("css.show: sheet.cssRules FAILS for "+(styleSheets[i]?styleSheets[i].href:"null sheet")+e, e);
26804 cleanupSheetHandler: function(event, context)
26806 var target = event.target || event.srcElement,
26807 tagName = (target.tagName || "").toLowerCase();
26808 if (tagName == "link")
26810 this.cleanupSheets(target.ownerDocument, context);
26813 watchWindow: function(context, win)
26815 var cleanupSheets = bind(this.cleanupSheets, this),
26816 cleanupSheetHandler = bind(this.cleanupSheetHandler, this, context),
26817 doc = win.document;
26819 //doc.addEventListener("DOMAttrModified", cleanupSheetHandler, false);
26820 //doc.addEventListener("DOMNodeInserted", cleanupSheetHandler, false);
26822 loadedContext: function(context)
26825 iterateWindows(context.browser.contentWindow, function(subwin)
26827 self.cleanupSheets(subwin.document, context);
26833 // ************************************************************************************************
26835 Firebug.CSSStyleSheetPanel = function() {};
26837 Firebug.CSSStyleSheetPanel.prototype = extend(Firebug.SourceBoxPanel,
26839 template: domplate(
26842 DIV({"class": "cssSheet insertInto a11yCSSView"},
26843 FOR("rule", "$rules",
26846 DIV({"class": "cssSheet editable insertBefore"}, "")
26850 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
26852 refresh: function()
26855 this.updateLocation(this.location);
26856 else if (this.selection)
26857 this.updateSelection(this.selection);
26860 toggleEditing: function()
26862 if (!this.stylesheetEditor)
26863 this.stylesheetEditor = new StyleSheetEditor(this.document);
26866 Firebug.Editor.stopEditing();
26869 if (!this.location)
26872 var styleSheet = this.location.editStyleSheet
26873 ? this.location.editStyleSheet.sheet
26876 var css = getStyleSheetCSS(styleSheet, this.context);
26877 //var topmost = getTopmostRuleLine(this.panelNode);
26879 this.stylesheetEditor.styleSheet = this.location;
26880 Firebug.Editor.startEditing(this.panelNode, css, this.stylesheetEditor);
26881 //this.stylesheetEditor.scrollToLine(topmost.line, topmost.offset);
26885 getStylesheetURL: function(rule)
26887 if (this.location.href)
26888 return this.location.href;
26890 return this.context.window.location.href;
26893 getRuleByLine: function(styleSheet, line)
26898 var cssRules = styleSheet.cssRules;
26899 for (var i = 0; i < cssRules.length; ++i)
26901 var rule = cssRules[i];
26902 if (rule instanceof CSSStyleRule)
26904 var ruleLine = domUtils.getRuleLine(rule);
26905 if (ruleLine >= line)
26911 highlightRule: function(rule)
26913 var ruleElement = Firebug.getElementByRepObject(this.panelNode.firstChild, rule);
26916 scrollIntoCenterView(ruleElement, this.panelNode);
26917 setClassTimed(ruleElement, "jumpHighlight", this.context);
26921 getStyleSheetRules: function(context, styleSheet)
26923 var isSystemSheet = isSystemStyleSheet(styleSheet);
26925 function appendRules(cssRules)
26927 for (var i = 0; i < cssRules.length; ++i)
26929 var rule = cssRules[i];
26931 // TODO: xxxpedro opera instanceof stylesheet remove the following comments when
26932 // the issue with opera and style sheet Classes has been solved.
26934 //if (rule instanceof CSSStyleRule)
26935 if (instanceOf(rule, "CSSStyleRule"))
26937 var props = this.getRuleProperties(context, rule);
26938 //var line = domUtils.getRuleLine(rule);
26941 var selector = rule.selectorText;
26945 selector = selector.replace(reSelectorTag,
26946 function(s){return s.toLowerCase();});
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});
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]);
26963 if (FBTrace.DBG_ERRORS || FBTrace.DBG_CSS)
26964 FBTrace.sysout("css getStyleSheetRules failed to classify a rule ", rule);
26970 appendRules.apply(this, [styleSheet.cssRules || styleSheet.rules]);
26974 parseCSSProps: function(style, inheritMode)
26978 if (Firebug.expandShorthandProps)
26980 var count = style.length-1,
26981 index = style.length;
26984 var propName = style.item(count - index);
26985 this.addProperty(propName, style.getPropertyValue(propName), !!style.getPropertyPriority(propName), false, inheritMode, props);
26990 var lines = style.cssText.match(/(?:[^;\(]*(?:\([^\)]*?\))?[^;\(]*)*;?/g);
26991 var propRE = /\s*([^:\s]*)\s*:\s*(.*?)\s*(! important)?;?$/;
26993 // TODO: xxxpedro port to firebug: variable leaked into global namespace
26996 while(line=lines[i++]){
26997 m = propRE.exec(line);
27000 //var name = m[1], value = m[2], important = !!m[3];
27002 this.addProperty(m[1], m[2], !!m[3], false, inheritMode, props);
27009 getRuleProperties: function(context, rule, inheritMode)
27011 var props = this.parseCSSProps(rule.style, inheritMode);
27013 // TODO: xxxpedro port to firebug: variable leaked into global namespace
27014 //var line = domUtils.getRuleLine(rule);
27016 var ruleId = rule.selectorText+"/"+line;
27017 this.addOldProperties(context, ruleId, inheritMode, props);
27018 sortProperties(props);
27023 addOldProperties: function(context, ruleId, inheritMode, props)
27025 if (context.selectorMap && context.selectorMap.hasOwnProperty(ruleId) )
27027 var moreProps = context.selectorMap[ruleId];
27028 for (var i = 0; i < moreProps.length; ++i)
27030 var prop = moreProps[i];
27031 this.addProperty(prop.name, prop.value, prop.important, true, inheritMode, props);
27036 addProperty: function(name, value, important, disabled, inheritMode, props)
27038 name = name.toLowerCase();
27040 if (inheritMode && !inheritedStyleNames[name])
27043 name = this.translateName(name, value);
27046 value = stripUnits(rgbToHex(value));
27047 important = important ? " !important" : "";
27049 var prop = {name: name, value: value, important: important, disabled: disabled};
27054 translateName: function(name, value)
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")))
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!
27090 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
27092 editElementStyle: function()
27094 ///var rulesBox = this.panelNode.getElementsByClassName("cssElementRuleContainer")[0];
27095 var rulesBox = $$(".cssElementRuleContainer", this.panelNode)[0];
27096 var styleRuleBox = rulesBox && Firebug.getElementByRepObject(rulesBox, this.selection);
27099 var rule = {rule: this.selection, inherited: false, selector: "element.style", props: []};
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);
27108 ///styleRuleBox = styleRuleBox.getElementsByClassName("cssElementRuleContainer")[0];
27109 styleRuleBox = $$(".cssElementRuleContainer", styleRuleBox)[0];
27112 styleRuleBox = this.template.ruleTag.insertBefore({rule: rule}, rulesBox);
27114 ///styleRuleBox = styleRuleBox.getElementsByClassName("insertInto")[0];
27115 styleRuleBox = $$(".insertInto", styleRuleBox)[0];
27118 Firebug.Editor.insertRowForObject(styleRuleBox);
27121 insertPropertyRow: function(row)
27123 Firebug.Editor.insertRowForObject(row);
27126 insertRule: function(row)
27128 var location = getAncestorByClass(row, "cssRule");
27131 location = getChildByClass(this.panelNode, "cssSheet");
27132 Firebug.Editor.insertRowForObject(location);
27136 Firebug.Editor.insertRow(location, "before");
27140 editPropertyRow: function(row)
27142 var propValueBox = getChildByClass(row, "cssPropValue");
27143 Firebug.Editor.startEditing(propValueBox);
27146 deletePropertyRow: function(row)
27148 var rule = Firebug.getRepObject(row);
27149 var propName = getChildByClass(row, "cssPropName")[textContent];
27150 Firebug.CSSModule.removeProperty(rule, propName);
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) )
27156 var map = this.context.selectorMap[ruleId];
27157 for (var i = 0; i < map.length; ++i)
27159 if (map[i].name == propName)
27166 if (this.name == "stylesheet")
27167 dispatch([Firebug.A11yModel], 'onInlineEditorClose', [this, row.firstChild, true]);
27168 row.parentNode.removeChild(row);
27170 this.markChange(this.name == "stylesheet");
27173 disablePropertyRow: function(row)
27175 toggleClass(row, "disabledStyle");
27177 var rule = Firebug.getRepObject(row);
27178 var propName = getChildByClass(row, "cssPropName")[textContent];
27180 if (!this.context.selectorMap)
27181 this.context.selectorMap = {};
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] = [];
27188 var map = this.context.selectorMap[ruleId];
27189 var propValue = getChildByClass(row, "cssPropValue")[textContent];
27190 var parsedValue = parsePriority(propValue);
27191 if (hasClass(row, "disabledStyle"))
27193 Firebug.CSSModule.removeProperty(rule, propName);
27195 map.push({"name": propName, "value": parsedValue.value,
27196 "important": parsedValue.priority});
27200 Firebug.CSSModule.setProperty(rule, propName, parsedValue.value, parsedValue.priority);
27202 var index = findPropByName(map, propName);
27203 map.splice(index, 1);
27206 this.markChange(this.name == "stylesheet");
27209 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
27211 onMouseDown: function(event)
27213 //console.log("onMouseDown", event.target || event.srcElement, event);
27215 // xxxpedro adjusting coordinates because the panel isn't a window yet
27216 var offset = event.clientX - this.panelNode.parentNode.offsetLeft;
27218 // XXjoe Hack to only allow clicking on the checkbox
27219 if (!isLeftClick(event) || offset > 20)
27222 var target = event.target || event.srcElement;
27223 if (hasClass(target, "textEditor"))
27226 var row = getAncestorByClass(target, "cssProp");
27227 if (row && hasClass(row, "editGroup"))
27229 this.disablePropertyRow(row);
27230 cancelEvent(event);
27234 onDoubleClick: function(event)
27236 //console.log("onDoubleClick", event.target || event.srcElement, event);
27238 // xxxpedro adjusting coordinates because the panel isn't a window yet
27239 var offset = event.clientX - this.panelNode.parentNode.offsetLeft;
27241 if (!isLeftClick(event) || offset <= 20)
27244 var target = event.target || event.srcElement;
27246 //console.log("ok", target, hasClass(target, "textEditorInner"), !isLeftClick(event), offset <= 20);
27248 // if the inline editor was clicked, don't insert a new rule
27249 if (hasClass(target, "textEditorInner"))
27252 var row = getAncestorByClass(target, "cssRule");
27253 if (row && !getAncestorByClass(target, "cssPropName")
27254 && !getAncestorByClass(target, "cssPropValue"))
27256 this.insertPropertyRow(row);
27257 cancelEvent(event);
27261 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
27264 name: "stylesheet",
27268 dependents: ["css", "stylesheet", "dom", "domSide", "layout"],
27272 hasToolButtons: true
27277 Firebug.Panel.create.apply(this, arguments);
27279 this.onMouseDown = bind(this.onMouseDown, this);
27280 this.onDoubleClick = bind(this.onDoubleClick, this);
27282 if (this.name == "stylesheet")
27284 this.onChangeSelect = bind(this.onChangeSelect, this);
27286 var doc = Firebug.browser.document;
27287 var selectNode = this.selectNode = createElement("select");
27289 CssAnalyzer.processAllStyleSheets(doc, function(doc, styleSheet)
27291 var key = StyleSheetCache.key(styleSheet);
27292 var fileName = getFileName(styleSheet.href) || getFileName(doc.location.href);
27293 var option = createElement("option", {value: key});
27295 option.appendChild(Firebug.chrome.document.createTextNode(fileName));
27296 selectNode.appendChild(option);
27299 this.toolButtonsNode.appendChild(selectNode);
27304 onChangeSelect: function(event)
27306 event = event || window.event;
27307 var target = event.srcElement || event.currentTarget;
27308 var key = target.value;
27309 var styleSheet = StyleSheetCache.get(key);
27311 this.updateLocation(styleSheet);
27314 initialize: function()
27316 Firebug.Panel.initialize.apply(this, arguments);
27321 // domUtils = CCSV("@mozilla.org/inspector/dom-utils;1", "inIDOMUtils");
27323 // if (FBTrace.DBG_ERRORS)
27324 // FBTrace.sysout("@mozilla.org/inspector/dom-utils;1 FAILED to load: "+exc, exc);
27329 this.context = Firebug.chrome; // TODO: xxxpedro css2
27330 this.document = Firebug.chrome.document; // TODO: xxxpedro css2
27332 this.initializeNode();
27334 if (this.name == "stylesheet")
27336 var styleSheets = Firebug.browser.document.styleSheets;
27338 if (styleSheets.length > 0)
27340 addEvent(this.selectNode, "change", this.onChangeSelect);
27342 this.updateLocation(styleSheets[0]);
27346 //Firebug.SourceBoxPanel.initialize.apply(this, arguments);
27349 shutdown: function()
27351 // must destroy the editor when we leave the panel to avoid problems (Issue 2981)
27352 Firebug.Editor.stopEditing();
27354 if (this.name == "stylesheet")
27356 removeEvent(this.selectNode, "change", this.onChangeSelect);
27359 this.destroyNode();
27361 Firebug.Panel.shutdown.apply(this, arguments);
27364 destroy: function(state)
27366 //state.scrollTop = this.panelNode.scrollTop ? this.panelNode.scrollTop : this.lastScrollTop;
27368 //persistObjects(this, state);
27370 // xxxpedro we are stopping the editor in the shutdown method already
27371 //Firebug.Editor.stopEditing();
27372 Firebug.Panel.destroy.apply(this, arguments);
27375 initializeNode: function(oldPanelNode)
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']);
27383 destroyNode: function()
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']);
27391 ishow: function(state)
27393 Firebug.Inspector.stopInspecting(true);
27395 this.showToolbarButtons("fbCSSButtons", true);
27397 if (this.context.loaded && !this.location) // wait for loadedContext to restore the panel
27399 restoreObjects(this, state);
27401 if (!this.location)
27402 this.location = this.getDefaultLocation();
27404 if (state && state.scrollTop)
27405 this.panelNode.scrollTop = state.scrollTop;
27411 this.showToolbarButtons("fbCSSButtons", false);
27413 this.lastScrollTop = this.panelNode.scrollTop;
27416 supportsObject: function(object)
27418 if (object instanceof CSSStyleSheet)
27420 else if (object instanceof CSSStyleRule)
27422 else if (object instanceof CSSStyleDeclaration)
27424 else if (object instanceof SourceLink && object.type == "css" && reCSS.test(object.href))
27430 updateLocation: function(styleSheet)
27434 if (styleSheet.editStyleSheet)
27435 styleSheet = styleSheet.editStyleSheet.sheet;
27437 // if it is a restricted stylesheet, show the warning message and abort the update process
27438 if (styleSheet.restricted)
27440 FirebugReps.Warning.tag.replace({object: "AccessRestricted"}, this.panelNode);
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. ",
27446 href: "http://getfirebug.com/wiki/index.php/Firebug_Lite_FAQ#I_keep_seeing_.22Access_to_restricted_URI_denied.22"
27447 }, this.panelNode);
27452 var rules = this.getStyleSheetRules(this.context, styleSheet);
27456 // FIXME xxxpedro chromenew this is making iPad's Safari to crash
27457 result = this.template.tag.replace({rules: rules}, this.panelNode);
27459 result = FirebugReps.Warning.tag.replace({object: "EmptyStyleSheet"}, this.panelNode);
27461 // TODO: xxxpedro need to fix showToolbarButtons function
27462 //this.showToolbarButtons("fbCSSButtons", !isSystemStyleSheet(this.location));
27464 //dispatch([Firebug.A11yModel], 'onCSSRulesAdded', [this, this.panelNode]);
27467 updateSelection: function(object)
27469 this.selection = null;
27471 if (object instanceof CSSStyleDeclaration) {
27472 object = object.parentRule;
27475 if (object instanceof CSSStyleRule)
27477 this.navigate(object.parentStyleSheet);
27478 this.highlightRule(object);
27480 else if (object instanceof CSSStyleSheet)
27482 this.navigate(object);
27484 else if (object instanceof SourceLink)
27488 var sourceLink = object;
27490 var sourceFile = getSourceFileByHref(sourceLink.href, this.context);
27493 clearNode(this.panelNode); // replace rendered stylesheets
27494 this.showSourceFile(sourceFile);
27496 var lineNo = object.line;
27498 this.scrollToLine(lineNo, this.jumpHighlightFactory(lineNo, this.context));
27500 else // XXXjjb we should not be taking this path
27502 var stylesheet = getStyleSheetByHref(sourceLink.href, this.context);
27504 this.navigate(stylesheet);
27507 if (FBTrace.DBG_CSS)
27508 FBTrace.sysout("css.updateSelection no sourceFile for "+sourceLink.href, sourceLink);
27513 if (FBTrace.DBG_CSS)
27514 FBTrace.sysout("css.upDateSelection FAILS "+exc, exc);
27519 updateOption: function(name, value)
27521 if (name == "expandShorthandProps")
27525 getLocationList: function()
27527 var styleSheets = getAllStyleSheets(this.context);
27528 return styleSheets;
27531 getOptionsMenuItems: function()
27534 {label: "Expand Shorthand Properties", type: "checkbox", checked: Firebug.expandShorthandProps,
27535 command: bindFixed(Firebug.togglePref, Firebug, "expandShorthandProps") },
27537 {label: "Refresh", command: bind(this.refresh, this) }
27541 getContextMenuItems: function(style, target)
27545 if (this.infoTipType == "color")
27548 {label: "CopyColor",
27549 command: bindFixed(copyToClipboard, FBL, this.infoTipObject) }
27552 else if (this.infoTipType == "image")
27555 {label: "CopyImageLocation",
27556 command: bindFixed(copyToClipboard, FBL, this.infoTipObject) },
27557 {label: "OpenImageInNewTab",
27558 command: bindFixed(openNewTab, FBL, this.infoTipObject) }
27562 ///if (this.selection instanceof Element)
27563 if (isElement(this.selection))
27567 {label: "EditStyle",
27568 command: bindFixed(this.editElementStyle, this) }
27571 else if (!isSystemStyleSheet(this.selection))
27576 command: bindFixed(this.insertRule, this, target) }
27580 var cssRule = getAncestorByClass(target, "cssRule");
27581 if (cssRule && hasClass(cssRule, "cssEditableRule"))
27586 command: bindFixed(this.insertPropertyRow, this, target) }
27589 var propRow = getAncestorByClass(target, "cssProp");
27592 var propName = getChildByClass(propRow, "cssPropName")[textContent];
27593 var isDisabled = hasClass(propRow, "disabledStyle");
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) }
27609 {label: "Refresh", command: bind(this.refresh, this) }
27615 browseObject: function(object)
27617 if (this.infoTipType == "image")
27619 openNewTab(this.infoTipObject);
27624 showInfoTip: function(infoTip, target, x, y)
27626 var propValue = getAncestorByClass(target, "cssPropValue");
27629 var offset = getClientOffset(propValue);
27630 var offsetX = x-offset.x;
27632 var text = propValue[textContent];
27633 var charWidth = propValue.offsetWidth/text.length;
27634 var charOffset = Math.floor(offsetX/charWidth);
27636 var cssValue = parseCSSValue(text, charOffset);
27639 if (cssValue.value == this.infoTipValue)
27642 this.infoTipValue = cssValue.value;
27644 if (cssValue.type == "rgb" || (!cssValue.type && isColorKeyword(cssValue.value)))
27646 this.infoTipType = "color";
27647 this.infoTipObject = cssValue.value;
27649 return Firebug.InfoTip.populateColorInfoTip(infoTip, cssValue.value);
27651 else if (cssValue.type == "url")
27653 ///var propNameNode = target.parentNode.getElementsByClassName("cssPropName").item(0);
27654 var propNameNode = getElementByClass(target.parentNode, "cssPropName");
27655 if (propNameNode && isImageRule(propNameNode[textContent]))
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);
27663 this.infoTipType = "image";
27664 this.infoTipObject = absURL;
27666 return Firebug.InfoTip.populateImageInfoTip(infoTip, absURL, repeat);
27672 delete this.infoTipType;
27673 delete this.infoTipValue;
27674 delete this.infoTipObject;
27677 getEditor: function(target, value)
27679 if (target == this.panelNode
27680 || hasClass(target, "cssSelector") || hasClass(target, "cssRule")
27681 || hasClass(target, "cssSheet"))
27683 if (!this.ruleEditor)
27684 this.ruleEditor = new CSSRuleEditor(this.document);
27686 return this.ruleEditor;
27691 this.editor = new CSSEditor(this.document);
27693 return this.editor;
27697 getDefaultLocation: function()
27701 var styleSheets = this.context.window.document.styleSheets;
27702 if (styleSheets.length)
27704 var sheet = styleSheets[0];
27705 return (Firebug.filterSystemURLs && isSystemURL(getURLForStyleSheet(sheet))) ? null : sheet;
27710 if (FBTrace.DBG_LOCATIONS)
27711 FBTrace.sysout("css.getDefaultLocation FAILS "+exc, exc);
27715 getObjectDescription: function(styleSheet)
27717 var url = getURLForStyleSheet(styleSheet);
27718 var instance = getInstanceForStyleSheet(styleSheet);
27720 var baseDescription = splitURLBase(url);
27722 baseDescription.name = baseDescription.name + " #" + (instance + 1);
27724 return baseDescription;
27727 search: function(text, reverse)
27729 var curDoc = this.searchCurrentDoc(!Firebug.searchGlobal, text, reverse);
27730 if (!curDoc && Firebug.searchGlobal)
27732 return this.searchOtherDocs(text, reverse);
27737 searchOtherDocs: function(text, reverse)
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++)
27745 if (scanRE.test(styleSheet.cssRules[i].cssText))
27752 if (this.navigateToNextDocument(scanDoc, reverse))
27754 return this.searchCurrentDoc(true, text, reverse);
27758 searchCurrentDoc: function(wrapSearch, text, reverse)
27762 delete this.currentSearch;
27767 if (this.currentSearch && text == this.currentSearch.text)
27769 row = this.currentSearch.findNext(wrapSearch, false, reverse, Firebug.Search.isCaseSensitive(text));
27775 this.currentSearch = new TextSearch(this.stylesheetEditor.box);
27776 row = this.currentSearch.find(text, reverse, Firebug.Search.isCaseSensitive(text));
27780 var sel = this.document.defaultView.getSelection();
27781 sel.removeAllRanges();
27782 sel.addRange(this.currentSearch.range);
27783 scrollSelectionIntoView(this);
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));
27799 this.document.defaultView.getSelection().selectAllChildren(row);
27800 scrollIntoCenterView(row, this.panelNode);
27801 dispatch([Firebug.A11yModel], 'onCSSSearchMatchFound', [this, text, row]);
27806 dispatch([Firebug.A11yModel], 'onCSSSearchMatchFound', [this, text, null]);
27811 getSearchOptionsMenuItems: function()
27814 Firebug.Search.searchOptionMenu("search.Case_Sensitive", "searchCaseSensitive"),
27815 Firebug.Search.searchOptionMenu("search.Multiple_Files", "searchGlobal")
27820 // ************************************************************************************************
27822 function CSSElementPanel() {}
27824 CSSElementPanel.prototype = extend(Firebug.CSSStyleSheetPanel.prototype,
27826 template: domplate(
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"})
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"})
27841 DIV({role : 'group'},
27842 FOR("rule", "$section.rules",
27843 TAG("$ruleTag", {rule: "$rule"})
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"})
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"})
27866 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
27868 updateCascadeView: function(element)
27870 //dispatch([Firebug.A11yModel], 'onBeforeCSSRulesAdded', [this]);
27871 var rules = [], sections = [], usedProps = {};
27872 this.getInheritedRules(element, sections, usedProps);
27873 this.getElementRules(element, rules, usedProps);
27875 if (rules.length || sections.length)
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]);
27884 var result = FirebugReps.Warning.tag.replace({object: "EmptyElementCSS"}, this.panelNode);
27885 //dispatch([Firebug.A11yModel], 'onCSSRulesAdded', [this, result]);
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. ",
27894 href: "http://getfirebug.com/wiki/index.php/Firebug_Lite_FAQ#I_keep_seeing_.22This_element_has_no_style_rules.22"
27895 }, this.panelNode);
27898 getStylesheetURL: function(rule)
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;
27905 return this.selection.ownerDocument.location.href;
27908 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
27910 getInheritedRules: function(element, sections, usedProps)
27912 var parent = element.parentNode;
27913 if (parent && parent.nodeType == 1)
27915 this.getInheritedRules(parent, sections, usedProps);
27918 this.getElementRules(parent, rules, usedProps, true);
27921 sections.splice(0, 0, {element: parent, rules: rules});
27925 getElementRules: function(element, rules, usedProps, inheritMode)
27927 var inspectedRules, displayedRules = {};
27929 inspectedRules = CssAnalyzer.getElementCSSRules(element);
27931 if (inspectedRules)
27933 for (var i = 0, length=inspectedRules.length; i < length; ++i)
27935 var ruleId = inspectedRules[i];
27936 var ruleData = CssAnalyzer.getRuleData(ruleId);
27937 var rule = ruleData.rule;
27939 var ssid = ruleData.styleSheetId;
27940 var parentStyleSheet = StyleSheetCache.get(ssid);
27942 var href = parentStyleSheet.externalURL ? parentStyleSheet.externalURL : parentStyleSheet.href; // Null means inline
27944 var instance = null;
27945 //var instance = getInstanceForStyleSheet(rule.parentStyleSheet, element.ownerDocument);
27947 var isSystemSheet = false;
27948 //var isSystemSheet = isSystemStyleSheet(rule.parentStyleSheet);
27950 if (!Firebug.showUserAgentCSS && isSystemSheet) // This removes user agent rules
27954 href = element.ownerDocument.location.href; // http://code.google.com/p/fbug/issues/detail?id=452
27956 var props = this.getRuleProperties(this.context, rule, inheritMode);
27957 if (inheritMode && !props.length)
27961 //var line = domUtils.getRuleLine(rule);
27962 // TODO: xxxpedro CSS line number
27963 var line = ruleData.lineNo;
27965 var ruleId = rule.selectorText+"/"+line;
27966 var sourceLink = new SourceLink(href, line, "css", rule, instance);
27968 this.markOverridenProps(props, usedProps, inheritMode);
27970 rules.splice(0, 0, {rule: rule, id: ruleId,
27971 selector: ruleData.selector, sourceLink: sourceLink,
27972 props: props, inherited: inheritMode,
27973 isSystemSheet: isSystemSheet});
27978 this.getStyleProperties(element, rules, usedProps, inheritMode);
27980 if (FBTrace.DBG_CSS)
27981 FBTrace.sysout("getElementRules "+rules.length+" rules for "+getElementXPath(element), rules);
27984 getElementRules: function(element, rules, usedProps, inheritMode)
27986 var inspectedRules, displayedRules = {};
27989 inspectedRules = domUtils ? domUtils.getCSSStyleRules(element) : null;
27992 if (inspectedRules)
27994 for (var i = 0; i < inspectedRules.Count(); ++i)
27996 var rule = QI(inspectedRules.GetElementAt(i), nsIDOMCSSStyleRule);
27998 var href = rule.parentStyleSheet.href; // Null means inline
28000 var instance = getInstanceForStyleSheet(rule.parentStyleSheet, element.ownerDocument);
28002 var isSystemSheet = isSystemStyleSheet(rule.parentStyleSheet);
28003 if (!Firebug.showUserAgentCSS && isSystemSheet) // This removes user agent rules
28006 href = element.ownerDocument.location.href; // http://code.google.com/p/fbug/issues/detail?id=452
28008 var props = this.getRuleProperties(this.context, rule, inheritMode);
28009 if (inheritMode && !props.length)
28012 var line = domUtils.getRuleLine(rule);
28013 var ruleId = rule.selectorText+"/"+line;
28014 var sourceLink = new SourceLink(href, line, "css", rule, instance);
28016 this.markOverridenProps(props, usedProps, inheritMode);
28018 rules.splice(0, 0, {rule: rule, id: ruleId,
28019 selector: rule.selectorText, sourceLink: sourceLink,
28020 props: props, inherited: inheritMode,
28021 isSystemSheet: isSystemSheet});
28026 this.getStyleProperties(element, rules, usedProps, inheritMode);
28028 if (FBTrace.DBG_CSS)
28029 FBTrace.sysout("getElementRules "+rules.length+" rules for "+getElementXPath(element), rules);
28032 markOverridenProps: function(props, usedProps, inheritMode)
28034 for (var i = 0; i < props.length; ++i)
28036 var prop = props[i];
28037 if ( usedProps.hasOwnProperty(prop.name) )
28039 var deadProps = usedProps[prop.name]; // all previous occurrences of this property
28040 for (var j = 0; j < deadProps.length; ++j)
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
28050 usedProps[prop.name] = [];
28052 prop.wasInherited = inheritMode ? true : false;
28053 usedProps[prop.name].push(prop); // all occurrences of a property seen so far, by name
28057 getStyleProperties: function(element, rules, usedProps, inheritMode)
28059 var props = this.parseCSSProps(element.style, inheritMode);
28060 this.addOldProperties(this.context, getElementXPath(element), inheritMode, props);
28062 sortProperties(props);
28063 this.markOverridenProps(props, usedProps, inheritMode);
28067 {rule: element, id: getElementXPath(element),
28068 selector: "element.style", props: props, inherited: inheritMode});
28071 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
28076 parentPanel: "HTML",
28079 initialize: function()
28081 this.context = Firebug.chrome; // TODO: xxxpedro css2
28082 this.document = Firebug.chrome.document; // TODO: xxxpedro css2
28084 Firebug.CSSStyleSheetPanel.prototype.initialize.apply(this, arguments);
28086 // TODO: xxxpedro css2
28087 var selection = ElementCache.get(Firebug.context.persistedState.selectedHTMLElementId);
28089 this.select(selection, true);
28091 //this.updateCascadeView(document.getElementsByTagName("h1")[0]);
28092 //this.updateCascadeView(document.getElementById("build"));
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);
28101 ishow: function(state)
28105 watchWindow: function(win)
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);
28116 unwatchWindow: function(win)
28118 var doc = win.document;
28119 ///removeEvent(doc, "mouseover", this.onHoverChange);
28120 ///removeEvent(doc, "mousedown", this.onActiveChange);
28122 if (isAncestor(this.stateChangeEl, doc))
28124 this.removeStateChangeHandlers();
28128 supportsObject: function(object)
28130 return object instanceof Element ? 1 : 0;
28133 updateView: function(element)
28135 this.updateCascadeView(element);
28138 this.contentState = safeGetContentState(element);
28139 this.addStateChangeHandlers(element);
28143 updateSelection: function(element)
28145 if ( !instanceOf(element , "Element") ) // html supports SourceLink
28148 if (sothinkInstalled)
28150 FirebugReps.Warning.tag.replace({object: "SothinkWarning"}, this.panelNode);
28157 FirebugReps.Warning.tag.replace({object: "DOMInspectorWarning"}, this.panelNode);
28165 this.updateView(element);
28168 updateOption: function(name, value)
28170 if (name == "showUserAgentCSS" || name == "expandShorthandProps")
28174 getOptionsMenuItems: function()
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") }
28182 if (domUtils && this.selection)
28184 var state = safeGetContentState(this.selection);
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)});
28195 updateContentState: function(state, remove)
28197 domUtils.setContentState(remove ? this.selection.ownerDocument.documentElement : this.selection, state);
28201 addStateChangeHandlers: function(el)
28203 this.removeStateChangeHandlers();
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);
28214 this.stateChangeEl = el;
28217 removeStateChangeHandlers: function()
28219 var sel = this.stateChangeEl;
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);
28233 contentStateCheck: function(state)
28235 if (!state || this.contentState & state)
28237 var timeoutRunner = bindFixed(function()
28239 var newState = safeGetContentState(this.selection);
28240 if (newState != this.contentState)
28242 this.context.invalidatePanels(this.name);
28246 // Delay exec until after the event has processed and the state has been updated
28247 setTimeout(timeoutRunner, 0);
28252 function safeGetContentState(selection)
28256 return domUtils.getContentState(selection);
28260 if (FBTrace.DBG_ERRORS)
28261 FBTrace.sysout("css.safeGetContentState; EXCEPTION", e);
28265 // ************************************************************************************************
28267 function CSSComputedElementPanel() {}
28269 CSSComputedElementPanel.prototype = extend(CSSElementPanel.prototype,
28271 template: domplate(
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")
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")
28293 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
28295 updateComputedView: function(element)
28298 element.ownerDocument.parentWindow :
28299 element.ownerDocument.defaultView;
28302 element.currentStyle :
28303 win.getComputedStyle(element, "");
28307 for (var groupName in styleGroups)
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);
28315 var props = styleGroups[groupName];
28316 for (var i = 0; i < props.length; ++i)
28318 var propName = props[i];
28319 var propValue = style.getPropertyValue ?
28320 style.getPropertyValue(propName) :
28321 ""+style[toCamelCase(propName)];
28323 if (propValue === undefined || propValue === null)
28326 propValue = stripUnits(rgbToHex(propValue));
28328 group.props.push({name: propName, value: propValue});
28332 var result = this.template.computedTag.replace({groups: groups}, this.panelNode);
28333 //dispatch([Firebug.A11yModel], 'onCSSRulesAdded', [this, result]);
28336 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
28341 parentPanel: "HTML",
28344 updateView: function(element)
28346 this.updateComputedView(element);
28349 getOptionsMenuItems: function()
28352 {label: "Refresh", command: bind(this.refresh, this) }
28357 // ************************************************************************************************
28360 function CSSEditor(doc)
28362 this.initializeInline(doc);
28365 CSSEditor.prototype = domplate(Firebug.InlineEditor.prototype,
28367 insertNewRow: function(target, insertWhere)
28369 var rule = Firebug.getRepObject(target);
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?
28380 if (insertWhere == "before")
28381 return CSSPropTag.tag.insertBefore({prop: emptyProp, rule: rule}, target);
28383 return CSSPropTag.tag.insertAfter({prop: emptyProp, rule: rule}, target);
28386 saveEdit: function(target, value, previousValue)
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;
28392 target.innerHTML = escapeForCss(value);
28394 var row = getAncestorByClass(target, "cssProp");
28395 if (hasClass(row, "disabledStyle"))
28396 toggleClass(row, "disabledStyle");
28398 var rule = Firebug.getRepObject(target);
28400 if (hasClass(target, "cssPropName"))
28402 if (value && previousValue != value) // name of property has changed.
28404 var propValue = getChildByClass(row, "cssPropValue")[textContent];
28405 var parsedValue = parsePriority(propValue);
28407 if (propValue && propValue != "undefined") {
28408 if (FBTrace.DBG_CSS)
28409 FBTrace.sysout("CSSEditor.saveEdit : "+previousValue+"->"+value+" = "+propValue+"\n");
28411 Firebug.CSSModule.removeProperty(rule, previousValue);
28412 Firebug.CSSModule.setProperty(rule, value, parsedValue.value, parsedValue.priority);
28415 else if (!value) // name of the property has been deleted, so remove the property.
28416 Firebug.CSSModule.removeProperty(rule, previousValue);
28418 else if (getAncestorByClass(target, "cssPropValue"))
28420 var propName = getChildByClass(row, "cssPropName")[textContent];
28421 var propValue = getChildByClass(row, "cssPropValue")[textContent];
28423 if (FBTrace.DBG_CSS)
28425 FBTrace.sysout("CSSEditor.saveEdit propName=propValue: "+propName +" = "+propValue+"\n");
28426 // FBTrace.sysout("CSSEditor.saveEdit BEFORE style:",style);
28429 if (value && value != "null")
28431 var parsedValue = parsePriority(value);
28432 Firebug.CSSModule.setProperty(rule, propName, parsedValue.value, parsedValue.priority);
28434 else if (previousValue && previousValue != "null")
28435 Firebug.CSSModule.removeProperty(rule, propName);
28438 this.panel.markChange(this.panel.name == "stylesheet");
28441 advanceToNext: function(target, charCode)
28443 if (charCode == 58 /*":"*/ && hasClass(target, "cssPropName"))
28447 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
28449 getAutoCompleteRange: function(value, offset)
28451 if (hasClass(this.target, "cssPropName"))
28452 return {start: 0, end: value.length-1};
28454 return parseCSSValue(value, offset);
28457 getAutoCompleteList: function(preExpr, expr, postExpr)
28459 if (hasClass(this.target, "cssPropName"))
28461 return getCSSPropertyNames();
28465 var row = getAncestorByClass(this.target, "cssProp");
28466 var propName = getChildByClass(row, "cssPropName")[textContent];
28467 return getCSSKeywordsByProperty(propName);
28472 //************************************************************************************************
28475 function CSSRuleEditor(doc)
28477 this.initializeInline(doc);
28478 this.completeAsYouType = false;
28480 CSSRuleEditor.uniquifier = 0;
28481 CSSRuleEditor.prototype = domplate(Firebug.InlineEditor.prototype,
28483 insertNewRow: function(target, insertWhere)
28489 isSelectorEditable: true
28492 if (insertWhere == "before")
28493 return CSSStyleRuleTag.tag.insertBefore({rule: emptyRule}, target);
28495 return CSSStyleRuleTag.tag.insertAfter({rule: emptyRule}, target);
28498 saveEdit: function(target, value, previousValue)
28500 if (FBTrace.DBG_CSS)
28501 FBTrace.sysout("CSSRuleEditor.saveEdit: '" + value + "' '" + previousValue + "'", target);
28503 target.innerHTML = escapeForCss(value);
28505 if (value === previousValue) return;
28507 var row = getAncestorByClass(target, "cssRule");
28508 var styleSheet = this.panel.location;
28509 styleSheet = styleSheet.editStyleSheet ? styleSheet.editStyleSheet.sheet : styleSheet;
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))
28516 var searchRule = rule || Firebug.getRepObject(row.nextSibling);
28517 for (ruleIndex=0; ruleIndex<cssRules.length && searchRule!=cssRules[ruleIndex]; ruleIndex++) {}
28520 // Delete in all cases except for new add
28521 // We want to do this before the insert to ease change tracking
28524 Firebug.CSSModule.deleteRule(styleSheet, ruleIndex);
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
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]);
28542 cssText.push(getChildByClass(propEl, "cssPropValue")[textContent]);
28547 cssText = cssText.join("");
28551 var insertLoc = Firebug.CSSModule.insertRule(styleSheet, cssText, ruleIndex);
28552 rule = cssRules[insertLoc];
28557 if (FBTrace.DBG_CSS || FBTrace.DBG_ERRORS)
28558 FBTrace.sysout("CSS Insert Error: "+err, err);
28560 target.innerHTML = escapeForCss(previousValue);
28561 row.repObject = undefined;
28568 // Update the rep object
28569 row.repObject = rule;
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);
28579 this.panel.markChange(this.panel.name == "stylesheet");
28583 // ************************************************************************************************
28584 // StyleSheetEditor
28586 function StyleSheetEditor(doc)
28588 this.box = this.tag.replace({}, doc, this);
28589 this.input = this.box.firstChild;
28592 StyleSheetEditor.prototype = domplate(Firebug.BaseEditor,
28597 TEXTAREA({"class": "styleSheetEditor fullPanelEditor", oninput: "$onInput"})
28600 getValue: function()
28602 return this.input.value;
28605 setValue: function(value)
28607 return this.input.value = value;
28610 show: function(target, panel, value, textSize, targetSize)
28612 this.target = target;
28613 this.panel = panel;
28615 this.panel.panelNode.appendChild(this.box);
28617 this.input.value = value;
28618 this.input.focus();
28620 var command = Firebug.chrome.$("cmd_toggleCSSEditing");
28621 command.setAttribute("checked", true);
28626 var command = Firebug.chrome.$("cmd_toggleCSSEditing");
28627 command.setAttribute("checked", false);
28629 if (this.box.parentNode == this.panel.panelNode)
28630 this.panel.panelNode.removeChild(this.box);
28632 delete this.target;
28634 delete this.styleSheet;
28637 saveEdit: function(target, value, previousValue)
28639 Firebug.CSSModule.freeEdit(this.styleSheet, value);
28642 endEditing: function()
28644 this.panel.refresh();
28648 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
28650 onInput: function()
28652 Firebug.Editor.update();
28655 scrollToLine: function(line, offset)
28657 this.startMeasuring(this.input);
28658 var lineHeight = this.measureText().height;
28659 this.stopMeasuring();
28661 this.input.scrollTop = (line * lineHeight) + offset;
28665 // ************************************************************************************************
28668 var rgbToHex = function rgbToHex(value)
28670 return value.replace(/\brgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)/gi, rgbToHexReplacer);
28673 var rgbToHexReplacer = function(_, r, g, b) {
28674 return '#' + ((1 << 24) + (r << 16) + (g << 8) + (b << 0)).toString(16).substr(-6).toUpperCase();
28677 var stripUnits = function stripUnits(value)
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);
28683 var stripUnitsReplacer = function(_, skip, remove, whitespace) {
28684 return skip || ('0' + whitespace);
28687 function parsePriority(value)
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};
28696 function parseURLValue(value)
28698 var m = reURL.exec(value);
28699 return m ? m[1] : "";
28702 function parseRepeatValue(value)
28704 var m = reRepeat.exec(value);
28705 return m ? m[0] : "";
28708 function parseCSSValue(value, offset)
28714 m = reSplitCSS.exec(value);
28715 if (m && m.index+m[0].length < offset)
28717 value = value.substr(m.index+m[0].length);
28718 start += m.index+m[0].length;
28719 offset -= m.index+m[0].length;
28730 else if (m[2] || m[3])
28735 return {value: m[0], start: start+m.index, end: start+m.index+(m[0].length-1), type: type};
28739 function findPropByName(props, name)
28741 for (var i = 0; i < props.length; ++i)
28743 if (props[i].name == name)
28748 function sortProperties(props)
28750 props.sort(function(a, b)
28752 return a.name > b.name ? 1 : -1;
28756 function getTopmostRuleLine(panelNode)
28758 for (var child = panelNode.firstChild; child; child = child.nextSibling)
28760 if (child.offsetTop+child.offsetHeight > panelNode.scrollTop)
28762 var rule = child.repObject;
28765 line: domUtils.getRuleLine(rule),
28766 offset: panelNode.scrollTop-child.offsetTop
28773 function getStyleSheetCSS(sheet, context)
28775 if (sheet.ownerNode instanceof HTMLStyleElement)
28776 return sheet.ownerNode.innerHTML;
28778 return context.sourceCache.load(sheet.href).join("");
28781 function getStyleSheetOwnerNode(sheet) {
28782 for (; sheet && !sheet.ownerNode; sheet = sheet.parentStyleSheet);
28784 return sheet.ownerNode;
28787 function scrollSelectionIntoView(panel)
28789 var selCon = getSelectionController(panel);
28790 selCon.scrollSelectionIntoView(
28791 nsISelectionController.SELECTION_NORMAL,
28792 nsISelectionController.SELECTION_FOCUS_REGION, true);
28795 function getSelectionController(panel)
28797 var browser = Firebug.chrome.getPanelBrowser(panel);
28798 return browser.docShell.QueryInterface(nsIInterfaceRequestor)
28799 .getInterface(nsISelectionDisplay)
28800 .QueryInterface(nsISelectionController);
28803 // ************************************************************************************************
28805 Firebug.registerModule(Firebug.CSSModule);
28806 Firebug.registerPanel(Firebug.CSSStyleSheetPanel);
28807 Firebug.registerPanel(CSSElementPanel);
28808 Firebug.registerPanel(CSSComputedElementPanel);
28810 // ************************************************************************************************
28815 /* See license.txt for terms of usage */
28817 FBL.ns(function() { with (FBL) {
28818 // ************************************************************************************************
28820 // ************************************************************************************************
28823 Firebug.Script = extend(Firebug.Module,
28825 getPanel: function()
28827 return Firebug.chrome ? Firebug.chrome.getPanel("Script") : null;
28830 selectSourceCode: function(index)
28832 this.getPanel().selectSourceCode(index);
28836 Firebug.registerModule(Firebug.Script);
28839 // ************************************************************************************************
28842 function ScriptPanel(){};
28844 ScriptPanel.prototype = extend(Firebug.Panel,
28849 selectIndex: 0, // index of the current selectNode's option
28850 sourceIndex: -1, // index of the script node, based in doc.getElementsByTagName("script")
28853 hasToolButtons: true
28858 Firebug.Panel.create.apply(this, arguments);
28860 this.onChangeSelect = bind(this.onChangeSelect, this);
28862 var doc = Firebug.browser.document;
28863 var scripts = doc.getElementsByTagName("script");
28864 var selectNode = this.selectNode = createElement("select");
28866 for(var i=0, script; script=scripts[i]; i++)
28868 // Don't show Firebug Lite source code in the list of options
28869 if (Firebug.ignoreFirebugElements && script.getAttribute("firebugIgnore"))
28872 var fileName = getFileName(script.src) || getFileName(doc.location.href);
28873 var option = createElement("option", {value:i});
28875 option.appendChild(Firebug.chrome.document.createTextNode(fileName));
28876 selectNode.appendChild(option);
28879 this.toolButtonsNode.appendChild(selectNode);
28882 initialize: function()
28884 // we must render the code first, so the persistent state can be restore
28885 this.selectSourceCode(this.selectIndex);
28887 Firebug.Panel.initialize.apply(this, arguments);
28889 addEvent(this.selectNode, "change", this.onChangeSelect);
28892 shutdown: function()
28894 removeEvent(this.selectNode, "change", this.onChangeSelect);
28896 Firebug.Panel.shutdown.apply(this, arguments);
28899 detach: function(oldChrome, newChrome)
28901 Firebug.Panel.detach.apply(this, arguments);
28903 var oldPanel = oldChrome.getPanel("Script");
28904 var index = oldPanel.selectIndex;
28906 this.selectNode.selectedIndex = index;
28907 this.selectIndex = index;
28908 this.sourceIndex = -1;
28911 onChangeSelect: function(event)
28913 var select = this.selectNode;
28915 this.selectIndex = select.selectedIndex;
28917 var option = select.options[select.selectedIndex];
28921 var selectedSourceIndex = parseInt(option.value);
28923 this.renderSourceCode(selectedSourceIndex);
28926 selectSourceCode: function(index)
28928 var select = this.selectNode;
28929 select.selectedIndex = index;
28931 var option = select.options[index];
28935 var selectedSourceIndex = parseInt(option.value);
28937 this.renderSourceCode(selectedSourceIndex);
28940 renderSourceCode: function(index)
28942 if (this.sourceIndex != index)
28944 var renderProcess = function renderProcess(src)
28949 src = isIE && !isExternal ?
28950 src+'\n' : // IE put an extra line when reading source of local resources
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;
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">';
28965 // render the line number divs
28966 for(var l=1, lines; l<=lines; l++)
28968 html[hl++] = '<div line="';
28972 html[hl++] = '</div>';
28975 html[hl++] = '</div></div>';
28980 var updatePanel = function(html)
28982 self.panelNode.innerHTML = html.join("");
28984 // IE needs this timeout, otherwise the panel won't scroll
28985 setTimeout(function(){
28986 self.synchronizeUI();
28990 var onFailure = function()
28992 FirebugReps.Warning.tag.replace({object: "AccessRestricted"}, self.panelNode);
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;
29004 if (Firebug.disableResourceFetching)
29006 renderProcess(Firebug.Lite.Proxy.fetchResourceDisabledMessage);
29008 else if (isExternal)
29010 Ajax.request({url: url, onSuccess: renderProcess, onFailure: onFailure});
29014 var src = script.innerHTML;
29015 renderProcess(src);
29023 this.sourceIndex = index;
29028 Firebug.registerPanel(ScriptPanel);
29031 // ************************************************************************************************
29034 var getScriptURL = function getScriptURL(script)
29036 var reFile = /([^\/\?#]+)(#.+)?$/;
29037 var rePath = /^(.*\/)/;
29038 var reProtocol = /^\w+:\/\//;
29040 var doc = Firebug.browser.document;
29042 var file = reFile.exec(script.src);
29046 var fileName = file[1];
29047 var fileOptions = file[2];
29050 if (reProtocol.test(script.src)) {
29051 path = rePath.exec(script.src)[1];
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];
29066 var j = backDir[1].length/3;
29069 path = reLastDir.exec(path)[1];
29071 path += backDir[2];
29074 else if(src.indexOf("/") != -1)
29077 if(/^\.\/./.test(src))
29079 path += src.substring(2);
29082 else if(/^\/./.test(src))
29084 var domain = /^(\w+:\/\/[^\/]+)/.exec(path);
29085 path = domain[1] + src;
29096 var m = path && path.match(/([^\/]+)\/$/) || null;
29100 return path + fileName;
29104 var getFileName = function getFileName(path)
29106 if (!path) return "";
29108 var match = path && path.match(/[^\/]+(\?.*)?(#.*)?$/);
29110 return match && match[0] || path;
29114 // ************************************************************************************************
29117 /* See license.txt for terms of usage */
29119 FBL.ns(function() { with (FBL) {
29120 // ************************************************************************************************
29122 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
29124 var ElementCache = Firebug.Lite.Cache.Element;
29126 var insertSliceSize = 18;
29127 var insertInterval = 40;
29134 // We are forced to ignore Java-related variables, because
29135 // trying to access them causes browser freeze
29145 "_FirebugConsole": 1,
29146 "_FirebugCommandLine": 1
29149 if (Firebug.ignoreFirebugElements)
29150 ignoreVars[Firebug.Lite.Cache.ID] = 1;
29152 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
29154 var memberPanelRep =
29156 {"class": "memberLabel $member.type\\Label", href: "javacript:void(0)"}
29158 {"class": "memberLabel $member.type\\Label"};
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'},
29165 SPAN({}, "$member.name")
29168 TD({"class": "memberValueCell", role : 'presentation'},
29169 TAG("$member.tag", {object: "$member.value"})
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')},
29184 TR({role : 'presentation'},
29185 TD({width: "30%"}),
29189 var domTableClass = isIElt8 ? "domTable domTableIE" : "domTable";
29190 var DirTablePlate = domplate(Firebug.Rep,
29193 TABLE({"class": domTableClass, cellpadding: 0, cellspacing: 0, onclick: "$onClick", role :"tree"},
29194 TBODY({role: 'presentation'},
29196 FOR("member", "$object|memberIterator", RowTag)
29201 TABLE({"class": domTableClass, cellpadding: 0, cellspacing: 0,
29202 _toggles: "$toggles", _domPanel: "$domPanel", onclick: "$onClick", role : 'tree'},
29203 TBODY({role : 'presentation'},
29210 TABLE({"class": domTableClass, cellpadding: 0, cellspacing: 0,
29211 _toggles: "$toggles", _domPanel: "$domPanel", onclick: "$onClick", role : 'tree'},
29212 TBODY({role : 'presentation'},
29218 FOR("member", "$members", RowTag),
29220 memberIterator: function(object, level)
29222 return getMembers(object, level);
29225 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
29227 onClick: function(event)
29229 if (!isLeftClick(event))
29232 var target = event.target || event.srcElement;
29234 var row = getAncestorByClass(target, "memberRow");
29235 var label = getAncestorByClass(target, "memberLabel");
29236 if (label && hasClass(row, "hasChildren"))
29238 var row = label.parentNode.parentNode;
29239 this.toggleRow(row);
29243 var object = Firebug.getRepObject(target);
29244 if (typeof(object) == "function")
29246 Firebug.chrome.select(object, "script");
29247 cancelEvent(event);
29249 else if (event.detail == 2 && !object)
29251 var panel = row.parentNode.parentNode.domPanel;
29254 var rowValue = panel.getRowPropertyValue(row);
29255 if (typeof(rowValue) == "boolean")
29256 panel.setPropertyValue(row, !rowValue);
29258 panel.editProperty(row);
29260 cancelEvent(event);
29268 toggleRow: function(row)
29270 var level = parseInt(row.getAttribute("level"));
29271 var toggles = row.parentNode.parentNode.toggles;
29273 if (hasClass(row, "opened"))
29275 removeClass(row, "opened");
29279 var path = getPath(row);
29281 // Remove the path from the toggle tree
29282 for (var i = 0; i < path.length; ++i)
29284 if (i == path.length-1)
29285 delete toggles[path[i]];
29287 toggles = toggles[path[i]];
29291 var rowTag = this.rowTag;
29292 var tbody = row.parentNode;
29294 setTimeout(function()
29296 for (var firstRow = row.nextSibling; firstRow; firstRow = row.nextSibling)
29298 if (parseInt(firstRow.getAttribute("level")) <= level)
29301 tbody.removeChild(firstRow);
29303 }, row.insertTimeout ? row.insertTimeout : 0);
29307 setClass(row, "opened");
29311 var path = getPath(row);
29313 // Mark the path in the toggle tree
29314 for (var i = 0; i < path.length; ++i)
29316 var name = path[i];
29317 if (toggles.hasOwnProperty(name))
29318 toggles = toggles[name];
29320 toggles = toggles[name] = {};
29324 var value = row.lastChild.firstChild.repObject;
29325 var members = getMembers(value, level+1);
29327 var rowTag = this.rowTag;
29331 //var setSize = members.length;
29332 //var rowCount = 1;
29333 while (members.length)
29335 with({slice: members.splice(0, insertSliceSize), isLast: !members.length})
29337 setTimeout(function()
29339 if (lastRow.parentNode)
29341 var result = rowTag.insertRows({members: slice}, lastRow);
29342 lastRow = result[1];
29343 //dispatch([Firebug.A11yModel], 'onMemberRowSliceAdded', [null, result, rowCount, setSize]);
29344 //rowCount += insertSliceSize;
29347 row.removeAttribute("insertTimeout");
29351 delay += insertInterval;
29354 row.insertTimeout = delay;
29361 // ************************************************************************************************
29363 Firebug.DOMBasePanel = function() {};
29365 Firebug.DOMBasePanel.prototype = extend(Firebug.Panel,
29367 tag: DirTablePlate.tableTag,
29369 getRealObject: function(object)
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;
29379 rebuild: function(update, scrollTop)
29381 //dispatch([Firebug.A11yModel], 'onBeforeDomUpdateSelection', [this]);
29382 var members = getMembers(this.selection);
29383 expandMembers(members, this.toggles, 0, 0);
29385 this.showMembers(members, update, scrollTop);
29387 //TODO: xxxpedro statusbar
29388 if (!this.parentPanel)
29389 updateStatusBar(this);
29392 showMembers: function(members, update, scrollTop)
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
29398 for (var i = 0; i < this.timeouts.length; ++i)
29399 this.context.clearTimeout(this.timeouts[i]);
29400 delete this.timeouts;
29403 if (!members.length)
29404 return this.showEmptyMembers();
29406 var panelNode = this.panelNode;
29407 var priorScrollTop = scrollTop == undefined ? panelNode.scrollTop : scrollTop;
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;
29414 var table = this.tag.replace({domPanel: this, toggles: this.toggles}, dest);
29415 var tbody = table.lastChild;
29416 var rowTag = DirTablePlate.rowTag;
29418 // Insert the first slice immediately
29419 //var slice = members.splice(0, insertSliceSize);
29420 //var result = rowTag.insertRows({members: slice}, tbody.lastChild);
29422 //var setSize = members.length;
29423 //var rowCount = 1;
29428 //dispatch([Firebug.A11yModel], 'onMemberRowSliceAdded', [panel, result, rowCount, setSize]);
29433 // enable to measure rendering performance
29434 var renderStart = new Date().getTime();
29435 while (members.length)
29437 with({slice: members.splice(0, insertSliceSize), isLast: !members.length})
29439 timeouts.push(this.context.setTimeout(function()
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;
29446 result = rowTag.insertRows({members: slice}, tbody.lastChild);
29448 //rowCount += insertSliceSize;
29449 //dispatch([Firebug.A11yModel], 'onMemberRowSliceAdded', [panel, result, rowCount, setSize]);
29451 if ((panelNode.scrollHeight+panelNode.offsetHeight) >= priorScrollTop)
29452 panelNode.scrollTop = priorScrollTop;
29455 // enable to measure rendering performance
29456 //if (isLast) alert(new Date().getTime() - renderStart + "ms");
29461 delay += insertInterval;
29467 timeouts.push(this.context.setTimeout(function()
29469 if (panelNode.firstChild)
29470 panelNode.replaceChild(table, panelNode.firstChild);
29472 panelNode.appendChild(table);
29474 // Scroll back to where we were before
29475 panelNode.scrollTop = priorScrollTop;
29480 timeouts.push(this.context.setTimeout(function()
29482 panelNode.scrollTop = scrollTop == undefined ? 0 : scrollTop;
29485 this.timeouts = timeouts;
29490 showMembers: function(members, update, scrollTop)
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
29496 for (var i = 0; i < this.timeouts.length; ++i)
29497 this.context.clearTimeout(this.timeouts[i]);
29498 delete this.timeouts;
29501 if (!members.length)
29502 return this.showEmptyMembers();
29504 var panelNode = this.panelNode;
29505 var priorScrollTop = scrollTop == undefined ? panelNode.scrollTop : scrollTop;
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;
29512 var table = this.tag.replace({domPanel: this, toggles: this.toggles}, dest);
29513 var tbody = table.lastChild;
29514 var rowTag = DirTablePlate.rowTag;
29516 // Insert the first slice immediately
29517 //var slice = members.splice(0, insertSliceSize);
29518 //var result = rowTag.insertRows({members: slice}, tbody.lastChild);
29520 //var setSize = members.length;
29521 //var rowCount = 1;
29526 //dispatch([Firebug.A11yModel], 'onMemberRowSliceAdded', [panel, result, rowCount, setSize]);
29530 var _insertSliceSize = insertSliceSize;
29531 var _insertInterval = insertInterval;
29533 // enable to measure rendering performance
29534 var renderStart = new Date().getTime();
29535 var lastSkip = renderStart, now;
29537 while (members.length)
29539 with({slice: members.splice(0, _insertSliceSize), isLast: !members.length})
29541 var _tbody = tbody;
29542 var _rowTag = rowTag;
29543 var _panelNode = panelNode;
29544 var _priorScrollTop = priorScrollTop;
29546 timeouts.push(this.context.setTimeout(function()
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;
29553 result = _rowTag.insertRows({members: slice}, _tbody.lastChild);
29555 //rowCount += _insertSliceSize;
29556 //dispatch([Firebug.A11yModel], 'onMemberRowSliceAdded', [panel, result, rowCount, setSize]);
29558 if ((_panelNode.scrollHeight + _panelNode.offsetHeight) >= _priorScrollTop)
29559 _panelNode.scrollTop = _priorScrollTop;
29562 // enable to measure rendering performance
29563 //alert("gap: " + (new Date().getTime() - lastSkip));
29564 //lastSkip = new Date().getTime();
29566 //if (isLast) alert("new: " + (new Date().getTime() - renderStart) + "ms");
29570 delay += _insertInterval;
29576 timeouts.push(this.context.setTimeout(function()
29578 if (panelNode.firstChild)
29579 panelNode.replaceChild(table, panelNode.firstChild);
29581 panelNode.appendChild(table);
29583 // Scroll back to where we were before
29584 panelNode.scrollTop = priorScrollTop;
29589 timeouts.push(this.context.setTimeout(function()
29591 panelNode.scrollTop = scrollTop == undefined ? 0 : scrollTop;
29594 this.timeouts = timeouts;
29598 showEmptyMembers: function()
29600 FirebugReps.Warning.tag.replace({object: "NoMembersWarning"}, this.panelNode);
29603 findPathObject: function(object)
29605 var pathIndex = -1;
29606 for (var i = 0; i < this.objectPath.length; ++i)
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)
29618 getPathObject: function(index)
29620 var object = this.objectPath[index];
29622 if (object instanceof Property)
29623 return object.getObject();
29628 getRowObject: function(row)
29630 var object = getRowOwnerObject(row);
29631 return object ? object : this.selection;
29634 getRowPropertyValue: function(row)
29636 var object = this.getRowObject(row);
29637 object = this.getRealObject(object);
29640 var propName = getRowName(row);
29642 if (object instanceof jsdIStackFrame)
29643 return Firebug.Debugger.evaluate(propName, this.context);
29645 return object[propName];
29649 copyProperty: function(row)
29651 var value = this.getRowPropertyValue(row);
29652 copyToClipboard(value);
29655 editProperty: function(row, editValue)
29657 if (hasClass(row, "watchNewRow"))
29659 if (this.context.stopped)
29660 Firebug.Editor.startEditing(row, "");
29661 else if (Firebug.Console.isAlwaysEnabled()) // not stopped in debugger, need command line
29663 if (Firebug.CommandLine.onCommandLineFocus())
29664 Firebug.Editor.startEditing(row, "");
29666 row.innerHTML = $STR("warning.Command line blocked?");
29669 row.innerHTML = $STR("warning.Console must be enabled");
29671 else if (hasClass(row, "watchRow"))
29672 Firebug.Editor.startEditing(row, getRowName(row));
29675 var object = this.getRowObject(row);
29676 this.context.thisValue = object;
29680 var propValue = this.getRowPropertyValue(row);
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);
29692 editValue = "this." + getRowName(row);
29696 Firebug.Editor.startEditing(row, editValue);
29700 deleteProperty: function(row)
29702 if (hasClass(row, "watchRow"))
29703 this.deleteWatch(row);
29706 var object = getRowOwnerObject(row);
29708 object = this.selection;
29709 object = this.getRealObject(object);
29713 var name = getRowName(row);
29716 delete object[name];
29723 this.rebuild(true);
29729 setPropertyValue: function(row, value) // value must be string
29731 if(FBTrace.DBG_DOM)
29733 FBTrace.sysout("row: "+row);
29734 FBTrace.sysout("value: "+value+" type "+typeof(value), value);
29737 var name = getRowName(row);
29738 if (name == "this")
29741 var object = this.getRowObject(row);
29742 object = this.getRealObject(object);
29743 if (object && !(object instanceof jsdIStackFrame))
29745 // unwrappedJSObject.property = unwrappedJSObject
29746 Firebug.CommandLine.evaluate(value, this.context, object, this.context.getGlobalScope(),
29747 function success(result, context)
29749 if (FBTrace.DBG_DOM)
29750 FBTrace.sysout("setPropertyValue evaluate success object["+name+"]="+result+" type "+typeof(result), result);
29751 object[name] = result;
29753 function failed(exc, context)
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
29762 object[name] = String(value); // unwrappedJSobject.property = string
29771 else if (this.context.stopped)
29775 Firebug.CommandLine.evaluate(name+"="+value, this.context);
29781 // See catch block above...
29782 object[name] = String(value); // unwrappedJSobject.property = string
29791 this.rebuild(true);
29795 highlightRow: function(row)
29797 if (this.highlightedRow)
29798 cancelClassTimed(this.highlightedRow, "jumpHighlight", this.context);
29800 this.highlightedRow = row;
29803 setClassTimed(row, "jumpHighlight", this.context);
29806 onMouseMove: function(event)
29808 var target = event.srcElement || event.target;
29810 var object = getAncestorByClass(target, "objectLink-element");
29811 object = object ? object.repObject : null;
29813 if(object && instanceOf(object, "Element") && object.nodeType == 1)
29815 if(object != lastHighlightedObject)
29817 Firebug.Inspector.drawBoxModel(object);
29818 object = lastHighlightedObject;
29822 Firebug.Inspector.hideBoxModel();
29826 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
29832 this.context = Firebug.browser;
29834 this.objectPath = [];
29835 this.propertyPath = [];
29836 this.viewPath = [];
29837 this.pathIndex = -1;
29840 Firebug.Panel.create.apply(this, arguments);
29842 this.panelNode.style.padding = "0 1px";
29845 initialize: function(){
29846 Firebug.Panel.initialize.apply(this, arguments);
29848 addEvent(this.panelNode, "mousemove", this.onMouseMove);
29851 shutdown: function()
29853 removeEvent(this.panelNode, "mousemove", this.onMouseMove);
29855 Firebug.Panel.shutdown.apply(this, arguments);
29859 destroy: function(state)
29861 var view = this.viewPath[this.pathIndex];
29862 if (view && this.panelNode.scrollTop)
29863 view.scrollTop = this.panelNode.scrollTop;
29865 if (this.pathIndex)
29866 state.pathIndex = this.pathIndex;
29868 state.viewPath = this.viewPath;
29869 if (this.propertyPath)
29870 state.propertyPath = this.propertyPath;
29872 if (this.propertyPath.length > 0 && !this.propertyPath[1])
29873 state.firstSelection = persistObject(this.getPathObject(1), this.context);
29875 Firebug.Panel.destroy.apply(this, arguments);
29879 ishow: function(state)
29881 if (this.context.loaded && !this.selection)
29888 if (state.viewPath)
29889 this.viewPath = state.viewPath;
29890 if (state.propertyPath)
29891 this.propertyPath = state.propertyPath;
29893 var defaultObject = this.getDefaultSelection(this.context);
29894 var selectObject = defaultObject;
29896 if (state.firstSelection)
29898 var restored = state.firstSelection(this.context);
29901 selectObject = restored;
29902 this.objectPath = [defaultObject, restored];
29905 this.objectPath = [defaultObject];
29908 this.objectPath = [defaultObject];
29910 if (this.propertyPath.length > 1)
29912 for (var i = 1; i < this.propertyPath.length; ++i)
29914 var name = this.propertyPath[i];
29918 var object = selectObject;
29921 selectObject = object[name];
29925 selectObject = null;
29930 this.objectPath.push(new Property(object, name));
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);
29944 var selection = state.pathIndex <= this.objectPath.length-1
29945 ? this.getPathObject(state.pathIndex)
29946 : this.getPathObject(this.objectPath.length-1);
29948 this.select(selection);
29954 var view = this.viewPath[this.pathIndex];
29955 if (view && this.panelNode.scrollTop)
29956 view.scrollTop = this.panelNode.scrollTop;
29960 supportsObject: function(object)
29962 if (object == null)
29965 if (typeof(object) == "undefined")
29967 else if (object instanceof SourceLink)
29970 return 1; // just agree to support everything but not agressively.
29973 refresh: function()
29975 this.rebuild(true);
29978 updateSelection: function(object)
29980 var previousIndex = this.pathIndex;
29981 var previousView = previousIndex == -1 ? null : this.viewPath[previousIndex];
29983 var newPath = this.pathToAppend;
29984 delete this.pathToAppend;
29986 var pathIndex = this.findPathObject(object);
29987 if (newPath || pathIndex == -1)
29993 // Remove everything after the point where we are inserting, so we
29994 // essentially replace it with the new path
29997 if (this.panelNode.scrollTop)
29998 previousView.scrollTop = this.panelNode.scrollTop;
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;
30005 this.objectPath.splice(start, length);
30006 this.propertyPath.splice(start, length);
30007 this.viewPath.splice(start, length);
30010 var value = this.getPathObject(previousIndex);
30013 if (FBTrace.DBG_ERRORS)
30014 FBTrace.sysout("dom.updateSelection no pathObject for "+previousIndex+"\n");
30018 for (var i = 0, length = newPath.length; i < length; ++i)
30020 var name = newPath[i];
30021 var object = value;
30024 value = value[name];
30028 if (FBTrace.DBG_ERRORS)
30029 FBTrace.sysout("dom.updateSelection FAILS at path_i="+i+" for name:"+name+"\n");
30034 this.objectPath.push(new Property(object, name));
30035 this.propertyPath.push(name);
30036 this.viewPath.push({toggles: this.toggles, scrollTop: 0});
30043 var win = Firebug.browser.window;
30044 //var win = this.context.getGlobalScope();
30045 if (object === win)
30047 this.pathIndex = 0;
30048 this.objectPath = [win];
30049 this.propertyPath = [null];
30050 this.viewPath = [{toggles: this.toggles, scrollTop: 0}];
30054 this.pathIndex = 1;
30055 this.objectPath = [win, object];
30056 this.propertyPath = [null, null];
30058 {toggles: {}, scrollTop: 0},
30059 {toggles: this.toggles, scrollTop: 0}
30064 this.panelNode.scrollTop = 0;
30069 this.pathIndex = pathIndex;
30071 var view = this.viewPath[pathIndex];
30072 this.toggles = view.toggles;
30074 // Persist the current scroll location
30075 if (previousView && this.panelNode.scrollTop)
30076 previousView.scrollTop = this.panelNode.scrollTop;
30078 this.rebuild(false, view.scrollTop);
30082 getObjectPath: function(object)
30084 return this.objectPath;
30087 getDefaultSelection: function()
30089 return Firebug.browser.window;
30090 //return this.context.getGlobalScope();
30093 updateOption: function(name, value)
30095 const optionMap = {showUserProps: 1, showUserFuncs: 1, showDOMProps: 1,
30096 showDOMFuncs: 1, showDOMConstants: 1};
30097 if ( optionMap.hasOwnProperty(name) )
30098 this.rebuild(true);
30101 getOptionsMenuItems: function()
30104 optionMenu("ShowUserProps", "showUserProps"),
30105 optionMenu("ShowUserFuncs", "showUserFuncs"),
30106 optionMenu("ShowDOMProps", "showDOMProps"),
30107 optionMenu("ShowDOMFuncs", "showDOMFuncs"),
30108 optionMenu("ShowDOMConstants", "showDOMConstants"),
30110 {label: "Refresh", command: bindFixed(this.rebuild, this, true) }
30114 getContextMenuItems: function(object, target)
30116 var row = getAncestorByClass(target, "memberRow");
30122 var rowName = getRowName(row);
30123 var rowObject = this.getRowObject(row);
30124 var rowValue = this.getRowPropertyValue(row);
30126 var isWatch = hasClass(row, "watchRow");
30127 var isStackFrame = rowObject instanceof jsdIStackFrame;
30129 if (typeof(rowValue) == "string" || typeof(rowValue) == "number")
30131 // Functions already have a copy item in their context menu
30134 {label: "CopyValue",
30135 command: bindFixed(this.copyProperty, this, row) }
30141 {label: isWatch ? "EditWatch" : (isStackFrame ? "EditVariable" : "EditProperty"),
30142 command: bindFixed(this.editProperty, this, row) }
30145 if (isWatch || (!isStackFrame && !isDOMMember(rowObject, rowName)))
30148 {label: isWatch ? "DeleteWatch" : "DeleteProperty",
30149 command: bindFixed(this.deleteProperty, this, row) }
30156 {label: "Refresh", command: bindFixed(this.rebuild, this, true) }
30162 getEditor: function(target, value)
30165 this.editor = new DOMEditor(this.document);
30167 return this.editor;
30171 // ************************************************************************************************
30173 // TODO: xxxpedro statusbar
30174 var updateStatusBar = function(panel)
30176 var path = panel.propertyPath;
30177 var index = panel.pathIndex;
30181 for (var i=0, l=path.length; i<l; i++)
30183 r.push(i==index ? '<a class="fbHover fbButton fbBtnSelected" ' : '<a class="fbHover fbButton" ');
30184 r.push('pathIndex=');
30188 r.push(' href="javascript:void(0)"');
30191 r.push(i==0 ? "window" : path[i] || "Object");
30195 r.push('<span class="fbStatusSeparator">></span>');
30197 panel.statusBarNode.innerHTML = r.join("");
30201 var DOMMainPanel = Firebug.DOMPanel = function () {};
30203 Firebug.DOMPanel.DirTable = DirTablePlate;
30205 DOMMainPanel.prototype = extend(Firebug.DOMBasePanel.prototype,
30207 onClickStatusBar: function(event)
30209 var target = event.srcElement || event.target;
30210 var element = getAncestorByClass(target, "fbHover");
30214 var pathIndex = element.getAttribute("pathIndex");
30218 this.select(this.getPathObject(pathIndex));
30223 selectRow: function(row, target)
30226 target = row.lastChild.firstChild;
30228 if (!target || !target.repObject)
30231 this.pathToAppend = getPath(row);
30233 // If the object is inside an array, look up its index
30234 var valueBox = row.lastChild.firstChild;
30235 if (hasClass(valueBox, "objectBox-array"))
30237 var arrayIndex = FirebugReps.Arr.getItemIndex(target);
30238 this.pathToAppend.push(arrayIndex);
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();
30245 this.select(target.repObject, true);
30248 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
30250 onClick: function(event)
30252 var target = event.srcElement || event.target;
30253 var repNode = Firebug.getRepNode(target);
30256 var row = getAncestorByClass(target, "memberRow");
30259 this.selectRow(row, repNode);
30260 cancelEvent(event);
30265 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
30271 statusSeparator: ">",
30274 hasToolButtons: true,
30280 Firebug.DOMBasePanel.prototype.create.apply(this, arguments);
30282 this.onClick = bind(this.onClick, this);
30285 this.onClickStatusBar = bind(this.onClickStatusBar, this);
30287 this.panelNode.style.padding = "0 1px";
30290 initialize: function(oldPanelNode)
30292 //this.panelNode.addEventListener("click", this.onClick, false);
30293 //dispatch([Firebug.A11yModel], 'onInitializeNode', [this, 'console']);
30295 Firebug.DOMBasePanel.prototype.initialize.apply(this, arguments);
30297 addEvent(this.panelNode, "click", this.onClick);
30299 // TODO: xxxpedro dom
30303 addEvent(this.statusBarNode, "click", this.onClickStatusBar);
30306 shutdown: function()
30308 //this.panelNode.removeEventListener("click", this.onClick, false);
30309 //dispatch([Firebug.A11yModel], 'onDestroyNode', [this, 'console']);
30311 removeEvent(this.panelNode, "click", this.onClick);
30313 Firebug.DOMBasePanel.prototype.shutdown.apply(this, arguments);
30316 search: function(text, reverse)
30320 delete this.currentSearch;
30321 this.highlightRow(null);
30326 if (this.currentSearch && text == this.currentSearch.text)
30327 row = this.currentSearch.findNext(true, undefined, reverse, Firebug.searchCaseSensitive);
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);
30337 var sel = this.document.defaultView.getSelection();
30338 sel.removeAllRanges();
30339 sel.addRange(this.currentSearch.range);
30341 scrollIntoCenterView(row, this.panelNode);
30343 this.highlightRow(row);
30344 dispatch([Firebug.A11yModel], 'onDomSearchMatchFound', [this, text, row]);
30349 dispatch([Firebug.A11yModel], 'onDomSearchMatchFound', [this, text, null]);
30355 Firebug.registerPanel(DOMMainPanel);
30358 // ************************************************************************************************
30362 // ************************************************************************************************
30365 var getMembers = function getMembers(object, level) // we expect object to be user-level object wrapped in security blanket
30370 var ordinals = [], userProps = [], userClasses = [], userFuncs = [],
30371 domProps = [], domFuncs = [], domConstants = [];
30375 var domMembers = getDOMMembers(object);
30376 //var domMembers = {}; // TODO: xxxpedro
30377 //var domConstantMap = {}; // TODO: xxxpedro
30379 if (object.wrappedJSObject)
30380 var insecureObject = object.wrappedJSObject;
30382 var insecureObject = object;
30384 // IE function prototype is not listed in (for..in)
30385 if (isIE && isFunction(object))
30386 addMember("user", userProps, "prototype", object.prototype, level);
30388 for (var name in insecureObject) // enumeration is safe
30390 if (ignoreVars[name] == 1) // javascript.options.strict says ignoreVars is undefined.
30396 val = insecureObject[name]; // getter is safe
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);
30405 var ordinal = parseInt(name);
30406 if (ordinal || ordinal == 0)
30408 addMember("ordinal", ordinals, name, val, level);
30410 else if (isFunction(val))
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]);
30417 addMember("userFunction", userFuncs, name, val, level);
30423 var getterFunction = insecureObject.__lookupGetter__(name),
30424 setterFunction = insecureObject.__lookupSetter__(name),
30427 if(getterFunction && !setterFunction)
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);
30438 addMember("user", userProps, (prefix+name), val, level);
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
30447 if (FBTrace.DBG_ERRORS && FBTrace.DBG_DOM)
30448 FBTrace.sysout("dom.getMembers FAILS: ", exc);
30452 function sortName(a, b) { return a.name > b.name ? 1 : -1; }
30453 function sortOrder(a, b) { return a.order > b.order ? 1 : -1; }
30457 members.push.apply(members, ordinals);
30459 Firebug.showUserProps = true; // TODO: xxxpedro
30460 Firebug.showUserFuncs = true; // TODO: xxxpedro
30461 Firebug.showDOMProps = true;
30462 Firebug.showDOMFuncs = true;
30463 Firebug.showDOMConstants = true;
30465 if (Firebug.showUserProps)
30467 userProps.sort(sortName);
30468 members.push.apply(members, userProps);
30471 if (Firebug.showUserFuncs)
30473 userClasses.sort(sortName);
30474 members.push.apply(members, userClasses);
30476 userFuncs.sort(sortName);
30477 members.push.apply(members, userFuncs);
30480 if (Firebug.showDOMProps)
30482 domProps.sort(sortName);
30483 members.push.apply(members, domProps);
30486 if (Firebug.showDOMFuncs)
30488 domFuncs.sort(sortName);
30489 members.push.apply(members, domFuncs);
30492 if (Firebug.showDOMConstants)
30493 members.push.apply(members, domConstants);
30498 function expandMembers(members, toggles, offset, level) // recursion starts with offset=0, level=0
30501 for (var i = offset; i < members.length; ++i)
30503 var member = members[i];
30504 if (member.level > level)
30507 if ( toggles.hasOwnProperty(member.name) )
30509 member.open = "opened"; // member.level <= level && member.name in toggles.
30511 var newMembers = getMembers(member.value, level+1); // sets newMembers.level to level+1
30513 var args = [i+1, 0];
30514 args.push.apply(args, newMembers);
30515 members.splice.apply(members, args);
30518 if (FBTrace.DBG_DOM)
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);
30527 expanded += newMembers.length;
30528 i += newMembers.length + expandMembers(members, toggles[member.name], i+1, level+1);
30535 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
30538 function isClassFunction(fn)
30542 for (var name in fn.prototype)
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)
30557 // for (var name in ob)
30559 // } catch (exc) {}
30561 // // IE function prototype is not listed in (for..in)
30562 // if (isFunction(ob)) return true;
30567 FBL.ErrorCopy = function(message)
30569 this.message = message;
30572 var addMember = function addMember(type, props, name, value, level, order)
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;
30577 var ErrorCopy = function(){}; //TODO: xxxpedro
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));
30588 rowClass: "memberRow-"+type,
30593 hasChildren: hasChildren,
30598 var getWatchRowIndex = function getWatchRowIndex(row)
30601 for (; row && hasClass(row, "watchRow"); row = row.previousSibling)
30606 var getRowName = function getRowName(row)
30608 var node = row.firstChild;
30609 return node.textContent ? node.textContent : node.innerText;
30612 var getRowValue = function getRowValue(row)
30614 return row.lastChild.firstChild.repObject;
30617 var getRowOwnerObject = function getRowOwnerObject(row)
30619 var parentRow = getParentRow(row);
30621 return getRowValue(parentRow);
30624 var getParentRow = function getParentRow(row)
30626 var level = parseInt(row.getAttribute("level"))-1;
30627 for (row = row.previousSibling; row; row = row.previousSibling)
30629 if (parseInt(row.getAttribute("level")) == level)
30634 var getPath = function getPath(row)
30636 var name = getRowName(row);
30639 var level = parseInt(row.getAttribute("level"))-1;
30640 for (row = row.previousSibling; row; row = row.previousSibling)
30642 if (parseInt(row.getAttribute("level")) == level)
30644 var name = getRowName(row);
30645 path.splice(0, 0, name);
30654 // ************************************************************************************************
30657 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
30658 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
30659 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
30662 // ************************************************************************************************
30665 Firebug.DOM = extend(Firebug.Module,
30667 getPanel: function()
30669 return Firebug.chrome ? Firebug.chrome.getPanel("DOM") : null;
30673 Firebug.registerModule(Firebug.DOM);
30676 // ************************************************************************************************
30679 var lastHighlightedObject;
30681 function DOMSidePanel(){};
30683 DOMSidePanel.prototype = extend(Firebug.DOMBasePanel.prototype,
30685 selectRow: function(row, target)
30688 target = row.lastChild.firstChild;
30690 if (!target || !target.repObject)
30693 this.pathToAppend = getPath(row);
30695 // If the object is inside an array, look up its index
30696 var valueBox = row.lastChild.firstChild;
30697 if (hasClass(valueBox, "objectBox-array"))
30699 var arrayIndex = FirebugReps.Arr.getItemIndex(target);
30700 this.pathToAppend.push(arrayIndex);
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();
30707 var object = target.repObject;
30709 if (instanceOf(object, "Element"))
30711 Firebug.HTML.selectTreeNode(ElementCache(object));
30715 Firebug.chrome.selectPanel("DOM");
30716 Firebug.chrome.getPanel("DOM").select(object, true);
30720 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
30722 onClick: function(event)
30725 var target = event.srcElement || event.target;
30727 var object = getAncestorByClass(target, "objectLink");
30728 object = object ? object.repObject : null;
30730 if(!object) return;
30732 if (instanceOf(object, "Element"))
30734 Firebug.HTML.selectTreeNode(ElementCache(object));
30738 Firebug.chrome.selectPanel("DOM");
30739 Firebug.chrome.getPanel("DOM").select(object, true);
30744 var target = event.srcElement || event.target;
30745 var repNode = Firebug.getRepNode(target);
30748 var row = getAncestorByClass(target, "memberRow");
30751 this.selectRow(row, repNode);
30752 cancelEvent(event);
30758 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
30761 name: "DOMSidePanel",
30762 parentPanel: "HTML",
30766 hasToolButtons: true
30769 isInitialized: false,
30773 Firebug.DOMBasePanel.prototype.create.apply(this, arguments);
30775 this.onClick = bind(this.onClick, this);
30778 initialize: function(){
30779 Firebug.DOMBasePanel.prototype.initialize.apply(this, arguments);
30781 addEvent(this.panelNode, "click", this.onClick);
30783 // TODO: xxxpedro css2
30784 var selection = ElementCache.get(Firebug.context.persistedState.selectedHTMLElementId);
30786 this.select(selection, true);
30789 shutdown: function()
30791 removeEvent(this.panelNode, "click", this.onClick);
30793 Firebug.DOMBasePanel.prototype.shutdown.apply(this, arguments);
30796 reattach: function(oldChrome)
30798 //this.isInitialized = oldChrome.getPanel("DOM").isInitialized;
30799 this.toggles = oldChrome.getPanel("DOMSidePanel").toggles;
30804 Firebug.registerPanel(DOMSidePanel);
30807 // ************************************************************************************************
30810 /* See license.txt for terms of usage */
30815 // ************************************************************************************************
30817 var traceOptions = {
30826 this.module = null;
30828 this.initialize = function()
30830 if (!this.messageQueue)
30831 this.messageQueue = [];
30833 for (var name in traceOptions)
30834 this[name] = traceOptions[name];
30837 // ************************************************************************************************
30840 this.sysout = function()
30842 return this.logFormatted(arguments, "");
30845 this.dumpProperties = function(title, object)
30847 return this.logFormatted("dumpProperties() not supported.", "warning");
30850 this.dumpStack = function()
30852 return this.logFormatted("dumpStack() not supported.", "warning");
30855 this.flush = function(module)
30857 this.module = module;
30859 var queue = this.messageQueue;
30860 this.messageQueue = [];
30862 for (var i = 0; i < queue.length; ++i)
30863 this.writeMessage(queue[i][0], queue[i][1], queue[i][2]);
30866 this.getPanel = function()
30868 return this.module ? this.module.getPanel() : null;
30871 //*************************************************************************************************
30873 this.logFormatted = function(objects, className)
30875 var html = this.DBG_TIMESTAMP ? [getTimestamp(), " | "] : [];
30876 var length = objects.length;
30878 for (var i = 0; i < length; ++i)
30880 appendText(" ", html);
30882 var object = objects[i];
30887 appendText(object, html);
30891 appendText(object, html);
30894 return this.logRow(html, className);
30897 this.logRow = function(message, className)
30899 var panel = this.getPanel();
30901 if (panel && panel.panelNode)
30902 this.writeMessage(message, className);
30905 this.messageQueue.push([message, className]);
30908 return this.LOG_COMMAND;
30911 this.writeMessage = function(message, className)
30913 var container = this.getPanel().containerNode;
30914 var isScrolledToBottom =
30915 container.scrollTop + container.offsetHeight >= container.scrollHeight;
30917 this.writeRow.call(this, message, className);
30919 if (isScrolledToBottom)
30920 container.scrollTop = container.scrollHeight - container.offsetHeight;
30923 this.appendRow = function(row)
30925 var container = this.getPanel().panelNode;
30926 container.appendChild(row);
30929 this.writeRow = function(message, className)
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);
30937 //*************************************************************************************************
30939 function appendText(object, html)
30941 html.push(escapeHTML(objectToString(object)));
30944 function getTimestamp()
30946 var now = new Date();
30947 var ms = "" + (now.getMilliseconds() / 1000).toFixed(3);
30950 return now.toLocaleTimeString() + "." + ms;
30953 //*************************************************************************************************
30964 function replaceChars(ch)
30966 return HTMLtoEntity[ch];
30969 function escapeHTML(value)
30971 return (value+"").replace(/[<>&"']/g, replaceChars);
30974 //*************************************************************************************************
30976 function objectToString(object)
30988 // ************************************************************************************************
30989 }).apply(FBL.FBTrace);
30991 /* See license.txt for terms of usage */
30993 FBL.ns(function() { with (FBL) {
30994 // ************************************************************************************************
30996 // If application isn't in trace mode, the FBTrace panel won't be loaded
30997 if (!Env.Options.enableTrace) return;
30999 // ************************************************************************************************
31002 Firebug.Trace = extend(Firebug.Module,
31004 getPanel: function()
31006 return Firebug.chrome ? Firebug.chrome.getPanel("Trace") : null;
31011 this.getPanel().panelNode.innerHTML = "";
31015 Firebug.registerModule(Firebug.Trace);
31018 // ************************************************************************************************
31021 function TracePanel(){};
31023 TracePanel.prototype = extend(Firebug.Panel,
31029 hasToolButtons: true,
31030 innerHTMLSync: true
31033 create: function(){
31034 Firebug.Panel.create.apply(this, arguments);
31036 this.clearButton = new Button({
31038 title: "Clear FBTrace logs",
31039 owner: Firebug.Trace,
31040 onClick: Firebug.Trace.clear
31044 initialize: function(){
31045 Firebug.Panel.initialize.apply(this, arguments);
31047 this.clearButton.initialize();
31050 shutdown: function()
31052 this.clearButton.shutdown();
31054 Firebug.Panel.shutdown.apply(this, arguments);
31059 Firebug.registerPanel(TracePanel);
31061 // ************************************************************************************************
31064 /* See license.txt for terms of usage */
31066 FBL.ns(function() { with (FBL) {
31067 // ************************************************************************************************
31069 // ************************************************************************************************
31073 var panelTypes = [];
31074 var panelTypeMap = {};
31076 var parentPanelMap = {};
31079 var registerModule = Firebug.registerModule;
31080 var registerPanel = Firebug.registerPanel;
31082 // ************************************************************************************************
31085 extend: function(fn)
31087 if (Firebug.chrome && Firebug.chrome.addPanel)
31089 var namespace = ns(fn);
31090 fn.call(namespace, FBL);
31094 setTimeout(function(){Firebug.extend(fn);},100);
31098 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
31101 registerModule: function()
31103 registerModule.apply(Firebug, arguments);
31105 modules.push.apply(modules, arguments);
31107 dispatch(modules, "initialize", []);
31109 if (FBTrace.DBG_INITIALIZE) FBTrace.sysout("Firebug.registerModule");
31112 registerPanel: function()
31114 registerPanel.apply(Firebug, arguments);
31116 panelTypes.push.apply(panelTypes, arguments);
31118 for (var i = 0, panelType; panelType = arguments[i]; ++i)
31120 // TODO: xxxpedro investigate why Dev Panel throws an error
31121 if (panelType.prototype.name == "Dev") continue;
31123 panelTypeMap[panelType.prototype.name] = arguments[i];
31125 var parentPanelName = panelType.prototype.parentPanel;
31126 if (parentPanelName)
31128 parentPanelMap[parentPanelName] = 1;
31132 var panelName = panelType.prototype.name;
31133 var chrome = Firebug.chrome;
31134 chrome.addPanel(panelName);
31136 // tab click handler
31137 var onTabClick = function onTabClick()
31139 chrome.selectPanel(panelName);
31143 chrome.addController([chrome.panelMap[panelName].tabNode, "mousedown", onTabClick]);
31147 if (FBTrace.DBG_INITIALIZE)
31148 for (var i = 0; i < arguments.length; ++i)
31149 FBTrace.sysout("Firebug.registerPanel", arguments[i].prototype.name);
31157 // ************************************************************************************************
31160 FBL.ns(function() { with (FBL) {
31161 // ************************************************************************************************
31163 FirebugChrome.Skin =
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"> </a><a id="fbWindow_btDetach" class="fbSmallButton fbHover" title="Open Firebug in popup window"> </a><a id="fbWindow_btClose" class="fbSmallButton fbHover" title="Minimize Firebug"> </a></div><div id="fbToolbar"><div id="fbToolbarContent"><span id="fbToolbarIcon"><a id="fbFirebugButton" class="fbIconButton" class="fbHover" target="_blank"> </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"> </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"> </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">>>></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>'
31169 // ************************************************************************************************
31172 // ************************************************************************************************
31174 // ************************************************************************************************