(function(){ /*!************************************************************* * * Firebug Lite 1.4.0 * * 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 */ // FIXME: xxxpedro if we use "var FBL = {}" the FBL won't appear in the DOM Panel in IE 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 = {}; // 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... // The problem is that we don't have the Firebug object yet, so we can't use // Firebug.loadPrefs. We're using the Store module directly instead. var prefs = FBL.Store.get("FirebugLite") || {}; FBL.Env.DefaultOptions = FBL.Env.Options; FBL.Env.Options = FBL.extend(FBL.Env.Options, prefs.options || {}); 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"); } FBL.Ajax.initialize(); // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // finish environment initialization FBL.Firebug.loadPrefs(); 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: true, saveWindowPosition: false, saveCommandLineHistory: false, startOpened: false, startInNewWindow: false, showIconWhenHidden: true, overrideConsole: true, ignoreFirebugElements: true, disableWhenFirebugActive: true, disableXHRListener: false, disableResourceFetching: 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 reGetFirebugSite = /(?:http|https):\/\/getfirebug.com\//; var isGetFirebugSite; var rePath = /^(.*\/)/; var reProtocol = /^\w+:\/\//; var path = null; var doc = document; // Firebug Lite 1.3.0 bookmarklet identification var script = doc.getElementById("FirebugLite"); var scriptSrc; var hasSrcAttribute = true; // If the script was loaded via bookmarklet, we already have the script tag if (script) { scriptSrc = script.src; file = reFirebugFile.exec(scriptSrc); var version = script.getAttribute("FirebugLite"); var number = version ? parseInt(version) : 0; if (!version || !number || number < bookmarkletVersion) { FBL.Env.bookmarkletOutdated = true; } } // otherwise we must search for the correct script tag else { for(var i=0, s=doc.getElementsByTagName("script"), si; si=s[i]; i++) { var file = null; if ( si.nodeName.toLowerCase() == "script" ) { if (file = reFirebugFile.exec(si.getAttribute("firebugSrc"))) { scriptSrc = si.getAttribute("firebugSrc"); hasSrcAttribute = false; } else if (file = reFirebugFile.exec(si.src)) { scriptSrc = si.src; } else continue; script = si; break; } } } // mark the script tag to be ignored by Firebug Lite if (script) script.firebugIgnore = true; if (file) { var fileName = file[1]; var fileOptions = file[2]; // absolute path if (reProtocol.test(scriptSrc)) { path = rePath.exec(scriptSrc)[1]; } // relative path else { var r = rePath.exec(scriptSrc); var src = r ? r[1] : scriptSrc; 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}"}; } isGetFirebugSite = reGetFirebugSite.test(path); if (isGetFirebugSite && path.indexOf("/releases/lite/") == -1) { // See Issue 4587 - If we are loading the script from getfirebug.com shortcut, like // https://getfirebug.com/firebug-lite.js, then we must manually add the full path, // otherwise the Env.Location will hold the wrong path, which will in turn lead to // undesirable effects like the problem in Issue 4587 path += "releases/lite/" + (fileName == "firebug-lite-beta.js" ? "beta/" : "latest/"); } 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 && // but we cannot use the locan skin when loaded from getfirebug.com, otherwise // the bookmarklet won't work when visiting getfirebug.com !isGetFirebugSite; // 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 if (hasSrcAttribute) { 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; } } } } if (!Env.Options.saveCookies) FBL.Store.remove("FirebugLite"); // 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) { try { // FIXME: xxxpedro this is failing in IE for the global "external" object return ob + ""; } catch(E) { FBTrace.sysout("Lib.safeToString() failed for ", ob); return ""; } } 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; try { // FIXME: xxxpedro this is failing in IE for the global "external" object return toString.call(object) === "[object Function]" || this.isIE && typeof object != "string" && reFunction.test(""+object); } catch (E) { FBTrace.sysout("Lib.isFunction() failed for ", object); return false; } }; // ************************************************************************************************ // 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 = FBL.getNativeXHRObject(); }, 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.0", 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; Firebug.loadPrefs(); Firebug.context.persistedState.isOpen = false; // Document must be cached before chrome initialization cacheDocument(); if (Firebug.Inspector && Firebug.Inspector.create) Firebug.Inspector.create(); if (FBL.CssAnalyzer && FBL.CssAnalyzer.processAllStyleSheets) FBL.CssAnalyzer.processAllStyleSheets(Firebug.browser.document); FirebugChrome.initialize(); dispatch(modules, "initialize", []); if (Firebug.disableResourceFetching) Firebug.Console.logFormatted(["Some Firebug Lite features are not working because " + "resource fetching is disabled. To enabled it set the Firebug Lite option " + "\"disableResourceFetching\" to \"false\". More info at " + "http://getfirebug.com/firebuglite#Options"], Firebug.context, "warn"); if (Env.onLoad) { var onLoad = Env.onLoad; delete Env.onLoad; setTimeout(onLoad, 200); } }, shutdown: function() { if (Firebug.saveCookies) Firebug.savePrefs(); 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; Firebug.savePrefs(); }, setPrefs: function(prefs) { for (var name in prefs) { if (prefs.hasOwnProperty(name)) Firebug[name] = prefs[name]; } Firebug.savePrefs(); }, restorePrefs: function() { var Options = Env.DefaultOptions; for (var name in Options) { Firebug[name] = Options[name]; } }, loadPrefs: function() { this.restorePrefs(); var prefs = Store.get("FirebugLite") || {}; var options = prefs.options; var persistedState = prefs.persistedState || FBL.defaultPersistedState; for (var name in options) { if (options.hasOwnProperty(name)) Firebug[name] = options[name]; } if (Firebug.context && persistedState) Firebug.context.persistedState = persistedState; }, savePrefs: function() { var prefs = { options: {} }; var EnvOptions = Env.Options; var options = prefs.options; for (var name in EnvOptions) { if (EnvOptions.hasOwnProperty(name)) { options[name] = Firebug[name]; } } var persistedState = Firebug.context.persistedState; if (!persistedState) { persistedState = Firebug.context.persistedState = FBL.defaultPersistedState; } prefs.persistedState = persistedState; Store.set("FirebugLite", prefs); }, erasePrefs: function() { Store.remove("FirebugLite"); this.restorePrefs(); } }; 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; if (Firebug.InfoTip) Firebug.InfoTip.initializeBrowser(Firebug.chrome); }, shutdown: function() { if (FBTrace.DBG_INITIALIZE) FBTrace.sysout("Firebug.Panel.shutdown", this.name); /// TODO: xxxpedro infoTip Hack if (Firebug.InfoTip) 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 && oldChrome.selectedPanel && 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) Firebug.context.persistedState.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); else if (unit == "em") return this.emToPixels(el, value); else if (unit == "%") return this.percentToPixels(el, value); else if (unit == "ex") return this.exToPixels(el, value); // TODO: add other units. Maybe create a better general way // to calculate measurements in different units. }, 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: 350 // obsolete }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 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 FBL.defaultPersistedState = { // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * isOpen: false, height: 300, sidePanelWidth: 350, selectedPanelName: "Console", selectedHTMLElementId: null, htmlSelectionStack: [] // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * }; /**@namespace*/ FBL.FirebugChrome = { // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //isOpen: false, //height: 300, //sidePanelWidth: 350, //selectedPanelName: "Console", //selectedHTMLElementId: null, chromeMap: {}, htmlSelectionStack: [], // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 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 browserWin = Env.browser.window; var browserContext = new Context(browserWin); var prefs = Store.get("FirebugLite"); var persistedState = prefs && prefs.persistedState || defaultPersistedState; 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; } var browserWinSize = browserContext.getWindowSize(); var height = persistedState.height || 300; height = Math.min(browserWinSize.height, height); height = Math.max(200, height); 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 = 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 = persistedState.popupHeight || 300; var browserWinSize = browserContext.getWindowSize(); var browserWinLeft = typeof browserWin.screenX == "number" ? browserWin.screenX : browserWin.screenLeft; var popupLeft = typeof persistedState.popupLeft == "number" ? persistedState.popupLeft : browserWinLeft; var browserWinTop = typeof browserWin.screenY == "number" ? browserWin.screenY : browserWin.screenTop; var popupTop = typeof persistedState.popupTop == "number" ? persistedState.popupTop : Math.max( 0, Math.min( browserWinTop + browserWinSize.height - height, // Google Chrome bug screen.availHeight - height - 61 ) ); var popupWidth = typeof persistedState.popupWidth == "number" ? persistedState.popupWidth : Math.max( 0, Math.min( browserWinSize.width, // Opera opens popup in a new tab if it's too big! screen.availWidth-10 ) ); var popupHeight = typeof persistedState.popupHeight == "number" ? persistedState.popupHeight : 300; var options = [ "true,top=", popupTop, ",left=", popupLeft, ",height=", popupHeight, ",width=", popupWidth, ",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]); 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: "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: "Disable Resource Fetching", type: "checkbox", value: "disableResourceFetching", checked: Firebug.disableResourceFetching, 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); }, restorePrefs: function(target) { Firebug.erasePrefs(); if (target) this.updateMenu(target); }, 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(Firebug.context.persistedState.selectedPanelName); if (Firebug.context.persistedState.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 || !Firebug.context.persistedState.isOpen; if(shouldOpen) this.open(); else this.close(); } }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * detach: function() { if(!FirebugChrome.chromeMap.popup) { this.close(); 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 Firebug.context.persistedState.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 ? Firebug.context.persistedState.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() { var size = Firebug.chrome.getWindowSize(); Firebug.context.persistedState.height = size.height; if (Firebug.saveCookies) Firebug.savePrefs(); 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 (!Firebug.context.persistedState.isOpen) { Firebug.context.persistedState.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 (Firebug.context.persistedState.isOpen) { if (this.isInitialized) { //dispatch(Firebug.modules, "shutdown", []); this.shutdown(); } Firebug.context.persistedState.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 = Firebug.context.persistedState.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 = Firebug.context.persistedState.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] //[Firebug.chrome.window, "beforeunload", 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() { var chromeWin = Firebug.chrome.window; var left = chromeWin.screenX || chromeWin.screenLeft; var top = chromeWin.screenY || chromeWin.screenTop; var size = Firebug.chrome.getWindowSize(); Firebug.context.persistedState.popupTop = top; Firebug.context.persistedState.popupLeft = left; Firebug.context.persistedState.popupWidth = size.width; Firebug.context.persistedState.popupHeight = size.height; if (Firebug.saveCookies) Firebug.savePrefs(); // 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); Firebug.loadPrefs(); 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(); Firebug.Console.logFormatted( ["Firebug could not capture console calls during " + persistDelay + "ms"], Firebug.context, "info" ); setTimeout(function(){ var htmlPanel = chrome.getPanel("HTML"); htmlPanel.createUI(); },50); } 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.defaultView || 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); Firebug.context.persistedState.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); Firebug.context.persistedState.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.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) { // ************************************************************************************************ // ************************************************************************************************ var sourceMap = {}; // ************************************************************************************************ 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); }); } }; // ************************************************************************************************ Firebug.Lite.Proxy.fetchResourceDisabledMessage = "/* Firebug Lite resource fetching is disabled.\n" + "To enabled it set the Firebug Lite option \"disableResourceFetching\" to \"false\".\n" + "More info at http://getfirebug.com/firebuglite#Options */"; var fetchResource = function(url) { if (Firebug.disableResourceFetching) { var source = sourceMap[url] = Firebug.Lite.Proxy.fetchResourceDisabledMessage; return source; } if (sourceMap.hasOwnProperty(url)) return sourceMap[url]; // Getting the native XHR object so our calls won't be logged in the Console Panel var xhr = FBL.getNativeXHRObject(); xhr.open("get", url, false); xhr.send(); var source = sourceMap[url] = xhr.responseText; return source; }; var fetchProxyResource = function(url) { if (sourceMap.hasOwnProperty(url)) return sourceMap[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."; } var source = data ? data.contents : ""; return source; }; // ************************************************************************************************ }}); /* See license.txt for terms of usage */ FBL.ns(function() { with (FBL) { // ************************************************************************************************ Firebug.Lite.Style = { }; // ************************************************************************************************ }}); /* 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.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 */ /* http://www.JSON.org/json2.js 2010-03-20 Public Domain. NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. See http://www.JSON.org/js.html This code should be minified before deployment. See http://javascript.crockford.com/jsmin.html USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO NOT CONTROL. This file creates a global JSON object containing two methods: stringify and parse. JSON.stringify(value, replacer, space) value any JavaScript value, usually an object or array. replacer an optional parameter that determines how object values are stringified for objects. It can be a function or an array of strings. space an optional parameter that specifies the indentation of nested structures. If it is omitted, the text will be packed without extra whitespace. If it is a number, it will specify the number of spaces to indent at each level. If it is a string (such as '\t' or ' '), it contains the characters used to indent at each level. This method produces a JSON text from a JavaScript value. When an object value is found, if the object contains a toJSON method, its toJSON method will be called and the result will be stringified. A toJSON method does not serialize: it returns the value represented by the name/value pair that should be serialized, or undefined if nothing should be serialized. The toJSON method will be passed the key associated with the value, and this will be bound to the value For example, this would serialize Dates as ISO strings. Date.prototype.toJSON = function (key) { function f(n) { // Format integers to have at least two digits. return n < 10 ? '0' + n : n; } return this.getUTCFullYear() + '-' + f(this.getUTCMonth() + 1) + '-' + f(this.getUTCDate()) + 'T' + f(this.getUTCHours()) + ':' + f(this.getUTCMinutes()) + ':' + f(this.getUTCSeconds()) + 'Z'; }; You can provide an optional replacer method. It will be passed the key and value of each member, with this bound to the containing object. The value that is returned from your method will be serialized. If your method returns undefined, then the member will be excluded from the serialization. If the replacer parameter is an array of strings, then it will be used to select the members to be serialized. It filters the results such that only members with keys listed in the replacer array are stringified. Values that do not have JSON representations, such as undefined or functions, will not be serialized. Such values in objects will be dropped; in arrays they will be replaced with null. You can use a replacer function to replace those with JSON values. JSON.stringify(undefined) returns undefined. The optional space parameter produces a stringification of the value that is filled with line breaks and indentation to make it easier to read. If the space parameter is a non-empty string, then that string will be used for indentation. If the space parameter is a number, then the indentation will be that many spaces. Example: text = JSON.stringify(['e', {pluribus: 'unum'}]); // text is '["e",{"pluribus":"unum"}]' text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t'); // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]' text = JSON.stringify([new Date()], function (key, value) { return this[key] instanceof Date ? 'Date(' + this[key] + ')' : value; }); // text is '["Date(---current time---)"]' JSON.parse(text, reviver) This method parses a JSON text to produce an object or array. It can throw a SyntaxError exception. The optional reviver parameter is a function that can filter and transform the results. It receives each of the keys and values, and its return value is used instead of the original value. If it returns what it received, then the structure is not modified. If it returns undefined then the member is deleted. Example: // Parse the text. Values that look like ISO date strings will // be converted to Date objects. myData = JSON.parse(text, function (key, value) { var a; if (typeof value === 'string') { a = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); if (a) { return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], +a[5], +a[6])); } } return value; }); myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) { var d; if (typeof value === 'string' && value.slice(0, 5) === 'Date(' && value.slice(-1) === ')') { d = new Date(value.slice(5, -1)); if (d) { return d; } } return value; }); This is a reference implementation. You are free to copy, modify, or redistribute. */ /*jslint evil: true, strict: false */ /*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply, call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, lastIndex, length, parse, prototype, push, replace, slice, stringify, test, toJSON, toString, valueOf */ // Create a JSON object only if one does not already exist. We create the // methods in a closure to avoid creating global variables. // ************************************************************************************************ var JSON = window.JSON || {}; // ************************************************************************************************ (function () { function f(n) { // Format integers to have at least two digits. return n < 10 ? '0' + n : n; } if (typeof Date.prototype.toJSON !== 'function') { Date.prototype.toJSON = function (key) { return isFinite(this.valueOf()) ? this.getUTCFullYear() + '-' + f(this.getUTCMonth() + 1) + '-' + f(this.getUTCDate()) + 'T' + f(this.getUTCHours()) + ':' + f(this.getUTCMinutes()) + ':' + f(this.getUTCSeconds()) + 'Z' : null; }; String.prototype.toJSON = Number.prototype.toJSON = Boolean.prototype.toJSON = function (key) { return this.valueOf(); }; } var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, gap, indent, meta = { // table of character substitutions '\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"' : '\\"', '\\': '\\\\' }, rep; function quote(string) { // If the string contains no control characters, no quote characters, and no // backslash characters, then we can safely slap some quotes around it. // Otherwise we must also replace the offending characters with safe escape // sequences. escapable.lastIndex = 0; return escapable.test(string) ? '"' + string.replace(escapable, function (a) { var c = meta[a]; return typeof c === 'string' ? c : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); }) + '"' : '"' + string + '"'; } function str(key, holder) { // Produce a string from holder[key]. var i, // The loop counter. k, // The member key. v, // The member value. length, mind = gap, partial, value = holder[key]; // If the value has a toJSON method, call it to obtain a replacement value. if (value && typeof value === 'object' && typeof value.toJSON === 'function') { value = value.toJSON(key); } // If we were called with a replacer function, then call the replacer to // obtain a replacement value. if (typeof rep === 'function') { value = rep.call(holder, key, value); } // What happens next depends on the value's type. switch (typeof value) { case 'string': return quote(value); case 'number': // JSON numbers must be finite. Encode non-finite numbers as null. return isFinite(value) ? String(value) : 'null'; case 'boolean': case 'null': // If the value is a boolean or null, convert it to a string. Note: // typeof null does not produce 'null'. The case is included here in // the remote chance that this gets fixed someday. return String(value); // If the type is 'object', we might be dealing with an object or an array or // null. case 'object': // Due to a specification blunder in ECMAScript, typeof null is 'object', // so watch out for that case. if (!value) { return 'null'; } // Make an array to hold the partial results of stringifying this object value. gap += indent; partial = []; // Is the value an array? if (Object.prototype.toString.apply(value) === '[object Array]') { // The value is an array. Stringify every element. Use null as a placeholder // for non-JSON values. length = value.length; for (i = 0; i < length; i += 1) { partial[i] = str(i, value) || 'null'; } // Join all of the elements together, separated with commas, and wrap them in // brackets. v = partial.length === 0 ? '[]' : gap ? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' : '[' + partial.join(',') + ']'; gap = mind; return v; } // If the replacer is an array, use it to select the members to be stringified. if (rep && typeof rep === 'object') { length = rep.length; for (i = 0; i < length; i += 1) { k = rep[i]; if (typeof k === 'string') { v = str(k, value); if (v) { partial.push(quote(k) + (gap ? ': ' : ':') + v); } } } } else { // Otherwise, iterate through all of the keys in the object. for (k in value) { if (Object.hasOwnProperty.call(value, k)) { v = str(k, value); if (v) { partial.push(quote(k) + (gap ? ': ' : ':') + v); } } } } // Join all of the member texts together, separated with commas, // and wrap them in braces. v = partial.length === 0 ? '{}' : gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' : '{' + partial.join(',') + '}'; gap = mind; return v; } } // If the JSON object does not yet have a stringify method, give it one. if (typeof JSON.stringify !== 'function') { JSON.stringify = function (value, replacer, space) { // The stringify method takes a value and an optional replacer, and an optional // space parameter, and returns a JSON text. The replacer can be a function // that can replace values, or an array of strings that will select the keys. // A default replacer method can be provided. Use of the space parameter can // produce text that is more easily readable. var i; gap = ''; indent = ''; // If the space parameter is a number, make an indent string containing that // many spaces. if (typeof space === 'number') { for (i = 0; i < space; i += 1) { indent += ' '; } // If the space parameter is a string, it will be used as the indent string. } else if (typeof space === 'string') { indent = space; } // If there is a replacer, it must be a function or an array. // Otherwise, throw an error. rep = replacer; if (replacer && typeof replacer !== 'function' && (typeof replacer !== 'object' || typeof replacer.length !== 'number')) { throw new Error('JSON.stringify'); } // Make a fake root object containing our value under the key of ''. // Return the result of stringifying the value. return str('', {'': value}); }; } // If the JSON object does not yet have a parse method, give it one. if (typeof JSON.parse !== 'function') { JSON.parse = function (text, reviver) { // The parse method takes a text and an optional reviver function, and returns // a JavaScript value if the text is a valid JSON text. var j; function walk(holder, key) { // The walk method is used to recursively walk the resulting structure so // that modifications can be made. var k, v, value = holder[key]; if (value && typeof value === 'object') { for (k in value) { if (Object.hasOwnProperty.call(value, k)) { v = walk(value, k); if (v !== undefined) { value[k] = v; } else { delete value[k]; } } } } return reviver.call(holder, key, value); } // Parsing happens in four stages. In the first stage, we replace certain // Unicode characters with escape sequences. JavaScript handles many characters // incorrectly, either silently deleting them, or treating them as line endings. text = String(text); cx.lastIndex = 0; if (cx.test(text)) { text = text.replace(cx, function (a) { return '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); }); } // In the second stage, we run the text against regular expressions that look // for non-JSON patterns. We are especially concerned with '()' and 'new' // because they can cause invocation, and '=' because it can cause mutation. // But just to be safe, we want to reject all unexpected forms. // We split the second stage into 4 regexp operations in order to work around // crippling inefficiencies in IE's and Safari's regexp engines. First we // replace the JSON backslash pairs with '@' (a non-JSON character). Second, we // replace all simple value tokens with ']' characters. Third, we delete all // open brackets that follow a colon or comma or that begin the text. Finally, // we look to see that the remaining characters are only whitespace or ']' or // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval. if (/^[\],:{}\s]*$/. test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@'). replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']'). replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { // In the third stage we use the eval function to compile the text into a // JavaScript structure. The '{' operator is subject to a syntactic ambiguity // in JavaScript: it can begin a block or an object literal. We wrap the text // in parens to eliminate the ambiguity. j = eval('(' + text + ')'); // In the optional fourth stage, we recursively walk the new structure, passing // each name/value pair to a reviver function for possible transformation. return typeof reviver === 'function' ? walk({'': j}, '') : j; } // If the text is not JSON parseable, then a SyntaxError is thrown. throw new SyntaxError('JSON.parse'); }; } // ************************************************************************************************ // registration FBL.JSON = JSON; // ************************************************************************************************ }()); /* See license.txt for terms of usage */ (function(){ // ************************************************************************************************ /* Copyright (c) 2010-2011 Marcus Westin * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ var store = (function(){ var api = {}, win = window, doc = win.document, localStorageName = 'localStorage', globalStorageName = 'globalStorage', namespace = '__firebug__storejs__', storage api.disabled = false api.set = function(key, value) {} api.get = function(key) {} api.remove = function(key) {} api.clear = function() {} api.transact = function(key, transactionFn) { var val = api.get(key) if (typeof val == 'undefined') { val = {} } transactionFn(val) api.set(key, val) } api.serialize = function(value) { return JSON.stringify(value) } api.deserialize = function(value) { if (typeof value != 'string') { return undefined } return JSON.parse(value) } // Functions to encapsulate questionable FireFox 3.6.13 behavior // when about.config::dom.storage.enabled === false // See https://github.com/marcuswestin/store.js/issues#issue/13 function isLocalStorageNameSupported() { try { return (localStorageName in win && win[localStorageName]) } catch(err) { return false } } function isGlobalStorageNameSupported() { try { return (globalStorageName in win && win[globalStorageName] && win[globalStorageName][win.location.hostname]) } catch(err) { return false } } if (isLocalStorageNameSupported()) { storage = win[localStorageName] api.set = function(key, val) { storage.setItem(key, api.serialize(val)) } api.get = function(key) { return api.deserialize(storage.getItem(key)) } api.remove = function(key) { storage.removeItem(key) } api.clear = function() { storage.clear() } } else if (isGlobalStorageNameSupported()) { storage = win[globalStorageName][win.location.hostname] api.set = function(key, val) { storage[key] = api.serialize(val) } api.get = function(key) { return api.deserialize(storage[key] && storage[key].value) } api.remove = function(key) { delete storage[key] } api.clear = function() { for (var key in storage ) { delete storage[key] } } } else if (doc.documentElement.addBehavior) { var storage = doc.createElement('div') function withIEStorage(storeFunction) { return function() { var args = Array.prototype.slice.call(arguments, 0) args.unshift(storage) // See http://msdn.microsoft.com/en-us/library/ms531081(v=VS.85).aspx // and http://msdn.microsoft.com/en-us/library/ms531424(v=VS.85).aspx // TODO: xxxpedro doc.body is not always available so we must use doc.documentElement. // We need to make sure this change won't affect the behavior of this library. doc.documentElement.appendChild(storage) storage.addBehavior('#default#userData') storage.load(localStorageName) var result = storeFunction.apply(api, args) doc.documentElement.removeChild(storage) return result } } api.set = withIEStorage(function(storage, key, val) { storage.setAttribute(key, api.serialize(val)) storage.save(localStorageName) }) api.get = withIEStorage(function(storage, key) { return api.deserialize(storage.getAttribute(key)) }) api.remove = withIEStorage(function(storage, key) { storage.removeAttribute(key) storage.save(localStorageName) }) api.clear = withIEStorage(function(storage) { var attributes = storage.XMLDocument.documentElement.attributes storage.load(localStorageName) for (var i=0, attr; attr = attributes[i]; i++) { storage.removeAttribute(attr.name) } storage.save(localStorageName) }) } try { api.set(namespace, namespace) if (api.get(namespace) != namespace) { api.disabled = true } api.remove(namespace) } catch(e) { api.disabled = true } return api })(); if (typeof module != 'undefined') { module.exports = store } // ************************************************************************************************ // registration FBL.Store = store; // ************************************************************************************************ })(); /* 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; /**#@-*/ // ************************************************************************************************ }}); /* See license.txt for terms of usage */ FBL.ns(function() { with (FBL) { // ************************************************************************************************ // ************************************************************************************************ // Inspector Module var ElementCache = Firebug.Lite.Cache.Element; var inspectorTS, inspectorTimer, isInspecting; Firebug.Inspector = { create: function() { offlineFragment = Env.browser.document.createDocumentFragment(); createBoxModelInspector(); createOutlineInspector(); }, destroy: function() { destroyBoxModelInspector(); destroyOutlineInspector(); offlineFragment = null; }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Inspect functions toggleInspect: function() { if (isInspecting) { this.stopInspecting(); } else { Firebug.chrome.inspectButton.changeState("pressed"); this.startInspecting(); } }, startInspecting: function() { isInspecting = true; Firebug.chrome.selectPanel("HTML"); createInspectorFrame(); var size = Firebug.browser.getWindowScrollSize(); fbInspectFrame.style.width = size.width + "px"; fbInspectFrame.style.height = size.height + "px"; //addEvent(Firebug.browser.document.documentElement, "mousemove", Firebug.Inspector.onInspectingBody); addEvent(fbInspectFrame, "mousemove", Firebug.Inspector.onInspecting); addEvent(fbInspectFrame, "mousedown", Firebug.Inspector.onInspectingClick); }, stopInspecting: function() { isInspecting = false; if (outlineVisible) this.hideOutline(); removeEvent(fbInspectFrame, "mousemove", Firebug.Inspector.onInspecting); removeEvent(fbInspectFrame, "mousedown", Firebug.Inspector.onInspectingClick); destroyInspectorFrame(); Firebug.chrome.inspectButton.restore(); if (Firebug.chrome.type == "popup") Firebug.chrome.node.focus(); }, onInspectingClick: function(e) { fbInspectFrame.style.display = "none"; var targ = Firebug.browser.getElementFromPoint(e.clientX, e.clientY); fbInspectFrame.style.display = "block"; // Avoid inspecting the outline, and the FirebugUI var id = targ.id; if (id && /^fbOutline\w$/.test(id)) return; if (id == "FirebugUI") return; // Avoid looking at text nodes in Opera while (targ.nodeType != 1) targ = targ.parentNode; //Firebug.Console.log(targ); Firebug.Inspector.stopInspecting(); }, onInspecting: function(e) { if (new Date().getTime() - lastInspecting > 30) { fbInspectFrame.style.display = "none"; var targ = Firebug.browser.getElementFromPoint(e.clientX, e.clientY); fbInspectFrame.style.display = "block"; // Avoid inspecting the outline, and the FirebugUI var id = targ.id; if (id && /^fbOutline\w$/.test(id)) return; if (id == "FirebugUI") return; // Avoid looking at text nodes in Opera while (targ.nodeType != 1) targ = targ.parentNode; if (targ.nodeName.toLowerCase() == "body") return; //Firebug.Console.log(e.clientX, e.clientY, targ); Firebug.Inspector.drawOutline(targ); if (ElementCache(targ)) { var target = ""+ElementCache.key(targ); var lazySelect = function() { inspectorTS = new Date().getTime(); if (Firebug.HTML) Firebug.HTML.selectTreeNode(""+ElementCache.key(targ)); }; if (inspectorTimer) { clearTimeout(inspectorTimer); inspectorTimer = null; } if (new Date().getTime() - inspectorTS > 200) setTimeout(lazySelect, 0); else inspectorTimer = setTimeout(lazySelect, 300); } lastInspecting = new Date().getTime(); } }, // TODO: xxxpedro remove this? onInspectingBody: function(e) { if (new Date().getTime() - lastInspecting > 30) { var targ = e.target; // Avoid inspecting the outline, and the FirebugUI var id = targ.id; if (id && /^fbOutline\w$/.test(id)) return; if (id == "FirebugUI") return; // Avoid looking at text nodes in Opera while (targ.nodeType != 1) targ = targ.parentNode; if (targ.nodeName.toLowerCase() == "body") return; //Firebug.Console.log(e.clientX, e.clientY, targ); Firebug.Inspector.drawOutline(targ); if (ElementCache.has(targ)) FBL.Firebug.HTML.selectTreeNode(""+ElementCache.key(targ)); lastInspecting = new Date().getTime(); } }, /** * * llttttttrr * llttttttrr * ll rr * ll rr * llbbbbbbrr * llbbbbbbrr */ drawOutline: function(el) { var border = 2; var scrollbarSize = 17; var windowSize = Firebug.browser.getWindowSize(); var scrollSize = Firebug.browser.getWindowScrollSize(); var scrollPosition = Firebug.browser.getWindowScrollPosition(); var box = Firebug.browser.getElementBox(el); var top = box.top; var left = box.left; var height = box.height; var width = box.width; var freeHorizontalSpace = scrollPosition.left + windowSize.width - left - width - (!isIE && scrollSize.height > windowSize.height ? // is *vertical* scrollbar visible scrollbarSize : 0); var freeVerticalSpace = scrollPosition.top + windowSize.height - top - height - (!isIE && scrollSize.width > windowSize.width ? // is *horizontal* scrollbar visible scrollbarSize : 0); var numVerticalBorders = freeVerticalSpace > 0 ? 2 : 1; var o = outlineElements; var style; style = o.fbOutlineT.style; style.top = top-border + "px"; style.left = left + "px"; style.height = border + "px"; // TODO: on initialize() style.width = width + "px"; style = o.fbOutlineL.style; style.top = top-border + "px"; style.left = left-border + "px"; style.height = height+ numVerticalBorders*border + "px"; style.width = border + "px"; // TODO: on initialize() style = o.fbOutlineB.style; if (freeVerticalSpace > 0) { style.top = top+height + "px"; style.left = left + "px"; style.width = width + "px"; //style.height = border + "px"; // TODO: on initialize() or worst case? } else { style.top = -2*border + "px"; style.left = -2*border + "px"; style.width = border + "px"; //style.height = border + "px"; } style = o.fbOutlineR.style; if (freeHorizontalSpace > 0) { style.top = top-border + "px"; style.left = left+width + "px"; style.height = height + numVerticalBorders*border + "px"; style.width = (freeHorizontalSpace < border ? freeHorizontalSpace : border) + "px"; } else { style.top = -2*border + "px"; style.left = -2*border + "px"; style.height = border + "px"; style.width = border + "px"; } if (!outlineVisible) this.showOutline(); }, hideOutline: function() { if (!outlineVisible) return; for (var name in outline) offlineFragment.appendChild(outlineElements[name]); outlineVisible = false; }, showOutline: function() { if (outlineVisible) return; if (boxModelVisible) this.hideBoxModel(); for (var name in outline) Firebug.browser.document.getElementsByTagName("body")[0].appendChild(outlineElements[name]); outlineVisible = true; }, // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Box Model drawBoxModel: function(el) { // avoid error when the element is not attached a document if (!el || !el.parentNode) return; var box = Firebug.browser.getElementBox(el); var windowSize = Firebug.browser.getWindowSize(); var scrollPosition = Firebug.browser.getWindowScrollPosition(); // element may be occluded by the chrome, when in frame mode var offsetHeight = Firebug.chrome.type == "frame" ? Firebug.context.persistedState.height : 0; // if element box is not inside the viewport, don't draw the box model if (box.top > scrollPosition.top + windowSize.height - offsetHeight || box.left > scrollPosition.left + windowSize.width || scrollPosition.top > box.top + box.height || scrollPosition.left > box.left + box.width ) return; var top = box.top; var left = box.left; var height = box.height; var width = box.width; var margin = Firebug.browser.getMeasurementBox(el, "margin"); var padding = Firebug.browser.getMeasurementBox(el, "padding"); var border = Firebug.browser.getMeasurementBox(el, "border"); boxModelStyle.top = top - margin.top + "px"; boxModelStyle.left = left - margin.left + "px"; boxModelStyle.height = height + margin.top + margin.bottom + "px"; boxModelStyle.width = width + margin.left + margin.right + "px"; boxBorderStyle.top = margin.top + "px"; boxBorderStyle.left = margin.left + "px"; boxBorderStyle.height = height + "px"; boxBorderStyle.width = width + "px"; boxPaddingStyle.top = margin.top + border.top + "px"; boxPaddingStyle.left = margin.left + border.left + "px"; boxPaddingStyle.height = height - border.top - border.bottom + "px"; boxPaddingStyle.width = width - border.left - border.right + "px"; boxContentStyle.top = margin.top + border.top + padding.top + "px"; boxContentStyle.left = margin.left + border.left + padding.left + "px"; boxContentStyle.height = height - border.top - padding.top - padding.bottom - border.bottom + "px"; boxContentStyle.width = width - border.left - padding.left - padding.right - border.right + "px"; if (!boxModelVisible) this.showBoxModel(); }, hideBoxModel: function() { if (!boxModelVisible) return; offlineFragment.appendChild(boxModel); boxModelVisible = false; }, showBoxModel: function() { if (boxModelVisible) return; if (outlineVisible) this.hideOutline(); Firebug.browser.document.getElementsByTagName("body")[0].appendChild(boxModel); boxModelVisible = true; } }; // ************************************************************************************************ // Inspector Internals // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Shared variables // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Internal variables var offlineFragment = null; var boxModelVisible = false; var boxModel, boxModelStyle, boxMargin, boxMarginStyle, boxBorder, boxBorderStyle, boxPadding, boxPaddingStyle, boxContent, boxContentStyle; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * var resetStyle = "margin:0; padding:0; border:0; position:absolute; overflow:hidden; display:block;"; var offscreenStyle = resetStyle + "top:-1234px; left:-1234px;"; var inspectStyle = resetStyle + "z-index: 2147483500;"; var inspectFrameStyle = resetStyle + "z-index: 2147483550; top:0; left:0; background:url(" + Env.Location.skinDir + "pixel_transparent.gif);"; //if (Env.Options.enableTrace) inspectFrameStyle = resetStyle + "z-index: 2147483550; top: 0; left: 0; background: #ff0; opacity: 0.05; _filter: alpha(opacity=5);"; var inspectModelOpacity = isIE ? "filter:alpha(opacity=80);" : "opacity:0.8;"; var inspectModelStyle = inspectStyle + inspectModelOpacity; var inspectMarginStyle = inspectStyle + "background: #EDFF64; height:100%; width:100%;"; var inspectBorderStyle = inspectStyle + "background: #666;"; var inspectPaddingStyle = inspectStyle + "background: SlateBlue;"; var inspectContentStyle = inspectStyle + "background: SkyBlue;"; var outlineStyle = { fbHorizontalLine: "background: #3875D7;height: 2px;", fbVerticalLine: "background: #3875D7;width: 2px;" }; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * var lastInspecting = 0; var fbInspectFrame = null; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * var outlineVisible = false; var outlineElements = {}; var outline = { "fbOutlineT": "fbHorizontalLine", "fbOutlineL": "fbVerticalLine", "fbOutlineB": "fbHorizontalLine", "fbOutlineR": "fbVerticalLine" }; var getInspectingTarget = function() { }; // ************************************************************************************************ // Section var createInspectorFrame = function createInspectorFrame() { fbInspectFrame = createGlobalElement("div"); fbInspectFrame.id = "fbInspectFrame"; fbInspectFrame.firebugIgnore = true; fbInspectFrame.style.cssText = inspectFrameStyle; Firebug.browser.document.getElementsByTagName("body")[0].appendChild(fbInspectFrame); }; var destroyInspectorFrame = function destroyInspectorFrame() { if (fbInspectFrame) { Firebug.browser.document.getElementsByTagName("body")[0].removeChild(fbInspectFrame); fbInspectFrame = null; } }; var createOutlineInspector = function createOutlineInspector() { for (var name in outline) { var el = outlineElements[name] = createGlobalElement("div"); el.id = name; el.firebugIgnore = true; el.style.cssText = inspectStyle + outlineStyle[outline[name]]; offlineFragment.appendChild(el); } }; var destroyOutlineInspector = function destroyOutlineInspector() { for (var name in outline) { var el = outlineElements[name]; el.parentNode.removeChild(el); } }; var createBoxModelInspector = function createBoxModelInspector() { boxModel = createGlobalElement("div"); boxModel.id = "fbBoxModel"; boxModel.firebugIgnore = true; boxModelStyle = boxModel.style; boxModelStyle.cssText = inspectModelStyle; boxMargin = createGlobalElement("div"); boxMargin.id = "fbBoxMargin"; boxMarginStyle = boxMargin.style; boxMarginStyle.cssText = inspectMarginStyle; boxModel.appendChild(boxMargin); boxBorder = createGlobalElement("div"); boxBorder.id = "fbBoxBorder"; boxBorderStyle = boxBorder.style; boxBorderStyle.cssText = inspectBorderStyle; boxModel.appendChild(boxBorder); boxPadding = createGlobalElement("div"); boxPadding.id = "fbBoxPadding"; boxPaddingStyle = boxPadding.style; boxPaddingStyle.cssText = inspectPaddingStyle; boxModel.appendChild(boxPadding); boxContent = createGlobalElement("div"); boxContent.id = "fbBoxContent"; boxContentStyle = boxContent.style; boxContentStyle.cssText = inspectContentStyle; boxModel.appendChild(boxContent); offlineFragment.appendChild(boxModel); }; var destroyBoxModelInspector = function destroyBoxModelInspector() { boxModel.parentNode.removeChild(boxModel); }; // ************************************************************************************************ // Section // ************************************************************************************************ }}); // 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