function firebugInit(autoOpen, handleError)
{
	var handleError = handleError == null ? true : false;
	
	if (!("console" in window) || !("firebug" in console)) {
	(function()
	{
		window.console = 
		{
			log: function()
			{
				logFormatted(arguments, "");
			},
			
			debug: function()
			{
				logFormatted(arguments, "debug");
			},
			
			info: function()
			{
				logFormatted(arguments, "info");
			},
			
			warn: function()
			{
				logFormatted(arguments, "warning");
			},
			
			error: function()
			{
				logFormatted(arguments, "error");
			},
			
			assert: function(truth, message)
			{
				if (!truth)
				{
					var args = [];
					for (var i = 1; i < arguments.length; ++i)
						args.push(arguments[i]);
					
					logFormatted(args.length ? args : ["Assertion Failure"], "error");
					throw message ? message : "Assertion Failure";
				}
			},
			
			dir: function(object)
			{
				var html = [];
							
				var pairs = [];
				for (var name in object)
				{
					try
					{
						pairs.push([name, object[name]]);
					}
					catch (exc)
					{
					}
				}
				
				pairs.sort(function(a, b) { return a[0] < b[0] ? -1 : 1; });
				
				html.push('<table>');
				for (var i = 0; i < pairs.length; ++i)
				{
					var name = pairs[i][0], value = pairs[i][1];
					
					html.push('<tr>', 
					'<td class="propertyNameCell"><span class="propertyName">',
						escapeHTML(name), '</span></td>', '<td><span class="propertyValue">');
					appendObject(value, html);
					html.push('</span></td></tr>');
				}
				html.push('</table>');
				
				logRow(html, "dir");
			},
			
			dirxml: function(node)
			{
				var html = [];
				
				appendNode(node, html);
				logRow(html, "dirxml");
			},
			
			group: function()
			{
				logRow(arguments, "group", pushGroup);
			},
			
			groupEnd: function()
			{
				logRow(arguments, "", popGroup);
			},
			
			time: function(name)
			{
				timeMap[name] = (new Date()).getTime();
			},
			
			timeEnd: function(name)
			{
				if (name in timeMap)
				{
					var delta = (new Date()).getTime() - timeMap[name];
					logFormatted([name+ ":", delta+"ms"]);
					delete timeMap[name];
				}
			},
			
			count: function()
			{
				this.warn(["count() not supported."]);
			},
			
			trace: function()
			{
				this.warn(["trace() not supported."]);
			},
			
			profile: function()
			{
				this.warn(["profile() not supported."]);
			},
			
			profileEnd: function()
			{
			},
			
			clear: function()
			{
				consoleBody.innerHTML = "";
			},
	
			open: function()
			{
				toggleConsole(true);
			},
			
			close: function()
			{
				if (frameVisible)
					toggleConsole();
			},
			update: function()
			{
				if (frameVisible)
				{
					toggleConsole();
					toggleConsole(true);
				}
			}
		};
	 
		// ********************************************************************************************
		   
		var consoleFrame = null;
		var consoleBody = null;
		var commandLine = null;
		
		var frameVisible = autoOpen == true ? true : false;
		var messageQueue = [];
		var groupStack = [];
		var timeMap = {};
		
		var clPrefix = ">>> ";
		
		var isFirefox = navigator.userAgent.indexOf("Firefox") != -1;
		var isIE = navigator.userAgent.indexOf("MSIE") != -1;
		var isOpera = navigator.userAgent.indexOf("Opera") != -1;
		var isSafari = navigator.userAgent.indexOf("AppleWebKit") != -1;
	
		// ********************************************************************************************
	
		function toggleConsole(forceOpen)
		{
			frameVisible = forceOpen || !frameVisible;
			if (consoleFrame)
				consoleFrame.style.visibility = frameVisible ? "visible" : "hidden";
			else
				waitForBody();
		}
	
		function focusCommandLine()
		{
			toggleConsole(true);
			if (commandLine)
				commandLine.focus();
		}
	
		function waitForBody()
		{
			if (document.body)
				createFrame();
			else
				setTimeout(waitForBody, 200);
		}    
	
		function createFrame()
		{
			if (consoleFrame)
				return;
			
			window.onFirebugReady = function(doc)
			{
				window.onFirebugReady = null;
	
				var toolbar = doc.getElementById("toolbar");
				toolbar.onmousedown = onSplitterMouseDown;
	
				commandLine = doc.getElementById("commandLine");
				addEvent(commandLine, "keydown", onCommandLineKeyDown);
	
				addEvent(doc, isIE || isSafari ? "keydown" : "keypress", onKeyDown);
				
				consoleBody = doc.getElementById("log");
				layout();
				flush();
			}
	
			var baseURL = getFirebugURL();
	
			consoleFrame = document.createElement("iframe");
			consoleFrame.setAttribute("src", baseURL+"/firebug.html");
			consoleFrame.setAttribute("frameBorder", "0");
			consoleFrame.style.visibility = (frameVisible ? "visible" : "hidden");    
			consoleFrame.style.zIndex = "214748364";
			consoleFrame.style.position = "fixed";
			consoleFrame.style.width = "100%";
			consoleFrame.style.left = "0";
			consoleFrame.style.bottom = "0";
			consoleFrame.style.height = "100px";
			document.body.appendChild(consoleFrame);
		}
		
		function getFirebugURL()
		{
			var scripts = document.getElementsByTagName("script");
			for (var i = 0; i < scripts.length; ++i)
			{
				if (scripts[i].src.indexOf("firebug.js") != -1)
				{
					var lastSlash = scripts[i].src.lastIndexOf("/");
					return scripts[i].src.substr(0, lastSlash);
				}
			}
		}
		
		function evalCommandLine()
		{
			var text = commandLine.value;
			commandLine.value = "";
	
			logRow([clPrefix, text], "command");
			
			var value;
			try
			{
				value = eval(text);
			}
			catch (exc)
			{
			}
	
			console.log(value);
		}
		
		function layout()
		{
			var toolbar = consoleBody.ownerDocument.getElementById("toolbar");
			var height = consoleFrame.offsetHeight - (toolbar.offsetHeight + commandLine.offsetHeight);
			consoleBody.style.top = Math.max(0, toolbar.offsetHeight) + "px";
			consoleBody.style.height = Math.max(0, height) + "px";
			
			commandLine.style.top = Math.max(0, (consoleFrame.offsetHeight - commandLine.offsetHeight)) + "px";
		}
		
		function logRow(message, className, handler)
		{
			if (consoleBody)
				writeMessage(message, className, handler);
			else
			{
				messageQueue.push([message, className, handler]);
				waitForBody();
			}
		}
		
		function flush()
		{
			var queue = messageQueue;
			messageQueue = [];
			
			for (var i = 0; i < queue.length; ++i)
				writeMessage(queue[i][0], queue[i][1], queue[i][2]);
		}
	
		function writeMessage(message, className, handler)
		{
			var isScrolledToBottom =
				consoleBody.scrollTop + consoleBody.offsetHeight >= consoleBody.scrollHeight;
	
			if (!handler)
				handler = writeRow;
			
			handler(message, className);
			
			if (isScrolledToBottom)
				consoleBody.scrollTop = consoleBody.scrollHeight - consoleBody.offsetHeight;
		}
		
		function appendRow(row)
		{
			var container = groupStack.length ? groupStack[groupStack.length-1] : consoleBody;
			container.appendChild(row);
		}
	
		function writeRow(message, className)
		{
			var row = consoleBody.ownerDocument.createElement("div");
			row.className = "logRow" + (className ? " logRow-"+className : "");
			row.innerHTML = message.join("");
			appendRow(row);
		}
	
		function pushGroup(message, className)
		{
			logFormatted(message, className);
	
			var groupRow = consoleBody.ownerDocument.createElement("div");
			groupRow.className = "logGroup";
			var groupRowBox = consoleBody.ownerDocument.createElement("div");
			groupRowBox.className = "logGroupBox";
			groupRow.appendChild(groupRowBox);
			appendRow(groupRowBox);
			groupStack.push(groupRowBox);
		}
	
		function popGroup()
		{
			groupStack.pop();
		}
		
		// ********************************************************************************************
	
		function logFormatted(objects, className)
		{
			var html = [];
	
			var format = objects[0];
			var objIndex = 0;
	
			if (typeof(format) != "string")
			{
				format = "";
				objIndex = -1;
			}
	
			var parts = parseFormat(format);
			for (var i = 0; i < parts.length; ++i)
			{
				var part = parts[i];
				if (part && typeof(part) == "object")
				{
					var object = objects[++objIndex];
					part.appender(object, html);
				}
				else
					appendText(part, html);
			}
	
			for (var i = objIndex+1; i < objects.length; ++i)
			{
				appendText(" ", html);
				
				var object = objects[i];
				if (typeof(object) == "string")
					appendText(object, html);
				else
					appendObject(object, html);
			}
			
			logRow(html, className);
		}
	
		function parseFormat(format)
		{
			var parts = [];
	
			var reg = /((^%|[^\\]%)(\d+)?(\.)([a-zA-Z]))|((^%|[^\\]%)([a-zA-Z]))/;    
			var appenderMap = {s: appendText, d: appendInteger, i: appendInteger, f: appendFloat};
	
			for (var m = reg.exec(format); m; m = reg.exec(format))
			{
				var type = m[8] ? m[8] : m[5];
				var appender = type in appenderMap ? appenderMap[type] : appendObject;
				var precision = m[3] ? parseInt(m[3]) : (m[4] == "." ? -1 : 0);
	
				parts.push(format.substr(0, m[0][0] == "%" ? m.index : m.index+1));
				parts.push({appender: appender, precision: precision});
	
				format = format.substr(m.index+m[0].length);
			}
	
			parts.push(format);
	
			return parts;
		}
	
		function escapeHTML(value)
		{
			function replaceChars(ch)
			{
				switch (ch)
				{
					case "<":
						return "&lt;";
					case ">":
						return "&gt;";
					case "&":
						return "&amp;";
					case "'":
						return "&#39;";
					case '"':
						return "&quot;";
				}
				return "?";
			};
			return String(value).replace(/[<>&"']/g, replaceChars);
		}
	
		function objectToString(object)
		{
			try
			{
				return object+"";
			}
			catch (exc)
			{
				return null;
			}
		}
	
		// ********************************************************************************************
	
		function appendText(object, html)
		{
			html.push(escapeHTML(objectToString(object)));
		}
	
		function appendNull(object, html)
		{
			html.push('<span class="objectBox-null">', escapeHTML(objectToString(object)), '</span>');
		}
	
		function appendString(object, html)
		{
			html.push('<span class="objectBox-string">&quot;', escapeHTML(objectToString(object)),
				'&quot;</span>');
		}
	
		function appendInteger(object, html)
		{
			html.push('<span class="objectBox-number">', escapeHTML(objectToString(object)), '</span>');
		}
	
		function appendFloat(object, html)
		{
			html.push('<span class="objectBox-number">', escapeHTML(objectToString(object)), '</span>');
		}
	
		function appendFunction(object, html)
		{
			var reName = /function ?(.*?)\(/;
			var m = reName.exec(objectToString(object));
			var name = m ? m[1] : "function";
			html.push('<span class="objectBox-function">', escapeHTML(name), '()</span>');
		}
		
		function appendObject(object, html)
		{
			try
			{
				if (object == undefined)
					appendNull("undefined", html);
				else if (object == null)
					appendNull("null", html);
				else if (typeof object == "string")
					appendString(object, html);
				else if (typeof object == "number")
					appendInteger(object, html);
				else if (typeof object == "function")
					appendFunction(object, html);
				else if (object.nodeType == 1)
					appendSelector(object, html);
				else if (typeof object == "object")
					appendObjectFormatted(object, html);
				else
					appendText(object, html);
			}
			catch (exc)
			{
			}
		}
			
		function appendObjectFormatted(object, html)
		{
			var text = objectToString(object);
			var reObject = /\[object (.*?)\]/;
	
			var m = reObject.exec(text);
			html.push('<span class="objectBox-object">', m ? m[1] : text, '</span>')
		}
		
		function appendSelector(object, html)
		{
			html.push('<span class="objectBox-selector">');
	
			html.push('<span class="selectorTag">', escapeHTML(object.nodeName.toLowerCase()), '</span>');
			if (object.id)
				html.push('<span class="selectorId">#', escapeHTML(object.id), '</span>');
			if (object.className)
				html.push('<span class="selectorClass">.', escapeHTML(object.className), '</span>');
	
			html.push('</span>');
		}
	
		function appendNode(node, html)
		{
			if (node.nodeType == 1)
			{
				html.push(
					'<div class="objectBox-element">',
						'&lt;<span class="nodeTag">', node.nodeName.toLowerCase(), '</span>');
	
				for (var i = 0; i < node.attributes.length; ++i)
				{
					var attr = node.attributes[i];
					if (!attr.specified)
						continue;
					
					html.push('&nbsp;<span class="nodeName">', attr.nodeName.toLowerCase(),
						'</span>=&quot;<span class="nodeValue">', escapeHTML(attr.nodeValue),
						'</span>&quot;')
				}
	
				if (node.firstChild)
				{
					html.push('&gt;</div><div class="nodeChildren">');
	
					for (var child = node.firstChild; child; child = child.nextSibling)
						appendNode(child, html);
						
					html.push('</div><div class="objectBox-element">&lt;/<span class="nodeTag">', 
						node.nodeName.toLowerCase(), '&gt;</span></div>');
				}
				else
					html.push('/&gt;</div>');
			}
			else if (node.nodeType == 3)
			{
				html.push('<div class="nodeText">', escapeHTML(node.nodeValue),
					'</div>');
			}
		}
	
		// ********************************************************************************************
		
		function addEvent(object, name, handler)
		{
			if (document.all)
				object.attachEvent("on"+name, handler);
			else
				object.addEventListener(name, handler, false);
		}
		
		function removeEvent(object, name, handler)
		{
			if (document.all)
				object.detachEvent("on"+name, handler);
			else
				object.removeEventListener(name, handler, false);
		}
		
		function cancelEvent(event)
		{
			if (document.all)
				event.cancelBubble = true;
			else
				event.stopPropagation();        
		}
	
		function onError(msg, href, lineNo)
		{
			var html = [];
			
			var lastSlash = href.lastIndexOf("/");
			var fileName = lastSlash == -1 ? href : href.substr(lastSlash+1);
			
			html.push(
				'<span class="errorMessage">', msg, '</span>', 
				'<div class="objectBox-sourceLink">', fileName, ' (line ', lineNo, ')</div>'
			);
			
			logRow(html, "error");
		};
	
		function onKeyDown(event)
		{
			if (event.keyCode == 123)
				toggleConsole();
			else if ((event.keyCode == 108 || event.keyCode == 76) && event.shiftKey
					 && (event.metaKey || event.ctrlKey))
				focusCommandLine();
			else
				return;
			
			cancelEvent(event);
		}
	
		function onSplitterMouseDown(event)
		{
			if (isSafari || isOpera)
				return;
			
			addEvent(document, "mousemove", onSplitterMouseMove);
			addEvent(document, "mouseup", onSplitterMouseUp);
	
			for (var i = 0; i < frames.length; ++i)
			{
				addEvent(frames[i].document, "mousemove", onSplitterMouseMove);
				addEvent(frames[i].document, "mouseup", onSplitterMouseUp);
			}
		}
		
		function onSplitterMouseMove(event)
		{
			var win = document.all
				? event.srcElement.ownerDocument.parentWindow
				: event.target.ownerDocument.defaultView;
	
			var clientY = event.clientY;
			if (win != win.parent)
				clientY += win.frameElement ? win.frameElement.offsetTop : 0;
			
			var height = consoleFrame.offsetTop + consoleFrame.clientHeight;
			var y = Math.max(40, height - clientY);
			
			consoleFrame.style.height = y + "px";
			layout();
		}
		
		function onSplitterMouseUp(event)
		{
			removeEvent(document, "mousemove", onSplitterMouseMove);
			removeEvent(document, "mouseup", onSplitterMouseUp);
	
			for (var i = 0; i < frames.length; ++i)
			{
				removeEvent(frames[i].document, "mousemove", onSplitterMouseMove);
				removeEvent(frames[i].document, "mouseup", onSplitterMouseUp);
			}
		}
		
		function onCommandLineKeyDown(event)
		{
			if (event.keyCode == 13)
				evalCommandLine();
			else if (event.keyCode == 27)
				commandLine.value = "";
		}
		
		if (handleError)
			window.onerror = onError;
		addEvent(document, isIE || isSafari ? "keydown" : "keypress", onKeyDown);
		
		if (document.documentElement.getAttribute("debug") == "true")
			toggleConsole(true);
	})();
	}
}