/**
 * KTK Functions (part of KTK API)
 * Copyright by J. Christopher Pereira Zimmermann
 * Copyright by IMATRONIX S.A.
 * Prohibited use without granted permissions.
 * For more info about this API, please write to (kripper at imatronix dot cl).
 */

// Convierte "0" a 0 para poder verificar valores de checkboxes en forma sencilla
function ktkPHPBool(val) {
	return val == "0" ? 0 : val;
}

function ktkEditableComponentSelected() {
	type = event.srcElement.type;
	if (type == 'text' || type =='textarea' || event.srcElement.canSelect) {
		return true;
	} else {
		return false;
	}
}

function onSelectStart(e) {
	return ktkEditableComponentSelected();
}

/*
<?
if($KTK_DISABLE_SELECTION) {
?>
	//if IE4+
	document.onselectstart = onSelectStart;

	//if NS6
	if (window.sidebar){
		document.onmousedown=new Function("return false");
		document.onclick=new Function("return true");
	}
<?
}
?>
*/

function ktkIsFunction(a) {
    return typeof a == 'function';
}

function ktkIsObject(a) {
    return (a && typeof a == 'object') || ktkIsFunction(a);
}

function ktkDump(obj) {
	if(typeof(DUMP_WINDOW) == "undefined") {
		DUMP_WINDOW = window.open("name=ktkDump,width=500,height=200");
	}
	var win = DUMP_WINDOW;
	win.document.write("<textarea style='width:100%;height:100%'>");
	win.document.write(_ktkDump(obj));
	win.document.write("</textarea>");
	win.document.close();
}

function _ktkDump(obj, level) {
	if(!level) level = 0;
	
	if(!ktkIsObject(obj)) {
		var type = typeof(obj);
		if(type == 'number') {
			return obj + "\n";

		} else if(type == 'string') {
			obj = obj.replace(/textarea/gi, '_textarea');
			if(obj == 'yes') {
				return obj + "\n";
			} else {
				return obj + "\n";
			}

		} else {
			return "type: " + type + "\n";
		}
	}
	
	try {
		var space = "";
		for(var i = 0; i < level; i++) {
			space = space + "   ";
		}
		var txt = "{\n"
		for(var p in obj) {
			if (p != "parent" && level < 3) {
				txt += space + "   " + p + " => " + _ktkDump(obj[p], level + 1);
			}
		}
		txt += space + "}\n";
		return txt;
	} catch(e) {
		return "(?)\n";
	}
}

function ktkGetMousePosition(e) {
	var x, y;
    if (document.layers) {
		// Netscape
        x = e.pageX;
        y = e.pageY;
        // xMousePosMax = window.innerWidth+window.pageXOffset;
        // yMousePosMax = window.innerHeight+window.pageYOffset;
    } else if (document.all) {
		// IE
        x = window.event.x+document.body.scrollLeft;
        y = window.event.y+document.body.scrollTop;
        // xMousePosMax = document.body.clientWidth+document.body.scrollLeft;
        // yMousePosMax = document.body.clientHeight+document.body.scrollTop;
    } else if (document.getElementById) {
        // Netscape 6 behaves the same as Netscape 4 in this regard 
        x = e.pageX;
        y = e.pageY;
        // xMousePosMax = window.innerWidth+window.pageXOffset;
        // yMousePosMax = window.innerHeight+window.pageYOffset;
    }
	return new Array(x,y);
}

function ktkCopyObject(obj) {
	var res = new Array();
	for(key in obj) {
		res[key] = obj[key];
	}
	return res;
}

function ktkLeftTrim(str)
{
   var whitespace = new String(" \t\n\r");

   var s = new String(str);

   if (whitespace.indexOf(s.charAt(0)) != -1) {
      // We have a string with leading blank(s)...

      var j=0, i = s.length;

      // Iterate from the far left of string until we
      // don't have any more whitespace...
      while (j < i && whitespace.indexOf(s.charAt(j)) != -1)
         j++;

      // Get the substring from the first non-whitespace
      // character to the end of the string...
      s = s.substring(j, i);
   }
   return s;
}

function ktkRightTrim(str) {
   var whitespace = new String(" \t\n\r");

   var s = new String(str);

   if (whitespace.indexOf(s.charAt(s.length - 1)) != -1) {
      // We have a string with trailing blank(s)...

      var i = s.length - 1;       // Get length of string

      // Iterate from the far right of string until we
      // don't have any more whitespace...
      while (i >= 0 && whitespace.indexOf(s.charAt(i)) != -1)
         i--;


      // Get the substring from the front of the string to
      // where the last non-whitespace character is...
      s = s.substring(0, i+1);
   }

   return s;
}

function ktkTrim(str) {
   return ktkRightTrim(ktkLeftTrim(str));
}

function ktkValidateUnitsField(objName, arr, def) {
	objName.value = ktkValidateUnits(objName.value, arr, def);
}

function ktkValidateUnits(str, arr, def) {
	str = str.toLowerCase();
	
	var min = str.length;
	var singular;
	var plural;
	var ok = false;
	
	for(var i in arr) {
		var splited = arr[i].split("|");
		var abrev = splited[0];
		
		var pos = str.indexOf(abrev);
		if (pos != -1 && pos < min) {
			min = pos;
			singular = splited[1];
			plural = splited[2];
			ok = true;
		}
		if (!singular) {
			return ktkValidateUnits(str + def, arr);
		}
	}
	
	var val = parseInt(ktkTrim(str.substr(0, min)), 10);
	if(isNaN(val)) val = 0;
	return val + " " + (val == 1 ? singular : plural);
}

function ktkRemoveUnits(str) {

	str = "" + str;
	
	var arr = str.split(" ");
	if (arr.length == 1) return str;
	var res = "";
	for(var i = 0; i < arr.length - 1; i++) {
		if (res) res += " ";
		res += arr[i];
	}
	return res;
}

function ktkReload() {
	location.reload(true);
}

function ktkRedirect(url) {
	location = url;
}

function ktkValidate(obj, arr) {
	for(k in arr) {
		if(obj[k].value == "") {
			alert("Debe ingresar '" + arr[k] + "'");
			return false;
		}
	}
	return true;
}

// --- Storage ---

function ktkGetVar(set, key) {
	ktkInitDict(set);
	var value = KTK_JS_DICTS[set][key];
	return value;
}

function ktkSetVar(set, key, value) {
	ktkInitDict(set);
	KTK_JS_DICTS[set][key] = value;
	ktkSaveDict(set); // TODO: OPT: Hacer al ejecutar unload (?)
}

function ktkInitDict(set) {
	if(!window.KTK_JS_DICTS) {
		KTK_JS_DICTS = {};
		ktkSTONE = new JSTONE(JSON, false);
	}
	if(!KTK_JS_DICTS[set]) {
		if(ktkSTONE) {
			val = ktkSTONE.read(set);
			KTK_JS_DICTS[set] = val ? val : {};
		}
	}
}

function ktkSaveDict(set) {
	ktkSTONE.write(set, KTK_JS_DICTS[set])
}

function ktkToJSON(dict) {
	var res = "";
	for(var i in dict) {
		if(res) res += ",";
		var val = dict[i];
		if(typeof(val) != "number") val = '"' + val + '"';
		res += '"' + i + '":' + val;
	}
	return res;
}

function ktkGetCookie(name) {
	var bikky = document.cookie;
	var index = bikky.indexOf(name + "=");
	if (index == -1) return null;
	// index = bikky.indexOf("=", index) + 1;
	index += name.length + 1;
	var endstr = bikky.indexOf(";", index);
	if (endstr == -1) endstr = bikky.length;
	var val = unescape(bikky.substring(index, endstr));
	return val;
}

function ktkSetCookie(name, value) {
	var today = new Date();
	var expiry = new Date(today.getTime() + 28 * 24 * 60 * 60 * 1000); // plus 28 days
	if (value != null && value + "" != "") {
		document.cookie = name + "=" + escape(value) + "; expires=" + expiry.toGMTString();
	}
}

if(1) {
// JSTONE v0.3

// (C) Andrea Giammarchi - www.3site.eu
function JSONError(message){this.message=message||"";this.name="JSONError"};JSONError.prototype=new Error;
function JSONRequestError(message){this.message=message||"";this.name="JSONRequestError"};JSONRequestError.prototype=new Error;
function JSTONE(JSON, free, clear){
	this.clear = function(){
		for(var	key = k.split("."), i = 0, l = key.length - 1, t = get(); i < l; i++) {
			if(!t[key[i]])t[key[i]] = {};
			t = t[key[i]];
		};	t[key[i]] = {};
		sync();
	};
	
	this.read = function(key){
		var	o = find(key);
		return o.o[o.key]
	};
	
	this.write = function(key, value){
		var	o = find(key);
		o.o[o.key] = value;
		sync();
	};
	
	var rootFrame = ktkGetRootFrame();

	var	find = function(key) {
			for(var i = 0, l = (key = k.concat(".", key).split(".")).length - 1, t = get(); i < l; i++) {
				if(!t[key[i]])t[key[i]] = {};
				t = t[key[i]];
			};	return {o:t, key:key.pop()}
		},
		get = function() {
			if(o) return o;
			try {
				if(rootFrame.name) {
					var res = JSON.parse(rootFrame.name);
					if(res["_IMATRONIX"]) return o = res;
				}
			} catch (e) {}
			return {};
		},
		sync = function() {
			if(!o) o = {};
			o["_IMATRONIX"] = 1;
			rootFrame.name = JSON.stringify(o);
		},
		unload = clear ? function(){rootFrame.name = ""} : null,
		k = !free ? location.href.split("/").slice(2,3)[0].replace(/:[0-9]+/, "") : "o.o",
		o = null;
	get();
	if(unload)
		window.addEventListener ? window.addEventListener("unload", unload, false) : window.attachEvent("on".concat("unload"), unload);
};
}
//

function ktkExtendName(name, postfix) {
	var i = name.indexOf('[');
	if(i >= 0) {
		return name.substr(0, i) + "_" + postfix + name.substr(i);
	} else {
		return name + "_" + postfix;
	}
}

function ktkRemoveExtend(name) {
	var i = name.lastIndexOf('[');
	var j = name.lastIndexOf('_');
	if(i >= 0) {
		return name.substr(0, j) + name.substr(i);
	} else {
		return name.substr(0, j);
	}
}

function ktkGetQueryVar(name) {
	var query = '&' + window.location.search.substr(1);
	var i = query.indexOf('&' + name);
	if(i == -1) return "";
	i = i + name.length + 2;
	var f = query.indexOf("&", i);
	if(f == -1) {
		return query.substr(i);
	} else {
		return query.substr(i, f - i);
	}
}

function ktkExtendQuery(arr) {
	query = window.location.search.substr(1);
	return ktkChangeQueryVars(query, arr);
}

// query es una URL completa (url + vars)
function ktkChangeQuery(query, arr) {
	var pos = query.indexOf('?');
	var url, vars;
	if(pos >= 0) {
		url = query.substr(0, pos);
		vars = query.substr(pos + 1);
	} else {
		url = query;
		vars = "";
	}
	vars = ktkChangeQueryVars(vars, arr);
	if(vars) url = url + "?" + vars;
	return url;
}
	
// query es sólo las variables
function ktkChangeQueryVars(query, arr) {
	// Get old array
	var old = new Array();
	var args = query.split(/&/);

	for(var i = 0; i < args.length; i++) {
		if(args[i]) {
			var tmp = args[i].split(/=/);
			if(tmp.length != 2) {
				tmp[1] = '';
			}
			old[tmp[0]] = tmp[1];
		}
	}
	
	// Overwrite old array
	for(var i in arr) {
		old[i] = arr[i];
	}

	// Get new string
	var str = '';
	for(var i in old) {
		if(old[i] != null) {
			str += '&' + i + '=' + old[i];
		}
	}
	return str;
}

// TODO: Deprecar
function ktkIsUserEvent() {
	return !window.KTK_IS_NOT_USER_EVENT;
}

function ktkIsLoading() {
	// TODO: No usar ktkIsUserEvent()
	return !ktkIsUserEvent();
}

function ktkPadLeft(str, val, chr) {
	str = "" + str;
	var n = val - str.length;
	for(i=0; i < n; i++) {
		str = chr + str;
	}
	return str
}

function ktkPadRight(str, val, chr) {
	str = "" + str;
	var n = val - str.length;
	for(i=0; i < n; i++) {
		str += chr;
	}
	return str
}

function ktkSetObjectValue(obj, value, isUserEvent, ignoreError) {
	var ret;

	if(!obj) {
		if(!ignoreError) {
			ktkDebug("Objeto no existe");
		}
		return;
	}
	
	// ktkDebug(obj.id + " = " + value);
	
	if(window.KTK_TRANSACTION) {
		// Ignorar asignaciones duplicadas dentro de una transacción
		
		// BUG: REMOVED: En Transtecnia > NV > Importar dscto => calculaba mal totales (ver doc. papel 26/02/2007)
		// if(obj.transaction && obj.transaction == KTK_TRANSACTION) return;
		
		obj.transaction = KTK_TRANSACTION;
	}

	var bak = window.KTK_IS_NOT_USER_EVENT;
	if(typeof(isUserEvent) != 'undefined' && isUserEvent != null) KTK_IS_NOT_USER_EVENT = !isUserEvent;
	
	var ktkType = obj.getAttribute('ktkType');
	if(ktkType == "ktkImage") {
		var imgField = ktkExtendName(obj.id, "img");
		var imgObj = document.getElementById(imgField);
		imgObj.src = IMAGES_URL + "/loading.gif";
		// TODO: Wait until loaded
		 // TODO: Pa q ktkPHPBool?
		if(ktkPHPBool(value) && value != 'd') {
			imgObj.src = "/?a=download&file=" + value;
		} else {
			imgObj.src = ROOT_URL + "/images/dot.gif";
		}
	}
	
	if(ktkType == "ktkFile" || ktkType == "ktkImage") {
		var div = ktkObj(ktkExtendName(obj.id, "div"));
		if(!div.getAttribute("ktkReadOnly")) {
			ktkCompatSetStyle(div, "display", (value && value != "0")? "" : "none");
		}
	}
	
	if(obj.type == "checkbox") {
		if(value && value != "0") value = 1; // OBS: permitir '', 0 ó 1
		oldVal = obj.checked ? 1 : 0;
		obj.checked = (value == 1);
		
	} else if(obj.type) {
		if(ktkType == "ktkNumber" || ktkType == "ktkPercent") {
			value = ktkNumberBox_ApplyPrecision(obj, value);
		}
		
		if(value == null) value = "";
		oldVal = obj.value;
		obj.value = value;
		
    } else {
		oldVal = obj.innerHTML;
		obj.innerHTML = value;
    }
	
	if(ktkType == "ktkDate") {
		ktkDate_SetAuxFields(obj.id, ktkDate_GetDate(value));
	}
	
	if(oldVal != value + "" && obj.onchange) {
		// ktkDebug("ENQUEUE: " + obj.id + ", notUserEvent = " + KTK_IS_NOT_USER_EVENT);
		if(window.KTK_ENQUEUE_ONCHANGE_EVENTS) {
			KTK_ONCHANGE_QUEUE.push({
				"object": obj,
				"notUserEvent": KTK_IS_NOT_USER_EVENT
			});
		} else {
			// ktkDebugLog("Trigger OnChange: " + obj.id + " = " + obj.onchange);
			if(obj.type == "checkbox") {
				var f = obj.onclick;
			} else {
				var f = obj.onchange;
			}
			ret = f.apply(obj); // OBS: No hacer directamente obj.onchange() ya que produce stack overflow en menos de 10 llamadas recurrentes.
		}
	}
	
	window.KTK_IS_NOT_USER_EVENT = bak;
	return ret;
}

function ktkSelectFirstInputBox() {
}

// Funciones generales para manejo de ventanas

// Retorna la ventana desde la cual se creó la ventana hija.
// En el caso de los iframes, no necesariamente es el rootWindow.
// Esto es util para obtener la ventana en la cual se encuentran las funciones.
function ktkGetParentWindow() {
	if(typeof(ktkParentWindow) != "undefined") {
		return ktkParentWindow;
	} else if(window.parent && window.parent != window) {
		return window.parent;
	} else {
		return null;
	}
}

function ktkGetRootWindow() {
	if(typeof(ktkRootWindow) != "undefined") {
		return ktkRootWindow;
	} else if(window.parent && window.parent != window) {
		// TODO: Retornar rootWindow
		return window.parent;
	} else {
		return null;
	}
}

// Evento que se invoca cuando la página ha cambiado de tamano, para que haga resize de un iframe padre (cuando exista)
// TODO: En una ventana normal, sólo se carga al inicio. No se vuelve a ejecutar al hacer resize.
function ktkWindowResized() {
	ktkResizeListView();
	if(!window.notifiedLoad) return;
	if(ktkIsWindow()) {
		// Es un ktkWindow
		// TODO: Unir resize de ventanas con el de iframes
		ktkWndObj.resize();
		
	} else if(window.parent != window) {
		// Es un ktkFrame
		if(window.parent.ktkResizeIFrame) { // OBS: Si tiene inTab y no está en un frame, genera error
			window.parent.ktkResizeIFrame(window);
		}
	}
}

function ktkBaseName(str) {
	var i = str.lastIndexOf('\\', '/');
	if(i != -1) {
		return str.substr(i + 1);
	}
	return '';
}

// Ajusta tamaño del tableContainer en ListView
function ktkResizeListView() {
	var o = ktkObj("tableContainer");
	if(o) {
		var divMin = ktkObj("ktkDivMin"); // Height = contenido actual
		var divMax = ktkObj("ktkDivMax"); // Height = 100%
		
		if(divMax) {
			var diff = divMax.offsetHeight - divMin.offsetHeight;
			var origHeight = parseFloat(o.offsetHeight);
	
			o.style.height = origHeight + diff + "px"
	
			/*
			divMax.style.backgroundColor = "red";
			divMin.style.backgroundColor = "blue";
			*/
		}
	}
}

// Se ejecuta en la ventana (childWin)
function ktkDocLoaded() {
	if(window.notifiedLoad) return;
	window.notifiedLoad = true;
	
	// Ver si es una ventana e inicializa
	if(window.parent.ktkWindowInitFromChild) {
		window.parent.ktkWindowInitFromChild(window);
	}
	
	// Si no es ventana => hacer resize
	if(!ktkIsWindow()) {
		ktkWindowResized();
	}
	
	// Verificar si estamos en un frameset (editor)
	/*
	if(parent.frames['ktkBottomFrame']) {
		TODO: Add evento to document.body.onResize
	}
	*/
}

function ktkHideWindow() {
	if(ktkIsWindow()) {
		ktkWndObj.hide();
	}
}

function ktkCloseWindow() {
	if(ktkIsWindow()) {
		ktkWndObj.close();
	}
}

function ktkIsWindow() {
	return (typeof(ktkWndObj) != 'undefined' && ktkWndObj);
}

function ktkFocus(id, selectAll) {
	var obj = ktkGetInputControl(id);
	ktkFocus2(obj, selectAll);
}

function ktkFocus2(obj, selectAll) {
/*
	setTimeout(new Function("ktkFocus2_sub('" + obj.id + "', " + (selectAll ? 1 : 0) + ")"), 100);
}
	
function ktkFocus2_sub(id, selectAll) {
	var obj = ktkObj(id);
*/
	try {
		obj.focus();
		if(MSIE) {
			if (obj.createTextRange) {
				var v = obj.value;
				var r = obj.createTextRange();
				if(!selectAll) {
					r.moveStart('character', v.length);
				}
				r.select();
			 }
		} else {
			obj.select();
		}
		return true;
	} catch(e) {
		return false;
	}
}

function ktkGetFrameSet() {
	if(parent.document.body.rows) {
		return window.parent.parent;
	}
}

/**
 * Abre un link dentro del frame raíz o en una nueva ventana si se presionó shift.
 * Uso: <a href="#" onClick="return ktkOpenLink(event?, "<url>", ...)
 *
 * evt: Evento JS utilizado para determinar botones utilizados.
 * openInWindow: Si no se usa Shift o Ctrl, se abre dentro de un ktkWindow.
 *
 */
function ktkOpenLink(evt, url, openInRootFrame, target, newWindow, openInWindow, windowTitle) {
	evt = ktkCompatGetEvent(evt);
	var fullUrl = ktkChangeQuery(url, {
		wndId : null
	});

    if(evt && (evt.shiftKey || evt.ctrlKey) || target || newWindow) {
        if(!target) {
			if(evt.ctrlKey) {
				if(MSIE) {
					// No funciona _newtab
					var d = new Date();
					target = d.getTime();
				} else {
					target = "_newtab";
				}

			} else {
				var d = new Date();
				target = d.getTime();
			}
        }

		try {
			window.open(fullUrl, target).focus();
		} catch(e) {}

		if(MOZ) {
			evt.cancelBubble = true;
		}
	} else if(openInWindow) {
		ktkWindowOpen(windowTitle, target, url);
		
	} else if(openInRootFrame) {
		var win = ktkGetRootFrame(1);
		win.location = fullUrl;
			
	} else {
		window.location = url;
	}
	return false;
}

function ktkOpenInFrame(url, id) {
	var name = 'procEditor';
	var frameSet = ktkGetFrameSet();
	if(frameSet) {
		frameSet.frames[1].frames[1].location.href = ktkURL_AddParam(url, 'ktkInFrame=1');
		frameSet.ktkShowFrame(frameSet.frames[1].frames[0]);
	} else {
		// Split Frame
		url = DWB_URL + '/frameset.php?t=' + document.title + '&url1=' + escape(window.location.href) + '&n2=' + name + '&url2=' + escape(ktkURL_AddParam(url, 'ktkInFrame=1'));
		var win = ktkGetRootFrame(1);
		win.location = url;
	}
}

function ktkCloseThisFrame() {
	if(parent.frames['ktkTopFrame']) {
		window.parent.location = parent.frames['ktkTopFrame'].window.location.href;
	}
}

function ktkHideThisFrame_animate(rows) {
	parent.document.body.rows = rows;
}

function ktkHideThisFrame(buttonName) {
	var button = document.getElementById(buttonName);
	if(button.value == "Ocultar") {
		button.value = "Mostrar";
		ktkHideThisFrame_animate("*,60");
	} else {
		button.value = "Ocultar";
		ktkHideThisFrame_animate("0,*");
	}
	parent.document.body.frameButtonId = buttonName;
}

// Conversión

function int(str) {
	if(!str) {
		return 0;
	}
	return parseInt(str, 10);
}

// Convierte a flotante y redondea
function float(str, decimals) {
	if(!str) {
		return 0;
	} else {
		if(!decimals && decimals != 0) {
			return parseFloat(str)
		} else {
			return parseFloat(parseFloat(str).toFixed(decimals)); // OBS: toFixed retorna string
		}
	}
}

function ktkPlaySound(url) {
	var bgSound = document.createElement('BGSOUND');
	bgSound.id = "ktkSound";
	bgSound.autostart = "TRUE";
	bgSound.src = url;
	document.body.appendChild(bgSound);
}

function ktkURL_AddParam(url, param) {
	if(url.indexOf("?") != -1) {
		return url + "&" + param;
	} else {
		return url + "?" + param;
	}
}

function ktkGetSerial() {
	return (new Date()).getTime();
}

function ktkEvents_StartEnqueue(atomicTransaction) {
	if(atomicTransaction) window.KTK_TRANSACTION = ktkGetSerial();
	KTK_ENQUEUE_ONCHANGE_EVENTS = true;
	KTK_ONCHANGE_QUEUE = new Array();
}

function ktkEvents_EndEnqueue() {
	window.KTK_ENQUEUE_ONCHANGE_EVENTS = false;
	var bak = window.KTK_IS_NOT_USER_EVENT;
	for(var i in KTK_ONCHANGE_QUEUE) {
		var e = KTK_ONCHANGE_QUEUE[i];
		if(e.object.onchange) { // TODO: En Transtecnia > NV, TipoOperación.onchange cambia a NULL
			window.KTK_IS_NOT_USER_EVENT = e.notUserEvent;
			// ktkProfilerStart("debKey", e.object.onchange + "");
			// CHANGED: WAS: e.object.onchange();
			e.object.onchange(e.object);
			// ktkProfilerStop("debKey");
		}
	}
	window.KTK_IS_NOT_USER_EVENT = bak;
	
	KTK_TRANSACTION = null;
}

// Debugging

function ktkDebug(msg) {
	if(window.KTK_DEBUG) {
		if(msg) {
			msg += ". ";
		} else {
			msg = "";
		}
		if(confirm(msg + "¿Depurar?")) debugger;
	}
}

// Ver con: javascript:alert(KTK_DEBUG_LOG)
function ktkDebugLog(str) {
	if(!window.KTK_DEBUG_LOG) KTK_DEBUG_LOG = '';
	KTK_DEBUG_LOG += str + "\n";
}

function ktkGetFuncName(f) {
	var arr = f.toString().match(/function (\w*)/);
	if(!arr) return f.toString();
	var s = arr[1];
	if ((s == null) || (s.length==0)) return "anonymous";
	return s;
}

function ktkStackTrace() {
	var s = "";
	for (var a = arguments.caller; a !=null; a = a.caller) {
	s += "->" + ktkGetFuncName(a.callee) + "\n";
	if (a.caller == a) {s+="*"; break;}
	}
	return s;
}

function ktkPrintStackTrace() {
	alert(ktkStackTrace());
}

function ktkSubmitForm(obj) {
	if(obj.onsubmit && !obj.onsubmit()) return false;
	obj.submit();
	return true;
}

function ktkEnableSubmit(name, val) {
	buttonName = name + '_ktkSubmitButton';
	document.getElementById(buttonName).disabled = !val;
}

function ktkObj(id) {
	return document.getElementById(id);
}

function ktkFormObj(id) {
	return document.form.elements[id];
}

function ktkObjChk(id) {
	var obj = ktkObj(id);
	if(!obj) ktkDebug("No existe el campo '" + id + "'");
	return obj;
}

function ktkGetInputControl(id) {
	return ktkObj(ktkExtendName(id, 'ktkField'))
		|| ktkObj(ktkExtendName(id, 'dd'))
		|| ktkObj(ktkExtendName(id, 'mm'))
		|| ktkObj(ktkExtendName(id, 'HH'))
		|| ktkObj(id)
		;
}

// Obtiene la ventana raíz, incluso si estamos en un frameset
function ktkGetRootFrame(checkContainer) {
	if(!window.KTK_ROOT_FRAME) {
		var win;
		for(win = window; win.parent && win.parent != win; win = win.parent) {
			try {
				if(checkContainer && window.parent.frames["ktkRootFrame"] == window) break;
			} catch (e) {
				// Pasó a otro sito
				break;
			}
		}
		KTK_ROOT_FRAME = win;
	}
	return KTK_ROOT_FRAME;
}

function ktkParseObjectId(val) {
	return val.substr(7, val.length - 8);
}

// Cuando se actualiza el padre (src), le ajusta el tamaño al hijo (dst)
function ktkCopyWidth(src, dst) {
	if(float(dst.style.width) != src.clientWidth) {
		P = src.parentNode.innerHTML;
		dst.style.width = src.clientWidth + "px";
		/*
		if(src.ktkResizedWidthCount > 5) {
			// Impedir que incremente infinitamente (TODO: BUG: Si no existe deadLock se incrementa 1 vez. A la quinta vez se ignorará el CopyWidth)
			src.ktkResizedWidthCount = 0; 
		} else {
			dst.style.width = src.clientWidth + "px";
		}
		*/
	}
}

function ktkCopyHeight(src, dst) {
	var fix = 0;
	if(dst.scrollWidth != dst.clientWidth) { // Existe scrollbar horizontal => considerar ScrollBarHeight
		var scrollBarHeight = 20;
		fix = scrollBarHeight;
	}
	dst.style.height = (src.clientHeight + fix) + "px";
}

// Utilizado para asignar al tableContainer DIV el tamaño de su padre, evitando el problema de que al cambiar el tamaño, se modifique recursivamente el tamaño del padre.
function ktkCopyParentsWidth(child) {
	var parent = child.ktkContainer;

	if(!parent.clientWidth) {
		return; // HACK: Fixes bug in AdvOptions > QuerySQL (only IE)
	}

	child.style.width = '0px'; // Asegurar que el hijo no esté agrandando al padre (obtener el ancho heredado del abuelo)
	var parentWidth1 = parent.clientWidth; // Ancho del padre heredado del abuelo (no agrandado por el hijo)
	// var parentWidth1 = parent.offsetWidth; // Ancho del padre heredado del abuelo (no agrandado por el hijo)

	child.style.width = parentWidth1; // Asignar nuevo ancho (se copia del padre)
	var delta = parent.clientWidth - parentWidth1; // Aumento que sufre el padre por asignarse el ancho del hijo
	// var delta = parent.offsetWidth - parentWidth1; // Aumento que sufre el padre por asignarse el ancho del hijo

	if(MOZ) child.style.width = '';
	child.style.width = parseFloat(child.style.width) - delta; // Corrección para caber justo
	
	// Verificación de que ahora el padre no se haya agrandado ni un pixel
	delta = parent.clientWidth - parentWidth1;
	// delta = parent.offsetWidth - parentWidth1;
	
	// if(window.KTK_DEBUG) if(delta) ktkDebug('Div padre aumentó de tamaño');
}

// Agrega padding si el objeto tiene scrollbars, para evitar scrollbar horizontal.
// Utilizado por ktkStaticTable para agregar padding a tableContainer
function ktkCustomResizeHandler(obj) {
	// TODO: No funciona en Registros de Implementación > Cons. Avanzada

	// Cuando aparece ScrollBar vertical, a veces el contenido queda cortado y aparece un scrollbar horizontal.
	if(ktkHasHScrollBar(obj)) {
		// DIV tiene Scrollbar horizontal => reservar espacio a la derecha para tratar de evitar visualización de ScrollBar vertical y así evitar ScrollBar horizontal.
		ktkCompatSetStyle(obj, "paddingRight", "17px");
	} else {
		ktkCompatSetStyle(obj, "paddingRight", "0px");
	}
}

function ktkAddFixedElement(elem, parent) {
	// if(!parent) parent = elem.parentNode;
	if(!parent) parent = elem.parentNode;
	elem.ktkContainer = parent;
	if(!window.KTK_FIXED_ELEMENTS) KTK_FIXED_ELEMENTS = [];
	KTK_FIXED_ELEMENTS.push(elem);
	if(window.resizeHandler) resizeHandler();
	ktkCopyParentsWidth(elem);
}

window.onresize = ktkResizeHandler;

function ktkResizeHandler() {
	if(window.resizeHandler) resizeHandler();
	if(window.KTK_FIXED_ELEMENTS) {
		for(var e in KTK_FIXED_ELEMENTS) {
			var elem = KTK_FIXED_ELEMENTS[e];
			ktkCopyParentsWidth(elem);
		}
	}
}

function ktkSetFlag(name, val) {
	if(!window.KTK_FLAGS) {
		KTK_FLAGS = new Object();
	}
	// alert(name + " = " + val); // OBS: Se dejan de procesar otros alerts
	KTK_FLAGS[name] = val;
}

function ktkGetFlag(name) {
	if(!window.KTK_FLAGS) {
		KTK_FLAGS = new Object();
	}
	return KTK_FLAGS[name];
}

function ktkInitDOM(obj) {
	if(window.Document_ReplaceEnterForTab) {
		Document_ReplaceEnterForTab(obj);
	}
}

function ktkEncodeFormField(field) {
	return field.replace(/[^a-zA-Z0-9]/g, '_');
}

// N.toFixed Fix

function Stretch(Q, L, c) { var S = Q
	if (c.length>0) while (S.length<L) { S = c+S }
	return S
}
function StrU(X, M, N) { // X>=0.0
	var T, S=new String(Math.round(X*Number("1e"+N)))
	if (S.search && S.search(/\D/)!=-1) { return ''+X }
	with (new String(Stretch(S, M+N, '0')))
	return substring(0, T=(length-N)) + '.' + substring(T)
}
function Sign(X) { return X<0 ? '-' : ''; }
function StrS(X, M, N) { return Sign(X)+StrU(Math.abs(X), M, N) }
Number.prototype.toFixed= new Function('n','return StrS(this,1,n)');

function ktkSubmitCompatedForm(obj, doc) {
	var inputArrArr = [obj.getElementsByTagName("INPUT"), obj.getElementsByTagName("SELECT"), obj.getElementsByTagName("TEXTAREA"), obj.getElementsByTagName("HIDDEN")];
	var vars = {};
	for(var a in inputArrArr) {
		var inputArr = inputArrArr[a];
		for(var i in inputArr) {
			var io = inputArr[i];
			var val = ktkGetObjectValue(io);
			var name = io.name;
			if(name) {
				if(!val) val = null;
				vars[name] = val;
			}
		}
	}
	var url = ktkChangeQuery(doc.location.href, vars);
	try {
		doc.location.href = url;
	} catch(e) {
	};
	return false;
}

function ktkGetObjectValue(obj) {
	if(!obj) return null;
	if(obj.type == "checkbox") {
		return obj.checked;
	} else {
		return ktkPHPBool(obj.value); // TODO: Pa q?
	}
}

function ktkGetUniqueArray(arr) {
	arr.sort();
	var a = [], i;
	for(i=0; i < arr.length; i++) {
		if(arr[i] !== arr[i+1]) {
			a[a.length] = arr[i];
		}
	}
	return a;
}

function ktkProfilerStart(key, info) {
	if(!window.KTK_PROFILER_TIMES) KTK_PROFILER_TIMES = {};
	if(!KTK_PROFILER_TIMES[key]) {
		KTK_PROFILER_TIMES[key] = {count: 0, time: 0, info: info};
	}
	KTK_PROFILER_TIMES[key].count++;
	KTK_PROFILER_TIMES[key].time -= (new Date()).getTime();
}

function ktkProfilerStop(key) {
	KTK_PROFILER_TIMES[key].time += (new Date()).getTime();
}

function ktkProfilerShow() {
	if(!window.KTK_PROFILER_TIMES) {
		alert("No profile info");
		return;
	}
	var tmp = ktkHashToArray(KTK_PROFILER_TIMES);
	ktkDump(tmp.sort(function (a, b) {
		return a.time - b.time;
	}));
}

function ktkPrompt_Cancel(id) {
}

function ktkPrompt(title, caption, callBack, defaultVal) {
	ktkSetFlag('focus_changed', 1);
	if(!title) title = "";
	var win = new ktkWindow(title);
	var html = "<div style='margin:10px' align=left>" + caption;
	html += "<br><input id=ktkPrompt type=text size=40 value='" + (defaultVal ? defaultVal : "") + "' onKeyDown='if(event.keyCode == 13) {ktkObj(\"ktkOkButton\").onclick()}'>";
	html += "<br><div align=right><input id=ktkOkButton type=button value='Aceptar' onClick='var val = ktkObj(\"ktkPrompt\").value;ktkWindows[" + win.wndId + "].close();" + callBack + "(val)'>&nbsp<input type=button value='Cancelar' onClick='ktkWindows[" + win.wndId + "].close()'></div>";
	html + "</div>";
	win.showHTML(html);
	ktkFocus("ktkPrompt", 1);
	
	win.registerChildWindow(window);
}

function ktkSetWaitRPCButtonEnable(val) {
	return; // OPT: Disabled. TODO: Usar DIV de fondo
	var arr = document.getElementsByTagName("INPUT");
	for (var i = 0; i < arr.length; i++) {
		var obj = arr[i];
		if(obj.getAttribute("ktkWaitRPC")) {
			obj.disabled = !val;
		}
	}
}

// Obtiene el InputObject del ID de un botón selector.
function GetSelectorButtonInputObject(buttonId) {
	var i = buttonId.indexOf("[");
	if(i > -1) {
		var inputObjectId = buttonId.substr(0, i - "_selButton".length) + buttonId.substr(i);
		return ktkObj(inputObjectId);
	}
	return null;
}

// Se ejecuta al hacer click sobre un selector de listados
function RPC_Started(selectorFieldId, imgId) {
	var selectorObj = ktkObj(selectorFieldId);

	if(selectorObj.ktkSelected) {
		ktkDebug("Documentar / impedir");
		return false; // No se debe realizar RPC cuando el evento onChange se gatillo tras seleccionar.
	}

	ktkSetFlag("focus_changed", 0);
	KTK_SELECTOR_OBJECT = selectorObj;

	// ktkDebug("START: " + selectorObj.id);

	var tmp = GetSelectorButtonInputObject(selectorObj.id);
	if(tmp) KTK_FOCUSED_ELEMENT = tmp;

	ktkSetWaitRPCButtonEnable(false);
	if(imgId) {
		var imgObj = ktkObj(imgId);
		imgObj.originalSrc = imgObj.src;
		imgObj.src = IMAGES_URL + "/loading.gif";
		imgObj.disabled = true;
	}

	KTK_SELECTOR_OBJECT.ktkSelected = 1;
	return true;
}

function RPC_Ended(selectorFieldId, imgId) {
	var selectorObj = ktkObj(selectorFieldId);
	ktkSetWaitRPCButtonEnable(true);
	var imgObj = ktkObj(imgId);
	if(imgObj) {
		// ktkDebug("END: " + selectorObj.id);
		imgObj.src = imgObj.originalSrc;
		imgObj.disabled = false;
		selectorObj.ktkSelected = 0;
	}
}

function ktkTruncateText(p) {
	// TODO: Genera problema cuando se trunca interrumpiendo tag HTML ("texto <a href='...")
	return;

	var len = 100;
	if(window.KTK_LISTVIEW_TEXTAREA_MAX_CHARS) len = KTK_LISTVIEW_TEXTAREA_MAX_CHARS;
	if(len != 0) {
		var truncLen = len;
	} else {
		// Forzar una linea
		var truncLen = 35;
		// TODO: Hacer trunk dinámico
	}
	var trunc = p.innerHTML;
	if (trunc.length > truncLen) {
		trunc = trunc.substring(0, truncLen);
		trunc = trunc.replace(/\w+$/, '');
		trunc = ktkRightTrim(trunc);

		trunc += '<a href="#" style="" ' +
		  'onclick="this.parentNode.innerHTML=' +
		  'unescape(\'' + escape(p.innerHTML) + '\');return false;">' +
		  '...<\/a>';
		  
		// alert(trunc);

		p.innerHTML = trunc;
	}
}

function ktkTruncateTextAll() {
	var objs = document.getElementsByTagName("SPAN");
	for(var i = 0; i < objs.length; i++) {
		var obj = objs[i];
		if(obj.getAttribute("ktkTruncate")) {
			ktkTruncateText(obj);
		}
	}
}

function ktkReplace(str, from, to){
	return str.split(from).join(to);
}

function ktkPrint(obj, noPropmt) {
	if (noPropmt && navigator.appName == "Microsoft Internet Explorer") { 
		try {
			// Ver http://msdn2.microsoft.com/en-us/library/aa741315.aspx
			var PrintCommand = '<object ID="PrintCommandObject" WIDTH=0 HEIGHT=0 CLASSID="CLSID:8856F961-340A-11D0-A96B-00C04FD705A2"></object>';
			document.body.insertAdjacentHTML('beforeEnd', PrintCommand); 
			PrintCommandObject.ExecWB(6, 2); // Print
			// PrintCommandObject.ExecWB(7, 2); // Preview
			PrintCommandObject.outerHTML = ""; 
		} catch (e) {
			alert(e);
			obj.print();
		}
	}  else { 
		obj.print();
	} 
}

function ktkArrayInsert( arr, i, v ) {
	if(i == 0) {
		arr.unshift(v);
	} else {
		arr.splice(i, 0, v) 
		ktkDebug("Not tested");
	}
}

// TODO: Use http://developer.berlios.de/projects/jsgettext/
function _(str) {
	if(KTK_LOCALE == "en_US") {
		if(str == 'Sin Registros') return 'No Records';
	}
	return str;
}


function ktkUpdateComboOption(obj, value, caption, insertAt) {
	var found = false;

	for(var i = 0; i < obj.options.length; i++) {
		var opt = obj.options[i];
		if(opt.value == value) {
			opt.innerHTML = caption;
			found = true;
			break;
		}
	}

	if(!found) {
		obj.add(new Option(caption, value), insertAt);
	}
}

// DWB

function dwbLogin(user) {
	ktkRedirect(DWB_URL + '/admin/functions.php?login=' + user);
}

function ktkSetCursor(element, url) {
	var val = "url('" + url + "'),default";
	ktkCompatSetStyle(element, "cursor", val);
}
