/* Создание нового объекта XMLHttpRequest для общения с Web-сервером */


function myEncodeURI(str) // {{{
{
	// Fckn IE eat my brain every day!!!
	var letters = [
			'а','б','в','г','д','е','ё','ж','з','и','й','к','л','м','н','о','п','р','с','т','у','ф','х','ц','ч','ш','щ','ъ','ы','ь','э','ю','я',
			'А','Б','В','Г','Д','Е','Ё','Ж','З','И','Й','К','Л','М','Н','О','П','Р','С','Т','У','Ф','Х','Ц','Ч','Ш','Щ','Ъ','Ы','Ь','Э','Ю','Я',
			' ', "\n"];

	var codes = [
		'%D0%B0','%D0%B1','%D0%B2','%D0%B3','%D0%B4','%D0%B5','%D1%91',
		'%D0%B6','%D0%B7','%D0%B8','%D0%B9','%D0%BA','%D0%BB','%D0%BC',
		'%D0%BD','%D0%BE','%D0%BF','%D1%80','%D1%81','%D1%82','%D1%83',
		'%D1%84','%D1%85','%D1%86','%D1%87','%D1%88','%D1%89','%D1%8A',
		'%D1%8B','%D1%8C','%D1%8D','%D1%8E','%D1%8F',
	
		'%D0%90','%D0%91','%D0%92','%D0%93','%D0%94','%D0%95','%D0%81',
		'%D0%96','%D0%97','%D0%98','%D0%99','%D0%9A','%D0%9B','%D0%9C',
		'%D0%9D','%D0%9E','%D0%9F','%D0%A0','%D0%A1','%D0%A2','%D0%A3',
		'%D0%A4','%D0%A5','%D0%A6','%D0%A7','%D0%A8','%D0%A9','%D0%AA',
		'%D0%AB','%D0%AC','%D0%AD','%D0%AE','%D0%AF',

		'%20', '%0A'
		]

	var res = str;
	for(var i = 0; i < letters.length; i++){
		res = replaceAll(res, letters[i], codes[i]);
	}
	return res;
} // }}}

function getXmlHttp() // {{{
{
	var xmlHttp = false;
	try {
		xmlHttp = new ActiveXObject("Msxml2.XMLHTTP");
	} catch (e) {
		try {
			xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
		} catch (e2) {
			xmlHttp = false;
		}
	}

	if (!xmlHttp && typeof XMLHttpRequest != 'undefined') {
		xmlHttp = new XMLHttpRequest();
	}
	return xmlHttp;
} // }}}

function TaskComplete() // {{{
{
	if(xmlHttp.readyState == '4' && ac_object){
		ac_object.TaskComplete();
	}
} // }}}

try{
	if(!ac_object) ac_object = null;
}catch(e){
	ac_object = null;
}

function AjaxController(){ // {{{

	if(ac_object) return ac_object;

	this.stack = new Array();
	this.current_task = false;
	this.error_log = new Array();
	if(!ac_object) ac_object = this;
	// TODO: Добавить возможность вывода ошибок для отладки

	this.AddTask = function(url, func, params, method, file_inputs, upload_url, on_error) // {{{
	{

		if(!method) method = 'GET';
		method = method.toUpperCase();
		if(method != 'POST') method = 'GET';

		var param_str = '';

		if(params && params.length){
			var i = 0;
//			url += '?';
			for(i = 0; i < params.length; i++){
//				url += params[i]['name']+'='+escape(params[i]['value'])+'&';
				if(typeof params[i]['value'] == 'string'){
					params[i]['value'] = replaceAll(params[i]['value'],'&','%26');
				}
				try{
					param_str += params[i]['name']+'='+(method == 'GET' ? encodeURI(params[i]['value']) : params[i]['value'])+'&';
				}catch(e){
					// Fckn IE eat my brain every day!!!
					param_str += params[i]['name']+'='+(method == 'GET' ? myEncodeURI(params[i]['value']) : params[i]['value'])+'&';
				}
			}			
		}
	
		num = this.stack.length;
		var task = new Array();
		task['num'] = num;
		task['url'] = url;
		task['func'] = func;
		task['async'] = true;
		task['method'] = method;
		task['state'] = 'open';
		task['params'] = param_str;
		if(on_error) task['on_error_func'] = on_error;
		this.stack[num] = task;
		if(file_inputs) this.UploadFiles(num, file_inputs, (upload_url || null) );
		this.CheckStack();
	} // }}}

	this.GetNextTask = function() // {{{
	{
	
		var num = -1;
		var i;
		for(i = 0; i < this.stack.length; i++){
		
			if(num == -1 && this.stack[i]['state'] == 'open'){
				num = i;
			}
		}
		if(num > -1){
			return this.stack[num];
		}else{
			return false;
		}
	} // }}}

	this.DoTask = function(task) // {{{
	{
		if(this.current_task !== false) return;

		if(!task) task = this.GetNextTask();
		if(!task) return;

		this.current_task = task['num'];
		this.stack[task['num']]['state'] = 'in_progress';
		var url = task['url'];
		var async = task['async'] ? true : false;
		var method = task['method'];
		var params = task['params'];

//alert('task No '+this.current_task+' open: method = "'+method+'", url = "'+url+'"');

		xmlHttp = getXmlHttp();
		if(method == 'GET'){
			xmlHttp.open(method, url+'?'+params, async);
		}else{
			xmlHttp.open(method, url, async);
		}
		xmlHttp.onreadystatechange = TaskComplete;
		if(method == 'GET'){
			xmlHttp.send(null);
		}else{
			xmlHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
			xmlHttp.send(params);
		}

	} // }}}

	this.TaskComplete = function() // {{{
	{

		num = this.current_task;
	

		if (xmlHttp.readyState == '4') {

			var task = this.stack[num];
	
			if(xmlHttp.status == 200){

//				res = xmlHttp.responseText;
//				res = res.replace("\n", ' ');
//				res = res.replace("\r", '');

				ajaxTempElement = document.createElement('textarea');
				ajaxTempElement.value = xmlHttp.responseText;
			
				func_str = task['func']+'(ajaxTempElement.value)';
				setTimeout(func_str, 1);
				
			}else{
				res = "Ошибка отправки данных!\n Номер ошибки: "+xmlHttp.status+";\n Текст ошибки: "+xmlHttp.statusText+";\n url = "+task['url'];
			//	alert(res);
//				this.error_log[this.error_log.length] = res;
				this.onError(num, res);
			}
			this.current_task = false;
			this.stack[num]['state'] = 'closed';
			this.CheckStack();
		}
	} // }}}

	this.CheckStack = function() // {{{
	{
	
		if(this.current_task !== false) return;
		next_task = this.GetNextTask();
		if(next_task === false) return;
		this.DoTask(next_task);
	} // }}}

	this.UploadFiles = function(task_num, file_inputs, upload_url) // {{{
	{
		var task = this.stack[task_num];
		if(task['state'] != 'open' || !file_inputs.length) return false;

		task['state'] = 'upload_files';

		var fl = new FileLoader(file_inputs, this, task_num, (upload_url || null) );
		return true;

	} // }}}

	this.FilesUploaded = function(task_num, result) // {{{
	{
		var task;
		if(task = this.stack[task_num]){

			for(var k in result){
				task['params'] += '&__files['+k+']='+result[k];
			}

			task.state = 'open';
			this.CheckStack();
		}
	} // }}}

	this.onError = function(task_num, error_text) // {{{
	{
		var task = this.stack[task_num];
		var default_hand = true;

		if(task['on_error_func']){
			try{
				AC_onerror_temp = document.createElement('textarea');
				AC_onerror_temp.value = error_text;

				eval(task['on_error_func']+'(AC_onerror_temp.value)');
			}catch(e){
			}
		}

		this.error_log[this.error_log.length] = error_text;

	} // }}}
	
	this.uploadError = function(task_num, result) // {{{
	{
//		this.error_log[this.error_log.length] = result;
		this.onError(task_num, result);
	} // }}}

} // }}}

/*
*	отладочная функция. вынести в dsm
*/
function print_r2(el, tostr){ // {{{

	var str = '';
	var i = 0;

	if(!tostr) alert(el);

	for(k in el){

		str += (tostr ? "\t" : '')+k+": "+(el[k] || '')+"\n";
		if(typeof el[k] == 'object' && !tostr){
//			str += print_r2(el[k], true);
		}
		i++;
		if(i > 30 && !tostr){
			alert(str);
			i = 0;
			str = '';
		}
	}
	if(tostr){
		return str;
	}else{
		alert(str);
	}
} // }}}





file_loaders = new Array;

function FileSendComplete(id){ // {{{

	if(file_loaders[id]){
		 file_loaders[id].Complete();
	}else{
		alert('no file loader found ("'+id+'")');
	}
} // }}}

function FileLoader(file_inputs, ac_object, task_num, upload_url){ // {{{

	this.file_inputs = file_inputs;
	this.ac_object = ac_object;
	this.task_num = task_num;

	this.iframe = false;
	this.form = false;
	this.container = false;
	this.status = 'prepare';
	this.id = 'upload_'+Math.floor(Math.random() * 99999);
	this.fields_cnt = 0;
	this.upload_url = upload_url ? upload_url : '/upload.htm';
	file_loaders[this.id] = this;

	this.createElements = function() // {{{
	{
		if(this.container && this.iframe && this.form) return true;

		var c_id = 'container_'+this.id;
		var f_id = 'form_'+this.id;
		var i_id = 'iframe_'+this.id;
		

		var div = document.createElement('div');
		div.id = c_id;
		div.style.display = 'none';
		div.innerHTML = '<iframe width="100%" src="about:blank" id="'+i_id+'" name="'+i_id+'" onload="FileSendComplete(\''+this.id+'\')"></iframe><form action="'+this.upload_url+'" target="'+i_id+'" id="'+f_id+'" method="POST" enctype="multipart/form-data"></form>';

		document.body.appendChild(div);

		this.container = div;
		this.iframe = document.getElementById(i_id);
		this.form = document.getElementById(f_id);

		return (this.container && this.iframe && this.form) ? true : false;
	} // }}}

	this.insertFields = function(fields) // {{{
	{
		if(!this.createElements()){
			alert('FileLoader : creation error');
			return 0;
		}
		var cnt = 0;
		for(var i = 0; i < fields.length; i++){
			if(typeof fields[i] == 'object' && fields[i].tagName == 'INPUT' && fields[i].type == 'file'){
				// танцы с бубном во имя великого и ужасного IE
				var tmp = fields[i].cloneNode(true);
				fields[i].parentNode.insertBefore(tmp, fields[i].nextSibling);
				this.form.appendChild(fields[i]);
				cnt++;
			}
		}
		return cnt;

	} // }}}

	this.sendForm = function() // {{{
	{
		this.status = 'upload';
		this.fields_cnt = this.insertFields(this.file_inputs);

		if(!this.fields_cnt){
			this.Complete();
		}else{
			this.form.submit();
		}
	} // }}}


	this.Complete = function(){ // {{{

		var error = false;

		var doc=this.iframe.contentDocument;
		var i = 1;
		if (!doc && this.iframe.contentWindow){
			doc=this.iframe.contentWindow.document;
			i = 2;
		}
		if (!doc){
			doc=window.frames[i_id].document;
			i = 3;
		}
		if (!doc) error = 'no document found';
		if (doc.location=="about:blank") error = 'no document location';
		if (doc.XMLDocument) doc=doc.XMLDocument;

		//print_r2(doc);

		var res = new Array;
		var arr = doc.body.innerHTML.split("|");

		if(arr[0] != 'upload ok'){ // ошибка

			if(this.ac_object){
				this.ac_object.uploadError(this.task_num, doc.body.innerHTML);
			}
		}else{ // успешно
			for(var i = 0; i < arr.length; i++){
				var parts = arr[i].split(':');
				if(!parts[1]) continue;
				res[parts[0]] = parts[1] || '';
			}
			this.status = 'complete';
			if(this.ac_object){
				this.ac_object.FilesUploaded(this.task_num, res);
			}
		}

		/*
		*	при убивании созданного iframe, индикатор загрузк страницы продолжает крутиться
		*/
	//	this.container.parentNode.removeChild(this.container);


	} // }}}


	this.sendForm();

} // }}}


/*
--- функции для облегчения работы с целыми формами ---
*/

/*
*	Собирает все поля формы и возвращает объект вида:
*	{
*		'params'	=> <массив параметров для AjaxController::AddTask()>,
*		'files'		=> <массив ссылок на поля с файлами для AjaxController::AddTask()>
*	}
*/
function CollectForm(frm) // {{{
{
	if(typeof frm == 'string') frm = document.getElementById(frm);
	if(!frm) return false;
	var params = new Array;
	var files = new Array;

	for(var i = 0; i < frm.elements.length; i++){
		var f = frm.elements[i];
		if(f.disabled) continue;

		switch(f.type){
			case 'file':
				files[files.length] = f;
				break;
			case 'select-one':
				params[params.length] = {'name' : f.name, 'value' : f.options[f.selectedIndex].value};
				break;
			case 'select-multiple':
				for(var j = 0; j < f.options.length; j++){
					if(f.options[j].selected)
						params[params.length] = {'name' : f.name, 'value' : f.options[j].value};
				}
				break;
			case 'checkbox':
			case 'radio':
				if(f.checked){
					params[params.length] = {'name' : f.name, 'value' : f.value};
				}
				break;
			default:
				params[params.length] = {'name' : f.name, 'value' : f.value};
				break;
		}
	}
//print_r2(params);
//print_r2(files);
	
	return {'params' : params, 'files' : files};

} // }}}

/*
*	Отправляет форму через AjaxController
*	@param	object			ссылка на экземпляр AjaxController-а
*	@param	string|object	указатель на форму (идентификатор или ссылка на объект)
*	@param	string			имя функции для полчения результата
*	@param	string			url получателя формы (если не указан берется из action формы)
*	@param	string			POST|GET метод отправки запроса, если не указан - берется из method формы
*	@param	string			путь для закачки файлов (по умолчанию = '<path2url_node>/upload.htm')
*	@return boolean
*/
function SendForm(ajax_controller, frm, result_func, url, method, upload_url, on_error) // {{{
{
	if(!ajax_controller) return false;
	if(typeof frm == 'string') frm = document.getElementById(frm);
	if(!frm) return false;
	
	if(!result_func) return false;
	if(!url) url = frm.action;
	if(!method) method = frm.method;
	if(method.toLowerCase() != 'post') method = 'get';
	if(!upload_url) upload_url = url.substr(0, url.lastIndexOf('/'))+'/upload.htm';


	form_data = CollectForm(frm);
	ajax_controller.AddTask(url, result_func, form_data.params, method, form_data.files, upload_url, on_error);
	return true;

} // }}}

/* vim: set foldmethod=marker ts=4 sw=4: */
