/**
*	Jajax - Juman Ajax library
*	(c) Pavel Gudanets, 2009
*/

/*
dependencies:
	browser()	(send_form1)
	jd()		(_error_report)
*/

/*
Examples:
	// all links of class 'a', 'a sel' onclick make ajax-request; response is written to #content
	Jajax.convert_class(['a','a sel'], 'content');

	// all links with attribute 'ajax' make ajax-request; response is written to #content
	Jajax.convert_attr('ajax', 'content');

*/


var Jajax = {

	// Tools
	
	debug: false,		// display error reports?

	get: function(elid)
	{
		return document.getElementById(elid);
	},

	set_document_title: function(title, add)
	{
		if (add)
		{
			if (document.title)
				document.title += ' -';
			document.title += ' ' + title;
		}
		else
		{
			document.title = title;
		}
	},

	/**
	*	@author: prototype.js
	*	@todo: allowed: "tools/ext/class.ajax.php" {"file":{"name":"","type":"","tmp_name":"","error":4,"size":0}}
	*/
	is_json: function(str)
	{
		if (/^\s*$/.test(str)) return false;
		str = str.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, '');
		return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str);
	},

	str_to_json: function(json)
	{
		try {
			if (Jajax.is_json(json))
			{
				eval('var data = ' + json + ';');
				return data;
			}
			Jajax._error_report({title:"AjaxError: str_to_json()", response:json});
		} catch (e) { Jajax._error_report({title:"AjaxError: str_to_json()", response:json, error:e}); }
		return null;
	},

	// history of js files
	js_loaded: {},
	get_js_loaded: function()
	{
		return Jajax.js_loaded;
	},

	// Private methods

	_createRequestObject: function()
	{
		if (window.XMLHttpRequest)
		{
			try { return new XMLHttpRequest(); } catch (e) {}
		}
		else if (window.ActiveXObject)
		{
			try { return new ActiveXObject('Msxml2.XMLHTTP'); } catch (e) {}
			try { return new ActiveXObject('Microsoft.XMLHTTP'); } catch (e) {}
		}
		return null;
	},

	_error_report: function(data)
	{
		if (Jajax.debug)
		for (i in data)
		{
			jd('<b>'+i+'</b>:');
			jd(data[i]);
			jd('<hr>');
		}
	},

	// Public methods

	// @param url of the server script
	// @param func_cb - callback js function
	// @param params - parameters to send; URL-encoding them
	// @param req_type - 'GET', 'POST' etc.
	request: function(url, func_cb, params, req_type)
	{
		try
		{
			var req = Jajax._createRequestObject();
			if (!req)
				return;

			var param_str = '';
			for (var i in params)
			{
				if (param_str)
					param_str += '&';
				param_str += i + '=' + encodeURIComponent( params[i] );
			}

			//req.setRequestHeader("HTTP_X_REQUESTED_WITH", "XmlHTTPRequest");

			if (req_type == 'POST')
			{
				req.open('POST', url, true);
				req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
			}
			else // GET
			{
				param_str = (url.indexOf('?') == -1 ? '?' : '&') + param_str;
				req.open('GET', url + param_str, true);
				param_str = null;
			}

			req.onreadystatechange = function() // must be after 'req.open' call in IE
			{
				try
				{
					if (req.readyState == 4) // Complete
					{
						if (req.status == 200 && typeof(func_cb) == 'function')
						{
							func_cb(req.responseText, req.responseXML);
						}
					}
				}
				catch (e) { Jajax._error_report({title:"AjaxError: onreadystatechange()", error:e}); }
			};

			req.send(param_str);
		}
		catch (e) { Jajax._error_report({title:"AjaxError: request()", error:e}); }
	},

	/**
	*	find all <script> tags and include/eval them
	*/
	include_javascript_from_html: function(str)
	{
		str = str.replaceAll("\\n", ' ');
		str = str.replaceAll("\\r", ' ');

		var js_files_count = 0;			// how many js files should be loaded before javascript code can be evaled
		var js_files_loaded = 0;		// how many js files are already loaded
		var script;

		function when_all_js_files_loaded()
		{
			js_files_loaded += 1;

			//jd(js_files_loaded + ' ' + js_files_count);

			if (parseInt(js_files_loaded) < parseInt(js_files_count))	// nothing to do until all js files are loaded
				return;

			js_files_count = 0;

			// Opera issue: timeout is needed. identifiers from uploaded js files will be available after some delay
			setTimeout(function(){
				// <script>code</script> SECOND
				var re = new RegExp('<script[^>]*>(\\s*?<!--)*([\\s\\S]*?)((//)?\\s*?-->\\s*?)?</script[^>]*>', "ig");
				while (script = re.exec(str))
				{
					try{
					if (script[2])
					{
						eval(script[2]);
					}
					}catch(e){ Jajax._error_report({title:"AjaxError: JS_files:when_all_js_files_loaded()", script:script[2].replaceAll('<','&lt;'), error:e}); }
				}
			},1);
		}

		// <script src="script_file.js"></script> FIRST
		var re = new RegExp('<script.*?src=["\'](.*?)["\'].*?></script[^>]*>', 'ig');
		while (script = re.exec(str))
		{
			if (script[1])
			{
				Jajax.load_js(script[1], when_all_js_files_loaded);
				js_files_count += 1;
			}
		}
		// <script src="script_file.js"/> FIRST
		var re = new RegExp('<script.*?src=["\'](.*?)["\'].*?/>', 'ig');
		while (script = re.exec(str))
		{
			if (script[1])
			{
				Jajax.load_js(script[1], when_all_js_files_loaded);
				js_files_count += 1;
			}
		}
		if (js_files_count == 0)
			when_all_js_files_loaded();
	},

	// @param url of the server script
	// @param elid is id of the DOM node
	// @param op - (in|pre|post) data from the server script
	// @param func_cb - callback js function(html_str); must return true, otherwise further data procession will be stopped
	// @param params = {param_name:param_value, ...}
	// @param req_type - 'GET', 'POST'
	// @param allow_script - eval javascripts (some scripts will be injected in the Jajax.load_html() scope, so won't be seen outside)
	load_html: function(url, elid, op, func_cb, allow_script, params, req_type)
	{
		var JajaxLoadHTMLFunc = function(str)
		{
			try
			{
				/*
				if (typeof(func_cb) == 'function')
					if (!func_cb(str))
						return;
				*/
				switch (op)
				{
					case 'pre':
						Jajax.get(elid).innerHTML = str + Jajax.get(elid).innerHTML;
					break;
					case 'post':
						Jajax.get(elid).innerHTML += str;
					break;
					//case 'in':
					default:
						Jajax.get(elid).innerHTML = str;
					break;
				}
				//var scripts = new RegExp('<script(\s(type|charset|defer|language)="[^"]*")*>[\s\S]*?</script[^>]*>', "igm").exec(str);

				if (allow_script)
					Jajax.include_javascript_from_html(str);

				if (typeof(func_cb) == 'function')
					func_cb(str);
			}
			catch (e) { Jajax._error_report({title:"AjaxError: JajaxLoadHTMLFunc()", response:str, error:e}); }
		};
		Jajax.request(url, JajaxLoadHTMLFunc, params, req_type);
	},
	
	/**
	*	@param func_cb - callback js function(was_loaded_now)
	*		is called when js file was loaded (was_loaded_now=true); or it is determined that js file was loaded before (was_loaded_now=false)
	*	@param once - load only once (default: true);
	*/
	load_js: function(url, func_cb, once, charset)
	{
		var JajaxLoadJSFunc = function(code)
		{
			try {

				if (typeof once == 'undefined' || once)	// if js-file should be loaded only once, perform check
				{
					if (Jajax.js_loaded[url])
					{
						if (typeof(func_cb) == 'function')
							func_cb(false);
						return;
					}
					else
					{
						Jajax.js_loaded[url] = true;
					}
				}

				var head = document.getElementsByTagName("head")[0];
				var script = document.createElement("script");
				script.src = url;
				script.type = 'text/javascript';
				if (charset)
					script.charset = charset;

				var wait = true;
				script.onload = script.onreadystatechange = function()
				{
					if ( wait && (!this.readyState || this.readyState == "loaded" || this.readyState == "complete") )
					{
						wait = false;
						script.onload = script.onreadystatechange = null;	// handle memory leak in IE
						head.removeChild( script );

						if (typeof(func_cb) == 'function') func_cb(true);
					}
				};

				head.appendChild(script);

			} catch (e) { Jajax._error_report({title:"AjaxError: load_js_str()", response:code, error:e}); }
		}
		Jajax.request(url, JajaxLoadJSFunc);
	},

	/**
	*	@param func_cb - callback js function(data) (mandatory argument)
	*	@param once - load only once (default: true);
	*/
	load_json: function(url, func_cb, params, req_type)
	{
		var JajaxLoadJSONFunc = function(json)
		{
			func_cb( Jajax.str_to_json(json) );
		}

		Jajax.request(url, JajaxLoadJSONFunc, params, req_type);
	},

	load_xml: function(url, func_cb, params, req_type)
	{
		var JajaxLoadXMLFunc = function(text, xml)
		{
			func_cb(xml);
		}

		Jajax.request(url, JajaxLoadXMLFunc, params, req_type);
	},


	/**
	*	@author Gustavs Gutmanis
	*
	*	@param url
	*	@param func_cb - callback js function
	*	@param form - DOM object (document.formname)
	*
	*	@example: <form onSubmit="javascript:return Jajax.send_form1(url,func,this)" >
	*
	*	todo: radio field in ie6, file field
	*/
	send_form1: function(url, func_cb, form)
	{
		try
		{
			var post_params = new Array();

			if (browser() == 'ie')
			{
				var els = form.all;
				for (var i in els)
				{
					if (els[i].type == 'text' || els[i].type == 'password' || els[i].type == 'textarea' || els[i].type == 'select-one' || els[i].type == 'checkbox' || els[i].type == 'hidden' || els[i].type == 'submit') //  || els[i].type == 'radio'
					{
						post_params[i] = els[i].value;
					}
				}
			}
			else
			{
				var els = form.elements;
				for (var i in els)
				{
					if (els[i].type == 'text' || els[i].type == 'password' || els[i].type == 'textarea' || els[i].type == 'select-one' || els[i].type == 'checkbox' || els[i].type == 'radio' || els[i].type == 'hidden' || els[i].type == 'submit')
					{
						post_params[els[i].name] = els[i].value;
					}
				}
			}
			Jajax.request(url, func_cb, post_params, 'POST');
		}
		catch (e) { Jajax._error_report({title:"AjaxError: send_form1()", error:e}); }
		return false;
	},

	/**
	*	send file to the server without page refreshing
	*	todo: func_cb works only in Firefox.
	*/
	send_form: function(form, func_cb)
	{
		try {
			var div = document.createElement('div');
			var div_id = 'jajax_form_' + new Date().getSeconds() + Math.ceil(Math.random());
			div.innerHTML = 
			'<iframe style="display:none" src="javascript:true" id="'+div_id+'" name="'+div_id+'"></iframe>';

			form.appendChild(div);
			form.setAttribute('target', div_id);

			var iframe = Jajax.get(div_id);

			form.get_iframe = function()
			{
				return iframe;
			}

			form.cancel = function()
			{
				form.reset();
			}

			iframe.onload_counter = 0;
			iframe.onload = function()
			{
				if (iframe.onload_counter == 0)
				{
					//if (auto_submit)
						form.submit();
					iframe.onload_counter = 1;
				}
				else
				{
					try {
						iframe.onload_counter = 0;
						var content = this.contentWindow ? this.contentWindow : this.contentDocument,
							body = content.document.body,
							text = (typeof body.innerText == 'undefined' ? body.textContent : body.innerText);
						func_cb(text, form);
					} catch (e) { Jajax._error_report({title:"AjaxError: send_file() : iframe["+div_id+"]._onload", error:e}); }
				}
			}
			
		} catch (e) { Jajax._error_report({title:"AjaxError: send_file()", error:e}); }
		return false;
	},
	
	/**
	*	convert simple link to ajax
	*	@param link - link DOM object
	*	@param elid - id DOM element to upload request to
	*	@param func_cb - callback js function
	*
	*/
	convert_link: function(link, elid, func_cb)
	{
		link.onclick = function(event)
		{
			Jajax.load_html(link.href, elid, 'in', func_cb, true);
			return false;
		}
	},

	/**
	*	@param class_arr - array of css classes (only these links will be processed)
	*	@param func_start_cb - callback function onRequest
	*	@param func_end_cb - callback function onResponse
	*	func_end_cb must return true, otherwise html would not be included (so func_end_cb can validate html and return false)
	*	func_end_cb sometimes should call Jajax.convert_class(classes, elid, func_cb) to process links in the loaded html!
	*/
	convert_class: function(classes, elid, func_start_cb, func_end_cb)
	{
		for (var i=0; i < document.links.length; i++)
		{
			var li = document.links[i];
			if (li.className && Jajax._find_in_arr(classes, li.className))
			{
				li.onclick = function(event)
				{
					if (typeof func_start_cb == 'function') func_start_cb(this);
					Jajax.load_html(this.href + (this.href.indexOf('?')==-1?'?in=1':'&in=1'), elid, 'in', func_end_cb, true);
					return false;
				}
			}
		}
	},

	/**
	*	@param attr - attribute name of the link
	*	@param func_start_cb - callback function onRequest
	*	@param func_end_cb - callback function onResponse
	*/
	convert_attr: function(attr, elid, func_start_cb, func_end_cb)
	{
		for (var i=0; i < document.links.length; i++)
		{
			// find attribute
			var attrs = document.links[i].attributes;
			var has_attr = false;
			if (typeof attrs[attr] != 'undefined')
				has_attr = true;
			else
				for (j in attrs)
				{
					if (typeof attrs[j] == 'object')
					if (attrs[j].name == attr)
					{
						has_attr = true;
						break;
					}
				}

			// process link
			if (has_attr)
			{
				var li = document.links[i];
				li.style.backgroundcolor='green';
				li.onclick = function(event)
				{
					if (typeof func_start_cb == 'function') func_start_cb(this);
					Jajax.load_html(this.href + (this.href.indexOf('?')==-1?'?in=1':'&in=1'), elid, 'in', func_end_cb, true);
					return false;
				}
			}
		} // for
	},

	_find_in_arr: function(arr, el)
	{
		for (var i in arr)
			if (arr[i] == el)
				return true;
		return false;
	}


};



String.prototype.replaceAll = function(search, replace)
{
	return this.split(search).join(replace);
}

