/**
@created 13/07/2006
@updated 18/11/2006

@usage
	apenas chamar a funçao global:
	T(msg) para mostrar mensagens em texto com formatação especial para cada tipo
	D(var) faz for in nos objetos não hierarquicamente
	W(msg) escreve o dado na saída sem nenhum tipo de formatação ou quebra-de-linha
	Dbg.enabled = false; // para desabilitar o debug
@todo
	Criar atalho como duplo click com uma tecla pressionada para limpar a tela
	Hoje o DBG funciona em uma janela. Refatorar para que ele funcione em qualquer elemento que passar para ele (htmlElement ou textarea),
		como por exemplo uma td de uma tabela.
**/

var Dbg = {

	started : false, // controla se a janela já foi aberta
	wnd : null, // referência para a janela
	width : window.screen.availWidth - 800, // largura da janela de debug é o que sobrar além de 800
	area : null, // textarea
	enabled : false, // liga/desliga o debug
	areaTag : "div", // textarea ou div
	css : "<style>.\
body{line-height:20px}\
.String{color:darkred} .Number{color:purple} .object{color:darkgreen} .RegExp{color:green} .Array{color:darkblue} .Boolean{color:olive}\
.NativeFunction{color:blue} .DefinedFunction{color:orange} .Exception{color:fuchsia} .null{color:gray} .undefined{color:red}\
</style>",

	start: function()
	{
		// vários testes para manter compatibilidade entre MOZ e IE
		if (this.wnd && this.started && ! this.wnd.closed) return

//		this.wnd = this.openDebugWindow()
		this.resizeMainWindow()
		this.createDocument()
		this.area = this.wnd.document.getElementById('out')
		this.started = true
		window.focus()

		var parent = this
		window.onunload = function()
		{
			if(parent.wnd) // utilizar parent, pois this faz referência à janela
				parent.wnd.close()
		}

		// funciona apenas no mozilla. Não consegui atribuir eventos para a janela popup,
		// apenas utilizando a chamada no body do html dentro do popup
		this.wnd.onunload = function() { parent.onPopupClose() }
	},

	// @@@@@@@@@@@@@@@@@@@@ MÈTODOS DE TRATAMENTO E CRIAÇÃO DA JANELA @@@@@@@@@@@@@@@@@@@@

	/**
	 * Quando fechar o popup de debug, precisa marcar started como false,
	 * Pois a variável da janela já não existirá mais
	*/
	onPopupClose: function()
	{
		this.restoreMainWindow()
		this.started = false
	},

	openDebugWindow: function()
	{
		var features = {
			width : this.width - 10, //pixels de tolerância para o IE
			height : window.screen.availHeight - 40, //pixels de tolerância para o IE
			top : 0,
			left : window.screen.availWidth - this.width
		}

		return this.openWindow("", "dbg", features)
	},

	/**
	* Função genérica para abrir janela popup
	*/
	openWindow : function(url, name, features)
	{
		var strFeatures = ""
		for(var i in features)
			strFeatures += i + "=" + features[i] + ","

		return window.open(url, name, strFeatures)
	},

	/**
	 * Alinha a janela principal no lado esquerdo da tela ao lado da janela de debug do lado direito
	*/
	resizeMainWindow: function()
	{
		/**
		 * IE: Caso a janela principal esteja sendo arrastada, o camando abaixo irá gerar uma Exceção de Acesso Negado
		*/
		try {
			window.moveTo(0, 0)
			window.resizeTo(window.screen.availWidth - this.width, window.screen.availHeight)
		} catch(e) {
			/**
			 * Tentamos novamente após 1 segundo, até conseguir
			*/
			var parent = this
			setTimeout(function() { parent.resizeMainWindow.apply(parent) }, 1000)
		}
	},

	/**
	 * Quando fechar a janela de debug, retorna a janela principal para maximizado
	*/
	restoreMainWindow: function()
	{
		window.moveTo(0, 0)
		window.resizeTo(window.screen.availWidth, window.screen.availHeight)
	},

	createDocument: function()
	{
		this.wnd.document.write(
			this.css + 
			"\
			<body style='margin: 0; padding: 0'>\
				<" + this.areaTag + " readonly='readonly' id='out' wrap='off'\
					style='width:100%; height: 100%; font-family: Courier, \"Courier New\", monospace; font-size: 9px; overflow: auto'\
				></" + this.areaTag + ">\
			</body>\
			"
		)
		this.wnd.document.close()
	},

	/**
	 * Mantém a última linha sempre visível
	*/
	scroll: function()
	{
		this.area.scrollTop = this.area.scrollHeight
	},
	
	// @@@@@@@@@@@@@@@@@@@@ MÈTODOS DE ESCRITA NA JANELA @@@@@@@@@@@@@@@@@@@@
	
	trace: function(msg)
	{
		var type = this.getType(msg);
		msg = this.formatType(msg);
		this.write(">" + msg + "\n", type);
	},

	dump: function(obj)
	{
		this.trace(obj)
		for(var i in obj)
		{
			try
			{
				var v = obj[i]
				var type = this.getType(v);
				var msg = this.formatType(v);
				this.write(" " + i + ":" + msg + "\n", type)
			}
			catch(e)
			{
				this.write(i + ":EXCEPTION\n", "Exception")
			}
		}
	},	

	/**
	  * Escreve a string diretamente na saída, sem nenhum tipo de formatação ou concatenação
	  * @param type Informa o tipo, para fazer sintaxe colorida quando a areaTag == "div"
	  */
	write: function(msg, type)
	{
		if (!this.enabled) return
		this.start()

		try { // Novamente problema de acesso com o IE
			if (this.areaTag == "textarea")// precisa do if pois innerHTML não é aceito no IE para textarea
				this.area.value += msg
			else
			{
				msg = this.encodeHtmlSpecialChars(msg);
				if (type) msg = this.highlightType(msg, type);
				this.area.innerHTML += msg;
			}
			this.scroll()
		} catch (e) {
			var parent = this
			setTimeout(function() { parent.write.apply(parent, msg) }, 1000)
		}
	},

	clear: function()
	{
		if (this.area) this.areaTag == "textarea" ? this.area.value = "" : this.area.innerHTML = ""
	},

	// @@@@@@@@@@@@@@@@@@@@ MÈTODOS DE FORMATAÇÃO @@@@@@@@@@@@@@@@@@@@

	encodeHtmlSpecialChars: function(code)
	{
		code = code.replace(/&/g, "&amp;")
		code = code.replace(/"/g, "&quot;")
		code = code.replace(/'/g, "&apos;")
		code = code.replace(/</g, "&lt;")
		code = code.replace(/>/g, "&gt;")
		code = code.replace(/\r\n|\n|\r/g, "<br />")

		return code;
	},

	highlightType: function(msg, type)
	{
		return "<span class='"+ type +"'>"+ msg +"</span>";
	},

	/**
	 * true, caso o parâmetro seja uma função. Isto é preciso pois no IE, native code é considerado objeto e não Function
	*/
	isFunction: function(v)
	{
		if (v instanceof Function) return true
		var str = new String(v).replace(/(\r|\n|\s)+/g, "")
		return str.search(/function.*\(.*\)\{.*\}/) !== -1
	},

	/**
	 * true se além de ser uma função, também ser código nativo do navegador/javascript
	 * false caso não seja função, ou seja função mas definida pelo usuário
	*/
	isNativeFunction: function (v)
	{
		var str = new String(v).replace(/(\r|\n|\s)+/g, "")
		return str.search(/function.*\(.*\)\{\[nativecode\]\}/) !== -1
	},

	/**
	 * O processo de reconhecimento de tipos no JavaScript é complexo e diferente entre os navegadores
	*/	
	getType: function(type)
	{
		if (typeof type == "boolean") return "Boolean"
		if (type === null) return "null" // typeof null também é object
		if (type === undefined) return "undefined"
		if(type instanceof RegExp) return "RegExp"
		if(typeof type == "number") return "Number"
		if (type.constructor == String) return "String" // mostra string sempre entre aspas
		if (type instanceof Array) return "Array" // typeof de array retorna object
		if (this.isNativeFunction(type)) return "NativeFunction"
		if (this.isFunction(type)) return "DefinedFunction"
		if (typeof type == "object") return "object"
		return "default"
	},

	/**
	 * format um dado de acordo com seu tipo
	*/
	formatType: function(obj, type)
	{
		if (!type) type = this.getType(obj);
		switch(type)
		{
			case "object": return "{" + this.formatObject(obj) + "}"
			case "NativeFunction": return "function NATIVE"
			case "DefinedFunction": return "function DEFINED"
			case "Array": return "[" + obj + "]" // typeof de array retorna object
			case "String": return "\"" + obj + "\"" // mostra string sempre entre aspas
			case "null":
			case "undefined":
			case "RegExp":
			case "Number":
			case "Boolean":
			default: return obj
		}
	},
	
	/**
	 * A string do objeto é neste formato [object Object] e no ie, as vezes apenas assim [object]
	 * este método retira os colchetes e a primeira palavra object, convertendo:
	 * [object HTMLBodyElement] para apenas HTMLBodyElement
	 */
	formatObject:function(obj)
	{
	    try {
	        return new String(obj).match(/\[(?:object )?(\w+)\]/)[1]
	    }catch(e){ // caso não esteja no formato [object Object], mostrar como é, sem formatação
	        return new String(obj)
	    }
	}
}

// GLOBAL HOOK
 window.T = function(msg) { Dbg.trace(msg) }
 window.W = function(msg) { Dbg.write(msg) }
 window.D = function(obj) { Dbg.dump(obj) }
Dbg.enabled = false;