function Ajax() {

	var socket;
	var method;
	var target;
	var remote;
	var element;
	var tagtype;
	var postdata;
	var history;
	var bookmark;
	var elapse;
	var timer;
	var timer2;
	var requests;

	// cria objeto de conexao
	this.create_socket=function() {
		this.requests=0;
		if(window.XMLHttpRequest) {
			this.socket=new XMLHttpRequest();
		} else {
			var xmlhttp_versions=new Array('MSXML2.XMLHTTP.6.0','MSXML2.XMLHTTP.5.0',
			'MSXML2.XMLHTTP.4.0','MSXML2.XMLHTTP.3.0','MSXML2.XMLHTTP','Microsoft.XMLHTTP');
			for(var loop=0;loop<xmlhttp_versions.length && !this.socket;loop++) {
				try {
					this.socket=new ActiveXObject(xmlhttp_versions[loop]);
				} catch(errormsg) {
					this.socket=false;
				}
			}
		}
		if(!this.socket) {
			alert('ERROR: could not create the connection: '+errormsg.toString());
			throw('RUNTIME ERROR: '+errormsg.toString());
		}
		return this.socket;
	};

	// faz requisicao ao servidor
	this.http_request=function() {
		if(!this.socket) this.create_socket();
		if(this.socket.readyState==0 || this.socket.readyState==4) {
			if(!this.remote) {
				alert('ERROR: remote url not found for "'+this.target+'" ('+this.method+')');
				throw('RUNTIME ERROR: remote url not found');
			}
			if(!/^http/i.test(this.remote)) {
				var form=document.createElement('FORM');
				form.action=this.remote;
				this.remote=form.action;
			}
			this.remote=this.remote.replace(/[+]+/g,' ');
			if(typeof(this.target)=='string') {
				this.element=el(this.target);
			} else if(typeof(this.target)=='object') {
				this.element=this.target;
			} else {
				this.element=this.target;
				this.tagtype='other';
			}
			if(this.element) {
				if(this.tagtype!='other' && this.element.nodeName) {
					this.tagtype=this.element.nodeName.toLowerCase();
					if(this.tagtype=='input') this.tagtype=this.element.type;
					else if(typeof(this.tagtype)=='undefined') this.tagtype='other';
				}
			}
			if(this.element && (this.method=='get' || this.method=='post') && /div|span|td|th|p|body/i.test(this.tagtype)) {
				//var elem_pos=pageCenter(120,22);
				//var elem_pos=findPos(this.element);
				var elem_pos=getPageCenter(32,32);
				//var innerSize=getInnerSize();
				//var pageScroll=getPageScroll();
				//if(elem_pos.x<pageScroll.x || elem_pos.y<pageScroll.y || elem_pos.x>(pageScroll.x+innerSize.width-118) || elem_pos.y>(pageScroll.y+innerSize.height-21)) {
				//	setPageScroll((elem_pos.x>20?(elem_pos.x-20):0),(elem_pos.y>20?(elem_pos.y-20):0));
				//}
				var load_msg=document.createElement('DIV');
				load_msg.id=this.target+'_msg';
				//load_msg.style.color='#ffffff';
				//load_msg.style.border='solid 1px #000000';
				//load_msg.style.backgroundColor='#ff0000';
				load_msg.style.position='absolute';
				load_msg.style.display='block';
				load_msg.style.overflow='hidden';
				load_msg.style.left=(elem_pos.x)+'px';
				load_msg.style.top=(elem_pos.y)+'px';
				load_msg.style.width='32px';
				load_msg.style.height='32px';
				load_msg.style.zIndex=zIndex();
				//load_msg.style.padding='1px 6px 2px 2px';
				//load_msg.style.margin='0px';
				//load_msg.style.fontWeight='bold';
				//load_msg.style.fontFamily='verdana, arial, sans-serif, tahoma';
				load_msg.appendChild(ajax_loading_image);
				//if(language=='portuguese') var text_msg='&nbsp;Carregando...';
				//else if(language=='spanish') var text_msg='&nbsp;Cargando...';
				//else var text_msg='&nbsp;Loading...';
				//load_msg.innerHTML+=text_msg;
				//this.element.appendChild(load_msg);
				document.body.appendChild(load_msg); // correção para o findPos()
			}
			this.method=this.method.toLowerCase();
			var remote_url=encodeURI(this.remote);
			if(/get|data|xml/i.test(this.method)) { // metodo GET //
				if(!this.target) {
					alert('ERROR: target not found for "'+this.remote+'"');
					throw('RUNTIME ERROR: target not found');
				}
				if(/[a-z0-9_-]\.(php|phtml)/i.test(this.remote)) {
					if(remote_url.indexOf('?')<0) {
						remote_url+='?rnd='+new Date().getTime();
					} else {
						remote_url+='&rnd='+new Date().getTime();
					}
					if(this.remote.indexOf(session_name)<0) {
						if(remote_url.indexOf('?')<0) {
							remote_url+='?'+session_name+'='+session_id;
						} else {
							remote_url+='&'+session_name+'='+session_id;
						}
					}
					remote_url+='&session_name='+session_name;
				}
				this.socket.open('GET',remote_url,true);
				this.socket.setRequestHeader('Content-Type','text/html; charset=ISO-8859-1');
				this.socket.send('');
			} else if(this.method=='post') { // metodo POST //
				if(!this.target) {
					alert('ERROR: target not found for "'+this.remote+'"');
					throw('RUNTIME ERROR: target not found');
				}
				if(!this.postdata) {
					alert('ERROR: post data not found for "'+this.remote+'"');
					throw('RUNTIME ERROR: post data not found');
				} else {
					this.postdata+='&rnd='+new Date().getTime();
					if(this.postdata.indexOf(session_name)<0) {
						this.postdata+='&'+session_name+'='+session_id;
					}
					this.postdata+='&session_name='+session_name;
				}
				this.socket.open('POST',remote_url,true);
				this.socket.setRequestHeader('Content-Type','application/x-www-form-urlencoded; charset=ISO-8859-1');
				this.socket.send(this.postdata);
			} else if(this.method=='head') { // metodo HEAD //
				if(remote_url.indexOf('?')>0) {
					remote_url+='&rnd='+new Date().getTime();
				} else {
					remote_url+='?rnd='+new Date().getTime();
				}
				if(remote_url.indexOf(session_name)<0) {
					remote_url+='&'+session_name+'='+session_id;
				}
				remote_url+='&session_name='+session_name;
				this.socket.open('HEAD',remote_url,true);
				this.socket.send('');
			} else {
				alert('ERROR: method "'+this.method+'" not supported');
				throw('RUNTIME ERROR: method not supported');
			}
			var that=this;
			this.timer2=setInterval(function(){that.timeout_check();},5000);

			// gerencia retorno da requisicao
			this.socket.onreadystatechange=function() {
				if(el(that.target+'_msg')) {
					setTimeout(function(){
						removeElement(that.target+'_msg');
					},500);
				}
				if(!that.socket) {
					alert('ERROR: connection is not available for "'+that.remote+'"');
					throw('RUNTIME ERROR: connection is not available');
				} else if(that.socket.readyState==4) { // retorno recebido
					clearInterval(that.timer2); // cancela checagem de timeout
					var socket_error=false;
					try {
						var socket_status=that.socket.status;
					} catch(socket_error) {
						try {
							that.socket.abort();
						} catch(cant_abort) {
							socket_error+='\n'+cant_abort;
						}
						var socket_status=false;
					}
					if(!socket_error && (socket_status==200 || socket_status==0)) {
						if(/get|post/i.test(that.method)) { // retornos GET e POST //
							if(/text$|textarea|file|password|hidden|button|checkbox|option|radio|reset|submit/i.test(that.tagtype)) {
								that.element.value=that.socket.responseText;
							} else if(that.tagtype!='other' && typeof(that.element)!='undefined') {
								try {
									that.element.innerHTML=that.socket.responseText;
								} catch(errormsg) {
									that.element.innerHTML=errormsg.toString();
									alert('ERROR: possible invalid nested tags while loading "'+that.remote+'" into "'+that.target+'"');
									throw('RUNTIME ERROR: invalid nested tags');
								}
								// executa codigos javascript encontrados
								var scripts=that.element.getElementsByTagName('script');
								for(var loop=0;loop<scripts.length;loop++) {
									if(!scripts[loop].src && scripts[loop].innerHTML) {
										run_script(scripts[loop].innerHTML);
									} else {
										var runform=document.createElement('FORM');
										var runscript=document.createElement('SCRIPT');
										runscript.src=scripts[loop].src;
										runform.action=scripts[loop].src;
										runscript.id=md5(runform.action);
										//runscript.id=md5(runscript.src);
										if(el(runscript.id)) {
											removeElement(runscript.id);
										}
										document.body.appendChild(runscript);
									}
								}
							} else if(typeof(that.target)=='function') {
								var raw_data=that.socket.responseText;
								with(document) { // executa funcao
									that.target(raw_data,that.socket.getAllResponseHeaders());
								}
							}
						} else if(/data|xml/i.test(that.method)) { // retornos DATA e XML //
							var xml_data=that.socket.responseXML;
							var raw_data=that.socket.responseText;
							if(!xml_data || !xml_data.documentElement || xml_data.length==0) {
								if(raw_data.indexOf('<?xml')>-1) {
									alert('ERROR: invalid XML structure:\n'+raw_data);
									throw('RUNTIME ERROR: invalid XML structure');
								}
							} else if(xml_data.documentElement.nodeName=='parsererror') {
								alert('ERROR: invalid XML structure:\n'+raw_data);
								throw('RUNTIME ERROR: invalid XML structure');
							} else if(xml_data.indexOf && (xml_data.indexOf('ERRNO')>-1 || xml_data.indexOf('error:')>-1 || raw_data.indexOf('error</strong>:')>-1)) {
								alert('ERROR: '+xml_data+':\n'+that.socket.responseText);
								throw('RUNTIME ERROR: invalid XML structure');
							} else {
								var raw_data=xml_data.documentElement;
							}
							if(typeof(that.target)=='function') {
								with(document) { // executa funcao
									that.target(raw_data,that.socket.getAllResponseHeaders());
								}
							} else {
								alert('ERROR: target "'+that.target+'" is not a function');
								throw('RUNTIME ERROR: target is not a function');
							}
						} else if(that.method=='head') { // retornos HEAD //
							if(typeof(that.target)!='undefined' && that.target) {
								var elapsed_time=new Date().getTime()-that.elapse;
								with(document) {
									that.target(elapsed_time,that.socket.getAllResponseHeaders());
								}
							}
						}
						that.history_fix(); // ajusta historico do navegador
						//alert('id: '+that.target+'  response: '+that.socket.responseText); // debug
					} else { // erro de conexao
						if(!/keepalive/i.test(that.remote)) {
							if(!socket_error) {
								var socket_statusText=that.socket.statusText;
								if(!socket_statusText) {
									var socket_statusText='request failure';
								}
							} else {
								socket_status='408/504';
								var socket_statusText='request or gateway timeout';
							}
							/* if(language=='portuguese') {
								alert('Não foi possível carregar a página de:\n"'+this.remote+'"\nErro '+socket_status+': '+socket_statusText);
							} else if(language=='spanish') {
								alert('No fue posible cargar la pagina de:\n"'+this.remote+'"\nError '+socket_status+': '+socket_statusText);
							} else {
								alert('It was not possible to load the web page from:\n"'+this.remote+'"\nError '+socket_status+': '+socket_statusText);
							} */
							//throw('RUNTIME ERROR: request failure '+socket_statusText);
						}
					}
				}
			}
		} else {
			if(this.requests<30) {
				this.requests++;
				var that=this;
				setTimeout(function(){that.http_request()},500);
			}
		}
	};

	// verifica timeout da requisicao
	this.timeout_check=function() {
		var load_time=new Date().getTime()-this.elapse;
		if(load_time>260000) {
			clearInterval(this.timer2);
			this.socket.abort();
			delete this.socket;
			if(this.remote.indexOf('keepalive.php')<0) {
				/* if(language=='portuguese') {
					alert('ERRO: tempo de conexão expirado ao carregar "'+this.remote+'"\nO servidor não respondeu.');
				} else if(language=='spanish') {
					alert('ERROR: tiempo de conexión expirado al cargar "'+this.remote+'"\nEl servidor no lo respondió.');
				} else {
					alert('ERROR: connection timeout while loading "'+this.remote+'"\nThe server did not answer.');
				}
				throw('RUNTIME ERROR: connection timeout'); */
			}
		}
	}

	// adiciona entrada no historico do navegador
	this.history_fix=function() {
		var that=this;
		if(!document.loaded && !this.timer) {
			this.timer=setTimeout(function(){that.history_fix()},200);
			return;
		} else if(this.timer) {
			clearTimeout(this.timer);
		}
		if(this.remote.indexOf('://')>-1) { // FIX BOOKMARK
			var base_url=document.createElement('A');
			base_url.href=this.remote;
			base_url=base_url.pathname.substr(1);
		} else {
			var base_url=this.remote;
		}
		if(this.history==true) {
			if(isIE || !this.bookmark) {
				window.last_remote=this.remote;
				if(!el('history_frame')) {
					var history_frame=document.createElement('IFRAME');
					history_frame.setAttribute('id','history_frame');
					history_frame.setAttribute('width',0);
					history_frame.setAttribute('height',0);
					history_frame.style.display='none';
					document.body.appendChild(history_frame);
				} else {
					var history_frame=el('history_frame');
				}
				var frame_url=libpath+'history.php?target='+this.target;
				frame_url+='&remote='+escape(this.remote)+'&rnd='+new Date().getTime();
				history_frame.src=frame_url;
			} else {
				window.last_bookmark=this.target+':'+escape(base_url);
				window.location.hash=this.target+':'+escape(base_url);
				if(!document.history_timer) {
					document.history_timer=setInterval(function(){that.check_history()},500);
				}
			}
		}
		// ajusta url no navegador
		if(this.bookmark==true && (isIE || !this.history)) {
			var url_href=window.location.href;
			url_href=url_href.substr(0,url_href.indexOf('#')-1);
			var url_hash=this.target+':'+escape(base_url);
			window.location.replace(url_href+'#'+url_hash);
		}
	}

	// verifica historico do navegador
	this.check_history=function() {
		var url_hash=window.location.hash.substr(1);
		if(/[0-9a-z_]+[:].+/i.test(url_hash)) {
			if(window.last_bookmark && window.last_bookmark!=url_hash) {
				window.last_bookmark=url_hash;
				var remote_url=url_hash.split(':');
				get_remote(remote_url[0],remote_url[1]);
			} else {
				window.last_bookmark=url_hash;
			}
		}
	}

} // fim da classe Ajax

// executa codigos em javascript
function run_script(script_src) {
	with(document) {
		try {
			eval(script_src);
		} catch(errormsg) {
			alert('SCRIPT ERROR: "'+errormsg.toString()+'" in:\n'+script_src);
			throw('RUNTIME ERROR: '+errormsg.toString());
		}
	}
}

// obtem dados de um formulario e retorna uma string
function get_form(form) {
	if(typeof(form)!='object') {
		alert('ERROR: Invalid form "'+form.toString()+'" ('+typeof(form)+')');
		throw('RUNTIME ERROR: invalid form at get_form()');
	}
	var postdata='',upload_url='';
	var elements=form.getElementsByTagName('INPUT');
	for(var loop=0;loop<elements.length;loop++) {
		var elem=elements[loop];
		if(elem.type=='text' || elem.type=='hidden' || elem.type=='password') {
			postdata+=escape(elem.name)+'='+escape(elem.value)+'&';
		}
		if(elem.type=='checkbox') {
			if(elem.checked) {
				postdata+=escape(elem.name)+'='+escape(elem.value)+'&';
			} else {
				postdata+=escape(elem.name)+'=&';
			}
		}
		if(elem.type=='radio') {
			if(elem.checked) {
				postdata+=escape(elem.name)+'='+escape(elem.value)+'&';
			}
		}
		/* if(elem.type=='file') {
			if(elem.id) {
				upload_url=elem.id;
			} else {
				upload_url=elem.name.replace('[','_').replace(']','');
			}
			file_upload(elem.value,upload_url+'_url');
		} */
	}
	var elements=form.getElementsByTagName('TEXTAREA');
	if(elements.length>0) {
		for(var loop=0;loop<elements.length;loop++) {
			elem=elements[loop];
			postdata+=escape(elem.name)+'='+escape(elem.value)+'&';
		}
	}
	var elements=form.getElementsByTagName('SELECT');
	if(elements.length>0) {
		for(var loop=0;loop<elements.length;loop++) {
			var elem=elements[loop];
			postdata+=escape(elem.name)+'='+escape(elem.options[elem.selectedIndex].value)+'&';
		}
	}
	return postdata;
}

// le dados em url remota e insire em elemento
function get_remote(target,url,history,bookmark) {
	if(typeof(history)=='undefined') history=true;
	if(typeof(bookmark)=='undefined') bookmark=false;
	var ajax=new Ajax();
	ajax.elapse=new Date().getTime();
	ajax.method='get';
	ajax.target=target;
	ajax.remote=url;
	ajax.history=history;
	ajax.bookmark=bookmark;
	ajax.http_request();
};

// envia formulario para o servidor
function submit_form(target,form,url,history,bookmark) {
	if(typeof(history)=='undefined') history=true;
	if(typeof(bookmark)=='undefined') bookmark=false;
	form.onsubmit=function(){return false;}
	var ajax=new Ajax();
	ajax.elapse=new Date().getTime();
	ajax.method='post';
	ajax.target=target;
	if(typeof(url)=='undefined' || !url) {
		ajax.remote=form.action;
	} else {
		ajax.remote=url;
	}
	ajax.postdata=get_form(form);
	ajax.history=history;
	ajax.bookmark=bookmark;
	ajax.http_request();
};

// obtem dados em url remota e retorna para funcao
function get_data(target,url) {
	var ajax=new Ajax();
	ajax.elapse=new Date().getTime();
	ajax.method='data';
	ajax.target=target;
	ajax.remote=url;
	ajax.history=false;
	ajax.bookmark=false;
	ajax.http_request();
}

// teste de ping em url
function ping_remote(target,url) {
	var ajax=new Ajax();
	ajax.elapse=new Date().getTime();
	ajax.method='head';
	ajax.target=target;
	ajax.remote=url;
	ajax.history=false;
	ajax.bookmark=false;
	ajax.http_request();
}
