 function lw_zombigClass(){

	this.prefix = "lw_zombig";
	var ajaxCalls = 0;
	
	this.incOutstandingCalls = function() {
		ajaxCalls++;
		var waitIcon = $(this.prefix+"AjaxLoadIcon");
		if(ajaxCalls > 0 && waitIcon.getStyle("display") != "block")
		{
			waitIcon.setStyles({"visibility":"hidden","display":"block"});
			waitIcon.setStyles({"margin-left":waitIcon.width / -2,"margin-top":waitIcon.height / -2});

			// waitIcon.setStyles({"position":"fixed","visibility":"visible","top":"50%","left":"50%"}); // no position fixed for IE6
			waitIcon.setStyles({"visibility":"visible","top":window.getHeight()/2 + window.getScrollTop(),"left":window.getWidth()/2 + window.getScrollLeft()});
			$(document.body).addClass("ajaxBusy");
		}
		
	}

	this.decOutstandingCalls = function() {
		ajaxCalls--;
		var waitIcon = $(this.prefix+"AjaxLoadIcon");
		if(ajaxCalls < 1 && waitIcon.getStyle("display") == "block")
		{
			waitIcon.setStyles({"display":"none"});
			$(document.body).removeClass("ajaxBusy");
		}
	}
	
	this.showNonDynamic = function(on) {$$("."+this.prefix+"NonDynamicLink").setStyle("visibility",on ? "visible" : "hidden");}
	
	this.slowDelete = function(obj) {
		obj = $(obj);
		var oldDims = obj.getCoordinates();
		obj.setStyle("overflow","hidden");
		new Fx.Styles(obj,{duration:1000,transition:Fx.Transitions.Cubic.easeOut}).start({"height":[oldDims.height,0]}).addEvent("onComplete",function(){obj.remove();}).start();
	}
	
	this.xCallResultHandler = function(result,callback)
	{
		try {
			
			if(!result)
				return;

			if(result["debug"])
			{
				if($(this.prefix+"Errors"))
					$(this.prefix+"Errors").setHTML(result["debug"]);
				else
					alert("error:"+result["error"]);
			}
			
			for(var i = 0;i<result["commands"].length;i++)
			{
				try {
					var comm = result["commands"][i];
					switch(comm["c"]) {
						case "as": // assign dom object property
							eval("$('"+comm["id"]+"')."+comm["prp"]+' = comm["val"]');
							//$(comm["id"])[comm["prp"]] = comm["val"];
							break;
						case "js": // execute code, wrapped in a function to protect the current scope
							var func = new Function(comm["src"]).bind(window);
							func();
							break;
						case "jc": // call function
							eval((comm["obj"] == "" ? "" : comm["obj"]+".")+comm["fun"]).apply(eval(comm["obj"]),comm["arg"]);
							break;
						default:
							alert("unknown command: "+comm["c"]);
							break;
					}
				} catch(e)
				{
					alert(e);
				}
			}
			if(callback)
				callback(result["payload"]);
				
		} finally {
			this.decOutstandingCalls();
		}
	}
	
	this.xCall = function(funcName,params,callback)
	{
		var loc = document.location;
		loc = loc.protocol+"//"+loc.host+"/";
		var syncResult = null;
		
		var jsonArgs = {
			async : callback !== false,
			onFailure : function() {
				this.decOutstandingCalls();
			}.bind(this),
			onComplete : function(result) {
				syncResult = result;
				this.xCallResultHandler(result,callback);
			}.bind(this)
		};
		this.incOutstandingCalls();
		var jSonRequest = new Json.Remote(loc,jsonArgs).send({"function":funcName,"arguments":params});
		if(callback === false && syncResult)
			return syncResult["payload"];
	}
	
	this.xIncrementalSearch = function(form)
	{
		form = $(form);
		var params = arguments[1];
		params = $merge(params,{"serializedParams":form.toQueryString()});
		
		if(params.submode && params.submode != form.elements.submode.value)
		{
			form.elements.submode.value = params.submode
			form.submit();
			return;
		}
		
		if (!$(this.prefix+"Searchresults"))
		{
			form.submit();
			return;
		}
			
			
		this.xCall("xIncrementalSearch",params,function(r){reslides.forEach(function(elm){elm.reset();})});
	}
	
	var formContainer;
	var currentFormParams;
	var reloadRequired = false;
	
	this.xDeleteItem = function(params, name, removeDom)
	{
		this.xCall("xGetLL",[["confirmDelete",name]],function(result) {
			if(confirm(result[0]))
				this.xCall("xDeleteItem",params,function(result){
					$$(removeDom).each(function(elm){elm.remove()});
			});
		}.bind(this));
		
	}
	
	this.onDomChangeCleanup = function() {
		$ES(".jsAutoHide").setStyle('display','none').removeClass('jsAutoHide');
		$ES(".jsOnly").setStyle('display','').removeClass('jsOnly');
		if(drop)
			drop.setStyle("display","none");
	}
	
	
	this.registerAutoFill = function(obj,id,type,target)
	{
		obj = $(obj);
		obj.onkeyup = null;
		var timer;
		obj.addEvent("keyup",function(e) {
			$clear(timer);
			timer = function() {
				this.xCall("xGetAutoFill",{"ztype":type,"input":obj.value,"zid":id},function(result) {
					if(result)
						$(target).value = result;
				});
			}.bind(this).delay(250)								  
		}.bind(this));
	}
	
	var drop;
	
	this.registerAutoSuggest = function(obj,query)
	{
		var subtype = arguments[3];
		obj = $(obj);
		var dim = obj.getCoordinates();
		obj.onfocus = null; // clear registration handler
		
		$(obj.form).setProperty("autocomplete","off"); // disable browser autocomplete

		if(!drop)
			drop = new Element("div",{"styles":{"display":"none","position":"absolute"},"class":"dropDown"}).injectInside($(document.body));
		drop.setStyles({"left":dim.left,"top":dim.top+dim.height});

		var timer;
		var prevValue = obj.value;
		var lastNotFoundPrefix = null;
		
		$(obj).addEvents({
			"focus": function(e) { drop.empty().setStyle('width',obj.getSize().size.x); }, // clear and show
			"blur": function(e) { drop.setStyle('display','none'); }, // hide
			"keyup": function(e) {
				e = new Event(e);
				if(e.key == 'up' || e.key == 'down') { // navigate through dropdown list
					var cur = drop.getChildren().filterByClass("autoSuggested").removeClass("autoSuggested")[0];
					var next;
					if(cur)
						next = e.key == 'up' ? cur.getPrevious() : cur.getNext();
					else
						next = e.key == 'up' ? drop.getLast() : drop.getFirst();
						
					if(next) {	// highlight new element and fetch suggested text
						next.addClass("autoSuggested");
						obj.value = next.lastChild.data; 
					}
					e.stop();
				} else if(e.key == "enter") {
					return;
				} else { // check if content changed, refresh dropdown list
					if(obj.value == prevValue)
						return true;
					prevValue = obj.value;
					$clear(timer);
					if(obj.value.length > 0 && (!lastNotFoundPrefix || obj.value.indexOf(lastNotFoundPrefix) != 0))
					{
						timer = function() {
							this.xCall("xGetAutoSuggest",{"query":query,"input":obj.value},
								function(result){
									if(!result)
									{
										lastNotFoundPrefix = obj.value;
										drop.empty().setStyle("display","none");
									} else {
										drop.empty().setHTML(result).setStyle("display","block").getChildren().addEvent("mouseenter",
										function() {
											drop.getChildren().removeClass("autoSuggested")
											$(this).addClass("autoSuggested");
											obj.value = this.lastChild.data;
										});
									}
								}
							);
						}.bind(this).delay(300);  // delay refresh to prevent hammering
					} else {
						drop.empty(); // 0-string, clear dropdown
						drop.setStyle("display","none");
					}
				}
			}.bind(this)
		}).fireEvent("focus",null);

	}
	
	Fx.ReSlide = Fx.Slide.extend({
		"visible":true,
		"reset":function() {if(this.visible) { this.hide();this.show()} else this.hide();return this;},
		"toggle":function() {this.visible = !this.visible;return this.parent();},
		"hide":function() {this.visible = false;return this.parent();},
		"slideIn":function(){this.visible = true;return this.parent();},
		"slideOut":function() {this.visible = false;return this.parent();},
		"show":function() {this.visible = true;return this.parent();}
	});
	
	var reslides = [];
	
	this.foldSearch = function(obj) {
		obj = $(obj);
		if(!obj.mooSlide)
		{
			obj.slided = obj.getNext();
			obj.mooSlide = new Fx.ReSlide(obj.getNext(),{duration:500,fps:40}).addEvent("onStart",function() {obj.toggleClass("heading_close");});
			reslides.push(obj.mooSlide);
		}
		if(obj.slided.getStyle("display") == "none")
		{
			obj.mooSlide.hide();
			obj.slided.setStyle("display","block");
			obj.mooSlide.slideIn();
		}
			
		obj.mooSlide.toggle();
	}
	
	this.xEditInPlace = function(query,container,showForm)
	{
		container = $(container);
		var form = arguments[3];
		var submitdata, fileUpload;
		
		if(form)
		{
			for(var i=0;i<form.elements.length && !fileUpload;i++)
			{
				var elm = $(form.elements[i]); // check for file upload, use iframe-submit if that's the case
				if(elm.getProperty("type") && elm.getProperty("type").toLowerCase() == "file" && elm.value != "")
					fileUpload = true;
			}
			
			submitdata = $(form).toQueryString();
		} else
			submitdata = null;

		var callback = function(result) {
			if(container.getTag() == "td")
			{
				container.setHTML(result)
			} else {
				// recreate node every time as inserting block elements into inline elements can cause rendering issues and is not quite valid
				container.setStyle("display","none").empty().clone().injectBefore(container).setHTML(result).setStyle("display","");
				container.remove();
			}
			this.onDomChangeCleanup();
		}.bind(this);

		if(fileUpload)
		{
			this.incOutstandingCalls();
			var iFrame = $(this.prefix+"AjaxFileUpload");
			iFrame.removeEvents("load");
			
			new Element("input",{"type":"hidden","name":"forceAjax","value":"1"}).injectInside(form);
			new Element("input",{"type":"hidden","name":"json","value":Json.toString({
				"function":"xEditInPlace",
				"arguments":{
					"query":query,
					"showForm":showForm,
					"submitdata":submitdata
				}
			})}).injectInside(form);
			
			form.target = iFrame.name;
			form.onsubmit = null;
			iFrame.src = "about:blank";

			form.submit();
			

			
			iFrame.addEvent("load",function() {
				// ok, submitted to iframe, now read the json result from the server-side prepared html
				var frameWind = iFrame.contentWindow;
				this.xCallResultHandler(frameWind.lw_zombig.json,callback);
			}.bind(this));
		} else {
			this.xCall("xEditInPlace",{"query":query,"showForm":showForm,"submitdata":submitdata},callback);
		}
	}
	
	// called from the serverside
	this.xUpdateMform = function(mquery,payload)
	{
		mquery = $merge({"form":"","ztype":"","table":"","zid":"","field":"","tag_type":""},mquery);
		var target = $("mform"+mquery["form"]+mquery["ztype"]+mquery["table"]+mquery["zid"]+mquery["field"]+mquery["tag_type"]);
		if(target)
		{
			var replacement = new Element("div").setHTML(payload);
			replacement.getChildren().injectBefore(target);
			target.remove();
		}
	}
	
	// called from the serverside
	
	this.xAppendHTML = function(id,html) {
		var top = arguments[2] ? true : false;
		var target = $(id);
		if(target.getTag() == "table") // hack around the IE .innerHTML restriction on tables
			target.adopt(new Element("div").setHTML("<table>"+html+"</table>").getChildren()[0].tBodies[0]);
		else if(!top)
			new Element("div").setHTML(html).getChildren().injectInside(target);
		else
			new Element("div").setHTML(html).getChildren().injectTop(target);
	}
	
	this.fillTime = function(obj,num) {
		var entries = $(obj).getParent().getParent().getParent().getElements(".splitTime");
		entries.shift();
		for(var i=0;i<entries.length-2;i++)
			entries[i].getElements("input")[num].value = obj.value;
	}
	
	window.addEvent('domready', function() {
		this.onDomChangeCleanup();
	}.bind(this));
}

var lw_zombig = new lw_zombigClass();

// return false if server delivers malformed content
var oldEval = Json.evaluate;
Json.evaluate = function(arg) {try{return oldEval(arg)}catch(e){return false;}}
