function DebugReq() {}

DebugReq.timeout = 5 * 60 * 1000; // 5 minutes

DebugReq.makeRequest = function(p_url, p_busyReq, p_progId, p_successCallBack, p_errorCallBack, p_pass, p_object) {
	//p_url: the web service url
	//p_busyReq: is a request for this object currently in progress?
	//p_progId: element id where progress HTML should be shown
	//p_successCallBack: callback function for successful response
	//p_errorCallBack: callback function for erroneous response
	//p_pass: string of params to pass to callback functions
	//p_object: object of params to pass to callback functions
	if (p_busyReq) {
		return;
	}
	var req = DebugReq.getRequest();
	if (req != null) {
		p_busyReq = true;
		DebugReq.showProgress(p_progId);
		req.onreadystatechange = function() {
			if (req.readyState == 4) {
				p_busyReq = false;
				window.clearTimeout(toId);
				if (req.status == 200) {
					p_successCallBack(req,p_pass,p_object);
				} else {
					p_errorCallBack(req,p_pass,p_object);
				}
			}
		}
		var $ajax_mark = (p_url.indexOf('?') ? '&' : '?') + 'ajax=yes';
		req.open('GET', p_url + $ajax_mark, true);
		req.setRequestHeader('If-Modified-Since', 'Sat, 1 Jan 2000 00:00:00 GMT');
		req.send(null);
		var toId = window.setTimeout( function() {if (p_busyReq) req.abort();}, DebugReq.timeout );
	}
}

DebugReq.getRequest = function() {
	var xmlHttp;
	try { xmlHttp = new ActiveXObject('MSXML2.XMLHTTP'); return xmlHttp; } catch (e) {}
	try { xmlHttp = new ActiveXObject('Microsoft.XMLHTTP'); return xmlHttp; } catch (e) {}
	try { xmlHttp = new XMLHttpRequest(); return xmlHttp; } catch(e) {}
	return null;
}

DebugReq.showProgress = function(p_id) {
	$Debugger.AppendRow(DebugReq.getProgressHtml());
}

DebugReq.getProgressHtml = function() {
	return 'Loading ...';
}

DebugReq.getErrorHtml = function(p_req) {
	//TODO: implement accepted way to handle request error
	return "<p>" + "(" + p_req.status + ") " + p_req.statusText + "</p>"
}

// Debugger
function Debugger($params) {
	this.RowSeparator = '';
	this.IsFatalError = false;
	this.ErrorsCount = 0;
	this.SQLCount = 0;
	this.SQLTime = 0;
	this.ScriptTime = 0;
	this.ScriptMemory = 0;
	this.Shortcut = 'F12';

	for (var $param_name in $params) {
		this[$param_name] = $params[$param_name];
	}

	this.IsQueried = false;
	this.IsVisible = false;
	this.DebuggerDIV = document.getElementById('debug_layer');
	this.DebuggerTable = document.getElementById('debug_table');
	this.RowCount = 0;
	this.busyRequest = false;

	this.DragObject = null;
	this.LastDragObject = null;
	this.MouseOffset = [0,0];
	this.ResizeHappening = false;
	this.ResizeTimer = null;
	this.InitialPos = null;

//	window.$Debugger = this; // this should be uncommented in case if debugger variable is not $Debugger
	this.AddEvent(window, 'scroll', function (ev) { window.$Debugger.Resize(ev); });
	this.AddEvent(window, 'resize', function (ev) { window.$Debugger.Resize(ev); });
	this.AddEvent(document, 'keydown', function (ev) { window.$Debugger.KeyDown(ev); }); // don't work in IE
}

Debugger.prototype.createEnvironment = function($outer_width, $inner_width) {
	if (!this.DebuggerDIV) {
		// when debugger wasn't added already
		var $container = document.createElement('DIV');
		$container.id = 'debug_layer';
		$container.className = 'debug_layer_container';
		$container.style.display = 'none';
		$container.style.width = $outer_width + 'px';

		var $debug_layer = document.createElement('DIV');
		$debug_layer.className = 'debug_layer';
		$debug_layer.style.width = $inner_width + 'px';
		$container.insertBefore($debug_layer, $container.firstChild);

		var $table = document.createElement('TABLE');
		$table.style.width = '100%';
		$table.className = 'debug_layer_table';
		$table.style.width = $inner_width + 'px';
		$debug_layer.insertBefore($table, $debug_layer.firstChild);

		var $tbody = document.createElement('TBODY');
		$tbody.id = 'debug_table';
		$table.insertBefore($tbody, $table.firstChild);

		var $body = document.getElementsByTagName('BODY')[0];
		$body.insertBefore($container, $body.lastChild);

		this.DebuggerDIV = document.getElementById('debug_layer');
		this.DebuggerTable = document.getElementById('debug_table');
	}
	else {
		this.Clear();
	}
}

Debugger.prototype.SetOpacity = function(opacity)
{
	this.DebuggerToolbar.style.opacity = (opacity / 100);
	this.DebuggerToolbar.style.MozOpacity = (opacity / 100);
	this.DebuggerToolbar.style.KhtmlOpacity = (opacity / 100);
	this.DebuggerToolbar.style.filter = "alpha(opacity=" + opacity + ")";
}

Debugger.prototype.ToolbarClick = function ($button) {
	switch ($button.id) {
		case 'dbg_ReloadFrame':
			self.location.reload();
			break;

		case 'dbg_ShowDebugger':
			this.Toggle();
			break;

		case 'dbg_TurnOff':
			var $exdate = new Date();

			$exdate.setDate( $exdate.getDate() + 365 );
			document.cookie = 'debug_off=1; expires=' + $exdate.toUTCString() + '; path=' + this.BasePath + '/';
			self.location.reload();
			break;
	}
}

Debugger.prototype.jQueryFound = function () {
	return typeof jQuery == 'function';
}

Debugger.prototype.AddToolbar = function($var_name) {
	if (document.getElementById('debug_toolbar_span')) {
		// toolbar was already created before
		if (this.jQueryFound()) {
			$('#debug_toolbar_span').remove();
		}
		else {
			return ;
		}
	}

	var $span = document.createElement('SPAN');
	$span.style.position = 'absolute';
	$span.style.zIndex= 99;
	$span.style.top = '0px';
	$span.style.left = '0px';
	$span.id = 'debug_toolbar_span';
	document.body.style.textAlign = 'left';

	var $toolbar_content = '<td class="dbg-button" id="dbg_ReloadFrame" onclick="' + $var_name + '.ToolbarClick(this);">Reload Frame</td>';

	if (this.ErrorsCount > 0) {
		$toolbar_content += '<td class="dbg-separator"></td><td class="dbg-button debug_error" style="font-weight: bold;" id="dbg_ShowDebugger" onclick="' + $var_name + '.ToolbarClick(this);">Show Debugger (' + this.ErrorsCount + ' errors)</td>';
	}
	else {
		$toolbar_content += '<td class="dbg-button" id="dbg_ShowDebugger" onclick="' + $var_name + '.ToolbarClick(this);">Show Debugger</td>';
	}

	$toolbar_content += '<td class="dbg-button" id="dbg_TurnOff" onclick="' + $var_name + '.ToolbarClick(this);">Turn Off</td>';

	if (this.SQLCount > 0) {
		$toolbar_content += '<td class="dbg-separator"></td><td style="cursor: move"><strong>' + this.SQLCount + '</strong> sqls (' + this.SQLTime + ' s)</td>';
	}

	if (this.ScriptTime > 0) {
		$toolbar_content += '<td class="dbg-separator"></td><td style="cursor: move">' + this.ScriptTime + ' s (' + this.ScriptMemory + ')</td>';
	}

	$span.innerHTML = '<table style="height: 30px" cellpadding="0" cellspacing="3" class="dbg-toolbar"><tr>' + $toolbar_content + '</tr></table>';

	this.DebuggerToolbar = $span;
	this.SetOpacity(20);
	$span.onmouseover = function() {
		$Debugger.SetOpacity(100);
	}
	$span.onmouseout = function() {
		$Debugger.SetOpacity(20);
	}

	var $body = document.getElementsByTagName('BODY')[0];
	$body.insertBefore($span, $body.firstChild);

//	alert($span.offsetWidth)
	this.MakeDragable('debug_toolbar_span', function() {}, function() {}, function() {});
}

Debugger.prototype.AppendRow = function($html) {
	this.RowCount++;
	var $tr = document.createElement('TR');
	this.DebuggerTable.appendChild($tr);
	$tr.className = 'debug_row_' + (this.RowCount % 2 ? 'odd' : 'even');
	$tr.id = 'debug_row_' + this.RowCount;
	var	$td = document.createElement('TD');
	$td.className = 'debug_cell';
	$td.innerHTML = $html;
	$tr.appendChild($td);
}

Debugger.prototype.RemoveRow = function($row_index) {
	this.DebuggerTable.deleteRow($row_index);
	this.RowCount--;
}

Debugger.prototype.Clear = function() {
	if (!this.IsQueried) return false;

	while (this.DebuggerTable.rows.length) {
		this.RemoveRow(0);
	}

	this.Toggle(27);
	this.IsQueried = false;
}

Debugger.prototype.KeyDown = function($e) {
	var $matched_parts = 0,
		$KeyCode = this.GetKeyCode($e),
		$shortcut_parts = this.Shortcut.toLowerCase().split('+');

	$e = ($e) ? $e : event;

	var $modifier_map = {
		'ctrl': $e.ctrlKey,
		'shift': $e.shiftKey,
		'alt': $e.altKey,
		'meta': $e.metaKey
	};

	for (var $i = 0; $i < $shortcut_parts.length; $i++) {
		for (var $modifier in $modifier_map) {
			if ( $shortcut_parts[$i] == $modifier && $modifier_map[$modifier] ) {
				$matched_parts++;
				break;
			}
		}

		if ( this.getKeyCodeFromString($shortcut_parts[$i]) == $KeyCode ) {
			$matched_parts++;
		}
	}

	if ( $matched_parts == $shortcut_parts.length || $KeyCode == 27 ) {// F12 or ESC
		this.Toggle($KeyCode);
		this.StopEvent($e);
	}
}

Debugger.prototype.getKeyCodeFromString = function($string) {
	var $special_keys = {
		'esc': 27,
		'escape': 27,
		'tab': 9,
		'space': 32,
		'return': 13,
		'enter': 13,
		'backspace': 8,

		'scrolllock': 145,
		'scroll_lock': 145,
		'scroll': 145,
		'capslock': 20,
		'caps_lock': 20,
		'caps': 20,
		'numlock': 144,
		'num_lock': 144,
		'num': 144,

		'pause': 19,
		'break': 19,

		'insert': 45,
		'home': 36,
		'delete': 46,
		'end': 35,

		'pageup': 33,
		'page_up': 33,
		'pu': 33,

		'pagedown': 34,
		'page_down': 34,
		'pd': 34,

		'left': 37,
		'up': 38,
		'right': 39,
		'down': 40,

		'f1': 112,
		'f2': 113,
		'f3': 114,
		'f4': 115,
		'f5': 116,
		'f6': 117,
		'f7': 118,
		'f8': 119,
		'f9': 120,
		'f10': 121,
		'f11': 122,
		'f12': 123
	};

	$string = $string.toLowerCase();

	if ( $special_keys[$string] !== undefined ) {
		return $special_keys[$string];
	}

	return $string.charCodeAt(0);
}

Debugger.prototype.OpenDOMViewer = function() {
	var $value = document.getElementById('dbg_domviewer').value;
	DOMViewerObj = ($value.indexOf('"') != -1) ? document.getElementById( $value.substring(1,$value.length-1) ) : eval($value);
	window.open(this.DOMViewerURL);
	return false;
}

Debugger.prototype.GetKeyCode = function($e) {
	$e = ($e) ? $e : event;
	var charCode = ($e.charCode) ? $e.charCode : (($e.which) ? $e.which : $e.keyCode);

	if ( charCode == 27 ) {
		// don't lowercase ESC
		return charCode;
	}

	return String.fromCharCode(charCode).toLowerCase().charCodeAt(0);
}

Debugger.prototype.StopEvent = function($e) {
	$e = ($e) ? $e : event;
	$e.cancelBubble = true;
	if ($e.stopPropagation) $e.stopPropagation();
}

Debugger.prototype.Toggle = function($KeyCode) {
	if(!this.DebuggerDIV) return false;
	this.IsVisible = this.DebuggerDIV.style.display == 'none' ? false : true;
	if (!this.IsVisible && $KeyCode == 27) {
		return false;
	}

	this.Resize(null);
	if (!this.IsQueried) {
		this.Query();
	}

	this.DebuggerDIV.style.display = this.IsVisible ? 'none' : 'block';
}

Debugger.prototype.Query = function() {
	DebugReq.makeRequest(this.DebugURL, this.busyRequest, '', this.successCallback, this.errorCallback, '', this);
}

Debugger.prototype.successCallback = function(p_req, p_pass, p_object) {
	if (p_pass == 'resetCache') {
		alert('Requested action performed.');
		return ;
	}

	var contents = p_req.responseText;

	contents = contents.split(p_object.RowSeparator);
	if (contents.length == 1) {
		alert('error: '+p_req.responseText);
		p_object.IsQueried = true;
		return ;
	}

	for (var $i = 0; $i < contents.length - 1; $i++) {
		p_object.AppendRow(contents[$i]);
	}

	if ( p_object.jQueryFound() ) {
		var $stats_table = $('.dbg_stats_table:first').clone();
		var $statistics_html = $( $('<div></div>').html($stats_table) ).html();

		if ($statistics_html) {
			$('.dbg_stats_table:first').remove();
			p_object.AppendRow($statistics_html);
		}
	}

	p_object.Refresh();
}

Debugger.prototype.errorCallback = function(p_req, p_pass, p_object) {
	alert('AJAX ERROR: '+DebugReq.getErrorHtml(p_req));
	p_object.Refresh();
}

Debugger.prototype.Refresh = function() {
	// progress meter row

	this.RemoveRow(0);
	this.IsQueried = true;
	this.DebuggerDIV.scrollTop = this.IsFatalError ? 10000000 : 0;
	this.DebuggerDIV.scrollLeft = 0;
}

Debugger.prototype.Resize = function($e) {
	if (!this.DebuggerDIV) return false;
	var $pageTop = document.all ? document.body.offsetTop + document.body.scrollTop : window.scrollY;

	this.DebuggerDIV.style.top = $pageTop + 'px';
	this.DebuggerDIV.style.height = GetWindowHeight() + 'px';
	return true;
}

function GetWindowHeight() {
	var currWinHeight;

//	if (document.body.clientHeight) {
//		currWinHeight = document.body.clientHeight;

	if (window.innerHeight) {//FireFox with correction for status bar at bottom of window
		currWinHeight = window.innerHeight;
	} else if (document.documentElement.clientHeight) {//IE 7 with correction for address bar
		currWinHeight = document.documentElement.clientHeight;
	} else if (document.body.offsetHeight) {//IE 4+
		currWinHeight = document.body.offsetHeight + 10;
	}
	return currWinHeight - 10; // 10 - horizontal scrollbar height
}


/*function GetWinHeight() {
	if (window.innerHeight) return window.innerHeight;
	else if (document.documentElement.clientHeight) return document.documentElement.clientHeight;
	else if (document.body.offsetHeight) return document.body.offsetHeight;
	else return _winHeight;
}*/

Debugger.prototype.SetClipboard = function(copyText) {
	if (window.clipboardData) {
		// IE send-to-clipboard method.
		window.clipboardData.setData('Text', copyText);
	}
	else if (window.netscape) {
		// You have to sign the code to enable this or allow the action in about:config by changing user_pref("signed.applets.codebase_principal_support", true);
		netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');

		// Store support string in an object.
		var str = Components.classes['@mozilla.org/supports-string;1'].createInstance(Components.interfaces.nsISupportsString);
		if (!str) {
			return false;
		}
		str.data = copyText;

		// Make transferable.
		var trans = Components.classes['@mozilla.org/widget/transferable;1'].createInstance(Components.interfaces.nsITransferable);
		if (!trans) {
			return false;
		}

		// Specify what datatypes we want to obtain, which is text in this case.
		trans.addDataFlavor('text/unicode');
		trans.setTransferData('text/unicode', str, copyText.length * 2);

		var clipid = Components.interfaces.nsIClipboard;
		var clip = Components.classes['@mozilla.org/widget/clipboard;1'].getService(clipid);
		if (!clip) {
			return false;
		}

		clip.setData(trans, null, clipid.kGlobalClipboard);
	}
}

Debugger.prototype.ShowProps = function($Obj, $Name) {
	var $ret = '';
	for ($Prop in $Obj) {
		$ret += $Name + '.' + $Prop + ' = ' + $Obj[$Prop] + "\n";
	}
	return alert($ret);
}

Debugger.prototype.editFile = function($fileName, $lineNo) {
	if (!document.all) {
		alert('Only works in IE');
		return;
	}

	if (!this.EditorPath) {
		alert('Editor path not defined!');
		return;
	}

	var $launch_object = new ActiveXObject('LaunchinIE.Launch');
	var $editor_path = this.EditorPath;
	$editor_path = $editor_path.replace('%F', $fileName);
	$editor_path = $editor_path.replace('%L',$lineNo);
	$launch_object.LaunchApplication($editor_path);
}

Debugger.prototype.ToggleTraceArgs = function($arguments_layer_id) {
	var $arguments_layer = document.getElementById($arguments_layer_id);
	$arguments_layer.style.display = ($arguments_layer.style.display == 'none') ? 'block' : 'none';
}

Debugger.prototype.AddEvent = function (el, evname, func) {
	var $status = false;
	if (document.all) {
		$status = el.attachEvent('on' + evname, func);
	} else {
		$status = el.addEventListener(evname, func, true);
	}
}

Debugger.prototype.resetCache = function ($event_source) {
	var $events = document.getElementById($event_source);
	var $event = $events.options[$events.selectedIndex].value;

	if (!$event) {
		alert('Please select action to perform first!');
	}
	else if (confirm('Really perform "' + $events.options[$events.selectedIndex].innerHTML + '"?')) {
		DebugReq.makeRequest(this.EventURL + '&' + $event, this.busyRequest, '', this.successCallback, this.errorCallback, 'resetCache', this);
	}
}

Debugger.prototype.mouseCoords = function(ev)
{
	if(ev.pageX || ev.pageY){
		var res = {x:ev.pageX, y:ev.pageY};
	}
	else {
		var res = {
			x:ev.clientX + document.body.scrollLeft - document.body.clientLeft,
			y:ev.clientY + document.body.scrollTop  - document.body.clientTop
		};
	}
	return res;
}

Debugger.prototype.MakeDragable = function(object_id, startCallback, moveCallback, endCallback, options)
{
	var drag_object = document.getElementById(object_id);
	var cur_options = {'VerticalDrag': 1, 'HorizontalDrag': 1};
	if (options) {
		for(var i in options) {
			cur_options[i] = options[i];
		}
	}
	var the_debugger = this;
	this.AddEvent(drag_object, 'mousedown', function(ev){
		ev = ev || window.event;
		the_debugger.InitialPos = dbg_findPos(drag_object);
		var coords = the_debugger.mouseCoords(ev);
		var pos = dbg_findPos(drag_object);
		the_debugger.MouseOffset = [coords.x - pos[0], coords.y - pos[1]];
		the_debugger.DragObject = drag_object;
		the_debugger.LastDragObject = drag_object;
		the_debugger.DragObject.style.position = 'absolute';
		the_debugger.Options = cur_options;
		startCallback(drag_object);
	});
	this.AddEvent(document, 'mousemove', function(ev) {
//		window.status = 'mouse at: '+coords.x+','+coords.y;
		if(the_debugger.DragObject){
			ev = ev || window.event;
			var coords = the_debugger.mouseCoords(ev);
			if (the_debugger.Options.VerticalDrag) {
				the_debugger.DragObject.style.top = (coords.y - the_debugger.MouseOffset[1] ) + 'px' // ;
			}
			if (the_debugger.Options.HorizontalDrag) {
				the_debugger.DragObject.style.left = (coords.x - the_debugger.MouseOffset[0] ) + 'px' // ;
			}
			moveCallback(drag_object, coords)
			return false;
		}
	});
	this.AddEvent(document, 'mouseup', function(ev){
		var tmp = the_debugger.DragObject;
		the_debugger.DragObject = null;
		if(tmp){
			endCallback(tmp);
		}
		var pos = dbg_findPos(drag_object);
	});
}

function dbg_findPos(obj) {
	var curleft = curtop = 0;
	if (obj.offsetParent) {
		curleft = obj.offsetLeft
		curtop = obj.offsetTop
		while (obj = obj.offsetParent) {
			curleft += obj.offsetLeft
			curtop += obj.offsetTop
		}
	}
	return [curleft,curtop];
}