(function(){ /*!************************************************************* * * Firebug Lite 1.4.0a1 * * Copyright (c) 2007, Parakey Inc. * Released under BSD license. * More information: http://getfirebug.com/firebuglite * **************************************************************/ /*! * CSS selectors powered by: * * Sizzle CSS Selector Engine - v1.0 * Copyright 2009, The Dojo Foundation * Released under the MIT, BSD, and GPL Licenses. * More information: http://sizzlejs.com/ */ /** @namespace describe lib */ var FBL = {}; ( /** @scope s_lib @this FBL */ function() { // ************************************************************************************************ // ************************************************************************************************ // Constants var productionDir = "http://getfirebug.com/releases/lite/"; var bookmarkletVersion = 4; // ************************************************************************************************ var reNotWhitespace = /[^\s]/; var reSplitFile = /:\/{1,3}(.*?)\/([^\/]*?)\/?($|\?.*)/; // Globals this.reJavascript = /\s*javascript:\s*(.*)/; this.reChrome = /chrome:\/\/([^\/]*)\//; this.reFile = /file:\/\/([^\/]*)\//; // ************************************************************************************************ // properties var userAgent = navigator.userAgent.toLowerCase(); this.isFirefox = /firefox/.test(userAgent); this.isOpera = /opera/.test(userAgent); this.isSafari = /webkit/.test(userAgent); this.isIE = /msie/.test(userAgent) && !/opera/.test(userAgent); this.isIE6 = /msie 6/i.test(navigator.appVersion); this.browserVersion = (userAgent.match( /.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/ ) || [0,'0'])[1]; this.isIElt8 = this.isIE && (this.browserVersion-0 < 8); // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * this.NS = null; this.pixelsPerInch = null; // ************************************************************************************************ // Namespaces var namespaces = []; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * this.ns = function(fn) { var ns = {}; namespaces.push(fn, ns); return ns; }; var FBTrace = null; this.initialize = function() { // Firebug Lite is already running in persistent mode so we just quit if (window.firebug && firebug.firebuglite || window.console && console.firebuglite) return; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // initialize environment // point the FBTrace object to the local variable if (FBL.FBTrace) FBTrace = FBL.FBTrace; else FBTrace = FBL.FBTrace = {}; FBL.Ajax.initialize(); // check if the actual window is a persisted chrome context var isChromeContext = window.Firebug && typeof window.Firebug.SharedEnv == "object"; // chrome context of the persistent application if (isChromeContext) { // TODO: xxxpedro persist - make a better synchronization sharedEnv = window.Firebug.SharedEnv; delete window.Firebug.SharedEnv; FBL.Env = sharedEnv; FBL.Env.isChromeContext = true; FBTrace.messageQueue = FBL.Env.traceMessageQueue; } // non-persistent application else { FBL.NS = document.documentElement.namespaceURI; FBL.Env.browser = window; FBL.Env.destroy = destroyEnvironment; if (document.documentElement.getAttribute("debug") == "true") FBL.Env.Options.startOpened = true; // find the URL location of the loaded application findLocation(); // TODO: get preferences here... var prefs = eval("(" + FBL.readCookie("FirebugLite") + ")"); if (prefs) { FBL.Env.Options.startOpened = prefs.startOpened; FBL.Env.Options.enableTrace = prefs.enableTrace; FBL.Env.Options.enablePersistent = prefs.enablePersistent; FBL.Env.Options.disableXHRListener = prefs.disableXHRListener; } if (FBL.isFirefox && typeof FBL.Env.browser.console == "object" && FBL.Env.browser.console.firebug && FBL.Env.Options.disableWhenFirebugActive) return; } // exposes the FBL to the global namespace when in debug mode if (FBL.Env.isDebugMode) { FBL.Env.browser.FBL = FBL; } // check browser compatibilities this.isQuiksMode = FBL.Env.browser.document.compatMode == "BackCompat"; this.isIEQuiksMode = this.isIE && this.isQuiksMode; this.isIEStantandMode = this.isIE && !this.isQuiksMode; this.noFixedPosition = this.isIE6 || this.isIEQuiksMode; // after creating/synchronizing the environment, initialize the FBTrace module if (FBL.Env.Options.enableTrace) FBTrace.initialize(); if (FBTrace.DBG_INITIALIZE && isChromeContext) FBTrace.sysout("FBL.initialize - persistent application", "initialize chrome context"); // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // initialize namespaces if (FBTrace.DBG_INITIALIZE) FBTrace.sysout("FBL.initialize", namespaces.length/2+" namespaces BEGIN"); for (var i = 0; i < namespaces.length; i += 2) { var fn = namespaces[i]; var ns = namespaces[i+1]; fn.apply(ns); } if (FBTrace.DBG_INITIALIZE) { FBTrace.sysout("FBL.initialize", namespaces.length/2+" namespaces END"); FBTrace.sysout("FBL waitForDocument", "waiting document load"); } // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // finish environment initialization FBL.Firebug.loadPrefs(prefs); if (FBL.Env.Options.enablePersistent) { // TODO: xxxpedro persist - make a better synchronization if (isChromeContext) { FBL.FirebugChrome.clone(FBL.Env.FirebugChrome); } else { FBL.Env.FirebugChrome = FBL.FirebugChrome; FBL.Env.traceMessageQueue = FBTrace.messageQueue; } } // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // wait document load waitForDocument(); }; var waitForDocument = function waitForDocument() { // document.body not available in XML+XSL documents in Firefox var doc = FBL.Env.browser.document; var body = doc.getElementsByTagName("body")[0]; if (body) { calculatePixelsPerInch(doc, body); onDocumentLoad(); } else setTimeout(waitForDocument, 50); }; var onDocumentLoad = function onDocumentLoad() { if (FBTrace.DBG_INITIALIZE) FBTrace.sysout("FBL onDocumentLoad", "document loaded"); // fix IE6 problem with cache of background images, causing a lot of flickering if (FBL.isIE6) fixIE6BackgroundImageCache(); // chrome context of the persistent application if (FBL.Env.Options.enablePersistent && FBL.Env.isChromeContext) { // finally, start the application in the chrome context FBL.Firebug.initialize(); // if is not development mode, remove the shared environment cache object // used to synchronize the both persistent contexts if (!FBL.Env.isDevelopmentMode) { sharedEnv.destroy(); sharedEnv = null; } } // non-persistent application else { FBL.FirebugChrome.create(); } }; // ************************************************************************************************ // Env var sharedEnv; this.Env = { // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Env Options (will be transported to Firebug options) Options: { saveCookies: false, saveWindowPosition: false, saveCommandLineHistory: false, startOpened: false, startInNewWindow: false, showIconWhenHidden: true, overrideConsole: true, ignoreFirebugElements: true, disableWhenFirebugActive: true, disableXHRListener: false, enableTrace: false, enablePersistent: false }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Library location Location: { sourceDir: null, baseDir: null, skinDir: null, skin: null, app: null }, skin: "xp", useLocalSkin: false, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Env states isDevelopmentMode: false, isDebugMode: false, isChromeContext: false, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Env references browser: null, chrome: null }; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * var destroyEnvironment = function destroyEnvironment() { setTimeout(function() { FBL = null; }, 100); }; // ************************************************************************************************ // Library location var findLocation = function findLocation() { var reFirebugFile = /(firebug-lite(?:-\w+)?(?:\.js|\.jgz))(?:#(.+))?$/; var rePath = /^(.*\/)/; var reProtocol = /^\w+:\/\//; var path = null; var doc = document; // Firebug Lite 1.3.0 bookmarklet identification var script = doc.getElementById("FirebugLite"); if (script) { file = reFirebugFile.exec(script.src); var version = script.getAttribute("FirebugLite"); var number = version ? parseInt(version) : 0; if (!version || !number || number < bookmarkletVersion) { FBL.Env.bookmarkletOutdated = true; } } else { for(var i=0, s=doc.getElementsByTagName("script"), si; si=s[i]; i++) { var file = null; if ( si.nodeName.toLowerCase() == "script" && (file = reFirebugFile.exec(si.src)) ) { script = si; break; } } } if (script) script.firebugIgnore = true; if (file) { var fileName = file[1]; var fileOptions = file[2]; // absolute path if (reProtocol.test(script.src)) { path = rePath.exec(script.src)[1]; } // relative path else { var r = rePath.exec(script.src); var src = r ? r[1] : script.src; var backDir = /^((?:\.\.\/)+)(.*)/.exec(src); var reLastDir = /^(.*\/)[^\/]+\/$/; path = rePath.exec(location.href)[1]; // "../some/path" if (backDir) { var j = backDir[1].length/3; var p; while (j-- > 0) path = reLastDir.exec(path)[1]; path += backDir[2]; } else if(src.indexOf("/") != -1) { // "./some/path" if(/^\.\/./.test(src)) { path += src.substring(2); } // "/some/path" else if(/^\/./.test(src)) { var domain = /^(\w+:\/\/[^\/]+)/.exec(path); path = domain[1] + src; } // "some/path" else { path += src; } } } } FBL.Env.isChromeExtension = script && script.getAttribute("extension") == "Chrome"; if (FBL.Env.isChromeExtension) { path = productionDir; FBL.Env.bookmarkletOutdated = false; script = {innerHTML: "{showIconWhenHidden:false}"}; } var m = path && path.match(/([^\/]+)\/$/) || null; if (path && m) { var Env = FBL.Env; // Always use the local skin when running in the same domain // See Issue 3554: Firebug Lite should use local images when loaded locally Env.useLocalSkin = path.indexOf(location.protocol + "//" + location.host + "/") == 0; // detecting development and debug modes via file name if (fileName == "firebug-lite-dev.js") { Env.isDevelopmentMode = true; Env.isDebugMode = true; } else if (fileName == "firebug-lite-debug.js") { Env.isDebugMode = true; } // process the if (Env.browser.document.documentElement.getAttribute("debug") == "true") { Env.Options.startOpened = true; } // process the Script URL Options if (fileOptions) { var options = fileOptions.split(","); for (var i = 0, length = options.length; i < length; i++) { var option = options[i]; var name, value; if (option.indexOf("=") != -1) { var parts = option.split("="); name = parts[0]; value = eval(unescape(parts[1])); } else { name = option; value = true; } if (name == "debug") { Env.isDebugMode = !!value; } else if (name in Env.Options) { Env.Options[name] = value; } else { Env[name] = value; } } } // process the Script JSON Options var innerOptions = FBL.trim(script.innerHTML); if (innerOptions) { var innerOptionsObject = eval("(" + innerOptions + ")"); for (var name in innerOptionsObject) { var value = innerOptionsObject[name]; if (name == "debug") { Env.isDebugMode = !!value; } else if (name in Env.Options) { Env.Options[name] = value; } else { Env[name] = value; } } } // process the Debug Mode if (Env.isDebugMode) { Env.Options.startOpened = true; Env.Options.enableTrace = true; Env.Options.disableWhenFirebugActive = false; } var loc = Env.Location; var isProductionRelease = path.indexOf(productionDir) != -1; loc.sourceDir = path; loc.baseDir = path.substr(0, path.length - m[1].length - 1); loc.skinDir = (isProductionRelease ? path : loc.baseDir) + "skin/" + Env.skin + "/"; loc.skin = loc.skinDir + "firebug.html"; loc.app = path + fileName; } else { throw new Error("Firebug Error: Library path not found"); } }; // ************************************************************************************************ // Basics this.bind = function() // fn, thisObject, args => thisObject.fn(args, arguments); { var args = cloneArray(arguments), fn = args.shift(), object = args.shift(); return function() { return fn.apply(object, arrayInsert(cloneArray(args), 0, arguments)); }; }; this.bindFixed = function() // fn, thisObject, args => thisObject.fn(args); { var args = cloneArray(arguments), fn = args.shift(), object = args.shift(); return function() { return fn.apply(object, args); }; }; this.extend = function(l, r) { var newOb = {}; for (var n in l) newOb[n] = l[n]; for (var n in r) newOb[n] = r[n]; return newOb; }; this.descend = function(prototypeParent, childProperties) { function protoSetter() {}; protoSetter.prototype = prototypeParent; var newOb = new protoSetter(); for (var n in childProperties) newOb[n] = childProperties[n]; return newOb; }; this.append = function(l, r) { for (var n in r) l[n] = r[n]; return l; }; this.keys = function(map) // At least sometimes the keys will be on user-level window objects { var keys = []; try { for (var name in map) // enumeration is safe keys.push(name); // name is string, safe } catch (exc) { // Sometimes we get exceptions trying to iterate properties } return keys; // return is safe }; this.values = function(map) { var values = []; try { for (var name in map) { try { values.push(map[name]); } catch (exc) { // Sometimes we get exceptions trying to access properties if (FBTrace.DBG_ERRORS) FBTrace.sysout("lib.values FAILED ", exc); } } } catch (exc) { // Sometimes we get exceptions trying to iterate properties if (FBTrace.DBG_ERRORS) FBTrace.sysout("lib.values FAILED ", exc); } return values; }; this.remove = function(list, item) { for (var i = 0; i < list.length; ++i) { if (list[i] == item) { list.splice(i, 1); break; } } }; this.sliceArray = function(array, index) { var slice = []; for (var i = index; i < array.length; ++i) slice.push(array[i]); return slice; }; function cloneArray(array, fn) { var newArray = []; if (fn) for (var i = 0; i < array.length; ++i) newArray.push(fn(array[i])); else for (var i = 0; i < array.length; ++i) newArray.push(array[i]); return newArray; } function extendArray(array, array2) { var newArray = []; newArray.push.apply(newArray, array); newArray.push.apply(newArray, array2); return newArray; } this.extendArray = extendArray; this.cloneArray = cloneArray; function arrayInsert(array, index, other) { for (var i = 0; i < other.length; ++i) array.splice(i+index, 0, other[i]); return array; } // ************************************************************************************************ this.createStyleSheet = function(doc, url) { //TODO: xxxpedro //var style = doc.createElementNS("http://www.w3.org/1999/xhtml", "style"); var style = this.createElement("link"); style.setAttribute("charset","utf-8"); style.firebugIgnore = true; style.setAttribute("rel", "stylesheet"); style.setAttribute("type", "text/css"); style.setAttribute("href", url); //TODO: xxxpedro //style.innerHTML = this.getResource(url); return style; }; this.addStyleSheet = function(doc, style) { var heads = doc.getElementsByTagName("head"); if (heads.length) heads[0].appendChild(style); else doc.documentElement.appendChild(style); }; this.appendStylesheet = function(doc, uri) { // Make sure the stylesheet is not appended twice. if (this.$(uri, doc)) return; var styleSheet = this.createStyleSheet(doc, uri); styleSheet.setAttribute("id", uri); this.addStyleSheet(doc, styleSheet); }; this.addScript = function(doc, id, src) { var element = doc.createElementNS("http://www.w3.org/1999/xhtml", "html:script"); element.setAttribute("type", "text/javascript"); element.setAttribute("id", id); if (!FBTrace.DBG_CONSOLE) FBL.unwrapObject(element).firebugIgnore = true; element.innerHTML = src; if (doc.documentElement) doc.documentElement.appendChild(element); else { // See issue 1079, the svg test case gives this error if (FBTrace.DBG_ERRORS) FBTrace.sysout("lib.addScript doc has no documentElement:", doc); } return element; }; // ************************************************************************************************ this.getStyle = this.isIE ? function(el, name) { return el.currentStyle[name] || el.style[name] || undefined; } : function(el, name) { return el.ownerDocument.defaultView.getComputedStyle(el,null)[name] || el.style[name] || undefined; }; // ************************************************************************************************ // Whitespace and Entity conversions var entityConversionLists = this.entityConversionLists = { normal : { whitespace : { '\t' : '\u200c\u2192', '\n' : '\u200c\u00b6', '\r' : '\u200c\u00ac', ' ' : '\u200c\u00b7' } }, reverse : { whitespace : { ' ' : '\t', ' ' : '\n', '\u200c\u2192' : '\t', '\u200c\u00b6' : '\n', '\u200c\u00ac' : '\r', '\u200c\u00b7' : ' ' } } }; var normal = entityConversionLists.normal, reverse = entityConversionLists.reverse; function addEntityMapToList(ccode, entity) { var lists = Array.prototype.slice.call(arguments, 2), len = lists.length, ch = String.fromCharCode(ccode); for (var i = 0; i < len; i++) { var list = lists[i]; normal[list]=normal[list] || {}; normal[list][ch] = '&' + entity + ';'; reverse[list]=reverse[list] || {}; reverse[list]['&' + entity + ';'] = ch; } }; var e = addEntityMapToList, white = 'whitespace', text = 'text', attr = 'attributes', css = 'css', editor = 'editor'; e(0x0022, 'quot', attr, css); e(0x0026, 'amp', attr, text, css); e(0x0027, 'apos', css); e(0x003c, 'lt', attr, text, css); e(0x003e, 'gt', attr, text, css); e(0xa9, 'copy', text, editor); e(0xae, 'reg', text, editor); e(0x2122, 'trade', text, editor); // See http://en.wikipedia.org/wiki/Dash e(0x2012, '#8210', attr, text, editor); // figure dash e(0x2013, 'ndash', attr, text, editor); // en dash e(0x2014, 'mdash', attr, text, editor); // em dash e(0x2015, '#8213', attr, text, editor); // horizontal bar e(0x00a0, 'nbsp', attr, text, white, editor); e(0x2002, 'ensp', attr, text, white, editor); e(0x2003, 'emsp', attr, text, white, editor); e(0x2009, 'thinsp', attr, text, white, editor); e(0x200c, 'zwnj', attr, text, white, editor); e(0x200d, 'zwj', attr, text, white, editor); e(0x200e, 'lrm', attr, text, white, editor); e(0x200f, 'rlm', attr, text, white, editor); e(0x200b, '#8203', attr, text, white, editor); // zero-width space (ZWSP) //************************************************************************************************ // Entity escaping var entityConversionRegexes = { normal : {}, reverse : {} }; var escapeEntitiesRegEx = { normal : function(list) { var chars = []; for ( var ch in list) { chars.push(ch); } return new RegExp('([' + chars.join('') + '])', 'gm'); }, reverse : function(list) { var chars = []; for ( var ch in list) { chars.push(ch); } return new RegExp('(' + chars.join('|') + ')', 'gm'); } }; function getEscapeRegexp(direction, lists) { var name = '', re; var groups = [].concat(lists); for (i = 0; i < groups.length; i++) { name += groups[i].group; } re = entityConversionRegexes[direction][name]; if (!re) { var list = {}; if (groups.length > 1) { for ( var i = 0; i < groups.length; i++) { var aList = entityConversionLists[direction][groups[i].group]; for ( var item in aList) list[item] = aList[item]; } } else if (groups.length==1) { list = entityConversionLists[direction][groups[0].group]; // faster for special case } else { list = {}; // perhaps should print out an error here? } re = entityConversionRegexes[direction][name] = escapeEntitiesRegEx[direction](list); } return re; }; function createSimpleEscape(name, direction) { return function(value) { var list = entityConversionLists[direction][name]; return String(value).replace( getEscapeRegexp(direction, { group : name, list : list }), function(ch) { return list[ch]; } ); }; }; function escapeGroupsForEntities(str, lists) { lists = [].concat(lists); var re = getEscapeRegexp('normal', lists), split = String(str).split(re), len = split.length, results = [], cur, r, i, ri = 0, l, list, last = ''; if (!len) return [ { str : String(str), group : '', name : '' } ]; for (i = 0; i < len; i++) { cur = split[i]; if (cur == '') continue; for (l = 0; l < lists.length; l++) { list = lists[l]; r = entityConversionLists.normal[list.group][cur]; // if (cur == ' ' && list.group == 'whitespace' && last == ' ') // only show for runs of more than one space // r = ' '; if (r) { results[ri] = { 'str' : r, 'class' : list['class'], 'extra' : list.extra[cur] ? list['class'] + list.extra[cur] : '' }; break; } } // last=cur; if (!r) results[ri] = { 'str' : cur, 'class' : '', 'extra' : '' }; ri++; } return results; }; this.escapeGroupsForEntities = escapeGroupsForEntities; function unescapeEntities(str, lists) { var re = getEscapeRegexp('reverse', lists), split = String(str).split(re), len = split.length, results = [], cur, r, i, ri = 0, l, list; if (!len) return str; lists = [].concat(lists); for (i = 0; i < len; i++) { cur = split[i]; if (cur == '') continue; for (l = 0; l < lists.length; l++) { list = lists[l]; r = entityConversionLists.reverse[list.group][cur]; if (r) { results[ri] = r; break; } } if (!r) results[ri] = cur; ri++; } return results.join('') || ''; }; // ************************************************************************************************ // String escaping var escapeForTextNode = this.escapeForTextNode = createSimpleEscape('text', 'normal'); var escapeForHtmlEditor = this.escapeForHtmlEditor = createSimpleEscape('editor', 'normal'); var escapeForElementAttribute = this.escapeForElementAttribute = createSimpleEscape('attributes', 'normal'); var escapeForCss = this.escapeForCss = createSimpleEscape('css', 'normal'); // deprecated compatibility functions //this.deprecateEscapeHTML = createSimpleEscape('text', 'normal'); //this.deprecatedUnescapeHTML = createSimpleEscape('text', 'reverse'); //this.escapeHTML = deprecated("use appropriate escapeFor... function", this.deprecateEscapeHTML); //this.unescapeHTML = deprecated("use appropriate unescapeFor... function", this.deprecatedUnescapeHTML); var escapeForSourceLine = this.escapeForSourceLine = createSimpleEscape('text', 'normal'); var unescapeWhitespace = createSimpleEscape('whitespace', 'reverse'); this.unescapeForTextNode = function(str) { if (Firebug.showTextNodesWithWhitespace) str = unescapeWhitespace(str); if (!Firebug.showTextNodesWithEntities) str = escapeForElementAttribute(str); return str; }; this.escapeNewLines = function(value) { return value.replace(/\r/g, "\\r").replace(/\n/g, "\\n"); }; this.stripNewLines = function(value) { return typeof(value) == "string" ? value.replace(/[\r\n]/g, " ") : value; }; this.escapeJS = function(value) { return value.replace(/\r/g, "\\r").replace(/\n/g, "\\n").replace('"', '\\"', "g"); }; function escapeHTMLAttribute(value) { function replaceChars(ch) { switch (ch) { case "&": return "&"; case "'": return apos; case '"': return quot; } return "?"; }; var apos = "'", quot = """, around = '"'; if( value.indexOf('"') == -1 ) { quot = '"'; apos = "'"; } else if( value.indexOf("'") == -1 ) { quot = '"'; around = "'"; } return around + (String(value).replace(/[&'"]/g, replaceChars)) + around; } function escapeHTML(value) { function replaceChars(ch) { switch (ch) { case "<": return "<"; case ">": return ">"; case "&": return "&"; case "'": return "'"; case '"': return """; } return "?"; }; return String(value).replace(/[<>&"']/g, replaceChars); } this.escapeHTML = escapeHTML; this.cropString = function(text, limit) { text = text + ""; if (!limit) var halfLimit = 50; else var halfLimit = limit / 2; if (text.length > limit) return this.escapeNewLines(text.substr(0, halfLimit) + "..." + text.substr(text.length-halfLimit)); else return this.escapeNewLines(text); }; this.isWhitespace = function(text) { return !reNotWhitespace.exec(text); }; this.splitLines = function(text) { var reSplitLines2 = /.*(:?\r\n|\n|\r)?/mg; var lines; if (text.match) { lines = text.match(reSplitLines2); } else { var str = text+""; lines = str.match(reSplitLines2); } lines.pop(); return lines; }; // ************************************************************************************************ this.safeToString = function(ob) { if (this.isIE) return ob + ""; try { if (ob && "toString" in ob && typeof ob.toString == "function") return ob.toString(); } catch (exc) { // xxxpedro it is not safe to use ob+""? return ob + ""; ///return "[an object with no toString() function]"; } }; // ************************************************************************************************ this.hasProperties = function(ob) { try { for (var name in ob) return true; } catch (exc) {} return false; }; // ************************************************************************************************ // String Util var reTrim = /^\s+|\s+$/g; this.trim = function(s) { return s.replace(reTrim, ""); }; // ************************************************************************************************ // Empty this.emptyFn = function(){}; // ************************************************************************************************ // Visibility this.isVisible = function(elt) { /* if (elt instanceof XULElement) { //FBTrace.sysout("isVisible elt.offsetWidth: "+elt.offsetWidth+" offsetHeight:"+ elt.offsetHeight+" localName:"+ elt.localName+" nameSpace:"+elt.nameSpaceURI+"\n"); return (!elt.hidden && !elt.collapsed); } /**/ return this.getStyle(elt, "visibility") != "hidden" && ( elt.offsetWidth > 0 || elt.offsetHeight > 0 || elt.tagName in invisibleTags || elt.namespaceURI == "http://www.w3.org/2000/svg" || elt.namespaceURI == "http://www.w3.org/1998/Math/MathML" ); }; this.collapse = function(elt, collapsed) { // IE6 doesn't support the [collapsed] CSS selector. IE7 does support the selector, // but it is causing a bug (the element disappears when you set the "collapsed" // attribute, but it doesn't appear when you remove the attribute. So, for those // cases, we need to use the class attribute. if (this.isIElt8) { if (collapsed) this.setClass(elt, "collapsed"); else this.removeClass(elt, "collapsed"); } else elt.setAttribute("collapsed", collapsed ? "true" : "false"); }; this.obscure = function(elt, obscured) { if (obscured) this.setClass(elt, "obscured"); else this.removeClass(elt, "obscured"); }; this.hide = function(elt, hidden) { elt.style.visibility = hidden ? "hidden" : "visible"; }; this.clearNode = function(node) { var nodeName = " " + node.nodeName.toLowerCase() + " "; var ignoreTags = " table tbody thead tfoot th tr td "; // IE can't use innerHTML of table elements if (this.isIE && ignoreTags.indexOf(nodeName) != -1) this.eraseNode(node); else node.innerHTML = ""; }; this.eraseNode = function(node) { while (node.lastChild) node.removeChild(node.lastChild); }; // ************************************************************************************************ // Window iteration this.iterateWindows = function(win, handler) { if (!win || !win.document) return; handler(win); if (win == top || !win.frames) return; // XXXjjb hack for chromeBug for (var i = 0; i < win.frames.length; ++i) { var subWin = win.frames[i]; if (subWin != win) this.iterateWindows(subWin, handler); } }; this.getRootWindow = function(win) { for (; win; win = win.parent) { if (!win.parent || win == win.parent || !this.instanceOf(win.parent, "Window")) return win; } return null; }; // ************************************************************************************************ // Graphics this.getClientOffset = function(elt) { var addOffset = function addOffset(elt, coords, view) { var p = elt.offsetParent; ///var style = isIE ? elt.currentStyle : view.getComputedStyle(elt, ""); var chrome = Firebug.chrome; if (elt.offsetLeft) ///coords.x += elt.offsetLeft + parseInt(style.borderLeftWidth); coords.x += elt.offsetLeft + chrome.getMeasurementInPixels(elt, "borderLeft"); if (elt.offsetTop) ///coords.y += elt.offsetTop + parseInt(style.borderTopWidth); coords.y += elt.offsetTop + chrome.getMeasurementInPixels(elt, "borderTop"); if (p) { if (p.nodeType == 1) addOffset(p, coords, view); } else { var otherView = isIE ? elt.ownerDocument.parentWindow : elt.ownerDocument.defaultView; // IE will fail when reading the frameElement property of a popup window. // We don't need it anyway once it is outside the (popup) viewport, so we're // ignoring the frameElement check when the window is a popup if (!otherView.opener && otherView.frameElement) addOffset(otherView.frameElement, coords, otherView); } }; var isIE = this.isIE; var coords = {x: 0, y: 0}; if (elt) { var view = isIE ? elt.ownerDocument.parentWindow : elt.ownerDocument.defaultView; addOffset(elt, coords, view); } return coords; }; this.getViewOffset = function(elt, singleFrame) { function addOffset(elt, coords, view) { var p = elt.offsetParent; coords.x += elt.offsetLeft - (p ? p.scrollLeft : 0); coords.y += elt.offsetTop - (p ? p.scrollTop : 0); if (p) { if (p.nodeType == 1) { var parentStyle = view.getComputedStyle(p, ""); if (parentStyle.position != "static") { coords.x += parseInt(parentStyle.borderLeftWidth); coords.y += parseInt(parentStyle.borderTopWidth); if (p.localName == "TABLE") { coords.x += parseInt(parentStyle.paddingLeft); coords.y += parseInt(parentStyle.paddingTop); } else if (p.localName == "BODY") { var style = view.getComputedStyle(elt, ""); coords.x += parseInt(style.marginLeft); coords.y += parseInt(style.marginTop); } } else if (p.localName == "BODY") { coords.x += parseInt(parentStyle.borderLeftWidth); coords.y += parseInt(parentStyle.borderTopWidth); } var parent = elt.parentNode; while (p != parent) { coords.x -= parent.scrollLeft; coords.y -= parent.scrollTop; parent = parent.parentNode; } addOffset(p, coords, view); } } else { if (elt.localName == "BODY") { var style = view.getComputedStyle(elt, ""); coords.x += parseInt(style.borderLeftWidth); coords.y += parseInt(style.borderTopWidth); var htmlStyle = view.getComputedStyle(elt.parentNode, ""); coords.x -= parseInt(htmlStyle.paddingLeft); coords.y -= parseInt(htmlStyle.paddingTop); } if (elt.scrollLeft) coords.x += elt.scrollLeft; if (elt.scrollTop) coords.y += elt.scrollTop; var win = elt.ownerDocument.defaultView; if (win && (!singleFrame && win.frameElement)) addOffset(win.frameElement, coords, win); } } var coords = {x: 0, y: 0}; if (elt) addOffset(elt, coords, elt.ownerDocument.defaultView); return coords; }; this.getLTRBWH = function(elt) { var bcrect, dims = {"left": 0, "top": 0, "right": 0, "bottom": 0, "width": 0, "height": 0}; if (elt) { bcrect = elt.getBoundingClientRect(); dims.left = bcrect.left; dims.top = bcrect.top; dims.right = bcrect.right; dims.bottom = bcrect.bottom; if(bcrect.width) { dims.width = bcrect.width; dims.height = bcrect.height; } else { dims.width = dims.right - dims.left; dims.height = dims.bottom - dims.top; } } return dims; }; this.applyBodyOffsets = function(elt, clientRect) { var od = elt.ownerDocument; if (!od.body) return clientRect; var style = od.defaultView.getComputedStyle(od.body, null); var pos = style.getPropertyValue('position'); if(pos === 'absolute' || pos === 'relative') { var borderLeft = parseInt(style.getPropertyValue('border-left-width').replace('px', ''),10) || 0; var borderTop = parseInt(style.getPropertyValue('border-top-width').replace('px', ''),10) || 0; var paddingLeft = parseInt(style.getPropertyValue('padding-left').replace('px', ''),10) || 0; var paddingTop = parseInt(style.getPropertyValue('padding-top').replace('px', ''),10) || 0; var marginLeft = parseInt(style.getPropertyValue('margin-left').replace('px', ''),10) || 0; var marginTop = parseInt(style.getPropertyValue('margin-top').replace('px', ''),10) || 0; var offsetX = borderLeft + paddingLeft + marginLeft; var offsetY = borderTop + paddingTop + marginTop; clientRect.left -= offsetX; clientRect.top -= offsetY; clientRect.right -= offsetX; clientRect.bottom -= offsetY; } return clientRect; }; this.getOffsetSize = function(elt) { return {width: elt.offsetWidth, height: elt.offsetHeight}; }; this.getOverflowParent = function(element) { for (var scrollParent = element.parentNode; scrollParent; scrollParent = scrollParent.offsetParent) { if (scrollParent.scrollHeight > scrollParent.offsetHeight) return scrollParent; } }; this.isScrolledToBottom = function(element) { var onBottom = (element.scrollTop + element.offsetHeight) == element.scrollHeight; if (FBTrace.DBG_CONSOLE) FBTrace.sysout("isScrolledToBottom offsetHeight: "+element.offsetHeight +" onBottom:"+onBottom); return onBottom; }; this.scrollToBottom = function(element) { element.scrollTop = element.scrollHeight; if (FBTrace.DBG_CONSOLE) { FBTrace.sysout("scrollToBottom reset scrollTop "+element.scrollTop+" = "+element.scrollHeight); if (element.scrollHeight == element.offsetHeight) FBTrace.sysout("scrollToBottom attempt to scroll non-scrollable element "+element, element); } return (element.scrollTop == element.scrollHeight); }; this.move = function(element, x, y) { element.style.left = x + "px"; element.style.top = y + "px"; }; this.resize = function(element, w, h) { element.style.width = w + "px"; element.style.height = h + "px"; }; this.linesIntoCenterView = function(element, scrollBox) // {before: int, after: int} { if (!scrollBox) scrollBox = this.getOverflowParent(element); if (!scrollBox) return; var offset = this.getClientOffset(element); var topSpace = offset.y - scrollBox.scrollTop; var bottomSpace = (scrollBox.scrollTop + scrollBox.clientHeight) - (offset.y + element.offsetHeight); if (topSpace < 0 || bottomSpace < 0) { var split = (scrollBox.clientHeight/2); var centerY = offset.y - split; scrollBox.scrollTop = centerY; topSpace = split; bottomSpace = split - element.offsetHeight; } return {before: Math.round((topSpace/element.offsetHeight) + 0.5), after: Math.round((bottomSpace/element.offsetHeight) + 0.5) }; }; this.scrollIntoCenterView = function(element, scrollBox, notX, notY) { if (!element) return; if (!scrollBox) scrollBox = this.getOverflowParent(element); if (!scrollBox) return; var offset = this.getClientOffset(element); if (!notY) { var topSpace = offset.y - scrollBox.scrollTop; var bottomSpace = (scrollBox.scrollTop + scrollBox.clientHeight) - (offset.y + element.offsetHeight); if (topSpace < 0 || bottomSpace < 0) { var centerY = offset.y - (scrollBox.clientHeight/2); scrollBox.scrollTop = centerY; } } if (!notX) { var leftSpace = offset.x - scrollBox.scrollLeft; var rightSpace = (scrollBox.scrollLeft + scrollBox.clientWidth) - (offset.x + element.clientWidth); if (leftSpace < 0 || rightSpace < 0) { var centerX = offset.x - (scrollBox.clientWidth/2); scrollBox.scrollLeft = centerX; } } if (FBTrace.DBG_SOURCEFILES) FBTrace.sysout("lib.scrollIntoCenterView ","Element:"+element.innerHTML); }; // ************************************************************************************************ // CSS var cssKeywordMap = null; var cssPropNames = null; var cssColorNames = null; var imageRules = null; this.getCSSKeywordsByProperty = function(propName) { if (!cssKeywordMap) { cssKeywordMap = {}; for (var name in this.cssInfo) { var list = []; var types = this.cssInfo[name]; for (var i = 0; i < types.length; ++i) { var keywords = this.cssKeywords[types[i]]; if (keywords) list.push.apply(list, keywords); } cssKeywordMap[name] = list; } } return propName in cssKeywordMap ? cssKeywordMap[propName] : []; }; this.getCSSPropertyNames = function() { if (!cssPropNames) { cssPropNames = []; for (var name in this.cssInfo) cssPropNames.push(name); } return cssPropNames; }; this.isColorKeyword = function(keyword) { if (keyword == "transparent") return false; if (!cssColorNames) { cssColorNames = []; var colors = this.cssKeywords["color"]; for (var i = 0; i < colors.length; ++i) cssColorNames.push(colors[i].toLowerCase()); var systemColors = this.cssKeywords["systemColor"]; for (var i = 0; i < systemColors.length; ++i) cssColorNames.push(systemColors[i].toLowerCase()); } return cssColorNames.indexOf ? // Array.indexOf is not available in IE cssColorNames.indexOf(keyword.toLowerCase()) != -1 : (" " + cssColorNames.join(" ") + " ").indexOf(" " + keyword.toLowerCase() + " ") != -1; }; this.isImageRule = function(rule) { if (!imageRules) { imageRules = []; for (var i in this.cssInfo) { var r = i.toLowerCase(); var suffix = "image"; if (r.match(suffix + "$") == suffix || r == "background") imageRules.push(r); } } return imageRules.indexOf ? // Array.indexOf is not available in IE imageRules.indexOf(rule.toLowerCase()) != -1 : (" " + imageRules.join(" ") + " ").indexOf(" " + rule.toLowerCase() + " ") != -1; }; this.copyTextStyles = function(fromNode, toNode, style) { var view = this.isIE ? fromNode.ownerDocument.parentWindow : fromNode.ownerDocument.defaultView; if (view) { if (!style) style = this.isIE ? fromNode.currentStyle : view.getComputedStyle(fromNode, ""); toNode.style.fontFamily = style.fontFamily; // TODO: xxxpedro need to create a FBL.getComputedStyle() because IE // returns wrong computed styles for inherited properties (like font-*) // // Also would be good to create a FBL.getStyle() toNode.style.fontSize = style.fontSize; toNode.style.fontWeight = style.fontWeight; toNode.style.fontStyle = style.fontStyle; return style; } }; this.copyBoxStyles = function(fromNode, toNode, style) { var view = this.isIE ? fromNode.ownerDocument.parentWindow : fromNode.ownerDocument.defaultView; if (view) { if (!style) style = this.isIE ? fromNode.currentStyle : view.getComputedStyle(fromNode, ""); toNode.style.marginTop = style.marginTop; toNode.style.marginRight = style.marginRight; toNode.style.marginBottom = style.marginBottom; toNode.style.marginLeft = style.marginLeft; toNode.style.borderTopWidth = style.borderTopWidth; toNode.style.borderRightWidth = style.borderRightWidth; toNode.style.borderBottomWidth = style.borderBottomWidth; toNode.style.borderLeftWidth = style.borderLeftWidth; return style; } }; this.readBoxStyles = function(style) { var styleNames = { "margin-top": "marginTop", "margin-right": "marginRight", "margin-left": "marginLeft", "margin-bottom": "marginBottom", "border-top-width": "borderTop", "border-right-width": "borderRight", "border-left-width": "borderLeft", "border-bottom-width": "borderBottom", "padding-top": "paddingTop", "padding-right": "paddingRight", "padding-left": "paddingLeft", "padding-bottom": "paddingBottom", "z-index": "zIndex" }; var styles = {}; for (var styleName in styleNames) styles[styleNames[styleName]] = parseInt(style.getPropertyCSSValue(styleName).cssText) || 0; if (FBTrace.DBG_INSPECT) FBTrace.sysout("readBoxStyles ", styles); return styles; }; this.getBoxFromStyles = function(style, element) { var args = this.readBoxStyles(style); args.width = element.offsetWidth - (args.paddingLeft+args.paddingRight+args.borderLeft+args.borderRight); args.height = element.offsetHeight - (args.paddingTop+args.paddingBottom+args.borderTop+args.borderBottom); return args; }; this.getElementCSSSelector = function(element) { var label = element.localName.toLowerCase(); if (element.id) label += "#" + element.id; if (element.hasAttribute("class")) label += "." + element.getAttribute("class").split(" ")[0]; return label; }; this.getURLForStyleSheet= function(styleSheet) { //http://www.w3.org/TR/DOM-Level-2-Style/stylesheets.html#StyleSheets-StyleSheet. For inline style sheets, the value of this attribute is null. return (styleSheet.href ? styleSheet.href : styleSheet.ownerNode.ownerDocument.URL); }; this.getDocumentForStyleSheet = function(styleSheet) { while (styleSheet.parentStyleSheet && !styleSheet.ownerNode) { styleSheet = styleSheet.parentStyleSheet; } if (styleSheet.ownerNode) return styleSheet.ownerNode.ownerDocument; }; /** * Retrieves the instance number for a given style sheet. The instance number * is sheet's index within the set of all other sheets whose URL is the same. */ this.getInstanceForStyleSheet = function(styleSheet, ownerDocument) { // System URLs are always unique (or at least we are making this assumption) if (FBL.isSystemStyleSheet(styleSheet)) return 0; // ownerDocument is an optional hint for performance if (FBTrace.DBG_CSS) FBTrace.sysout("getInstanceForStyleSheet: " + styleSheet.href + " " + styleSheet.media.mediaText + " " + (styleSheet.ownerNode && FBL.getElementXPath(styleSheet.ownerNode)), ownerDocument); ownerDocument = ownerDocument || FBL.getDocumentForStyleSheet(styleSheet); var ret = 0, styleSheets = ownerDocument.styleSheets, href = styleSheet.href; for (var i = 0; i < styleSheets.length; i++) { var curSheet = styleSheets[i]; if (FBTrace.DBG_CSS) FBTrace.sysout("getInstanceForStyleSheet: compare href " + i + " " + curSheet.href + " " + curSheet.media.mediaText + " " + (curSheet.ownerNode && FBL.getElementXPath(curSheet.ownerNode))); if (curSheet == styleSheet) break; if (curSheet.href == href) ret++; } return ret; }; // ************************************************************************************************ // HTML and XML Serialization var getElementType = this.getElementType = function(node) { if (isElementXUL(node)) return 'xul'; else if (isElementSVG(node)) return 'svg'; else if (isElementMathML(node)) return 'mathml'; else if (isElementXHTML(node)) return 'xhtml'; else if (isElementHTML(node)) return 'html'; } var getElementSimpleType = this.getElementSimpleType = function(node) { if (isElementSVG(node)) return 'svg'; else if (isElementMathML(node)) return 'mathml'; else return 'html'; } var isElementHTML = this.isElementHTML = function(node) { return node.nodeName == node.nodeName.toUpperCase(); } var isElementXHTML = this.isElementXHTML = function(node) { return node.nodeName == node.nodeName.toLowerCase(); } var isElementMathML = this.isElementMathML = function(node) { return node.namespaceURI == 'http://www.w3.org/1998/Math/MathML'; } var isElementSVG = this.isElementSVG = function(node) { return node.namespaceURI == 'http://www.w3.org/2000/svg'; } var isElementXUL = this.isElementXUL = function(node) { return node instanceof XULElement; } this.isSelfClosing = function(element) { if (isElementSVG(element) || isElementMathML(element)) return true; var tag = element.localName.toLowerCase(); return (this.selfClosingTags.hasOwnProperty(tag)); }; this.getElementHTML = function(element) { var self=this; function toHTML(elt) { if (elt.nodeType == Node.ELEMENT_NODE) { if (unwrapObject(elt).firebugIgnore) return; html.push('<', elt.nodeName.toLowerCase()); for (var i = 0; i < elt.attributes.length; ++i) { var attr = elt.attributes[i]; // Hide attributes set by Firebug if (attr.localName.indexOf("firebug-") == 0) continue; // MathML if (attr.localName.indexOf("-moz-math") == 0) { // just hide for now continue; } html.push(' ', attr.nodeName, '="', escapeForElementAttribute(attr.nodeValue),'"'); } if (elt.firstChild) { html.push('>'); var pureText=true; for (var child = element.firstChild; child; child = child.nextSibling) pureText=pureText && (child.nodeType == Node.TEXT_NODE); if (pureText) html.push(escapeForHtmlEditor(elt.textContent)); else { for (var child = elt.firstChild; child; child = child.nextSibling) toHTML(child); } html.push(''); } else if (isElementSVG(elt) || isElementMathML(elt)) { html.push('/>'); } else if (self.isSelfClosing(elt)) { html.push((isElementXHTML(elt))?'/>':'>'); } else { html.push('>'); } } else if (elt.nodeType == Node.TEXT_NODE) html.push(escapeForTextNode(elt.textContent)); else if (elt.nodeType == Node.CDATA_SECTION_NODE) html.push(''); else if (elt.nodeType == Node.COMMENT_NODE) html.push(''); } var html = []; toHTML(element); return html.join(""); }; this.getElementXML = function(element) { function toXML(elt) { if (elt.nodeType == Node.ELEMENT_NODE) { if (unwrapObject(elt).firebugIgnore) return; xml.push('<', elt.nodeName.toLowerCase()); for (var i = 0; i < elt.attributes.length; ++i) { var attr = elt.attributes[i]; // Hide attributes set by Firebug if (attr.localName.indexOf("firebug-") == 0) continue; // MathML if (attr.localName.indexOf("-moz-math") == 0) { // just hide for now continue; } xml.push(' ', attr.nodeName, '="', escapeForElementAttribute(attr.nodeValue),'"'); } if (elt.firstChild) { xml.push('>'); for (var child = elt.firstChild; child; child = child.nextSibling) toXML(child); xml.push(''); } else xml.push('/>'); } else if (elt.nodeType == Node.TEXT_NODE) xml.push(elt.nodeValue); else if (elt.nodeType == Node.CDATA_SECTION_NODE) xml.push(''); else if (elt.nodeType == Node.COMMENT_NODE) xml.push(''); } var xml = []; toXML(element); return xml.join(""); }; // ************************************************************************************************ // CSS classes this.hasClass = function(node, name) // className, className, ... { // TODO: xxxpedro when lib.hasClass is called with more than 2 arguments? // this function can be optimized a lot if assumed 2 arguments only, // which seems to be what happens 99% of the time if (arguments.length == 2) return (' '+node.className+' ').indexOf(' '+name+' ') != -1; if (!node || node.nodeType != 1) return false; else { for (var i=1; i= 0) { var size = name.length; node.className = node.className.substr(0,index-1) + node.className.substr(index+size); } } }; this.toggleClass = function(elt, name) { if ((' '+elt.className+' ').indexOf(' '+name+' ') != -1) ///if (this.hasClass(elt, name)) this.removeClass(elt, name); else this.setClass(elt, name); }; this.setClassTimed = function(elt, name, context, timeout) { if (!timeout) timeout = 1300; if (elt.__setClassTimeout) context.clearTimeout(elt.__setClassTimeout); else this.setClass(elt, name); elt.__setClassTimeout = context.setTimeout(function() { delete elt.__setClassTimeout; FBL.removeClass(elt, name); }, timeout); }; this.cancelClassTimed = function(elt, name, context) { if (elt.__setClassTimeout) { FBL.removeClass(elt, name); context.clearTimeout(elt.__setClassTimeout); delete elt.__setClassTimeout; } }; // ************************************************************************************************ // DOM queries this.$ = function(id, doc) { if (doc) return doc.getElementById(id); else { return FBL.Firebug.chrome.document.getElementById(id); } }; this.$$ = function(selector, doc) { if (doc || !FBL.Firebug.chrome) return FBL.Firebug.Selector(selector, doc); else { return FBL.Firebug.Selector(selector, FBL.Firebug.chrome.document); } }; this.getChildByClass = function(node) // ,classname, classname, classname... { for (var i = 1; i < arguments.length; ++i) { var className = arguments[i]; var child = node.firstChild; node = null; for (; child; child = child.nextSibling) { if (this.hasClass(child, className)) { node = child; break; } } } return node; }; this.getAncestorByClass = function(node, className) { for (var parent = node; parent; parent = parent.parentNode) { if (this.hasClass(parent, className)) return parent; } return null; }; this.getElementsByClass = function(node, className) { var result = []; for (var child = node.firstChild; child; child = child.nextSibling) { if (this.hasClass(child, className)) result.push(child); } return result; }; this.getElementByClass = function(node, className) // className, className, ... { var args = cloneArray(arguments); args.splice(0, 1); for (var child = node.firstChild; child; child = child.nextSibling) { var args1 = cloneArray(args); args1.unshift(child); if (FBL.hasClass.apply(null, args1)) return child; else { var found = FBL.getElementByClass.apply(null, args1); if (found) return found; } } return null; }; this.isAncestor = function(node, potentialAncestor) { for (var parent = node; parent; parent = parent.parentNode) { if (parent == potentialAncestor) return true; } return false; }; this.getNextElement = function(node) { while (node && node.nodeType != 1) node = node.nextSibling; return node; }; this.getPreviousElement = function(node) { while (node && node.nodeType != 1) node = node.previousSibling; return node; }; this.getBody = function(doc) { if (doc.body) return doc.body; var body = doc.getElementsByTagName("body")[0]; if (body) return body; return doc.firstChild; // For non-HTML docs }; this.findNextDown = function(node, criteria) { if (!node) return null; for (var child = node.firstChild; child; child = child.nextSibling) { if (criteria(child)) return child; var next = this.findNextDown(child, criteria); if (next) return next; } }; this.findPreviousUp = function(node, criteria) { if (!node) return null; for (var child = node.lastChild; child; child = child.previousSibling) { var next = this.findPreviousUp(child, criteria); if (next) return next; if (criteria(child)) return child; } }; this.findNext = function(node, criteria, upOnly, maxRoot) { if (!node) return null; if (!upOnly) { var next = this.findNextDown(node, criteria); if (next) return next; } for (var sib = node.nextSibling; sib; sib = sib.nextSibling) { if (criteria(sib)) return sib; var next = this.findNextDown(sib, criteria); if (next) return next; } if (node.parentNode && node.parentNode != maxRoot) return this.findNext(node.parentNode, criteria, true); }; this.findPrevious = function(node, criteria, downOnly, maxRoot) { if (!node) return null; for (var sib = node.previousSibling; sib; sib = sib.previousSibling) { var prev = this.findPreviousUp(sib, criteria); if (prev) return prev; if (criteria(sib)) return sib; } if (!downOnly) { var next = this.findPreviousUp(node, criteria); if (next) return next; } if (node.parentNode && node.parentNode != maxRoot) { if (criteria(node.parentNode)) return node.parentNode; return this.findPrevious(node.parentNode, criteria, true); } }; this.getNextByClass = function(root, state) { var iter = function iter(node) { return node.nodeType == 1 && FBL.hasClass(node, state); }; return this.findNext(root, iter); }; this.getPreviousByClass = function(root, state) { var iter = function iter(node) { return node.nodeType == 1 && FBL.hasClass(node, state); }; return this.findPrevious(root, iter); }; this.isElement = function(o) { try { return o && this.instanceOf(o, "Element"); } catch (ex) { return false; } }; // ************************************************************************************************ // DOM Modification // TODO: xxxpedro use doc fragments in Context API var appendFragment = null; this.appendInnerHTML = function(element, html, referenceElement) { // if undefined, we must convert it to null otherwise it will throw an error in IE // when executing element.insertBefore(firstChild, referenceElement) referenceElement = referenceElement || null; var doc = element.ownerDocument; // doc.createRange not available in IE if (doc.createRange) { var range = doc.createRange(); // a helper object range.selectNodeContents(element); // the environment to interpret the html var fragment = range.createContextualFragment(html); // parse var firstChild = fragment.firstChild; element.insertBefore(fragment, referenceElement); } else { if (!appendFragment || appendFragment.ownerDocument != doc) appendFragment = doc.createDocumentFragment(); var div = doc.createElement("div"); div.innerHTML = html; var firstChild = div.firstChild; while (div.firstChild) appendFragment.appendChild(div.firstChild); element.insertBefore(appendFragment, referenceElement); div = null; } return firstChild; }; // ************************************************************************************************ // DOM creation this.createElement = function(tagName, properties) { properties = properties || {}; var doc = properties.document || FBL.Firebug.chrome.document; var element = doc.createElement(tagName); for(var name in properties) { if (name != "document") { element[name] = properties[name]; } } return element; }; this.createGlobalElement = function(tagName, properties) { properties = properties || {}; var doc = FBL.Env.browser.document; var element = this.NS && doc.createElementNS ? doc.createElementNS(FBL.NS, tagName) : doc.createElement(tagName); for(var name in properties) { var propname = name; if (FBL.isIE && name == "class") propname = "className"; if (name != "document") { element.setAttribute(propname, properties[name]); } } return element; }; //************************************************************************************************ this.safeGetWindowLocation = function(window) { try { if (window) { if (window.closed) return "(window.closed)"; if ("location" in window) return window.location+""; else return "(no window.location)"; } else return "(no context.window)"; } catch(exc) { if (FBTrace.DBG_WINDOWS || FBTrace.DBG_ERRORS) FBTrace.sysout("TabContext.getWindowLocation failed "+exc, exc); FBTrace.sysout("TabContext.getWindowLocation failed window:", window); return "(getWindowLocation: "+exc+")"; } }; // ************************************************************************************************ // Events this.isLeftClick = function(event) { return (this.isIE && event.type != "click" && event.type != "dblclick" ? event.button == 1 : // IE "click" and "dblclick" button model event.button == 0) && // others this.noKeyModifiers(event); }; this.isMiddleClick = function(event) { return (this.isIE && event.type != "click" && event.type != "dblclick" ? event.button == 4 : // IE "click" and "dblclick" button model event.button == 1) && this.noKeyModifiers(event); }; this.isRightClick = function(event) { return (this.isIE && event.type != "click" && event.type != "dblclick" ? event.button == 2 : // IE "click" and "dblclick" button model event.button == 2) && this.noKeyModifiers(event); }; this.noKeyModifiers = function(event) { return !event.ctrlKey && !event.shiftKey && !event.altKey && !event.metaKey; }; this.isControlClick = function(event) { return (this.isIE && event.type != "click" && event.type != "dblclick" ? event.button == 1 : // IE "click" and "dblclick" button model event.button == 0) && this.isControl(event); }; this.isShiftClick = function(event) { return (this.isIE && event.type != "click" && event.type != "dblclick" ? event.button == 1 : // IE "click" and "dblclick" button model event.button == 0) && this.isShift(event); }; this.isControl = function(event) { return (event.metaKey || event.ctrlKey) && !event.shiftKey && !event.altKey; }; this.isAlt = function(event) { return event.altKey && !event.ctrlKey && !event.shiftKey && !event.metaKey; }; this.isAltClick = function(event) { return (this.isIE && event.type != "click" && event.type != "dblclick" ? event.button == 1 : // IE "click" and "dblclick" button model event.button == 0) && this.isAlt(event); }; this.isControlShift = function(event) { return (event.metaKey || event.ctrlKey) && event.shiftKey && !event.altKey; }; this.isShift = function(event) { return event.shiftKey && !event.metaKey && !event.ctrlKey && !event.altKey; }; this.addEvent = function(object, name, handler, useCapture) { if (object.addEventListener) object.addEventListener(name, handler, useCapture); else object.attachEvent("on"+name, handler); }; this.removeEvent = function(object, name, handler, useCapture) { try { if (object.removeEventListener) object.removeEventListener(name, handler, useCapture); else object.detachEvent("on"+name, handler); } catch(e) { if (FBTrace.DBG_ERRORS) FBTrace.sysout("FBL.removeEvent error: ", object, name); } }; this.cancelEvent = function(e, preventDefault) { if (!e) return; if (preventDefault) { if (e.preventDefault) e.preventDefault(); else e.returnValue = false; } if (e.stopPropagation) e.stopPropagation(); else e.cancelBubble = true; }; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * this.addGlobalEvent = function(name, handler) { var doc = this.Firebug.browser.document; var frames = this.Firebug.browser.window.frames; this.addEvent(doc, name, handler); if (this.Firebug.chrome.type == "popup") this.addEvent(this.Firebug.chrome.document, name, handler); for (var i = 0, frame; frame = frames[i]; i++) { try { this.addEvent(frame.document, name, handler); } catch(E) { // Avoid acess denied } } }; this.removeGlobalEvent = function(name, handler) { var doc = this.Firebug.browser.document; var frames = this.Firebug.browser.window.frames; this.removeEvent(doc, name, handler); if (this.Firebug.chrome.type == "popup") this.removeEvent(this.Firebug.chrome.document, name, handler); for (var i = 0, frame; frame = frames[i]; i++) { try { this.removeEvent(frame.document, name, handler); } catch(E) { // Avoid acess denied } } }; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * this.dispatch = function(listeners, name, args) { if (!listeners) return; try {/**/ if (typeof listeners.length != "undefined") { if (FBTrace.DBG_DISPATCH) FBTrace.sysout("FBL.dispatch", name+" to "+listeners.length+" listeners"); for (var i = 0; i < listeners.length; ++i) { var listener = listeners[i]; if ( listener[name] ) listener[name].apply(listener, args); } } else { if (FBTrace.DBG_DISPATCH) FBTrace.sysout("FBL.dispatch", name+" to listeners of an object"); for (var prop in listeners) { var listener = listeners[prop]; if ( listener[name] ) listener[name].apply(listener, args); } } } catch (exc) { if (FBTrace.DBG_ERRORS) { FBTrace.sysout(" Exception in lib.dispatch "+ name, exc); //FBTrace.dumpProperties(" Exception in lib.dispatch listener", listener); } } /**/ }; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * var disableTextSelectionHandler = function(event) { FBL.cancelEvent(event, true); return false; }; this.disableTextSelection = function(e) { if (typeof e.onselectstart != "undefined") // IE this.addEvent(e, "selectstart", disableTextSelectionHandler); else // others { e.style.cssText = "user-select: none; -khtml-user-select: none; -moz-user-select: none;"; // canceling the event in FF will prevent the menu popups to close when clicking over // text-disabled elements if (!this.isFirefox) this.addEvent(e, "mousedown", disableTextSelectionHandler); } e.style.cursor = "default"; }; this.restoreTextSelection = function(e) { if (typeof e.onselectstart != "undefined") // IE this.removeEvent(e, "selectstart", disableTextSelectionHandler); else // others { e.style.cssText = "cursor: default;"; // canceling the event in FF will prevent the menu popups to close when clicking over // text-disabled elements if (!this.isFirefox) this.removeEvent(e, "mousedown", disableTextSelectionHandler); } }; // ************************************************************************************************ // DOM Events var eventTypes = { composition: [ "composition", "compositionstart", "compositionend" ], contextmenu: [ "contextmenu" ], drag: [ "dragenter", "dragover", "dragexit", "dragdrop", "draggesture" ], focus: [ "focus", "blur" ], form: [ "submit", "reset", "change", "select", "input" ], key: [ "keydown", "keyup", "keypress" ], load: [ "load", "beforeunload", "unload", "abort", "error" ], mouse: [ "mousedown", "mouseup", "click", "dblclick", "mouseover", "mouseout", "mousemove" ], mutation: [ "DOMSubtreeModified", "DOMNodeInserted", "DOMNodeRemoved", "DOMNodeRemovedFromDocument", "DOMNodeInsertedIntoDocument", "DOMAttrModified", "DOMCharacterDataModified" ], paint: [ "paint", "resize", "scroll" ], scroll: [ "overflow", "underflow", "overflowchanged" ], text: [ "text" ], ui: [ "DOMActivate", "DOMFocusIn", "DOMFocusOut" ], xul: [ "popupshowing", "popupshown", "popuphiding", "popuphidden", "close", "command", "broadcast", "commandupdate" ] }; this.getEventFamily = function(eventType) { if (!this.families) { this.families = {}; for (var family in eventTypes) { var types = eventTypes[family]; for (var i = 0; i < types.length; ++i) this.families[types[i]] = family; } } return this.families[eventType]; }; // ************************************************************************************************ // URLs this.getFileName = function(url) { var split = this.splitURLBase(url); return split.name; }; this.splitURLBase = function(url) { if (this.isDataURL(url)) return this.splitDataURL(url); return this.splitURLTrue(url); }; this.splitDataURL = function(url) { var mark = url.indexOf(':', 3); if (mark != 4) return false; // the first 5 chars must be 'data:' var point = url.indexOf(',', mark+1); if (point < mark) return false; // syntax error var props = { encodedContent: url.substr(point+1) }; var metadataBuffer = url.substr(mark+1, point); var metadata = metadataBuffer.split(';'); for (var i = 0; i < metadata.length; i++) { var nv = metadata[i].split('='); if (nv.length == 2) props[nv[0]] = nv[1]; } // Additional Firebug-specific properties if (props.hasOwnProperty('fileName')) { var caller_URL = decodeURIComponent(props['fileName']); var caller_split = this.splitURLTrue(caller_URL); if (props.hasOwnProperty('baseLineNumber')) // this means it's probably an eval() { props['path'] = caller_split.path; props['line'] = props['baseLineNumber']; var hint = decodeURIComponent(props['encodedContent'].substr(0,200)).replace(/\s*$/, ""); props['name'] = 'eval->'+hint; } else { props['name'] = caller_split.name; props['path'] = caller_split.path; } } else { if (!props.hasOwnProperty('path')) props['path'] = "data:"; if (!props.hasOwnProperty('name')) props['name'] = decodeURIComponent(props['encodedContent'].substr(0,200)).replace(/\s*$/, ""); } return props; }; this.splitURLTrue = function(url) { var m = reSplitFile.exec(url); if (!m) return {name: url, path: url}; else if (!m[2]) return {path: m[1], name: m[1]}; else return {path: m[1], name: m[2]+m[3]}; }; this.getFileExtension = function(url) { if (!url) return null; // Remove query string from the URL if any. var queryString = url.indexOf("?"); if (queryString != -1) url = url.substr(0, queryString); // Now get the file extension. var lastDot = url.lastIndexOf("."); return url.substr(lastDot+1); }; this.isSystemURL = function(url) { if (!url) return true; if (url.length == 0) return true; if (url[0] == 'h') return false; if (url.substr(0, 9) == "resource:") return true; else if (url.substr(0, 16) == "chrome://firebug") return true; else if (url == "XPCSafeJSObjectWrapper.cpp") return true; else if (url.substr(0, 6) == "about:") return true; else if (url.indexOf("firebug-service.js") != -1) return true; else return false; }; this.isSystemPage = function(win) { try { var doc = win.document; if (!doc) return false; // Detect pages for pretty printed XML if ((doc.styleSheets.length && doc.styleSheets[0].href == "chrome://global/content/xml/XMLPrettyPrint.css") || (doc.styleSheets.length > 1 && doc.styleSheets[1].href == "chrome://browser/skin/feeds/subscribe.css")) return true; return FBL.isSystemURL(win.location.href); } catch (exc) { // Sometimes documents just aren't ready to be manipulated here, but don't let that // gum up the works ERROR("tabWatcher.isSystemPage document not ready:"+ exc); return false; } }; this.isSystemStyleSheet = function(sheet) { var href = sheet && sheet.href; return href && FBL.isSystemURL(href); }; this.getURIHost = function(uri) { try { if (uri) return uri.host; else return ""; } catch (exc) { return ""; } }; this.isLocalURL = function(url) { if (url.substr(0, 5) == "file:") return true; else if (url.substr(0, 8) == "wyciwyg:") return true; else return false; }; this.isDataURL = function(url) { return (url && url.substr(0,5) == "data:"); }; this.getLocalPath = function(url) { if (this.isLocalURL(url)) { var fileHandler = ioService.getProtocolHandler("file").QueryInterface(Ci.nsIFileProtocolHandler); var file = fileHandler.getFileFromURLSpec(url); return file.path; } }; this.getURLFromLocalFile = function(file) { var fileHandler = ioService.getProtocolHandler("file").QueryInterface(Ci.nsIFileProtocolHandler); var URL = fileHandler.getURLSpecFromFile(file); return URL; }; this.getDataURLForContent = function(content, url) { // data:text/javascript;fileName=x%2Cy.js;baseLineNumber=10, var uri = "data:text/html;"; uri += "fileName="+encodeURIComponent(url)+ ","; uri += encodeURIComponent(content); return uri; }, this.getDomain = function(url) { var m = /[^:]+:\/{1,3}([^\/]+)/.exec(url); return m ? m[1] : ""; }; this.getURLPath = function(url) { var m = /[^:]+:\/{1,3}[^\/]+(\/.*?)$/.exec(url); return m ? m[1] : ""; }; this.getPrettyDomain = function(url) { var m = /[^:]+:\/{1,3}(www\.)?([^\/]+)/.exec(url); return m ? m[2] : ""; }; this.absoluteURL = function(url, baseURL) { return this.absoluteURLWithDots(url, baseURL).replace("/./", "/", "g"); }; this.absoluteURLWithDots = function(url, baseURL) { if (url[0] == "?") return baseURL + url; var reURL = /(([^:]+:)\/{1,2}[^\/]*)(.*?)$/; var m = reURL.exec(url); if (m) return url; var m = reURL.exec(baseURL); if (!m) return ""; var head = m[1]; var tail = m[3]; if (url.substr(0, 2) == "//") return m[2] + url; else if (url[0] == "/") { return head + url; } else if (tail[tail.length-1] == "/") return baseURL + url; else { var parts = tail.split("/"); return head + parts.slice(0, parts.length-1).join("/") + "/" + url; } }; this.normalizeURL = function(url) // this gets called a lot, any performance improvement welcome { if (!url) return ""; // Replace one or more characters that are not forward-slash followed by /.., by space. if (url.length < 255) // guard against monsters. { // Replace one or more characters that are not forward-slash followed by /.., by space. url = url.replace(/[^\/]+\/\.\.\//, "", "g"); // Issue 1496, avoid # url = url.replace(/#.*/,""); // For some reason, JSDS reports file URLs like "file:/" instead of "file:///", so they // don't match up with the URLs we get back from the DOM url = url.replace(/file:\/([^\/])/g, "file:///$1"); if (url.indexOf('chrome:')==0) { var m = reChromeCase.exec(url); // 1 is package name, 2 is path if (m) { url = "chrome://"+m[1].toLowerCase()+"/"+m[2]; } } } return url; }; this.denormalizeURL = function(url) { return url.replace(/file:\/\/\//g, "file:/"); }; this.parseURLParams = function(url) { var q = url ? url.indexOf("?") : -1; if (q == -1) return []; var search = url.substr(q+1); var h = search.lastIndexOf("#"); if (h != -1) search = search.substr(0, h); if (!search) return []; return this.parseURLEncodedText(search); }; this.parseURLEncodedText = function(text) { var maxValueLength = 25000; var params = []; // Unescape '+' characters that are used to encode a space. // See section 2.2.in RFC 3986: http://www.ietf.org/rfc/rfc3986.txt text = text.replace(/\+/g, " "); var args = text.split("&"); for (var i = 0; i < args.length; ++i) { try { var parts = args[i].split("="); if (parts.length == 2) { if (parts[1].length > maxValueLength) parts[1] = this.$STR("LargeData"); params.push({name: decodeURIComponent(parts[0]), value: decodeURIComponent(parts[1])}); } else params.push({name: decodeURIComponent(parts[0]), value: ""}); } catch (e) { if (FBTrace.DBG_ERRORS) { FBTrace.sysout("parseURLEncodedText EXCEPTION ", e); FBTrace.sysout("parseURLEncodedText EXCEPTION URI", args[i]); } } } params.sort(function(a, b) { return a.name <= b.name ? -1 : 1; }); return params; }; // TODO: xxxpedro lib. why loops in domplate are requiring array in parameters // as in response/request headers and get/post parameters in Net module? this.parseURLParamsArray = function(url) { var q = url ? url.indexOf("?") : -1; if (q == -1) return []; var search = url.substr(q+1); var h = search.lastIndexOf("#"); if (h != -1) search = search.substr(0, h); if (!search) return []; return this.parseURLEncodedTextArray(search); }; this.parseURLEncodedTextArray = function(text) { var maxValueLength = 25000; var params = []; // Unescape '+' characters that are used to encode a space. // See section 2.2.in RFC 3986: http://www.ietf.org/rfc/rfc3986.txt text = text.replace(/\+/g, " "); var args = text.split("&"); for (var i = 0; i < args.length; ++i) { try { var parts = args[i].split("="); if (parts.length == 2) { if (parts[1].length > maxValueLength) parts[1] = this.$STR("LargeData"); params.push({name: decodeURIComponent(parts[0]), value: [decodeURIComponent(parts[1])]}); } else params.push({name: decodeURIComponent(parts[0]), value: [""]}); } catch (e) { if (FBTrace.DBG_ERRORS) { FBTrace.sysout("parseURLEncodedText EXCEPTION ", e); FBTrace.sysout("parseURLEncodedText EXCEPTION URI", args[i]); } } } params.sort(function(a, b) { return a.name <= b.name ? -1 : 1; }); return params; }; this.reEncodeURL = function(file, text) { var lines = text.split("\n"); var params = this.parseURLEncodedText(lines[lines.length-1]); var args = []; for (var i = 0; i < params.length; ++i) args.push(encodeURIComponent(params[i].name)+"="+encodeURIComponent(params[i].value)); var url = file.href; url += (url.indexOf("?") == -1 ? "?" : "&") + args.join("&"); return url; }; this.getResource = function(aURL) { try { var channel=ioService.newChannel(aURL,null,null); var input=channel.open(); return FBL.readFromStream(input); } catch (e) { if (FBTrace.DBG_ERRORS) FBTrace.sysout("lib.getResource FAILS for "+aURL, e); } }; this.parseJSONString = function(jsonString, originURL) { // See if this is a Prototype style *-secure request. var regex = new RegExp(/^\/\*-secure-([\s\S]*)\*\/\s*$/); var matches = regex.exec(jsonString); if (matches) { jsonString = matches[1]; if (jsonString[0] == "\\" && jsonString[1] == "n") jsonString = jsonString.substr(2); if (jsonString[jsonString.length-2] == "\\" && jsonString[jsonString.length-1] == "n") jsonString = jsonString.substr(0, jsonString.length-2); } if (jsonString.indexOf("&&&START&&&")) { regex = new RegExp(/&&&START&&& (.+) &&&END&&&/); matches = regex.exec(jsonString); if (matches) jsonString = matches[1]; } // throw on the extra parentheses jsonString = "(" + jsonString + ")"; ///var s = Components.utils.Sandbox(originURL); var jsonObject = null; try { ///jsonObject = Components.utils.evalInSandbox(jsonString, s); //jsonObject = Firebug.context.eval(jsonString); jsonObject = Firebug.context.evaluate(jsonString, null, null, function(){return null;}); } catch(e) { /*** if (e.message.indexOf("is not defined")) { var parts = e.message.split(" "); s[parts[0]] = function(str){ return str; }; try { jsonObject = Components.utils.evalInSandbox(jsonString, s); } catch(ex) { if (FBTrace.DBG_ERRORS || FBTrace.DBG_JSONVIEWER) FBTrace.sysout("jsonviewer.parseJSON EXCEPTION", e); return null; } } else {/**/ if (FBTrace.DBG_ERRORS || FBTrace.DBG_JSONVIEWER) FBTrace.sysout("jsonviewer.parseJSON EXCEPTION", e); return null; ///} } return jsonObject; }; // ************************************************************************************************ this.objectToString = function(object) { try { return object+""; } catch (exc) { return null; } }; // ************************************************************************************************ // Input Caret Position this.setSelectionRange = function(input, start, length) { if (input.createTextRange) { var range = input.createTextRange(); range.moveStart("character", start); range.moveEnd("character", length - input.value.length); range.select(); } else if (input.setSelectionRange) { input.setSelectionRange(start, length); input.focus(); } }; // ************************************************************************************************ // Input Selection Start / Caret Position this.getInputSelectionStart = function(input) { if (document.selection) { var range = input.ownerDocument.selection.createRange(); var text = range.text; //console.log("range", range.text); // if there is a selection, find the start position if (text) { return input.value.indexOf(text); } // if there is no selection, find the caret position else { range.moveStart("character", -input.value.length); return range.text.length; } } else if (typeof input.selectionStart != "undefined") return input.selectionStart; return 0; }; // ************************************************************************************************ // Opera Tab Fix function onOperaTabBlur(e) { if (this.lastKey == 9) this.focus(); }; function onOperaTabKeyDown(e) { this.lastKey = e.keyCode; }; function onOperaTabFocus(e) { this.lastKey = null; }; this.fixOperaTabKey = function(el) { el.onfocus = onOperaTabFocus; el.onblur = onOperaTabBlur; el.onkeydown = onOperaTabKeyDown; }; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * this.Property = function(object, name) { this.object = object; this.name = name; this.getObject = function() { return object[name]; }; }; this.ErrorCopy = function(message) { this.message = message; }; function EventCopy(event) { // Because event objects are destroyed arbitrarily by Gecko, we must make a copy of them to // represent them long term in the inspector. for (var name in event) { try { this[name] = event[name]; } catch (exc) { } } } this.EventCopy = EventCopy; // ************************************************************************************************ // Type Checking var toString = Object.prototype.toString; var reFunction = /^\s*function(\s+[\w_$][\w\d_$]*)?\s*\(/; this.isArray = function(object) { return toString.call(object) === '[object Array]'; }; this.isFunction = function(object) { if (!object) return false; return toString.call(object) === "[object Function]" || this.isIE && typeof object != "string" && reFunction.test(""+object); }; // ************************************************************************************************ // Instance Checking this.instanceOf = function(object, className) { if (!object || typeof object != "object") return false; // Try to use the native instanceof operator. We can only use it when we know // exactly the window where the object is located at if (object.ownerDocument) { // find the correct window of the object var win = object.ownerDocument.defaultView || object.ownerDocument.parentWindow; // if the class is accessible in the window, uses the native instanceof operator // if the instanceof evaluates to "true" we can assume it is a instance, but if it // evaluates to "false" we must continue with the duck type detection below because // the native object may be extended, thus breaking the instanceof result // See Issue 3524: Firebug Lite Style Panel doesn't work if the native Element is extended if (className in win && object instanceof win[className]) return true; } // If the object doesn't have the ownerDocument property, we'll try to look at // the current context's window else { // TODO: xxxpedro context // Since we're not using yet a Firebug.context, we'll just use the top window // (browser) as a reference var win = Firebug.browser.window; if (className in win) return object instanceof win[className]; } // get the duck type model from the cache var cache = instanceCheckMap[className]; if (!cache) return false; // starts the hacky duck type detection for(var n in cache) { var obj = cache[n]; var type = typeof obj; obj = type == "object" ? obj : [obj]; for(var name in obj) { // avoid problems with extended native objects // See Issue 3524: Firebug Lite Style Panel doesn't work if the native Element is extended if (!obj.hasOwnProperty(name)) continue; var value = obj[name]; if( n == "property" && !(value in object) || n == "method" && !this.isFunction(object[value]) || n == "value" && (""+object[name]).toLowerCase() != (""+value).toLowerCase() ) return false; } } return true; }; var instanceCheckMap = { // DuckTypeCheck: // { // property: ["window", "document"], // method: "setTimeout", // value: {nodeType: 1} // }, Window: { property: ["window", "document"], method: "setTimeout" }, Document: { property: ["body", "cookie"], method: "getElementById" }, Node: { property: "ownerDocument", method: "appendChild" }, Element: { property: "tagName", value: {nodeType: 1} }, Location: { property: ["hostname", "protocol"], method: "assign" }, HTMLImageElement: { property: "useMap", value: { nodeType: 1, tagName: "img" } }, HTMLAnchorElement: { property: "hreflang", value: { nodeType: 1, tagName: "a" } }, HTMLInputElement: { property: "form", value: { nodeType: 1, tagName: "input" } }, HTMLButtonElement: { // ? }, HTMLFormElement: { method: "submit", value: { nodeType: 1, tagName: "form" } }, HTMLBodyElement: { }, HTMLHtmlElement: { }, CSSStyleRule: { property: ["selectorText", "style"] } }; // ************************************************************************************************ // DOM Constants /* Problems: - IE does not have window.Node, window.Element, etc - for (var name in Node.prototype) return nothing on FF */ var domMemberMap2 = {}; var domMemberMap2Sandbox = null; var getDomMemberMap2 = function(name) { if (!domMemberMap2Sandbox) { var doc = Firebug.chrome.document; var frame = doc.createElement("iframe"); frame.id = "FirebugSandbox"; frame.style.display = "none"; frame.src = "about:blank"; doc.body.appendChild(frame); domMemberMap2Sandbox = frame.window || frame.contentWindow; } var props = []; //var object = domMemberMap2Sandbox[name]; //object = object.prototype || object; var object = null; if (name == "Window") object = domMemberMap2Sandbox.window; else if (name == "Document") object = domMemberMap2Sandbox.document; else if (name == "HTMLScriptElement") object = domMemberMap2Sandbox.document.createElement("script"); else if (name == "HTMLAnchorElement") object = domMemberMap2Sandbox.document.createElement("a"); else if (name.indexOf("Element") != -1) { object = domMemberMap2Sandbox.document.createElement("div"); } if (object) { //object = object.prototype || object; //props = 'addEventListener,document,location,navigator,window'.split(','); for (var n in object) props.push(n); } /**/ return props; return extendArray(props, domMemberMap[name]); }; // xxxpedro experimental get DOM members this.getDOMMembers = function(object) { if (!domMemberCache) { FBL.domMemberCache = domMemberCache = {}; for (var name in domMemberMap) { var builtins = getDomMemberMap2(name); var cache = domMemberCache[name] = {}; /* if (name.indexOf("Element") != -1) { this.append(cache, this.getDOMMembers("Node")); this.append(cache, this.getDOMMembers("Element")); } /**/ for (var i = 0; i < builtins.length; ++i) cache[builtins[i]] = i; } } try { if (this.instanceOf(object, "Window")) { return domMemberCache.Window; } else if (this.instanceOf(object, "Document") || this.instanceOf(object, "XMLDocument")) { return domMemberCache.Document; } else if (this.instanceOf(object, "Location")) { return domMemberCache.Location; } else if (this.instanceOf(object, "HTMLImageElement")) { return domMemberCache.HTMLImageElement; } else if (this.instanceOf(object, "HTMLAnchorElement")) { return domMemberCache.HTMLAnchorElement; } else if (this.instanceOf(object, "HTMLInputElement")) { return domMemberCache.HTMLInputElement; } else if (this.instanceOf(object, "HTMLButtonElement")) { return domMemberCache.HTMLButtonElement; } else if (this.instanceOf(object, "HTMLFormElement")) { return domMemberCache.HTMLFormElement; } else if (this.instanceOf(object, "HTMLBodyElement")) { return domMemberCache.HTMLBodyElement; } else if (this.instanceOf(object, "HTMLHtmlElement")) { return domMemberCache.HTMLHtmlElement; } else if (this.instanceOf(object, "HTMLScriptElement")) { return domMemberCache.HTMLScriptElement; } else if (this.instanceOf(object, "HTMLTableElement")) { return domMemberCache.HTMLTableElement; } else if (this.instanceOf(object, "HTMLTableRowElement")) { return domMemberCache.HTMLTableRowElement; } else if (this.instanceOf(object, "HTMLTableCellElement")) { return domMemberCache.HTMLTableCellElement; } else if (this.instanceOf(object, "HTMLIFrameElement")) { return domMemberCache.HTMLIFrameElement; } else if (this.instanceOf(object, "SVGSVGElement")) { return domMemberCache.SVGSVGElement; } else if (this.instanceOf(object, "SVGElement")) { return domMemberCache.SVGElement; } else if (this.instanceOf(object, "Element")) { return domMemberCache.Element; } else if (this.instanceOf(object, "Text") || this.instanceOf(object, "CDATASection")) { return domMemberCache.Text; } else if (this.instanceOf(object, "Attr")) { return domMemberCache.Attr; } else if (this.instanceOf(object, "Node")) { return domMemberCache.Node; } else if (this.instanceOf(object, "Event") || this.instanceOf(object, "EventCopy")) { return domMemberCache.Event; } else return {}; } catch(E) { if (FBTrace.DBG_ERRORS) FBTrace.sysout("lib.getDOMMembers FAILED ", E); return {}; } }; /* this.getDOMMembers = function(object) { if (!domMemberCache) { domMemberCache = {}; for (var name in domMemberMap) { var builtins = domMemberMap[name]; var cache = domMemberCache[name] = {}; for (var i = 0; i < builtins.length; ++i) cache[builtins[i]] = i; } } try { if (this.instanceOf(object, "Window")) { return domMemberCache.Window; } else if (object instanceof Document || object instanceof XMLDocument) { return domMemberCache.Document; } else if (object instanceof Location) { return domMemberCache.Location; } else if (object instanceof HTMLImageElement) { return domMemberCache.HTMLImageElement; } else if (object instanceof HTMLAnchorElement) { return domMemberCache.HTMLAnchorElement; } else if (object instanceof HTMLInputElement) { return domMemberCache.HTMLInputElement; } else if (object instanceof HTMLButtonElement) { return domMemberCache.HTMLButtonElement; } else if (object instanceof HTMLFormElement) { return domMemberCache.HTMLFormElement; } else if (object instanceof HTMLBodyElement) { return domMemberCache.HTMLBodyElement; } else if (object instanceof HTMLHtmlElement) { return domMemberCache.HTMLHtmlElement; } else if (object instanceof HTMLScriptElement) { return domMemberCache.HTMLScriptElement; } else if (object instanceof HTMLTableElement) { return domMemberCache.HTMLTableElement; } else if (object instanceof HTMLTableRowElement) { return domMemberCache.HTMLTableRowElement; } else if (object instanceof HTMLTableCellElement) { return domMemberCache.HTMLTableCellElement; } else if (object instanceof HTMLIFrameElement) { return domMemberCache.HTMLIFrameElement; } else if (object instanceof SVGSVGElement) { return domMemberCache.SVGSVGElement; } else if (object instanceof SVGElement) { return domMemberCache.SVGElement; } else if (object instanceof Element) { return domMemberCache.Element; } else if (object instanceof Text || object instanceof CDATASection) { return domMemberCache.Text; } else if (object instanceof Attr) { return domMemberCache.Attr; } else if (object instanceof Node) { return domMemberCache.Node; } else if (object instanceof Event || object instanceof EventCopy) { return domMemberCache.Event; } else return {}; } catch(E) { return {}; } }; /**/ this.isDOMMember = function(object, propName) { var members = this.getDOMMembers(object); return members && propName in members; }; var domMemberCache = null; var domMemberMap = {}; domMemberMap.Window = [ "document", "frameElement", "innerWidth", "innerHeight", "outerWidth", "outerHeight", "screenX", "screenY", "pageXOffset", "pageYOffset", "scrollX", "scrollY", "scrollMaxX", "scrollMaxY", "status", "defaultStatus", "parent", "opener", "top", "window", "content", "self", "location", "history", "frames", "navigator", "screen", "menubar", "toolbar", "locationbar", "personalbar", "statusbar", "directories", "scrollbars", "fullScreen", "netscape", "java", "console", "Components", "controllers", "closed", "crypto", "pkcs11", "name", "property", "length", "sessionStorage", "globalStorage", "setTimeout", "setInterval", "clearTimeout", "clearInterval", "addEventListener", "removeEventListener", "dispatchEvent", "getComputedStyle", "captureEvents", "releaseEvents", "routeEvent", "enableExternalCapture", "disableExternalCapture", "moveTo", "moveBy", "resizeTo", "resizeBy", "scroll", "scrollTo", "scrollBy", "scrollByLines", "scrollByPages", "sizeToContent", "setResizable", "getSelection", "open", "openDialog", "close", "alert", "confirm", "prompt", "dump", "focus", "blur", "find", "back", "forward", "home", "stop", "print", "atob", "btoa", "updateCommands", "XPCNativeWrapper", "GeckoActiveXObject", "applicationCache" // FF3 ]; domMemberMap.Location = [ "href", "protocol", "host", "hostname", "port", "pathname", "search", "hash", "assign", "reload", "replace" ]; domMemberMap.Node = [ "id", "className", "nodeType", "tagName", "nodeName", "localName", "prefix", "namespaceURI", "nodeValue", "ownerDocument", "parentNode", "offsetParent", "nextSibling", "previousSibling", "firstChild", "lastChild", "childNodes", "attributes", "dir", "baseURI", "textContent", "innerHTML", "addEventListener", "removeEventListener", "dispatchEvent", "cloneNode", "appendChild", "insertBefore", "replaceChild", "removeChild", "compareDocumentPosition", "hasAttributes", "hasChildNodes", "lookupNamespaceURI", "lookupPrefix", "normalize", "isDefaultNamespace", "isEqualNode", "isSameNode", "isSupported", "getFeature", "getUserData", "setUserData" ]; domMemberMap.Document = extendArray(domMemberMap.Node, [ "documentElement", "body", "title", "location", "referrer", "cookie", "contentType", "lastModified", "characterSet", "inputEncoding", "xmlEncoding", "xmlStandalone", "xmlVersion", "strictErrorChecking", "documentURI", "URL", "defaultView", "doctype", "implementation", "styleSheets", "images", "links", "forms", "anchors", "embeds", "plugins", "applets", "width", "height", "designMode", "compatMode", "async", "preferredStylesheetSet", "alinkColor", "linkColor", "vlinkColor", "bgColor", "fgColor", "domain", "addEventListener", "removeEventListener", "dispatchEvent", "captureEvents", "releaseEvents", "routeEvent", "clear", "open", "close", "execCommand", "execCommandShowHelp", "getElementsByName", "getSelection", "queryCommandEnabled", "queryCommandIndeterm", "queryCommandState", "queryCommandSupported", "queryCommandText", "queryCommandValue", "write", "writeln", "adoptNode", "appendChild", "removeChild", "renameNode", "cloneNode", "compareDocumentPosition", "createAttribute", "createAttributeNS", "createCDATASection", "createComment", "createDocumentFragment", "createElement", "createElementNS", "createEntityReference", "createEvent", "createExpression", "createNSResolver", "createNodeIterator", "createProcessingInstruction", "createRange", "createTextNode", "createTreeWalker", "domConfig", "evaluate", "evaluateFIXptr", "evaluateXPointer", "getAnonymousElementByAttribute", "getAnonymousNodes", "addBinding", "removeBinding", "getBindingParent", "getBoxObjectFor", "setBoxObjectFor", "getElementById", "getElementsByTagName", "getElementsByTagNameNS", "hasAttributes", "hasChildNodes", "importNode", "insertBefore", "isDefaultNamespace", "isEqualNode", "isSameNode", "isSupported", "load", "loadBindingDocument", "lookupNamespaceURI", "lookupPrefix", "normalize", "normalizeDocument", "getFeature", "getUserData", "setUserData" ]); domMemberMap.Element = extendArray(domMemberMap.Node, [ "clientWidth", "clientHeight", "offsetLeft", "offsetTop", "offsetWidth", "offsetHeight", "scrollLeft", "scrollTop", "scrollWidth", "scrollHeight", "style", "tabIndex", "title", "lang", "align", "spellcheck", "addEventListener", "removeEventListener", "dispatchEvent", "focus", "blur", "cloneNode", "appendChild", "insertBefore", "replaceChild", "removeChild", "compareDocumentPosition", "getElementsByTagName", "getElementsByTagNameNS", "getAttribute", "getAttributeNS", "getAttributeNode", "getAttributeNodeNS", "setAttribute", "setAttributeNS", "setAttributeNode", "setAttributeNodeNS", "removeAttribute", "removeAttributeNS", "removeAttributeNode", "hasAttribute", "hasAttributeNS", "hasAttributes", "hasChildNodes", "lookupNamespaceURI", "lookupPrefix", "normalize", "isDefaultNamespace", "isEqualNode", "isSameNode", "isSupported", "getFeature", "getUserData", "setUserData" ]); domMemberMap.SVGElement = extendArray(domMemberMap.Element, [ "x", "y", "width", "height", "rx", "ry", "transform", "href", "ownerSVGElement", "viewportElement", "farthestViewportElement", "nearestViewportElement", "getBBox", "getCTM", "getScreenCTM", "getTransformToElement", "getPresentationAttribute", "preserveAspectRatio" ]); domMemberMap.SVGSVGElement = extendArray(domMemberMap.Element, [ "x", "y", "width", "height", "rx", "ry", "transform", "viewBox", "viewport", "currentView", "useCurrentView", "pixelUnitToMillimeterX", "pixelUnitToMillimeterY", "screenPixelToMillimeterX", "screenPixelToMillimeterY", "currentScale", "currentTranslate", "zoomAndPan", "ownerSVGElement", "viewportElement", "farthestViewportElement", "nearestViewportElement", "contentScriptType", "contentStyleType", "getBBox", "getCTM", "getScreenCTM", "getTransformToElement", "getEnclosureList", "getIntersectionList", "getViewboxToViewportTransform", "getPresentationAttribute", "getElementById", "checkEnclosure", "checkIntersection", "createSVGAngle", "createSVGLength", "createSVGMatrix", "createSVGNumber", "createSVGPoint", "createSVGRect", "createSVGString", "createSVGTransform", "createSVGTransformFromMatrix", "deSelectAll", "preserveAspectRatio", "forceRedraw", "suspendRedraw", "unsuspendRedraw", "unsuspendRedrawAll", "getCurrentTime", "setCurrentTime", "animationsPaused", "pauseAnimations", "unpauseAnimations" ]); domMemberMap.HTMLImageElement = extendArray(domMemberMap.Element, [ "src", "naturalWidth", "naturalHeight", "width", "height", "x", "y", "name", "alt", "longDesc", "lowsrc", "border", "complete", "hspace", "vspace", "isMap", "useMap" ]); domMemberMap.HTMLAnchorElement = extendArray(domMemberMap.Element, [ "name", "target", "accessKey", "href", "protocol", "host", "hostname", "port", "pathname", "search", "hash", "hreflang", "coords", "shape", "text", "type", "rel", "rev", "charset" ]); domMemberMap.HTMLIFrameElement = extendArray(domMemberMap.Element, [ "contentDocument", "contentWindow", "frameBorder", "height", "longDesc", "marginHeight", "marginWidth", "name", "scrolling", "src", "width" ]); domMemberMap.HTMLTableElement = extendArray(domMemberMap.Element, [ "bgColor", "border", "caption", "cellPadding", "cellSpacing", "frame", "rows", "rules", "summary", "tBodies", "tFoot", "tHead", "width", "createCaption", "createTFoot", "createTHead", "deleteCaption", "deleteRow", "deleteTFoot", "deleteTHead", "insertRow" ]); domMemberMap.HTMLTableRowElement = extendArray(domMemberMap.Element, [ "bgColor", "cells", "ch", "chOff", "rowIndex", "sectionRowIndex", "vAlign", "deleteCell", "insertCell" ]); domMemberMap.HTMLTableCellElement = extendArray(domMemberMap.Element, [ "abbr", "axis", "bgColor", "cellIndex", "ch", "chOff", "colSpan", "headers", "height", "noWrap", "rowSpan", "scope", "vAlign", "width" ]); domMemberMap.HTMLScriptElement = extendArray(domMemberMap.Element, [ "src" ]); domMemberMap.HTMLButtonElement = extendArray(domMemberMap.Element, [ "accessKey", "disabled", "form", "name", "type", "value", "click" ]); domMemberMap.HTMLInputElement = extendArray(domMemberMap.Element, [ "type", "value", "checked", "accept", "accessKey", "alt", "controllers", "defaultChecked", "defaultValue", "disabled", "form", "maxLength", "name", "readOnly", "selectionEnd", "selectionStart", "size", "src", "textLength", "useMap", "click", "select", "setSelectionRange" ]); domMemberMap.HTMLFormElement = extendArray(domMemberMap.Element, [ "acceptCharset", "action", "author", "elements", "encoding", "enctype", "entry_id", "length", "method", "name", "post", "target", "text", "url", "reset", "submit" ]); domMemberMap.HTMLBodyElement = extendArray(domMemberMap.Element, [ "aLink", "background", "bgColor", "link", "text", "vLink" ]); domMemberMap.HTMLHtmlElement = extendArray(domMemberMap.Element, [ "version" ]); domMemberMap.Text = extendArray(domMemberMap.Node, [ "data", "length", "appendData", "deleteData", "insertData", "replaceData", "splitText", "substringData" ]); domMemberMap.Attr = extendArray(domMemberMap.Node, [ "name", "value", "specified", "ownerElement" ]); domMemberMap.Event = [ "type", "target", "currentTarget", "originalTarget", "explicitOriginalTarget", "relatedTarget", "rangeParent", "rangeOffset", "view", "keyCode", "charCode", "screenX", "screenY", "clientX", "clientY", "layerX", "layerY", "pageX", "pageY", "detail", "button", "which", "ctrlKey", "shiftKey", "altKey", "metaKey", "eventPhase", "timeStamp", "bubbles", "cancelable", "cancelBubble", "isTrusted", "isChar", "getPreventDefault", "initEvent", "initMouseEvent", "initKeyEvent", "initUIEvent", "preventBubble", "preventCapture", "preventDefault", "stopPropagation" ]; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * this.domConstantMap = { "ELEMENT_NODE": 1, "ATTRIBUTE_NODE": 1, "TEXT_NODE": 1, "CDATA_SECTION_NODE": 1, "ENTITY_REFERENCE_NODE": 1, "ENTITY_NODE": 1, "PROCESSING_INSTRUCTION_NODE": 1, "COMMENT_NODE": 1, "DOCUMENT_NODE": 1, "DOCUMENT_TYPE_NODE": 1, "DOCUMENT_FRAGMENT_NODE": 1, "NOTATION_NODE": 1, "DOCUMENT_POSITION_DISCONNECTED": 1, "DOCUMENT_POSITION_PRECEDING": 1, "DOCUMENT_POSITION_FOLLOWING": 1, "DOCUMENT_POSITION_CONTAINS": 1, "DOCUMENT_POSITION_CONTAINED_BY": 1, "DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC": 1, "UNKNOWN_RULE": 1, "STYLE_RULE": 1, "CHARSET_RULE": 1, "IMPORT_RULE": 1, "MEDIA_RULE": 1, "FONT_FACE_RULE": 1, "PAGE_RULE": 1, "CAPTURING_PHASE": 1, "AT_TARGET": 1, "BUBBLING_PHASE": 1, "SCROLL_PAGE_UP": 1, "SCROLL_PAGE_DOWN": 1, "MOUSEUP": 1, "MOUSEDOWN": 1, "MOUSEOVER": 1, "MOUSEOUT": 1, "MOUSEMOVE": 1, "MOUSEDRAG": 1, "CLICK": 1, "DBLCLICK": 1, "KEYDOWN": 1, "KEYUP": 1, "KEYPRESS": 1, "DRAGDROP": 1, "FOCUS": 1, "BLUR": 1, "SELECT": 1, "CHANGE": 1, "RESET": 1, "SUBMIT": 1, "SCROLL": 1, "LOAD": 1, "UNLOAD": 1, "XFER_DONE": 1, "ABORT": 1, "ERROR": 1, "LOCATE": 1, "MOVE": 1, "RESIZE": 1, "FORWARD": 1, "HELP": 1, "BACK": 1, "TEXT": 1, "ALT_MASK": 1, "CONTROL_MASK": 1, "SHIFT_MASK": 1, "META_MASK": 1, "DOM_VK_TAB": 1, "DOM_VK_PAGE_UP": 1, "DOM_VK_PAGE_DOWN": 1, "DOM_VK_UP": 1, "DOM_VK_DOWN": 1, "DOM_VK_LEFT": 1, "DOM_VK_RIGHT": 1, "DOM_VK_CANCEL": 1, "DOM_VK_HELP": 1, "DOM_VK_BACK_SPACE": 1, "DOM_VK_CLEAR": 1, "DOM_VK_RETURN": 1, "DOM_VK_ENTER": 1, "DOM_VK_SHIFT": 1, "DOM_VK_CONTROL": 1, "DOM_VK_ALT": 1, "DOM_VK_PAUSE": 1, "DOM_VK_CAPS_LOCK": 1, "DOM_VK_ESCAPE": 1, "DOM_VK_SPACE": 1, "DOM_VK_END": 1, "DOM_VK_HOME": 1, "DOM_VK_PRINTSCREEN": 1, "DOM_VK_INSERT": 1, "DOM_VK_DELETE": 1, "DOM_VK_0": 1, "DOM_VK_1": 1, "DOM_VK_2": 1, "DOM_VK_3": 1, "DOM_VK_4": 1, "DOM_VK_5": 1, "DOM_VK_6": 1, "DOM_VK_7": 1, "DOM_VK_8": 1, "DOM_VK_9": 1, "DOM_VK_SEMICOLON": 1, "DOM_VK_EQUALS": 1, "DOM_VK_A": 1, "DOM_VK_B": 1, "DOM_VK_C": 1, "DOM_VK_D": 1, "DOM_VK_E": 1, "DOM_VK_F": 1, "DOM_VK_G": 1, "DOM_VK_H": 1, "DOM_VK_I": 1, "DOM_VK_J": 1, "DOM_VK_K": 1, "DOM_VK_L": 1, "DOM_VK_M": 1, "DOM_VK_N": 1, "DOM_VK_O": 1, "DOM_VK_P": 1, "DOM_VK_Q": 1, "DOM_VK_R": 1, "DOM_VK_S": 1, "DOM_VK_T": 1, "DOM_VK_U": 1, "DOM_VK_V": 1, "DOM_VK_W": 1, "DOM_VK_X": 1, "DOM_VK_Y": 1, "DOM_VK_Z": 1, "DOM_VK_CONTEXT_MENU": 1, "DOM_VK_NUMPAD0": 1, "DOM_VK_NUMPAD1": 1, "DOM_VK_NUMPAD2": 1, "DOM_VK_NUMPAD3": 1, "DOM_VK_NUMPAD4": 1, "DOM_VK_NUMPAD5": 1, "DOM_VK_NUMPAD6": 1, "DOM_VK_NUMPAD7": 1, "DOM_VK_NUMPAD8": 1, "DOM_VK_NUMPAD9": 1, "DOM_VK_MULTIPLY": 1, "DOM_VK_ADD": 1, "DOM_VK_SEPARATOR": 1, "DOM_VK_SUBTRACT": 1, "DOM_VK_DECIMAL": 1, "DOM_VK_DIVIDE": 1, "DOM_VK_F1": 1, "DOM_VK_F2": 1, "DOM_VK_F3": 1, "DOM_VK_F4": 1, "DOM_VK_F5": 1, "DOM_VK_F6": 1, "DOM_VK_F7": 1, "DOM_VK_F8": 1, "DOM_VK_F9": 1, "DOM_VK_F10": 1, "DOM_VK_F11": 1, "DOM_VK_F12": 1, "DOM_VK_F13": 1, "DOM_VK_F14": 1, "DOM_VK_F15": 1, "DOM_VK_F16": 1, "DOM_VK_F17": 1, "DOM_VK_F18": 1, "DOM_VK_F19": 1, "DOM_VK_F20": 1, "DOM_VK_F21": 1, "DOM_VK_F22": 1, "DOM_VK_F23": 1, "DOM_VK_F24": 1, "DOM_VK_NUM_LOCK": 1, "DOM_VK_SCROLL_LOCK": 1, "DOM_VK_COMMA": 1, "DOM_VK_PERIOD": 1, "DOM_VK_SLASH": 1, "DOM_VK_BACK_QUOTE": 1, "DOM_VK_OPEN_BRACKET": 1, "DOM_VK_BACK_SLASH": 1, "DOM_VK_CLOSE_BRACKET": 1, "DOM_VK_QUOTE": 1, "DOM_VK_META": 1, "SVG_ZOOMANDPAN_DISABLE": 1, "SVG_ZOOMANDPAN_MAGNIFY": 1, "SVG_ZOOMANDPAN_UNKNOWN": 1 }; this.cssInfo = { "background": ["bgRepeat", "bgAttachment", "bgPosition", "color", "systemColor", "none"], "background-attachment": ["bgAttachment"], "background-color": ["color", "systemColor"], "background-image": ["none"], "background-position": ["bgPosition"], "background-repeat": ["bgRepeat"], "border": ["borderStyle", "thickness", "color", "systemColor", "none"], "border-top": ["borderStyle", "borderCollapse", "color", "systemColor", "none"], "border-right": ["borderStyle", "borderCollapse", "color", "systemColor", "none"], "border-bottom": ["borderStyle", "borderCollapse", "color", "systemColor", "none"], "border-left": ["borderStyle", "borderCollapse", "color", "systemColor", "none"], "border-collapse": ["borderCollapse"], "border-color": ["color", "systemColor"], "border-top-color": ["color", "systemColor"], "border-right-color": ["color", "systemColor"], "border-bottom-color": ["color", "systemColor"], "border-left-color": ["color", "systemColor"], "border-spacing": [], "border-style": ["borderStyle"], "border-top-style": ["borderStyle"], "border-right-style": ["borderStyle"], "border-bottom-style": ["borderStyle"], "border-left-style": ["borderStyle"], "border-width": ["thickness"], "border-top-width": ["thickness"], "border-right-width": ["thickness"], "border-bottom-width": ["thickness"], "border-left-width": ["thickness"], "bottom": ["auto"], "caption-side": ["captionSide"], "clear": ["clear", "none"], "clip": ["auto"], "color": ["color", "systemColor"], "content": ["content"], "counter-increment": ["none"], "counter-reset": ["none"], "cursor": ["cursor", "none"], "direction": ["direction"], "display": ["display", "none"], "empty-cells": [], "float": ["float", "none"], "font": ["fontStyle", "fontVariant", "fontWeight", "fontFamily"], "font-family": ["fontFamily"], "font-size": ["fontSize"], "font-size-adjust": [], "font-stretch": [], "font-style": ["fontStyle"], "font-variant": ["fontVariant"], "font-weight": ["fontWeight"], "height": ["auto"], "left": ["auto"], "letter-spacing": [], "line-height": [], "list-style": ["listStyleType", "listStylePosition", "none"], "list-style-image": ["none"], "list-style-position": ["listStylePosition"], "list-style-type": ["listStyleType", "none"], "margin": [], "margin-top": [], "margin-right": [], "margin-bottom": [], "margin-left": [], "marker-offset": ["auto"], "min-height": ["none"], "max-height": ["none"], "min-width": ["none"], "max-width": ["none"], "outline": ["borderStyle", "color", "systemColor", "none"], "outline-color": ["color", "systemColor"], "outline-style": ["borderStyle"], "outline-width": [], "overflow": ["overflow", "auto"], "overflow-x": ["overflow", "auto"], "overflow-y": ["overflow", "auto"], "padding": [], "padding-top": [], "padding-right": [], "padding-bottom": [], "padding-left": [], "position": ["position"], "quotes": ["none"], "right": ["auto"], "table-layout": ["tableLayout", "auto"], "text-align": ["textAlign"], "text-decoration": ["textDecoration", "none"], "text-indent": [], "text-shadow": [], "text-transform": ["textTransform", "none"], "top": ["auto"], "unicode-bidi": [], "vertical-align": ["verticalAlign"], "white-space": ["whiteSpace"], "width": ["auto"], "word-spacing": [], "z-index": [], "-moz-appearance": ["mozAppearance"], "-moz-border-radius": [], "-moz-border-radius-bottomleft": [], "-moz-border-radius-bottomright": [], "-moz-border-radius-topleft": [], "-moz-border-radius-topright": [], "-moz-border-top-colors": ["color", "systemColor"], "-moz-border-right-colors": ["color", "systemColor"], "-moz-border-bottom-colors": ["color", "systemColor"], "-moz-border-left-colors": ["color", "systemColor"], "-moz-box-align": ["mozBoxAlign"], "-moz-box-direction": ["mozBoxDirection"], "-moz-box-flex": [], "-moz-box-ordinal-group": [], "-moz-box-orient": ["mozBoxOrient"], "-moz-box-pack": ["mozBoxPack"], "-moz-box-sizing": ["mozBoxSizing"], "-moz-opacity": [], "-moz-user-focus": ["userFocus", "none"], "-moz-user-input": ["userInput"], "-moz-user-modify": [], "-moz-user-select": ["userSelect", "none"], "-moz-background-clip": [], "-moz-background-inline-policy": [], "-moz-background-origin": [], "-moz-binding": [], "-moz-column-count": [], "-moz-column-gap": [], "-moz-column-width": [], "-moz-image-region": [] }; this.inheritedStyleNames = { "border-collapse": 1, "border-spacing": 1, "border-style": 1, "caption-side": 1, "color": 1, "cursor": 1, "direction": 1, "empty-cells": 1, "font": 1, "font-family": 1, "font-size-adjust": 1, "font-size": 1, "font-style": 1, "font-variant": 1, "font-weight": 1, "letter-spacing": 1, "line-height": 1, "list-style": 1, "list-style-image": 1, "list-style-position": 1, "list-style-type": 1, "quotes": 1, "text-align": 1, "text-decoration": 1, "text-indent": 1, "text-shadow": 1, "text-transform": 1, "white-space": 1, "word-spacing": 1 }; this.cssKeywords = { "appearance": [ "button", "button-small", "checkbox", "checkbox-container", "checkbox-small", "dialog", "listbox", "menuitem", "menulist", "menulist-button", "menulist-textfield", "menupopup", "progressbar", "radio", "radio-container", "radio-small", "resizer", "scrollbar", "scrollbarbutton-down", "scrollbarbutton-left", "scrollbarbutton-right", "scrollbarbutton-up", "scrollbartrack-horizontal", "scrollbartrack-vertical", "separator", "statusbar", "tab", "tab-left-edge", "tabpanels", "textfield", "toolbar", "toolbarbutton", "toolbox", "tooltip", "treeheadercell", "treeheadersortarrow", "treeitem", "treetwisty", "treetwistyopen", "treeview", "window" ], "systemColor": [ "ActiveBorder", "ActiveCaption", "AppWorkspace", "Background", "ButtonFace", "ButtonHighlight", "ButtonShadow", "ButtonText", "CaptionText", "GrayText", "Highlight", "HighlightText", "InactiveBorder", "InactiveCaption", "InactiveCaptionText", "InfoBackground", "InfoText", "Menu", "MenuText", "Scrollbar", "ThreeDDarkShadow", "ThreeDFace", "ThreeDHighlight", "ThreeDLightShadow", "ThreeDShadow", "Window", "WindowFrame", "WindowText", "-moz-field", "-moz-fieldtext", "-moz-workspace", "-moz-visitedhyperlinktext", "-moz-use-text-color" ], "color": [ "AliceBlue", "AntiqueWhite", "Aqua", "Aquamarine", "Azure", "Beige", "Bisque", "Black", "BlanchedAlmond", "Blue", "BlueViolet", "Brown", "BurlyWood", "CadetBlue", "Chartreuse", "Chocolate", "Coral", "CornflowerBlue", "Cornsilk", "Crimson", "Cyan", "DarkBlue", "DarkCyan", "DarkGoldenRod", "DarkGray", "DarkGreen", "DarkKhaki", "DarkMagenta", "DarkOliveGreen", "DarkOrange", "DarkOrchid", "DarkRed", "DarkSalmon", "DarkSeaGreen", "DarkSlateBlue", "DarkSlateGray", "DarkTurquoise", "DarkViolet", "DeepPink", "DarkSkyBlue", "DimGray", "DodgerBlue", "Feldspar", "FireBrick", "FloralWhite", "ForestGreen", "Fuchsia", "Gainsboro", "GhostWhite", "Gold", "GoldenRod", "Gray", "Green", "GreenYellow", "HoneyDew", "HotPink", "IndianRed", "Indigo", "Ivory", "Khaki", "Lavender", "LavenderBlush", "LawnGreen", "LemonChiffon", "LightBlue", "LightCoral", "LightCyan", "LightGoldenRodYellow", "LightGrey", "LightGreen", "LightPink", "LightSalmon", "LightSeaGreen", "LightSkyBlue", "LightSlateBlue", "LightSlateGray", "LightSteelBlue", "LightYellow", "Lime", "LimeGreen", "Linen", "Magenta", "Maroon", "MediumAquaMarine", "MediumBlue", "MediumOrchid", "MediumPurple", "MediumSeaGreen", "MediumSlateBlue", "MediumSpringGreen", "MediumTurquoise", "MediumVioletRed", "MidnightBlue", "MintCream", "MistyRose", "Moccasin", "NavajoWhite", "Navy", "OldLace", "Olive", "OliveDrab", "Orange", "OrangeRed", "Orchid", "PaleGoldenRod", "PaleGreen", "PaleTurquoise", "PaleVioletRed", "PapayaWhip", "PeachPuff", "Peru", "Pink", "Plum", "PowderBlue", "Purple", "Red", "RosyBrown", "RoyalBlue", "SaddleBrown", "Salmon", "SandyBrown", "SeaGreen", "SeaShell", "Sienna", "Silver", "SkyBlue", "SlateBlue", "SlateGray", "Snow", "SpringGreen", "SteelBlue", "Tan", "Teal", "Thistle", "Tomato", "Turquoise", "Violet", "VioletRed", "Wheat", "White", "WhiteSmoke", "Yellow", "YellowGreen", "transparent", "invert" ], "auto": [ "auto" ], "none": [ "none" ], "captionSide": [ "top", "bottom", "left", "right" ], "clear": [ "left", "right", "both" ], "cursor": [ "auto", "cell", "context-menu", "crosshair", "default", "help", "pointer", "progress", "move", "e-resize", "all-scroll", "ne-resize", "nw-resize", "n-resize", "se-resize", "sw-resize", "s-resize", "w-resize", "ew-resize", "ns-resize", "nesw-resize", "nwse-resize", "col-resize", "row-resize", "text", "vertical-text", "wait", "alias", "copy", "move", "no-drop", "not-allowed", "-moz-alias", "-moz-cell", "-moz-copy", "-moz-grab", "-moz-grabbing", "-moz-contextmenu", "-moz-zoom-in", "-moz-zoom-out", "-moz-spinning" ], "direction": [ "ltr", "rtl" ], "bgAttachment": [ "scroll", "fixed" ], "bgPosition": [ "top", "center", "bottom", "left", "right" ], "bgRepeat": [ "repeat", "repeat-x", "repeat-y", "no-repeat" ], "borderStyle": [ "hidden", "dotted", "dashed", "solid", "double", "groove", "ridge", "inset", "outset", "-moz-bg-inset", "-moz-bg-outset", "-moz-bg-solid" ], "borderCollapse": [ "collapse", "separate" ], "overflow": [ "visible", "hidden", "scroll", "-moz-scrollbars-horizontal", "-moz-scrollbars-none", "-moz-scrollbars-vertical" ], "listStyleType": [ "disc", "circle", "square", "decimal", "decimal-leading-zero", "lower-roman", "upper-roman", "lower-greek", "lower-alpha", "lower-latin", "upper-alpha", "upper-latin", "hebrew", "armenian", "georgian", "cjk-ideographic", "hiragana", "katakana", "hiragana-iroha", "katakana-iroha", "inherit" ], "listStylePosition": [ "inside", "outside" ], "content": [ "open-quote", "close-quote", "no-open-quote", "no-close-quote", "inherit" ], "fontStyle": [ "normal", "italic", "oblique", "inherit" ], "fontVariant": [ "normal", "small-caps", "inherit" ], "fontWeight": [ "normal", "bold", "bolder", "lighter", "inherit" ], "fontSize": [ "xx-small", "x-small", "small", "medium", "large", "x-large", "xx-large", "smaller", "larger" ], "fontFamily": [ "Arial", "Comic Sans MS", "Georgia", "Tahoma", "Verdana", "Times New Roman", "Trebuchet MS", "Lucida Grande", "Helvetica", "serif", "sans-serif", "cursive", "fantasy", "monospace", "caption", "icon", "menu", "message-box", "small-caption", "status-bar", "inherit" ], "display": [ "block", "inline", "inline-block", "list-item", "marker", "run-in", "compact", "table", "inline-table", "table-row-group", "table-column", "table-column-group", "table-header-group", "table-footer-group", "table-row", "table-cell", "table-caption", "-moz-box", "-moz-compact", "-moz-deck", "-moz-grid", "-moz-grid-group", "-moz-grid-line", "-moz-groupbox", "-moz-inline-block", "-moz-inline-box", "-moz-inline-grid", "-moz-inline-stack", "-moz-inline-table", "-moz-marker", "-moz-popup", "-moz-runin", "-moz-stack" ], "position": [ "static", "relative", "absolute", "fixed", "inherit" ], "float": [ "left", "right" ], "textAlign": [ "left", "right", "center", "justify" ], "tableLayout": [ "fixed" ], "textDecoration": [ "underline", "overline", "line-through", "blink" ], "textTransform": [ "capitalize", "lowercase", "uppercase", "inherit" ], "unicodeBidi": [ "normal", "embed", "bidi-override" ], "whiteSpace": [ "normal", "pre", "nowrap" ], "verticalAlign": [ "baseline", "sub", "super", "top", "text-top", "middle", "bottom", "text-bottom", "inherit" ], "thickness": [ "thin", "medium", "thick" ], "userFocus": [ "ignore", "normal" ], "userInput": [ "disabled", "enabled" ], "userSelect": [ "normal" ], "mozBoxSizing": [ "content-box", "padding-box", "border-box" ], "mozBoxAlign": [ "start", "center", "end", "baseline", "stretch" ], "mozBoxDirection": [ "normal", "reverse" ], "mozBoxOrient": [ "horizontal", "vertical" ], "mozBoxPack": [ "start", "center", "end" ] }; this.nonEditableTags = { "HTML": 1, "HEAD": 1, "html": 1, "head": 1 }; this.innerEditableTags = { "BODY": 1, "body": 1 }; this.selfClosingTags = { // End tags for void elements are forbidden http://wiki.whatwg.org/wiki/HTML_vs._XHTML "meta": 1, "link": 1, "area": 1, "base": 1, "col": 1, "input": 1, "img": 1, "br": 1, "hr": 1, "param":1, "embed":1 }; var invisibleTags = this.invisibleTags = { "HTML": 1, "HEAD": 1, "TITLE": 1, "META": 1, "LINK": 1, "STYLE": 1, "SCRIPT": 1, "NOSCRIPT": 1, "BR": 1, "PARAM": 1, "COL": 1, "html": 1, "head": 1, "title": 1, "meta": 1, "link": 1, "style": 1, "script": 1, "noscript": 1, "br": 1, "param": 1, "col": 1 /* "window": 1, "browser": 1, "frame": 1, "tabbrowser": 1, "WINDOW": 1, "BROWSER": 1, "FRAME": 1, "TABBROWSER": 1, */ }; if (typeof KeyEvent == "undefined") { this.KeyEvent = { DOM_VK_CANCEL: 3, DOM_VK_HELP: 6, DOM_VK_BACK_SPACE: 8, DOM_VK_TAB: 9, DOM_VK_CLEAR: 12, DOM_VK_RETURN: 13, DOM_VK_ENTER: 14, DOM_VK_SHIFT: 16, DOM_VK_CONTROL: 17, DOM_VK_ALT: 18, DOM_VK_PAUSE: 19, DOM_VK_CAPS_LOCK: 20, DOM_VK_ESCAPE: 27, DOM_VK_SPACE: 32, DOM_VK_PAGE_UP: 33, DOM_VK_PAGE_DOWN: 34, DOM_VK_END: 35, DOM_VK_HOME: 36, DOM_VK_LEFT: 37, DOM_VK_UP: 38, DOM_VK_RIGHT: 39, DOM_VK_DOWN: 40, DOM_VK_PRINTSCREEN: 44, DOM_VK_INSERT: 45, DOM_VK_DELETE: 46, DOM_VK_0: 48, DOM_VK_1: 49, DOM_VK_2: 50, DOM_VK_3: 51, DOM_VK_4: 52, DOM_VK_5: 53, DOM_VK_6: 54, DOM_VK_7: 55, DOM_VK_8: 56, DOM_VK_9: 57, DOM_VK_SEMICOLON: 59, DOM_VK_EQUALS: 61, DOM_VK_A: 65, DOM_VK_B: 66, DOM_VK_C: 67, DOM_VK_D: 68, DOM_VK_E: 69, DOM_VK_F: 70, DOM_VK_G: 71, DOM_VK_H: 72, DOM_VK_I: 73, DOM_VK_J: 74, DOM_VK_K: 75, DOM_VK_L: 76, DOM_VK_M: 77, DOM_VK_N: 78, DOM_VK_O: 79, DOM_VK_P: 80, DOM_VK_Q: 81, DOM_VK_R: 82, DOM_VK_S: 83, DOM_VK_T: 84, DOM_VK_U: 85, DOM_VK_V: 86, DOM_VK_W: 87, DOM_VK_X: 88, DOM_VK_Y: 89, DOM_VK_Z: 90, DOM_VK_CONTEXT_MENU: 93, DOM_VK_NUMPAD0: 96, DOM_VK_NUMPAD1: 97, DOM_VK_NUMPAD2: 98, DOM_VK_NUMPAD3: 99, DOM_VK_NUMPAD4: 100, DOM_VK_NUMPAD5: 101, DOM_VK_NUMPAD6: 102, DOM_VK_NUMPAD7: 103, DOM_VK_NUMPAD8: 104, DOM_VK_NUMPAD9: 105, DOM_VK_MULTIPLY: 106, DOM_VK_ADD: 107, DOM_VK_SEPARATOR: 108, DOM_VK_SUBTRACT: 109, DOM_VK_DECIMAL: 110, DOM_VK_DIVIDE: 111, DOM_VK_F1: 112, DOM_VK_F2: 113, DOM_VK_F3: 114, DOM_VK_F4: 115, DOM_VK_F5: 116, DOM_VK_F6: 117, DOM_VK_F7: 118, DOM_VK_F8: 119, DOM_VK_F9: 120, DOM_VK_F10: 121, DOM_VK_F11: 122, DOM_VK_F12: 123, DOM_VK_F13: 124, DOM_VK_F14: 125, DOM_VK_F15: 126, DOM_VK_F16: 127, DOM_VK_F17: 128, DOM_VK_F18: 129, DOM_VK_F19: 130, DOM_VK_F20: 131, DOM_VK_F21: 132, DOM_VK_F22: 133, DOM_VK_F23: 134, DOM_VK_F24: 135, DOM_VK_NUM_LOCK: 144, DOM_VK_SCROLL_LOCK: 145, DOM_VK_COMMA: 188, DOM_VK_PERIOD: 190, DOM_VK_SLASH: 191, DOM_VK_BACK_QUOTE: 192, DOM_VK_OPEN_BRACKET: 219, DOM_VK_BACK_SLASH: 220, DOM_VK_CLOSE_BRACKET: 221, DOM_VK_QUOTE: 222, DOM_VK_META: 224 }; } // ************************************************************************************************ // Ajax /** * @namespace */ this.Ajax = { requests: [], transport: null, states: ["Uninitialized","Loading","Loaded","Interactive","Complete"], initialize: function() { this.transport = this.getXHRObject(); }, getXHRObject: function() { var xhrObj = false; try { xhrObj = new XMLHttpRequest(); } catch(e) { var progid = [ "MSXML2.XMLHTTP.5.0", "MSXML2.XMLHTTP.4.0", "MSXML2.XMLHTTP.3.0", "MSXML2.XMLHTTP", "Microsoft.XMLHTTP" ]; for ( var i=0; i < progid.length; ++i ) { try { xhrObj = new ActiveXObject(progid[i]); } catch(e) { continue; } break; } } finally { return xhrObj; } }, /** * Create a AJAX request. * * @name request * @param {Object} options request options * @param {String} options.url URL to be requested * @param {String} options.type Request type ("get" ou "post"). Default is "get". * @param {Boolean} options.async Asynchronous flag. Default is "true". * @param {String} options.dataType Data type ("text", "html", "xml" or "json"). Default is "text". * @param {String} options.contentType Content-type of the data being sent. Default is "application/x-www-form-urlencoded". * @param {Function} options.onLoading onLoading callback * @param {Function} options.onLoaded onLoaded callback * @param {Function} options.onInteractive onInteractive callback * @param {Function} options.onComplete onComplete callback * @param {Function} options.onUpdate onUpdate callback * @param {Function} options.onSuccess onSuccess callback * @param {Function} options.onFailure onFailure callback */ request: function(options) { // process options var o = FBL.extend( { // default values type: "get", async: true, dataType: "text", contentType: "application/x-www-form-urlencoded" }, options || {} ); this.requests.push(o); var s = this.getState(); if (s == "Uninitialized" || s == "Complete" || s == "Loaded") this.sendRequest(); }, serialize: function(data) { var r = [""], rl = 0; if (data) { if (typeof data == "string") r[rl++] = data; else if (data.innerHTML && data.elements) { for (var i=0,el,l=(el=data.elements).length; i < l; i++) if (el[i].name) { r[rl++] = encodeURIComponent(el[i].name); r[rl++] = "="; r[rl++] = encodeURIComponent(el[i].value); r[rl++] = "&"; } } else for(var param in data) { r[rl++] = encodeURIComponent(param); r[rl++] = "="; r[rl++] = encodeURIComponent(data[param]); r[rl++] = "&"; } } return r.join("").replace(/&$/, ""); }, sendRequest: function() { var t = FBL.Ajax.transport, r = FBL.Ajax.requests.shift(), data; // open XHR object t.open(r.type, r.url, r.async); //setRequestHeaders(); // indicates that it is a XHR request to the server t.setRequestHeader("X-Requested-With", "XMLHttpRequest"); // if data is being sent, sets the appropriate content-type if (data = FBL.Ajax.serialize(r.data)) t.setRequestHeader("Content-Type", r.contentType); /** @ignore */ // onreadystatechange handler t.onreadystatechange = function() { FBL.Ajax.onStateChange(r); }; // send the request t.send(data); }, /** * Handles the state change */ onStateChange: function(options) { var fn, o = options, t = this.transport; var state = this.getState(t); if (fn = o["on" + state]) fn(this.getResponse(o), o); if (state == "Complete") { var success = t.status == 200, response = this.getResponse(o); if (fn = o["onUpdate"]) fn(response, o); if (fn = o["on" + (success ? "Success" : "Failure")]) fn(response, o); t.onreadystatechange = FBL.emptyFn; if (this.requests.length > 0) setTimeout(this.sendRequest, 10); } }, /** * gets the appropriate response value according the type */ getResponse: function(options) { var t = this.transport, type = options.dataType; if (t.status != 200) return t.statusText; else if (type == "text") return t.responseText; else if (type == "html") return t.responseText; else if (type == "xml") return t.responseXML; else if (type == "json") return eval("(" + t.responseText + ")"); }, /** * returns the current state of the XHR object */ getState: function() { return this.states[this.transport.readyState]; } }; // ************************************************************************************************ // Cookie, from http://www.quirksmode.org/js/cookies.html this.createCookie = function(name,value,days) { if ('cookie' in document) { if (days) { var date = new Date(); date.setTime(date.getTime()+(days*24*60*60*1000)); var expires = "; expires="+date.toGMTString(); } else var expires = ""; document.cookie = name+"="+value+expires+"; path=/"; } }; this.readCookie = function (name) { if ('cookie' in document) { var nameEQ = name + "="; var ca = document.cookie.split(';'); for(var i=0; i < ca.length; i++) { var c = ca[i]; while (c.charAt(0)==' ') c = c.substring(1,c.length); if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length); } } return null; }; this.removeCookie = function(name) { this.createCookie(name, "", -1); }; // ************************************************************************************************ // http://www.mister-pixel.com/#Content__state=is_that_simple var fixIE6BackgroundImageCache = function(doc) { doc = doc || document; try { doc.execCommand("BackgroundImageCache", false, true); } catch(E) { } }; // ************************************************************************************************ // calculatePixelsPerInch var resetStyle = "margin:0; padding:0; border:0; position:absolute; overflow:hidden; display:block;"; var calculatePixelsPerInch = function calculatePixelsPerInch(doc, body) { var inch = FBL.createGlobalElement("div"); inch.style.cssText = resetStyle + "width:1in; height:1in; position:absolute; top:-1234px; left:-1234px;"; body.appendChild(inch); FBL.pixelsPerInch = { x: inch.offsetWidth, y: inch.offsetHeight }; body.removeChild(inch); }; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * this.SourceLink = function(url, line, type, object, instance) { this.href = url; this.instance = instance; this.line = line; this.type = type; this.object = object; }; this.SourceLink.prototype = { toString: function() { return this.href; }, toJSON: function() // until 3.1... { return "{\"href\":\""+this.href+"\", "+ (this.line?("\"line\":"+this.line+","):"")+ (this.type?(" \"type\":\""+this.type+"\","):"")+ "}"; } }; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * this.SourceText = function(lines, owner) { this.lines = lines; this.owner = owner; }; this.SourceText.getLineAsHTML = function(lineNo) { return escapeForSourceLine(this.lines[lineNo-1]); }; // ************************************************************************************************ }).apply(FBL); /* See license.txt for terms of usage */ FBL.ns( /** @scope s_i18n */ function() { with (FBL) { // ************************************************************************************************ // TODO: xxxpedro localization var oSTR = { "NoMembersWarning": "There are no properties to show for this object.", "EmptyStyleSheet": "There are no rules in this stylesheet.", "EmptyElementCSS": "This element has no style rules.", "AccessRestricted": "Access to restricted URI denied.", "net.label.Parameters": "Parameters", "net.label.Source": "Source", "URLParameters": "Params", "EditStyle": "Edit Element Style...", "NewRule": "New Rule...", "NewProp": "New Property...", "EditProp": 'Edit "%s"', "DeleteProp": 'Delete "%s"', "DisableProp": 'Disable "%s"' }; // ************************************************************************************************ FBL.$STR = function(name) { return oSTR.hasOwnProperty(name) ? oSTR[name] : name; }; FBL.$STRF = function(name, args) { if (!oSTR.hasOwnProperty(name)) return name; var format = oSTR[name]; var objIndex = 0; var parts = parseFormat(format); var trialIndex = objIndex; var objects = args; for (var i= 0; i < parts.length; i++) { var part = parts[i]; if (part && typeof(part) == "object") { if (++trialIndex > objects.length) // then too few parameters for format, assume unformatted. { format = ""; objIndex = -1; parts.length = 0; break; } } } var result = []; for (var i = 0; i < parts.length; ++i) { var part = parts[i]; if (part && typeof(part) == "object") { result.push(""+args.shift()); } else result.push(part); } return result.join(""); }; // ************************************************************************************************ var parseFormat = function parseFormat(format) { var parts = []; if (format.length <= 0) return parts; var reg = /((^%|.%)(\d+)?(\.)([a-zA-Z]))|((^%|.%)([a-zA-Z]))/; for (var m = reg.exec(format); m; m = reg.exec(format)) { if (m[0].substr(0, 2) == "%%") { parts.push(format.substr(0, m.index)); parts.push(m[0].substr(1)); } else { var type = m[8] ? m[8] : m[5]; var precision = m[3] ? parseInt(m[3]) : (m[4] == "." ? -1 : 0); var rep = null; switch (type) { case "s": rep = FirebugReps.Text; break; case "f": case "i": case "d": rep = FirebugReps.Number; break; case "o": rep = null; break; } parts.push(format.substr(0, m[0][0] == "%" ? m.index : m.index+1)); parts.push({rep: rep, precision: precision, type: ("%" + type)}); } format = format.substr(m.index+m[0].length); } parts.push(format); return parts; }; // ************************************************************************************************ }}); /* See license.txt for terms of usage */ FBL.ns( /** @scope s_firebug */ function() { with (FBL) { // ************************************************************************************************ // ************************************************************************************************ // Globals // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Internals var modules = []; var panelTypes = []; var panelTypeMap = {}; var reps = []; var parentPanelMap = {}; // ************************************************************************************************ // Firebug /** * @namespace describe Firebug * @exports FBL.Firebug as Firebug */ FBL.Firebug = { // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * version: "Firebug Lite 1.4.0a1", revision: "$Revision$", // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * modules: modules, panelTypes: panelTypes, panelTypeMap: panelTypeMap, reps: reps, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Initialization initialize: function() { if (FBTrace.DBG_INITIALIZE) FBTrace.sysout("Firebug.initialize", "initializing application"); Firebug.browser = new Context(Env.browser); Firebug.context = Firebug.browser; // Document must be cached before chrome initialization cacheDocument(); if (Firebug.Inspector && Firebug.Inspector.create) Firebug.Inspector.create(); if (FBL.processAllStyleSheets) processAllStyleSheets(Firebug.browser.document); FirebugChrome.initialize(); dispatch(modules, "initialize", []); if (Env.onLoad) { var onLoad = Env.onLoad; delete Env.onLoad; setTimeout(onLoad, 200); } }, shutdown: function() { if (Firebug.Inspector) Firebug.Inspector.destroy(); dispatch(modules, "shutdown", []); var chromeMap = FirebugChrome.chromeMap; for (var name in chromeMap) { if (chromeMap.hasOwnProperty(name)) { try { chromeMap[name].destroy(); } catch(E) { if (FBTrace.DBG_ERRORS) FBTrace.sysout("chrome.destroy() failed to: " + name); } } } Firebug.Lite.Cache.Element.clear(); Firebug.Lite.Cache.StyleSheet.clear(); Firebug.browser = null; Firebug.context = null; }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Registration registerModule: function() { modules.push.apply(modules, arguments); if (FBTrace.DBG_INITIALIZE) FBTrace.sysout("Firebug.registerModule"); }, registerPanel: function() { panelTypes.push.apply(panelTypes, arguments); for (var i = 0, panelType; panelType = arguments[i]; ++i) { panelTypeMap[panelType.prototype.name] = arguments[i]; if (panelType.prototype.parentPanel) parentPanelMap[panelType.prototype.parentPanel] = 1; } if (FBTrace.DBG_INITIALIZE) for (var i = 0; i < arguments.length; ++i) FBTrace.sysout("Firebug.registerPanel", arguments[i].prototype.name); }, registerRep: function() { reps.push.apply(reps, arguments); }, unregisterRep: function() { for (var i = 0; i < arguments.length; ++i) remove(reps, arguments[i]); }, setDefaultReps: function(funcRep, rep) { FBL.defaultRep = rep; FBL.defaultFuncRep = funcRep; }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Reps getRep: function(object) { var type = typeof object; if (isIE && isFunction(object)) type = "function"; for (var i = 0; i < reps.length; ++i) { var rep = reps[i]; try { if (rep.supportsObject(object, type)) { if (FBTrace.DBG_DOM) FBTrace.sysout("getRep type: "+type+" object: "+object, rep); return rep; } } catch (exc) { if (FBTrace.DBG_ERRORS) { FBTrace.sysout("firebug.getRep FAILS: ", exc.message || exc); FBTrace.sysout("firebug.getRep reps["+i+"/"+reps.length+"]: Rep="+reps[i].className); // TODO: xxxpedro add trace to FBTrace logs like in Firebug //firebug.trace(); } } } return (type == 'function') ? defaultFuncRep : defaultRep; }, getRepObject: function(node) { var target = null; for (var child = node; child; child = child.parentNode) { if (hasClass(child, "repTarget")) target = child; if (child.repObject) { if (!target && hasClass(child, "repIgnore")) break; else return child.repObject; } } }, getRepNode: function(node) { for (var child = node; child; child = child.parentNode) { if (child.repObject) return child; } }, getElementByRepObject: function(element, object) { for (var child = element.firstChild; child; child = child.nextSibling) { if (child.repObject == object) return child; } }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Preferences getPref: function(name) { return Firebug[name]; }, setPref: function(name, value) { Firebug[name] = value; this.savePrefs(); }, setPrefs: function(prefs) { for (var name in prefs) { if (prefs.hasOwnProperty(name)) Firebug[name] = prefs[name]; } this.savePrefs(); }, restorePrefs: function() { var Options = Env.Options; for (var name in Options) { Firebug[name] = Options[name]; } }, loadPrefs: function(prefs) { this.restorePrefs(); prefs = prefs || eval("(" + readCookie("FirebugLite") + ")"); for (var name in prefs) { if (prefs.hasOwnProperty(name)) Firebug[name] = prefs[name]; } }, savePrefs: function() { var json = ['{'], jl = 0; var Options = Env.Options; for (var name in Options) { if (Options.hasOwnProperty(name)) { var value = Firebug[name]; json[++jl] = '"'; json[++jl] = name; var type = typeof value; if (type == "boolean" || type == "number") { json[++jl] = '":'; json[++jl] = value; json[++jl] = ','; } else { json[++jl] = '":"'; json[++jl] = value; json[++jl] = '",'; } } } json.length = jl--; json[++jl] = '}'; createCookie("FirebugLite", json.join("")); }, erasePrefs: function() { removeCookie("FirebugLite"); } }; Firebug.restorePrefs(); // xxxpedro should we remove this? window.Firebug = FBL.Firebug; if (!Env.Options.enablePersistent || Env.Options.enablePersistent && Env.isChromeContext || Env.isDebugMode) Env.browser.window.Firebug = FBL.Firebug; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Other methods FBL.cacheDocument = function cacheDocument() { var ElementCache = Firebug.Lite.Cache.Element; var els = Firebug.browser.document.getElementsByTagName("*"); for (var i=0, l=els.length, el; iFirebug.registerModule method. There is always one instance of a module object * per browser window. * @extends Firebug.Listener */ Firebug.Module = extend(new Firebug.Listener(), /** @extend Firebug.Module */ { /** * Called when the window is opened. */ initialize: function() { }, /** * Called when the window is closed. */ shutdown: function() { }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /** * Called when a new context is created but before the page is loaded. */ initContext: function(context) { }, /** * Called after a context is detached to a separate window; */ reattachContext: function(browser, context) { }, /** * Called when a context is destroyed. Module may store info on persistedState for reloaded pages. */ destroyContext: function(context, persistedState) { }, // Called when a FF tab is create or activated (user changes FF tab) // Called after context is created or with context == null (to abort?) showContext: function(browser, context) { }, /** * Called after a context's page gets DOMContentLoaded */ loadedContext: function(context) { }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * showPanel: function(browser, panel) { }, showSidePanel: function(browser, panel) { }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * updateOption: function(name, value) { }, getObjectByURL: function(context, url) { } }); // ************************************************************************************************ // Panel /** * @panel Base class for all panels. Every derived panel must define a constructor and * register with "Firebug.registerPanel" method. An instance of the panel * object is created by the framework for each browser tab where Firebug is activated. */ Firebug.Panel = { name: "HelloWorld", title: "Hello World!", parentPanel: null, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * options: { hasCommandLine: false, hasStatusBar: false, hasToolButtons: false, // Pre-rendered panels are those included in the skin file (firebug.html) isPreRendered: false, innerHTMLSync: false /* // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // To be used by external extensions panelHTML: "", panelCSS: "", toolButtonsHTML: "" /**/ }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * tabNode: null, panelNode: null, sidePanelNode: null, statusBarNode: null, toolButtonsNode: null, panelBarNode: null, sidePanelBarBoxNode: null, sidePanelBarNode: null, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * sidePanelBar: null, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * searchable: false, editable: true, order: 2147483647, statusSeparator: "<", create: function(context, doc) { this.hasSidePanel = parentPanelMap.hasOwnProperty(this.name); this.panelBarNode = $("fbPanelBar1"); this.sidePanelBarBoxNode = $("fbPanelBar2"); if (this.hasSidePanel) { this.sidePanelBar = extend({}, PanelBar); this.sidePanelBar.create(this); } var options = this.options = extend(Firebug.Panel.options, this.options); var panelId = "fb" + this.name; if (options.isPreRendered) { this.panelNode = $(panelId); this.tabNode = $(panelId + "Tab"); this.tabNode.style.display = "block"; if (options.hasToolButtons) { this.toolButtonsNode = $(panelId + "Buttons"); } if (options.hasStatusBar) { this.statusBarBox = $("fbStatusBarBox"); this.statusBarNode = $(panelId + "StatusBar"); } } else { var containerSufix = this.parentPanel ? "2" : "1"; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Create Panel var panelNode = this.panelNode = createElement("div", { id: panelId, className: "fbPanel" }); $("fbPanel" + containerSufix).appendChild(panelNode); // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Create Panel Tab var tabHTML = '' + this.title + ''; var tabNode = this.tabNode = createElement("a", { id: panelId + "Tab", className: "fbTab fbHover", innerHTML: tabHTML }); if (isIE6) { tabNode.href = "javascript:void(0)"; } var panelBarNode = this.parentPanel ? Firebug.chrome.getPanel(this.parentPanel).sidePanelBarNode : this.panelBarNode; panelBarNode.appendChild(tabNode); tabNode.style.display = "block"; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // create ToolButtons if (options.hasToolButtons) { this.toolButtonsNode = createElement("span", { id: panelId + "Buttons", className: "fbToolbarButtons" }); $("fbToolbarButtons").appendChild(this.toolButtonsNode); } // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // create StatusBar if (options.hasStatusBar) { this.statusBarBox = $("fbStatusBarBox"); this.statusBarNode = createElement("span", { id: panelId + "StatusBar", className: "fbToolbarButtons fbStatusBar" }); this.statusBarBox.appendChild(this.statusBarNode); } // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // create SidePanel } this.containerNode = this.panelNode.parentNode; if (FBTrace.DBG_INITIALIZE) FBTrace.sysout("Firebug.Panel.create", this.name); // xxxpedro contextMenu this.onContextMenu = bind(this.onContextMenu, this); /* this.context = context; this.document = doc; this.panelNode = doc.createElement("div"); this.panelNode.ownerPanel = this; setClass(this.panelNode, "panelNode panelNode-"+this.name+" contextUID="+context.uid); doc.body.appendChild(this.panelNode); if (FBTrace.DBG_INITIALIZE) FBTrace.sysout("firebug.initialize panelNode for "+this.name+"\n"); this.initializeNode(this.panelNode); /**/ }, destroy: function(state) // Panel may store info on state { if (FBTrace.DBG_INITIALIZE) FBTrace.sysout("Firebug.Panel.destroy", this.name); if (this.hasSidePanel) { this.sidePanelBar.destroy(); this.sidePanelBar = null; } this.options = null; this.name = null; this.parentPanel = null; this.tabNode = null; this.panelNode = null; this.containerNode = null; this.toolButtonsNode = null; this.statusBarBox = null; this.statusBarNode = null; //if (this.panelNode) // delete this.panelNode.ownerPanel; //this.destroyNode(); }, initialize: function() { if (FBTrace.DBG_INITIALIZE) FBTrace.sysout("Firebug.Panel.initialize", this.name); // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * if (this.hasSidePanel) { this.sidePanelBar.initialize(); } var options = this.options = extend(Firebug.Panel.options, this.options); var panelId = "fb" + this.name; this.panelNode = $(panelId); this.tabNode = $(panelId + "Tab"); this.tabNode.style.display = "block"; if (options.hasStatusBar) { this.statusBarBox = $("fbStatusBarBox"); this.statusBarNode = $(panelId + "StatusBar"); } if (options.hasToolButtons) { this.toolButtonsNode = $(panelId + "Buttons"); } this.containerNode = this.panelNode.parentNode; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // restore persistent state this.containerNode.scrollTop = this.lastScrollTop; // xxxpedro contextMenu addEvent(this.containerNode, "contextmenu", this.onContextMenu); /// TODO: xxxpedro infoTip Hack Firebug.chrome.currentPanel = Firebug.chrome.selectedPanel && Firebug.chrome.selectedPanel.sidePanelBar ? Firebug.chrome.selectedPanel.sidePanelBar.selectedPanel : Firebug.chrome.selectedPanel; Firebug.showInfoTips = true; Firebug.InfoTip.initializeBrowser(Firebug.chrome); }, shutdown: function() { if (FBTrace.DBG_INITIALIZE) FBTrace.sysout("Firebug.Panel.shutdown", this.name); /// TODO: xxxpedro infoTip Hack Firebug.InfoTip.uninitializeBrowser(Firebug.chrome); if (Firebug.chrome.largeCommandLineVisible) Firebug.chrome.hideLargeCommandLine(); // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * if (this.hasSidePanel) { // TODO: xxxpedro firebug1.3a6 // new PanelBar mechanism will need to call shutdown to hide the panels (so it // doesn't appears in other panel's sidePanelBar. Therefore, we need to implement // a "remember selected panel" feature in the sidePanelBar //this.sidePanelBar.shutdown(); } // store persistent state this.lastScrollTop = this.containerNode.scrollTop; // xxxpedro contextMenu removeEvent(this.containerNode, "contextmenu", this.onContextMenu); }, detach: function(oldChrome, newChrome) { if (oldChrome.selectedPanel.name == this.name) this.lastScrollTop = oldChrome.selectedPanel.containerNode.scrollTop; }, reattach: function(doc) { if (this.options.innerHTMLSync) this.synchronizeUI(); }, synchronizeUI: function() { this.containerNode.scrollTop = this.lastScrollTop || 0; }, show: function(state) { var options = this.options; if (options.hasStatusBar) { this.statusBarBox.style.display = "inline"; this.statusBarNode.style.display = "inline"; } if (options.hasToolButtons) { this.toolButtonsNode.style.display = "inline"; } this.panelNode.style.display = "block"; this.visible = true; if (!this.parentPanel) Firebug.chrome.layout(this); }, hide: function(state) { var options = this.options; if (options.hasStatusBar) { this.statusBarBox.style.display = "none"; this.statusBarNode.style.display = "none"; } if (options.hasToolButtons) { this.toolButtonsNode.style.display = "none"; } this.panelNode.style.display = "none"; this.visible = false; }, watchWindow: function(win) { }, unwatchWindow: function(win) { }, updateOption: function(name, value) { }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /** * Toolbar helpers */ showToolbarButtons: function(buttonsId, show) { try { if (!this.context.browser) // XXXjjb this is bug. Somehow the panel context is not FirebugContext. { if (FBTrace.DBG_ERRORS) FBTrace.sysout("firebug.Panel showToolbarButtons this.context has no browser, this:", this); return; } var buttons = this.context.browser.chrome.$(buttonsId); if (buttons) collapse(buttons, show ? "false" : "true"); } catch (exc) { if (FBTrace.DBG_ERRORS) { FBTrace.dumpProperties("firebug.Panel showToolbarButtons FAILS", exc); if (!this.context.browser)FBTrace.dumpStack("firebug.Panel showToolbarButtons no browser"); } } }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /** * Returns a number indicating the view's ability to inspect the object. * * Zero means not supported, and higher numbers indicate specificity. */ supportsObject: function(object) { return 0; }, hasObject: function(object) // beyond type testing, is this object selectable? { return false; }, select: function(object, forceUpdate) { if (!object) object = this.getDefaultSelection(this.context); if(FBTrace.DBG_PANELS) FBTrace.sysout("firebug.select "+this.name+" forceUpdate: "+forceUpdate+" "+object+((object==this.selection)?"==":"!=")+this.selection); if (forceUpdate || object != this.selection) { this.selection = object; this.updateSelection(object); // TODO: xxxpedro // XXXjoe This is kind of cheating, but, feh. //Firebug.chrome.onPanelSelect(object, this); //if (uiListeners.length > 0) // dispatch(uiListeners, "onPanelSelect", [object, this]); // TODO: make Firebug.chrome a uiListener } }, updateSelection: function(object) { }, markChange: function(skipSelf) { if (this.dependents) { if (skipSelf) { for (var i = 0; i < this.dependents.length; ++i) { var panelName = this.dependents[i]; if (panelName != this.name) this.context.invalidatePanels(panelName); } } else this.context.invalidatePanels.apply(this.context, this.dependents); } }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * startInspecting: function() { }, stopInspecting: function(object, cancelled) { }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * search: function(text, reverse) { }, /** * Retrieves the search options that this modules supports. * This is used by the search UI to present the proper options. */ getSearchOptionsMenuItems: function() { return [ Firebug.Search.searchOptionMenu("search.Case Sensitive", "searchCaseSensitive") ]; }, /** * Navigates to the next document whose match parameter returns true. */ navigateToNextDocument: function(match, reverse) { // This is an approximation of the UI that is displayed by the location // selector. This should be close enough, although it may be better // to simply generate the sorted list within the module, rather than // sorting within the UI. var self = this; function compare(a, b) { var locA = self.getObjectDescription(a); var locB = self.getObjectDescription(b); if(locA.path > locB.path) return 1; if(locA.path < locB.path) return -1; if(locA.name > locB.name) return 1; if(locA.name < locB.name) return -1; return 0; } var allLocs = this.getLocationList().sort(compare); for (var curPos = 0; curPos < allLocs.length && allLocs[curPos] != this.location; curPos++); function transformIndex(index) { if (reverse) { // For the reverse case we need to implement wrap around. var intermediate = curPos - index - 1; return (intermediate < 0 ? allLocs.length : 0) + intermediate; } else { return (curPos + index + 1) % allLocs.length; } }; for (var next = 0; next < allLocs.length - 1; next++) { var object = allLocs[transformIndex(next)]; if (match(object)) { this.navigate(object); return object; } } }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Called when "Options" clicked. Return array of // {label: 'name', nol10n: true, type: "checkbox", checked: , command:function to set } getOptionsMenuItems: function() { return null; }, /* * Called by chrome.onContextMenu to build the context menu when this panel has focus. * See also FirebugRep for a similar function also called by onContextMenu * Extensions may monkey patch and chain off this call * @param object: the 'realObject', a model value, eg a DOM property * @param target: the HTML element clicked on. * @return an array of menu items. */ getContextMenuItems: function(object, target) { return []; }, getBreakOnMenuItems: function() { return []; }, getEditor: function(target, value) { }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * getDefaultSelection: function() { return null; }, browseObject: function(object) { }, getPopupObject: function(target) { return Firebug.getRepObject(target); }, getTooltipObject: function(target) { return Firebug.getRepObject(target); }, showInfoTip: function(infoTip, x, y) { }, getObjectPath: function(object) { return null; }, // An array of objects that can be passed to getObjectLocation. // The list of things a panel can show, eg sourceFiles. // Only shown if panel.location defined and supportsObject true getLocationList: function() { return null; }, getDefaultLocation: function() { return null; }, getObjectLocation: function(object) { return ""; }, // Text for the location list menu eg script panel source file list // return.path: group/category label, return.name: item label getObjectDescription: function(object) { var url = this.getObjectLocation(object); return FBL.splitURLBase(url); }, /* * UI signal that a tab needs attention, eg Script panel is currently stopped on a breakpoint * @param: show boolean, true turns on. */ highlight: function(show) { var tab = this.getTab(); if (!tab) return; if (show) tab.setAttribute("highlight", "true"); else tab.removeAttribute("highlight"); }, getTab: function() { var chrome = Firebug.chrome; var tab = chrome.$("fbPanelBar2").getTab(this.name); if (!tab) tab = chrome.$("fbPanelBar1").getTab(this.name); return tab; }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Support for Break On Next /** * Called by the framework when the user clicks on the Break On Next button. * @param {Boolean} armed Set to true if the Break On Next feature is * to be armed for action and set to false if the Break On Next should be disarmed. * If 'armed' is true, then the next call to shouldBreakOnNext should be |true|. */ breakOnNext: function(armed) { }, /** * Called when a panel is selected/displayed. The method should return true * if the Break On Next feature is currently armed for this panel. */ shouldBreakOnNext: function() { return false; }, /** * Returns labels for Break On Next tooltip (one for enabled and one for disabled state). * @param {Boolean} enabled Set to true if the Break On Next feature is * currently activated for this panel. */ getBreakOnNextTooltip: function(enabled) { return null; }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // xxxpedro contextMenu onContextMenu: function(event) { if (!this.getContextMenuItems) return; cancelEvent(event, true); var target = event.target || event.srcElement; var menu = this.getContextMenuItems(this.selection, target); if (!menu) return; var contextMenu = new Menu( { id: "fbPanelContextMenu", items: menu }); contextMenu.show(event.clientX, event.clientY); return true; /* // TODO: xxxpedro move code to somewhere. code to get cross-browser // window to screen coordinates var box = Firebug.browser.getElementPosition(Firebug.chrome.node); var screenY = 0; // Firefox if (typeof window.mozInnerScreenY != "undefined") { screenY = window.mozInnerScreenY; } // Chrome else if (typeof window.innerHeight != "undefined") { screenY = window.outerHeight - window.innerHeight; } // IE else if (typeof window.screenTop != "undefined") { screenY = window.screenTop; } contextMenu.show(event.screenX-box.left, event.screenY-screenY-box.top); /**/ } // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * }; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /** * MeasureBox * To get pixels size.width and size.height: *
  • this.startMeasuring(view);
  • *
  • var size = this.measureText(lineNoCharsSpacer);
  • *
  • this.stopMeasuring();
  • *
* * @namespace */ Firebug.MeasureBox = { startMeasuring: function(target) { if (!this.measureBox) { this.measureBox = target.ownerDocument.createElement("span"); this.measureBox.className = "measureBox"; } copyTextStyles(target, this.measureBox); target.ownerDocument.body.appendChild(this.measureBox); }, getMeasuringElement: function() { return this.measureBox; }, measureText: function(value) { this.measureBox.innerHTML = value ? escapeForSourceLine(value) : "m"; return {width: this.measureBox.offsetWidth, height: this.measureBox.offsetHeight-1}; }, measureInputText: function(value) { value = value ? escapeForTextNode(value) : "m"; if (!Firebug.showTextNodesWithWhitespace) value = value.replace(/\t/g,'mmmmmm').replace(/\ /g,'m'); this.measureBox.innerHTML = value; return {width: this.measureBox.offsetWidth, height: this.measureBox.offsetHeight-1}; }, getBox: function(target) { var style = this.measureBox.ownerDocument.defaultView.getComputedStyle(this.measureBox, ""); var box = getBoxFromStyles(style, this.measureBox); return box; }, stopMeasuring: function() { this.measureBox.parentNode.removeChild(this.measureBox); } }; // ************************************************************************************************ if (FBL.domplate) Firebug.Rep = domplate( { className: "", inspectable: true, supportsObject: function(object, type) { return false; }, inspectObject: function(object, context) { Firebug.chrome.select(object); }, browseObject: function(object, context) { }, persistObject: function(object, context) { }, getRealObject: function(object, context) { return object; }, getTitle: function(object) { var label = safeToString(object); var re = /\[object (.*?)\]/; var m = re.exec(label); ///return m ? m[1] : label; // if the label is in the "[object TYPE]" format return its type if (m) { return m[1]; } // if it is IE we need to handle some special cases else if ( // safeToString() fails to recognize some objects in IE isIE && // safeToString() returns "[object]" for some objects like window.Image (label == "[object]" || // safeToString() returns undefined for some objects like window.clientInformation typeof object == "object" && typeof label == "undefined") ) { return "Object"; } else { return label; } }, getTooltip: function(object) { return null; }, getContextMenuItems: function(object, target, context) { return []; }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Convenience for domplates STR: function(name) { return $STR(name); }, cropString: function(text) { return cropString(text); }, cropMultipleLines: function(text, limit) { return cropMultipleLines(text, limit); }, toLowerCase: function(text) { return text ? text.toLowerCase() : text; }, plural: function(n) { return n == 1 ? "" : "s"; } }); // ************************************************************************************************ // ************************************************************************************************ }}); /* See license.txt for terms of usage */ FBL.ns( /** @scope s_gui */ function() { with (FBL) { // ************************************************************************************************ // ************************************************************************************************ // Controller /**@namespace*/ FBL.Controller = { controllers: null, controllerContext: null, initialize: function(context) { this.controllers = []; this.controllerContext = context || Firebug.chrome; }, shutdown: function() { this.removeControllers(); //this.controllers = null; //this.controllerContext = null; }, addController: function() { for (var i=0, arg; arg=arguments[i]; i++) { // If the first argument is a string, make a selector query // within the controller node context if (typeof arg[0] == "string") { arg[0] = $$(arg[0], this.controllerContext); } // bind the handler to the proper context var handler = arg[2]; arg[2] = bind(handler, this); // save the original handler as an extra-argument, so we can // look for it later, when removing a particular controller arg[3] = handler; this.controllers.push(arg); addEvent.apply(this, arg); } }, removeController: function() { for (var i=0, arg; arg=arguments[i]; i++) { for (var j=0, c; c=this.controllers[j]; j++) { if (arg[0] == c[0] && arg[1] == c[1] && arg[2] == c[3]) removeEvent.apply(this, c); } } }, removeControllers: function() { for (var i=0, c; c=this.controllers[i]; i++) { removeEvent.apply(this, c); } } }; // ************************************************************************************************ // PanelBar /**@namespace*/ FBL.PanelBar = { // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * panelMap: null, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * selectedPanel: null, parentPanelName: null, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * create: function(ownerPanel) { this.panelMap = {}; this.ownerPanel = ownerPanel; if (ownerPanel) { ownerPanel.sidePanelBarNode = createElement("span"); ownerPanel.sidePanelBarNode.style.display = "none"; ownerPanel.sidePanelBarBoxNode.appendChild(ownerPanel.sidePanelBarNode); } var panels = Firebug.panelTypes; for (var i=0, p; p=panels[i]; i++) { if ( // normal Panel of the Chrome's PanelBar !ownerPanel && !p.prototype.parentPanel || // Child Panel of the current Panel's SidePanelBar ownerPanel && p.prototype.parentPanel && ownerPanel.name == p.prototype.parentPanel) { this.addPanel(p.prototype.name); } } }, destroy: function() { PanelBar.shutdown.call(this); for (var name in this.panelMap) { this.removePanel(name); var panel = this.panelMap[name]; panel.destroy(); this.panelMap[name] = null; delete this.panelMap[name]; } this.panelMap = null; this.ownerPanel = null; }, initialize: function() { if (this.ownerPanel) this.ownerPanel.sidePanelBarNode.style.display = "inline"; for(var name in this.panelMap) { (function(self, name){ // tab click handler var onTabClick = function onTabClick() { self.selectPanel(name); return false; }; Firebug.chrome.addController([self.panelMap[name].tabNode, "mousedown", onTabClick]); })(this, name); } }, shutdown: function() { var selectedPanel = this.selectedPanel; if (selectedPanel) { removeClass(selectedPanel.tabNode, "fbSelectedTab"); selectedPanel.hide(); selectedPanel.shutdown(); } if (this.ownerPanel) this.ownerPanel.sidePanelBarNode.style.display = "none"; this.selectedPanel = null; }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * addPanel: function(panelName, parentPanel) { var PanelType = Firebug.panelTypeMap[panelName]; var panel = this.panelMap[panelName] = new PanelType(); panel.create(); }, removePanel: function(panelName) { var panel = this.panelMap[panelName]; if (panel.hasOwnProperty(panelName)) panel.destroy(); }, selectPanel: function(panelName) { var selectedPanel = this.selectedPanel; var panel = this.panelMap[panelName]; if (panel && selectedPanel != panel) { if (selectedPanel) { removeClass(selectedPanel.tabNode, "fbSelectedTab"); selectedPanel.shutdown(); selectedPanel.hide(); } if (!panel.parentPanel) FirebugChrome.selectedPanelName = panelName; this.selectedPanel = panel; setClass(panel.tabNode, "fbSelectedTab"); panel.show(); panel.initialize(); } }, getPanel: function(panelName) { var panel = this.panelMap[panelName]; return panel; } }; //************************************************************************************************ // Button /** * options.element * options.caption * options.title * * options.owner * options.className * options.pressedClassName * * options.onPress * options.onUnpress * options.onClick * * @class * @extends FBL.Controller * */ FBL.Button = function(options) { options = options || {}; append(this, options); this.state = "unpressed"; this.display = "unpressed"; if (this.element) { this.container = this.element.parentNode; } else { this.shouldDestroy = true; this.container = this.owner.getPanel().toolButtonsNode; this.element = createElement("a", { className: this.baseClassName + " " + this.className + " fbHover", innerHTML: this.caption }); if (this.title) this.element.title = this.title; this.container.appendChild(this.element); } }; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Button.prototype = extend(Controller, /**@extend FBL.Button.prototype*/ { type: "normal", caption: "caption", title: null, className: "", // custom class baseClassName: "fbButton", // control class pressedClassName: "fbBtnPressed", // control pressed class element: null, container: null, owner: null, state: null, display: null, destroy: function() { this.shutdown(); // only remove if it is a dynamically generated button (not pre-rendered) if (this.shouldDestroy) this.container.removeChild(this.element); this.element = null; this.container = null; this.owner = null; }, initialize: function() { Controller.initialize.apply(this); var element = this.element; this.addController([element, "mousedown", this.handlePress]); if (this.type == "normal") this.addController( [element, "mouseup", this.handleUnpress], [element, "mouseout", this.handleUnpress], [element, "click", this.handleClick] ); }, shutdown: function() { Controller.shutdown.apply(this); }, restore: function() { this.changeState("unpressed"); }, changeState: function(state) { this.state = state; this.changeDisplay(state); }, changeDisplay: function(display) { if (display != this.display) { if (display == "pressed") { setClass(this.element, this.pressedClassName); } else if (display == "unpressed") { removeClass(this.element, this.pressedClassName); } this.display = display; } }, handlePress: function(event) { cancelEvent(event, true); if (this.type == "normal") { this.changeDisplay("pressed"); this.beforeClick = true; } else if (this.type == "toggle") { if (this.state == "pressed") { this.changeState("unpressed"); if (this.onUnpress) this.onUnpress.apply(this.owner, arguments); } else { this.changeState("pressed"); if (this.onPress) this.onPress.apply(this.owner, arguments); } if (this.onClick) this.onClick.apply(this.owner, arguments); } return false; }, handleUnpress: function(event) { cancelEvent(event, true); if (this.beforeClick) this.changeDisplay("unpressed"); return false; }, handleClick: function(event) { cancelEvent(event, true); if (this.type == "normal") { if (this.onClick) this.onClick.apply(this.owner); this.changeState("unpressed"); } this.beforeClick = false; return false; } }); // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /** * @class * @extends FBL.Button */ FBL.IconButton = function() { Button.apply(this, arguments); }; IconButton.prototype = extend(Button.prototype, /**@extend FBL.IconButton.prototype*/ { baseClassName: "fbIconButton", pressedClassName: "fbIconPressed" }); //************************************************************************************************ // Menu var menuItemProps = {"class": "$item.className", type: "$item.type", value: "$item.value", _command: "$item.command"}; if (isIE6) menuItemProps.href = "javascript:void(0)"; // Allow GUI to be loaded even when Domplate module is not installed. if (FBL.domplate) var MenuPlate = domplate(Firebug.Rep, { tag: DIV({"class": "fbMenu fbShadow"}, DIV({"class": "fbMenuContent fbShadowContent"}, FOR("item", "$object.items|memberIterator", TAG("$item.tag", {item: "$item"}) ) ) ), itemTag: A(menuItemProps, "$item.label" ), checkBoxTag: A(extend(menuItemProps, {checked : "$item.checked"}), "$item.label" ), radioButtonTag: A(extend(menuItemProps, {selected : "$item.selected"}), "$item.label" ), groupTag: A(extend(menuItemProps, {child: "$item.child"}), "$item.label" ), shortcutTag: A(menuItemProps, "$item.label", SPAN({"class": "fbMenuShortcutKey"}, "$item.key" ) ), separatorTag: SPAN({"class": "fbMenuSeparator"}), memberIterator: function(items) { var result = []; for (var i=0, length=items.length; i width || el.scrollHeight > height)) { width = el.scrollWidth; height = el.scrollHeight; } return {width: width, height: height}; }, getWindowScrollPosition: function() { var top=0, left=0, el; if(typeof this.window.pageYOffset == "number") { top = this.window.pageYOffset; left = this.window.pageXOffset; } else if((el=this.document.body) && (el.scrollTop || el.scrollLeft)) { top = el.scrollTop; left = el.scrollLeft; } else if((el=this.document.documentElement) && (el.scrollTop || el.scrollLeft)) { top = el.scrollTop; left = el.scrollLeft; } return {top:top, left:left}; }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Element Methods getElementFromPoint: function(x, y) { if (shouldFixElementFromPoint) { var scroll = this.getWindowScrollPosition(); return this.document.elementFromPoint(x + scroll.left, y + scroll.top); } else return this.document.elementFromPoint(x, y); }, getElementPosition: function(el) { var left = 0 var top = 0; do { left += el.offsetLeft; top += el.offsetTop; } while (el = el.offsetParent); return {left:left, top:top}; }, getElementBox: function(el) { var result = {}; if (el.getBoundingClientRect) { var rect = el.getBoundingClientRect(); // fix IE problem with offset when not in fullscreen mode var offset = isIE ? this.document.body.clientTop || this.document.documentElement.clientTop: 0; var scroll = this.getWindowScrollPosition(); result.top = Math.round(rect.top - offset + scroll.top); result.left = Math.round(rect.left - offset + scroll.left); result.height = Math.round(rect.bottom - rect.top); result.width = Math.round(rect.right - rect.left); } else { var position = this.getElementPosition(el); result.top = position.top; result.left = position.left; result.height = el.offsetHeight; result.width = el.offsetWidth; } return result; }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Measurement Methods getMeasurement: function(el, name) { var result = {value: 0, unit: "px"}; var cssValue = this.getStyle(el, name); if (!cssValue) return result; if (cssValue.toLowerCase() == "auto") return result; var reMeasure = /(\d+\.?\d*)(.*)/; var m = cssValue.match(reMeasure); if (m) { result.value = m[1]-0; result.unit = m[2].toLowerCase(); } return result; }, getMeasurementInPixels: function(el, name) { if (!el) return null; var m = this.getMeasurement(el, name); var value = m.value; var unit = m.unit; if (unit == "px") return value; else if (unit == "pt") return this.pointsToPixels(name, value); if (unit == "em") return this.emToPixels(el, value); else if (unit == "%") return this.percentToPixels(el, value); }, getMeasurementBox1: function(el, name) { var sufixes = ["Top", "Left", "Bottom", "Right"]; var result = []; for(var i=0, sufix; sufix=sufixes[i]; i++) result[i] = Math.round(this.getMeasurementInPixels(el, name + sufix)); return {top:result[0], left:result[1], bottom:result[2], right:result[3]}; }, getMeasurementBox: function(el, name) { var result = []; var sufixes = name == "border" ? ["TopWidth", "LeftWidth", "BottomWidth", "RightWidth"] : ["Top", "Left", "Bottom", "Right"]; if (isIE) { var propName, cssValue; var autoMargin = null; for(var i=0, sufix; sufix=sufixes[i]; i++) { propName = name + sufix; cssValue = el.currentStyle[propName] || el.style[propName]; if (cssValue == "auto") { if (!autoMargin) autoMargin = this.getCSSAutoMarginBox(el); result[i] = autoMargin[sufix.toLowerCase()]; } else result[i] = this.getMeasurementInPixels(el, propName); } } else { for(var i=0, sufix; sufix=sufixes[i]; i++) result[i] = this.getMeasurementInPixels(el, name + sufix); } return {top:result[0], left:result[1], bottom:result[2], right:result[3]}; }, getCSSAutoMarginBox: function(el) { if (isIE && " meta title input script link a ".indexOf(" "+el.nodeName.toLowerCase()+" ") != -1) return {top:0, left:0, bottom:0, right:0}; /**/ if (isIE && " h1 h2 h3 h4 h5 h6 h7 ul p ".indexOf(" "+el.nodeName.toLowerCase()+" ") == -1) return {top:0, left:0, bottom:0, right:0}; /**/ var offsetTop = 0; if (false && isIEStantandMode) { var scrollSize = Firebug.browser.getWindowScrollSize(); offsetTop = scrollSize.height; } var box = this.document.createElement("div"); //box.style.cssText = "margin:0; padding:1px; border: 0; position:static; overflow:hidden; visibility: hidden;"; box.style.cssText = "margin:0; padding:1px; border: 0; visibility: hidden;"; var clone = el.cloneNode(false); var text = this.document.createTextNode(" "); clone.appendChild(text); box.appendChild(clone); this.document.body.appendChild(box); var marginTop = clone.offsetTop - box.offsetTop - 1; var marginBottom = box.offsetHeight - clone.offsetHeight - 2 - marginTop; var marginLeft = clone.offsetLeft - box.offsetLeft - 1; var marginRight = box.offsetWidth - clone.offsetWidth - 2 - marginLeft; this.document.body.removeChild(box); return {top:marginTop+offsetTop, left:marginLeft, bottom:marginBottom-offsetTop, right:marginRight}; }, getFontSizeInPixels: function(el) { var size = this.getMeasurement(el, "fontSize"); if (size.unit == "px") return size.value; // get font size, the dirty way var computeDirtyFontSize = function(el, calibration) { var div = this.document.createElement("div"); var divStyle = offscreenStyle; if (calibration) divStyle += " font-size:"+calibration+"px;"; div.style.cssText = divStyle; div.innerHTML = "A"; el.appendChild(div); var value = div.offsetHeight; el.removeChild(div); return value; } /* var calibrationBase = 200; var calibrationValue = computeDirtyFontSize(el, calibrationBase); var rate = calibrationBase / calibrationValue; /**/ // the "dirty technique" fails in some environments, so we're using a static value // based in some tests. var rate = 200 / 225; var value = computeDirtyFontSize(el); return value * rate; }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Unit Funtions pointsToPixels: function(name, value, returnFloat) { var axis = /Top$|Bottom$/.test(name) ? "y" : "x"; var result = value * pixelsPerInch[axis] / 72; return returnFloat ? result : Math.round(result); }, emToPixels: function(el, value) { if (!el) return null; var fontSize = this.getFontSizeInPixels(el); return Math.round(value * fontSize); }, exToPixels: function(el, value) { if (!el) return null; // get ex value, the dirty way var div = this.document.createElement("div"); div.style.cssText = offscreenStyle + "width:"+value + "ex;"; el.appendChild(div); var value = div.offsetWidth; el.removeChild(div); return value; }, percentToPixels: function(el, value) { if (!el) return null; // get % value, the dirty way var div = this.document.createElement("div"); div.style.cssText = offscreenStyle + "width:"+value + "%;"; el.appendChild(div); var value = div.offsetWidth; el.removeChild(div); return value; }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * getStyle: isIE ? function(el, name) { return el.currentStyle[name] || el.style[name] || undefined; } : function(el, name) { return this.document.defaultView.getComputedStyle(el,null)[name] || el.style[name] || undefined; } }; // ************************************************************************************************ }}); /* See license.txt for terms of usage */ FBL.ns( /**@scope ns-chrome*/ function() { with (FBL) { // ************************************************************************************************ // ************************************************************************************************ // Globals // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Window Options var WindowDefaultOptions = { type: "frame", id: "FirebugUI", height: 250 }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Instantiated objects commandLine, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Interface Elements Cache fbTop, fbContent, fbContentStyle, fbBottom, fbBtnInspect, fbToolbar, fbPanelBox1, fbPanelBox1Style, fbPanelBox2, fbPanelBox2Style, fbPanelBar2Box, fbPanelBar2BoxStyle, fbHSplitter, fbVSplitter, fbVSplitterStyle, fbPanel1, fbPanel1Style, fbPanel2, fbPanel2Style, fbConsole, fbConsoleStyle, fbHTML, fbCommandLine, fbLargeCommandLine, fbLargeCommandButtons, //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Cached size values topHeight, topPartialHeight, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * chromeRedrawSkipRate = isIE ? 75 : isOpera ? 80 : 75, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * lastSelectedPanelName, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * focusCommandLineState = 0, lastFocusedPanelName, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * lastHSplitterMouseMove = 0, onHSplitterMouseMoveBuffer = null, onHSplitterMouseMoveTimer = null, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * lastVSplitterMouseMove = 0; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // ************************************************************************************************ // FirebugChrome /**@namespace*/ FBL.FirebugChrome = { // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * isOpen: false, height: 250, sidePanelWidth: 350, selectedPanelName: "Console", selectedHTMLElementId: null, chromeMap: {}, htmlSelectionStack: [], consoleMessageQueue: [], // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * create: function() { if (FBTrace.DBG_INITIALIZE) FBTrace.sysout("FirebugChrome.create", "creating chrome window"); createChromeWindow(); }, initialize: function() { if (FBTrace.DBG_INITIALIZE) FBTrace.sysout("FirebugChrome.initialize", "initializing chrome window"); if (Env.chrome.type == "frame" || Env.chrome.type == "div") ChromeMini.create(Env.chrome); var chrome = Firebug.chrome = new Chrome(Env.chrome); FirebugChrome.chromeMap[chrome.type] = chrome; addGlobalEvent("keydown", onGlobalKeyDown); if (Env.Options.enablePersistent && chrome.type == "popup") { // TODO: xxxpedro persist - revise chrome synchronization when in persistent mode var frame = FirebugChrome.chromeMap.frame; if (frame) frame.close(); //chrome.reattach(frame, chrome); //TODO: xxxpedro persist synchronize? chrome.initialize(); } }, clone: function(FBChrome) { for (var name in FBChrome) { var prop = FBChrome[name]; if (FBChrome.hasOwnProperty(name) && !isFunction(prop)) { this[name] = prop; } } } }; // ************************************************************************************************ // Chrome Window Creation var createChromeWindow = function(options) { options = extend(WindowDefaultOptions, options || {}); //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Locals var chrome = {}, context = options.context || Env.browser, type = chrome.type = Env.Options.enablePersistent ? "popup" : options.type, isChromeFrame = type == "frame", useLocalSkin = Env.useLocalSkin, url = useLocalSkin ? Env.Location.skin : "about:blank", // document.body not available in XML+XSL documents in Firefox body = context.document.getElementsByTagName("body")[0], formatNode = function(node) { if (!Env.isDebugMode) { node.firebugIgnore = true; } node.style.border = "0"; node.style.visibility = "hidden"; node.style.zIndex = "2147483647"; // MAX z-index = 2147483647 node.style.position = noFixedPosition ? "absolute" : "fixed"; node.style.width = "100%"; // "102%"; IE auto margin bug node.style.left = "0"; node.style.bottom = noFixedPosition ? "-1px" : "0"; node.style.height = options.height + "px"; // avoid flickering during chrome rendering if (isFirefox) node.style.display = "none"; }, createChromeDiv = function() { //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."); var node = chrome.node = createGlobalElement("div"), style = createGlobalElement("style"), css = FirebugChrome.Skin.CSS /* .replace(/;/g, " !important;") .replace(/!important\s!important/g, "!important") .replace(/display\s*:\s*(\w+)\s*!important;/g, "display:$1;")*/, // reset some styles to minimize interference from the main page's style rules = ".fbBody *{margin:0;padding:0;font-size:11px;line-height:13px;color:inherit;}" + // load the chrome styles css + // adjust some remaining styles ".fbBody #fbHSplitter{position:absolute !important;} .fbBody #fbHTML span{line-height:14px;} .fbBody .lineNo div{line-height:inherit !important;}"; /* if (isIE) { // IE7 CSS bug (FbChrome table bigger than its parent div) rules += ".fbBody table.fbChrome{position: static !important;}"; }/**/ style.type = "text/css"; if (style.styleSheet) style.styleSheet.cssText = rules; else style.appendChild(context.document.createTextNode(rules)); document.getElementsByTagName("head")[0].appendChild(style); node.className = "fbBody"; node.style.overflow = "hidden"; node.innerHTML = getChromeDivTemplate(); if (isIE) { // IE7 CSS bug (FbChrome table bigger than its parent div) setTimeout(function(){ node.firstChild.style.height = "1px"; node.firstChild.style.position = "static"; },0); /**/ } formatNode(node); body.appendChild(node); chrome.window = window; chrome.document = document; onChromeLoad(chrome); }; //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * try { //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // create the Chrome as a "div" (windowless mode) if (type == "div") { createChromeDiv(); return; } //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // cretate the Chrome as an "iframe" else if (isChromeFrame) { // Create the Chrome Frame var node = chrome.node = createGlobalElement("iframe"); node.setAttribute("src", url); node.setAttribute("frameBorder", "0"); formatNode(node); body.appendChild(node); // must set the id after appending to the document, otherwise will cause an // strange error in IE, making the iframe load the page in which the bookmarklet // was created (like getfirebug.com), before loading the injected UI HTML, // generating an "Access Denied" error. node.id = options.id; } //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // create the Chrome as a "popup" else { var height = FirebugChrome.height || options.height, options = [ "true,top=", Math.max(screen.availHeight - height - 61 /* Google Chrome bug */, 0), ",left=0,height=", height, ",width=", screen.availWidth-10, // Opera opens popup in a new tab if it's too big! ",resizable" ].join(""), node = chrome.node = context.window.open( url, "popup", options ); if (node) { try { node.focus(); } catch(E) { alert("Firebug Error: Firebug popup was blocked."); return; } } else { alert("Firebug Error: Firebug popup was blocked."); return; } } //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Inject the interface HTML if it is not using the local skin if (!useLocalSkin) { var tpl = getChromeTemplate(!isChromeFrame), doc = isChromeFrame ? node.contentWindow.document : node.document; doc.write(tpl); doc.close(); } //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Wait the Window to be loaded var win, waitDelay = useLocalSkin ? isChromeFrame ? 200 : 300 : 100, waitForWindow = function() { if ( // Frame loaded... OR isChromeFrame && (win=node.contentWindow) && node.contentWindow.document.getElementById("fbCommandLine") || // Popup loaded !isChromeFrame && (win=node.window) && node.document && node.document.getElementById("fbCommandLine") ) { chrome.window = win.window; chrome.document = win.document; // Prevent getting the wrong chrome height in FF when opening a popup setTimeout(function(){ onChromeLoad(chrome); }, useLocalSkin ? 200 : 0); } else setTimeout(waitForWindow, waitDelay); }; waitForWindow(); } catch(e) { var msg = e.message || e; if (/access/i.test(msg)) { // Firebug Lite could not create a window for its Graphical User Interface due to // a access restriction. This happens in some pages, when loading via bookmarklet. // In such cases, the only way is to load the GUI in a "windowless mode". if (isChromeFrame) body.removeChild(node); else if(type == "popup") node.close(); // Load the GUI in a "windowless mode" createChromeDiv(); } else { alert("Firebug Error: Firebug GUI could not be created."); } } }; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * var onChromeLoad = function onChromeLoad(chrome) { Env.chrome = chrome; if (FBTrace.DBG_INITIALIZE) FBTrace.sysout("Chrome onChromeLoad", "chrome window loaded"); if (Env.Options.enablePersistent) { // TODO: xxxpedro persist - make better chrome synchronization when in persistent mode Env.FirebugChrome = FirebugChrome; chrome.window.Firebug = chrome.window.Firebug || {}; chrome.window.Firebug.SharedEnv = Env; if (Env.isDevelopmentMode) { Env.browser.window.FBDev.loadChromeApplication(chrome); } else { var doc = chrome.document; var script = doc.createElement("script"); script.src = Env.Location.app + "#remote,persist"; doc.getElementsByTagName("head")[0].appendChild(script); } } else { if (chrome.type == "frame" || chrome.type == "div") { // initialize the chrome application setTimeout(function(){ FBL.Firebug.initialize(); },0); } else if (chrome.type == "popup") { var oldChrome = FirebugChrome.chromeMap.frame; var newChrome = new Chrome(chrome); // TODO: xxxpedro sync detach reattach attach dispatch(newChrome.panelMap, "detach", [oldChrome, newChrome]); if (oldChrome) oldChrome.close(); newChrome.reattach(oldChrome, newChrome); } } }; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * var getChromeDivTemplate = function() { return FirebugChrome.Skin.HTML; }; var getChromeTemplate = function(isPopup) { var tpl = FirebugChrome.Skin; var r = [], i = -1; r[++i] = ''; r[++i] = ''; r[++i] = Firebug.version; /* r[++i] = ''; /**/ r[++i] = ''; /**/ r[++i] = ''; r[++i] = tpl.HTML; r[++i] = ''; return r.join(""); }; // ************************************************************************************************ // Chrome Class /**@class*/ var Chrome = function Chrome(chrome) { var type = chrome.type; var Base = type == "frame" || type == "div" ? ChromeFrameBase : ChromePopupBase; append(this, Base); // inherit from base class (ChromeFrameBase or ChromePopupBase) append(this, chrome); // inherit chrome window properties append(this, new Context(chrome.window)); // inherit from Context class FirebugChrome.chromeMap[type] = this; Firebug.chrome = this; Env.chrome = chrome.window; this.commandLineVisible = false; this.sidePanelVisible = false; this.create(); return this; }; // ************************************************************************************************ // ChromeBase /** * @namespace * @extends FBL.Controller * @extends FBL.PanelBar **/ var ChromeBase = {}; append(ChromeBase, Controller); append(ChromeBase, PanelBar); append(ChromeBase, /**@extend ns-chrome-ChromeBase*/ { // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // inherited properties // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // inherited from createChrome function node: null, type: null, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // inherited from Context.prototype document: null, window: null, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // value properties sidePanelVisible: false, commandLineVisible: false, largeCommandLineVisible: false, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // object properties inspectButton: null, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * create: function() { PanelBar.create.call(this); if (Firebug.Inspector) this.inspectButton = new Button({ type: "toggle", element: $("fbChrome_btInspect"), owner: Firebug.Inspector, onPress: Firebug.Inspector.startInspecting, onUnpress: Firebug.Inspector.stopInspecting }); }, destroy: function() { if(Firebug.Inspector) this.inspectButton.destroy(); PanelBar.destroy.call(this); this.shutdown(); }, testMenu: function() { var firebugMenu = new Menu( { id: "fbFirebugMenu", items: [ { label: "Open Firebug", type: "shortcut", key: isFirefox ? "Shift+F12" : "F12", checked: true, command: "toggleChrome" }, { label: "Open Firebug in New Window", type: "shortcut", key: isFirefox ? "Ctrl+Shift+F12" : "Ctrl+F12", command: "openPopup" }, { label: "Inspect Element", type: "shortcut", key: "Ctrl+Shift+C", command: "toggleInspect" }, { label: "Command Line", type: "shortcut", key: "Ctrl+Shift+L", command: "focusCommandLine" }, "-", { label: "Options", type: "group", child: "fbFirebugOptionsMenu" }, "-", { label: "Firebug Lite Website...", command: "visitWebsite" }, { label: "Discussion Group...", command: "visitDiscussionGroup" }, { label: "Issue Tracker...", command: "visitIssueTracker" } ], onHide: function() { iconButton.restore(); }, toggleChrome: function() { Firebug.chrome.toggle(); }, openPopup: function() { Firebug.chrome.toggle(true, true); }, toggleInspect: function() { Firebug.Inspector.toggleInspect(); }, focusCommandLine: function() { Firebug.chrome.focusCommandLine(); }, visitWebsite: function() { this.visit("http://getfirebug.com/lite.html"); }, visitDiscussionGroup: function() { this.visit("http://groups.google.com/group/firebug"); }, visitIssueTracker: function() { this.visit("http://code.google.com/p/fbug/issues/list"); }, visit: function(url) { window.open(url); } }); /**@private*/ var firebugOptionsMenu = { id: "fbFirebugOptionsMenu", getItems: function() { var cookiesDisabled = !Firebug.saveCookies; return [ { label: "Save Options in Cookies", type: "checkbox", value: "saveCookies", checked: Firebug.saveCookies, command: "saveOptions" }, "-", { label: "Start Opened", type: "checkbox", value: "startOpened", checked: Firebug.startOpened, disabled: cookiesDisabled }, { label: "Start in New Window", type: "checkbox", value: "startInNewWindow", checked: Firebug.startInNewWindow, disabled: cookiesDisabled }, { label: "Show Icon When Hidden", type: "checkbox", value: "showIconWhenHidden", checked: Firebug.showIconWhenHidden, disabled: cookiesDisabled }, { label: "Override Console Object", type: "checkbox", value: "overrideConsole", checked: Firebug.overrideConsole, disabled: cookiesDisabled }, { label: "Ignore Firebug Elements", type: "checkbox", value: "ignoreFirebugElements", checked: Firebug.ignoreFirebugElements, disabled: cookiesDisabled }, { label: "Disable When Firebug Active", type: "checkbox", value: "disableWhenFirebugActive", checked: Firebug.disableWhenFirebugActive, disabled: cookiesDisabled }, { label: "Disable XHR Listener", type: "checkbox", value: "disableXHRListener", checked: Firebug.disableXHRListener, disabled: cookiesDisabled }, { label: "Enable Trace Mode", type: "checkbox", value: "enableTrace", checked: Firebug.enableTrace, disabled: cookiesDisabled }, { label: "Enable Persistent Mode (experimental)", type: "checkbox", value: "enablePersistent", checked: Firebug.enablePersistent, disabled: cookiesDisabled }, "-", { label: "Reset All Firebug Options", command: "restorePrefs", disabled: cookiesDisabled } ]; }, onCheck: function(target, value, checked) { Firebug.setPref(value, checked); }, saveOptions: function(target) { var saveEnabled = target.getAttribute("checked"); if (!saveEnabled) this.restorePrefs(); this.updateMenu(target); return false; }, restorePrefs: function(target) { Firebug.restorePrefs(); if(Firebug.saveCookies) Firebug.savePrefs(); else Firebug.erasePrefs(); if (target) this.updateMenu(target); return false; }, updateMenu: function(target) { var options = getElementsByClass(target.parentNode, "fbMenuOption"); var firstOption = options[0]; var enabled = Firebug.saveCookies; if (enabled) Menu.check(firstOption); else Menu.uncheck(firstOption); if (enabled) Menu.check(options[0]); else Menu.uncheck(options[0]); for (var i = 1, length = options.length; i < length; i++) { var option = options[i]; var value = option.getAttribute("value"); var pref = Firebug[value]; if (pref) Menu.check(option); else Menu.uncheck(option); if (enabled) Menu.enable(option); else Menu.disable(option); } } }; Menu.register(firebugOptionsMenu); var menu = firebugMenu; var testMenuClick = function(event) { //console.log("testMenuClick"); cancelEvent(event, true); var target = event.target || event.srcElement; if (menu.isVisible) menu.hide(); else { var offsetLeft = isIE6 ? 1 : -4, // IE6 problem with fixed position chrome = Firebug.chrome, box = chrome.getElementBox(target), offset = chrome.type == "div" ? chrome.getElementPosition(chrome.node) : {top: 0, left: 0}; menu.show( box.left + offsetLeft - offset.left, box.top + box.height -5 - offset.top ); } return false; }; var iconButton = new IconButton({ type: "toggle", element: $("fbFirebugButton"), onClick: testMenuClick }); iconButton.initialize(); //addEvent($("fbToolbarIcon"), "click", testMenuClick); }, initialize: function() { // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * if (Env.bookmarkletOutdated) Firebug.Console.logFormatted([ "A new bookmarklet version is available. " + "Please visit http://getfirebug.com/firebuglite#Install and update it." ], Firebug.context, "warn"); // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * if (Firebug.Console) Firebug.Console.flush(); if (Firebug.Trace) FBTrace.flush(Firebug.Trace); // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * if (FBTrace.DBG_INITIALIZE) FBTrace.sysout("Firebug.chrome.initialize", "initializing chrome application"); // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // initialize inherited classes Controller.initialize.call(this); PanelBar.initialize.call(this); // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // create the interface elements cache fbTop = $("fbTop"); fbContent = $("fbContent"); fbContentStyle = fbContent.style; fbBottom = $("fbBottom"); fbBtnInspect = $("fbBtnInspect"); fbToolbar = $("fbToolbar"); fbPanelBox1 = $("fbPanelBox1"); fbPanelBox1Style = fbPanelBox1.style; fbPanelBox2 = $("fbPanelBox2"); fbPanelBox2Style = fbPanelBox2.style; fbPanelBar2Box = $("fbPanelBar2Box"); fbPanelBar2BoxStyle = fbPanelBar2Box.style; fbHSplitter = $("fbHSplitter"); fbVSplitter = $("fbVSplitter"); fbVSplitterStyle = fbVSplitter.style; fbPanel1 = $("fbPanel1"); fbPanel1Style = fbPanel1.style; fbPanel2 = $("fbPanel2"); fbPanel2Style = fbPanel2.style; fbConsole = $("fbConsole"); fbConsoleStyle = fbConsole.style; fbHTML = $("fbHTML"); fbCommandLine = $("fbCommandLine"); fbLargeCommandLine = $("fbLargeCommandLine"); fbLargeCommandButtons = $("fbLargeCommandButtons"); // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // static values cache topHeight = fbTop.offsetHeight; topPartialHeight = fbToolbar.offsetHeight; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * disableTextSelection($("fbToolbar")); disableTextSelection($("fbPanelBarBox")); disableTextSelection($("fbPanelBar1")); disableTextSelection($("fbPanelBar2")); // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Add the "javascript:void(0)" href attributes used to make the hover effect in IE6 if (isIE6 && Firebug.Selector) { // TODO: xxxpedro change to getElementsByClass var as = $$(".fbHover"); for (var i=0, a; a=as[i]; i++) { a.setAttribute("href", "javascript:void(0)"); } } // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // initialize all panels /* var panelMap = Firebug.panelTypes; for (var i=0, p; p=panelMap[i]; i++) { if (!p.parentPanel) { this.addPanel(p.prototype.name); } } /**/ // ************************************************************************************************ // ************************************************************************************************ // ************************************************************************************************ // ************************************************************************************************ if(Firebug.Inspector) this.inspectButton.initialize(); // ************************************************************************************************ // ************************************************************************************************ // ************************************************************************************************ // ************************************************************************************************ this.addController( [$("fbLargeCommandLineIcon"), "click", this.showLargeCommandLine] ); // ************************************************************************************************ // Select the first registered panel // TODO: BUG IE7 var self = this; setTimeout(function(){ self.selectPanel(FirebugChrome.selectedPanelName); if (FirebugChrome.selectedPanelName == "Console" && Firebug.CommandLine) Firebug.chrome.focusCommandLine(); },0); // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //this.draw(); // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * var onPanelMouseDown = function onPanelMouseDown(event) { //console.log("onPanelMouseDown", event.target || event.srcElement, event); var target = event.target || event.srcElement; if (FBL.isLeftClick(event)) { var editable = FBL.getAncestorByClass(target, "editable"); // if an editable element has been clicked then start editing if (editable) { Firebug.Editor.startEditing(editable); FBL.cancelEvent(event); } // if any other element has been clicked then stop editing else { if (!hasClass(target, "textEditorInner")) Firebug.Editor.stopEditing(); } } else if (FBL.isMiddleClick(event) && Firebug.getRepNode(target)) { // Prevent auto-scroll when middle-clicking a rep object FBL.cancelEvent(event); } }; Firebug.getElementPanel = function(element) { var panelNode = getAncestorByClass(element, "fbPanel"); var id = panelNode.id.substr(2); var panel = Firebug.chrome.panelMap[id]; if (!panel) { if (Firebug.chrome.selectedPanel.sidePanelBar) panel = Firebug.chrome.selectedPanel.sidePanelBar.panelMap[id]; } return panel; }; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // TODO: xxxpedro port to Firebug // Improved window key code event listener. Only one "keydown" event will be attached // to the window, and the onKeyCodeListen() function will delegate which listeners // should be called according to the event.keyCode fired. var onKeyCodeListenersMap = []; var onKeyCodeListen = function(event) { for (var keyCode in onKeyCodeListenersMap) { var listeners = onKeyCodeListenersMap[keyCode]; for (var i = 0, listener; listener = listeners[i]; i++) { var filter = listener.filter || FBL.noKeyModifiers; if (event.keyCode == keyCode && (!filter || filter(event))) { listener.listener(); FBL.cancelEvent(event, true); return false; } } } }; addEvent(Firebug.chrome.document, "keydown", onKeyCodeListen); /** * @name keyCodeListen * @memberOf FBL.FirebugChrome */ Firebug.chrome.keyCodeListen = function(key, filter, listener, capture) { var keyCode = KeyEvent["DOM_VK_"+key]; if (!onKeyCodeListenersMap[keyCode]) onKeyCodeListenersMap[keyCode] = []; onKeyCodeListenersMap[keyCode].push({ filter: filter, listener: listener }); return keyCode; }; /** * @name keyIgnore * @memberOf FBL.FirebugChrome */ Firebug.chrome.keyIgnore = function(keyCode) { onKeyCodeListenersMap[keyCode] = null; delete onKeyCodeListenersMap[keyCode]; }; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /**/ // move to shutdown //removeEvent(Firebug.chrome.document, "keydown", listener[0]); /* Firebug.chrome.keyCodeListen = function(key, filter, listener, capture) { if (!filter) filter = FBL.noKeyModifiers; var keyCode = KeyEvent["DOM_VK_"+key]; var fn = function fn(event) { if (event.keyCode == keyCode && (!filter || filter(event))) { listener(); FBL.cancelEvent(event, true); return false; } } addEvent(Firebug.chrome.document, "keydown", fn); return [fn, capture]; }; Firebug.chrome.keyIgnore = function(listener) { removeEvent(Firebug.chrome.document, "keydown", listener[0]); }; /**/ this.addController( [fbPanel1, "mousedown", onPanelMouseDown], [fbPanel2, "mousedown", onPanelMouseDown] ); /**/ // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // menus can be used without domplate if (FBL.domplate) this.testMenu(); /**/ //test XHR /* setTimeout(function(){ FBL.Ajax.request({url: "../content/firebug/boot.js"}); FBL.Ajax.request({url: "../content/firebug/boot.js.invalid"}); },1000); /**/ }, shutdown: function() { // ************************************************************************************************ // ************************************************************************************************ // ************************************************************************************************ // ************************************************************************************************ if(Firebug.Inspector) this.inspectButton.shutdown(); // ************************************************************************************************ // ************************************************************************************************ // ************************************************************************************************ // ************************************************************************************************ // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // remove disableTextSelection event handlers restoreTextSelection($("fbToolbar")); restoreTextSelection($("fbPanelBarBox")); restoreTextSelection($("fbPanelBar1")); restoreTextSelection($("fbPanelBar2")); // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // shutdown inherited classes Controller.shutdown.call(this); PanelBar.shutdown.call(this); // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Remove the interface elements cache (this must happen after calling // the shutdown method of all dependent components to avoid errors) fbTop = null; fbContent = null; fbContentStyle = null; fbBottom = null; fbBtnInspect = null; fbToolbar = null; fbPanelBox1 = null; fbPanelBox1Style = null; fbPanelBox2 = null; fbPanelBox2Style = null; fbPanelBar2Box = null; fbPanelBar2BoxStyle = null; fbHSplitter = null; fbVSplitter = null; fbVSplitterStyle = null; fbPanel1 = null; fbPanel1Style = null; fbPanel2 = null; fbConsole = null; fbConsoleStyle = null; fbHTML = null; fbCommandLine = null; fbLargeCommandLine = null; fbLargeCommandButtons = null; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // static values cache topHeight = null; topPartialHeight = null; }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * toggle: function(forceOpen, popup) { if(popup) { this.detach(); } else { if (isOpera && Firebug.chrome.type == "popup" && Firebug.chrome.node.closed) { var frame = FirebugChrome.chromeMap.frame; frame.reattach(); FirebugChrome.chromeMap.popup = null; frame.open(); return; } // If the context is a popup, ignores the toggle process if (Firebug.chrome.type == "popup") return; var shouldOpen = forceOpen || !FirebugChrome.isOpen; if(shouldOpen) this.open(); else this.close(); } }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * detach: function() { if(!FirebugChrome.chromeMap.popup) { createChromeWindow({type: "popup"}); } }, reattach: function(oldChrome, newChrome) { Firebug.browser.window.Firebug = Firebug; // chrome synchronization var newPanelMap = newChrome.panelMap; var oldPanelMap = oldChrome.panelMap; var panel; for(var name in newPanelMap) { // TODO: xxxpedro innerHTML panel = newPanelMap[name]; if (panel.options.innerHTMLSync) panel.panelNode.innerHTML = oldPanelMap[name].panelNode.innerHTML; } Firebug.chrome = newChrome; // TODO: xxxpedro sync detach reattach attach //dispatch(Firebug.chrome.panelMap, "detach", [oldChrome, newChrome]); if (newChrome.type == "popup") { newChrome.initialize(); //dispatch(Firebug.modules, "initialize", []); } else { // TODO: xxxpedro only needed in persistent // should use FirebugChrome.clone, but popup FBChrome // isn't acessible FirebugChrome.selectedPanelName = oldChrome.selectedPanel.name; } dispatch(newPanelMap, "reattach", [oldChrome, newChrome]); }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * draw: function() { var size = this.getSize(); // Height related values var commandLineHeight = Firebug.chrome.commandLineVisible ? fbCommandLine.offsetHeight : 0, y = Math.max(size.height /* chrome height */, topHeight), heightValue = Math.max(y - topHeight - commandLineHeight /* fixed height */, 0), height = heightValue + "px", // Width related values sideWidthValue = Firebug.chrome.sidePanelVisible ? FirebugChrome.sidePanelWidth : 0, width = Math.max(size.width /* chrome width */ - sideWidthValue, 0) + "px"; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Height related rendering fbPanelBox1Style.height = height; fbPanel1Style.height = height; if (isIE || isOpera) { // Fix IE and Opera problems with auto resizing the verticall splitter fbVSplitterStyle.height = Math.max(y - topPartialHeight - commandLineHeight, 0) + "px"; } //xxxpedro FF2 only? /* else if (isFirefox) { // Fix Firefox problem with table rows with 100% height (fit height) fbContentStyle.maxHeight = Math.max(y - fixedHeight, 0)+ "px"; }/**/ // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Width related rendering fbPanelBox1Style.width = width; fbPanel1Style.width = width; // SidePanel rendering if (Firebug.chrome.sidePanelVisible) { sideWidthValue = Math.max(sideWidthValue - 6, 0); var sideWidth = sideWidthValue + "px"; fbPanelBox2Style.width = sideWidth; fbVSplitterStyle.right = sideWidth; if (Firebug.chrome.largeCommandLineVisible) { fbLargeCommandLine = $("fbLargeCommandLine"); fbLargeCommandLine.style.height = heightValue - 4 + "px"; fbLargeCommandLine.style.width = sideWidthValue - 2 + "px"; fbLargeCommandButtons = $("fbLargeCommandButtons"); fbLargeCommandButtons.style.width = sideWidth; } else { fbPanel2Style.height = height; fbPanel2Style.width = sideWidth; fbPanelBar2BoxStyle.width = sideWidth; } } }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * getSize: function() { return this.type == "div" ? { height: this.node.offsetHeight, width: this.node.offsetWidth } : this.getWindowSize(); }, resize: function() { var self = this; // avoid partial resize when maximizing window setTimeout(function(){ self.draw(); if (noFixedPosition && (self.type == "frame" || self.type == "div")) self.fixIEPosition(); }, 0); }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * layout: function(panel) { if (FBTrace.DBG_CHROME) FBTrace.sysout("Chrome.layout", ""); var options = panel.options; changeCommandLineVisibility(options.hasCommandLine); changeSidePanelVisibility(panel.hasSidePanel); Firebug.chrome.draw(); }, showLargeCommandLine: function(hideToggleIcon) { var chrome = Firebug.chrome; if (!chrome.largeCommandLineVisible) { chrome.largeCommandLineVisible = true; if (chrome.selectedPanel.options.hasCommandLine) { if (Firebug.CommandLine) Firebug.CommandLine.blur(); changeCommandLineVisibility(false); } changeSidePanelVisibility(true); fbLargeCommandLine.style.display = "block"; fbLargeCommandButtons.style.display = "block"; fbPanel2Style.display = "none"; fbPanelBar2BoxStyle.display = "none"; chrome.draw(); fbLargeCommandLine.focus(); if (Firebug.CommandLine) Firebug.CommandLine.setMultiLine(true); } }, hideLargeCommandLine: function() { if (Firebug.chrome.largeCommandLineVisible) { Firebug.chrome.largeCommandLineVisible = false; if (Firebug.CommandLine) Firebug.CommandLine.setMultiLine(false); fbLargeCommandLine.blur(); fbPanel2Style.display = "block"; fbPanelBar2BoxStyle.display = "block"; fbLargeCommandLine.style.display = "none"; fbLargeCommandButtons.style.display = "none"; changeSidePanelVisibility(false); if (Firebug.chrome.selectedPanel.options.hasCommandLine) changeCommandLineVisibility(true); Firebug.chrome.draw(); } }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * focusCommandLine: function() { var selectedPanelName = this.selectedPanel.name, panelToSelect; if (focusCommandLineState == 0 || selectedPanelName != "Console") { focusCommandLineState = 0; lastFocusedPanelName = selectedPanelName; panelToSelect = "Console"; } if (focusCommandLineState == 1) { panelToSelect = lastFocusedPanelName; } this.selectPanel(panelToSelect); try { if (Firebug.CommandLine) { if (panelToSelect == "Console") Firebug.CommandLine.focus(); else Firebug.CommandLine.blur(); } } catch(e) { //TODO: xxxpedro trace error } focusCommandLineState = ++focusCommandLineState % 2; } }); // ************************************************************************************************ // ChromeFrameBase /** * @namespace * @extends ns-chrome-ChromeBase */ var ChromeFrameBase = extend(ChromeBase, /**@extend ns-chrome-ChromeFrameBase*/ { create: function() { ChromeBase.create.call(this); // restore display for the anti-flicker trick if (isFirefox) this.node.style.display = "block"; if (Env.Options.startInNewWindow) { this.close(); this.toggle(true, true); return; } if (Env.Options.startOpened) this.open(); else this.close(); }, destroy: function() { removeGlobalEvent("keydown", onGlobalKeyDown); ChromeBase.destroy.call(this); this.document = null; delete this.document; this.window = null; delete this.window; this.node.parentNode.removeChild(this.node); this.node = null; delete this.node; }, initialize: function() { //FBTrace.sysout("Frame", "initialize();") ChromeBase.initialize.call(this); this.addController( [Firebug.browser.window, "resize", this.resize], [$("fbWindow_btClose"), "click", this.close], [$("fbWindow_btDetach"), "click", this.detach], [$("fbWindow_btDeactivate"), "click", this.deactivate] ); if (!Env.Options.enablePersistent) this.addController([Firebug.browser.window, "unload", Firebug.shutdown]); if (noFixedPosition) { this.addController( [Firebug.browser.window, "scroll", this.fixIEPosition] ); } fbVSplitter.onmousedown = onVSplitterMouseDown; fbHSplitter.onmousedown = onHSplitterMouseDown; this.isInitialized = true; }, shutdown: function() { fbVSplitter.onmousedown = null; fbHSplitter.onmousedown = null; ChromeBase.shutdown.apply(this); this.isInitialized = false; }, reattach: function() { var frame = FirebugChrome.chromeMap.frame; ChromeBase.reattach(FirebugChrome.chromeMap.popup, this); }, open: function() { if (!FirebugChrome.isOpen) { FirebugChrome.isOpen = true; if (Env.isChromeExtension) localStorage.setItem("Firebug", "1,1"); var node = this.node; node.style.visibility = "hidden"; // Avoid flickering if (Firebug.showIconWhenHidden) { if (ChromeMini.isInitialized) { ChromeMini.shutdown(); } } else node.style.display = "block"; var main = $("fbChrome"); // IE6 throws an error when setting this property! why? //main.style.display = "table"; main.style.display = ""; var self = this; /// TODO: xxxpedro FOUC node.style.visibility = "visible"; setTimeout(function(){ ///node.style.visibility = "visible"; //dispatch(Firebug.modules, "initialize", []); self.initialize(); if (noFixedPosition) self.fixIEPosition(); self.draw(); }, 10); } }, close: function() { if (FirebugChrome.isOpen || !this.isInitialized) { if (this.isInitialized) { //dispatch(Firebug.modules, "shutdown", []); this.shutdown(); } FirebugChrome.isOpen = false; if (Env.isChromeExtension) localStorage.setItem("Firebug", "1,0"); var node = this.node; if (Firebug.showIconWhenHidden) { node.style.visibility = "hidden"; // Avoid flickering // TODO: xxxpedro - persist IE fixed? var main = $("fbChrome", FirebugChrome.chromeMap.frame.document); main.style.display = "none"; ChromeMini.initialize(); node.style.visibility = "visible"; } else node.style.display = "none"; } }, deactivate: function() { // if it is running as a Chrome extension, dispatch a message to the extension signaling // that Firebug should be deactivated for the current tab if (Env.isChromeExtension) { localStorage.removeItem("Firebug"); Firebug.GoogleChrome.dispatch("FB_deactivate"); // xxxpedro problem here regarding Chrome extension. We can't deactivate the whole // app, otherwise it won't be able to be reactivated without reloading the page. // but we need to stop listening global keys, otherwise the key activation won't work. Firebug.chrome.close(); } else { Firebug.shutdown(); } }, fixIEPosition: function() { // fix IE problem with offset when not in fullscreen mode var doc = this.document; var offset = isIE ? doc.body.clientTop || doc.documentElement.clientTop: 0; var size = Firebug.browser.getWindowSize(); var scroll = Firebug.browser.getWindowScrollPosition(); var maxHeight = size.height; var height = this.node.offsetHeight; var bodyStyle = doc.body.currentStyle; this.node.style.top = maxHeight - height + scroll.top + "px"; if ((this.type == "frame" || this.type == "div") && (bodyStyle.marginLeft || bodyStyle.marginRight)) { this.node.style.width = size.width + "px"; } if (fbVSplitterStyle) fbVSplitterStyle.right = FirebugChrome.sidePanelWidth + "px"; this.draw(); } }); // ************************************************************************************************ // ChromeMini /** * @namespace * @extends FBL.Controller */ var ChromeMini = extend(Controller, /**@extend ns-chrome-ChromeMini*/ { create: function(chrome) { append(this, chrome); this.type = "mini"; }, initialize: function() { Controller.initialize.apply(this); var doc = FirebugChrome.chromeMap.frame.document; var mini = $("fbMiniChrome", doc); mini.style.display = "block"; var miniIcon = $("fbMiniIcon", doc); var width = miniIcon.offsetWidth + 10; miniIcon.title = "Open " + Firebug.version; var errors = $("fbMiniErrors", doc); if (errors.offsetWidth) width += errors.offsetWidth + 10; var node = this.node; node.style.height = "27px"; node.style.width = width + "px"; node.style.left = ""; node.style.right = 0; if (this.node.nodeName.toLowerCase() == "iframe") { node.setAttribute("allowTransparency", "true"); this.document.body.style.backgroundColor = "transparent"; } else node.style.background = "transparent"; if (noFixedPosition) this.fixIEPosition(); this.addController( [$("fbMiniIcon", doc), "click", onMiniIconClick] ); if (noFixedPosition) { this.addController( [Firebug.browser.window, "scroll", this.fixIEPosition] ); } this.isInitialized = true; }, shutdown: function() { var node = this.node; node.style.height = FirebugChrome.height + "px"; node.style.width = "100%"; node.style.left = 0; node.style.right = ""; if (this.node.nodeName.toLowerCase() == "iframe") { node.setAttribute("allowTransparency", "false"); this.document.body.style.backgroundColor = "#fff"; } else node.style.background = "#fff"; if (noFixedPosition) this.fixIEPosition(); var doc = FirebugChrome.chromeMap.frame.document; var mini = $("fbMiniChrome", doc); mini.style.display = "none"; Controller.shutdown.apply(this); this.isInitialized = false; }, draw: function() { }, fixIEPosition: ChromeFrameBase.fixIEPosition }); // ************************************************************************************************ // ChromePopupBase /** * @namespace * @extends ns-chrome-ChromeBase */ var ChromePopupBase = extend(ChromeBase, /**@extend ns-chrome-ChromePopupBase*/ { initialize: function() { setClass(this.document.body, "FirebugPopup"); ChromeBase.initialize.call(this); this.addController( [Firebug.chrome.window, "resize", this.resize], [Firebug.chrome.window, "unload", this.destroy] ); if (Env.Options.enablePersistent) { this.persist = bind(this.persist, this); addEvent(Firebug.browser.window, "unload", this.persist); } else this.addController( [Firebug.browser.window, "unload", this.close] ); fbVSplitter.onmousedown = onVSplitterMouseDown; }, destroy: function() { // TODO: xxxpedro sync detach reattach attach var frame = FirebugChrome.chromeMap.frame; if(frame) { dispatch(frame.panelMap, "detach", [this, frame]); frame.reattach(this, frame); } if (Env.Options.enablePersistent) { removeEvent(Firebug.browser.window, "unload", this.persist); } ChromeBase.destroy.apply(this); FirebugChrome.chromeMap.popup = null; this.node.close(); }, persist: function() { persistTimeStart = new Date().getTime(); removeEvent(Firebug.browser.window, "unload", this.persist); Firebug.Inspector.destroy(); Firebug.browser.window.FirebugOldBrowser = true; var persistTimeStart = new Date().getTime(); var waitMainWindow = function() { var doc, head; try { if (window.opener && !window.opener.FirebugOldBrowser && (doc = window.opener.document)/* && doc.documentElement && (head = doc.documentElement.firstChild)*/) { try { // exposes the FBL to the global namespace when in debug mode if (Env.isDebugMode) { window.FBL = FBL; } window.Firebug = Firebug; window.opener.Firebug = Firebug; Env.browser = window.opener; Firebug.browser = Firebug.context = new Context(Env.browser); registerConsole(); // the delay time should be calculated right after registering the // console, once right after the console registration, call log messages // will be properly handled var persistDelay = new Date().getTime() - persistTimeStart; var chrome = Firebug.chrome; addEvent(Firebug.browser.window, "unload", chrome.persist); FBL.cacheDocument(); Firebug.Inspector.create(); var htmlPanel = chrome.getPanel("HTML"); htmlPanel.createUI(); Firebug.Console.logFormatted( ["Firebug could not capture console calls during " + persistDelay + "ms"], Firebug.context, "info" ); } catch(pE) { alert("persist error: " + (pE.message || pE)); } } else { window.setTimeout(waitMainWindow, 0); } } catch (E) { window.close(); } }; waitMainWindow(); }, close: function() { this.destroy(); } }); //************************************************************************************************ // UI helpers var changeCommandLineVisibility = function changeCommandLineVisibility(visibility) { var last = Firebug.chrome.commandLineVisible; var visible = Firebug.chrome.commandLineVisible = typeof visibility == "boolean" ? visibility : !Firebug.chrome.commandLineVisible; if (visible != last) { if (visible) { fbBottom.className = ""; if (Firebug.CommandLine) Firebug.CommandLine.activate(); } else { if (Firebug.CommandLine) Firebug.CommandLine.deactivate(); fbBottom.className = "hide"; } } }; var changeSidePanelVisibility = function changeSidePanelVisibility(visibility) { var last = Firebug.chrome.sidePanelVisible; Firebug.chrome.sidePanelVisible = typeof visibility == "boolean" ? visibility : !Firebug.chrome.sidePanelVisible; if (Firebug.chrome.sidePanelVisible != last) { fbPanelBox2.className = Firebug.chrome.sidePanelVisible ? "" : "hide"; fbPanelBar2Box.className = Firebug.chrome.sidePanelVisible ? "" : "hide"; } }; // ************************************************************************************************ // F12 Handler var onGlobalKeyDown = function onGlobalKeyDown(event) { var keyCode = event.keyCode; var shiftKey = event.shiftKey; var ctrlKey = event.ctrlKey; if (keyCode == 123 /* F12 */ && (!isFirefox && !shiftKey || shiftKey && isFirefox)) { Firebug.chrome.toggle(false, ctrlKey); cancelEvent(event, true); // TODO: xxxpedro replace with a better solution. we're doing this // to allow reactivating with the F12 key after being deactivated if (Env.isChromeExtension) { Firebug.GoogleChrome.dispatch("FB_enableIcon"); } } else if (keyCode == 67 /* C */ && ctrlKey && shiftKey) { Firebug.Inspector.toggleInspect(); cancelEvent(event, true); } else if (keyCode == 76 /* L */ && ctrlKey && shiftKey) { Firebug.chrome.focusCommandLine(); cancelEvent(event, true); } }; var onMiniIconClick = function onMiniIconClick(event) { Firebug.chrome.toggle(false, event.ctrlKey); cancelEvent(event, true); }; // ************************************************************************************************ // Horizontal Splitter Handling var onHSplitterMouseDown = function onHSplitterMouseDown(event) { addGlobalEvent("mousemove", onHSplitterMouseMove); addGlobalEvent("mouseup", onHSplitterMouseUp); if (isIE) addEvent(Firebug.browser.document.documentElement, "mouseleave", onHSplitterMouseUp); fbHSplitter.className = "fbOnMovingHSplitter"; return false; }; var onHSplitterMouseMove = function onHSplitterMouseMove(event) { cancelEvent(event, true); var clientY = event.clientY; var win = isIE ? event.srcElement.ownerDocument.parentWindow : event.target.ownerDocument && event.target.ownerDocument.defaultView; if (!win) return; if (win != win.parent) { var frameElement = win.frameElement; if (frameElement) { var framePos = Firebug.browser.getElementPosition(frameElement).top; clientY += framePos; if (frameElement.style.position != "fixed") clientY -= Firebug.browser.getWindowScrollPosition().top; } } if (isOpera && isQuiksMode && win.frameElement.id == "FirebugUI") { clientY = Firebug.browser.getWindowSize().height - win.frameElement.offsetHeight + clientY; } /* console.log( typeof win.FBL != "undefined" ? "no-Chrome" : "Chrome", //win.frameElement.id, event.target, clientY );/**/ onHSplitterMouseMoveBuffer = clientY; // buffer if (new Date().getTime() - lastHSplitterMouseMove > chromeRedrawSkipRate) // frame skipping { lastHSplitterMouseMove = new Date().getTime(); handleHSplitterMouseMove(); } else if (!onHSplitterMouseMoveTimer) onHSplitterMouseMoveTimer = setTimeout(handleHSplitterMouseMove, chromeRedrawSkipRate); // improving the resizing performance by canceling the mouse event. // canceling events will prevent the page to receive such events, which would imply // in more processing being expended. cancelEvent(event, true); return false; }; var handleHSplitterMouseMove = function() { if (onHSplitterMouseMoveTimer) { clearTimeout(onHSplitterMouseMoveTimer); onHSplitterMouseMoveTimer = null; } var clientY = onHSplitterMouseMoveBuffer; var windowSize = Firebug.browser.getWindowSize(); var scrollSize = Firebug.browser.getWindowScrollSize(); // compute chrome fixed size (top bar and command line) var commandLineHeight = Firebug.chrome.commandLineVisible ? fbCommandLine.offsetHeight : 0; var fixedHeight = topHeight + commandLineHeight; var chromeNode = Firebug.chrome.node; var scrollbarSize = !isIE && (scrollSize.width > windowSize.width) ? 17 : 0; //var height = !isOpera ? chromeNode.offsetTop + chromeNode.clientHeight : windowSize.height; var height = windowSize.height; // compute the min and max size of the chrome var chromeHeight = Math.max(height - clientY + 5 - scrollbarSize, fixedHeight); chromeHeight = Math.min(chromeHeight, windowSize.height - scrollbarSize); FirebugChrome.height = chromeHeight; chromeNode.style.height = chromeHeight + "px"; if (noFixedPosition) Firebug.chrome.fixIEPosition(); Firebug.chrome.draw(); }; var onHSplitterMouseUp = function onHSplitterMouseUp(event) { removeGlobalEvent("mousemove", onHSplitterMouseMove); removeGlobalEvent("mouseup", onHSplitterMouseUp); if (isIE) removeEvent(Firebug.browser.document.documentElement, "mouseleave", onHSplitterMouseUp); fbHSplitter.className = ""; Firebug.chrome.draw(); // avoid text selection in IE when returning to the document // after the mouse leaves the document during the resizing return false; }; // ************************************************************************************************ // Vertical Splitter Handling var onVSplitterMouseDown = function onVSplitterMouseDown(event) { addGlobalEvent("mousemove", onVSplitterMouseMove); addGlobalEvent("mouseup", onVSplitterMouseUp); return false; }; var onVSplitterMouseMove = function onVSplitterMouseMove(event) { if (new Date().getTime() - lastVSplitterMouseMove > chromeRedrawSkipRate) // frame skipping { var target = event.target || event.srcElement; if (target && target.ownerDocument) // avoid error when cursor reaches out of the chrome { var clientX = event.clientX; var win = document.all ? event.srcElement.ownerDocument.parentWindow : event.target.ownerDocument.defaultView; if (win != win.parent) clientX += win.frameElement ? win.frameElement.offsetLeft : 0; var size = Firebug.chrome.getSize(); var x = Math.max(size.width - clientX + 3, 6); FirebugChrome.sidePanelWidth = x; Firebug.chrome.draw(); } lastVSplitterMouseMove = new Date().getTime(); } cancelEvent(event, true); return false; }; var onVSplitterMouseUp = function onVSplitterMouseUp(event) { removeGlobalEvent("mousemove", onVSplitterMouseMove); removeGlobalEvent("mouseup", onVSplitterMouseUp); Firebug.chrome.draw(); }; // ************************************************************************************************ }}); /* See license.txt for terms of usage */ FBL.ns(function() { with (FBL) { // ************************************************************************************************ Firebug.Lite = { }; // ************************************************************************************************ }}); /* See license.txt for terms of usage */ FBL.ns(function() { with (FBL) { // ************************************************************************************************ Firebug.Lite.Browser = function(window) { this.contentWindow = window; this.contentDocument = window.document; this.currentURI = { spec: window.location.href }; }; Firebug.Lite.Browser.prototype = { toString: function() { return "Firebug.Lite.Browser"; } }; // ************************************************************************************************ }}); /* See license.txt for terms of usage */ FBL.ns(function() { with (FBL) { // ************************************************************************************************ Firebug.Lite.Cache = { ID: "firebug-" + new Date().getTime() }; // ************************************************************************************************ /** * TODO: if a cached element is cloned, the expando property will be cloned too in IE * which will result in a bug. Firebug Lite will think the new cloned node is the old * one. * * TODO: Investigate a possibility of cache validation, to be customized by each * kind of cache. For ElementCache it should validate if the element still is * inserted at the DOM. */ var cacheUID = 0; var createCache = function() { var map = {}; var data = {}; var CID = Firebug.Lite.Cache.ID; // better detection var supportsDeleteExpando = !document.all; var cacheFunction = function(element) { return cacheAPI.set(element); }; var cacheAPI = { get: function(key) { return map.hasOwnProperty(key) ? map[key] : null; }, set: function(element) { var id = getValidatedKey(element); if (!id) { id = ++cacheUID; element[CID] = id; } if (!map.hasOwnProperty(id)) { map[id] = element; data[id] = {}; } return id; }, unset: function(element) { var id = getValidatedKey(element); if (!id) return; if (supportsDeleteExpando) { delete element[CID]; } else if (element.removeAttribute) { element.removeAttribute(CID); } delete map[id]; delete data[id]; }, key: function(element) { return getValidatedKey(element); }, has: function(element) { var id = getValidatedKey(element); return id && map.hasOwnProperty(id); }, each: function(callback) { for (var key in map) { if (map.hasOwnProperty(key)) { callback(key, map[key]); } } }, data: function(element, name, value) { // set data if (value) { if (!name) return null; var id = cacheAPI.set(element); return data[id][name] = value; } // get data else { var id = cacheAPI.key(element); return data.hasOwnProperty(id) && data[id].hasOwnProperty(name) ? data[id][name] : null; } }, clear: function() { for (var id in map) { var element = map[id]; cacheAPI.unset(element); } } }; var getValidatedKey = function(element) { var id = element[CID]; // If a cached element is cloned in IE, the expando property CID will be also // cloned (differently than other browsers) resulting in a bug: Firebug Lite // will think the new cloned node is the old one. To prevent this problem we're // checking if the cached element matches the given element. if ( !supportsDeleteExpando && // the problem happens when supportsDeleteExpando is false id && // the element has the expando property map.hasOwnProperty(id) && // there is a cached element with the same id map[id] != element // but it is a different element than the current one ) { // remove the problematic property element.removeAttribute(CID); id = null; } return id; } FBL.append(cacheFunction, cacheAPI); return cacheFunction; }; // ************************************************************************************************ // TODO: xxxpedro : check if we need really this on FBL scope Firebug.Lite.Cache.StyleSheet = createCache(); Firebug.Lite.Cache.Element = createCache(); // TODO: xxxpedro Firebug.Lite.Cache.Event = createCache(); // ************************************************************************************************ }}); /* See license.txt for terms of usage */ FBL.ns(function() { with (FBL) { // ************************************************************************************************ Firebug.Lite.Proxy = { // jsonp callbacks _callbacks: {}, /** * Load a resource, either locally (directly) or externally (via proxy) using * synchronous XHR calls. Loading external resources requires the proxy plugin to * be installed and configured (see /plugin/proxy/proxy.php). */ load: function(url) { var resourceDomain = getDomain(url); var isLocalResource = // empty domain means local URL !resourceDomain || // same domain means local too resourceDomain == Firebug.context.window.location.host; // TODO: xxxpedro context return isLocalResource ? fetchResource(url) : fetchProxyResource(url); }, /** * Load a resource using JSONP technique. */ loadJSONP: function(url, callback) { var script = createGlobalElement("script"), doc = Firebug.context.document, uid = "" + new Date().getTime(), callbackName = "callback=Firebug.Lite.Proxy._callbacks." + uid, jsonpURL = url.indexOf("?") != -1 ? url + "&" + callbackName : url + "?" + callbackName; Firebug.Lite.Proxy._callbacks[uid] = function(data) { if (callback) callback(data); script.parentNode.removeChild(script); delete Firebug.Lite.Proxy._callbacks[uid]; }; script.src = jsonpURL; if (doc.documentElement) doc.documentElement.appendChild(script); }, /** * Load a resource using YQL (not reliable). */ YQL: function(url, callback) { var yql = "http://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20html%20where%20url%3D%22" + encodeURIComponent(url) + "%22&format=xml"; this.loadJSONP(yql, function(data) { var source = data.results[0]; // clean up YQL bogus elements var match = /\s+

([\s\S]+)<\/p>\s+<\/body>$/.exec(source); if (match) source = match[1]; console.log(source); }); } }; // ************************************************************************************************ var fetchResource = function(url) { var xhr = FBL.Ajax.getXHRObject(); xhr.open("get", url, false); xhr.send(); return xhr.responseText; }; var fetchProxyResource = function(url) { var proxyURL = Env.Location.baseDir + "plugin/proxy/proxy.php?url=" + encodeURIComponent(url); var response = fetchResource(proxyURL); try { var data = eval("(" + response + ")"); } catch(E) { return "ERROR: Firebug Lite Proxy plugin returned an invalid response."; } return data ? data.contents : ""; }; // ************************************************************************************************ }}); /* See license.txt for terms of usage */ FBL.ns(function() { with (FBL) { // ************************************************************************************************ Firebug.Lite.Script = function(window) { this.fileName = null; this.isValid = null; this.baseLineNumber = null; this.lineExtent = null; this.tag = null; this.functionName = null; this.functionSource = null; }; Firebug.Lite.Script.prototype = { isLineExecutable: function(){}, pcToLine: function(){}, lineToPc: function(){}, toString: function() { return "Firebug.Lite.Script"; } }; // ************************************************************************************************ }}); /* See license.txt for terms of usage */ FBL.ns(function() { with (FBL) { // ************************************************************************************************ Firebug.Lite.Style = { }; // ************************************************************************************************ }}); /* See license.txt for terms of usage */ FBL.ns( /**@scope s_selector*/ function() { with (FBL) { // ************************************************************************************************ /* * Sizzle CSS Selector Engine - v1.0 * Copyright 2009, The Dojo Foundation * Released under the MIT, BSD, and GPL Licenses. * More information: http://sizzlejs.com/ */ var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g, done = 0, toString = Object.prototype.toString, hasDuplicate = false, baseHasDuplicate = true; // Here we check if the JavaScript engine is using some sort of // optimization where it does not always call our comparision // function. If that is the case, discard the hasDuplicate value. // Thus far that includes Google Chrome. [0, 0].sort(function(){ baseHasDuplicate = false; return 0; }); /** * @name Firebug.Selector * @namespace */ /** * @exports Sizzle as Firebug.Selector */ var Sizzle = function(selector, context, results, seed) { results = results || []; var origContext = context = context || document; if ( context.nodeType !== 1 && context.nodeType !== 9 ) { return []; } if ( !selector || typeof selector !== "string" ) { return results; } var parts = [], m, set, checkSet, check, mode, extra, prune = true, contextXML = isXML(context), soFar = selector; // Reset the position of the chunker regexp (start from head) while ( (chunker.exec(""), m = chunker.exec(soFar)) !== null ) { soFar = m[3]; parts.push( m[1] ); if ( m[2] ) { extra = m[3]; break; } } if ( parts.length > 1 && origPOS.exec( selector ) ) { if ( parts.length === 2 && Expr.relative[ parts[0] ] ) { set = posProcess( parts[0] + parts[1], context ); } else { set = Expr.relative[ parts[0] ] ? [ context ] : Sizzle( parts.shift(), context ); while ( parts.length ) { selector = parts.shift(); if ( Expr.relative[ selector ] ) selector += parts.shift(); set = posProcess( selector, set ); } } } else { // Take a shortcut and set the context if the root selector is an ID // (but not if it'll be faster if the inner selector is an ID) if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML && Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) { var ret = Sizzle.find( parts.shift(), context, contextXML ); context = ret.expr ? Sizzle.filter( ret.expr, ret.set )[0] : ret.set[0]; } if ( context ) { var ret = seed ? { expr: parts.pop(), set: makeArray(seed) } : Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML ); set = ret.expr ? Sizzle.filter( ret.expr, ret.set ) : ret.set; if ( parts.length > 0 ) { checkSet = makeArray(set); } else { prune = false; } while ( parts.length ) { var cur = parts.pop(), pop = cur; if ( !Expr.relative[ cur ] ) { cur = ""; } else { pop = parts.pop(); } if ( pop == null ) { pop = context; } Expr.relative[ cur ]( checkSet, pop, contextXML ); } } else { checkSet = parts = []; } } if ( !checkSet ) { checkSet = set; } if ( !checkSet ) { throw "Syntax error, unrecognized expression: " + (cur || selector); } if ( toString.call(checkSet) === "[object Array]" ) { if ( !prune ) { results.push.apply( results, checkSet ); } else if ( context && context.nodeType === 1 ) { for ( var i = 0; checkSet[i] != null; i++ ) { if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && contains(context, checkSet[i])) ) { results.push( set[i] ); } } } else { for ( var i = 0; checkSet[i] != null; i++ ) { if ( checkSet[i] && checkSet[i].nodeType === 1 ) { results.push( set[i] ); } } } } else { makeArray( checkSet, results ); } if ( extra ) { Sizzle( extra, origContext, results, seed ); Sizzle.uniqueSort( results ); } return results; }; Sizzle.uniqueSort = function(results){ if ( sortOrder ) { hasDuplicate = baseHasDuplicate; results.sort(sortOrder); if ( hasDuplicate ) { for ( var i = 1; i < results.length; i++ ) { if ( results[i] === results[i-1] ) { results.splice(i--, 1); } } } } return results; }; Sizzle.matches = function(expr, set){ return Sizzle(expr, null, null, set); }; Sizzle.find = function(expr, context, isXML){ var set, match; if ( !expr ) { return []; } for ( var i = 0, l = Expr.order.length; i < l; i++ ) { var type = Expr.order[i], match; if ( (match = Expr.leftMatch[ type ].exec( expr )) ) { var left = match[1]; match.splice(1,1); if ( left.substr( left.length - 1 ) !== "\\" ) { match[1] = (match[1] || "").replace(/\\/g, ""); set = Expr.find[ type ]( match, context, isXML ); if ( set != null ) { expr = expr.replace( Expr.match[ type ], "" ); break; } } } } if ( !set ) { set = context.getElementsByTagName("*"); } return {set: set, expr: expr}; }; Sizzle.filter = function(expr, set, inplace, not){ var old = expr, result = [], curLoop = set, match, anyFound, isXMLFilter = set && set[0] && isXML(set[0]); while ( expr && set.length ) { for ( var type in Expr.filter ) { if ( (match = Expr.match[ type ].exec( expr )) != null ) { var filter = Expr.filter[ type ], found, item; anyFound = false; if ( curLoop == result ) { result = []; } if ( Expr.preFilter[ type ] ) { match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter ); if ( !match ) { anyFound = found = true; } else if ( match === true ) { continue; } } if ( match ) { for ( var i = 0; (item = curLoop[i]) != null; i++ ) { if ( item ) { found = filter( item, match, i, curLoop ); var pass = not ^ !!found; if ( inplace && found != null ) { if ( pass ) { anyFound = true; } else { curLoop[i] = false; } } else if ( pass ) { result.push( item ); anyFound = true; } } } } if ( found !== undefined ) { if ( !inplace ) { curLoop = result; } expr = expr.replace( Expr.match[ type ], "" ); if ( !anyFound ) { return []; } break; } } } // Improper expression if ( expr == old ) { if ( anyFound == null ) { throw "Syntax error, unrecognized expression: " + expr; } else { break; } } old = expr; } return curLoop; }; /**#@+ @ignore */ var Expr = Sizzle.selectors = { order: [ "ID", "NAME", "TAG" ], match: { ID: /#((?:[\w\u00c0-\uFFFF-]|\\.)+)/, CLASS: /\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/, NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/, ATTR: /\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/, TAG: /^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/, CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/, POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/, PSEUDO: /:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/ }, leftMatch: {}, attrMap: { "class": "className", "for": "htmlFor" }, attrHandle: { href: function(elem){ return elem.getAttribute("href"); } }, relative: { "+": function(checkSet, part, isXML){ var isPartStr = typeof part === "string", isTag = isPartStr && !/\W/.test(part), isPartStrNotTag = isPartStr && !isTag; if ( isTag && !isXML ) { part = part.toUpperCase(); } for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) { if ( (elem = checkSet[i]) ) { while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {} checkSet[i] = isPartStrNotTag || elem && elem.nodeName === part ? elem || false : elem === part; } } if ( isPartStrNotTag ) { Sizzle.filter( part, checkSet, true ); } }, ">": function(checkSet, part, isXML){ var isPartStr = typeof part === "string"; if ( isPartStr && !/\W/.test(part) ) { part = isXML ? part : part.toUpperCase(); for ( var i = 0, l = checkSet.length; i < l; i++ ) { var elem = checkSet[i]; if ( elem ) { var parent = elem.parentNode; checkSet[i] = parent.nodeName === part ? parent : false; } } } else { for ( var i = 0, l = checkSet.length; i < l; i++ ) { var elem = checkSet[i]; if ( elem ) { checkSet[i] = isPartStr ? elem.parentNode : elem.parentNode === part; } } if ( isPartStr ) { Sizzle.filter( part, checkSet, true ); } } }, "": function(checkSet, part, isXML){ var doneName = done++, checkFn = dirCheck; if ( !/\W/.test(part) ) { var nodeCheck = part = isXML ? part : part.toUpperCase(); checkFn = dirNodeCheck; } checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML); }, "~": function(checkSet, part, isXML){ var doneName = done++, checkFn = dirCheck; if ( typeof part === "string" && !/\W/.test(part) ) { var nodeCheck = part = isXML ? part : part.toUpperCase(); checkFn = dirNodeCheck; } checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML); } }, find: { ID: function(match, context, isXML){ if ( typeof context.getElementById !== "undefined" && !isXML ) { var m = context.getElementById(match[1]); return m ? [m] : []; } }, NAME: function(match, context, isXML){ if ( typeof context.getElementsByName !== "undefined" ) { var ret = [], results = context.getElementsByName(match[1]); for ( var i = 0, l = results.length; i < l; i++ ) { if ( results[i].getAttribute("name") === match[1] ) { ret.push( results[i] ); } } return ret.length === 0 ? null : ret; } }, TAG: function(match, context){ return context.getElementsByTagName(match[1]); } }, preFilter: { CLASS: function(match, curLoop, inplace, result, not, isXML){ match = " " + match[1].replace(/\\/g, "") + " "; if ( isXML ) { return match; } for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) { if ( elem ) { if ( not ^ (elem.className && (" " + elem.className + " ").indexOf(match) >= 0) ) { if ( !inplace ) result.push( elem ); } else if ( inplace ) { curLoop[i] = false; } } } return false; }, ID: function(match){ return match[1].replace(/\\/g, ""); }, TAG: function(match, curLoop){ for ( var i = 0; curLoop[i] === false; i++ ){} return curLoop[i] && isXML(curLoop[i]) ? match[1] : match[1].toUpperCase(); }, CHILD: function(match){ if ( match[1] == "nth" ) { // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6' var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec( match[2] == "even" && "2n" || match[2] == "odd" && "2n+1" || !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]); // calculate the numbers (first)n+(last) including if they are negative match[2] = (test[1] + (test[2] || 1)) - 0; match[3] = test[3] - 0; } // TODO: Move to normal caching system match[0] = done++; return match; }, ATTR: function(match, curLoop, inplace, result, not, isXML){ var name = match[1].replace(/\\/g, ""); if ( !isXML && Expr.attrMap[name] ) { match[1] = Expr.attrMap[name]; } if ( match[2] === "~=" ) { match[4] = " " + match[4] + " "; } return match; }, PSEUDO: function(match, curLoop, inplace, result, not){ if ( match[1] === "not" ) { // If we're dealing with a complex expression, or a simple one if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) { match[3] = Sizzle(match[3], null, null, curLoop); } else { var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not); if ( !inplace ) { result.push.apply( result, ret ); } return false; } } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) { return true; } return match; }, POS: function(match){ match.unshift( true ); return match; } }, filters: { enabled: function(elem){ return elem.disabled === false && elem.type !== "hidden"; }, disabled: function(elem){ return elem.disabled === true; }, checked: function(elem){ return elem.checked === true; }, selected: function(elem){ // Accessing this property makes selected-by-default // options in Safari work properly elem.parentNode.selectedIndex; return elem.selected === true; }, parent: function(elem){ return !!elem.firstChild; }, empty: function(elem){ return !elem.firstChild; }, has: function(elem, i, match){ return !!Sizzle( match[3], elem ).length; }, header: function(elem){ return /h\d/i.test( elem.nodeName ); }, text: function(elem){ return "text" === elem.type; }, radio: function(elem){ return "radio" === elem.type; }, checkbox: function(elem){ return "checkbox" === elem.type; }, file: function(elem){ return "file" === elem.type; }, password: function(elem){ return "password" === elem.type; }, submit: function(elem){ return "submit" === elem.type; }, image: function(elem){ return "image" === elem.type; }, reset: function(elem){ return "reset" === elem.type; }, button: function(elem){ return "button" === elem.type || elem.nodeName.toUpperCase() === "BUTTON"; }, input: function(elem){ return /input|select|textarea|button/i.test(elem.nodeName); } }, setFilters: { first: function(elem, i){ return i === 0; }, last: function(elem, i, match, array){ return i === array.length - 1; }, even: function(elem, i){ return i % 2 === 0; }, odd: function(elem, i){ return i % 2 === 1; }, lt: function(elem, i, match){ return i < match[3] - 0; }, gt: function(elem, i, match){ return i > match[3] - 0; }, nth: function(elem, i, match){ return match[3] - 0 == i; }, eq: function(elem, i, match){ return match[3] - 0 == i; } }, filter: { PSEUDO: function(elem, match, i, array){ var name = match[1], filter = Expr.filters[ name ]; if ( filter ) { return filter( elem, i, match, array ); } else if ( name === "contains" ) { return (elem.textContent || elem.innerText || "").indexOf(match[3]) >= 0; } else if ( name === "not" ) { var not = match[3]; for ( var i = 0, l = not.length; i < l; i++ ) { if ( not[i] === elem ) { return false; } } return true; } }, CHILD: function(elem, match){ var type = match[1], node = elem; switch (type) { case 'only': case 'first': while ( (node = node.previousSibling) ) { if ( node.nodeType === 1 ) return false; } if ( type == 'first') return true; node = elem; case 'last': while ( (node = node.nextSibling) ) { if ( node.nodeType === 1 ) return false; } return true; case 'nth': var first = match[2], last = match[3]; if ( first == 1 && last == 0 ) { return true; } var doneName = match[0], parent = elem.parentNode; if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) { var count = 0; for ( node = parent.firstChild; node; node = node.nextSibling ) { if ( node.nodeType === 1 ) { node.nodeIndex = ++count; } } parent.sizcache = doneName; } var diff = elem.nodeIndex - last; if ( first == 0 ) { return diff == 0; } else { return ( diff % first == 0 && diff / first >= 0 ); } } }, ID: function(elem, match){ return elem.nodeType === 1 && elem.getAttribute("id") === match; }, TAG: function(elem, match){ return (match === "*" && elem.nodeType === 1) || elem.nodeName === match; }, CLASS: function(elem, match){ return (" " + (elem.className || elem.getAttribute("class")) + " ") .indexOf( match ) > -1; }, ATTR: function(elem, match){ var name = match[1], result = Expr.attrHandle[ name ] ? Expr.attrHandle[ name ]( elem ) : elem[ name ] != null ? elem[ name ] : elem.getAttribute( name ), value = result + "", type = match[2], check = match[4]; return result == null ? type === "!=" : type === "=" ? value === check : type === "*=" ? value.indexOf(check) >= 0 : type === "~=" ? (" " + value + " ").indexOf(check) >= 0 : !check ? value && result !== false : type === "!=" ? value != check : type === "^=" ? value.indexOf(check) === 0 : type === "$=" ? value.substr(value.length - check.length) === check : type === "|=" ? value === check || value.substr(0, check.length + 1) === check + "-" : false; }, POS: function(elem, match, i, array){ var name = match[2], filter = Expr.setFilters[ name ]; if ( filter ) { return filter( elem, i, match, array ); } } } }; var origPOS = Expr.match.POS; for ( var type in Expr.match ) { Expr.match[ type ] = new RegExp( Expr.match[ type ].source + /(?![^\[]*\])(?![^\(]*\))/.source ); Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source ); } var makeArray = function(array, results) { array = Array.prototype.slice.call( array, 0 ); if ( results ) { results.push.apply( results, array ); return results; } return array; }; // Perform a simple check to determine if the browser is capable of // converting a NodeList to an array using builtin methods. try { Array.prototype.slice.call( document.documentElement.childNodes, 0 ); // Provide a fallback method if it does not work } catch(e){ makeArray = function(array, results) { var ret = results || []; if ( toString.call(array) === "[object Array]" ) { Array.prototype.push.apply( ret, array ); } else { if ( typeof array.length === "number" ) { for ( var i = 0, l = array.length; i < l; i++ ) { ret.push( array[i] ); } } else { for ( var i = 0; array[i]; i++ ) { ret.push( array[i] ); } } } return ret; }; } var sortOrder; if ( document.documentElement.compareDocumentPosition ) { sortOrder = function( a, b ) { if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) { if ( a == b ) { hasDuplicate = true; } return 0; } var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1; if ( ret === 0 ) { hasDuplicate = true; } return ret; }; } else if ( "sourceIndex" in document.documentElement ) { sortOrder = function( a, b ) { if ( !a.sourceIndex || !b.sourceIndex ) { if ( a == b ) { hasDuplicate = true; } return 0; } var ret = a.sourceIndex - b.sourceIndex; if ( ret === 0 ) { hasDuplicate = true; } return ret; }; } else if ( document.createRange ) { sortOrder = function( a, b ) { if ( !a.ownerDocument || !b.ownerDocument ) { if ( a == b ) { hasDuplicate = true; } return 0; } var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange(); aRange.setStart(a, 0); aRange.setEnd(a, 0); bRange.setStart(b, 0); bRange.setEnd(b, 0); var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange); if ( ret === 0 ) { hasDuplicate = true; } return ret; }; } // Check to see if the browser returns elements by name when // querying by getElementById (and provide a workaround) (function(){ // We're going to inject a fake input element with a specified name var form = document.createElement("div"), id = "script" + (new Date).getTime(); form.innerHTML = ""; // Inject it into the root element, check its status, and remove it quickly var root = document.documentElement; root.insertBefore( form, root.firstChild ); // The workaround has to do additional checks after a getElementById // Which slows things down for other browsers (hence the branching) if ( !!document.getElementById( id ) ) { Expr.find.ID = function(match, context, isXML){ if ( typeof context.getElementById !== "undefined" && !isXML ) { var m = context.getElementById(match[1]); return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : []; } }; Expr.filter.ID = function(elem, match){ var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id"); return elem.nodeType === 1 && node && node.nodeValue === match; }; } root.removeChild( form ); root = form = null; // release memory in IE })(); (function(){ // Check to see if the browser returns only elements // when doing getElementsByTagName("*") // Create a fake element var div = document.createElement("div"); div.appendChild( document.createComment("") ); // Make sure no comments are found if ( div.getElementsByTagName("*").length > 0 ) { Expr.find.TAG = function(match, context){ var results = context.getElementsByTagName(match[1]); // Filter out possible comments if ( match[1] === "*" ) { var tmp = []; for ( var i = 0; results[i]; i++ ) { if ( results[i].nodeType === 1 ) { tmp.push( results[i] ); } } results = tmp; } return results; }; } // Check to see if an attribute returns normalized href attributes div.innerHTML = ""; if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" && div.firstChild.getAttribute("href") !== "#" ) { Expr.attrHandle.href = function(elem){ return elem.getAttribute("href", 2); }; } div = null; // release memory in IE })(); if ( document.querySelectorAll ) (function(){ var oldSizzle = Sizzle, div = document.createElement("div"); div.innerHTML = "

"; // Safari can't handle uppercase or unicode characters when // in quirks mode. if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) { return; } Sizzle = function(query, context, extra, seed){ context = context || document; // Only use querySelectorAll on non-XML documents // (ID selectors don't work in non-HTML documents) if ( !seed && context.nodeType === 9 && !isXML(context) ) { try { return makeArray( context.querySelectorAll(query), extra ); } catch(e){} } return oldSizzle(query, context, extra, seed); }; for ( var prop in oldSizzle ) { Sizzle[ prop ] = oldSizzle[ prop ]; } div = null; // release memory in IE })(); if ( document.getElementsByClassName && document.documentElement.getElementsByClassName ) (function(){ var div = document.createElement("div"); div.innerHTML = "
"; // Opera can't find a second classname (in 9.6) if ( div.getElementsByClassName("e").length === 0 ) return; // Safari caches class attributes, doesn't catch changes (in 3.2) div.lastChild.className = "e"; if ( div.getElementsByClassName("e").length === 1 ) return; Expr.order.splice(1, 0, "CLASS"); Expr.find.CLASS = function(match, context, isXML) { if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) { return context.getElementsByClassName(match[1]); } }; div = null; // release memory in IE })(); function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { var sibDir = dir == "previousSibling" && !isXML; for ( var i = 0, l = checkSet.length; i < l; i++ ) { var elem = checkSet[i]; if ( elem ) { if ( sibDir && elem.nodeType === 1 ){ elem.sizcache = doneName; elem.sizset = i; } elem = elem[dir]; var match = false; while ( elem ) { if ( elem.sizcache === doneName ) { match = checkSet[elem.sizset]; break; } if ( elem.nodeType === 1 && !isXML ){ elem.sizcache = doneName; elem.sizset = i; } if ( elem.nodeName === cur ) { match = elem; break; } elem = elem[dir]; } checkSet[i] = match; } } } function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { var sibDir = dir == "previousSibling" && !isXML; for ( var i = 0, l = checkSet.length; i < l; i++ ) { var elem = checkSet[i]; if ( elem ) { if ( sibDir && elem.nodeType === 1 ) { elem.sizcache = doneName; elem.sizset = i; } elem = elem[dir]; var match = false; while ( elem ) { if ( elem.sizcache === doneName ) { match = checkSet[elem.sizset]; break; } if ( elem.nodeType === 1 ) { if ( !isXML ) { elem.sizcache = doneName; elem.sizset = i; } if ( typeof cur !== "string" ) { if ( elem === cur ) { match = true; break; } } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) { match = elem; break; } } elem = elem[dir]; } checkSet[i] = match; } } } var contains = document.compareDocumentPosition ? function(a, b){ return a.compareDocumentPosition(b) & 16; } : function(a, b){ return a !== b && (a.contains ? a.contains(b) : true); }; var isXML = function(elem){ return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" || !!elem.ownerDocument && elem.ownerDocument.documentElement.nodeName !== "HTML"; }; var posProcess = function(selector, context){ var tmpSet = [], later = "", match, root = context.nodeType ? [context] : context; // Position selectors must be done after the filter // And so must :not(positional) so we move all PSEUDOs to the end while ( (match = Expr.match.PSEUDO.exec( selector )) ) { later += match[0]; selector = selector.replace( Expr.match.PSEUDO, "" ); } selector = Expr.relative[selector] ? selector + "*" : selector; for ( var i = 0, l = root.length; i < l; i++ ) { Sizzle( selector, root[i], tmpSet ); } return Sizzle.filter( later, tmpSet ); }; // EXPOSE Firebug.Selector = Sizzle; /**#@-*/ // ************************************************************************************************ }}); // Problems in IE // FIXED - eval return // FIXED - addEventListener problem in IE // FIXED doc.createRange? // // class reserved word // test all honza examples in IE6 and IE7 /* See license.txt for terms of usage */ ( /** @scope s_domplate */ function() { // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /** @class */ FBL.DomplateTag = function DomplateTag(tagName) { this.tagName = tagName; }; /** * @class * @extends FBL.DomplateTag */ FBL.DomplateEmbed = function DomplateEmbed() { }; /** * @class * @extends FBL.DomplateTag */ FBL.DomplateLoop = function DomplateLoop() { }; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * var DomplateTag = FBL.DomplateTag; var DomplateEmbed = FBL.DomplateEmbed; var DomplateLoop = FBL.DomplateLoop; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * var womb = null; FBL.domplate = function() { var lastSubject; for (var i = 0; i < arguments.length; ++i) lastSubject = lastSubject ? copyObject(lastSubject, arguments[i]) : arguments[i]; for (var name in lastSubject) { var val = lastSubject[name]; if (isTag(val)) val.tag.subject = lastSubject; } return lastSubject; }; var domplate = FBL.domplate; FBL.domplate.context = function(context, fn) { var lastContext = domplate.lastContext; domplate.topContext = context; fn.apply(context); domplate.topContext = lastContext; }; FBL.TAG = function() { var embed = new DomplateEmbed(); return embed.merge(arguments); }; FBL.FOR = function() { var loop = new DomplateLoop(); return loop.merge(arguments); }; FBL.DomplateTag.prototype = { merge: function(args, oldTag) { if (oldTag) this.tagName = oldTag.tagName; this.context = oldTag ? oldTag.context : null; this.subject = oldTag ? oldTag.subject : null; this.attrs = oldTag ? copyObject(oldTag.attrs) : {}; this.classes = oldTag ? copyObject(oldTag.classes) : {}; this.props = oldTag ? copyObject(oldTag.props) : null; this.listeners = oldTag ? copyArray(oldTag.listeners) : null; this.children = oldTag ? copyArray(oldTag.children) : []; this.vars = oldTag ? copyArray(oldTag.vars) : []; var attrs = args.length ? args[0] : null; var hasAttrs = typeof(attrs) == "object" && !isTag(attrs); this.children = []; if (domplate.topContext) this.context = domplate.topContext; if (args.length) parseChildren(args, hasAttrs ? 1 : 0, this.vars, this.children); if (hasAttrs) this.parseAttrs(attrs); return creator(this, DomplateTag); }, parseAttrs: function(args) { for (var name in args) { var val = parseValue(args[name]); readPartNames(val, this.vars); if (name.indexOf("on") == 0) { var eventName = name.substr(2); if (!this.listeners) this.listeners = []; this.listeners.push(eventName, val); } else if (name.indexOf("_") == 0) { var propName = name.substr(1); if (!this.props) this.props = {}; this.props[propName] = val; } else if (name.indexOf("$") == 0) { var className = name.substr(1); if (!this.classes) this.classes = {}; this.classes[className] = val; } else { if (name == "class" && this.attrs.hasOwnProperty(name) ) this.attrs[name] += " " + val; else this.attrs[name] = val; } } }, compile: function() { if (this.renderMarkup) return; this.compileMarkup(); this.compileDOM(); //if (FBTrace.DBG_DOM) FBTrace.sysout("domplate renderMarkup: ", this.renderMarkup); //if (FBTrace.DBG_DOM) FBTrace.sysout("domplate renderDOM:", this.renderDOM); //if (FBTrace.DBG_DOM) FBTrace.sysout("domplate domArgs:", this.domArgs); }, compileMarkup: function() { this.markupArgs = []; var topBlock = [], topOuts = [], blocks = [], info = {args: this.markupArgs, argIndex: 0}; this.generateMarkup(topBlock, topOuts, blocks, info); this.addCode(topBlock, topOuts, blocks); var fnBlock = ['r=(function (__code__, __context__, __in__, __out__']; for (var i = 0; i < info.argIndex; ++i) fnBlock.push(', s', i); fnBlock.push(') {'); if (this.subject) fnBlock.push('with (this) {'); if (this.context) fnBlock.push('with (__context__) {'); fnBlock.push('with (__in__) {'); fnBlock.push.apply(fnBlock, blocks); if (this.subject) fnBlock.push('}'); if (this.context) fnBlock.push('}'); fnBlock.push('}})'); function __link__(tag, code, outputs, args) { if (!tag || !tag.tag) return; tag.tag.compile(); var tagOutputs = []; var markupArgs = [code, tag.tag.context, args, tagOutputs]; markupArgs.push.apply(markupArgs, tag.tag.markupArgs); tag.tag.renderMarkup.apply(tag.tag.subject, markupArgs); outputs.push(tag); outputs.push(tagOutputs); } function __escape__(value) { function replaceChars(ch) { switch (ch) { case "<": return "<"; case ">": return ">"; case "&": return "&"; case "'": return "'"; case '"': return """; } return "?"; }; return String(value).replace(/[<>&"']/g, replaceChars); } function __loop__(iter, outputs, fn) { var iterOuts = []; outputs.push(iterOuts); if (iter instanceof Array) iter = new ArrayIterator(iter); try { while (1) { var value = iter.next(); var itemOuts = [0,0]; iterOuts.push(itemOuts); fn.apply(this, [value, itemOuts]); } } catch (exc) { if (exc != StopIteration) throw exc; } } var js = fnBlock.join(""); var r = null; eval(js); this.renderMarkup = r; }, getVarNames: function(args) { if (this.vars) args.push.apply(args, this.vars); for (var i = 0; i < this.children.length; ++i) { var child = this.children[i]; if (isTag(child)) child.tag.getVarNames(args); else if (child instanceof Parts) { for (var i = 0; i < child.parts.length; ++i) { if (child.parts[i] instanceof Variable) { var name = child.parts[i].name; var names = name.split("."); args.push(names[0]); } } } } }, generateMarkup: function(topBlock, topOuts, blocks, info) { topBlock.push(',"<', this.tagName, '"'); for (var name in this.attrs) { if (name != "class") { var val = this.attrs[name]; topBlock.push(', " ', name, '=\\""'); addParts(val, ',', topBlock, info, true); topBlock.push(', "\\""'); } } if (this.listeners) { for (var i = 0; i < this.listeners.length; i += 2) readPartNames(this.listeners[i+1], topOuts); } if (this.props) { for (var name in this.props) readPartNames(this.props[name], topOuts); } if ( this.attrs.hasOwnProperty("class") || this.classes) { topBlock.push(', " class=\\""'); if (this.attrs.hasOwnProperty("class")) addParts(this.attrs["class"], ',', topBlock, info, true); topBlock.push(', " "'); for (var name in this.classes) { topBlock.push(', ('); addParts(this.classes[name], '', topBlock, info); topBlock.push(' ? "', name, '" + " " : "")'); } topBlock.push(', "\\""'); } topBlock.push(',">"'); this.generateChildMarkup(topBlock, topOuts, blocks, info); topBlock.push(',""'); }, generateChildMarkup: function(topBlock, topOuts, blocks, info) { for (var i = 0; i < this.children.length; ++i) { var child = this.children[i]; if (isTag(child)) child.tag.generateMarkup(topBlock, topOuts, blocks, info); else addParts(child, ',', topBlock, info, true); } }, addCode: function(topBlock, topOuts, blocks) { if (topBlock.length) blocks.push('__code__.push(""', topBlock.join(""), ');'); if (topOuts.length) blocks.push('__out__.push(', topOuts.join(","), ');'); topBlock.splice(0, topBlock.length); topOuts.splice(0, topOuts.length); }, addLocals: function(blocks) { var varNames = []; this.getVarNames(varNames); var map = {}; for (var i = 0; i < varNames.length; ++i) { var name = varNames[i]; if ( map.hasOwnProperty(name) ) continue; map[name] = 1; var names = name.split("."); blocks.push('var ', names[0] + ' = ' + '__in__.' + names[0] + ';'); } }, compileDOM: function() { var path = []; var blocks = []; this.domArgs = []; path.embedIndex = 0; path.loopIndex = 0; path.staticIndex = 0; path.renderIndex = 0; var nodeCount = this.generateDOM(path, blocks, this.domArgs); var fnBlock = ['r=(function (root, context, o']; for (var i = 0; i < path.staticIndex; ++i) fnBlock.push(', ', 's'+i); for (var i = 0; i < path.renderIndex; ++i) fnBlock.push(', ', 'd'+i); fnBlock.push(') {'); for (var i = 0; i < path.loopIndex; ++i) fnBlock.push('var l', i, ' = 0;'); for (var i = 0; i < path.embedIndex; ++i) fnBlock.push('var e', i, ' = 0;'); if (this.subject) fnBlock.push('with (this) {'); if (this.context) fnBlock.push('with (context) {'); fnBlock.push(blocks.join("")); if (this.subject) fnBlock.push('}'); if (this.context) fnBlock.push('}'); fnBlock.push('return ', nodeCount, ';'); fnBlock.push('})'); function __bind__(object, fn) { return function(event) { return fn.apply(object, [event]); }; } function __link__(node, tag, args) { if (!tag || !tag.tag) return; tag.tag.compile(); var domArgs = [node, tag.tag.context, 0]; domArgs.push.apply(domArgs, tag.tag.domArgs); domArgs.push.apply(domArgs, args); //if (FBTrace.DBG_DOM) FBTrace.dumpProperties("domplate__link__ domArgs:", domArgs); return tag.tag.renderDOM.apply(tag.tag.subject, domArgs); } var self = this; function __loop__(iter, fn) { var nodeCount = 0; for (var i = 0; i < iter.length; ++i) { iter[i][0] = i; iter[i][1] = nodeCount; nodeCount += fn.apply(this, iter[i]); //if (FBTrace.DBG_DOM) FBTrace.sysout("nodeCount", nodeCount); } return nodeCount; } function __path__(parent, offset) { //if (FBTrace.DBG_DOM) FBTrace.sysout("domplate __path__ offset: "+ offset+"\n"); var root = parent; for (var i = 2; i < arguments.length; ++i) { var index = arguments[i]; if (i == 3) index += offset; if (index == -1) parent = parent.parentNode; else parent = parent.childNodes[index]; } //if (FBTrace.DBG_DOM) FBTrace.sysout("domplate: "+arguments[2]+", root: "+ root+", parent: "+ parent+"\n"); return parent; } var js = fnBlock.join(""); //if (FBTrace.DBG_DOM) FBTrace.sysout(js.replace(/(\;|\{)/g, "$1\n")); var r = null; eval(js); this.renderDOM = r; }, generateDOM: function(path, blocks, args) { if (this.listeners || this.props) this.generateNodePath(path, blocks); if (this.listeners) { for (var i = 0; i < this.listeners.length; i += 2) { var val = this.listeners[i+1]; var arg = generateArg(val, path, args); //blocks.push('node.addEventListener("', this.listeners[i], '", __bind__(this, ', arg, '), false);'); blocks.push('addEvent(node, "', this.listeners[i], '", __bind__(this, ', arg, '), false);'); } } if (this.props) { for (var name in this.props) { var val = this.props[name]; var arg = generateArg(val, path, args); blocks.push('node.', name, ' = ', arg, ';'); } } this.generateChildDOM(path, blocks, args); return 1; }, generateNodePath: function(path, blocks) { blocks.push("var node = __path__(root, o"); for (var i = 0; i < path.length; ++i) blocks.push(",", path[i]); blocks.push(");"); }, generateChildDOM: function(path, blocks, args) { path.push(0); for (var i = 0; i < this.children.length; ++i) { var child = this.children[i]; if (isTag(child)) path[path.length-1] += '+' + child.tag.generateDOM(path, blocks, args); else path[path.length-1] += '+1'; } path.pop(); } }; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * FBL.DomplateEmbed.prototype = copyObject(FBL.DomplateTag.prototype, /** @lends FBL.DomplateEmbed.prototype */ { merge: function(args, oldTag) { this.value = oldTag ? oldTag.value : parseValue(args[0]); this.attrs = oldTag ? oldTag.attrs : {}; this.vars = oldTag ? copyArray(oldTag.vars) : []; var attrs = args[1]; for (var name in attrs) { var val = parseValue(attrs[name]); this.attrs[name] = val; readPartNames(val, this.vars); } return creator(this, DomplateEmbed); }, getVarNames: function(names) { if (this.value instanceof Parts) names.push(this.value.parts[0].name); if (this.vars) names.push.apply(names, this.vars); }, generateMarkup: function(topBlock, topOuts, blocks, info) { this.addCode(topBlock, topOuts, blocks); blocks.push('__link__('); addParts(this.value, '', blocks, info); blocks.push(', __code__, __out__, {'); var lastName = null; for (var name in this.attrs) { if (lastName) blocks.push(','); lastName = name; var val = this.attrs[name]; blocks.push('"', name, '":'); addParts(val, '', blocks, info); } blocks.push('});'); //this.generateChildMarkup(topBlock, topOuts, blocks, info); }, generateDOM: function(path, blocks, args) { var embedName = 'e'+path.embedIndex++; this.generateNodePath(path, blocks); var valueName = 'd' + path.renderIndex++; var argsName = 'd' + path.renderIndex++; blocks.push(embedName + ' = __link__(node, ', valueName, ', ', argsName, ');'); return embedName; } }); // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * FBL.DomplateLoop.prototype = copyObject(FBL.DomplateTag.prototype, /** @lends FBL.DomplateLoop.prototype */ { merge: function(args, oldTag) { this.varName = oldTag ? oldTag.varName : args[0]; this.iter = oldTag ? oldTag.iter : parseValue(args[1]); this.vars = []; this.children = oldTag ? copyArray(oldTag.children) : []; var offset = Math.min(args.length, 2); parseChildren(args, offset, this.vars, this.children); return creator(this, DomplateLoop); }, getVarNames: function(names) { if (this.iter instanceof Parts) names.push(this.iter.parts[0].name); DomplateTag.prototype.getVarNames.apply(this, [names]); }, generateMarkup: function(topBlock, topOuts, blocks, info) { this.addCode(topBlock, topOuts, blocks); var iterName; if (this.iter instanceof Parts) { var part = this.iter.parts[0]; iterName = part.name; if (part.format) { for (var i = 0; i < part.format.length; ++i) iterName = part.format[i] + "(" + iterName + ")"; } } else iterName = this.iter; blocks.push('__loop__.apply(this, [', iterName, ', __out__, function(', this.varName, ', __out__) {'); this.generateChildMarkup(topBlock, topOuts, blocks, info); this.addCode(topBlock, topOuts, blocks); blocks.push('}]);'); }, generateDOM: function(path, blocks, args) { var iterName = 'd'+path.renderIndex++; var counterName = 'i'+path.loopIndex; var loopName = 'l'+path.loopIndex++; if (!path.length) path.push(-1, 0); var preIndex = path.renderIndex; path.renderIndex = 0; var nodeCount = 0; var subBlocks = []; var basePath = path[path.length-1]; for (var i = 0; i < this.children.length; ++i) { path[path.length-1] = basePath+'+'+loopName+'+'+nodeCount; var child = this.children[i]; if (isTag(child)) nodeCount += '+' + child.tag.generateDOM(path, subBlocks, args); else nodeCount += '+1'; } path[path.length-1] = basePath+'+'+loopName; blocks.push(loopName,' = __loop__.apply(this, [', iterName, ', function(', counterName,',',loopName); for (var i = 0; i < path.renderIndex; ++i) blocks.push(',d'+i); blocks.push(') {'); blocks.push(subBlocks.join("")); blocks.push('return ', nodeCount, ';'); blocks.push('}]);'); path.renderIndex = preIndex; return loopName; } }); // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /** @class */ function Variable(name, format) { this.name = name; this.format = format; } /** @class */ function Parts(parts) { this.parts = parts; } // ************************************************************************************************ function parseParts(str) { var re = /\$([_A-Za-z][_A-Za-z0-9.|]*)/g; var index = 0; var parts = []; var m; while (m = re.exec(str)) { var pre = str.substr(index, (re.lastIndex-m[0].length)-index); if (pre) parts.push(pre); var expr = m[1].split("|"); parts.push(new Variable(expr[0], expr.slice(1))); index = re.lastIndex; } if (!index) return str; var post = str.substr(index); if (post) parts.push(post); return new Parts(parts); } function parseValue(val) { return typeof(val) == 'string' ? parseParts(val) : val; } function parseChildren(args, offset, vars, children) { for (var i = offset; i < args.length; ++i) { var val = parseValue(args[i]); children.push(val); readPartNames(val, vars); } } function readPartNames(val, vars) { if (val instanceof Parts) { for (var i = 0; i < val.parts.length; ++i) { var part = val.parts[i]; if (part instanceof Variable) vars.push(part.name); } } } function generateArg(val, path, args) { if (val instanceof Parts) { var vals = []; for (var i = 0; i < val.parts.length; ++i) { var part = val.parts[i]; if (part instanceof Variable) { var varName = 'd'+path.renderIndex++; if (part.format) { for (var j = 0; j < part.format.length; ++j) varName = part.format[j] + '(' + varName + ')'; } vals.push(varName); } else vals.push('"'+part.replace(/"/g, '\\"')+'"'); } return vals.join('+'); } else { args.push(val); return 's' + path.staticIndex++; } } function addParts(val, delim, block, info, escapeIt) { var vals = []; if (val instanceof Parts) { for (var i = 0; i < val.parts.length; ++i) { var part = val.parts[i]; if (part instanceof Variable) { var partName = part.name; if (part.format) { for (var j = 0; j < part.format.length; ++j) partName = part.format[j] + "(" + partName + ")"; } if (escapeIt) vals.push("__escape__(" + partName + ")"); else vals.push(partName); } else vals.push('"'+ part + '"'); } } else if (isTag(val)) { info.args.push(val); vals.push('s'+info.argIndex++); } else vals.push('"'+ val + '"'); var parts = vals.join(delim); if (parts) block.push(delim, parts); } function isTag(obj) { return (typeof(obj) == "function" || obj instanceof Function) && !!obj.tag; } function creator(tag, cons) { var fn = new Function( "var tag = arguments.callee.tag;" + "var cons = arguments.callee.cons;" + "var newTag = new cons();" + "return newTag.merge(arguments, tag);"); fn.tag = tag; fn.cons = cons; extend(fn, Renderer); return fn; } // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * function copyArray(oldArray) { var ary = []; if (oldArray) for (var i = 0; i < oldArray.length; ++i) ary.push(oldArray[i]); return ary; } function copyObject(l, r) { var m = {}; extend(m, l); extend(m, r); return m; } function extend(l, r) { for (var n in r) l[n] = r[n]; } function addEvent(object, name, handler) { if (document.all) object.attachEvent("on"+name, handler); else object.addEventListener(name, handler, false); } // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /** @class */ function ArrayIterator(array) { var index = -1; this.next = function() { if (++index >= array.length) throw StopIteration; return array[index]; }; } /** @class */ function StopIteration() {} FBL.$break = function() { throw StopIteration; }; // ************************************************************************************************ /** @namespace */ var Renderer = { renderHTML: function(args, outputs, self) { var code = []; var markupArgs = [code, this.tag.context, args, outputs]; markupArgs.push.apply(markupArgs, this.tag.markupArgs); this.tag.renderMarkup.apply(self ? self : this.tag.subject, markupArgs); return code.join(""); }, insertRows: function(args, before, self) { this.tag.compile(); var outputs = []; var html = this.renderHTML(args, outputs, self); var doc = before.ownerDocument; var div = doc.createElement("div"); div.innerHTML = ""+html+"
"; var tbody = div.firstChild.firstChild; var parent = before.tagName == "TR" ? before.parentNode : before; var after = before.tagName == "TR" ? before.nextSibling : null; var firstRow = tbody.firstChild, lastRow; while (tbody.firstChild) { lastRow = tbody.firstChild; if (after) parent.insertBefore(lastRow, after); else parent.appendChild(lastRow); } var offset = 0; if (before.tagName == "TR") { var node = firstRow.parentNode.firstChild; for (; node && node != firstRow; node = node.nextSibling) ++offset; } var domArgs = [firstRow, this.tag.context, offset]; domArgs.push.apply(domArgs, this.tag.domArgs); domArgs.push.apply(domArgs, outputs); this.tag.renderDOM.apply(self ? self : this.tag.subject, domArgs); return [firstRow, lastRow]; }, insertBefore: function(args, before, self) { return this.insertNode(args, before.ownerDocument, before, false, self); }, insertAfter: function(args, after, self) { return this.insertNode(args, after.ownerDocument, after, true, self); }, insertNode: function(args, doc, element, isAfter, self) { if (!args) args = {}; this.tag.compile(); var outputs = []; var html = this.renderHTML(args, outputs, self); //if (FBTrace.DBG_DOM) // FBTrace.sysout("domplate.insertNode html: "+html+"\n"); var doc = element.ownerDocument; if (!womb || womb.ownerDocument != doc) womb = doc.createElement("div"); womb.innerHTML = html; var root = womb.firstChild; if (isAfter) { while (womb.firstChild) if (element.nextSibling) element.parentNode.insertBefore(womb.firstChild, element.nextSibling); else element.parentNode.appendChild(womb.firstChild); } else { while (womb.lastChild) element.parentNode.insertBefore(womb.lastChild, element); } var domArgs = [root, this.tag.context, 0]; domArgs.push.apply(domArgs, this.tag.domArgs); domArgs.push.apply(domArgs, outputs); //if (FBTrace.DBG_DOM) // FBTrace.sysout("domplate.insertNode domArgs:", domArgs); this.tag.renderDOM.apply(self ? self : this.tag.subject, domArgs); return root; }, /**/ /* insertAfter: function(args, before, self) { this.tag.compile(); var outputs = []; var html = this.renderHTML(args, outputs, self); var doc = before.ownerDocument; if (!womb || womb.ownerDocument != doc) womb = doc.createElement("div"); womb.innerHTML = html; var root = womb.firstChild; while (womb.firstChild) if (before.nextSibling) before.parentNode.insertBefore(womb.firstChild, before.nextSibling); else before.parentNode.appendChild(womb.firstChild); var domArgs = [root, this.tag.context, 0]; domArgs.push.apply(domArgs, this.tag.domArgs); domArgs.push.apply(domArgs, outputs); this.tag.renderDOM.apply(self ? self : (this.tag.subject ? this.tag.subject : null), domArgs); return root; }, /**/ replace: function(args, parent, self) { this.tag.compile(); var outputs = []; var html = this.renderHTML(args, outputs, self); var root; if (parent.nodeType == 1) { parent.innerHTML = html; root = parent.firstChild; } else { if (!parent || parent.nodeType != 9) parent = document; if (!womb || womb.ownerDocument != parent) womb = parent.createElement("div"); womb.innerHTML = html; root = womb.firstChild; //womb.removeChild(root); } var domArgs = [root, this.tag.context, 0]; domArgs.push.apply(domArgs, this.tag.domArgs); domArgs.push.apply(domArgs, outputs); this.tag.renderDOM.apply(self ? self : this.tag.subject, domArgs); return root; }, append: function(args, parent, self) { this.tag.compile(); var outputs = []; var html = this.renderHTML(args, outputs, self); //if (FBTrace.DBG_DOM) FBTrace.sysout("domplate.append html: "+html+"\n"); if (!womb || womb.ownerDocument != parent.ownerDocument) womb = parent.ownerDocument.createElement("div"); womb.innerHTML = html; // TODO: xxxpedro domplate port to Firebug var root = womb.firstChild; while (womb.firstChild) parent.appendChild(womb.firstChild); // clearing element reference to avoid reference error in IE8 when switching contexts womb = null; var domArgs = [root, this.tag.context, 0]; domArgs.push.apply(domArgs, this.tag.domArgs); domArgs.push.apply(domArgs, outputs); //if (FBTrace.DBG_DOM) FBTrace.dumpProperties("domplate append domArgs:", domArgs); this.tag.renderDOM.apply(self ? self : this.tag.subject, domArgs); return root; } }; // ************************************************************************************************ function defineTags() { for (var i = 0; i < arguments.length; ++i) { var tagName = arguments[i]; var fn = new Function("var newTag = new arguments.callee.DomplateTag('"+tagName+"'); return newTag.merge(arguments);"); fn.DomplateTag = DomplateTag; var fnName = tagName.toUpperCase(); FBL[fnName] = fn; } } defineTags( "a", "button", "br", "canvas", "code", "col", "colgroup", "div", "fieldset", "form", "h1", "h2", "h3", "hr", "img", "input", "label", "legend", "li", "ol", "optgroup", "option", "p", "pre", "select", "span", "strong", "table", "tbody", "td", "textarea", "tfoot", "th", "thead", "tr", "tt", "ul", "iframe" ); })(); /* See license.txt for terms of usage */ var FirebugReps = FBL.ns(function() { with (FBL) { // ************************************************************************************************ // Common Tags var OBJECTBOX = this.OBJECTBOX = SPAN({"class": "objectBox objectBox-$className"}); var OBJECTBLOCK = this.OBJECTBLOCK = DIV({"class": "objectBox objectBox-$className"}); var OBJECTLINK = this.OBJECTLINK = isIE6 ? // IE6 object link representation A({ "class": "objectLink objectLink-$className a11yFocus", href: "javascript:void(0)", // workaround to show XPath (a better approach would use the tooltip on mouseover, // so the XPath information would be calculated dynamically, but we need to create // a tooltip class/wrapper around Menu or InfoTip) title: "$object|FBL.getElementXPath", _repObject: "$object" }) : // Other browsers A({ "class": "objectLink objectLink-$className a11yFocus", // workaround to show XPath (a better approach would use the tooltip on mouseover, // so the XPath information would be calculated dynamically, but we need to create // a tooltip class/wrapper around Menu or InfoTip) title: "$object|FBL.getElementXPath", _repObject: "$object" }); // ************************************************************************************************ this.Undefined = domplate(Firebug.Rep, { tag: OBJECTBOX("undefined"), // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * className: "undefined", supportsObject: function(object, type) { return type == "undefined"; } }); // ************************************************************************************************ this.Null = domplate(Firebug.Rep, { tag: OBJECTBOX("null"), // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * className: "null", supportsObject: function(object, type) { return object == null; } }); // ************************************************************************************************ this.Nada = domplate(Firebug.Rep, { tag: SPAN(""), // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * className: "nada" }); // ************************************************************************************************ this.Number = domplate(Firebug.Rep, { tag: OBJECTBOX("$object"), // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * className: "number", supportsObject: function(object, type) { return type == "boolean" || type == "number"; } }); // ************************************************************************************************ this.String = domplate(Firebug.Rep, { tag: OBJECTBOX(""$object""), shortTag: OBJECTBOX(""$object|cropString""), // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * className: "string", supportsObject: function(object, type) { return type == "string"; } }); // ************************************************************************************************ this.Text = domplate(Firebug.Rep, { tag: OBJECTBOX("$object"), shortTag: OBJECTBOX("$object|cropString"), // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * className: "text" }); // ************************************************************************************************ this.Caption = domplate(Firebug.Rep, { tag: SPAN({"class": "caption"}, "$object") }); // ************************************************************************************************ this.Warning = domplate(Firebug.Rep, { tag: DIV({"class": "warning focusRow", role : 'listitem'}, "$object|STR") }); // ************************************************************************************************ this.Func = domplate(Firebug.Rep, { tag: OBJECTLINK("$object|summarizeFunction"), summarizeFunction: function(fn) { var fnRegex = /function ([^(]+\([^)]*\)) \{/; var fnText = safeToString(fn); var m = fnRegex.exec(fnText); return m ? m[1] : "function()"; }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * copySource: function(fn) { copyToClipboard(safeToString(fn)); }, monitor: function(fn, script, monitored) { if (monitored) Firebug.Debugger.unmonitorScript(fn, script, "monitor"); else Firebug.Debugger.monitorScript(fn, script, "monitor"); }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * className: "function", supportsObject: function(object, type) { return isFunction(object); }, inspectObject: function(fn, context) { var sourceLink = findSourceForFunction(fn, context); if (sourceLink) Firebug.chrome.select(sourceLink); if (FBTrace.DBG_FUNCTION_NAME) FBTrace.sysout("reps.function.inspectObject selected sourceLink is ", sourceLink); }, getTooltip: function(fn, context) { var script = findScriptForFunctionInContext(context, fn); if (script) return $STRF("Line", [normalizeURL(script.fileName), script.baseLineNumber]); else if (fn.toString) return fn.toString(); }, getTitle: function(fn, context) { var name = fn.name ? fn.name : "function"; return name + "()"; }, getContextMenuItems: function(fn, target, context, script) { if (!script) script = findScriptForFunctionInContext(context, fn); if (!script) return; var scriptInfo = getSourceFileAndLineByScript(context, script); var monitored = scriptInfo ? fbs.isMonitored(scriptInfo.sourceFile.href, scriptInfo.lineNo) : false; var name = script ? getFunctionName(script, context) : fn.name; return [ {label: "CopySource", command: bindFixed(this.copySource, this, fn) }, "-", {label: $STRF("ShowCallsInConsole", [name]), nol10n: true, type: "checkbox", checked: monitored, command: bindFixed(this.monitor, this, fn, script, monitored) } ]; } }); // ************************************************************************************************ /* this.jsdScript = domplate(Firebug.Rep, { copySource: function(script) { var fn = script.functionObject.getWrappedValue(); return FirebugReps.Func.copySource(fn); }, monitor: function(fn, script, monitored) { fn = script.functionObject.getWrappedValue(); return FirebugReps.Func.monitor(fn, script, monitored); }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * className: "jsdScript", inspectable: false, supportsObject: function(object, type) { return object instanceof jsdIScript; }, inspectObject: function(script, context) { var sourceLink = getSourceLinkForScript(script, context); if (sourceLink) Firebug.chrome.select(sourceLink); }, getRealObject: function(script, context) { return script; }, getTooltip: function(script) { return $STRF("jsdIScript", [script.tag]); }, getTitle: function(script, context) { var fn = script.functionObject.getWrappedValue(); return FirebugReps.Func.getTitle(fn, context); }, getContextMenuItems: function(script, target, context) { var fn = script.functionObject.getWrappedValue(); var scriptInfo = getSourceFileAndLineByScript(context, script); var monitored = scriptInfo ? fbs.isMonitored(scriptInfo.sourceFile.href, scriptInfo.lineNo) : false; var name = getFunctionName(script, context); return [ {label: "CopySource", command: bindFixed(this.copySource, this, script) }, "-", {label: $STRF("ShowCallsInConsole", [name]), nol10n: true, type: "checkbox", checked: monitored, command: bindFixed(this.monitor, this, fn, script, monitored) } ]; } }); /**/ //************************************************************************************************ this.Obj = domplate(Firebug.Rep, { tag: OBJECTLINK( SPAN({"class": "objectTitle"}, "$object|getTitle "), SPAN({"class": "objectProps"}, SPAN({"class": "objectLeftBrace", role: "presentation"}, "{"), FOR("prop", "$object|propIterator", SPAN({"class": "objectPropName", role: "presentation"}, "$prop.name"), SPAN({"class": "objectEqual", role: "presentation"}, "$prop.equal"), TAG("$prop.tag", {object: "$prop.object"}), SPAN({"class": "objectComma", role: "presentation"}, "$prop.delim") ), SPAN({"class": "objectRightBrace"}, "}") ) ), propNumberTag: SPAN({"class": "objectProp-number"}, "$object"), propStringTag: SPAN({"class": "objectProp-string"}, ""$object""), propObjectTag: SPAN({"class": "objectProp-object"}, "$object"), propIterator: function (object) { ///Firebug.ObjectShortIteratorMax; var maxLength = 55; // default max length for long representation if (!object) return []; var props = []; var length = 0; var numProperties = 0; var numPropertiesShown = 0; var maxLengthReached = false; var lib = this; var propRepsMap = { "boolean": this.propNumberTag, "number": this.propNumberTag, "string": this.propStringTag, "object": this.propObjectTag }; try { var title = Firebug.Rep.getTitle(object); length += title.length; for (var name in object) { var value; try { value = object[name]; } catch (exc) { continue; } var type = typeof(value); if (type == "boolean" || type == "number" || (type == "string" && value) || (type == "object" && value && value.toString)) { var tag = propRepsMap[type]; var value = (type == "object") ? Firebug.getRep(value).getTitle(value) : value + ""; length += name.length + value.length + 4; if (length <= maxLength) { props.push({ tag: tag, name: name, object: value, equal: "=", delim: ", " }); numPropertiesShown++; } else maxLengthReached = true; } numProperties++; if (maxLengthReached && numProperties > numPropertiesShown) break; } if (numProperties > numPropertiesShown) { props.push({ object: "...", //xxxHonza localization tag: FirebugReps.Caption.tag, name: "", equal:"", delim:"" }); } else if (props.length > 0) { props[props.length-1].delim = ''; } } catch (exc) { // Sometimes we get exceptions when trying to read from certain objects, like // StorageList, but don't let that gum up the works // XXXjjb also History.previous fails because object is a web-page object which does not have // permission to read the history } return props; }, fb_1_6_propIterator: function (object, max) { max = max || 3; if (!object) return []; var props = []; var len = 0, count = 0; try { for (var name in object) { var value; try { value = object[name]; } catch (exc) { continue; } var t = typeof(value); if (t == "boolean" || t == "number" || (t == "string" && value) || (t == "object" && value && value.toString)) { var rep = Firebug.getRep(value); var tag = rep.shortTag || rep.tag; if (t == "object") { value = rep.getTitle(value); tag = rep.titleTag; } count++; if (count <= max) props.push({tag: tag, name: name, object: value, equal: "=", delim: ", "}); else break; } } if (count > max) { props[Math.max(1,max-1)] = { object: "more...", //xxxHonza localization tag: FirebugReps.Caption.tag, name: "", equal:"", delim:"" }; } else if (props.length > 0) { props[props.length-1].delim = ''; } } catch (exc) { // Sometimes we get exceptions when trying to read from certain objects, like // StorageList, but don't let that gum up the works // XXXjjb also History.previous fails because object is a web-page object which does not have // permission to read the history } return props; }, /* propIterator: function (object) { if (!object) return []; var props = []; var len = 0; try { for (var name in object) { var val; try { val = object[name]; } catch (exc) { continue; } var t = typeof val; if (t == "boolean" || t == "number" || (t == "string" && val) || (t == "object" && !isFunction(val) && val && val.toString)) { var title = (t == "object") ? Firebug.getRep(val).getTitle(val) : val+""; len += name.length + title.length + 1; if (len < 50) props.push({name: name, value: title}); else break; } } } catch (exc) { // Sometimes we get exceptions when trying to read from certain objects, like // StorageList, but don't let that gum up the works // XXXjjb also History.previous fails because object is a web-page object which does not have // permission to read the history } return props; }, /**/ // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * className: "object", supportsObject: function(object, type) { return true; } }); // ************************************************************************************************ this.Arr = domplate(Firebug.Rep, { tag: OBJECTBOX({_repObject: "$object"}, SPAN({"class": "arrayLeftBracket", role : "presentation"}, "["), FOR("item", "$object|arrayIterator", TAG("$item.tag", {object: "$item.object"}), SPAN({"class": "arrayComma", role : "presentation"}, "$item.delim") ), SPAN({"class": "arrayRightBracket", role : "presentation"}, "]") ), shortTag: OBJECTBOX({_repObject: "$object"}, SPAN({"class": "arrayLeftBracket", role : "presentation"}, "["), FOR("item", "$object|shortArrayIterator", TAG("$item.tag", {object: "$item.object"}), SPAN({"class": "arrayComma", role : "presentation"}, "$item.delim") ), // TODO: xxxpedro - confirm this on Firebug //FOR("prop", "$object|shortPropIterator", // " $prop.name=", // SPAN({"class": "objectPropValue"}, "$prop.value|cropString") //), SPAN({"class": "arrayRightBracket"}, "]") ), arrayIterator: function(array) { var items = []; for (var i = 0; i < array.length; ++i) { var value = array[i]; var rep = Firebug.getRep(value); var tag = rep.shortTag ? rep.shortTag : rep.tag; var delim = (i == array.length-1 ? "" : ", "); items.push({object: value, tag: tag, delim: delim}); } return items; }, shortArrayIterator: function(array) { var items = []; for (var i = 0; i < array.length && i < 3; ++i) { var value = array[i]; var rep = Firebug.getRep(value); var tag = rep.shortTag ? rep.shortTag : rep.tag; var delim = (i == array.length-1 ? "" : ", "); items.push({object: value, tag: tag, delim: delim}); } if (array.length > 3) items.push({object: (array.length-3) + " more...", tag: FirebugReps.Caption.tag, delim: ""}); return items; }, shortPropIterator: this.Obj.propIterator, getItemIndex: function(child) { var arrayIndex = 0; for (child = child.previousSibling; child; child = child.previousSibling) { if (child.repObject) ++arrayIndex; } return arrayIndex; }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * className: "array", supportsObject: function(object) { return this.isArray(object); }, // http://code.google.com/p/fbug/issues/detail?id=874 // BEGIN Yahoo BSD Source (modified here) YAHOO.lang.isArray, YUI 2.2.2 June 2007 isArray: function(obj) { try { if (!obj) return false; else if (isIE && !isFunction(obj) && typeof obj == "object" && isFinite(obj.length) && obj.nodeType != 8) return true; else if (isFinite(obj.length) && isFunction(obj.splice)) return true; else if (isFinite(obj.length) && isFunction(obj.callee)) // arguments return true; else if (instanceOf(obj, "HTMLCollection")) return true; else if (instanceOf(obj, "NodeList")) return true; else return false; } catch(exc) { if (FBTrace.DBG_ERRORS) { FBTrace.sysout("isArray FAILS:", exc); /* Something weird: without the try/catch, OOM, with no exception?? */ FBTrace.sysout("isArray Fails on obj", obj); } } return false; }, // END Yahoo BSD SOURCE See license below. getTitle: function(object, context) { return "[" + object.length + "]"; } }); // ************************************************************************************************ this.Property = domplate(Firebug.Rep, { supportsObject: function(object) { return object instanceof Property; }, getRealObject: function(prop, context) { return prop.object[prop.name]; }, getTitle: function(prop, context) { return prop.name; } }); // ************************************************************************************************ this.NetFile = domplate(this.Obj, { supportsObject: function(object) { return object instanceof Firebug.NetFile; }, browseObject: function(file, context) { openNewTab(file.href); return true; }, getRealObject: function(file, context) { return null; } }); // ************************************************************************************************ this.Except = domplate(Firebug.Rep, { tag: OBJECTBOX({_repObject: "$object"}, "$object.message"), // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * className: "exception", supportsObject: function(object) { return object instanceof ErrorCopy; } }); // ************************************************************************************************ this.Element = domplate(Firebug.Rep, { tag: OBJECTLINK( "<", SPAN({"class": "nodeTag"}, "$object.nodeName|toLowerCase"), FOR("attr", "$object|attrIterator", " $attr.nodeName="", SPAN({"class": "nodeValue"}, "$attr.nodeValue"), """ ), ">" ), shortTag: OBJECTLINK( SPAN({"class": "$object|getVisible"}, SPAN({"class": "selectorTag"}, "$object|getSelectorTag"), SPAN({"class": "selectorId"}, "$object|getSelectorId"), SPAN({"class": "selectorClass"}, "$object|getSelectorClass"), SPAN({"class": "selectorValue"}, "$object|getValue") ) ), getVisible: function(elt) { return isVisible(elt) ? "" : "selectorHidden"; }, getSelectorTag: function(elt) { return elt.nodeName.toLowerCase(); }, getSelectorId: function(elt) { return elt.id ? "#" + elt.id : ""; }, getSelectorClass: function(elt) { return elt.className ? "." + elt.className.split(" ")[0] : ""; }, getValue: function(elt) { // TODO: xxxpedro return ""; var value; if (elt instanceof HTMLImageElement) value = getFileName(elt.src); else if (elt instanceof HTMLAnchorElement) value = getFileName(elt.href); else if (elt instanceof HTMLInputElement) value = elt.value; else if (elt instanceof HTMLFormElement) value = getFileName(elt.action); else if (elt instanceof HTMLScriptElement) value = getFileName(elt.src); return value ? " " + cropString(value, 20) : ""; }, attrIterator: function(elt) { var attrs = []; var idAttr, classAttr; if (elt.attributes) { for (var i = 0; i < elt.attributes.length; ++i) { var attr = elt.attributes[i]; // we must check if the attribute is specified otherwise IE will show them if (!attr.specified || attr.nodeName && attr.nodeName.indexOf("firebug-") != -1) continue; else if (attr.nodeName == "id") idAttr = attr; else if (attr.nodeName == "class") classAttr = attr; else if (attr.nodeName == "style") attrs.push({ nodeName: attr.nodeName, nodeValue: attr.nodeValue || // IE won't recognize the attr.nodeValue of