/**

	Título..: VeryTinyAJAX 0.5, Wrapper JavaScript simple a funciones XMLHTTP para AJAX
  Licencia: GPL (http://www.gnu.org/licenses/gpl.txt)
	Autores.: Pablo Rodríguez Rey (mr.xkr -en- inertinc -punto- org)
	          http://mr.xkr.inertinc.org/
            Javier Gil Motos (cucaracha -en- inertinc -punto- org)
            http://cucaracha.inertinc.org/

	Agradecimientos a Cucaracha, por darme interés en el desarrollo de webs usando
	AJAX y proveerme del ejemplo básico con el que está desarrollada esta librería.
	También a Binny V A (binnyva -en- hotmail -punto- com
	http://www.geocities.com/binnyva) por la función adump.

*/


// modo de depuración global
var ajax_debug=true;

// declarar el objeto XML-HTTP global
var http;

// constante de versión de protocolo de transacción
var httpTP="VeryTinyAJAX/tp1:";

// constantes para httpRequest
var hGET=0;
var hPOST=1;

// funciones auxiliares generales
function gid(id) { return(document.getElementById(id)); }

// parsear cadena para ser enviada por GET/POST
function gescape(torg) {
	var d=""+torg;
	try { var d=d.replace(/\"/gi,"%22"); } catch(e) {}
	try { var d=d.replace(/\\/gi,"%5C"); } catch(e) {}
	try { var d=d.replace(/\?/gi,"%3F"); } catch(e) {}
	try { var d=d.replace(/&/gi,"%26"); } catch(e) {}
	try { var d=d.replace(/\+/gi,"%2B"); } catch(e) {}
	try { var d=d.replace(/ /gi,"%20"); } catch(e) {}
	return(d);
}

// hard escape: codifica aparte de los especiales,
// también aquellos cuyo ASCII sea <32 o >127
function hescape(t) {
	var s=gescape(t);
	var r="";
	var c;
	for (var i=0;i<s.length;i++) {
		c=s.charCodeAt(i);
		r+=(c<32 || c>127?"%"+c.toString(16):s.charAt(i));
	}
	return r;
}

// información de versión
function httpVersion() { return("VeryTinyAJAX/0.4b"); }

// crea el objeto XML-HTTP
function httpObject() {
	var xmlhttp;
	// comprobar que el navegador soporta XMLHttpRequest
	try { xmlhttp = new ActiveXObject("Msxml2.XMLHTTP"); }
	catch (e) { try { xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); }
	catch (e) { try { xmlhttp = new XMLHttpRequest(); }
	catch (e) { xmlhttp = false; } } }
	return(xmlhttp);
}

// estado de la petición
function httpState() {
	try { return(http.readyState); }
	catch (e) { return(5); }
}

// cadena de estado de la petición
function httpStateString() {
	try { var rs=http.readyState; }
	catch (e) { var rs=5; }
	switch (rs) {
	case 0: return("Uninitialized")
	case 1: return("Loading");
	case 2: return("Loaded");
	case 3: return("Interactive");
	case 4: return("Complete");
	case 5: return("Server Crashed");
	}
}

// Indicar si se ha completado la operación
function httpComplete() {
	if (http.readyState==4) return(true);
	else return(false);
}

// Devolver los datos recibidos
function httpData() {
	return(http.responseText);
}

// Devolver los datos recibidos en formato documento XML
function httpXML() {
	return(http.responseXML);
}

// Devolver el estado del servidor
// Si se detecta error, el servidor no estará disponible
function httpStatus() {
	try { return(http.status); }
	catch(e) { return(0); }
}

// Comprobar que la respuesta del servidor es la 200 (HTTP OK)
function httpError() {
	if (http.readyState==4) {
		try { var ok=(http.status!=200); }
		catch(e) { return(true); }
		return(ok);
	}
}

// Muestra un mensaje de error dependiendo del tipo de error encontrado
function httpErrorShow() {
	if (!ajax_debug) return;
	if (httpError()) {
		if (httpStatus()) alert("Se ha encontrado el error "+httpStatus()+" en el servidor.");
		else alert("El servidor no responde a la petición!\nPruebe dentro de unos instantes.");
	}
}

// Realizar un envío de datos http
function httpSend(method, url, data, eventfunction) {
	var sdata=(data?data:"");
	var async=(eventfunction?true:false);
	http=httpObject();
	switch (method) {
	case hGET: http.open("GET",url+"?"+sdata,async); sdata=null; break;
	case hPOST: http.open("POST",url,async); break;
	default: return(false);
	}
	if (async) http.onreadystatechange=eventfunction;
	http.setRequestHeader('Content-Type','application/x-www-form-urlencoded; charset=UTF-8"');
	http.send(sdata);
	return(true);
}

// Devuelve todos los campos y datos
// de un formulario en forma campo1=dato1&campo2=dato2&...
function httpFormFields(formObject) {
	var fields="";
	for (i=0;i<formObject.length;i++) {
		if (!formObject[i].name) continue;
		if (formObject[i].type=="radio" && !formObject[i].checked) continue;
		fields+=(i>0?"&":"")+formObject[i].name+"="+gescape(httpGetObjectValue(formObject[i]));
	}
	return(fields);
}

// Habilitar o deshabilitar la posibilidad de introducción
// o modificación de datos de un formulario completo.
function httpFormFieldsEnabled(formObject,isEnabled) {
	for (x=0;x<formObject.length;x++) {
		try { formObject[x].disabled=!isEnabled; }
		catch(e) {}
	}
}

// Devuelve todos los campos y datos de un lista de identificadores
// tipo id1,id2,id3 en forma id1=dato1&id2=dato2&...
function httpGetFields(fieldNames) {
	var fn=fieldNames.split(",");
	var fields="";
	var formObject;
	for (i=0;i<fn.length;i++) {
		formObject=gid(fn[i]);
		if (!formObject) continue;
		if (formObject.type=="radio" && !formObject.checked) continue;
		fields+=(i>0?"&":"")+fn[i]+"="+gescape(httpGetObjectValue(formObject));
	}
	return(fields);
}

// Devuelve un valor de un campo
function httpGetObjectValue(formObject) {
	switch (formObject.type) {
	case "checkbox": return(formObject.checked?"1":"0");
	case "radio": case "button": case "select-one": case "text": case "textarea": default: return(formObject.value);
	}
}

// devuelve un objeto con las variables creadas con la función aput
function aget(fullData) {
	var d=(fullData==null?httpData():fullData);
	var o=new Object();
	var n=0;
	var p0,p1,p2,isa;
	var debug=false;
	// comprobar si usa una transacción
	if (d.substring(0,httpTP.length)!=httpTP) debug=true;
	else d=d.substring(httpTP.length);
	// leer datos
	while (true) {
		try {
			p0=d.indexOf("="); if (!p0) break;
			p1=d.indexOf("("); if (!p1) break;
			p2=d.indexOf(")"); if (!p2) break;
			isa=d.substring(p2+1,p2+2); if (!isa) break;
			isa=(isa=="$"?true:false);
			name=d.substring(0,p1); if (!name) { if (ajax_debug) alert("aget warning: bad output"+"\n\n"+httpData()); break; }
			plength=parseInt(d.substring(p1+1,p2));
			data=d.substring(p0+1,p0+plength+1);
			if (!isa) eval("o."+name+"=data;");
			else eval("o."+name+"="+data+";");
			d=d.substring(p0+plength+2);
		} catch(e) {
			if (debug && ajax_debug) alert("aget error: "+e+"\n\n"+httpData());
			return(false);
		}
	}
	return(o);
}

// mensaje de error en aget predefinido
function agetErrorShow() {
	if (!ajax_debug) return;
	alert("Error en procesamiento. Salida devuelta:\n\n"+httpData());
}

// Función auxiliar para crear cadenas PHP sin caracteres de control
function aescape(torg) {
	var d=""+torg;
	try { var d=d.replace(/\\/gi,"\\\\"); } catch(e) {}
	try { var d=d.replace(/\"/gi,"\\\""); } catch(e) {}
	try { var d=d.replace(/\'/gi,"\\'"); } catch(e) {}
	try { var d=d.replace(/\n/gi,"\\n"); } catch(e) {}
	try { var d=d.replace(/\t/gi,"\\t"); } catch(e) {}
	try { var d=d.replace(/\f/gi,"\\f"); } catch(e) {}
	try { var d=d.replace(/\r/gi,"\\r"); } catch(e) {}
	return(d);
}

// Convertir variable JavaScript a objeto serializado en PHP (nueva versión)
// ATENCIÓN: Debe ser saneado por la función verifyArray(cadena)
//           para evitar inyecciones de código a PHP.
function aput(a,level) {
	if (!level) level=0;
	if (a==null) return 'null';
	switch (typeof(a)) {
	case 'object':
		var s="";
		for (var i in a) s+=(s?",":"")+(a.length?'':'"'+aescape(i)+'"=>')+aput(a[i],level+1);
		return (a.length?"Array(":"Array(")+s+(a.length?")":")");
	case 'boolean': return (a?'true':'false');
	case 'number': return a;
	case 'string': default: return '"'+aescape(a)+'"';
	}
	return null;
}

// Función auxiliar para crear cadenas JSON sin caracteres de control
function jescape(torg) {
	var d=""+torg;
	try { var d=d.replace(/\\/gi,"\\\\"); } catch(e) {}
	try { var d=d.replace(/\"/gi,"\\\""); } catch(e) {}
	try { var d=d.replace(/\n/gi,"\\n"); } catch(e) {}
	try { var d=d.replace(/\t/gi,"\\t"); } catch(e) {}
	try { var d=d.replace(/\f/gi,"\\f"); } catch(e) {}
	try { var d=d.replace(/\r/gi,"\\r"); } catch(e) {}
	return(d);
}


// convertir variable JavaScript a JSON
function json(a,level) {
	if (!level) level=0;
	if (a==null) return 'null';
	switch (typeof(a)) {
	case 'object':
		var s="";
		for (var i in a) s+=(s?",":"")+(a.length?'':'"'+jescape(i)+'":')+json(a[i],level+1);
		return (a.length?"[":"{")+s+(a.length?"]":"}");
	case 'boolean': return (a?'true':'false');
	case 'number': return a;
	case 'string': default: return '"'+jescape(a)+'"';
	}
	return null;
}

// volcar el árbol de una variable JavaScript
function adump(arr,level) {
	var dumped_text="";
	if (!level) level=0;
	if (level>6) return "{TooMuchRecursion}";

	// the padding given at the beginning of the line
	var level_padding="";
	for (var j=0;j<level;j++) level_padding+="\t";

	if (typeof(arr)=='object') { // Array/Hashes/Objects
		for (var item in arr) {
			var value=arr[item];
			if (typeof(value)=='object') { // if it is an array
				var size=0; for (var none in value) size++;
				dumped_text+=level_padding+'["' + item + '"] ('+size+') =>\n';
				dumped_text+=adump(value,level+1);
			} else {
				dumped_text+=level_padding+'["' + item + '"] => '+typeof(value)+'("' + value + '")\n';
			}
		}
	} else { // Strings/Chars/Numbers etc.
		dumped_text="("+typeof(arr)+") "+arr;
	}
	return dumped_text;
}
function gid(id) {
	try {
		if (typeof(id)=="object") return id;
		else return document.getElementById(id);
	} catch(e) {
		return(null);
	}
}

function comboAdd(combo,text,value,selected,sBold) {

	combo=gid(combo);

	var selOpcion=new Option(text,value);
	selOpcion.selected=selected;

	try {
		try { combo.add(selOpcion,null); } catch(e) { combo.add(selOpcion); }
		if (sBold!=undefined) selOpcion.style.fontWeight=(sBold?"bold":"normal");
	} catch(e) {
		//alert(e+"\nError adding option ("+value+") - "+text);
		return(false);
	}

	return(true);

}

function comboDel(combo,index) {
	combo=gid(combo);
	try {
		combo.options[index]=null;
		return(true);
	} catch(e) {
		return(false);
	}
}

function comboDelSelected(combo) {
	while (combo.selectedIndex>=0)
		comboDel(combo,combo.selectedIndex);
}

function comboClear(combo) {
	gid(combo).length=0;
}

function comboValue(combo,value) {
	combo=gid(combo);
	if (combo.selectedIndex<0) return(false);
	if (value!=undefined) combo.value=value;
	else return(combo.value); // OLD: combo.options[combo.selectedIndex].value
}

function comboText(combo) {
	combo=gid(combo);
	if (combo.selectedIndex<0) return(false);
	return(combo.options[combo.selectedIndex].text);
}

function comboIndexByValue(combo,value) {
	combo=gid(combo);
	for (var i=0;i<combo.options.length;i++)
		if (combo.options[i].value==value) return(i);
	return -1;
}

function comboIndexByText(combo,text) {
	combo=gid(combo);
	for (var i=0;i<combo.options.length;i++)
		if (combo.options[i].text==text) return(i);
	return -1;
}

function comboValueExists(combo,value) {
	combo=gid(combo);
	return (comboIndexByValue(combo,value)>=0);
}

function comboTextExists(combo,text) {
	combo=gid(combo);
	return (comboIndexByText(combo,text)>=0);
}

function comboClearFill(combo,lista,valorActual,formaMostrar,indiceValor,filtro) {
	combo=gid(combo);
	comboClear(combo);
	comboFill(combo,lista,valorActual,formaMostrar,indiceValor,filtro);
}

function comboFill(combo,lista,valorActual,formaMostrar,indiceValor,filtro) {

	combo=gid(combo);

	if (!indiceValor) indiceValor="i";
	if (!formaMostrar) formaMostrar="lista[i]";
	if (!filtro) filtro="";

	eval // es más eficiente meter el bucle for dentro que fuera, aunque el código quede más sucio
		("for (var i in lista) {"
		+"	"+(filtro?"if("+filtro+")":"")+"comboAdd(combo,"+formaMostrar+","+indiceValor+",(valorActual=="+indiceValor+"?true:false));"
		+"}");

}

function comboSortCompareText(o1, o2) {
	return (o1.text.toLowerCase()<o2.text.toLowerCase()?-1:o1.text.toLowerCase()>o2.text.toLowerCase()?1:0);
}

function comboSortCompareTextCaseSensitive(o1, o2) {
	return (o1.text<o2.text?-1:o1.text>o2.text?1:0);
}

function comboSort(combo,compareFunction) {
	combo=gid(combo);
	if (!compareFunction) compareFunction=comboSortCompareText;
	var options=new Array (combo.options.length);
	for (var i=0; i<options.length; i++)
		options[i]=new Option(
			combo.options[i].text,
			combo.options[i].value,
			combo.options[i].defaultSelected,
			combo.options[i].selected
		);
	options.sort(compareFunction);
	combo.options.length=0;
	for (var i=0; i<options.length; i++)
		combo.options[i]=options[i];
}

function comboMove(combo1,combo2,sorted,compareFunction) {
	combo1=gid(combo1);
	combo2=gid(combo2);
	while (combo1.selectedIndex>=0) {
		var option=new Option(
			combo1.options[combo1.selectedIndex].text,
			combo1.options[combo1.selectedIndex].value,
			combo1.options[combo1.selectedIndex].defaultSelected,
			combo1.options[combo1.selectedIndex].selected
		);
		try { combo2.add(option,null); } catch(e) { combo2.add(option); }
		combo1.options[combo1.selectedIndex]=null;
	}
	if (sorted==undefined || sorted) comboSort(combo2,compareFunction);
}

function comboMoveIndex(combo1,index,combo2,sorted,compareFunction) {
	combo1=gid(combo1);
	combo2=gid(combo2);
	var option=new Option(
		combo1.options[index].text,
		combo1.options[index].value,
		combo1.options[index].defaultSelected,
		combo1.options[index].selected
	);
	try { combo2.add(option,null); } catch(e) { combo2.add(option); }
	combo1.options[index]=null;
}

function combosVisible(visible) {
	var elements=document.body.getElementsByTagName("select");
	for (var i in elements)
		if (elements[i].type)
			if (elements[i].type=="select-one" || elements[i].type=="select-multiple")
				elements[i].style.visibility=(visible?"visible":"hidden");
}

function combosVisibleIE(visible) {
	if (isie()) combosVisible(visible);
}
// common.js by mr.xkr v2 rev.3

// pequeñas funciones para reducir código
function gid(id) {
	try {
		if (typeof(id)=="object") return id;
		else return document.getElementById(id);
	} catch(e) {
		return(null);
	}
}
function gidget(id) { return(gid(id).innerHTML); }
function gidset(id,html) { gid(id).innerHTML=html; }
function gidval(id,data) { if (data!=null) gid(id).value=data; else return( (gid(id)==null)?"":gid(id).value); }
function giddel(id) { var d=gid(id); d.parentNode.removeChild(d); }
function gidmove(id_org,id_dst) { gid(id_dst).innerHTML=gid(id_org).innerHTML; gid(id_org).innerHTML=""; }
function show(id) { gid(id).style.display="block"; }
function hide(id) { gid(id).style.display="none"; }
function cell(id) { gid(id).style.display="table-cell"; }
function visible(id) { gid(id).style.visibility="visible"; }
function hidden(id) { gid(id).style.visibility="hidden"; }
function isShow(id) { return(gid(id).style.display=="block"?true:false); }
function isVisible(id) { return(gid(id).style.display!="none"?true:false); }

// funciones auxiliares
function getTop(id) { var o=gid(id); var p=0; do { p+=o.offsetTop; } while (o=o.offsetParent); return(p); }
function getLeft(id) { var o=gid(id); var p=0; do { p+=o.offsetLeft; } while (o=o.offsetParent); return(p); }
function getWidth(id) { return gid(id).offsetWidth; }
function getHeight(id) { return gid(id).offsetHeight; }
function style(id,styles) { var o=gid(id); for (var i in styles) o.style[i]=styles[i]; }

// propiedades del documento y ventana
function ieTrueBody() { return((document.compatMode && document.compatMode!="BackCompat")?document.documentElement:document.body); }
function scrollLeft() { return(ieTrueBody().scrollLeft); }
function scrollTop() { return(ieTrueBody().scrollTop); }
function windowWidth() { return(document.documentElement.clientWidth?document.documentElement.clientWidth:window.innerWidth); }
function windowHeight() { return(document.documentElement.clientHeight?document.documentElement.clientHeight:window.innerHeight); }
function documentWidth() { return(document.body.clientWidth); }
function documentHeight() { return(document.body.clientHeight); }

// ancho sin los márgenes
function crossInnerWidth(element) {
	try {
		if (window.getComputedStyle(element,""))
			return(element.clientWidth-parseInt(window.getComputedStyle(element,"").getPropertyValue("padding-left"))-parseInt(window.getComputedStyle(element,"").getPropertyValue("padding-right")));
	} catch(e) {
		return(element.clientWidth-parseInt(element.currentStyle.paddingLeft)-parseInt(element.currentStyle.paddingRight));
	}
}

// alto sin los márgenes
function crossInnerHeight(element) {
	try {
		if (window.getComputedStyle(element,""))
			return(element.clientHeight-parseInt(window.getComputedStyle(element,"").getPropertyValue("padding-top"))-parseInt(window.getComputedStyle(element,"").getPropertyValue("padding-bottom")));
	} catch(e) {
		return(element.clientHeight-parseInt(element.currentStyle.paddingTop)-parseInt(element.currentStyle.paddingBottom));
	}
}

// precarga de imágenes
var imagePreloadList=new Object();
function imagePreload(imageorlist) {
	var image_list=(typeof image=="string"?[imageorlist]:imageorlist);
	for (var i in image_list) {
		var image=image_list[i];
		imagePreloadList[image]={"loaded":false,"img":new Image()};
		imagePreloadList[image].img.src=image;
		imagePreloadList[image].img.onload=function(){imagePreloadList[image].loaded=true;};
	}
}

// ancho del scroll vertical
// thanks to Alexandre Gomes (Portugal)
// http://www.alexandre-gomes.com/?p=115
function scrollWidth() {

	var inner=document.createElement('p');
	inner.style.width='100%';
	inner.style.height='200px';

	var outer=document.createElement('div');
	outer.style.position='absolute';
	outer.style.top='0px';
	outer.style.left='0px';
	outer.style.visibility='hidden';
	outer.style.width='200px';
	outer.style.height='150px';
	outer.style.overflow='hidden';

	outer.appendChild(inner);
	document.body.appendChild(outer);

	var w1=inner.offsetWidth;
	outer.style.overflow='scroll';
	var w2=inner.offsetWidth;
	if (w1==w2) w2=outer.clientWidth;

	document.body.removeChild(outer);
	return(w1-w2);
}

// almacenar cookie
function setCookie(name,value,days) {
	var expires="";
	value=""+value;
	if (days) {
		var date=new Date();
		date.setTime(date.getTime()+(days*86400000));
		expires="; expires="+date.toGMTString();
	}
	document.cookie=name+"="+value.replace(/\\/gi,"\\\\").replace(/\n/gi,"\\n").replace(/;/gi,"\\,")+expires+"; path=/";
}

// obtener cookie
function getCookie(name) {
	var nameEQ=name+"=";
	var ca=document.cookie.split(';');
	for (i=0;i<ca.length;i++) {
		var c=ca[i];
		while (c.charAt(0)==' ')
			c=c.substring(1,c.length);
		if (c.indexOf(nameEQ)==0)
			return(c.substring(nameEQ.length,c.length).replace(/\\\\/gi,"\\").replace(/\\n/gi,"\n").replace(/\\,/gi,";"));
	}
	return("");
}

// borrar cookie
function delCookie(name) {
	setCookie(name,"",-1);
}

// comprobación de navegadores
function isie() { return (navigator.userAgent.indexOf("MSIE")!=-1); }
function ismoz() { return (navigator.userAgent.indexOf("Firefox")!=-1 || navigator.userAgent.indexOf("Iceweasel")!=-1); }

// oculta todos los selects de la página si es IE
function hideSelects(hidden) {
	if (!isie()) return;
	selects=document.getElementsByTagName("select");
	for (i=0;i<selects.length;i++)
		selects[i].style.visibility=(hidden?"hidden":"visible");
}

// buscar y ejecutar tags <script> embebidos
function getrunjs(data) {
	runjs(getjs(data));
}

// buscar tags <script> embebidos
function getjs(data) {
	scode="";
	while (true) {
		ss=data.toLowerCase().indexOf("<script>"); if (ss<0) break;
		es=data.toLowerCase().indexOf("<\/script>",ss+2); if (es<0) break;
		scode=scode+data.substring(ss+8,es);
		data=data.substring(0,ss)+data.substring(es+9);
	}
	return(scode);
}

// ejecutar cadena de javascript (mucho mejor que eval)
function runjs(data) {
	if (!data) return;
	var escode=document.createElement("script");
	escode.setAttribute("type","text/javascript");
	escode.text=data;
	document.getElementsByTagName("body").item(0).appendChild(escode);
}

// convertir retornos de carro en nuevas lineas HTML
function nl2br(t) {
	try { var e=/\n/gi; t=t.replace(e,"<br />"); } catch(e) {}
	return(t);
}

// id/object merge: de una lista de identificadores separadas por comas,
// mezclar sus datos con objetos JavaScript previamente existentes
function ioMerge(ids,root,obj) {
	ids=ids.split(",");
	for (i=0;i<ids.length;i++)
		if (gid(root+ids[i]))
			obj[ids[i]]=gidval(root+ids[i]);
	return(obj);
}

// object/object merge: de una lista de objetos separados por comas,
// copia los los datos del primero objeto en el segundo y devuelve este
function ooMerge(ids,obj1,obj2) {
	ids=ids.split(",");
	for (i=0;i<ids.length;i++)
		obj2[ids[i]]=obj1[ids[i]];
	return(obj2);
}

// añade CSS al documento (no funciona bien en IE6)
function cssAdd(css) {
  var style=document.createElement("style");
  style.type="text/css";
  if (style.styleSheet) style.styleSheet.cssText=css;
  else style.appendChild(document.createTextNode(css));
  document.getElementsByTagName("head")[0].appendChild(style);
}

// array_merge: mezcla arrays puros o asociativos
// es equivalente a la funcion de PHP
function array_merge(a1,a2) {
	for (var i in a2) a1[i]=a2[i];
	return a1;
}

// array_remove: elimina claves de un array asociativo
function array_remove(a1,a2) {
	var a=new Object();
	var clone;
	for (var i in a1) {
		clone=true;
		for (var j in a2)
			if (i==a2[j]) {
				clone=false;
				break;
			}
		if (clone)
		a[i]=a1[i];
	}
	return a;
}

// array_get: devuelve las claves de un array dada una lista de ellas
function array_get(a,list) {
	var o=new Object();
	for (var i in list)
		o[list[i]]=a[list[i]];
	return o;
}

// array_save: busca las claves de la lista en el segundo array y los mezcla con el primero
function array_save(a1,a2,list) {
	for (var i in list)
		a1[list[i]]=a2[list[i]];
	return a1;
}

// copia en profundidad de un objeto
function array_copy(o) {
	if (typeof o != "object" || o === null) return o;
	var r = o.constructor == Array ? [] : {};
	for (var i in o) r[i] = array_copy(o[i]);
	return r;
}

// control de impresión, sobrecargar este método
function doprint() { window.print(); }

// onload
var init_func=new Array();
function init(add) {
	if (add) init_func[init_func.length]=add;
	else { for (var i in init_func) { try { init_func[i](); } catch(e) {} } }
}
window.onload=function(){ try { init(); } catch(e) {} }

// onunload
var unload_func=new Array();
function unload(add) {
	if (add) unload_func[unload_func.length]=add;
	else { for (var i in unload_func) { try { unload_func[i](); } catch(e) {} } }
}
window.onunload=function(){ try { unload(); } catch(e) {} }

// onresize
var resize_func=new Array();
function resize(add) {
	if (add) resize_func[resize_func.length]=add;
	else { for (var i in resize_func) { try { resize_func[i](); } catch(e) {} } }
}
window.onresize=function(){ try { resize(); } catch(e) {} }

// onkeydown
var keydown_func=new Array();
function keydown(p) {
	if (typeof(p)=="function") keydown_func[keydown_func.length]=p;
	else { for (var i in keydown_func) { try { keydown_func[i](p); } catch(e) {} } }
}
document.onkeydown=function(we){ try { keydown(we); } catch(e) {} }

// onscroll
var scroll_func=new Array();
function scroll(p) {
	if (typeof(p)=="function") scroll_func[scroll_func.length]=p;
	else { for (var i in scroll_func) { try { scroll_func[i](p); } catch(e) {} } }
}
document.onscroll=function(we){ try { scroll(we); } catch(e) {} }

// onmouseup
var mouseup_func=new Array();
function mouseup(p) {
	if (typeof(p)=="function") mouseup_func[mouseup_func.length]=p;
	else { for (var i in mouseup_func) { try { mouseup_func[i](p); } catch(e) {} } }
}
document.onmouseup=function(we){ if (!we) we=window.event; try { mouseup(we); } catch(e) {} }

// onmousedown
var mousedown_func=new Array();
function mousedown(p) {
	if (typeof(p)=="function") mousedown_func[mousedown_func.length]=p;
	else { for (var i in mousedown_func) { try { mousedown_func[i](p); } catch(e) {} } }
}
document.onmousedown=function(we){ if (!we) we=window.event; try { mousedown(we); } catch(e) {} }

// onmousemove
var mousemove_func=new Array();
function mousemove(p) {
	if (typeof(p)=="function") mousemove_func[mousemove_func.length]=p;
	else { for (var i in mousemove_func) { try { mousemove_func[i](p); } catch(e) {} } }
}
document.onmousemove=function(we){ if (!we) we=window.event; try { mousemove(we); } catch(e) {} }

// habilitar/deshabilitar seleccionar texto en un elemento
function selectionEnabled(o,enable) {
	var o=gid(o);
	if (typeof o.onselectstart!="undefined") { if (enable) o.onselectstart=null; else { o.onselectstart=function(){ return false; } } } // IE
	else if (typeof o.style.MozUserSelect!="undefined") { o.style.MozUserSelect=(enable?"":"none"); } //Firefox
	else { if (enable) o.onmousedown=null; else { o.onmousedown=function(){ return false; } } } // all other navs
	o.style.cursor="default";
}

// String to JSON object
function evalJson(src)
{
	var pattern =  /^\(.+\)$/gi; // Add parentheses if needed
	if(pattern.test(src) == false)
	{
		src = '(' + src + ')';
	}

	var data = '';
	if(typeof(JSON) == 'object' && JSON.parse)
	{
		try
		{
			data = JSON.parse(src);
		}
		catch(e)
		{
			try
			{
				data = eval(src);
			}
			catch(e)
			{
				data = src;
			}
		}

	}
	else
	{
		data = eval(src);
	}

	if(typeof(data) == 'object')
	{
		return data
	}
	else
	{
		return {stat:'ERROR',msg:'ERROR'};
	}
}
// requiere common.js

var newalertImages="images/";
var newalert_conf={
	"id":"newalert_",
	"border":"2px solid #666",
	"radius":"4px",
	"background":"#FFF",
	"max-width":"80%"
};

var newalert_active=new Object();
var newalert_return_function=new Object();
var newalert_iecomboshide=(navigator.userAgent.indexOf("MSIE 6.")!=-1); // sólo ocultar combos con IE6
var newalert_open_windows=0;

function newalert_support_opacity() { return (navigator.userAgent.indexOf("MSIE")==-1); } // soportar opacity si no es IE
function newalert_support_fixed() { return (navigator.userAgent.indexOf("MSIE 6.")==-1); } // soportar fixed si no es IE6

function newalert(o) {
	if (newalert_iecomboshide) combosVisibleIE(false);
	if (typeof(o)=="string") {
		var id="";
		var msg=o;
		var buttons=null;
	} else {
		var id=(o.id?o.id:"");
		var msg=(o.msg?o.msg:"");
		var buttons=o.buttons;
	}
	if (buttons==null) buttons=[{"caption":"Aceptar"}];
	newalert_return_function[id]=o.func;
	var d=document.createElement("div");
	newalert_remove(id);
	newalert_active[id]=true;
	d.setAttribute("id",newalert_conf.id+id);
	style(d,{
		"position":(newalert_support_fixed()?"fixed":"absolute"),
		"left":(newalert_support_fixed()?0:scrollLeft())+"px",
		"top":(newalert_support_fixed()?0:scrollTop())+"px",
		"width":windowWidth()+"px",
		"height":windowHeight()+"px",
		"zIndex":990
	});
	if (!o.noshadow) style(d,{"backgroundImage":"url('"+newalertImages+"trans."+(newalert_support_opacity()?"png":"gif")+"')"});
	var ico=(o.ico!=null && o.ico=="");
	var cols=(ico?"":"colspan='2'");
	s="<table cellpadding='0' cellspacing='0' width='100%' height='100%'><tr><td align='center'>";
	if (o.body) s+=o.body;
	else {
		s+="<table cellpadding='0' cellspacing='0' style='"
				+(newalert_conf.border?"border:"+newalert_conf.border+";":"")
				+(newalert_conf.radius?"-moz-border-radius:"+newalert_conf.radius+";":"")
				+(newalert_conf.background?"background-color:"+newalert_conf.background+";":"")
				+(newalert_conf["max-width"]?"max-width:"+newalert_conf["max-width"]+";":"")
				+"'>"
			+(o.title==null?"":"<tr><th "+cols+" style='padding:2px 9px;color:#FFF;background-color:#666;cursor:default;'>"+(o.title?o.title:"")+"</th></tr>")
			+(ico?"":"<tr><td width='48' style='padding:16px 16px;' valign=top>"
				+"<img src='"+newalertImages+"newalert-"+(o.ico?o.ico+(o.ico.indexOf(".")>=0?"":".png"):"notice.png")+"' alt='' />"
			+"</td>")
			+"<td style='color:#000;padding:16px 16px 16px "+(ico?"16":"0")+"px;' valign='middle'>"+msg+"</td></tr>";
		if (buttons.length) {
			s+="<tr><td "+cols+" style='padding:0px 16px 16px 16px;' align='center'>";
			for (var i in buttons)
				if (typeof(buttons[i])!="function")
					s+=" <input id='"+newalert_conf.id+"_cmd_"+i+"' class=cmd type=button onClick='javascript:"+(buttons[i].action?buttons[i].action:"newalert_close(\""+id+"\")")+";' value='"+buttons[i].caption+"' />";
			s+"</td></tr>";
		}
		s+="</table>";
	}
	s+="</td></tr></table>";
	d.innerHTML=s;
	document.body.appendChild(d);
	newalert_open_windows++;
	try { gid(newalert_conf.id+"_cmd_0").focus(); } catch(e) {}
}

function newalert_event_timer() {
	if (newalert_support_fixed()) return;
	for (var id in newalert_active) {
		if (newalert_active[id])
			style(newalert_conf.id+id,{left:scrollLeft()+"px",top:scrollTop()+"px",width:windowWidth()+"px",height:windowHeight()+"px"});
	}
	newalert_event_poll=setTimeout("newalert_event_timer()",100);
}

function newwait(o) {
	if (typeof(o)=="string") {
		var msg=o;
		o=new Object();
		o.id="wait";
		o.msg=msg;
		o.ico="busy.gif";
		o.buttons=[];
	} else {
		if (!o) o=new Object();
		o.id=(o.id!=null?o.id:"wait");
		o.msg="Proceso en curso, por favor, espere...";
		o.ico=(o.ico!=null?o.ico:"busy.gif");
		o.buttons=[];
	}
	newalert(o);
}

function newquestion(o) {
	if (typeof(o)=="string") {
		var msg=o;
		o=new Object();
		o.id="ask";
		o.msg=msg;
		o.ico="email.gif";
		o.buttons=[];
	} else {
		if (!o) o=new Object();
		o.id=(o.id!=null?o.id:"ask");
		o.msg=(o.msg!=null?o.msg:"Confirma el envio del email?");
		o.ico=(o.ico!=null?o.ico+".gif":"email.gif");
		o.buttons=[];
	}
	newalert(o);
}

function newsimplewait() {
	var o=new Object();
	o.id="wait";
	o.noshadow=true;
	o.body="<img src='"+newalertImages+"newalert-busy.gif' />";
	newalert(o);
}

function newalert_remove(id) {
	if (!id) var id="";
	newalert_active[id]=false;
	if (gid(newalert_conf.id+id)) {
		newalert_open_windows--;
		gid(newalert_conf.id+id).parentNode.removeChild(gid(newalert_conf.id+id));
		if (newalert_iecomboshide && !newalert_open_windows) combosVisibleIE(true);
	}
}

function newalert_close(id) {
	if (!id) var id="";
	newalert_remove(id);
	try { newalert_return_function[id](id); } catch(e) {}
}

function newwait_close(id) {
	newalert_close(id?id:'wait');
}

// lanzar timer de eventos
newalert_event_timer();
/*
 * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
 * Digest Algorithm, as defined in RFC 1321.
 * Version 2.1 Copyright (C) Paul Johnston 1999 - 2002.
 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
 * Distributed under the BSD License
 * See http://pajhome.org.uk/crypt/md5 for more info.
 */

/*
 * Configurable variables. You may need to tweak these to be compatible with
 * the server-side, but the defaults work in most cases.
 */
var hexcase = 0;  /* hex output format. 0 - lowercase; 1 - uppercase        */
var b64pad  = ""; /* base-64 pad character. "=" for strict RFC compliance   */
var chrsz   = 8;  /* bits per input character. 8 - ASCII; 16 - Unicode      */

/*
 * These are the functions you'll usually want to call
 * They take string arguments and return either hex or base-64 encoded strings
 */
function hex_md5(s){ return binl2hex(core_md5(str2binl(s), s.length * chrsz));}
function b64_md5(s){ return binl2b64(core_md5(str2binl(s), s.length * chrsz));}
function str_md5(s){ return binl2str(core_md5(str2binl(s), s.length * chrsz));}
function hex_hmac_md5(key, data) { return binl2hex(core_hmac_md5(key, data)); }
function b64_hmac_md5(key, data) { return binl2b64(core_hmac_md5(key, data)); }
function str_hmac_md5(key, data) { return binl2str(core_hmac_md5(key, data)); }

/*
 * Perform a simple self-test to see if the VM is working
 */
function md5_vm_test()
{
  return hex_md5("abc") == "900150983cd24fb0d6963f7d28e17f72";
}

/*
 * Calculate the MD5 of an array of little-endian words, and a bit length
 */
function core_md5(x, len)
{
  /* append padding */
  x[len >> 5] |= 0x80 << ((len) % 32);
  x[(((len + 64) >>> 9) << 4) + 14] = len;

  var a =  1732584193;
  var b = -271733879;
  var c = -1732584194;
  var d =  271733878;

  for(var i = 0; i < x.length; i += 16)
  {
    var olda = a;
    var oldb = b;
    var oldc = c;
    var oldd = d;

    a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);
    d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);
    c = md5_ff(c, d, a, b, x[i+ 2], 17,  606105819);
    b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330);
    a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);
    d = md5_ff(d, a, b, c, x[i+ 5], 12,  1200080426);
    c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);
    b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);
    a = md5_ff(a, b, c, d, x[i+ 8], 7 ,  1770035416);
    d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);
    c = md5_ff(c, d, a, b, x[i+10], 17, -42063);
    b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);
    a = md5_ff(a, b, c, d, x[i+12], 7 ,  1804603682);
    d = md5_ff(d, a, b, c, x[i+13], 12, -40341101);
    c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);
    b = md5_ff(b, c, d, a, x[i+15], 22,  1236535329);

    a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);
    d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
    c = md5_gg(c, d, a, b, x[i+11], 14,  643717713);
    b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);
    a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);
    d = md5_gg(d, a, b, c, x[i+10], 9 ,  38016083);
    c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);
    b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);
    a = md5_gg(a, b, c, d, x[i+ 9], 5 ,  568446438);
    d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);
    c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);
    b = md5_gg(b, c, d, a, x[i+ 8], 20,  1163531501);
    a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);
    d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);
    c = md5_gg(c, d, a, b, x[i+ 7], 14,  1735328473);
    b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);

    a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);
    d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);
    c = md5_hh(c, d, a, b, x[i+11], 16,  1839030562);
    b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);
    a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
    d = md5_hh(d, a, b, c, x[i+ 4], 11,  1272893353);
    c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);
    b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);
    a = md5_hh(a, b, c, d, x[i+13], 4 ,  681279174);
    d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);
    c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);
    b = md5_hh(b, c, d, a, x[i+ 6], 23,  76029189);
    a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);
    d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);
    c = md5_hh(c, d, a, b, x[i+15], 16,  530742520);
    b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);

    a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);
    d = md5_ii(d, a, b, c, x[i+ 7], 10,  1126891415);
    c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);
    b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);
    a = md5_ii(a, b, c, d, x[i+12], 6 ,  1700485571);
    d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);
    c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);
    b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);
    a = md5_ii(a, b, c, d, x[i+ 8], 6 ,  1873313359);
    d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);
    c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);
    b = md5_ii(b, c, d, a, x[i+13], 21,  1309151649);
    a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);
    d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);
    c = md5_ii(c, d, a, b, x[i+ 2], 15,  718787259);
    b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551);

    a = safe_add(a, olda);
    b = safe_add(b, oldb);
    c = safe_add(c, oldc);
    d = safe_add(d, oldd);
  }
  return Array(a, b, c, d);

}

/*
 * These functions implement the four basic operations the algorithm uses.
 */
function md5_cmn(q, a, b, x, s, t)
{
  return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b);
}
function md5_ff(a, b, c, d, x, s, t)
{
  return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
}
function md5_gg(a, b, c, d, x, s, t)
{
  return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
}
function md5_hh(a, b, c, d, x, s, t)
{
  return md5_cmn(b ^ c ^ d, a, b, x, s, t);
}
function md5_ii(a, b, c, d, x, s, t)
{
  return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
}

/*
 * Calculate the HMAC-MD5, of a key and some data
 */
function core_hmac_md5(key, data)
{
  var bkey = str2binl(key);
  if(bkey.length > 16) bkey = core_md5(bkey, key.length * chrsz);

  var ipad = Array(16), opad = Array(16);
  for(var i = 0; i < 16; i++)
  {
    ipad[i] = bkey[i] ^ 0x36363636;
    opad[i] = bkey[i] ^ 0x5C5C5C5C;
  }

  var hash = core_md5(ipad.concat(str2binl(data)), 512 + data.length * chrsz);
  return core_md5(opad.concat(hash), 512 + 128);
}

/*
 * Add integers, wrapping at 2^32. This uses 16-bit operations internally
 * to work around bugs in some JS interpreters.
 */
function safe_add(x, y)
{
  var lsw = (x & 0xFFFF) + (y & 0xFFFF);
  var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
  return (msw << 16) | (lsw & 0xFFFF);
}

/*
 * Bitwise rotate a 32-bit number to the left.
 */
function bit_rol(num, cnt)
{
  return (num << cnt) | (num >>> (32 - cnt));
}

/*
 * Convert a string to an array of little-endian words
 * If chrsz is ASCII, characters >255 have their hi-byte silently ignored.
 */
function str2binl(str)
{
  var bin = Array();
  var mask = (1 << chrsz) - 1;
  for(var i = 0; i < str.length * chrsz; i += chrsz)
    bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (i%32);
  return bin;
}

/*
 * Convert an array of little-endian words to a string
 */
function binl2str(bin)
{
  var str = "";
  var mask = (1 << chrsz) - 1;
  for(var i = 0; i < bin.length * 32; i += chrsz)
    str += String.fromCharCode((bin[i>>5] >>> (i % 32)) & mask);
  return str;
}

/*
 * Convert an array of little-endian words to a hex string.
 */
function binl2hex(binarray)
{
  var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
  var str = "";
  for(var i = 0; i < binarray.length * 4; i++)
  {
    str += hex_tab.charAt((binarray[i>>2] >> ((i%4)*8+4)) & 0xF) +
           hex_tab.charAt((binarray[i>>2] >> ((i%4)*8  )) & 0xF);
  }
  return str;
}

/*
 * Convert an array of little-endian words to a base-64 string
 */
function binl2b64(binarray)
{
  var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  var str = "";
  for(var i = 0; i < binarray.length * 4; i += 3)
  {
    var triplet = (((binarray[i   >> 2] >> 8 * ( i   %4)) & 0xFF) << 16)
                | (((binarray[i+1 >> 2] >> 8 * ((i+1)%4)) & 0xFF) << 8 )
                |  ((binarray[i+2 >> 2] >> 8 * ((i+2)%4)) & 0xFF);
    for(var j = 0; j < 4; j++)
    {
      if(i * 8 + j * 6 > binarray.length * 32) str += b64pad;
      else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F);
    }
  }
  return str;
}

/**
 * ProgressBar for Google Maps v3
 * @version 1.1++
 *
 * Licensed under the GPL licenses:
 * http://www.gnu.org/licenses/gpl.html
 *
 * by José Fernando Calcerrada. 
 * expanded by Nahún Olivares
 * 
 * Chagelog
 *
 * v1.1++
 * - Added steps
 * - Fixed fontFamily
 * - Added dissapear() to fadeOut (using jQuery)
 * - Added loadingText
 * - Added loadingCursor (must use hide() or dissapear() to clear)
 * - some more tweaks
 * 
 * v1.1
 * - IE fixed
 *
 */

progressBar = function(opts) {

  var options = progressBar.combineOptions(opts, {
    height:       '1.3em',
    width:        '150px',
    top:          '30px',
    right:        '5px',
    colorBar:     '#68C',
    background:   '#FFF',
    fontFamily:   'Arial, sans-serif',
    fontSize:     '12px',
    loadingText:  'Cargando...',
    steps:		  [],
    loadingCursor: false
  });

  var current = 0;
  var total = 0;

  var shadow = '1px 1px #888';


  var div = document.createElement('div');
  div.id  = 'pg_div';
  var dstyle = div.style;
  div.style.cssText = 'box-shadow: ' + shadow + '; '
                    + '-webkit-box-shadow: ' + shadow + '; '
                    + '-moz-box-shadow: ' + shadow + '; ';
  dstyle.display     = 'none';
  dstyle.width       = options.width;
  dstyle.height      = options.height;
  dstyle.marginRight = '6px';
  dstyle.border      = '1px solid #BBB';
  dstyle.background  = options.background;
  dstyle.fontSize    = options.fontSize;
  dstyle.textAlign   = 'left';

  var text = document.createElement('div');
  text.id  = 'pg_text';
  var tstyle = text.style;
  tstyle.position      = 'absolute';
  tstyle.width         = '100%';
  tstyle.border        = '5px';
  tstyle.textAlign     = 'center';
  tstyle.fontFamily    = options.fontFamily;
  tstyle.verticalAlign = 'bottom';

  var bar = document.createElement('div');
  bar.id                    = 'pg_bar';
  bar.style.height          = options.height;
  bar.style.backgroundColor = options.colorBar;

  div.appendChild(text);
  div.appendChild(bar);


  var draw = function(mapDiv) {
    div.style.cssText = control.style.cssText +
      'z-index: 20; position: absolute; '+
      'top: '+options.top+'; right: '+options.right+'; ';
      document.getElementById(mapDiv).children[0].appendChild(div);
  }

  var start = function(total_) {
    if (parseInt(total_) === total_ && total_ > 0) {
      total = total_;
      current = 0;
      bar.style.width = '0%';
      text.innerHTML = options.loadingText;
      div.style.display = 'block';
    }
    if (options.loadingCursor) $('body *').css('cursor', 'progress');

    return total;
  }

  var updateBar = function(increase) {
    if (parseInt(increase) === increase && total) {
      current += parseInt(increase);
      if (current > total) {
        current = total;
      } else if (current < 0) {
        current = 0;
      }
      if (typeof options.steps[current-1] != "undefined") {
    	  step = options.steps[current-1] + ' ';
      } else step = '';
      
      bar.style.width = Math.round((current/total)*100)+'%';
      text.innerHTML = step + current+' / '+total;

    } else if (!total){
      return total;
    }

    return current;
  }

  var dissapear = function() {
	  if (options.loadingCursor) $('body *').css('cursor', 'auto');
	  $(div).fadeOut(5000);	  
  }
  
  var hide = function() {
	  if (options.loadingCursor) $('body *').css('cursor', 'auto');
      div.style.display = 'none';
  }

  var getDiv = function() {
    return div;
  }

  var getTotal = function() {
    return total;
  }

  var setTotal = function(total_) {
    total = total_;
  }

  var getCurrent = function() {
    return current;
  }

  var setCurrent = function(current_) {
    return updateBar(current_-current);
  }

  return {
    draw:         draw,
    start:        start,
    updateBar:    updateBar,
    dissapear:    dissapear,
    hide:         hide,
    getDiv:       getDiv,
    getTotal:     getTotal,
    setTotal:     setTotal,
    getCurrent:   getCurrent,
    setCurrent:   setCurrent
  }

}

progressBar.combineOptions = function (overrides, defaults) {
  var result = {};
  if (!!overrides) {
    for (var prop in overrides) {
      if (overrides.hasOwnProperty(prop)) {
        result[prop] = overrides[prop];
      }
    }
  }
  if (!!defaults) {
    for (prop in defaults) {
      if (defaults.hasOwnProperty(prop) && (result[prop] === undefined)) {
        result[prop] = defaults[prop];
      }
    }
  }
  return result;
}/*
Copyright (c) 2005 Tim Taylor Consulting <http://tool-man.org/>

Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.
*/

var ToolMan = {
	events : function() {
		if (!ToolMan._eventsFactory) throw "ToolMan Events module isn't loaded";
		return ToolMan._eventsFactory
	},

	css : function() {
		if (!ToolMan._cssFactory) throw "ToolMan CSS module isn't loaded";
		return ToolMan._cssFactory
	},

	coordinates : function() {
		if (!ToolMan._coordinatesFactory) throw "ToolMan Coordinates module isn't loaded";
		return ToolMan._coordinatesFactory
	},

	drag : function() {
		if (!ToolMan._dragFactory) throw "ToolMan Drag module isn't loaded";
		return ToolMan._dragFactory
	},

	dragsort : function() {
		if (!ToolMan._dragsortFactory) throw "ToolMan DragSort module isn't loaded";
		return ToolMan._dragsortFactory
	},

	helpers : function() {
		return ToolMan._helpers
	},

	cookies : function() {
		if (!ToolMan._cookieOven) throw "ToolMan Cookie module isn't loaded";
		return ToolMan._cookieOven
	},

	junkdrawer : function() {
		return ToolMan._junkdrawer
	}

}

ToolMan._helpers = {
	map : function(array, func) {
		for (var i = 0, n = array.length; i < n; i++) func(array[i])
	},

	nextItem : function(item, nodeName) {
		if (item == null) return
		var next = item.nextSibling
		while (next != null) {
			if (next.nodeName == nodeName) return next
			next = next.nextSibling
		}
		return null
	},

	previousItem : function(item, nodeName) {
		var previous = item.previousSibling
		while (previous != null) {
			if (previous.nodeName == nodeName) return previous
			previous = previous.previousSibling
		}
		return null
	},

	moveBefore : function(item1, item2) {
		var parent = item1.parentNode
		parent.removeChild(item1)
		parent.insertBefore(item1, item2)
	},

	moveAfter : function(item1, item2) {
		var parent = item1.parentNode
		parent.removeChild(item1)
		parent.insertBefore(item1, item2 ? item2.nextSibling : null)
	}
}

/** 
 * scripts without a proper home
 *
 * stuff here is subject to change unapologetically and without warning
 */
ToolMan._junkdrawer = {
	serializeList : function(list) {
		var items = list.getElementsByTagName("li")
		var array = new Array()
		for (var i = 0, n = items.length; i < n; i++) {
			var item = items[i]

			array.push(ToolMan.junkdrawer()._identifier(item))
		}
		return array.join('|')
	},

	inspectListOrder : function(id) {
		alert(ToolMan.junkdrawer().serializeList(document.getElementById(id)))
	},

	restoreListOrder : function(listID) {
		var list = document.getElementById(listID)
		if (list == null) return

		var cookie = ToolMan.cookies().get("list-" + listID)
		if (!cookie) return;

		var IDs = cookie.split('|')
		var items = ToolMan.junkdrawer()._itemsByID(list)

		for (var i = 0, n = IDs.length; i < n; i++) {
			var itemID = IDs[i]
			if (itemID in items) {
				var item = items[itemID]
				list.removeChild(item)
				list.insertBefore(item, null)
			}
		}
	},

	_identifier : function(item) {
		var trim = ToolMan.junkdrawer().trim
		var identifier

		identifier = trim(item.getAttribute("id"))
		if (identifier != null && identifier.length > 0) return identifier;
		
		identifier = trim(item.getAttribute("itemID"))
		if (identifier != null && identifier.length > 0) return identifier;
		
		// FIXME: strip out special chars or make this an MD5 hash or something
		return trim(item.innerHTML)
	},

	_itemsByID : function(list) {
		var array = new Array()
		var items = list.getElementsByTagName('li')
		for (var i = 0, n = items.length; i < n; i++) {
			var item = items[i]
			array[ToolMan.junkdrawer()._identifier(item)] = item
		}
		return array
	},

	trim : function(text) {
		if (text == null) return null
		return text.replace(/^(\s+)?(.*\S)(\s+)?$/, '$2')
	}
}

/* Copyright (c) 2005 Tim Taylor Consulting (see LICENSE.txt)

based on http://www.quirksmode.org/js/cookies.html
*/

ToolMan._cookieOven = {

	set : function(name, value, expirationInDays) {
		if (expirationInDays) {
			var date = new Date()
			date.setTime(date.getTime() + (expirationInDays * 24 * 60 * 60 * 1000))
			var expires = "; expires=" + date.toGMTString()
		} else {
			var expires = ""
		}
		document.cookie = name + "=" + value + expires + "; path=/"
	},

	get : function(name) {
		var namePattern = name + "="
		var cookies = document.cookie.split(';')
		for(var i = 0, n = cookies.length; i < n; i++) {
			var c = cookies[i]
			while (c.charAt(0) == ' ') c = c.substring(1, c.length)
			if (c.indexOf(namePattern) == 0)
				return c.substring(namePattern.length, c.length)
		}
		return null
	},

	eraseCookie : function(name) {
		createCookie(name, "", -1)
	}
}
/* Copyright (c) 2005 Tim Taylor Consulting (see LICENSE.txt) */

/* FIXME: assumes position styles are specified in 'px' */

ToolMan._coordinatesFactory = {

	create : function(x, y) {
		// FIXME: Safari won't parse 'throw' and aborts trying to do anything with this file
		//if (isNaN(x) || isNaN(y)) throw "invalid x,y: " + x + "," + y
		return new _ToolManCoordinate(this, x, y)
	},

	origin : function() {
		return this.create(0, 0)
	},

	/*
	 * FIXME: Safari 1.2, returns (0,0) on absolutely positioned elements
	 */
	topLeftPosition : function(element) {
		var left = parseInt(ToolMan.css().readStyle(element, "left"))
		var left = isNaN(left) ? 0 : left
		var top = parseInt(ToolMan.css().readStyle(element, "top"))
		var top = isNaN(top) ? 0 : top

		return this.create(left, top)
	},

	bottomRightPosition : function(element) {
		return this.topLeftPosition(element).plus(this._size(element))
	},

	topLeftOffset : function(element) {
		var offset = this._offset(element) 

		var parent = element.offsetParent
		while (parent) {
			offset = offset.plus(this._offset(parent))
			parent = parent.offsetParent
		}
		return offset
	},

	bottomRightOffset : function(element) {
		return this.topLeftOffset(element).plus(
				this.create(element.offsetWidth, element.offsetHeight))
	},

	scrollOffset : function() {
		if (window.pageXOffset) {
			return this.create(window.pageXOffset, window.pageYOffset)
		} else if (document.documentElement) {
			return this.create(
					document.body.scrollLeft + document.documentElement.scrollLeft, 
					document.body.scrollTop + document.documentElement.scrollTop)
		} else if (document.body.scrollLeft >= 0) {
			return this.create(document.body.scrollLeft, document.body.scrollTop)
		} else {
			return this.create(0, 0)
		}
	},

	clientSize : function() {
		if (window.innerHeight >= 0) {
			return this.create(window.innerWidth, window.innerHeight)
		} else if (document.documentElement) {
			return this.create(document.documentElement.clientWidth,
					document.documentElement.clientHeight)
		} else if (document.body.clientHeight >= 0) {
			return this.create(document.body.clientWidth,
					document.body.clientHeight)
		} else {
			return this.create(0, 0)
		}
	},

	/**
	 * mouse coordinate relative to the window (technically the
	 * browser client area) i.e. the part showing your page
	 *
	 * NOTE: in Safari the coordinate is relative to the document
	 */
	mousePosition : function(event) {
		event = ToolMan.events().fix(event)
		return this.create(event.clientX, event.clientY)
	},

	/**
	 * mouse coordinate relative to the document
	 */
	mouseOffset : function(event) {
		event = ToolMan.events().fix(event)
		if (event.pageX >= 0 || event.pageX < 0) {
			return this.create(event.pageX, event.pageY)
		} else if (event.clientX >= 0 || event.clientX < 0) {
			return this.mousePosition(event).plus(this.scrollOffset())
		}
	},

	_size : function(element) {
	/* TODO: move to a Dimension class */
		return this.create(element.offsetWidth, element.offsetHeight)
	},

	_offset : function(element) {
		return this.create(element.offsetLeft, element.offsetTop)
	}
}

function _ToolManCoordinate(factory, x, y) {
	this.factory = factory
	this.x = isNaN(x) ? 0 : x
	this.y = isNaN(y) ? 0 : y
}

_ToolManCoordinate.prototype = {
	toString : function() {
		return "(" + this.x + "," + this.y + ")"
	},

	plus : function(that) {
		return this.factory.create(this.x + that.x, this.y + that.y)
	},

	minus : function(that) {
		return this.factory.create(this.x - that.x, this.y - that.y)
	},

	min : function(that) {
		return this.factory.create(
				Math.min(this.x , that.x), Math.min(this.y , that.y))
	},

	max : function(that) {
		return this.factory.create(
				Math.max(this.x , that.x), Math.max(this.y , that.y))
	},

	constrainTo : function (one, two) {
		var min = one.min(two)
		var max = one.max(two)

		return this.max(min).min(max)
	},

	distance : function (that) {
		return Math.sqrt(Math.pow(this.x - that.x, 2) + Math.pow(this.y - that.y, 2))
	},

	reposition : function(element) {
		element.style["top"] = this.y + "px"
		element.style["left"] = this.x + "px"
	}
}

/* Copyright (c) 2005 Tim Taylor Consulting (see LICENSE.txt) */

// TODO: write unit tests
ToolMan._cssFactory = {
	readStyle : function(element, property) {
		if (element.style[property]) {
			return element.style[property]
		} else if (element.currentStyle) {
			return element.currentStyle[property]
		} else if (document.defaultView && document.defaultView.getComputedStyle) {
			var style = document.defaultView.getComputedStyle(element, null)
			return style.getPropertyValue(property)
		} else {
			return null
		}
	}
}
/* Copyright (c) 2005 Tim Taylor Consulting (see LICENSE.txt) */

ToolMan._eventsFactory = {
	fix : function(event) {
		if (!event) event = window.event

		if (event.target) {
			if (event.target.nodeType == 3) event.target = event.target.parentNode
		} else if (event.srcElement) {
			event.target = event.srcElement
		}

		return event
	},

	register : function(element, type, func) {
		if (element.addEventListener) {
			element.addEventListener(type, func, false)
		} else if (element.attachEvent) {
			if (!element._listeners) element._listeners = new Array()
			if (!element._listeners[type]) element._listeners[type] = new Array()
			var workaroundFunc = function() {
				func.apply(element, new Array())
			}
			element._listeners[type][func] = workaroundFunc
			element.attachEvent('on' + type, workaroundFunc)
		}
	},

	unregister : function(element, type, func) {
		if (element.removeEventListener) {
			element.removeEventListener(type, func, false)
		} else if (element.detachEvent) {
			if (element._listeners 
					&& element._listeners[type] 
					&& element._listeners[type][func]) {

				element.detachEvent('on' + type, 
						element._listeners[type][func])
			}
		}
	}
}
/* Copyright (c) 2005 Tim Taylor Consulting (see LICENSE.txt) */

ToolMan._dragFactory = {
	createSimpleGroup : function(element, handle) {
		handle = handle ? handle : element
		var group = this.createGroup(element)
		group.setHandle(handle)
		group.transparentDrag()
		group.onTopWhileDragging()
		return group
	},

	createGroup : function(element) {
		var group = new _ToolManDragGroup(this, element)

		var position = ToolMan.css().readStyle(element, 'position')
		if (position == 'static') {
			element.style["position"] = 'relative'
		} else if (position == 'absolute') {
			/* for Safari 1.2 */
			ToolMan.coordinates().topLeftOffset(element).reposition(element)
		}

		// TODO: only if ToolMan.isDebugging()
		group.register('draginit', this._showDragEventStatus)
		group.register('dragmove', this._showDragEventStatus)
		group.register('dragend', this._showDragEventStatus)

		return group
	},

	_showDragEventStatus : function(dragEvent) {
		window.status = dragEvent.toString()
	},

	constraints : function() {
		return this._constraintFactory
	},

	_createEvent : function(type, event, group) {
		return new _ToolManDragEvent(type, event, group)
	}
}

function _ToolManDragGroup(factory, element) {
	this.factory = factory
	this.element = element
	this._handle = null
	this._thresholdDistance = 0
	this._transforms = new Array()
	// TODO: refactor into a helper object, move into events.js
	this._listeners = new Array()
	this._listeners['draginit'] = new Array()
	this._listeners['dragstart'] = new Array()
	this._listeners['dragmove'] = new Array()
	this._listeners['dragend'] = new Array()
}

_ToolManDragGroup.prototype = {
	/*
	 * TODO:
	 *   - unregister(type, func)
	 *   - move custom event listener stuff into Event library
	 *   - keyboard nudging of "selected" group
	 */

	setHandle : function(handle) {
		var events = ToolMan.events()

		handle.toolManDragGroup = this
		events.register(handle, 'mousedown', this._dragInit)
		handle.onmousedown = function() { return false }

		if (this.element != handle)
			events.unregister(this.element, 'mousedown', this._dragInit)
	},

	register : function(type, func) {
		this._listeners[type].push(func)
	},

	addTransform : function(transformFunc) {
		this._transforms.push(transformFunc)
	},

	verticalOnly : function() {
		this.addTransform(this.factory.constraints().vertical())
	},

	horizontalOnly : function() {
		this.addTransform(this.factory.constraints().horizontal())
	},

	setThreshold : function(thresholdDistance) {
		this._thresholdDistance = thresholdDistance
	},

	transparentDrag : function(opacity) {
		var opacity = typeof(opacity) != "undefined" ? opacity : 0.75;
		var originalOpacity = ToolMan.css().readStyle(this.element, "opacity")

		this.register('dragstart', function(dragEvent) {
			var element = dragEvent.group.element
			element.style.opacity = opacity
			element.style.filter = 'alpha(opacity=' + (opacity * 100) + ')'
		})
		this.register('dragend', function(dragEvent) {
			var element = dragEvent.group.element
			element.style.opacity = originalOpacity
			element.style.filter = 'alpha(opacity=100)'
		})
	},

	onTopWhileDragging : function(zIndex) {
		var zIndex = typeof(zIndex) != "undefined" ? zIndex : 100000;
		var originalZIndex = ToolMan.css().readStyle(this.element, "z-index")

		this.register('dragstart', function(dragEvent) {
			dragEvent.group.element.style.zIndex = zIndex
		})
		this.register('dragend', function(dragEvent) {
			dragEvent.group.element.style.zIndex = originalZIndex
		})
	},

	_dragInit : function(event) {
		event = ToolMan.events().fix(event)
		var group = document.toolManDragGroup = this.toolManDragGroup
		var dragEvent = group.factory._createEvent('draginit', event, group)

		group._isThresholdExceeded = false
		group._initialMouseOffset = dragEvent.mouseOffset
		group._grabOffset = dragEvent.mouseOffset.minus(dragEvent.topLeftOffset)
		ToolMan.events().register(document, 'mousemove', group._drag)
		document.onmousemove = function() { return false }
		ToolMan.events().register(document, 'mouseup', group._dragEnd)

		group._notifyListeners(dragEvent)
	},

	_drag : function(event) {
		event = ToolMan.events().fix(event)
		var coordinates = ToolMan.coordinates()
		var group = this.toolManDragGroup
		if (!group) return
		var dragEvent = group.factory._createEvent('dragmove', event, group)

		var newTopLeftOffset = dragEvent.mouseOffset.minus(group._grabOffset)

		// TODO: replace with DragThreshold object
		if (!group._isThresholdExceeded) {
			var distance = 
					dragEvent.mouseOffset.distance(group._initialMouseOffset)
			if (distance < group._thresholdDistance) return
			group._isThresholdExceeded = true
			group._notifyListeners(
					group.factory._createEvent('dragstart', event, group))
		}

		for (i in group._transforms) {
			var transform = group._transforms[i]
			newTopLeftOffset = transform(newTopLeftOffset, dragEvent)
		}

		var dragDelta = newTopLeftOffset.minus(dragEvent.topLeftOffset)
		var newTopLeftPosition = dragEvent.topLeftPosition.plus(dragDelta)
		newTopLeftPosition.reposition(group.element)
		dragEvent.transformedMouseOffset = newTopLeftOffset.plus(group._grabOffset)

		group._notifyListeners(dragEvent)

		var errorDelta = newTopLeftOffset.minus(coordinates.topLeftOffset(group.element))
		if (errorDelta.x != 0 || errorDelta.y != 0) {
			coordinates.topLeftPosition(group.element).plus(errorDelta).reposition(group.element)
		}
	},

	_dragEnd : function(event) {
		event = ToolMan.events().fix(event)
		var group = this.toolManDragGroup
		var dragEvent = group.factory._createEvent('dragend', event, group)

		group._notifyListeners(dragEvent)

		this.toolManDragGroup = null
		ToolMan.events().unregister(document, 'mousemove', group._drag)
		document.onmousemove = null
		ToolMan.events().unregister(document, 'mouseup', group._dragEnd)
	},

	_notifyListeners : function(dragEvent) {
		var listeners = this._listeners[dragEvent.type]
		for (i in listeners) {
			listeners[i](dragEvent)
		}
	}
}

function _ToolManDragEvent(type, event, group) {
	this.type = type
	this.group = group
	this.mousePosition = ToolMan.coordinates().mousePosition(event)
	this.mouseOffset = ToolMan.coordinates().mouseOffset(event)
	this.transformedMouseOffset = this.mouseOffset
	this.topLeftPosition = ToolMan.coordinates().topLeftPosition(group.element)
	this.topLeftOffset = ToolMan.coordinates().topLeftOffset(group.element)
}

_ToolManDragEvent.prototype = {
	toString : function() {
		return "mouse: " + this.mousePosition + this.mouseOffset + "    " +
				"xmouse: " + this.transformedMouseOffset + "    " +
				"left,top: " + this.topLeftPosition + this.topLeftOffset
	}
}

ToolMan._dragFactory._constraintFactory = {
	vertical : function() {
		return function(coordinate, dragEvent) {
			var x = dragEvent.topLeftOffset.x
			return coordinate.x != x
					? coordinate.factory.create(x, coordinate.y) 
					: coordinate
		}
	},

	horizontal : function() {
		return function(coordinate, dragEvent) {
			var y = dragEvent.topLeftOffset.y
			return coordinate.y != y
					? coordinate.factory.create(coordinate.x, y) 
					: coordinate
		}
	}
}
/* Copyright (c) 2005 Tim Taylor Consulting (see LICENSE.txt) */

ToolMan._dragsortFactory = {
	makeSortable : function(item) {
		var group = ToolMan.drag().createSimpleGroup(item)

		group.register('dragstart', this._onDragStart)
		group.register('dragmove', this._onDragMove)
		group.register('dragend', this._onDragEnd)

		return group
	},

	/** 
	 * Iterates over a list's items, making them sortable, applying
	 * optional functions to each item.
	 *
	 * example: makeListSortable(myList, myFunc1, myFunc2, ... , myFuncN)
	 */
	makeListSortable : function(list) {
		var helpers = ToolMan.helpers()
		var coordinates = ToolMan.coordinates()
		var items = list.getElementsByTagName("li")

		helpers.map(items, function(item) {
			var dragGroup = dragsort.makeSortable(item)
			dragGroup.setThreshold(4)
			var min, max
			dragGroup.addTransform(function(coordinate, dragEvent) {
				return coordinate.constrainTo(min, max)
			})
			dragGroup.register('dragstart', function() {
				var items = list.getElementsByTagName("li")
				min = max = coordinates.topLeftOffset(items[0])
				for (var i = 1, n = items.length; i < n; i++) {
					var offset = coordinates.topLeftOffset(items[i])
					min = min.min(offset)
					max = max.max(offset)
				}
			})
		})
		for (var i = 1, n = arguments.length; i < n; i++)
			helpers.map(items, arguments[i])
	},

	_onDragStart : function(dragEvent) {
	},

	_onDragMove : function(dragEvent) {
		var helpers = ToolMan.helpers()
		var coordinates = ToolMan.coordinates()

		var item = dragEvent.group.element
		var xmouse = dragEvent.transformedMouseOffset
		var moveTo = null

		var previous = helpers.previousItem(item, item.nodeName)
		while (previous != null) {
			var bottomRight = coordinates.bottomRightOffset(previous)
			if (xmouse.y <= bottomRight.y && xmouse.x <= bottomRight.x) {
				moveTo = previous
			}
			previous = helpers.previousItem(previous, item.nodeName)
		}
		if (moveTo != null) {
			helpers.moveBefore(item, moveTo)
			return
		}

		var next = helpers.nextItem(item, item.nodeName)
		while (next != null) {
			var topLeft = coordinates.topLeftOffset(next)
			if (topLeft.y <= xmouse.y && topLeft.x <= xmouse.x) {
				moveTo = next
			}
			next = helpers.nextItem(next, item.nodeName)
		}
		if (moveTo != null) {
			helpers.moveBefore(item, helpers.nextItem(moveTo, item.nodeName))
			return
		}
	},

	_onDragEnd : function(dragEvent) {
		ToolMan.coordinates().create(0, 0).reposition(dragEvent.group.element)
	}
}

