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

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();
	},

	last_requested_html: null,

	// @param url of the server script
	// @param elid is id of the DOM node
	// @param op - (in|pre|post|fade) - how to include and vizualize new downloaded html
	// @param options.cb - callback js function(html_str); must return true, otherwise further data procession will be stopped
	// @param options.params = {param_name:param_value, ...}
	// @param options.req_type - 'GET', 'POST'
	// @param options.allow_script - eval javascripts (some scripts will be injected in the Jajax.load_html() scope, so won't be seen outside)
	// @param options.allow_in_row - if true, this optimization forbiddens to make the same request many times in row (default: true)
	load_html: function(url, elid, op, options)
	{
		if (!options) options = {};

		if (options.allow_in_row === false)	// this optimization forbiddens to make the same request many times in row
		{
			var request_str = url+'|'+elid+'|'+op+'|'+options.cb;
			if (Jajax.last_requested_html == request_str)
				return;
			Jajax.last_requested_html = request_str;
		}

		var JajaxLoadHTMLFunc = function(str)
		{
			try
			{
				/*
				if (typeof(options.cb) == 'function')
					if (!options.cb(str))
						return;
				*/
				switch (op)
				{
					case 'pre':
						Jajax.get(elid).innerHTML = str + Jajax.get(elid).innerHTML;
						Jajax.onPageUpdate(url);
					break;
					case 'post':
						Jajax.get(elid).innerHTML += str;
						Jajax.onPageUpdate(url);
					break;
					case 'fade':	// jquery is required
						// this effect will be added to the queue, so if there are other effects, page show will be deleted
						var jobj = $('#'+elid);
						jobj.fadeOut('normal', function(){
								jobj.html(str);
								Jajax.onPageUpdate(url);
								jobj.fadeIn('normal');
							});
					break;
					//case 'in':
					default:
						Jajax.get(elid).innerHTML = str;
						Jajax.onPageUpdate(url);
					break;
				}
				//var scripts = new RegExp('<script(\s(type|charset|defer|language)="[^"]*")*>[\s\S]*?</script[^>]*>', "igm").exec(str);

				if (options.allow_script == true)
					Jajax.include_javascript_from_html(str);

				if (typeof(options.cb) == 'function')
					options.cb(str);
			}
			catch (e) { Jajax._error_report({title:"AjaxError: JajaxLoadHTMLFunc()", response:str, error:e}); }
		};
		Jajax.request(url, JajaxLoadHTMLFunc, options.params, options.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
	*	@param form - form (DOM object) to process
	*	@param func_cb - callback function(text, form)
	*	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+'" onload="javascript:this._onload()"></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()	// in IE you can't add an onload handler dynamically
			{
				//iframe.onload_counter++;
				//alert(iframe.onload_counter);	// opera: twice

				try {
					var content = this.contentWindow ? this.contentWindow : this.contentDocument;
					var body = content.document.body;
					text = (typeof body.innerText == 'undefined' ? body.textContent : body.innerText);
					func_cb(text, form);
				} catch (e) { alert(e); Jajax._error_report({title:"AjaxError: send_form() : iframe["+div_id+"]._onload", error:e}); }
				/*
				if (iframe.onload_counter == 0)
				{
					//if (auto_submit)
						//form.submit();
					iframe.onload_counter = 1;
				}
				else
				{
					try {
						var content = this.contentWindow ? this.contentWindow : this.contentDocument;
						var body = content.document.body;
						text = (typeof body.innerText == 'undefined' ? body.textContent : body.innerText);
						func_cb(text, form);
						iframe.onload_counter = 0;
					} catch (e) { alert(e); Jajax._error_report({title:"AjaxError: send_form() : iframe["+div_id+"]._onload", error:e}); }
				}
				*/
			}
			
		} catch (e) { Jajax._error_report({title:"AjaxError: send_form()", error:e}); }
		return false;
	},
	
	_find_in_arr: function(arr, el)
	{
		for (var i in arr)
			if (arr[i] == el)
				return true;
		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, op)
	{
		link.onclick = function(event)
		{
			Jajax.load_html(link.href, elid, op, func_cb, true);
			return false;
		}
	},

	/**
	*	@param classes - array of css classes (only these links will be processed)
	*	@param url_base - url_base will be deleted from link's href to create short hash
	*	@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, url_base, elid, func_start_cb, func_end_cb, op)
	{
		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);
					var hash = 'show/' + this.href.replace(url_base,'');
					//.replace('index.php?jq=',''); - if no Juman's .htaccess, better not to replace it
					Jajax.history_add(hash, elid, func_end_cb, op);

					/*
					var href = this.href + (this.href.indexOf('?')==-1?'?in=1':'&in=1');
					Jajax.load_html(href, elid, op,
						function()
						{
							if (func_end_cb) func_end_cb();
						}, true);
					*/
					return false;
				}
			}
		}
	},

	// Init browser history manager
	// onHistoryEvent(hash, type) callback function is called:
	// 1. after calling $.historyInit();				('type' argument is 'init')
	// 2. after calling $.historyLoad();				('type' argument is 'load')
	// 3. after pushing "Go Back" button of a browser	('type' argument is 'history')
	// 'hash' argument of the callback function doesn't contain the first # character and without '#show' prefix (such prefix specificies that it is ajax hash)
	set_onHistoryEvent: function(onHistoryEvent_cb)
	{
		// initialize history plugin
		jQuery.history.init(function(hash, type)
		{
			if (hash)
			{
				if (!Jajax.is_history_hash('#'+hash))
					return;
				hash = hash.replace('show', '');
			}
			onHistoryEvent_cb(hash, type);
		});
	},

	history_add: function(hash, elid, func_end_cb, op)
	{
		jQuery.history.load(hash); // can pack all needed parameters to 'params' array, so onHistoryEvent() could receive them
	},
	
	// it is Jajax-history hash or simple hash? hash begins with '#'
	is_history_hash: function(hash)
	{
		return hash.indexOf('#show/') == 0;
	},

/*
	onHistoryEvent: function(params)
	{
		// hash doesn't contain the first # character.
		if (params.hash)
		{
			// restore ajax loaded state
			//alert(hash);
			Jajax.load_html(
				params.hash + (params.hash.indexOf('?')==-1?'?in=1':'&in=1'),
				params.elid,
				params.op,
				params.cb,
				false);
		}
		else
		{
			// start page
		}
	},
*/

	/*
	Jajax.onPageUpdate() is called when page is loaded (initial call inside set_onPageUpdate) 
		and when html is loaded by ajax (called inside Jajax.load_html)
	It helps to apply common JavaScript transformations for all html on all pages
	Example:
		Jajax.set_onPageUpdate(function(url, update_count)
		{
			Jajax.convert_class(
				['ajax'],
				'http://www.example.com/'
				'content',
				null,null,
				'fade');

			if (typeof set_keyevent_handler == 'function')	// friendly to set_keyevent_handler(): no bubble for keypress keypress inside forms
				set_keyevent_handler();
		});
	*/
	onPageUpdate_func: null,
	PageUpdate_count: 1,
	set_onPageUpdate: function(cb)
	{
		Jajax.onPageUpdate_func = cb;
		cb( null, Jajax.PageUpdate_count++ );	// cb(1);
	},
	onPageUpdate: function(url)
	{
		if (Jajax.onPageUpdate_func)
			Jajax.onPageUpdate_func( url, Jajax.PageUpdate_count++ );
	}


};



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


