/*
	AjaxDispatcher
	The default ajax list behaviour for the Xierpa web library

	This dispatcher is closely related to the SiterBuilder Ajax functions. 
	An application subscribes to an event with a (target, methodname, eventname) tuple.
	When an Ajax event occures (e.g. by hitting a Xierpa ajaxlink(...) ), then the AjaxDispatcher.ajaxlink
	is sends a request with the defined parameters. The result is an asynchronic generated json that
	contains a blob of XHTML. The innerHTML of the target node then is set to the this blob.
	
	Example ajax call
	http://127.0.0.1/xierpa/rabobank/book/-/ajax/mode-activity/id-75/event-openfolder

	Note that in future versions the sid will be a required parameters too for security
	
	...
	self.ajaxlink(url, appid, table, anchorid, mode, event, target, class_='myclass')
	self.image(src=iconpath)
	self._ajaxlink()
	...
	self.ajaxform(self.FORMID, url, appid, mode, event, class_='myclass')
	self.image(src=iconpath)
	self._ajaxlink()

	Attributes
	appid			integer or string app id, indicating the calling application
	table			table name that the anchorid refers to
	anchorid		used for the id of the active record
	mode			identifier in case there is more than one ajaxlist used
	data			misc data field
	event			event name of the use action
	target			target id of the XHTML blob
	jscallback		javascript snippet or function that gets called after the AJAX request has completed
					NOTE: DO NOT USE SINGLE QUOTES IN YOUR SNIPPET, IT WILL FAIL!
					Examples:
					alert(1); -- will alert 1
					function(success, results) { alert(success); } -- will alert true on successful AJAX call and false otherwise
					somefunctionname -- name of a function that takes 2 params (first is true/false on success/fail, second is the results set)
	jsvalidator		javascript snippet or function that gets called before the AJAX request is made
					NOTE: DO NOT USE SINGLE QUOTES IN YOUR SNIPPET, IT WILL FAIL!
					Function returns true or false, if false, the AJAX request will not be made.
					Examples:
					return false; -- request will not be made
					function(oForm) { if(oForm.somevalue.length == 0) return false; return true; } -- request will only be made if somevalue is filled out in the form (for ajaxformlink)
	
	The Ajax application needs to answer a JSON structure with has at least the following field defined:
	targets		list of (target, xhtml) tuples that will be distributed amongst the target div tags.
	
*/
AjaxDispatcher = {

	// A J A X  S T U F F
	
	ajaxlink: function(url, appid, table, anchorid, mode, data, event, target, jscallback, jsvalidator){
		//
		//	The ajaxlink function will typically be called from a link in the ajax folder/file list of activities.
		// 	It does an asynchronic call to the page defined by url to get the JSON constrict
		//	with target content.
		//	The function is called by XierpaBuilder self.ajaxlink(url, id, mode, event, class_)
		//
		var params = {'appid': appid, 'table': table, 'anchorid': anchorid, 'mode': mode, 'data': data, 'event': event, 'target': target, 'jscallback': jscallback};
		//log('mode ajaxlink', mode);
		//log('url:', url, 'appid:', appid, 'table:', table, 'anchorid:', anchorid, 'mode:', mode, 'data:', data, 'event:', event, 'target:', target);

		// Check for a validator
		if(jsvalidator && jsvalidator.length > 0)
		{
			var fncCallback = eval(jsvalidator);
			if(fncCallback)
			{
				if(!fncCallback(null))
					return;
			}
			else
				return;
		}

		AjaxDispatcher.ajax(url, params)
	},
	addformparam: function(params, name, id, value){
		//
		//	Used by AjaxDispatcher.ajaxform to add the found form element to the params dict.
		//	Add the value to the list under name or id in params. 
		//	If the name or id does not exist in params, then maken empty Array entry first.
		//	We need this, in order to allow multiple parameter values with the same name in the url.
		//
		var key;
		if (name) key = name;
		else if (id) key = id;
		if (key){
			if (!params[key])
				params[key] = new Array();
			params[key].push(value);
		}
	},
	ajaxform: function(id, url, appid, table, anchorid, mode, data, event, target, jscallback, jsvalidator){
		//
		//	The form function will typically be called from a form definition upon submit.
		//	It collects all the available HTMLSelectElement instances of the form and adds
		//	them to the params of the ajax call. 
		//	It does an asynchronic call to the page defined by url to get the JSON construct 
		//	with target XHTML content
		//	The function is called by submit of the form with id
		//		
		var params = {'appid': appid, 'table': table, 'anchorid': anchorid, 'mode': mode, 'data': data, 'event': event, 'target': target, 'jscallback': jscallback};
		//log('mode ajaxform', mode);
		//log('form', id, appid, mode, data, event, target);
		if (id) {
			var form = getElement(id);
			var element;
			
			if (form){
				for (var i=0; i < form.elements.length; i++) {	// IE cannot iterate through elements directly
					element = form.elements[i];

					if (element.type == 'textarea'){
						// If the textarea is in tineMce mode, then get the content from there.
						// http://tinymce.moxiecode.com/punbb/viewtopic.php?id=9983
						if (document.tinyMCE && tinyMCE.get(element.id)){
							var value = tinyMCE.get(element.id).getContent();
							//log('Get value from tinyMce', value)
						} else {
							value = element.value;
							log('Get value from textarea', value)
						}
						log(element.type, params, element.id, element.name, element.id, value);
						AjaxDispatcher.addformparam(params, element.name, element.id, value);

					} else if (element.type == 'text' || element.type == 'password'){
						AjaxDispatcher.addformparam(params, element.name, element.id, element.value);

					} else if (element.type == 'checkbox'){
						var value = 0;
						if (element.checked) value = 1;
						log('*** Checkbox ***', params, 'name', element.name, 'id', element.id, 'value', value);
						AjaxDispatcher.addformparam(params, element.name, element.id, value);	
						
					} else if (element.type == 'radio'){
						if (element.checked){
							//log('*** Radio ***', params, 'name', element.name, 'id', element.id, 'checked');
							AjaxDispatcher.addformparam(params, element.name, element.id, element.value);
						}
					} else if (element.type == 'select-one'){
						AjaxDispatcher.addformparam(params, element.name, element.id, element.value);

					} else if (element.type == 'hidden'){
						// @@@ Commented because hidden don't seem to work properly. But I don't know why.
						//#AjaxDispatcher.addformparam(params, element.name, element.id, element.value);
						log('@@@', element.type, element.name, element.id, element.value)
					}
				}
			} else {
				log('No form found with id', id);
			}
		} else {
			log('No form id defined');
		}

		// Check for a validator
		if(jsvalidator && jsvalidator.length > 0)
		{
			var fncCallback = eval(jsvalidator);
			if(fncCallback)
			{
				if(!fncCallback(form))
					return false;
			}
			else
				return false;
		}

		AjaxDispatcher.ajax(url, params)
		return false;
	},
	loadPostJSONDoc: function (url, /* optional*/params) {
		//	Redefine the normal MochiKit doSimpleXMLHttpRequest, since for IE or proxy environments
		//	the url can become too long with 'GET', so it is truncated.
		//	This way we do all Ajax communication through POST.
    	//	Do a simple XMLHttpRequest to a URL and get the response
    	//		@param url: The URL to GET
		//		@param params: An associative dict of HTTP arguments to send as a
		//			POST. If not specified, a GET will be used with the URL.
		//		@type params: dictionary
		//		@rtype: L{Deferred} returning the evaluated JSON response
		
		var d;

		if (typeof(params) != 'undefined') {
			var postValue = "";
			var i = 0;
			var param, paraml;
			var paramtype;
			
			for (var k in params) {
				i += 1; // Check for the first parameter, to avoid double & output.
				// Check if it's an array
				param = params[k];
				paramtype = Object.prototype.toString.apply(param);
				// If these are simple values, then just add it asn parameter.
				if (paramtype === '[object String]'){
					//log('--string--', k, paramtype);
					if (i > 1) postValue += '&';
					postValue += k + '=' + encodeURIComponent(param);
				} else if (paramtype === '[object Number]'){
					//log('--number--', k, paramtype);
					if (i > 1) postValue += '&';
					postValue += k + '=' + encodeURIComponent(param);
				} else if (paramtype === '[object Array]') { 
					// There is an array of values, make a parameter for each element.
					//log('++array++', k, param, paramtype);
					for (var l in param) {
						paraml = param[l]
						paramtype = Object.prototype.toString.apply(paraml);
						if (paramtype === '[object String]'){
							//log('--string--', k, paraml, paramtype);
							if (i > 1) postValue += '&';
							postValue += k + '=' + encodeURIComponent(paraml);
						} else if (paramtype === '[object Number]'){
							//log('--number--', k, paraml, paramtype);
							if (i > 1) postValue += '&';
							postValue += k + '=' + encodeURIComponent(paraml);
						}
					}
				}
			}

			log('postValue:', postValue);
			
			var req = MochiKit.Async.getXMLHttpRequest();
			req.open("POST", url, true);
			req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
			d = sendXMLHttpRequest(req, postValue);

		} else {
			d = MochiKit.Async.doSimpleXMLHttpRequest(url);
		}
		return d.addCallback(MochiKit.Async.evalJSONRequest);
	},
	ajax: function(url, params){
		//
		//	Handle the ajax call to url
		//	Add a timestamp string to change the virtual url for every ajax call, so browsers as IE and FireFox
		//	don't try to cache the pages.
		//	NOTE: due to the "same-origin" policy of browsers, that url can only refer to the same domain name
		//	as the page loading this dispatcher is in.
		//
		//log('++++', url, params.appid, params.mode, params.data, params.event);
 		var t = new Date()
		var serialurl = url + '/' + t.getFullYear() + (t.getMonth()+1) + t.getDate() + t.getHours() + t.getMinutes() + t.getSeconds() + Math.floor(Math.random()*99999)
		//log('serialurl:', serialurl);
		var d = AjaxDispatcher.loadPostJSONDoc(serialurl, params)
		//	Make this works as POST one day, to make IE work too
		//	with the character length limits of GET.
		//	req.open("POST", url, true);
		//	req.setRequestHeader("Content-Type","application/x-www-form-urlencoded; charset=UTF-8");
		//	req.send(params);
		//	where params is formatted:
		//	"?get=htmlpage&calculatesalary=1&gethtmlupdate=1"
		d.addCallback(AjaxDispatcher.load);
		d.addErrback(AjaxDispatcher.loaderror);	
	},
	load: function(results){
		//
		//	The Ajax read was successful. 
		// 	Distribute the XHTML over the targets
		//	results has the following attributes:
		//		layout	((target, xhtml), (target, xhtml), ...)
		//		(for future use)
		//		url			The calling ajax url for debugging show in log if it fails
		//		appid		The application id that originally was used to call the Ajax page
		//		component	The component id as target of the call
		//		ancorid		The anchor record id that was used to call the Ajax page
		//		table		The name of the table that the anchor id refers to
		//		mode		The original mode
		//		data		The original data
		//		event		Name of the calling event
		//
		var index, target;
		//log(results.targets);
		for (index in results.targets) {
			result = results.targets[index];
			if (result[0]){
				target = getElement(result[0]);
				if (target.type == 'textarea'){
					timeMceUnLoad(result[0]);
					target.value = result[1];
					timeMceAfterLoad(result[0]);						
					log('[AjaxDispatcher.load] textarea:', result[0], 'target: ', target, 'with result', result[1]);
				} else if (target){
					//log('Normal result:', target.name, target, result[1]);
					target.innerHTML = result[1]; 
				} else {
					log('[AjaxDispatcher.load] Cannot find target: ', target);
					log(results.url)
				}
			} else {
				log('No value result for target', target);
			}
		}

		if(results.jscallback && results.jscallback.length > 0)
		{
			var fncCallback = eval(results.jscallback);
			if(fncCallback)
				fncCallback(true, results);
		}
	},
	loaderror: function(results){
		//
		//	There was an error in the Ajax application call
		//
		log('[AjaxDispatcher.loaderror] Cannot read ajax: ', results);

		if(results.jscallback && results.jscallback.length > 0)
		{
			var fncCallback = eval(results.jscallback);
			if(fncCallback)
				fncCallback(false, results);
		}
	},
	
	// T I M E R  S T U F F
	
	starttimer: function(url, appid, table, anchorid, mode, data, event, target, interval){
		//
		// Start an Ajax timer on the defined parameters, running every interval (in milliseconds).
		// The function answers the timer id that must me used to stop this timer.
		// Note that none of the parameters used in a timer sequence should containt single quotes
		// or else the function cannot build the calling link for the next time out.
		//
		return setTimeout("AjaxDispatcher.ajaxlink('" + url + "','" + appid + "','" + table + "','", anchorid + "','" + mode + "','" + data + "','" + event + "','" + target + "');", interval);
	},
	stoptimer: function(timerid){
		//
		// Stop the running timer id
		//
		//log("stopTimer", timerid);
		clearTimeout(timerid);
	}
}


