////////////////////////////////////////////////////////////////
// File: egw_essential.js
//
// Loads everything needed to run a module. Acts as a wrapper
// for widgets as well as a basis for frameworks.
//////////////////////////////////////////////////////////////

// ----------------------------------------
if (typeof baseurl == 'undefined') {
	var baseurl = 'http://localhost/';
}
if (baseurl.substr(baseurl.length - 1, 1) != "/") baseurl += "/";
baseurl = baseurl.replace(/\/{2,}/g, '/').replace(/:\//, '://');
// ----------------------------------------

/*** DEFINE VARIABLES ***/

/*
 * Group: Basic Variables
 */

/*
 * Variable: EGW
 * Object containing pretty much the entire system including
 * framework and modules. This helps keeping order and reducing
 * the footprint.
 */
var EGW = new Object();

/*
 * Variable: EGW.system
 * Contains some system specific methods and settings.
 */
EGW.system = new Object();
EGW.system.initialized = false;

/*
 * Variable: EGW.modules
 * Contains everything directly related to modules.
 */
EGW.modules = new Object();

/*
 * Object: EGW.modules.common
 * Contains everything that is shared by all modules.
 */
EGW.modules.common = new Object();

/*
 * Integer: EGW.modules.common.maxModID
 * Stores the last ID used when a new modules was created. For each
 * new module, the value is increased by one.
 */
EGW.modules.common.maxModID = 0;

/*
 * Array: EGW.modules.loadedModules
 * Array which stores loaded modules. Offset is defined by <EGW.modules.common.maxModID>.
 * Modules are loaded from <EGW.modules.store>.
 */
EGW.modules.loadedModules = [];
	
/*
 * Variable: EGW.modules.store
 * Contains the modules (as a JS-pseudo-class) when loaded.
 */
EGW.modules.store = new Object();

/*
 * Variable: EGW.ui
 * Namespace for User Interface
 */
EGW.ui = new Object();

/*
 * Variable: EGW.UnitTest
 * Contains all unit tests created.
 */
EGW.UnitTest = new Object();
EGW.UnitTest.init = new Object();

/*
 * Variable: EGW.misc
 * Space for miscellaneous.
 */
EGW.misc = new Object();

/*** LOAD JQUERY ***/

// Load jQuery the old way since loadJS is defined later and rearranging causes a mess in other respects.

if (typeof jQuery == 'undefined') {
	var fileref=document.createElement('script');
	fileref.setAttribute("type","text/javascript");
	fileref.setAttribute("src", baseurl + 'resources/jquery/jquery.js');
	document.getElementsByTagName("head")[0].appendChild(fileref);
}


/*
 * Group: Array functions
 */

/*
 * Function: in_array
 *   Function that checks if needle is in array
 * 
 * Parameters:
 *    array - The array to be searched in
 * 		needle -	Can be any simple value or an array, in which case
 * 					all values have to match.
 * 
 * Returns:
 * 		True or false, depending on the result.
 */

var in_array = function(array, needle) {
	//Array.prototype.in_array = function(needle) {
	if (typeof needle == 'array') {
		var counter = 0;
		for(var n=0; n < needle.length; n++) {
			for(var i=0; i < array.length; i++) if(array[i] === needle[n]) {
				counter++;
				break;
			}
		}
		if (array.length == counter) {
			return true;
		} else {
			return false;
		}
	} else {
		for(var i=0; i < array.length; i++) if(array[i] === needle) return true;
		return false;
	}
}


/*
 * Function: remove
 * Removes the element from array.
 *
 * Parameters:
 *   array - array the element is to be deleted from
 * 		e -	object of array element which must be deleted
 * 
 * Returns:
 * 		void
 */

var remove = function(array, e) {
	var i = array.indexOf( e );
	if(i != -1) array.splice(i, 1);
};


/*** PROTOTYPES ***/

/*
 * Group: Prototypes
 */

/*
 * Function: Function.prototype.scope
 * Assigns scope to the function (this)
 *
 * Parameters:
 * 		scope -	object, which scope should be passed
 *
 * Returns:
 * 		Function with a new scope
 */
Function.prototype.scope = function(scope) {
	var _function = this;
	return function() {
		return _function.apply(scope, arguments);
	}
}

/*
 * Function: String.prototype.paramReplace
 * Replaces all occurances of "~param~" in the string by the values given for the parameters in the params.
 * String sample: <div style="width:~param2~;">~param1~</div>
 *
 * Parameters:
 * 		params - array of parameters to be replaced (Ex: {param1: 'value', param2: '30px'})
 *
 * Returns:
 * 		String with replacements.
 */
String.prototype.paramReplace = function(params) {
	var text = this.toString();
	$.each(params, function(param, value){
		if (typeof param == "string"){
			text = text.replace("~"+param+"~", value);
		}
	});
	return text;
}


/*** SETTINGS ***/

/*
 * Group: Default Settings (EGW.settings)
 */

EGW.settings = {
	/*
	 * Object: EGW.settings.delayAction
	 * Defines the default numbers of iterations and the default speed used for <EGW.res.delayAction>.
	 */
	'delayAction' : {
		'defaultIterations': 400,
		'defaultSpeed': 50
	},
	/*
	 * Variable: EGW.settings.sessionID
	 * Creates a unique 18 characters large id used for settings and history
	 */
	'sessionID' : ((new Date()).getTime() + "" + Math.floor(Math.random() * 1000000)).substr(0, 18),
	/*
	 * Variable: EGW.settings.frameworkname
	 * Might be antiquated (not sure). Contains the name of the framework. 
	 */
	'frameworkname' : 'essential',
	/*
	 * Object: EGW.settings.flags
	 * Contains variables to be queried by modules in order to determine if a certain functionality is in place.
	 */
	'flags' : {
		'externalSearch' : false
	},
	'custom' : {} // Placeholder for custom settings which are then saved
}

/*** SYSTEM ***/

/*
 * Group: System (EGW.system)
 */

/*
 * Function: EGW.system.key
 * Registers when special keys (shift, ctrl, alt) are pressed and sets the variables EGW.system.key.shift, EGW.systsem.key.alt, or EGW.system.key.ctrl to either true or false.
 */
EGW.system.key = {
	shift: false,
	ctrl: false,
	alt: false,
	init: function(){
		$(window).keyup(this.up.scope(this)).keydown(this.down.scope(this)); // Didn't work and didn't understand it...
	},
	down : function(e) {
		if (!e) e = window.event;
		if (e.keyCode) code = e.keyCode;
		else if (e.which) code = e.which;
		if (code == 16) this.shift = true;
		if (code == 17 || code == 91) this.ctrl = true;
		if (code == 18) this.alt = true;
	},
	up : function(e) {
		if (!e) e = window.event;
		if (e.keyCode) code = e.keyCode;
		else if (e.which) code = e.which;
		if (code == 16) this.shift = false;
		if (code == 17 || code == 91) this.ctrl = false;
		if (code == 18) this.alt = false;
	}
}

/*** RESOURCES ***/

/*
 * Group: Resources (EGW.res)
 */

/*
 * Variable: EGW.res
 * Contains the resources commonly needed by the entire system.
 */
EGW.res = {

	/*** Used as function ***/

	/*
	 * Array: EGW.res.loadedJS
	 * Keeps track of JS files already loaded so they are not loaded twice.
	 */
	'loadedJS': [],

	/*
	 * Array: EGW.res.loadedCSS
	 * Keeps track of CSS files already loaded so they are not loaded twice.
	 */
	'loadedCSS': [],

	/*
	 * Array: EGW.res.loadedSkins
	 * Keeps track of Skins already loaded so they are not loaded twice.
	 */
	'loadedSkins': [],

	/*
	 * Function: EGW.res.loadJS
	 * Makes JavaScript code available.
	 *
	 * Parameters:
	 * 		path - Path to the javascript-file to be loaded.
	 * 		testvar - Variable (as string) which has to exist in order for the script to be considered available.
	 * 		callback - Function to be executed upon success (or if JS already loaded). Optional.
	 * 		timeout - optional. Function to be executed upon timeout.
	 *
	 * Returns:
	 * 		Nothing
	 */
	'loadJS': function(path, testvar, callback, timeout) {

		if (typeof timeout == "undefined") timeout = function() { };
		if (typeof callback == "undefined") callback = function() { };
		if (typeof testvar == "undefined") testvar = "EGW";
		if (typeof testvar == "function") {
			callback = testvar;
			testvar = "EGW";
		}
		
		if (in_array(EGW.res.loadedJS, path) == false) {
			// Slower, but easier for debugging - good for now.
			var fileref = document.createElement('script');
			fileref.setAttribute("type", "text/javascript");
			fileref.setAttribute("src", path);
			document.getElementsByTagName("head")[0].appendChild(fileref);
			EGW.res.loadedJS.push(path);
		}
		
		// Faster, but harder for debugging
		/*
        $.ajax({
        type: "GET",
        url: path,
        dataType: 'script',
        success: function() {
        if (typeof callback == 'function') {
        EGW.res.delayAction({
        'test': function() {
        return eval ('((typeof ' + testvar + ' != \'undefined\') ? true : false );');
        },
        'callback': callback,
        'timeout' : timeout
        });
					
				}
        }
        });
		 */
		
		
		if (typeof testvar == 'string' && typeof callback == 'function') {
                        
			EGW.res.delayAction({
				'test': function() {
					try {
						return eval('((typeof ' + testvar + ' != \'undefined\') ? true : false );');
					} catch(e) {
						return false;
					}
				},
				'callback': function() {
					callback();
				},
				'timeout': function() {
					dump('loadJS timed out: ' + testvar + ' (' + (typeof testvar) + ')');
				}
			});
		}
		
	},

	/*
	 * Function: EGW.res.loadResource
	 * Makes a resource available.
	 *
	 * Parameters:
	 * 	  res - The resource or an array of resources to be loaded including the path (namespace)
	 *					Ex: "bars.iconbar" loads the JS-file "resource/bars/iconbar/iconbar.js" and the CSS "resource/bars/iconbar/iconbar.css"
	 *		testvar - optional. needed if test differs from "jQuery.fn."+res.
	 *					If res is an array: either do not set, or array length has to match. undefined for array elements is allowed. Then "jQuery.fn."+res[i] is used.
	 *		callback - function to be executed when js is loaded
	 *		timeout - optional. function to be executed on timeout
	 *		css - optional. Specify if name of css differs from default value
	 *
	 * Usage:
	 * > EGW.res.loadResource("dynatree");
	 * > EGW.res.loadResource("dynatree", function(){...});
	 * > EGW.res.loadResource("dynatree", function(){...}, function(){...});
	 * > EGW.res.loadResource("dynatree", "jQuery.fn.dynatree", function(){...});
	 * > EGW.res.loadResource(["bars.elemcontainer", "bars.iconbar", "jpassword"], ["$.fn.elemContainer", "$.fn.iconBar", undefined], function(){...});
	 *
	 * Returns:
	 * 		Nothing
	 */
	'loadResource': function(res, testvar, callback, timeout, css) {
		if (typeof testvar == "function") {
			timeout = callback;
			callback = testvar;
			testvar = 'jQuery.fn.' + res;
		}
		if (typeof testvar == "undefined") testvar = (typeof res == "object") ? new Array(res.length) : 'jQuery.fn.' + res;

		var loadres = function(res, testvar, callback, timeout, tcss) {
			res = res.toLowerCase();
			tcss = tcss.toLowerCase();
			var resParts = res.split(".");
			var resName = resParts.splice(resParts.length - 1, 1);
			var resPath = res.replace('.', '/');
			EGW.res.loadCSS(baseurl + 'resources/' + resPath + '/' + resName + '.css'); //+ tcss + '.css'); don't know why you need this extra variable, but it doesn't work with resources like "bars.elemcontainer"
			EGW.res.loadJS(baseurl + 'resources/' + resPath + '/' + resName + '.js', testvar, callback, timeout);
		}

		if (typeof res == "object") {
			if (res.length != testvar.length) dump("loadResource: res.length = testvar.length not met!")
			var teststr = "true";
			for (var i=0;i<testvar.length;i++) {
				if (typeof testvar[i] != "string") testvar[i] = 'jQuery.fn.' + res[i];
				teststr=teststr.replace("true", '((typeof ' + testvar[i] + ' != "undefined") ? true : false )');
			}
			teststr+=";";
			
			var fn_test = function () {
				try {
					return eval(teststr);
				} catch(e) {
					return false;
				}
			};
			var runalready = false;
			var cb = function() {
				if (fn_test() && typeof callback == "function" && !runalready) {
					runalready = true;
					callback();
				}
			};
			for (i=0;i<res.length;i++) {
				if (typeof css != 'undefined' && typeof css[i] != 'undefined') {
					var tcss=css[i];
				} else {
					var tcss=res[i];
				}
				loadres(res[i], testvar[i], cb, timeout, tcss);
			}
		} else {
			loadres(res, testvar, callback, timeout, typeof css!='undefined'?css:res);
		}
	},

	/*
	 * Function: EGW.res.loadCSS
	 * Makes CSS available.
	 *
	 * Parameters:
	 * 		path - Path to the CSS-file to be loaded.
	 *
	 * Returns:
	 * 		Nothing
	 */
	'loadCSS': function(path, area) {
		if (!in_array(EGW.res.loadedCSS,path)) {
			if (typeof area == "undefined") {
				var oLink = document.createElement("link")
				oLink.href = path;
				oLink.rel = "stylesheet";
				oLink.type = "text/css";
				document.getElementsByTagName("head")[0].appendChild(oLink);
			} else {
				if (typeof document.getElementsByTagName("css_"+area) == "undefined") {
					var oLink = document.createElement("link")
					oLink.href = path;
					oLink.rel = "stylesheet";
					oLink.type = "text/css";
					oLink.id = "css_"+area;
					document.getElementsByTagName("head")[0].appendChild(oLink);
				} else {
					var oLink = document.getElementsByTagName(area);
					for (var i=0;i<EGW.res.loadedCSS.length;i++) {
						if (EGW.res.loadedCSS[i] == oLink.href) EGW.res.loadCSS.splice(i, 1);
					}
					oLink.href = path;
				}
			}

			EGW.res.loadedCSS.push(path);
		}
	},

	/*
	 * Function: EGW.res.loadDesign
	 * 
	 * Loads a design data from the DB into EGW.res.skins and triggers EGW.system.design.onChange.
	 * <EGW.system.design.onChange> is an event where all the functions which have to be executed upon a design change have to be added.
	 * This means that all modules have to register their function with EGW.system.design.onChange upon initialization.
	 * In most cases, triggering <EGW.res.loadSkin> (with the needed parameter) will suffice. Please read there for more information. 
	 * 
	 * Parameters:
	 * 	id_design - ID of the design.
	 * 
	 * See also:
	 * 	<EGW.res.event>
	 */
	'loadDesign': function(id_design, callback) {
		EGW.system.design.id = id_design;
		$.get(baseurl+"framework/essential/egw.php", {
			task: 'loadSkins',
			id_design: id_design
		}, function(data) {
			EGW.res.skins = data;
			EGW.system.design.onChange.fire();
			if (typeof callback != 'undefined') {
				callback();
			}
		}, "json");
	},

	/*
	 * Function: EGW.res.loadSkin
	 * 
	 * Loads a skin
	 * 
	 * Parameters:
	 * 		area - Name of area to be loaded, separated by dots
	 * 		html - Object containing infos about the files to be loaded
	 * 		html.[filename] - Object. Whereas 'filename' is the name of the file to be loaded, relative to the area, it is also the key for its information.
	 * 		html.[filename].loadInto - DOM-Element or string. Defines the wrapper for the new html.
	 * 		html.[filename].keepContent - Array of Objects. Specifies elements further down in the DOM and underneath the newly loaded html which are to be kept.
	 * 		html.[filename].keepContent['container'] - DOM-Element or String. Element of new HTML that will contain something that's kept.
	 * 		html.[filename].keepContent['keepParent'] - Parent-Element of HTML which is to be kept.
	 * 		html.[filename].variables - Object of key/value-pairs. Variables to be sent to PHP or Smarty-Files to be included.
	 * 		callback - Function. Things to be done after or before HTML is loaded. Receives the key/value-pairs from the DB as parameter.
	 * 		callbackFirst - Boolean. If set to true, the callback will be performed before the HTML is loaded.
	 * 
	 * Example:
	 * 
	 * > {
	 * >	'area': 'multi.window',
	 * >	'html': {
	 * >		'a.php': {
	 * >			'loadInto': 'jquery, dom or string',
	 * >			'keepContent': [
	 * >				{
	 * >					'container': 'string',
	 * >					'keepParent': 'jquery, dom or string' 
	 * >				},
	 * >				{
	 * >					'container': 'string',
	 * >					'keepParent': 'jquery, dom or string' 
	 * >				}
	 * >			],
	 * >			'variables': {
	 * >				'a': 'something',
	 * >				'b': 'something else'
	 * >			}
	 * >		}
	 * >	},
	 * >	'callback': function(r) {
	 * >		dump(r);
	 * >	},
	 * >	'callbackFirst': true
	 * > }
	 */
	'loadSkin': function(p, force) {

		var obj = this;
	 
		if (typeof p.area != 'undefined') {
			EGW.res.delayAction({
				test: function() {
					return (typeof EGW.res.skins != 'undefined' ? true : false);
				},
				callback: function() {
					
					
					var parts = p.area.split(".");
					var name = parts.slice(parts.length - 1, 1);
					var root = (parts[0] == "multi" || parts[0] == "single") ? "framework/"+parts[0]+"/skins" : "modules";
					var path = (root == "modules") ? parts.join("/") + "/skins" : parts.slice(1).join("/");
					var skinfolder = EGW.res.skins[p.area].skinnr; // eval('EGW.res.skins.' + p.area + '.skinnr;');
					var skinpath = root + "/" + path + "/" + skinfolder + "/";
					
					/*** CSS ***/
					if (!in_array(EGW.res.loadedSkins,p.area) || force) {
						var csspath = baseurl + skinpath + name + ".css";
						obj.loadCSS(csspath);
						EGW.res.loadedSkins.push(p.area);
					}
				
					var getElement = function(p) {
					
						//ID
						if (typeof p == 'string') {
							return $('#' + p);
						}
						//DOM
						else if (typeof ($(p).jquery == 'undefined')) {
							return $(p);
						}
						//JQUERY
						else {
							return p;
						}
					}
				
					/*** HTML ***/
					var html = function() {
						//debugger;
						// For each file...
						for (var file in p.html) {
							//if (typeof EGW.res.skins[p.area][file] != 'undefined') {
							
							// Prepare variables
							var sendData = {
								'task': 'skinsGetFile',
								'file': skinpath + file
							}
							for(var m in p.html[file].variables) {
								sendData['var_' + m] = p.html[file].variables[m];
							}
							
							// Get html
							$.ajax({
								url: baseurl+"framework/essential/egw.php",
								async: false,
								data: sendData,
								success: function(data) {
								
									// Replace content
									var newElement = $(data);
									if (typeof p.html[file].keepContent != 'undefined') {
										for(var o=0; o<p.html[file].keepContent.length; o++) {
											var keepParent = getElement(p.html[file].keepContent[o].keepParent);
											newElement.find('#' + p.html[file].keepContent[o].container).append(keepParent);
										}
									}
									var loadInto = getElement(p.html[file].loadInto);
									loadInto.empty();
									loadInto.append(newElement);
									
								}
							});

						//}
						}
					}
				
					if (typeof p.callbackFirst != 'undefined' && p.callbackFirst == true) {
						if (typeof p.callback == 'function') {
							p.callback(EGW.res.skins[p.name]);
						}
						html();
					} else {
						html();
						if (typeof p.callback == 'function') {
							p.callback(EGW.res.skins[p.name]);
						}
					}
					
				}
			});
			

		}
		
	},

	/*
	 * Function: EGW.res.getAjaxAsync
	 * Loads AJAX in asynchronous and GET mode.
	 *
	 * Parameters:
	 * 		path - Path to file including parameters
	 *
	 * Returns:
	 * 		Returned data.
	 */
	'getAjaxSync': function(path) {
		return $.ajax({
			type: "GET",
			url: path,
			async: false
		}).responseText;
	},

	/*
	 * Function: EGW.res.delayAction
	 * Waits for a certain condition to be fulfilled before it calls another function.
	 *
	 * Parameters:
	 *		p.callback -  Function to be executed when test function returns true.
	 * 		p.test - 			Function which has to return true for the callback to be executed.
	 * 		p.iterations -		Optional. Max iterations (attempts) before it fails.
	 * 		p.speed -			Optional. Interval (ms) between attempts (tests).
	 * 		p.mindelay - Optional. min-delay in ms to wait till callback should be called.
	 * 		p.timeout - 		Optional. Function to be executed if test runs into timeout
	 *
	 */
	'delayAction': function(p) {
		if (typeof p.speed == 'undefined') p.speed = EGW.settings.delayAction.defaultSpeed;
		if (typeof p.iterations == 'undefined') p.iterations = EGW.settings.delayAction.defaultIterations;
		if (typeof p.timeout == 'undefined') p.timeout = function() { };
		if (typeof p.mindelay == 'undefined') p.mindelay = 0;

		var counter = -1;
		var testThis = function() {
			counter++;
			if (counter <= p.iterations) {
				if (p.test() == true && p.mindelay <= (counter * p.speed)) {
					p.callback();
				}
				else {
					setTimeout(function() {
						testThis();
					}, p.speed);
				}
			} else {
				p.timeout();
			}
		}
		testThis();
	},

	/*
	 * Function: EGW.res.deepObjCopy
	 * Deep copies an object (instead of just referencing it).
	 *
	 * Parameters:
	 * 		dupeObj - Object to be copied
	 *
	 * Returns:
	 * 		Copied object
	 */
	'deepObjCopy': function(dupeObj) {
		var retObj = new Object();
		if (typeof (dupeObj) == 'object') {
			if (typeof (dupeObj.length) != 'undefined')
				var retObj = new Array();
			for (var objInd in dupeObj) {
				if (typeof (dupeObj[objInd]) == 'object') {
					retObj[objInd] = EGW.res.deepObjCopy(dupeObj[objInd]);
				} else if (typeof (dupeObj[objInd]) == 'string') {
					retObj[objInd] = dupeObj[objInd];
				} else if (typeof (dupeObj[objInd]) == 'number') {
					retObj[objInd] = dupeObj[objInd];
				} else if (typeof (dupeObj[objInd]) == 'boolean') {
					((dupeObj[objInd] == true) ? retObj[objInd] = true : retObj[objInd] = false);
				} else if (typeof (dupeObj[objInd]) == 'function') {
					retObj[objInd] = dupeObj[objInd];
				}
			}
		}
		return retObj;
	},

	/*
	 * Function: EGW.res.getSettings
	 * 
	 * Load settings for a certain area to EGW.settings
	 * 
	 * Parameters:
	 * 	area - area to be loaded.
	 *  callback - function to be executed after loading the area.
	 */
	'getSettings': function(area, callback) {
		$.ajax({
			url: baseurl + 'framework/essential/egw.php',
			data:{
				mode:"ajax",
				task:"getSettings",
				area: area
			},
			dataType: "json",
			success:function(data) {
				EGW.settings[area] = data;
				if (typeof callback == "function") callback();
			}
		});
	},
	/*
	 * Function: EGW.res.getPHP
	 * !!OBSOLETE!! -- use normal jQuery functions like $.ajax, $.get, $.post, $.getjson, etc.
	 * loads a php-file and returns its content in the callback function.
	 * Works cross-domain.
	 *
	 * Parameters:
	 * 		path - relative path to the PHP file (domain name NOT included!)
	 * 		parameters - optional. an object of data to be sent to the php as GET variables. Ex: {name: "John", lastname: "Walter"}
	 * 		jsondecode - optional. set this to true for automatic JSON-decoding of the returned content.
	 * 		callback - callback function getting the content of the php-file
	 *
	 * Returns:
	 * 		nothing.
	 * 		php-content in given callback function.
	 */
	'getPHP': function() {

		if (arguments.length == 1) {
			var path = arguments[0];
		}
		else if (arguments.length == 2) {
			var path = arguments[0];
			var callback = arguments[1];
		} else if (arguments.length == 3) {
			var path = arguments[0];
			if (typeof arguments[1] == "boolean") {
				var jsondecode = arguments[1];
			} else {
				var params = arguments[1];
			}
			var callback = arguments[2];
		}
		else if (arguments.length == 4) {
			var path = arguments[0];
			var params = arguments[1];
			var jsondecode = arguments[2];
			var callback = arguments[3];
		}

		if (typeof params == 'string') {
			var args = '&' + params;
		}
		else {
			var args = (typeof params == "undefined") ? "" : "&" + $.param(params);
		}
                                                     
		var parts = path.split("?");
		if (parts.length > 1) {
			args += "&" + parts[1];
			path = parts[0];
		}

		if (!args.match(/method2754/)) {
			args += '&method2754=get';
		}

		var dataKey = ((new Date()).getTime() + "" + Math.floor(Math.random() * 1000000)).substr(0, 18);
		var sendData = function(tpath, targs) {
			$.ajax({
				type: "GET",
				url: baseurl + "proxy.php?url2754=" + tpath + '&datakey=' + dataKey + targs,
				dataType: "script",
				success: function() {
					EGW.res.delayAction({
						iterations: 30,
						speed: 5,
						test: function() {
							return typeof window['data' + dataKey] != "undefined";
						},
						callback: function() {
							if (window['data' + dataKey] != '0false0' && typeof callback == 'function') {
								if (typeof jsondecode != 'undefined' && jsondecode == true) {
									window['data' + dataKey] = $.evalJSON(window['data' + dataKey]);
								}
								callback(window['data' + dataKey]);
							}
						},
						timeout: function() {
							throw "getPHP: returned answer has a problem."
						}
					});
				}
			});
		}

		if (args.length <= 1000) {
			sendData(path, args);
		}
		else {
			var allArgs = args.split('&');
			var sendString = '';
			var sendStrings = []; // Could be sent directly, but it's kinda hard to know beforehand how many transmissions would be necessary.
			for (var n = 0; n < allArgs.length; n++) {
				if (allArgs[n].length > 0) {
					sendString += '&';
					if (allArgs[n].length <= 1000 - sendString.length) {
						sendString += allArgs[n];
					}
					else {
						var thisArg = allArgs[n].split('=');
						var lengthNext = 1000 - sendString.length - thisArg[0].length - 1;
						sendString += thisArg[0] + '=' + thisArg[1].substr(0, lengthNext);
						allArgs[n] = thisArg[0] + '=' + thisArg[1].substr(lengthNext);
						n--;
					}

					if (sendString.length == 1000) {
						sendStrings.push(sendString);
						sendString = '';
					}
				}
			}
			sendStrings.push(sendString);
			var transmissionParts = sendStrings.length;
			var transmissionID = ((new Date()).getTime() + "" + Math.floor(Math.random() * 1000000)).substr(0, 18);
			for (n = 0; n < transmissionParts; n++) {
				var transmission = n + 1;
				sendData(path, sendStrings[n] + '&transno2754=' + transmission + '&transtot2754=' + transmissionParts + '&transid2754=' + transmissionID);
			}
		}
	},
	
	/*
	 * Function: EGW.res.countElements
	 * Counts the number of elements in an object (could be extended, but this should not be needed).
	 * 
	 * Parameters:
	 * 		obj - Object to be counted.
	 * 		type - Optional. Restrict count to a certain type.
	 * 
	 * Returns:
	 * 		Number of elements or false if obj is no object.
	 */
	'countElements' : function(obj, type) {
		var counter = 0;
		if (typeof obj == 'object') {
			for(var n in obj) {
				if (typeof type != 'undefined') {
					if (typeof obj[n] == type) {
						counter++;
					}
				}
				else {
					counter++;
				}
			}
			return counter;
		}
		return false;
	},

	/*
	 * Function: EGW.res.getElementsByAttribute
	 * Finds DOM-element based on name and value of its attribute.
	 * 
	 * Parameters:
	 * 		attr - Required. Name of the attribute.
	 * 		value - Required. Value of the attribute.
	 * 		id - Optional. Restrict search to an element with a certain id.
	 * 
	 * Returns:
	 * 		Element.
	 */
	'getElementsByAttibute' : function (attr, value, id) {
		
		if (typeof (id) != 'undefined') {
			var baseElement = typeof id == 'string' ? document.getElementById(id) : id;
		} else {
			var baseElement = document.getElementsByTagName('body');
		}
		
		var foundElement = false;
		var elements = [];
		
		var findElement = function(e) {

			if (foundElement == false) {
				var children = $(e).children();
				for (var n = 0; n < children.length; n++) {
					if (foundElement == false) {
						if (typeof(children[n].getAttribute) != 'undefined' && typeof(children[n].getAttribute(attr)) != 'undefined' && children[n].getAttribute(attr) == value) {
							elements.push(children[n]);
						}
						else {
							var subchildren = $(children[n]).children();
							if (subchildren.length > 0) {
								findElement(children[n]);
							}
						}
					}
				}
			}
		}

		findElement(baseElement);			
		
		if (elements.length <= 1) {
			return elements[0];
		} else {
			return elements;
		}
		
		
	},

	/*
	 * Function: EGW.res.updateModules
	 * 
	 * Updates other modules.
	 * 
	 * Parameters:
	 * 		p.state - Object. Contains the state that is sent to the other modules.
	 * 		p.category - Object. Contains the categories sent to the other modules.
	 * 		p.command - Object. Contains the command sent to the other modules.
	 * 		p.modulenames - Array of strings. Contains shortNames of the modules to be updated if they differ from default settings.
	 * 		p.modules - Array of objects. Contains the references to the modules to be updated. Used to update specific instances.	
	 * 		p.obj - Object. Sender module. Mandatory.
	 */
	'updateModules' : function(p) {

		var sendData = function(modref) {
			if (typeof p.state != 'undefined') {
				modref.update({
					'state': p.state,
					'customSessionCounter': lockedSessionCounter
				});
			}
			else if (typeof p.category != 'undefined') {
				modref.update({
					'category': p.category,
					'customSessionCounter': lockedSessionCounter
				});
			}
			else if (typeof p.command != 'undefined') {
				modref.update({
					'command': p.command,
					'customSessionCounter': lockedSessionCounter
				});
			}
		}
		
		
		var lockedSessionCounter = typeof EGW.res.settings.lastSessionCounter != 'undefined' ? EGW.res.settings.lastSessionCounter : (new Date()).getTime();
		
		
		if (typeof p.modules != 'undefined') {
			
			var modulesLength = p.modules.length;
			for(var i = 0; i < modulesLength; i++) {
				if (typeof p.modules[i] != 'undefined') {
					if (typeof p.modules[i] == 'string' || typeof p.modules[i] == 'number') {
						sendData(EGW.modules.loadedModules[p.modules[i]]);
					} else {
						sendData(p.modules[i]);
					}
				}
			}

		}
		else {
			var updateMods = [];
			if (typeof p.modulenames != 'undefined') {
				updateMods = p.modulenames;
			} else {
				updateMods = p.obj.targetModules;
			}
			
			var foundModule;
			for (var n in updateMods) {
				for (var o in EGW.modules.loadedModules) {
					if ((typeof EGW.modules.loadedModules[o] != 'undefined') && (EGW.modules.loadedModules[o].shortName == updateMods[n]) && (EGW.modules.loadedModules[o].updatedByOtherModules == true) && (p.obj.id != EGW.modules.loadedModules[o].id)) {
						sendData(EGW.modules.loadedModules[o]);
					}
				}
			}
		}
	},
	/*
	 * Function: EGW.res.findRelatedInfo
	 * 
	 * Sends a reqest to PHP which queries the DB for related information. The returned data can then be processed by the respective module, or can be sent to the module displaying related content.
	 * 
	 * Parameters:
	 * 		p.tableref - Array of Objects. Contains pairs of tablenames and tableids (see DB for more info).
	 * 		p.category - Array of Objects. Contains information about people, locations, etc. which are translated to tablerefs on the PHP side.
	 */
	'findRelatedInfo' : function(p, callback) {
		
		$.get(EGW.settings.general.baseurl + '/resources/infofinder/infofinder.php', {
			'data': p
		}, function(r) {
			callback(r);
		}, 'json');
		
	},
	

	/*
	 * Function: EGW.res.wrapResource
	 * Function to help wrap a resource into jQuery.
	 */
	'wrapResource': function(element, resourceName, config) {
		return EGW.misc.wrapCode("resource", element, resourceName, config);
	},

	/*
	 * Function: EGW.res.getUniqueID
	 *   Function to get a unique id number -- for whatever needed.
	 *   Maximum of 18 digits.
	 */
	'getUniqueID': function() {
		return (new Date()).getTime() + "" + Math.floor(Math.random() * 100000);
	},
	/*
	 * Function: EGW.res.getUniqueSessionID
	 *   Function to get a unique session id number. Approximately one week later the same ID could be generated again.
	 *   Maximum of 12 digits.
	 */
	'getUniqueSessionID': function() {
		return (new Date()).getTime().toString().substr(4, 20) + Math.floor(Math.random() * 1000);
	},
	
	/*
	 * Function: EGW.res.getUserSettings
	 * 	Gets settings from DB and writes them to EGW.user.settings.
	 * 
	 * Parameters:
	 * 	userid - Mandatory. ID of the user.
	 * 	area - Optional. Area specifier. Typically the name of the module or resource.
	 * 	callback - Optional. Function to be executed when settings are available. Settings are passed as parameter.
	 */
	'getUserSettings': function(userid, area, callback) {
		if (!(typeof area != 'undefined' && area != false && area.length > 0)) {
			area = '';
		}
		$.get(EGW.settings.general.baseurl + '/framework/essential/egw.php', {
			'task': 'getUserSettings',
			'userid': userid,
			'area': area
		}, function(r) {
			
			// First level is area, so iterate through it
			if (typeof EGW.user.settings == 'undefined') {
				EGW.user.settings = {};
			}
			for(var n in r) {
				if (typeof r[n] == 'object') {
					EGW.user.settings[n] = $.extend({}, r[n]);
				}
			}
			
			if (typeof callback != 'undefined') {
				callback(r);
			}
		}, 'json');
	},
	
	/*
	 *	Function: EGW.res.saveUserSettings
	 *	
	 *	Writes settings to EGW.user.settings.
	 *
	 *	Parameters:
	 *		userid - Mandatory. ID of the user.
	 *		area - Mandatory. Area of the settings. Generally the name of the module or resource.
	 *		settings - Mandatory. Object. Settings to be written.
	 *		callback - Optional. Function to be executed after settings were written.
	 */
	'saveUserSettings': function(userid, area, settings, callback) {
		
		$.get(EGW.settings.general.baseurl + '/framework/essential/egw.php', {
			'task':'saveUserSettings',
			'userid':userid,
			'area': area,
			'settings': $.toJSON(settings)
		}, function() {
			if (typeof callback != 'undefined') {
				callback();
			}
		});
		
	}
}


/*** LOAD OTHER NEEDED FILES ***/

// Load CSS
EGW.res.loadCSS(baseurl + 'framework/essential/egw_essential.css');
EGW.res.loadCSS(baseurl + 'resources/jquery/jquery-ui.custom.css');
EGW.res.loadCSS(baseurl + 'resources/jqGrid/jqgrid.css');
EGW.res.loadCSS(baseurl + 'resources/pdfviewer/pdfviewer.css');

// Load jQuery plugins once jQuery is loaded
EGW.res.delayAction({
	'iterations': EGW.settings.delayAction.defaultIterations,
	'speed': EGW.settings.delayAction.defaultSpeed,
	'test': function() {
		return ((typeof jQuery != 'undefined') ? true : false);
	},
	'callback': function() {
		EGW.misc.overwriteJQfunctions();
		EGW.misc.cssFunctions();
		EGW.misc.moduleFunctions();
		EGW.res.loadJS(baseurl + 'resources/jquery/plugins/jquery.tools.min.js', function() {
			EGW.res.loadJS(baseurl + 'resources/jquery/plugins/jquery.tools.dynamic.custom.js');
		});
		EGW.res.loadJS(baseurl + 'resources/jquery/plugins/jquery.cookie.js');
		EGW.res.loadJS(baseurl + 'resources/jqGrid/grid.locale-en.js');
		EGW.res.loadJS(baseurl + 'resources/jqGrid/jqgrid.js');
		EGW.res.loadJS(baseurl + 'resources/pdfviewer/pdfviewer.js');
		EGW.res.loadJS(baseurl + 'resources/jwplayer/jwplayer.js');
		EGW.res.loadJS(baseurl + 'resources/jquery/plugins/jquery.json.js', '$.evalJSON', function() {
			EGW.res.getSettings('general');
			EGW.res.loadResource(["uitranslation", "user"], ["EGW.ui.translation", "EGW.user"], function() {
				EGW.system.initialized = true;
			});
		});
		EGW.system.key.init.scope(EGW.system.key);
	}
});




/*** SEE IF GET PARAMETERS - IF YES, LOAD AUTOMATICALLY ***/

EGW.res.delayAction({
	'iterations': EGW.settings.delayAction.defaultIterations,
	'speed': EGW.settings.delayAction.defaultSpeed,
	'test': function() {
		return ((typeof jQuery != 'undefined') ? true : false);
	},
	'callback': function() {
		$('script').each(function() {
			if (typeof $(this).attr('src') != 'undefined' && $(this).attr('src').match(/egw_essential.js/)) {
				var scriptAddr = $(this).attr('src');
				
				// Check if there are parameters
				if (scriptAddr.indexOf('?') > 0) {
					
					// Get the parameters and put them into an array (sendParam)
					var param = scriptAddr.substr(scriptAddr.indexOf('?')+1).split('&');
					var sendParam = [];
					for(var n=0; n<param.length; n++) {
						var elm = param[n].split('=');
						sendParam[elm[0]] = elm[1];
					}
					
					// If no wrapper defined, but a createwrapper instead, create the div...
					if (typeof sendParam['wrapper'] == 'undefined' && typeof sendParam['createwrapper'] != 'undefined') {
						
						// If wrapperstyle is defined, take this...
						if (typeof sendParam['wrapperstyle'] != 'undefined') {
							var wrapperstyle = '';
							if (!(sendParam['wrapperstyle'].match(/position/))) {
								wrapperstyle += 'position: relative;'
							}
							wrapperstyle += sendParam['wrapperstyle'];
						}
						// Or else, just span it accross the entire page.
						else {
							var wrapperstyle = 'position: absolute; top: 0px; right: 0px; bottom: 0px; left: 0px; border: 1px solid #666666';
						}

						// If it's defined in the head, append it to body, else append put it after the script tag
						if ($(this).parents().map(function () {
							return this.tagName;
						}).get().join(",").match(/BODY/)) {
							$(this).after('<div id="' + sendParam['createwrapper'] + '" style="' + wrapperstyle + '"></div>');
						} else {
							$('body').append('<div id="' + sendParam['createwrapper'] + '" style="' + wrapperstyle + '"></div>');
						}
						
						// And finally, load everything...
						EGW.load(sendParam['module'], sendParam['createwrapper'], sendParam['param']);
					}
					
					// If wrapper is defined, just put everything in there
					else {
						EGW.load(sendParam['module'], sendParam['wrapper'], sendParam['param']);
					}
				}
			}
		});

	}
});

/*
 * Group: Miscellaneous (EGW.misc)
 * Contains functions which don't fit anywhere else.
 */
EGW.misc = {
	/*
	 * Function: EGW.misc.now
	 * Returns the current date.
	 */
	now: function(){
		return +new Date;
	},
	/*
	 * Function: EGW.misc.overwriteJQfunctions
	 * Overwrites the default ajax functionality of jQuery in order to make use of proxy.php
	 */
	overwriteJQfunctions: function() {
		$.extend(jQuery.ajaxSettings, {
			allowUseProxy: true,
			cr_splitted: false
		});

		$.fn.extend({
			load: function( url, params, callback ) {
				if ( typeof url !== "string" )
					return this._load( url );
		
				var off = url.indexOf(" ");
				if ( off >= 0 ) {
					var selector = url.slice(off, url.length);
					url = url.slice(0, off);
				}
		
				// Default to a GET request
				var type = "GET";
		
				// If the second parameter was provided
				if ( params )
					// If it's a function
					if ( jQuery.isFunction( params ) ) {
						// We assume that it's the callback
						callback = params;
						params = null;
		
					// Otherwise, build a param string
					} else if( typeof params === "object" ) {
						params = jQuery.param( params );
						type = "POST";
					}
		
				var self = this;
		
				// Request the remote document
				jQuery.ajax({
					url: url,
					type: type,
					dataType: "html",
					data: params,
					complete: function(data, status){
						// If successful, inject the HTML into all the matched elements
						var res = undefined
						if (typeof data.responseText != "undefined") {
							res = data;
							data = data.responseText;
						}
						if ( status == "success" || status == "notmodified" ) 
							// See if a selector was specified
							self.html( selector ?
								// Create a dummy div to hold the results
								jQuery("<div/>")
								// inject the contents of the document in, removing the scripts
								// to avoid any 'Permission Denied' errors in IE
								.append(data.replace(/<script(.|\s)*?\/script>/g, ""))
		
								// Locate the specified elements
								.find(selector) :
		
								// If not, just inject the full result
								data );
		
						if( callback )
							self.each( callback, [data, status, res] );
					}
				});
				return this;
			}
		});

		$.extend({
			ajax: function( s ) {			
				// Extend the settings, but re-extend 's' so that it can be
				// checked again later (in the test suite, specifically)
				s = jQuery.extend(true, s, jQuery.extend(true, {}, jQuery.ajaxSettings, s));

				var jsonp, jsre = /=\?(&|$)/g, status, data,
				type = s.type.toUpperCase();

				// convert data if not already a string
				if ( s.data && s.processData && typeof s.data !== "string" )
					s.data = jQuery.param(s.data);

				s.url = s.url.replace(/\/{2,}/g, '/').replace(/:\//, '://');
				// Gabriel: Check if cross domain non-script call
				var domain = location.protocol+"//"+location.host+"/";
				var url = location.href;
				var hasdomain = /^(\w+:)?\/\/([^\/?#]+)/.exec( s.url )==null ? false : true;
				var onserver = hasdomain ? (s.url.indexOf(baseurl) > -1 ? true : false) : false;
				var crossdomain = onserver ? (url.indexOf(baseurl) > -1 ? false : true) : false; //hasdomain ? s.url.indexOf(domain) > -1 ? false : true : (url.indexOf(baseurl) > -1) ? false : true;
				var useproxy = s.allowUseProxy ? (s.url.indexOf("proxy.php") > -1 ? false : onserver) : false;
				if (crossdomain) {
				//				dump("domain::"+domain);
				//				dump("url::"+url);
				//				dump("baseurl::"+baseurl);
				//				dump("s.url::"+s.url);
				//dump("onserver::"+onserver);
				//dump("hasdomain::"+hasdomain);
				}
				if (!hasdomain) dump("no domain given. baseurl forgotten?");
				//dump("s.allowUseProxy::"+s.allowUseProxy);
				//dump("crossdomain::"+crossdomain);
				//dump("useproxy::"+useproxy);
				
				// Handle JSONP Parameter Callbacks
				if ( s.dataType == "jsonp" ) {
					if ( type == "GET" ) {
						if ( !s.url.match(jsre) )
							s.url += (s.url.match(/\?/) ? "&" : "?") + (s.jsonp || "callback") + "=?";
					} else if ( !s.data || !s.data.match(jsre) )
						s.data = (s.data ? s.data + "&" : "") + (s.jsonp || "callback") + "=?";
					s.dataType = "json";
				}

				// Gabriel: create org object
				if (crossdomain)
					var org = {
						dataType: s.dataType,
						type: s.type.toUpperCase()
					};

				// Build temporary JSONP function
				if ( s.dataType == "json" && (s.data && s.data.match(jsre) || s.url.match(jsre)) ) {
					jsonp = "jsonp" + jsc++;

					// Replace the =? sequence both in the query string and the data
					if ( s.data )
						s.data = (s.data + "").replace(jsre, "=" + jsonp + "$1");
					s.url = s.url.replace(jsre, "=" + jsonp + "$1");

					// We need to make sure
					// that a JSONP style response is executed properly
					s.dataType = "script";

					// Handle JSONP-style loading
					window[ jsonp ] = function(tmp){
						data = tmp;
						success();
						complete();
						// Garbage collect
						window[ jsonp ] = undefined;
						try{
							delete window[ jsonp ];
						} catch(e){}
						if ( head )
							head.removeChild( script );
					};
				}

				if ( s.dataType == "script" && s.cache == null )
					s.cache = false;

				if ( s.cache === false && type == "GET" ) {
					var ts = EGW.misc.now();
					// try replacing _= if it is there
					var ret = s.url.replace(/(\?|&)_=.*?(&|$)/, "$1_=" + ts + "$2");
					// if nothing was replaced, add timestamp to the end
					s.url = ret + ((ret == s.url) ? (s.url.match(/\?/) ? "&" : "?") + "_=" + ts : "");
				}

				//if (crossdomain || useproxy) dump("s.url1::"+s.url);
				//Gabriel: crossdomain adjustments
				if (s.dataType == "script") {
					crossdomain = false;
					useproxy = false;
				}
				var parts = undefined;
				if (crossdomain) {
					s.type = "GET";
					type = "GET";
					s.dataType = "script";
					if (typeof s.complete == "function" && typeof s.success != "function") {
						s.success = s.complete;
						s.complete = undefined;
					}
					var dataKey = ((new Date()).getTime() + "" + Math.floor(Math.random() * 1000000)).substr(0, 18);
				//dump("s.url2::"+s.url);
				}
				//Gabriel: useproxy adjustments
				if (useproxy) {
					if (hasdomain) {
						var re = s.url.match(/\?/) ? new RegExp(baseurl.replace("/", "\/")+"([^?]+)[?](.+)") : new RegExp(baseurl.replace("/", "\/")+"(.+)");
						parts = re.exec(s.url);
					} else {
						parts = s.url.match(/\?/) ? /(.+)\?(.+)/.exec( s.url ) : /(.+)/.exec( s.url );
					}
					s.data = "url2754=" + parts[1] + (crossdomain ? "&datakey2754=" + dataKey + "&method2754=" + org.type + "&script2754=1" :"&script2754=0") + (s.url.match(/\?/) ? "&" + parts[2] : "") + "&" + s.data;
					s.url = baseurl + "proxy.php";
				}

				//Gabriel: split up if arguments length is too large for GET on crossdomain
				if (crossdomain && !s.cr_splitted && s.data.length > 1000) {
					var allArgs = s.data.split('&');
					var sendString = '';
					var sendStrings = []; // Could be sent directly, but it's kinda hard to know beforehand how many transmissions would be necessary.
					for (var n = 0; n < allArgs.length; n++) {
						if (allArgs[n].length > 0) {
							sendString += '&';
							if (allArgs[n].length <= 1000 - sendString.length) {
								sendString += allArgs[n];
							}
							else {
								var thisArg = allArgs[n].split('=');
								var lengthNext = 1000 - sendString.length - thisArg[0].length - 1;
								sendString += thisArg[0] + '=' + thisArg[1].substr(0, lengthNext);
								allArgs[n] = thisArg[0] + '=' + thisArg[1].substr(lengthNext);
								n--;
							}

							if (sendString.length == 1000) {
								sendStrings.push(sendString);
								sendString = '';
							}
						}
					}
					sendStrings.push(sendString);
					var transmissionParts = sendStrings.length;
					var transmissionID = ((new Date()).getTime() + "" + Math.floor(Math.random() * 1000000)).substr(0, 18);
					for (n = 0; n < transmissionParts; n++) {
						var transmission = n + 1;
						s.url = s.url.split("?")[0];
						s.data = sendStrings[n] + "&script2754=1&datakey2754=" + dataKey + '&transno2754=' + transmission + '&transtot2754=' + transmissionParts + '&transid2754=' + transmissionID
						s.cr_splitted = true;
						s.dataKey = dataKey;
						$.ajax(s);
					//sendData(path, sendStrings[n] + '&transno2754=' + transmission + '&transtot2754=' + transmissionParts + '&transid2754=' + transmissionID);
					}
					return undefined;
				//s.data = sendStrings[0] + '&transno2754=' + transmission + '&transtot2754=' + transmissionParts + '&transid2754=' + transmissionID;
				}


				// If data is available, append data to url for get requests
				if ( s.data && type == "GET" ) {
					s.url += (s.url.match(/\?/) ? "&" : "?") + s.data;

					// IE likes to send both get and post data, prevent this
					s.data = null;
				}
				//if (crossdomain || useproxy) dump("s.url3::"+s.url);

				// Watch for a new set of requests
				if ( s.global && ! jQuery.active++ )
					jQuery.event.trigger( "ajaxStart" );

				// Matches an absolute URL, and saves the domain
				parts = /^(\w+:)?\/\/([^\/?#]+)/.exec( s.url );

				// If we're requesting a remote document
				// and trying to load JSON or Script with a GET
				if ( s.dataType == "script" && type == "GET" && parts
					&& ( parts[1] && parts[1] != location.protocol || parts[2] != location.host )){

					var head = document.getElementsByTagName("head")[0];
					var script = document.createElement("script");
					script.src = s.url;
					if (s.scriptCharset)
						script.charset = s.scriptCharset;

					// Handle Script loading
					if ( !jsonp ) {
						var done = false;

						// Attach handlers for all browsers
						script.onload = script.onreadystatechange = function(){
							if ( !done && (!this.readyState ||
								this.readyState == "loaded" || this.readyState == "complete") ) {
								done = true;
								success();
								complete();

								// Handle memory leak in IE
								script.onload = script.onreadystatechange = null;
								head.removeChild( script );
							}
						};
					}

					head.appendChild(script);

					// We handle everything using the script element injection
					return undefined;
				}

				var requestDone = false;

				// Create the request object
				var xhr = s.xhr();

				// Open the socket
				// Passing null username, generates a login popup on Opera (#2865)
				if( s.username )
					xhr.open(type, s.url, s.async, s.username, s.password);
				else
					xhr.open(type, s.url, s.async);

				// Need an extra try/catch for cross domain requests in Firefox 3
				try {
					// Set the correct header, if data is being sent
					if ( s.data )
						xhr.setRequestHeader("Content-Type", s.contentType);

					// Set the If-Modified-Since header, if ifModified mode.
					if ( s.ifModified )
						xhr.setRequestHeader("If-Modified-Since",
							jQuery.lastModified[s.url] || "Thu, 01 Jan 1970 00:00:00 GMT" );

					// Set header so the called script knows that it's an XMLHttpRequest
					xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");

					// Set the Accepts header for the server, depending on the dataType
					xhr.setRequestHeader("Accept", s.dataType && s.accepts[ s.dataType ] ?
						s.accepts[ s.dataType ] + ", */*" :
						s.accepts._default );
				} catch(e){}

				// Allow custom headers/mimetypes and early abort
				if ( s.beforeSend && s.beforeSend(xhr, s) === false ) {
					// Handle the global AJAX counter
					if ( s.global && ! --jQuery.active )
						jQuery.event.trigger( "ajaxStop" );
					// close opended socket
					xhr.abort();
					return false;
				}

				if ( s.global )
					jQuery.event.trigger("ajaxSend", [xhr, s]);

				// Wait for a response to come back
				var onreadystatechange = function(isTimeout){
					// The request was aborted, clear the interval and decrement jQuery.active
					if (xhr.readyState == 0) {
						if (ival) {
							// clear poll interval
							clearInterval(ival);
							ival = null;
							// Handle the global AJAX counter
							if ( s.global && ! --jQuery.active )
								jQuery.event.trigger( "ajaxStop" );
						}
					// The transfer is complete and the data is available, or the request timed out
					} else if ( !requestDone && xhr && (xhr.readyState == 4 || isTimeout == "timeout") ) {
						requestDone = true;

						// clear poll interval
						if (ival) {
							clearInterval(ival);
							ival = null;
						}

						status = isTimeout == "timeout" ? "timeout" :
						!jQuery.httpSuccess( xhr ) ? "error" :
						s.ifModified && jQuery.httpNotModified( xhr, s.url ) ? "notmodified" : "success";

						if ( status == "success" ) {
							// Watch for, and catch, XML document parse errors
							try {
								// process the data (runs the xml through httpData regardless of callback)
								data = jQuery.httpData( xhr, s.dataType, s );
							} catch(e) {
								status = "parsererror";
							}
						}

						// Make sure that the request was successful or not modified
						if ( status == "success" ) {
							// Cache Last-Modified header, if ifModified mode.
							var modRes;
							try {
								modRes = xhr.getResponseHeader("Last-Modified");
							} catch(e) {} // swallow exception thrown by FF if header is not available

							if ( s.ifModified && modRes )
								jQuery.lastModified[s.url] = modRes;

							// JSONP handles its own success callback
							if ( !jsonp )
								success();
						} else
							jQuery.handleError(s, xhr, status);

						// Fire the complete handlers
						complete();

						if ( isTimeout )
							xhr.abort();

						// Stop memory leaks
						if ( s.async )
							xhr = null;
					}
				};

				if ( s.async ) {
					// don't attach the handler to the request, just poll it instead
					var ival = setInterval(onreadystatechange, 13);

					// Timeout checker
					if ( s.timeout > 0 )
						setTimeout(function(){
							// Check to see if the request is still happening
							if ( xhr && !requestDone )
								onreadystatechange( "timeout" );
						}, s.timeout);
				}

				// Send the data
				try {
					xhr.send(s.data);
				} catch(e) {
					jQuery.handleError(s, xhr, null, e);
				}

				// firefox 1.5 doesn't fire statechange for sync requests
				if ( !s.async )
					onreadystatechange();

				function success(){
					// If a local callback was specified, fire it and pass it the data
					if ( s.success ) {

						//Gabriel: read real data from js variable
						if (crossdomain || s.cr_splitted) {
							if (typeof dataKey == "undefined") dataKey = s.dataKey;
							//Stefan: replace -_-_- through linebreaks again
							data = window['data' + dataKey].replace(/-_-_-/g, "\n");
							data = org.dataType=="json" ? $.evalJSON(data) : data;
							status = "success";
							if (data != "0false0") s.success( data, status );
						} else {
							s.success( data, status );
						}
					}

					// Fire the global callback
					if ( s.global )
						jQuery.event.trigger( "ajaxSuccess", [xhr, s] );
				}

				function complete(){

					//					// Stefan: Fix responseText
					//					if (crossdomain) {
					//						var xhr = s.xhr();
					//						xhr.responseText = window['data' + dataKey].replace(/-_-_-/g, "\n");
					//						dump(xhr);
					//						dump(xhr.responseText);
					//						xhr.responseText = xhr.responseText.substring(28,xhr.responseText.length-2);
					//					}

					// Process result
					if ( s.complete )
						s.complete(xhr, status);

					// The request was completed
					if ( s.global )
						jQuery.event.trigger( "ajaxComplete", [xhr, s] );

					// Handle the global AJAX counter
					if ( s.global && ! --jQuery.active )
						jQuery.event.trigger( "ajaxStop" );
				}

				// return XMLHttpRequest to allow aborting the request etc.
				return xhr;
			}
		});

	//		$.extend({
	//			get : function(url, data, callback, type) {
	//				if (typeof data == "function") {
	//					type = callback;
	//					callback = data;
	//					data = {};
	//				}
	//				var json = (type == "json" | type == "jsonp") ? true : false;
	//				if (typeof data == 'object') {
	//					data = $.extend({
	//						method2754: "get"
	//					}, data);
	//				} else {
	//					data += '&method2754=get';
	//				}
	//				EGW.res.getPHP(url, data, json, callback);
	//			},
	//			post : function(url, data, callback, type) {
	//				if (typeof data == "function") {
	//					type = callback;
	//					callback = data;
	//					data = {};
	//				}
	//				var json = (type == "json" | type == "jsonp") ? true : false;
	//				if (typeof data == 'object') {
	//					data = $.extend({
	//						method2754: "post"
	//					}, data);
	//				} else {
	//					data += '&method2754=post';
	//				}
	//				EGW.res.getPHP(url, data, json, callback);
	//			},
	//			getScript : function(url, callback) {
	//				EGW.res.loadJS(url, 'EGW', callback, function() {});
	//			}
	//		});
	//		$.fn.extend({
	//			load: function(url, data, callback) {
	//				if (typeof data == "function") {
	//					callback = data;
	//					data = {};
	//				}
	//				var elem = $(this);
	//				var cb = function(htm) {
	//					elem.html(htm);
	//					callback();
	//				};
	//
	//				if (typeof data == 'object') {
	//					data = $.extend({
	//						method2754: "get"
	//					}, data);
	//				} else {
	//					data += '&method2754=get';
	//				}
	//				EGW.res.getPHP(url, data, cb);
	//			}
	//		});

	},
	/*
	 * Function: EGW.misc.cssFunctions
	 * Defines jQuery functions related to CSS
	 */
	cssFunctions: function() {
		/*
		 * Function: jQuery.fn.scrollWidth
		 * Gets or sets the scrollWidth. Gabriel: I wonder about setting...!?
		 *
		 * Parameters:
		 * 		val - if given function assumes you want to set the scrollWidth
		 */
		$.fn.scrollWidth = function(val) {
			if (!this[0]) return;

			return val != undefined ?

			// Set the scrollWidth
			this.each(function() {
				this == window || this == document ?
				window.scrollWidth( val ) :
				this[ 'scrollWidth' ] = val;
			}) :

			// Return the scrollWidth
			this[0] == window || this[0] == document ?
			self[ 'scrollWidth' ] ||
			$.boxModel && document.documentElement[ 'scrollWidth' ] ||
			document.body[ 'scrollWidth' ] :
			this[0][ 'scrollWidth' ];
		};
		/*
		 * Function: jQuery.fn.scrollHeight
		 * Gets or sets the scrollHeight. Gabriel: I wonder about setting...!?
		 *
		 * Parameters:
		 * 		val - optional. if given function assumes you want to set the scrollHeight
		 */
		$.fn.scrollHeight = function(val) {
			if (!this[0]) return undefined;

			return val != undefined ?

			// Set the scrollHeight
			this.each(function() {
				this == window || this == document ?
				window.scrollHeight( val ) :
				this[ 'scrollHeight' ] = val;
			}) :

			// Return the scrollHeight
			this[0] == window || this[0] == document ?
			self[ 'scrollHeight' ] ||
			$.boxModel && document.documentElement[ 'scrollHeight' ] ||
			document.body[ 'scrollHeight' ] :
			this[0][ 'scrollHeight' ];
		};

		/*
		 * Function: jQuery.fn.csspx
		 * Gets or sets a pixel value with the css function
		 *
		 * Parameters:
		 *		cssname - required. the css value you want to get or set.
		 * 		val - optional. If given this value will be set.
		 *
		 * Returns:
		 *		get: integer
		 *		set: undefined
		 */
		$.fn.csspx = function(cssname, val) {
			if (!this[0]) return undefined;

			if (typeof val != "undefined") {
				$(this[0]).css(cssname, val+"px");
				return undefined;
			} else {
				var value = $(this[0]).css(cssname).replace("px", "");
				return (isNaN(value) ? 0 : value*1);
			}
		};

		/*
		 * Function: jQuery.fn.cssper
		 * Gets or sets a percent value with the css function
		 *
		 * Parameters:
		 *		cssname - required. the css value you want to get or set.
		 * 		val - optional. If given this value will be set.
		 *
		 * Returns:
		 *		get: integer
		 *		set: undefined
		 */
		$.fn.cssper = function(cssname, val) {
			if (!this[0]) return undefined;

			if (typeof val != "undefined") {
				$(this[0]).css(cssname, val+"%");
				return undefined;
			} else {
				return $(this[0]).css(cssname).replace("%", "")*1;
			}
		};
	},
	wrapCode : function(type, element, name, config) {
		if(typeof config == 'string'){
			switch(config){
				case 'getObject':
					var obj = null;
					$(element).each(function(){
						if(this[name]){
							obj = this[name];
							return false;
						}
					});
					return obj;
					break;

				case 'getAllObjects':
					var arr = [];
					$(element).each(function(){
						if(this[name]){
							arr.push(this[name]);
						}
					});
					return arr;
					break;
			}
		} else if (typeof config == 'object' || typeof config == 'undefined'){
			if(typeof config == 'undefined'){
				config = {};
			}

			$(element).each(function(){
				var me = this;
				
				var callback = function(module) {
					me[name] = module;
					if (typeof config.modvar != 'undefined' && config.modvar != null) config.modvar = me[name];
					if (typeof me[name].settings != 'undefined') if (typeof me[name].settings.callback == 'function') me[name].settings.callback(me[name]);

					if (typeof config.callback == "function") config.callback(module);
				}
				var error = function() {
					dump(type+" "+name+" could not be loaded successfully.");

					if (typeof config.error == "function") config.error();
				}
				
				if (type == "resource") {
					this[name] = new EGW.res[name](this, config);
				} else if (type == "module") {
					EGW.load({
						name: config.name,
						wrapper: config.wrapper,
						param: config.param,
						modvar: null,
						callback: callback,
						error: error,
						headerbar: config.headerbar
					});
				}
			});
			return $(element);
		}
	},

	/*
	 * Function: EGW.misc.parseArgs
	 * Parses the function arguments due to passed conditions.
	 * This method is used when some of the function arguments are optional
	 *
	 * Parameters:
	 *      args - function arguments
	 *      minArgs - [optional] the number of required arguments (default 0)
	 *      conditions - object with conditions, which has the following syntax:
	 *  >      {
	 *  >          [argumentsCount1]: 'varName', // should be used when argumentsCount1==1
	 *  >          [argumentsCount2]: {
	 *  >              [#ofArgument1]: 'varName', // simple condition, varName = arguments[#ofArgument]
	 *  >              [#ofArgument2]: {
	 *  >                  [argumentType1]: 'varName1', // if type of the argument is argumentType1, then varName1 = arguments[#ofArgument2]
	 *  >                  [argumentType2]: 'varName2', // if type of the argument is argumentType1, then varName2 = arguments[#ofArgument2]
	 *  >                  .....
	 *  >                  'else': 'varName3', // if type of the argument is else than all above, then varName3 = arguments[#ofArgument2]
	 *  >              }
	 *  >          }
	 *  >      }
	 *
	 * Returns:
	 *      Object with properties as defined varNames
	 */

	parseArgs: function(args, minArgs, conditions){
		if(typeof minArgs != 'number'){
			conditions = minArgs;
			EGW.misc.parseArgs(args, 0, conditions);
			return;
		}

		if(minArgs > args.length){
			throw('The number of arguments is less than minimum required: '+minArgs);
		}
		var result = {};
		var condObject = conditions[args.length];
		if(typeof condObject == 'string'){
			result[condObject] = args[0];
		}else if(typeof condObject == 'object'){
			for(var n in condObject){
				if(typeof condObject[n] == 'string'){
					result[condObject[n]] = args[n];
				}else{
					for(var type in condObject[n]){
						if(typeof args[n] == type || type == 'else'){
							result[condObject[n][type]] = args[n];
							break;
						}
					}
				}
			}
		}
		return result;
	},
	
	/*
	 * Function: EGW.misc.retms
	 * Returns milliseconds and time since last measurement.
	 */
	retms: function() {
		var time = new Date();
		var newtime = ((time.getSeconds()*1000) + time.getMilliseconds());
		var difference = ((typeof oldtime != 'undefined') ? ' (' + (newtime - oldtime) + ')' : '');
		window['oldtime'] = newtime;
		return newtime + difference;
	},

	/*
	 * Group: Other
	*/
	moduleFunctions : function() {

		/*
		 * Function: $.loadModule
		 * jQuery Extension.
		 * Synonym for the EGW.load function. Loads a module into a html element.
		 *
		 * Parameters:
		 *		name - Name of the module to be loaded -- including path. "writings.search" loads /modules/wrtings/search/search.js
		 * 		params - optional. Parameters to be given to the module.
		 * 		modvar - optional. The variable the module will be saved in. If not given, it is not saved to a variable.
		 * 		headerbar - optional. A jQuery object of a division to hold the main iconBar of the module. Mainly used to bring the iconbar into the header of a window or pane.
		 * 		callback - optional. Callback function.
		 * 		error - optional. Callback function called if an error occurs.
		 *
		 * Returns:
		 *		given jQuery element
		 *
		 * Usage:
		 * > $('#div1').loadModule({
		 * >   name: "writings",
		 * >   callback: function(){...},
		 * > );
		 *
		 * > $('#div1').loadModule({
		 * >   name: "writings",
		 * >   params: {var1: "test"},
		 * >   modvar: variabletofillwithmodule,
		 * >   headerbar: $('#somediv'),
		 * >   callback: function(){...},
		 * >   error: function() {...}
		 * > });
		 *
		 */
		$.fn.extend({
			loadModule: function(params){
				params = $.extend({}, {
					param: {},
					wrapper: this,
					modvar: null,
					headerbar: undefined,
					callback: function(){},
					error: function(){}
				}, params);
				if (typeof params.name == 'undefined') throw "$.loadModule: name not given";
				EGW.misc.wrapCode("module", this, params.name, params);
			}
		});
		
		jQuery.download = function(url, data, method){
			//url and data options required
			if( url && data ){
				//data can be string of parameters or array/object
				data = typeof data == 'string' ? data : jQuery.param(data);
				//split params into form inputs
				var inputs = '';
				jQuery.each(data.split('&'), function(){
					var pair = this.split('=');
					inputs+='<input type="hidden" name="'+ pair[0] +'" value="'+ pair[1] +'" />';
				});
				//send request
				jQuery('<form action="'+ url +'" method="'+ (method||'post') +'">'+inputs+'</form>')
				.appendTo('body').submit().remove();
			};
		};
	}
}



/*
 * Function: EGW.load
 * Loads a module into a placeholder-div. Alternative jQuery extension: $.loadModule function
 *
 * Parameters:
 * 		name - Short name of the module.
 * 		wrapper - ID of the wrapper div.
 * 		param - optional. Object of additional parameters to be sent to the module.
 * 		modvar - optional. Variable to store the object to. If not set, it assumes, multi framework is loaded. Use null if no variable shall be filled with the module.
 * 		callback - optional. Callback function.
 * 		error - optional. Callback function called if an error occurs.
 * 		loadHistory - optional. Defines if History is automatically updated. Default: true
 * 		loadSettings - optional. Defines if Settings are automatically loaded. Default: true
 * 		forceID - optional. Assigns a specific ID to the module. If ID exists already it is overwritten.
 * 		saveSettings - optional. Routed to module to indicate that settings are not to be saved upon loading. Default: true
 * 		textSelection - optional. Defined if TextSelection is loaded. Default: true.
 * 		moduleIconsWrapper - optional. jQuery-Element of wrapper for module icons. Default: false.
 *
 * Usage:
 * > EGW.load({
 * >   name: "writings",
 * >   wrapper: "someid",
 * >   param: {var1: "test", var2: function() {...}},
 * >   callback: function() {},
 * >   loadHistory: false
 * > });
 */

EGW.load = function(params) {
	
	// Needed at least when called via essential-framework in certain browsers such as chrome.
	EGW.res.delayAction({
		test: function() {
			return typeof jQuery == 'undefined' ? false : true;
		},
		callback: function() {
			
			if (arguments.length > 1) throw "EGW.load only takes one object as input. More than one parameter given.";
			if (typeof params == 'string') params = eval('(' + params + ')');
		
			params = $.extend({}, {
				param : {},
				callback : function() {},
				error : function() {},
				loadHistory: true,
				loadSettings: true,
				forceID: false,
				saveSettings: true,
				textSelection: true,
				moduleIconsWrapper: false,
				loadTranslations: true,
				headerbar: undefined,
				emptywrapper: true
			}, params);
			if (typeof params.name == 'undefined') dump("EGW.load: name not given.");
			if (typeof params.wrapper == 'undefined') dump("EGW.load: wrapper not given.");
			if (typeof params.wrapper == "string") {
				params.wrapper = $('#' + params.wrapper)
			} else if (typeof params.wrapper == 'object' && typeof params.wrapper.jquery == 'undefined') {
				params.wrapper = $(params.wrapper);
			}
			if (typeof params.param == 'string') params.param = eval('(' + params.param + ')');

			// Load translations for the module.
			EGW.res.delayAction({
				'test': function() {
					return (typeof EGW.ui.translation == 'undefined' ? false : true);
				},
				'callback': function() {
					EGW.ui.translation.loadwords(params.name);
				}
			});

			// Load and init history (if needed)
			if (params.loadHistory && typeof EGW.res.history == 'undefined') {
				EGW.res.loadJS(baseurl + 'resources/jquery/plugins/jquery.history.js', '$.historyInit', function() {
					EGW.res.loadJS(baseurl + 'framework/essential/history.js', 'EGW.res.history', function() {
						$.historyInit(EGW.res.history.update, "egw.php");
					});
				});
			}
			
			// Load and init settings if needed
			if (params.loadSettings && typeof EGW.res.settings == 'undefined') {
				EGW.res.loadJS(baseurl + 'framework/essential/settings.js');
			}
			
			// Load textSelection if needed
			if (params.textSelection && typeof EGW.res.textSelection == 'undefined') {
				EGW.res.delayAction({
					'test': function() {
						return (typeof EGW.settings.general != 'undefined' ? true : false);
					},
					'callback': function() {
						EGW.res.loadJS(baseurl + 'resources/textselection/textselection.js');
						EGW.res.loadJS(baseurl + 'resources/jquery/plugins/jquery.textselection.js');
					}
				});
			}
			
			// Wait with actually accessing the module till everything else is loaded - including DOM wrapper
			EGW.res.delayAction({
				'test': function() {
					
					var returnWithoutOptional = (((typeof jQuery != 'undefined' && typeof EGW.settings.general != 'undefined' && typeof $.evalJSON != 'undefined' && typeof $.cookie != 'undefined')) ? true : false);
					if (returnWithoutOptional == false) {
						return false;
					}
					
					// If possibly true, optionals have to be tested...
					var loadedCounter = 0;
					var neededCounter = 0;
					if (params.loadHistory) {
						neededCounter++;
						if (typeof EGW.res.history != 'undefined') {
							loadedCounter++;
						}
					}
					if (params.loadSettings) {
						neededCounter++;
						if (typeof EGW.res.settings != 'undefined') {
							loadedCounter++;
						}
					}
					if (params.textSelection) {
						neededCounter++;
						if (typeof EGW.res.textSelection != 'undefined') {
							loadedCounter++
						}
					}
					if (params.loadTranslations) {
						neededCounter++;
						if (typeof EGW.ui.translation != 'undefined' && in_array(EGW.ui.translation.loadedAreas,params.name)) { //  && in_array(EGW.ui.translation.loadedAreas,params.name) ??
							loadedCounter++
						}
					}
					
					if (loadedCounter == neededCounter) {
						return true;
					} else {
						return false;
					}
					
				},
				'callback': function() {
					var modParts = params.name.split(".");
					var modName = "";
					var modPath = "";
					var cb_function = function() {
		
		
						if (params.forceID) {
							var moduleID = params.forceID;
						} else {
							var moduleID = ++EGW.modules.common.maxModID;
						}
		
		
						var postModuleLoadFunctions = function(module) {
		
							//  Add optional variables if necessary
							if (typeof module.ignoreGlobalSearch == 'undefined') {
								module.ignoreGlobalSearch = true;
							}
							if (typeof module.updatedByOtherModules == 'undefined') {
								module.updatedByOtherModules = false;
							}
							if (typeof module.targetModules == 'undefined') {
								module.targetModules = [];
							}
							if (typeof module.textSelectOptions == 'undefined') {
								module.textSelectOptions = [];
							}
		
							// Exec initialization if existing
							if (typeof module.initialization == 'function') {
								module.initialization.call(module, {
									'saveSettings' : params.saveSettings
								});
							}
		
							// Init Events
		
							// UpdateModules
							if (typeof EGW.res.settings != 'undefined' && typeof module.onUpdateSettings != 'undefined') {
								module.onUpdateSettings.bind('updateSettings', EGW.res.settings.saveModuleSettings);
							}
		
							// Window Title Rename
							if (typeof module.onRenameWindowTitle != 'undefined') {
								module.onRenameWindowTitle.bind('renameWindowTitle', function(title) {
									if (typeof EGW.ui.windows != 'undefined') {
										for(var n = 1; n < EGW.ui.windows.length; n++) {
											if (typeof EGW.ui.windows[n] != 'undefined' && typeof EGW.ui.windows[n].id != 'undefined') {
												if (EGW.ui.windows[n].loadedModuleID == module.id) {
													EGW.ui.windows[n].renameTitle(title);
												}
											}
										}
									}
								});
							}
		
							// UpdateModules
							if (typeof module.onUpdateModules != 'undefined') {
								module.onUpdateModules.bind('updateModules', EGW.res.updateModules);
							}
		
							// Find Related Info
							if (typeof module.onFindRelatedInfo != 'undefined') {
								module.onFindRelatedInfo.bind('findRelatedInfo', EGW.res.findRelatedInfo);
							}
		
						}
		
						if (params.emptywrapper) params.wrapper.find('*').remove();
		
						if (typeof params.modvar == 'undefined') {
							var moduleName = eval('(EGW.modules.store.'+params.name+')');
							var thismod = EGW.modules.loadedModules[moduleID] = new moduleName({
								'wrapper': params.wrapper,
								'initialParam': params.param,
								'moduleIconsWrapper': params.moduleIconsWrapper,
								'headerbar': params.headerbar
							});
							postModuleLoadFunctions(EGW.modules.loadedModules[moduleID]);
						} else if (params.modvar == null || typeof params.modvar == "boolean") {
							var moduleName = eval('(EGW.modules.store.'+params.name+')');
							var thismod = new moduleName({
								'wrapper': params.wrapper,
								'containingVariable': undefined,
								'initialParam': params.param,
								'moduleIconsWrapper': params.moduleIconsWrapper,
								'headerbar': params.headerbar
							});
							postModuleLoadFunctions(thismod);
						} else {
							var moduleName = eval('(EGW.modules.store.'+params.name+')');
							var thismod = {
								'module' : new moduleName({
									'wrapper': params.wrapper,
									'containingVariable': params.modvar,
									'initialParam': params.param,
									'moduleIconsWrapper': params.moduleIconsWrapper,
									'headerbar': params.headerbar
								}),
								'modid' : moduleID
							}
							postModuleLoadFunctions(thismod['module']);
							window[params.modvar] = thismod;
						}
		
						params.callback(thismod);
					};
		
					for (var i=0;i<modParts.length;i++) {
						modName = modParts[i];
						modPath += modName + "/";
						EGW.res.loadCSS(baseurl + 'modules/' + modPath + modName + '.css');
						EGW.res.loadJS(baseurl + 'modules/' + modPath + modName + '.js', 'EGW.modules.store.' + params.name, (i==modParts.length-1) ? cb_function : undefined);
					}
				//			modName = modParts.splice(modParts.length - 1, 1);
				//			modPath = params.name.replace('.', '/');
				//			EGW.res.loadCSS(baseurl + 'modules/' + modPath + '/' + modName + '.css');
				//			EGW.res.loadJS(baseurl + 'modules/' + modPath + '/' + modName + '.js', 'EGW.modules.store.' + params.name,
		
				},
				'timeout':function() {
					params.error();
				}
			});			
		}
	});
}




/*
 * Group: Root level functions
 */

/*
 * Function: dump
 * Dumps the variable either to firebug or to a little div at the
 * bottom-left corner of the screen. Only for localhost.
 *
 * Parameters:
 * 		p - Variable to be dumped. String or Array (Object) of Strings (to group entries, Firebug only)
 * 		printtime - Optional. Prefixes the actual dump with another dump containing the time (in milliseconds). Default: false;
 * 		groupname - Mandatory for grouping, otherwise optional. If p is Array, this is name of group.
 */
function dump(p, printtime, groupname) {
	var url = document.location.href;
	if (url.match(/localhost/)) {
		
		var thetime = false;
		if (typeof printtime != 'undefined' && printtime == true) {
			var thetime = (new Date()).getTime();
		}
		
		if (typeof window.console != 'undefined' && typeof window.console.firebug != 'undefined') {
			if (typeof p == 'object' && typeof groupname == 'string') {
				var gn = (typeof groupname == 'undefined' ? (new Date()).getTime() : groupname + (typeof printtime != 'undefined' && printtime == true ? ' (' + (new Date()).getTime() + ')' : ''));
				console.group(gn);
				for(var n = 0; n < p.length; n++) {
					console.log(p[n]);
				}
				console.groupEnd();
			}
			else {
				if (thetime) console.log(thetime);
				console.log(p);
			} 
			
		}
		else {
			if (document.getElementById('debugger') == null) {
				var debugWindow = document.createElement('div');
				debugWindow.setAttribute('id', 'debugger');
				debugWindow.setAttribute('style', 'overflow: scroll; padding: 2px; background-color: #DDDDDD; width: 300px; height: 150px; background-color: white; border: 1px solid black; position: fixed; left: 10px; bottom: 35px');
				var bodyTags = document.getElementsByTagName('body')[0].appendChild(debugWindow);
			}
			document.getElementById('debugger').innerHTML = (thetime ? thetime + ': ' : '') + p + '<br />' + document.getElementById('debugger').innerHTML;
		}
	}
}


/*** Used as class ***/
/*
 * Class: EGW.res.event
 * Allows to set up events.
 */
EGW.res.event = function() {
	/* Field: this.eventTypes
	 * Stores the function types of each bound function. Used to unbind a certain function */
	this.eventTypes = new Array();
	/* Field: this.eventActions
	 * Stores the functions to be executed upon fire. */
	this.eventActions = new Array();
	/* Field: this.eventScopes
	 * Stores the scope for the functions to be executed upon fire. */
	this.eventScopes = new Array();
	/* Field: this.eventamount
	 * Amount of the functions bound to this event. */
	this.eventamount = 0;

	/* Method: this.bind
	 * Adds functions to the event.
	 *
	 * Parameters:
	 *		type - A string identifying the function. Should be unique to identify the function. If type is in list already, function is overwritten.
	 *		fn - The function to bind.
	 *		scope - The scope of the called function.
	 */
	this.bind = function(type, fn, scope) {
		if (typeof fn == "undefined") dump("Undefined function bound to EGW.res.event!");
		scope = typeof scope == 'undefined' ? false : scope;
		var typeexists = false;
		var itemnumb = 0;
		for (var i = 1; i <= this.eventamount; i++) {
			if (this.eventTypes[i] == type) {
				typeexists = true;
				itemnumb = i
			}
		}
		if (typeexists) {
			this.eventActions[itemnumb] = fn;
			this.eventScopes[itemnumb] = scope;
		} else {
			this.eventamount++;
			this.eventTypes[this.eventamount] = type;
			this.eventActions[this.eventamount] = fn;
			this.eventScopes[this.eventamount] = scope;
		}
	};

	/* Method: this.undbind
	 * Takes a function off the event by type.
	 *
	 * Parameters:
	 *		type - A string identifying the function to be removed.
	 */
	this.unbind = function(type) {
		for (var i = 1; i <= this.eventamount; i++) {
			if (this.eventTypes[i] == type) {
				this.eventTypes[i] = undefined;
			}
		}
	};

	/* Method: this.fire
	 * Fires the event and executes all bound functions. All given parameters are passed on to the called functions.
	 */
	this.fire = function() {
		for (var i = 1; i <= this.eventamount; i++) {
			if (this.eventTypes[i] != undefined) this.eventActions[i].apply(typeof this.eventScopes[i] != false ? this.eventScopes[i] : this.eventActions[i], arguments);
		}
	};
}




/*
 * Object: EGW.system.design
 * Object for design related methods settings.
 */
EGW.system.design = {
	id : 1,
	/*
	 * Event: EGW.system.design.onChange
	 * Executed automatically upon design changes. All modules and framework have to register the changes to be made for them individually with EGW.system.design.onChange.
	 */
	onChange : new EGW.res.event()
}
EGW.res.delayAction({
	test: function() {
		return (typeof jQuery != 'undefined' ? true : false);
	},
	callback: function() {
		EGW.res.loadDesign(EGW.system.design.id);
	}
})