// $Debug Utilities and Extensions
// Copyright Microsoft 2007

// Debug Provides
//		Trace Window
//		Tracing
//		Immediate Window
//		Inspect Objects
//		Profiling

// $Debug.trace(p_obj /* p_obj, p_obj,… */)
// $Debug.traceEx(p_category, p_strName, p_obj /* p_obj, p_obj,… */)

// $trace(p_obj /* p_obj, p_obj,… */)
// $traceEx(p_category, p_strName, p_obj /* p_obj, p_obj,… */)

function $DebugRegisterNamespace()
{
	for(var n = 0;n < arguments.length;n++)
	{
        var w = window;
		var names = arguments[n].split(".");
		for(var i = 0;i < names.length; i++)
		{
			if( !w[names[i]] )
				w[names[i]] = {};
			w = w[names[i]];
		}
	}
}

$DebugRegisterNamespace("$Debug");
$DebugRegisterNamespace("$Debug.CancelEvent");
$DebugRegisterNamespace("$Debug.Settings");
$DebugRegisterNamespace("$Debug.toggleDisplay");
$DebugRegisterNamespace("$Debug.createElement");
$DebugRegisterNamespace("$Debug.ObjectViewer");
$DebugRegisterNamespace("$Debug.Window");
$DebugRegisterNamespace("$Debug.trace");
$DebugRegisterNamespace("$Debug.Ex");
$DebugRegisterNamespace("$debug");
$DebugRegisterNamespace("d");


// This will be assigned to the debug window
$Debug.Window = null;

// The trace element -- in the debug window.
$Debug._TraceElement = null;

// This will retain the trace made
$Debug._TraceLog = [];
$Debug._PerfLog = [];
$Debug._TraceWindow = null;
$Debug._InpectedObjects = [];

$Debug.trace = function(p_obj) /*obj,obj ....*/
{
    var args = [];
    if( arguments.length > 1 )
    {
        for(var i = 0;i < arguments.length;i++)
        {
            args.push(arguments[i]);
        }
    }
    else
    {
        args = p_obj;
    }

    var trace = function()
	{
		// if more than one argument, create an obj arraya
		$Debug._TraceLog.push(args);

		if( $Debug._TraceWindow )
		{
			// if a trace window is specified, then we will call traceWindow with param.
			$Debug._TraceWindow(args);
		}
	}
	window.setTimeout(trace,0);
}
d      =
$debug = $Debug.trace;

$Debug.Ex.eventHandles = [];
$Debug.Ex.attachEvent = function(obj,eventName,listener,useCapture)
{
	if( obj.attachEvent )
		obj.attachEvent(eventName,listener);
	else
		obj.addEventListener(eventName.substr(2),listener,useCapture);

	$Debug.Ex.eventHandles.push({obj: obj,eventName: eventName,listener: listener, useCapture: useCapture});
};

$Debug.Ex.detachEvent = function(obj,eventName,listener,useCapture)
{
	if( obj.detachEvent )
		obj.detachEvent(eventName,listener);
	else
		obj.removeEventListener(eventName.substr(2),listener,useCapture);
}

$Debug.createElement = function(obj,attributes)
{
    var r;
    $Debug.createElement.applyAttributes = function(el,attribs)
    {
        for(var o in attribs.style)
        {
            try{
                el.style[o] = attribs.style[o];
            }
            catch(e){
                alert(["Error applying style",e.description,o].join("\n"));
            }
        }

        for(var o in attribs)
        {
            try{
                if( o != "style" && o != "tagName" && attribs[o] != null )
                    el[o] = attribs[o];
            }
            catch(e){
                alert(["Error creating element",e.description,o].join("\n"));
            }
        }
    }

    if( typeof obj != "string" )
    {
        if( !obj.tagName )
        {
            alert(["Object needs a tagName",arguments.callee.caller].join("\n"));
            return {};
        }
        r = document.createElement(obj.tagName);
        $Debug.createElement.applyAttributes(r,obj)
    }
    else // if obj is a string
    {
        r = (obj ? document.createElement(obj) : null);

        if( r && attributes )
            $Debug.createElement.applyAttributes(r,attributes)
    }

    return r;
}

$Debug.Setup = function()
{
	var m_DebugWindow = null;

    $Debug.Setup.toggle = function()
    {
        toggleWindow();
    }

    function toggleWindow()
    {
        if( !m_DebugWindow )
        {
            m_DebugWindow = new $Debug.DebugWindow();
            m_DebugWindow.bindEvents({"onclose":onclose});
        }
        else
        {
            m_DebugWindow.win.hidden ? m_DebugWindow.show() :
                                   m_DebugWindow.hide();
        }
    }

	function keyCheck(ev)
	{
		ev=ev||event;
		if( ev.ctrlKey && ev.altKey && ev.keyCode == 68 )
		{
            toggleWindow();
		}
	}

	function onclose()
	{
		m_DebugWindow = null;
	}

	function closeWindow()
	{
		if( m_DebugWindow )
		{
			m_DebugWindow.close();
		}
	}

	$Debug.Ex.attachEvent(document,"onkeyup",keyCheck);
	$Debug.Ex.attachEvent(window,"onunload",closeWindow);
}
$Debug.Setup();

$Debug.Trim = function(val)
{
    val=val||"";
    while(val.substr(0,1) == " ")
        val=val.substr(1);
    while(val.substr(val.length-1) == " ")
        val=val.substr(0,val.length-1);
    return val;
}

$Debug.toggleDisplay = function(el,type)
{
    type = type||"block";
    var r = (el.style.display == "none" ? type : "none");
    el.style.display = r;
    return r == type;
}

$Debug.CancelEvent = function(ev)
{
    ev=ev||event;
    if( ev.stopPropagation )
    {
        ev.preventDefault();
        ev.stopPropagation();
    }
    else
    {
        ev.cancelReturn = true;
        ev.cancelBubble = true;
    }
    return false;
}

$Debug.Cookie =
{
    "Set": function(key,value)
    {
        var d = new Date();
        var inOneYear = new Date(d.getFullYear()+1,d.getMonth(),d.getDay()).toGMTString();
        var c = [(key + "=" + escape(value)),("expires=" + inOneYear)].join(";");
        document.cookie = c;
    },
    "Get": function(key)
    {
        key = key || null;
        if( !key )
            return r;

        var r = [];
        var c = document.cookie.split(";");

        var trim = $Debug.Trim;
        for(var i = 0;i < c.length;i++ )
        {
            var keyValue = trim(c[i].split("=")[0]);
            if( keyValue == key )
            {
                r.push(unescape(trim(c[i].split("=")[1])));
            }
        }
        // if not an array, and is an empty string return null
        r = ( r.length > 2 ) ? r.splice(0,1) : r.join("").length > 0 ? r.join("") : null;
        return r;
    }
}
$Debug.Settings = $Debug.Cookie;


$Debug.Profiler = function()
{
    var m_timers = {};
    var m_runCount = 0;

    var timer = function(id, start)
    {
        // construct a timer
        var r =
        {
            name: id,
            start: start,
            comment: null,
            end: "",
            count: 0,
            m_runCount: 0
        }
        return r;
    }

    var millisecondToString = function(value)
    {
        var seconds = 1000;
        var minutes = seconds * 60;
        var hours = minutes * 60;
        var days = hours * 24;
        var r =
        {
            data: [Math.floor(value/hours), Math.floor(value/minutes), Math.floor(value/seconds), value % 1000],
            toString: function()
            {
                return this.data.join(":");
            }
        }
        return r;
    };

    $Debug.Profiler.Start = function(name, override, comment)
    {
        var count = 0;
        if( !override && m_timers[name] )
        {
            count = (++m_timers[name].count);
            name = [name,"_",count].join("");
        }
        m_timers[name] = new timer(name, new Date().getTime());
        m_timers[name].count = count;
        m_timers[name].comment = null;
        m_timers[name].end = 0;
        m_runCount++;
        if( comment )
        {
            m_timers[name].comment = comment;
        }
        return name;
    }

    $Debug.Profiler.End = function(name)
    {
        m_timers[name].end = new Date().getTime();
        m_timers[name].m_runCount = --m_runCount;
    }

    $Debug.Profiler.Data = function()
    {
        var r =
        {
            items: [],
            toString: function()
            {
                return this.items.toString();
            },
            toHTML: function()
            {
                // simple html output as a sample of what you can do with this data.
                var r = [];
                var timers = this.items;
                for(var i = 0;i < timers.length;i++)
                {
                    r = r.concat(["<div>"]);
                    r = r.concat(["<b>label:",timers[i].name,"</b> "]);
                    r = r.concat(["<i>time:",timers[i].time,"</i> "]);
                    r = r.concat(["<u>nests:",timers[i].runCount,"</u> "]);
                    r = r.concat(["<i>info:",timers[i].runCount,"</i> "]);
                    r = r.concat(["</div>"]);
                }
                return r.join("");
            }
        }

        for(var n in m_timers)
        {
            millisecs = m_timers[n].end - m_timers[n].start;
            r.items.push({
                name: m_timers[n].name,
                millisecs: millisecs,
                time: millisecondToString(millisecs),
                runCount: m_timers[n].m_runCount,
                comment: m_timers[n].comment
                });
        }

        r.items.reverse();
        return r;
    }

    $Debug.Profiler.QuickView = function()
    {
        var r =
        {
            toString: function(){return this.string.toString()},
            timers: m_timers,
            string: []
        };

        var millisecs = 0;
        for(var n in m_timers)
        {
            millisecs = m_timers[n].end - m_timers[n].start;
            r.string.push([m_timers[n].name,": ",millisecs," [",millisecondToString(millisecs),"]"].join(""));
        }

        r.string.reverse();
        return r;
    }
}
$Debug.Profiler(); // force profiler compilation

$Debug.ObjectViewer =
{
	parent: function(){return $Debug.ObjectViewer},
	toString: function(){return "$Debug.ObjectViewer"},
	cancelEvent: $Debug.CancelEvent,
	createElement: $Debug.createElement,
	toggleDisplay: $Debug.toggleDisplay,
    getFunctionArguments: function(functionObject)
    {
        var fnstr = functionObject.toString();
        var start = fnstr.indexOf("(") + 1;
        var end   = fnstr.indexOf(")");
        var argstr= fnstr.substring(start,end);
        return argstr;
    },
    objectInfo: function(object,objectName,p_args)
    {
	/// <summary>
	/// </summary>
	/// <param name="object" type="object"></param>
	/// <param name="objectName" type="string"></param>
	/// <param name="p_args" type="object">
	///		string title: displays at the top of the UI for the object information
	/// </param>
	/// <remarks></remarks>
	/// <sample>
	/// </sample>
		objectName = objectName || "";
		p_args = p_args || {};
		var title = p_args.title || "";
        var div = this.createElement({tagName:"div",target: object});
        var m=[], p=[], other=[], error=[];
        var onclickValue, funcArgs;
		var _initType = typeof object;
		var m_this = this;
        var ul = {};
        var ulStyle = {listStyle:"none",margin:"0px",paddingLeft:"6px"};
		var _value;
        for(var o in object)
        {
			try
			{
				var fieldType = typeof object[o];
				onclickValue = ['return ',this,'.toggleChild(this,\'',o,'\',event)'].join("");
				switch( fieldType )
				{
					case "object" :
						try{
							_value = ((object[o]+"").substr(0,1)=="[" ? object[o] : fieldType);
						}
						catch(e){
							_value = fieldType;
						}
						p.push(['<li style="cursor:pointer;color:blue;" onclick="',onclickValue,'">','.',o,' <i style="color:black">',_value,'</i>'].join(""));
						p.push('<div style="color:black;border:1px solid #DDD;padding:2px;display:none"></div>');
						p.push('</li>');
						break;
					case "function" :
						m = m.concat(funcInfoUI(object,o));
						break;
					default:
						other.push(['<li title="',object[o],'" style="cursor:pointer" onclick="',onclickValue,'">','.',o," <i>",fieldType,"</i>"].join(""));
						other.push('<div style="display:none"></div>');
						other.push('</li>');
				}
			}
			catch(e)
			{
				error.push(['<li title="" style="cursor:pointer">Error:',fieldType," ",o," <i>",e.message||e.description,"</i>",'</li>'].join(""));
			}
        }

		p = p.concat(other);

        div.innerHTML = "";
		if ( objectName != "" )
            div.innerHTML = ['<div><i><u>',objectName,'</u></i> ',title,'</div>'].join("");

        if( _initType == "function" || (m.length == 0 && p.length == 0) )
        {
			var showFunc = ['return ',m_this,'.toggleChild(this,\'null\',event,{child:1})'].join("");
			if( p.length + m.length == 0 )
				div.innerHTML += ['<pre style="border:1px solid #DDD;margin:0px;padding: 2px;margin-left:10px">',object,'</pre>'].join("")
			else
				div.innerHTML += ['<div onclick="',showFunc,'"><span style="cursor:pointer;color:green">Function Text:</span><pre style="display:none;border:1px solid #DDD;margin:0px;padding: 2px;margin-left:10px">',object,'</pre></div>'].join("")
        }

		if( p.length > 0 )
            div.appendChild(section("Properties",ulStyle,p.join(""),object));

		if( m.length > 0 )
            div.appendChild(section("Methods",ulStyle,m.join(""),object));

		if( error.length > 0 )
            div.appendChild(section("Errors",ulStyle,error.join(""),object));

		return div;

		function section(strTitle,style,innerHTML,object)
		{
			var html = ['<div><b>',strTitle,'</b></div>',innerHTML].join("");
			var r = m_this.createElement({tagName: "ul",style: style,innerHTML: html,target: object});
			return r;
		}

		function funcInfoUI(object,o)
		{
			o = o || null;
			var r = [];
			var _object = o ? object[o] : object;
			var funcArgs = m_this.getFunctionArguments(_object);

			r.push(['<li>','.',o,'(',funcArgs,') <i>function</i>'].join(""));
			var onclickValue = ['return ',m_this,'.toggleChild(this,\'',o,'\',event,{child:1,parent:2,toString:true})'].join("");
			r.push('<span onclick="',onclickValue,'" style="padding-left:2px;" ><span style="cursor:pointer;color:green">[toString]:</span><pre style="display:none;border:1px solid green"></pre></span>');
			if( o )
			{
				onclickValue = ['return ',m_this,'.toggleChild(this,\'',o,'\',event,{child:1,parent:2,func:true})'].join("");
				// r.push('<span onclick="',onclickValue,'" style="padding-left:2px;" ><span style="cursor:pointer;color:blue">[value]:</span><div style="display:none;border:1px solid blue"></div></span>');
			}
			r.push('</li>');
			return r;
		}
    },
    displayObject: function(obj,targetElement,label,p_args)
    {
        var div = this.parent().objectInfo(obj,label);
        targetElement.appendChild(div);
    },
    toggleChild: function(el,field,ev,args)
    {
        args=args||{};
        var _child = (args.child ? args.child : 2);
        var _parent = (args.parent ? args.parent : 1);
        var _func = (args.func ? args.func : false);
        var _toString = (args.toString ? args.toString : false);
        var _parentEl = (el.parentNode||el.parentElement);
        while( _parent > 1 )
        {
            _parentEl = (_parentEl.parentNode||_parentEl.parentElement);
            _parent--;
        }
        var _childNode = el.childNodes[_child];
        var _targetObj = _parentEl.target;
        ev = ev||event;
		var r = true;
        if( _childNode.innerHTML == "" )
        {
            var obj = _func ? _targetObj[field]() : _targetObj[field];
            this.displayObject(obj,_childNode);
            r = this.cancelEvent(ev);
        }
        else
        {
            this.cancelEvent(ev);
        }
        this.toggleDisplay(_childNode);
        return r;
    }
}

$Debug.removeNode = function(node)
{
	var r;
	if( node )
	{
		if( node.removeNode )
		{
			r = node.removeNode(true);
		}
		else if( node.parentNode && node.parentNode.removeChild )
		{
			r = node.parentNode.removeChild(node);
		}
	}
	return r;
}

function dTrace(el,values)
{
    if( el )
    {
        var args = [];
        if( arguments.length > 2 )
        {
            for(var i = 1;i < arguments.length;i++)
            {
                args.push(arguments[i]);
            }
        }
        else
        {
            args = values;
        }

        var div = $Debug.createElement("DIV");
        div.innerHTML = $Debug.htmlEncode(args);

        if( el.insertAdjacentElement )
            el.insertAdjacentElement("afterBegin",div)
        else if( el.firstChild )
            el.insertBefore(div,el.firstChild);
    }
}

$Debug.Window = function(p_winProperties,p_content)
{
// using:
// $Debug.createElement
// $Debug.CancelEvent
// $Debug.Settings
// $Debug.toggleDisplay

/// object p_winProperties
/// Window properties can be sent as an object.
/// You can create from scratch or seed this object using new Web.UI.Window.Properties();
/*
	windowProperties =
	{
		id: "_win"
		titleBar: true,     // NOTE: if there is no titlebar, there is no way to move the window
		title: "",
		statusBar: true,
		sizable: 3,			// 0:off, 1:left, 2:right, 3:both (default),
		wrap: 3,			// 0:off, 1:horizonta, 2:vertical, 3: both (default)
		titleBarMin: true,
		titleBarClear: true,
		titleBarClose: true,
		statusText: "",
		x: 0,
		y: 0,
		width: 300,
		height: 200,
		opacity: 100
		min: false,
		opaque: true,
		zIndex: baseZIndex,
		saveState: false		// saves state (size,opacity,position) in cookie format in for page reloads, etc.
	}
*/
/// string|element p_content
///	Window content can be sent as string or element object
///	The innerHTML property is set for a string, if element the element is appended to the window.
// statics
	$Debug.Window.autoId = "w_";
	$Debug.Window.baseZindex = 90000;
	$Debug.Window.windows =
	{
		list: $Debug.Window.windows ? $Debug.Window.windows.list : [],
		items: $Debug.Window.windows ? $Debug.Window.windows.items : {},
		count: $Debug.Window.windows ? $Debug.Window.windows.count : 0,
		zIndex: $Debug.Window.windows ? $Debug.Window.windows.zIndex : $Debug.Window.baseZindex
	}

    var m_p = p_winProperties || {};

    // pre-populate id, it will get stomped if id is provided.
    this.id = m_p.id || ($Debug.Window.autoId + $Debug.Window.windows.count);
    var m_id = this.id;

    // Setup defaults for the window properties...
    m_p.titleBar = (undefined==m_p.titleBar ? true : m_p.titleBar);
    m_p.sizable = m_p.sizable || 3;
    m_p.wrap = m_p.wrap || 3;
    // NOTE: if there is no titlebar, there is no way to move the window
    if( m_p.titleBar )
    {
        m_p.title = m_p.title || "";
        m_p.titleBarMin = (undefined==m_p.titleBarMin ? true : m_p.titleBarMin);
        m_p.titleBarClear = (undefined==m_p.titleBarClear ? true : m_p.titleBarClear);
        m_p.titleBarClose = (undefined==m_p.titleBarClose ? true : m_p.titleBarClose);
    }
    m_p.statusBar = (undefined==m_p.statusBar ? true : m_p.statusBar);
    if( m_p.statusBar )
    {
        m_p.statusText = m_p.statusText || "";
    }
    m_p.x = (undefined==m_p.x ? 0 : m_p.x);
    m_p.y = (undefined==m_p.y ? 0 : m_p.y);
    m_p.width = (undefined==m_p.width ? 300 : m_p.width);
    m_p.height = (undefined==m_p.height ? 200 : m_p.height);
    m_p.opacity = (undefined==m_p.opacity ? 100 : m_p.opacity);
    m_p.zIndex = (undefined==m_p.zIndex ? $Debug.Window.baseZindex : m_p.zIndex);
    m_p.saveState = (undefined==m_p.saveState ? false : m_p.saveState);
	m_p.min = (undefined==m_p.min ? null : m_p.min);
	m_p.font = (undefined==m_p.min ? 'Arial' : m_p.font);

    var createElement = $Debug.createElement;
    var cancelEvent = $Debug.CancelEvent;

    var m_this = this;
	var m_defaultBorder = 4;
	var m_windowClass = "_htmlWindow";
	var m_borderClass = "_windowBorder";
	this.eventHandles = [];
    this.hidden = false;

	// COMPAT FUNCTIONS
	var attachEvent = function(element,eventName,listener,useCapture)
	{
		if( element.attachEvent )
			element.attachEvent(eventName,listener);
		else
			element.addEventListener(eventName.substr(2),listener,useCapture);
		m_this.eventHandles.push({element: element,eventName: eventName,listener: listener, useCapture: useCapture});
	};

	var detachEvent = function(element,eventName,listener,useCapture)
	{
		if( element.detachEvent )
			element.detachEvent(eventName,listener);
		else
			element.removeEventListener(eventName.substr(2),listener,useCapture);
	}

	var removeNode = $Debug.removeNode;

    var documentSelect =
    {
        enable: function()
        {
            var d = document;
            if( d )
            {
				d.onselect = (d._cache_onselect ? d._cache_onselect : null);
				d.onselectstart = (d._cache_onselectstart ? d._cache_onselectstart : null);

				if( d.body )
				{
					var db = d.body;
					db.onselectstart = (db._cache_onselectstart ? db._cache_onselectstart : null);
					db.onselect = (db._cache_onselect ? db._cache_onselect : null);
				}
            }
			m_el.main.onselectstart = m_el.main.onselect = function(){return true};
        },
        disable: function()
        {
            var d = document;
            if( d )
            {
                d._cache_onselect = (d.onselect == cancelEvent ? null : d.onselect);
                d._cache_onselectstart = (d.onselectstart == cancelEvent ? null : d.onselectstart)
                d.onselect = cancelEvent;
                d.onselectstart = cancelEvent;

                if( d.body )
                {
                    var db = d.body;
                    db._cache_onselectstart = (db.onselectstart == cancelEvent ? null : db.onselectstart);
                    db._cache_onselect = (db.onselect == cancelEvent ? null : db.onselect);
                    db.onselectstart = cancelEvent;
                    db.onselect = cancelEvent;
                }
            }
			m_el.main.onselectstart = m_el.main.onselect = cancelEvent;
        }
    }


	// TODO: m_Store should be an interface, remove hard-coding
    var m_Store =
	{
		"Get": function(key)
		{
			return $Debug.Settings.Get(m_this.id+key);
		},
		"Set": function(key,value)
		{
			var r = null;
			if( m_p.saveState )
			{
				r = $Debug.Settings.Set(m_this.id+key,value);
			}
			return r;
		}
	}

	this.setStorage = function(IStoreObject)
	{
		// IStoreObject must include methods
		// Get(key)
		// Set(key,value)

		m_Store = storeObject;
	}

	var m_opacityGimick =
	{
		steps: [100,80,60,40],
		text: ["[|]","[/]","[-]","[\\]"],
		step: 0
	}

	var m_Fields =
	{
		titleBar: m_p.titleBar,
		statusBar: m_p.statusBar,
		title: m_p.title
	}

	// using a member object to maintain pointers to elements.
	// note: order is important here, or we will leak.
	var m_el =
	{
		opacToggle: {},
		minToggle: {},
		closeWindow: {},
		title: {},
		border: {},
		status: {},
		titleBar: {},
		body: {},
		main: {},
		shield: {}
	}
	this.elements = m_el;

	p_content = p_content || null;

	// yes, it's the property bag.
    var m_state =
    {
        left: m_Store.Get("left"+m_id) || m_p.x,
        top: isNaN(m_Store.Get("top"+m_id)) ? 0 : m_Store.Get("top"+m_id) || m_p.y,
        width: m_Store.Get("width"+m_id) || m_p.width,
        height: m_Store.Get("height"+m_id) || m_p.height,
        opacity: m_p.opacity,
        logVis: m_Store.Get("logVis"+m_id) || 1,
        immediateVis: m_Store.Get("immediateVis"+m_id) || 1,
        winMin: m_Store.Get("winMin"+m_id) || 0,
        winOpacity: m_Store.Get("winOpacity"+m_id) || m_p.opacity,
		borderWidth: m_defaultBorder
    };
    this.state = m_state;

	// no going off the page...
    if( m_state.top < 0 )
        m_state.top = 0;

	function createWindowElements()
	{
		// Window Creation Time...
		// This is the main window, lets put all the pieces together.
		var winStyle = {left:m_state.left+"px",top:m_state.top+"px",width:m_state.width+"px",position:"absolute",zIndex:"90000",color:"#000",fontFamily: m_p.font, fontSize:"9pt",backgroundColor:"#FFF",filter:"alpha(opacity:100)"};
		m_el.main = createElement({tagName:"div",tabIndex: 1,tabStop: "true",style: winStyle,className: m_windowClass});

		// Lets create the main window areas
		m_el.border = createElement({tagName:"div",className: m_borderClass,style:{backgroundColor:"#FFF",border: m_state.borderWidth+"px solid silver",overflow:"hidden",height:"auto"}});
		m_el.status = createElement({tagName:"div",style:{height: "auto",width:"100%",backgroundColor:"#006",color:"#FFF",textAlign:"left",whiteSpace:"nowrap",overflow:"hidden"}});

		// The body element.
		// NOTE: this maintains the height.
		m_el.body = createElement({tagName:"div",className: "_windowBody",style:{height:m_state.height+"px",width:"100%",overflow:"auto"}});

        var titleBarHeight = 0;
        // We need to add all the action elements to the titlebar.
		if( m_Fields.titleBar )
		{
			m_el.titleBar = createElement({tagName:"div",style:{height: "auto",width:"100%",padding:"2px",cursor:"pointer",backgroundColor:"#006",color:"#FFF",overflow:"hidden",whiteSpace:"nowrap"}});
			m_el.opacToggle = createElement({tagName:"span",style:{fontFamily: "lucida console",cursor:"pointer"},innerHTML:"[|]"});
			m_el.minToggle = createElement({tagName:"span",style:{fontFamily: "lucida console",cursor:"pointer"},innerHTML:"[>]"});
			m_el.closeWindow = createElement({tagName:"span",style:{fontFamily: "lucida console",cursor:"pointer"},innerHTML:"[X]"});
			m_el.title = createElement({tagName:"span",style:{fontFamily: "lucida console",cursor:"pointer"},innerHTML: "&nbsp;"+m_Fields.title});

            if( m_p.titleBarClose )
    			m_el.titleBar.appendChild(m_el.closeWindow);

            if( m_p.titleBarMin )
    			m_el.titleBar.appendChild(m_el.minToggle);

            if( m_p.titleBarClear )
    			m_el.titleBar.appendChild(m_el.opacToggle);

			m_el.titleBar.appendChild(m_el.title);

			m_el.border.appendChild(m_el.titleBar);
            titleBarHeight = m_el.titleBar.style.height;
		}

		m_el.shield = createElement({tagName:"div",style:{position:"absolute",width:"100%",height:"100%",backgroundColor:"transparent",display:"none",left:m_state.borderWidth+"px",top:titleBarHeight},innerHTML:""});
		m_el.shield.onmousemove = m_el.shield.onmouseup = m_el.shield.onmousedown = cancelEvent;

		// m_el.border.appendChild(m_el.shield);
		m_el.border.appendChild(m_el.body);
		m_el.border.appendChild(m_el.status);
		m_el.main.appendChild(m_el.border);

		// p_content can be either HTML or an element (fake-polymorphism)
		if( document.body )
		{
			// we only create the window of document.body is present
			if( p_content )
			{
				if( typeof p_content == "string" )
					m_el.body.innerHTML = (p_content);
				else
					m_el.body.appendChild(p_content);
			}
			document.body.appendChild(m_el.main);
		}

		setOpacity(m_el.main,m_state.opacity);

		// lets cancel selecting the menu or sizer for IE
		m_el.titleBar.onselectstart = m_el.titleBar.onselect = cancelEvent;

		// These event handlers will allow me to move/size the window
		attachEvent(document,"onmousemove",doc_onmousemove,true);
		attachEvent(document,"onmouseup",doc_onmouseup,true);
		attachEvent(m_el.main,"onmousedown",winMain_onmousedown,true);
		attachEvent(m_el.titleBar,"onmousedown",winTitle_onmousedown,true);

		// These only apply to the title bar
		if( m_Fields.titleBar )
		{
			if( m_el.opacToggle )
			{
				// TODO: Commented out, over-rides init state
				// setWinOpacity(m_el.opacToggle);

				// OPACITY: This will toggle the opacity
				m_el.opacToggle.onclick = function()
				{
					setWinOpacity(this);
				}
			}

			if( m_el.minToggle )
			{
				m_el.minToggle.on = m_state.winMin == "1";

				setWinMin(m_el.minToggle);

				// MINIMIZE: This will hide the components and the statusbar
				m_el.minToggle.onclick = function()
				{
					setWinMin(this);
				}

			}

			if( m_el.closeWindow )
			{
				m_el.closeWindow.onclick = function()
				{
					m_this.close();
				}
			}

			if( m_p.min )
			{
				setWinMin(m_el.minToggle);
			}
		}
	}
	createWindowElements();

    this.status = function(text)
    {
        if( m_p.statusBar )
        {
            m_el.status.innerHTML = text;
        }
    }

    // METHODS:
	this.close = function()
	{
		// remove event hookups
		m_this.dispose();
	}

	this.tabManager =
	{
		tab: [],
		tabs: {},
		getTab: function(tab)
		{
			// tab == tab object or tab id
			var r = null;

            if( typeof tab == "object" )
                r = this.tabs[tab.id];
            else if( this.tabs[tab] )
                r = this.tabs[tab];

			return r;
		},
		removeAll: function()
		{
			var i = this.tab.length-1;
			while(i >= 0)
			{
				this.remove(this.tab[i]);
				i--;
			}
		},
		remove: function(tab)
		{
			// tab == tab object or tab id
            var tabMan = this;
			tab = this.getTab(tab);
			if( tab )
			{
				remove(tab);
				this.tabs[tab.id] = null;
			}

			function remove(tabObject)
            {
                var aTabs = tabMan.tab;
                for(var i = 0;i < aTabs.length;i++)
                {
                    if( aTabs[i] == tabObject )
                    {
                        aTabs.splice(i,1);
                        break;
                    }
                }
            }
		},
		add: function(tab)
		{
			this.tab.push(tab);
			if( tab.id )
				this.tabs[tab.id] = tab;
			this.click(tab.id);
		},
		click: function(tabId)
		{
			var l = this.tab.length-1;
			var tab;
			while(l >= 0)
			{
				tab = this.tab[l];
				if( tab.id != tabId )
				{
					$Debug.createElement.applyAttributes(tab.label,{style:this.style.normal});
					tab.body.style.display = "none";
				}
				else
				{
					$Debug.createElement.applyAttributes(tab.label,{style:this.style.selected});
					tab.body.style.display = "block";
				}
				l--;
			}
		},
		style:
		{
			selected:
			{
				fontSize: "9pt",
				textDecoration:"none",
				padding: "5px 7px 2px 7px",
				border: "1px solid #999",
				borderBottom: "0px !important",
				backgroundColor: "#FFF",
				borderBottom: "1px solid white"
			},
			normal:
			{
				fontSize: "9pt",
				color: "#000",
				textDecoration:"none",
				padding:"6px 8px 2px 8px",
				backgroundColor:"#DDD",
				border: "0px solid #8ED7F8",
				borderBottom:"1px solid #999",
				hover: "#009"
			}
		}
	}

	var m_paneSizing = false;
	var m_targetPane = null;

    // Adding Tabs:
    var m_tabStrip = null;
	this.addTab = function(p_Id,p_Title,p_Content,p_tabargs)
	{
		// Get current html, need to retain this and create a generic tab.
		var currentHTML = (m_el.body.innerHTML+"");
		var tabStrip =
		{
			tagName:"DIV",
			innerHTML:'<UL style="padding:0;list-style:none;margin:0;display:inline"></UL>',
			style: {backgroundColor:"#DDD",margin: "1px 0",padding: "2px 0",borderBottom: "1px solid #999"}
		}

		// if tab strip is not available, create it
		if( !m_tabStrip )
		{
			m_tabStrip = createElement(tabStrip)
			m_el.body.innerHTML = "";
			m_el.body.appendChild(m_tabStrip);
		}

		var newTab = createElement({tagName: "LI",style:{display:"inline",margin:"0",listStyle:"none"}});

		var label = createElement({
			tagName: "A",
			href: "#",
			onclick: function(){return false},
			innerHTML: p_Title,
			style: m_this.tabManager.style.normal});

		newTab.appendChild(label);
		m_tabStrip.firstChild.appendChild(newTab);

		var body = createElement({tagName: "DIV",style:{padding:"0"}});
		if( p_Content )
		{
			if(typeof(p_Content) == "object" )
				body.appendChild(p_Content);
			else
				body.innerHTML = p_Content;
		}

		// elements...
		var els =
		{
			body: body,
			tab: newTab,
			label: label
		}

		m_el.body.appendChild(els.body);

		var rTab =
        {
            body: els.body,
            tab: els.tab,
			label: els.label,
            id: p_Id,
            close: function()
			{
				m_this.removeTab(this);
			},
			show: function()
			{
				show();
			}
        }

		label.onclick = function()
		{
			show();
			return false;
		}

		function show()
		{
			m_this.tabManager.click(rTab.id);
		}

		attachEvent(window,"onunload",rTab.close);

		m_this.tabManager.add(rTab);

		return rTab;
	}
	this.addHPane = this.addTab;

	this.removeTab = function(tab)
	{
		// tab == tab object or tab id
		tab = m_this.tabManager.getTab(tab);
		if( tab )
		{
			tab.label.onclick = null;
			if( tab.body )
			{
				tab.body.style.display = "none";
				tab.body.innerHTML = "";
				tab.body = null;
			}
            tab.tab.style.display = "none";
			tab.tab = null;
			m_this.tabManager.remove(tab);
		}
	}

	// MOVED DIFFERENCE:
	function getMovedWinStyle(ev)
	{
		// I am using the drag object added to the titleBar to get the offsets.
		var r = {};		// I  will be only be returning r.left and r.top.
		var thresh = 2; // This will determine how many pixels are moved before it's a drag.

		if( Math.abs(ev.clientX - m_el.titleBar.drag.startX) > thresh ||
			Math.abs(ev.clientY - m_el.titleBar.drag.startY) > thresh )
		{
			// .. and it is such a drag :-)
			r =
			{
				left: (ev.clientX - m_el.titleBar.drag.offsetX)+"px",
				top:  (ev.clientY - m_el.titleBar.drag.offsetY)+"px"
			};
		}
		// either {} || r.left and r.top is returned
		return r;
	}

	// This will size the window
	function sizeWindow(ev)
	{
		if( !m_el.main.sizer )
			return;

		var srcElement = ev.srcElement||ev.target;

		var min = {width: 100,height: 0},
			prop = {x:0,y:0,w:0,h:0},
			sizer = m_el.main.sizer,
			mX = ev.clientX,
			mY = ev.clientY,
			win = sizer.win;

		// I create a resize object to encapsulate sizing routines.
		var resize =
		{
			n: function()
			{
				prop.h = sizer.y - mY;
				prop.y = mY - sizer.y;
			},
			s: function()
			{
				prop.h = mY - sizer.y;
			},
			e: function()
			{
				prop.w = mX - sizer.x;
			},
			w: function()
			{
				prop.w = sizer.x - mX;
				prop.x = mX - sizer.x;
			}
		}

		// I am using the cursor style set to determine which sides to resize.
		switch(sizer.cursor)
		{
			// please enjoy the ascii art.
			case "nw-resize" : resize.w();
			case "n-resize"  : resize.n();
				 break;
			case "se-resize" : resize.e();
			case "s-resize"  : resize.s();
				 break;
			case "ne-resize" : resize.n();
			case "e-resize"  : resize.e();
				 break;
			case "sw-resize" : resize.s();
			case "w-resize"  : resize.w();
				 break;
		}

		// Lets ensure that we are not breaking rules.
		var width = (win.w + prop.w);
		if( width < min.width )
			width = min.width;

		var height = (win.h + prop.h);
		if( height < min.height )
			height = min.height;

		// create a simple style object for main window
		var newStyle =
		{
			width: width,
			left: (win.x + prop.x),
			top: (win.y + prop.y)
		}

		// apply style to main
		for(var o in newStyle)
			m_el.main.style[o] = newStyle[o]+"px";

		// now apply style to body, and add it to newStyle object. body maintains height
		newStyle.height = height;
		m_el.body.style.height = newStyle.height+"px";

		// return newStyle so we can query it if needed.
		return newStyle;
	}

	// Opacity Gimick functionality
	function setWinOpacity(el,value)
	{
		value = value||null; // not yet used
		var step = m_opacityGimick.step;
		var steps = m_opacityGimick.steps;
		var set = {op:steps[step],text:m_opacityGimick.text[step]};
		el.innerHTML = set.text;
		setOpacity(m_el.main,set.op);
		m_Store.Set("winOpacity"+m_id,step);
		m_opacityGimick.step = (++step > steps.length-1 ? 0 : step); // increment
	}

	// Minimizer Functionality
	function setWinMin(el)
	{
		var set = (el.on ? {disp:"none",text:"[^]"} : {disp:"block",text:"[>]"});
		m_el.body.style.display = set.disp;
		m_el.status.style.display = set.disp;
		el.innerHTML = set.text;
		m_Store.Set("winMin"+m_id,(el.on?"1":"0"));
		el.on = !el.on;
	}

	// Determine if the mouse is over the sizer border
	function isOverSizer(ev,borderWidth)
	{
		// this is used to determine if the mouse is over the border for sizing events.
		// it returns an object.
		//		.sizable : boolean : determines if cursor is over the border.
		//		.cursor  : string  : this is set to the value of what the cursor style sould be.
		//						     you can also use this to where the cursor is on the border.

		ev = ev||event;
		var border = borderWidth || m_state.borderWidth;
		var srcElement = ev.srcElement||ev.target;
		var w = srcElement.offsetWidth;
		var h = srcElement.offsetHeight;
		var newCursor = srcElement.style.cursor || "auto";

		// I determine sizability by detecting cursor location releative to element.
		// I can then return the cursor style based on findings.
		var r = {sizable: false};
		if( srcElement.className == m_windowClass || srcElement.className == m_borderClass )
		{
			var sizeTop = ev.offsetY <= border,
				sizeBottom = h - ev.offsetY <= (border + 3),
				sizeRight = w - ev.offsetX <= (border + 3),
				sizeLeft = ev.offsetX <= border;

            newCursor = null;

			if( sizeTop || sizeBottom )
			{
				newCursor = sizeLeft  ? (sizeTop ? "nw-resize" : "sw-resize") :
							sizeRight ? (sizeTop ? "ne-resize" : "se-resize") :
							sizeTop   ? "n-resize" : "s-resize";
			}
			else if( sizeLeft || sizeRight )
			{
				newCursor = sizeTop    ? (sizeLeft ? "nw-resize" : "ne-resize") :
							sizeBottom ? (sizeLeft ? "sw-resize" : "se-resize") :
							sizeLeft ? "w-resize" : "e-resize";
			}
			r.sizable = (sizeTop || sizeBottom || sizeLeft || sizeRight);
		}
//dTrace(document.getElementById("dTraceOut"),srcElement.className,border,w - ev.offsetX,h - ev.offsetY);
		r.cursor = newCursor;
		return r;
	}

	// get window properties
	function getWin()
	{
		var win =
		{
			w: parseInt(m_el.main.style.width),
			h: parseInt(m_el.body.style.height),
			x: parseInt(m_el.main.style.left),
			y: parseInt(m_el.main.style.top)
		}
		return win;
	}

	function setOpacity(el,num)
    {
        if( el.style.filter )
            el.style.filter = ['alpha(opacity:',num,')'].join("");
        else
            el.style.MozOpacity = num * 0.01;
    }

	// WINDOW ENUMERATION
	if( document.body )
	{
		$Debug.Window.windows.list.push(m_this);
		$Debug.Window.windows.items[m_this.id] = m_this;
		$Debug.Window.windows.count++;
	}

	// EVENTS:
	// I like to place the event handles grouped at the bottom.

	// handles drag/drop
	function winTitle_onmousedown(ev)
	{
		var createElement = $Debug.createElement;
		ev=ev||event;
		var offX = ev.offsetX||ev.layerX;
		var offY = ev.offsetY||ev.layerY;

		m_el.shield.style.height = m_el.body.style.height;
		m_el.shield.width = m_el.main.style.width;
		m_el.shield.style.zIndex = m_el.main.style.zIndex;

		$Debug.toggleDisplay(m_el.shield);

		var ghostElement = m_el.main.cloneNode(true);
		ghostElement.style.zIndex = m_el.main.style.zIndex + 10;

		if( ghostElement.style.filter )
		{
			setOpacity(ghostElement,50);
		}
		else
		{
			setOpacity(m_el.main,50);
			ghostElement.firstChild.firstChild.nextSibling.nextSibling.innerHTML = "";
		}

		m_el.titleBar.drag = {ghost:ghostElement,ghostAdded:false,startX:ev.clientX,startY:ev.clientY,offsetX:offX,offsetY:offY};
		return false;
	}

	// DRAG|SIZE END: this event handler will either size or move the window
	function doc_onmouseup(ev,release)
	{
		ev = ev||event;
		release = release||true;	// provide the option to keep from releasing events.
		var srcElement = ev.srcElement||ev.target;
        var minPaneHt = 10;

		// window dragged
		if( m_el.titleBar.drag )
		{
			// get new location (s.left,s.top);
			var s = getMovedWinStyle(ev);

			for(var o in s)
			{
				m_Store.Set(o+m_id,parseInt(s[o]));	// This saves the value.
				m_el.main.style[o] = s[o];
			}

            documentSelect.enable();

			if( release )
			{
				if( m_el.titleBar.drag.ghostAdded )
					m_el.titleBar.drag.ghost.parentNode.removeChild(m_el.titleBar.drag.ghost);

				if( !m_el.main.style.filter )
					setOpacity(m_el.main,100);

				$Debug.toggleDisplay(m_el.shield);

				m_el.titleBar.drag = false;
			}
		}

		// window sized using border:
		if( m_el.main.sizer )
		{
			var style = sizeWindow(ev);
			if( release )
			{
				// null out events and se
				m_el.main.sizer = null;
				m_el.main.onselectstart = m_el.main.onselect = null;

                documentSelect.enable();

				for(var o in style)
				{
					m_Store.Set(o+m_id,style[o]);
				}
			}
		}
	}

	// GHOST WINDOW:
	function doc_onmousemove(ev)
	{
		ev = ev||event;
		var srcElement = ev.srcElement||ev.target;
		try
		{
			if( m_el.titleBar.drag )
			{
				var s = getMovedWinStyle(ev);
				documentSelect.disable();

				for(var o in s)
				{
					try{	// making things generally safe
						m_el.titleBar.drag.ghost.style[o] = s[o];
					}catch(e){}
				}

				if( undefined != s.left && !m_el.titleBar.drag.ghostAdded )
				{
					m_el.titleBar.drag.ghost.onselectstart = cancelEvent;
					document.body.appendChild(m_el.titleBar.drag.ghost);
					m_el.titleBar.drag.ghostAdded = true;
				}
			}

			if( m_el.main.sizer )
			{
				sizeWindow(ev,true);
			}

			srcElement.style.cursor = isOverSizer(ev).cursor;
		}
		catch(e){}
	}


	function winMain_onmousedown(ev)
	{
		ev = ev||event;
		var srcElement = ev.srcElement||ev.target;
		var mouseOver = isOverSizer(ev);

		if( mouseOver.sizable )
		{
            documentSelect.disable();
			var win = new getWin();
			m_el.main.sizer = {x: ev.clientX,y: ev.clientY, win: win, cursor: mouseOver.cursor};
		}

		m_el.main.style.zIndex = $Debug.Window.windows.zIndex++;
	}

	this.hide = function()
	{
		if( m_el && m_el.main && m_el.main.style )
		{
			m_el.main.style.display = "none";
            this.hidden = true;
		}
	}

	this.show = function()
	{
		if( m_el && m_el.main && m_el.main.style )
		{
			m_el.main.style.display = "block";
            this.hidden = false;
		}
	}

	this.dispose = function()
	{
		m_this.disposing = true;
		if( m_this.owner && m_this.owner.close )
		{
			m_this.owner.close();
			m_this.owner = null;
		}

		// Remove Window Event Listener
		var evh;
		for(var n = m_this.eventHandles.length-1;n >= 0;n--)
		{
			evh = m_this.eventHandles[n];
			detachEvent(evh.element,evh.eventName,evh.listener,evh.useCapture);

			evh = null;
			m_this.eventHandles.pop();
		}

		var tabManager = m_this.tabManager;
		var i = tabManager.tab.length-1;
		while(i >= 0)
		{
			m_this.removeTab(tabManager.tab[i]);
			i--;
		}

		m_this.tabManager.removeAll();

		// Remove direct element event handlers
		m_el.opacToggle ? m_el.opacToggle.onclick = null : null;
		m_el.minToggle ? m_el.minToggle.onclick = null : null;
		m_el.closeWindow ? m_el.closeWindow.onclick = null : null;

		// remove window instances from instance tracking
		for(var i = 0;i < $Debug.Window.windows.list.length;i++)
		{
			if( $Debug.Window.windows.list[i] == m_this )
			{
				$Debug.Window.windows.list.splice(i,1);
				break;
			}
		}
		$Debug.Window.windows.items[m_this.id] = null;
		$Debug.Window.windows.count--;

		// TODO: Should I parse the children and break any circular references?
		if( m_tabStrip )
		{
			removeNode(m_tabStrip);
			m_tabStrip = null;
		}

		// finally destroy elements;
		for(var o in m_this.elements)
		{
			removeNode(m_el[o]);
		}
	}
	attachEvent(window,"onunload",m_this.close);
}

function $DebugEventCompat()
{
	// Mozilla Event Shim
	function QuickLoc (el)
	{
		var c = {x : 0, y : 0};
		while (el) {
			c.x += el.offsetLeft;
			c.y += el.offsetTop;
			el = el.offsetParent;
		}
		return c;
	}

	function GetNonTextNode(n)
	{
		try
		{
			while (n && n.nodeType!=1) n=n.parentNode;
		}
		catch(ex)
		{
			n = null;
		}
		return n;
	}

	var w = window;
	if( window.addEventListener && w.Event )
	{
		var eventProto = w.Event.prototype;
		eventProto.__proto__ = {
			__proto__:eventProto.__proto__};
		eventProto.__defineGetter__('srcElement',function() {var n = this._FixOffset; if (!n) {n = GetNonTextNode(this.target)};return n;});
		eventProto.__defineSetter__('cancelBubble',function(v) {if (v) this.stopPropagation();});
		eventProto.__defineGetter__('offsetX',function() {return window.pageXOffset + this.clientX - ((this._FixOffset) ? QuickLoc(this._FixOffset).x : QuickLoc(this.srcElement).x);});
		eventProto.__defineGetter__('offsetY',function() {return window.pageYOffset + this.clientY - ((this._FixOffset) ? QuickLoc(this._FixOffset).y : QuickLoc(this.srcElement).y);});
		eventProto.__defineGetter__('x',function() {return this.offsetX;});
		eventProto.__defineGetter__('y',function() {return this.offsetY;});
		eventProto.__defineGetter__('returnValue',function() {return this.cancelDefault;});
		eventProto.__defineSetter__('returnValue',function(v) {if (!v) {this.preventDefault()}; this.cancelDefault = v;return v;});
		eventProto.__defineGetter__('button', function() {return (this.which == 1) ? 1 : (this.which == 3) ? 2 : 0});
		eventProto.__defineGetter__('fromElement',function() {
				var n;
				if (this.type == "mouseover")
					n = this.relatedTarget;
				else if (this.type == "mouseout")
					n = this.target;
				return GetNonTextNode(n);});
		eventProto.__defineGetter__('toElement',function() {
				var n =null;
				var ex;
				try
				{
					if (this.type == "mouseout")
						n = this.relatedTarget;
					else if (this.type == "mouseover")
						n = this.target;
				}
				catch(ex){}
				return GetNonTextNode(n);
				});
	}
}
$DebugEventCompat();

$Debug.inspectObject = function(obj,label)
{
	label = label || null;
	var m_debugWindow = $Debug.DebugWindow.Instance;
	if( m_debugWindow )
	{
		m_debugWindow.inspectObject(obj,label);
	}

	try{
		obj.__InspectLabel = label;
	}catch(e){}
	$Debug._InpectedObjects.push(obj);
}

$Debug.addEventHandler = function(obj)
{
	var r =
	{
		myEvents: [],
		bindEvents: function(objHandlers)
		{
			this.myEvents.push(objHandlers);
		},
		unBindEvents: function(objHandlers)
		{
			var ev = this.myEvents;
			for(var i = ev.length;i>=0;i--)
			{
				if( objHandlers )
				{
					if( ev[i] && ev[i] == objHandlers)
						ev[i] = null;
				}
				else
				{
					ev.pop();
					ev[i] = null;
				}
			}
		},
		fire: function(eventid,source)
		{
			var ev = this.myEvents;
			for(var i = 0;i < ev.length;i++)
				if( ev[i] && ev[i][eventid] )
					ev[i][eventid](source||this)
		}
	}
	for(var o in r)
		obj[o] = r[o];
}

$Debug.htmlEncode = function(value)
{
    var r = value;
    var map ={"<":"&lt;",">":"&gt;","\"":"&quot;","&":"&amp;"};
    var amplifyDeamplify = function(name){return map[name] || name;}
    if( r )
    {
        r = value.toString().replace(/[\<\>\"\&]/g, amplifyDeamplify);
    }
    return r;
}

$Debug.DebugWindow = function(p_WinArgs)
{
	// DebugWindow
	// Manages Trace,Inspect and Immediate windows

	p_WinArgs = p_WinArgs || {};
    var createElement = $Debug.createElement;
	var m_id = p_WinArgs.id || "_DebugWindow";
	var m_title = "Debug Window";

	p_WinArgs.id = m_id;
	p_WinArgs.title = p_WinArgs.title || m_title;
	if( undefined == p_WinArgs.saveState )
		p_WinArgs.saveState = true;

	// Ensure that there is not already a debug window...
	if( document.getElementById(m_id) && document.getElementById(m_id).owner )
	{
		// Early Return!!!
		return document.getElementById(m_id).owner;
	}

	var m_this = this;
	var m_traceDiv = createElement({tagName: "DIV",innerHTML:"<div></div>"});
	var m_win = new $Debug.Window(p_WinArgs);
	m_win.owner = m_this;

	this.traceTab = m_win.addTab("TraceTab","Trace",m_traceDiv);
	this.immediateTab = m_win.addTab("ImmediateTab","Immediate",createElement({tagName:"DIV"}));
	this.objectViewerTab = m_win.addTab("ObjectViewerTab","Objects",createElement({tagName:"DIV",style:{fontFamily:"Arial"}}));
    this.win = m_win;

	$Debug.addEventHandler(this);

	var m_traces = [];
	$Debug._TraceWindow = function(obj,num)
	{
		num = (undefined == num ? ($Debug._TraceLog.length-1) : num);
		var traceEntry = createElement({tagName: "DIV",id:"_traceEntry"});
		try
		{
			if( obj && obj.join )
			{
				obj = obj.join(", ");
			}
		}
		catch(e){}
		traceEntry.innerHTML = ['<span style="width:40px">',num,':</span>',$Debug.htmlEncode(obj)].join("");
		m_traces.push(traceEntry);
		m_this.insertTrace(traceEntry);
	}

	this.insertTrace = function(div,el)
	{
		el = el || m_this.traceTab.body || null;
		if( el && el.insertAdjacentElement )
			el.insertAdjacentElement("afterBegin",div)
		else if( el && el.firstChild )
			el.insertBefore(div,el.firstChild);
	}

	function parseCurrentTraces()
	{
		var l = 0;
		while(l < $Debug._TraceLog.length)
		{
			$Debug._TraceWindow($Debug._TraceLog[l],l);
			l++;
		}
	}
	parseCurrentTraces();

	var m_IEls =
	{
		debugConsole: null,
		debugConsoleKeyup: null,
		debugConsoleOutput: null,
		DebugImmediate: null
	}

	function ImmediateConsole()
	{
		var body = m_this.immediateTab.body;
		var el = m_IEls;
        // An Immediate Window will be cool
        var ImmediateHTML = '<input id="__DebugConsoleOnUp__" type="checkbox" checked="1"/><label for="__DebugConsoleOnUp__">Eval onkeyup</label><br/><input id="__DebugConsole__" type="text" style="width:99%"><textarea id="__DebugConsoleOutput__" readonly="true" style="width:99%;height:100px;"></textarea>';
        el.DebugImmediate = createElement({tagName:"div",id:"__DebugImmediate",style:{border:"2px inset silver"},innerHTML:ImmediateHTML});
		body.appendChild(el.DebugImmediate);

        el.debugConsole = document.getElementById("__DebugConsole__");
        el.debugConsoleOutput = document.getElementById("__DebugConsoleOutput__");
        el.debugConsoleKeyup = document.getElementById("__DebugConsoleOnUp__");

        el.debugConsole.onkeypress = function(ev)
        {
            ev=ev||event;
            if( ev.keyCode == 13 )
            {
                var js = el.debugConsole.value;
                try
                {
                    el.debugConsoleOutput.value = eval(js);
                }
                catch(e)
                {
                    el.debugConsoleOutput.value = e.description;
                }
            }

        }

        el.debugConsole.onkeyup = function(ev)
        {
            ev=ev||event;
            if( el.debugConsoleKeyup.checked )
            {
                var js = el.debugConsole.value;
                try
                {
                    el.debugConsoleOutput.value = eval(js);
                }
                catch(e)
                {
                    el.debugConsoleOutput.value = e.description;
                }
            }
        }
	}
	ImmediateConsole();

	this.inspectObject = function(obj,label)
	{
		label = label || "anonymous"
		var onclick =  "if(this.attachEvent){this.parentElement.removeNode(true)}else{this.parentNode.parentNode.removeChild(this.parentNode)};return false";
		var objectInfoBox = createElement({tagName: "DIV",id:"__objectInfoBox",innerHTML: ['<A href="#" onclick="',onclick,'">[X]</A>',label].join("")});
		var objectInfo = createElement({tagName:"DIV",id:"__objectInfo"});
		$Debug.ObjectViewer.displayObject(obj,objectInfo,label);
		objectInfoBox.appendChild(objectInfo);
		m_this.objectViewerTab.body.appendChild(objectInfoBox);
	}

	function inspectCurrentObjects()
	{
		var objs = $Debug._InpectedObjects;
		var l = objs.length-1;
		var label;
		while( l >=0 )
		{
			try{
				label = objs[l].__InspectLabel;
			}catch(e){
				label = null;
			}
			m_this.inspectObject(objs[l],label);
			l--;
		}
	}
	inspectCurrentObjects();

	function removeTraces()
	{
		for(var i = m_traces.length;i >= 0;i--)
		{
			$Debug.removeNode(m_traces[i]);
			m_traces[i] = null;
		}
	}

	this.dispose = function()
	{
		if( m_IEls )
		{
			if( m_IEls.debugConsole )
			{
				m_IEls.debugConsole.onkeyup = null;
				m_IEls.debugConsole.onkeypress = null;
			}
			for(var o in m_IEls)
			{
				if( m_IEls[o] )
					$Debug.removeNode(m_IEls[o]);
				m_IEls[o] = null;
			}
		}
		// removeTraces();
		if(m_traceDiv)
		{
			m_traceDiv.innerHTML = "";
			$Debug.removeNode(m_traceDiv);
			$Debug.removeNode(m_this.traceTab.body);
			m_traceDiv = null;
			m_this.traceTab.body = null;
			m_this.traceTab = m_this.immediateTab = m_this.objectViewerTab = null;
		}

		if( m_win && !m_win.disposing)
			m_win.close();

		this.fire("onclose");

		this.unBindEvents();

	}

	this.close = function()
	{
		this.dispose();
	}

    this.hide = function()
    {
        m_win.hide();
    }

    this.show = function()
    {
        m_win.show();
    }

	this.traceTab.show();

	$Debug.DebugWindow.Instance = m_this;
}

$Debug.AlertObject = function(obj)
{
	var s = [];
	for(var o in obj)
	{
		try{
			s.push(o + ": " + obj[o]);
		}catch(e){
			s.push(o + ": " + e.description);
		}
	}
alert(s.join("\n"));
}

$Debug.Log = function(obj)
{
	var num = Web.Debug.LogNum || 0;
	if( !Web.Debug.LogElement )
	{
		var t = document.createElement("DIV");
		var l = document.createElement("DIV");
		var log = document.getElementById("debugLog") || document.createElement("DIV");
		log.id = "debugLog";
		log.appendChild(t);
		log.appendChild(l);
		t.style.backgroundColor = "#00A";
		t.style.color = "#FFF";
		t.innerHTML = '<a href="#" accesskey="Q" style="color:#FFF;text-decoration:none" onclick="this.parentNode.parentNode.style.display=\'none\';return false;">Debug Output</a>';
		Web.Debug.LogElement = l;
		Web.Debug.LogNum = 0;
		var style = {position:"absolute",left:"0px",top:"0px",height:"200px",width:"400px",backgroundColor: "#FFF",filter:"alpha(opacity:60)",overflow:"auto",fontSize:"8pt",backgroundColor: "#FFF", color: "#A00", zIndex: "90000"};
		for(var o in style)
		{
			log.style[o] = style[o];
		}
		document.body.appendChild(log);
	}

	if( obj )
	{
		var d = document.createElement("DIV");
		d.innerHTML = [Web.Debug.LogNum,obj];
		Web.Debug.LogElement.insertAdjacentElement("afterBegin",d);
		Web.Debug.LogNum++;
		Web.Debug.LogElement.parentNode.style.display = "block";
	}
}


/*
NOTE: Just design right now....
JSDoc Template:
JS Doc will follow closely with NDocs with a few minor differences.

Rules:
    1.  Comments must be stated inside the function.
        This is for when sniffing for parameters and summary inline, the js can only see
        inside the function when only scoped to that function

/// <summary>
///     Describes the function or object.
/// </summary>

/// <param        Used to list parameters
///     type=""   Since JS is loose typed, this is were we can specify the desired type.
///     name="">  Parameter Name
///
///     Describe the parameter as what should be sent
///
/// </param>

/// <remarks>
///     Describes overall high-level functionality for objects,classes.
/// </remarks>

/// <example>
///     Example Description
///     Code blocks can be wrapped in a code element
///     <code>
///         var r;
///         if( ammountInPocket == 0 )
///         {
///             r = "Your out of dough.";
///         }
///     </code>
/// </example>

/// <returns
///     type="">
///         Describe what the function returns.
///         Provide the type
/// </returns>

/// Valid Types:
///     bool
///     string
///     number
///     array
///     regexp
///     error
///     date
///     object  :   Usually this means a custom object
                    Please reference the object constructor as type and if everything is documented,
                    JSDocs should be able to provide a references to that object as well.

Template:

/// <summary>
///
/// </summary>
/// <example>
///
/// </example>
/// <param name="" type=""></param>
/// <returns>
///
/// </returns>

*/

