////////////AJAX THREAD MANAGEMENT///////////////
//CREATED BY Nate Weiner at CHARD.NET//

function cProcess(test) {
	
	///DEFAULT VARIABLES///
	this.threads = new Array();
	this.threadStatus = new Array();
	this.threadCnt = 0;
	//this.token = Math.round(100*Math.random());
	//this.tid = 'thread_' + this.token;
	this.tid = 'cThread_';
	this.barExists = this.IfcProcess();
	this.test = false;
	
	//Create Thread Layer
	this.threadsList = document.createElement('ul');
	if (!test) { 		
		//this.threadsList.style.display = 'none'; //Safari ignores iframe with no display
		this.threadsList.style.position = 'absolute';
		this.threadsList.style.overflow = 'hidden';
		this.threadsList.style.width = '1px';
		this.threadsList.style.height = '1px';	
	} else {
		this.test = test;	
	}
	document.body.appendChild(this.threadsList);
	
};

cProcess.prototype = {
	
	//////////QUICK PROCESSES///////////////
	/*
	process - Opens a new thread of the desired url quickly
	page - the url to open
	*/
	process : function (page, desc, onComplete) {
		thread = new Thread();
		thread.url = page;
		thread.desc = desc;
		thread.AddEvent('onComplete', onComplete);
		thread.process();
		return thread.id;
	},
	
	/*
	processForm - Opens a new thread of the desired form process quickly
	id - the id of the form to process
	*/
	processForm : function (formId, desc, onComplete) {
		thread = new Thread('form');
		thread.formId = formId;
		thread.desc = desc;
		thread.AddEvent('onComplete', onComplete);
		thread.processForm();
		return thread.id;
	},
	///////////////////////////////////////
	
	
	
	//////////GENERAL FUNCTIONS///////////////
	
	/*
	ReserveThread - Opens an available thread, and returns the id
	*/
	ReserveThread : function () {
		var threadId;
		for(i=0; i<this.threadStatus.length; i++) {
			if (!this.threadStatus[i]) {
				threadId = i;
				break;
			}
		}
		if (threadId >= 0) {	} else {
			if (this.threadStatus.length < 101) {
				threadId = this.CreateThread();
			} else {
				alert('The page has loaded over 100 processing threads, either you are working too quickly or there may be an infinite loop occuring.  Please wait a moment and try what you were doing again.  If you receive this error again, please report it to support@chard.net. Thank you.  Please note that if you were attempting to save changes, they may not have saved yet.');
			}
		}
		this.Lock(threadId);
		return threadId;
	},
	
	/*
	CreateThread - creates a new thread iframe
	id - the thread id of the thread to run in the newly opened frame
	*/
	CreateThread : function (id) {
		if (!id) { id = this.threadStatus.length; }
		var li = document.createElement('li');
		li.className = 'open';
		if (!this.test) { sty = ' style="width:0px;height:0px;border:0px;"'; } else { sty = ''; }
		
		li.innerHTML = '<iframe name="'+this.tid+id+'" id="'+this.tid+id+'"'+sty+' src="javascript:false;"></iframe>';		
		this.threadsList.appendChild(li);
		return id;
	},
	
	/*
	lock - sets a thread to reserved
	id - the id of the thread to lock
	*/
	Lock : function (id) {
		this.threadStatus[id] = true;
		this.threadCnt++;
	},
	
	/*
	ThreadFinished - Functions to run when the thread finishes, this includes reopening the frame
	id - thread's id
	*/
	ThreadFinished : function (id) {	
		if (this.threads[id]['events']['onComplete']) { eval(this.threads[id]['events']['onComplete']); }
		this.threads[id]['events'] = null;	
		
		this.threads.done = true;
		this.threadStatus[id] = false;
		this.threadCnt--;
		this.StopcProcessbar(id);
	},
	///////////////////////////////////////
	
	
	
	//////////cPROCESS BAR FUNCTIONS///////////////
	
	/*
	IfcProcess - Checks to see if there is a cProcess bar
	*/
	IfcProcess : function() {
		if ($('cProcess_threadList')) {
			return true;
		} else {
			return false;
		}
	},
	
	/*
	StartcProcessbar - If the cProcess bar is onsite, start it
	threadId - thread's id
	desc - Description of the process
	*/
	StartcProcessbar : function (threadId, desc) {
		if (this.barExists) {
			$('cProcess_anim').className = 'loading';
			if (!desc) { desc = 'Thread #'+threadId; }
			this.threads[threadId].desc = desc;
			
			list = $('cProcess_threadList');
			newItem = document.createElement('li');
			newItem.setAttribute('id','threadList_id_'+threadId);
			newItem.innerHTML = desc;
			list.insertBefore(newItem, list.firstChild);
			window.onbeforeunload = function() { if (this.threadCnt > 0) { return 'There are still processes running, to make sure all of your data is saved, wait until the red/yellow bar disapears under cProcess.  To see what processes are running click on cProcess.  If you are uploading a large file, this may take several minutes.  Click okay to leave this page, click \'cancel\' to wait for the processes to finish.'; } };
		}
	},
	
	/*
	StopcProcessbar - If the cProcess bar is onsite and no other processes are running, stop it
	threadId - thread's id to remove from the current running list
	*/
	StopcProcessbar : function (threadId) { 
		if (this.barExists) {
			if (this.threadCnt <= 0) {
				$('cProcess_anim').className = 'stopped';	
				if ($('threadList_id_'+threadId)) {
					 $('cProcess_threadList').removeChild($('threadList_id_'+threadId));
				}
			}
		}
	}
		
};





function Thread(type) {

	/*
	thread - Opens and returns a new thread object
	*/
	this.id = cProcess.ReserveThread();
	this.tid = cProcess.tid + this.id;
	this.events = new Array();
	this.desc;
	this.reserved;
	this.tid;
	this.url;
	this.formId;
	
	if (type) { this.type = type; } else { this.type = 'page'; }

};

Thread.prototype = {

	/*
	AddEvent - Adds an event to a thread
	*/
	AddEvent : function (type, action) {
		this.events[type] = action;
	},
	
	/*
	process - Runs the process
	*/
	process : function () {
		cProcess.threads[this.id] = this;
		frames[this.tid].location.href = this.AddIdToLink(this.id, this.url);			
		cProcess.StartcProcessbar(this.id, this.desc);			
		return this.id;	
	},
	
	/*
	AddIdToLink - Adds the thread id to the get string
	*/
	AddIdToLink : function (id, page) {
		if (page.match(new RegExp('\\?'))) {
			page = page + '&threadId='+id;
		} else {
			page = page + '?threadId='+id;
		}
		return page;
	},
	
	/*
	processForm - Runs the form process
	*/
	processForm : function () {
		cProcess.threads[this.id] = this;
		this.SaveThreadidToForm(this.formId, this.id); 	
		$(this.formId).target = this.tid;
		$(this.formId).submit();
		cProcess.StartcProcessbar(this.id, this.desc);			
		return this.id;	
	},
	
	/*
	SaveThreadidToForm - Saves the thread id to the form variables
	*/
	SaveThreadidToForm : function (formId, threadId) {
		//formObj = $(formId);
		if ($('threadId_'+formId)) {	} else {
			newInput = document.createElement('input');
			newInput.setAttribute('type', 'hidden');
			newInput.setAttribute('name', 'threadId');
			newInput.setAttribute('id', 'threadId_'+formId);
			$(formId).appendChild(newInput);
		}
		$('threadId_'+formId).value = threadId;
		/*if (form.elements['threadId']) {	} else {
			newInput = document.createElement('input');
			newInput.setAttribute('type', 'hidden');
			newInput.setAttribute('name', 'threadId');
			$(formId).appendChild(newInput);
		}
		form.elements['threadId'].value = threadId;*/
	}
	
};



/////////ANIM & SINGLE FIELDS///////
function AjaxFields() {
};
AjaxFields.prototype = {
	
	/*
	activate - Turns field into ajax field
	obj - The field object
	values - Additional vars to send along when saving
	required - If the field is required or not, and what type of requirement
	url - The processing url, if not the default
	varName - The field name, if not already defined by the field.name
	btnTxt - Text to show on the button, defaults to 'Save'
	animType - Type of animation, defaults to spinner	
	*/
	activate : function (obj, values, required, url, varName, btnTxt, animType) {
		AjaxField = null;
		AjaxField = new AjaxFieldClass();
		AjaxField.field = obj;
		AjaxField.name = UseElse(obj.name, varName);
		AjaxField.required = required;
		AjaxField.btnTxt = UseElse(btnTxt, 'Save');
		AjaxField.url = UseElse(url, 'process/fields/' + AjaxField.name + '.php');
		AjaxField.vars = '';
		AjaxField.animType = UseElse(animType, 'spinner');
		
		if (values) { AjaxField.setVars(values); }
		
		AjaxField.create();
		AjaxField.field.onblur = AjaxField.remove;
	},
	
	/*
	addSpinner - Adds a loading spinner next to the field
	obj - the field to put the spinner next to
	align - what side to put the spinner on, left or right ~~NOT ADDED YET~~
	*/
	addSpinner : function(obj, SpinnerID) {
		if (SpinnerID) {} else { SpinnerID = this.GenId('spinner'); }
		spinner = document.createElement('img');
		spinner.src = '/_jsclasses/cFrame/i/loading_spin.gif';
		spinner.setAttribute('id', SpinnerID);
		obj.parentNode.insertBefore(spinner, obj.nextSibling);
		return SpinnerID;		
	},
	
	/*
	GenId - Generates a random id
	prefix - puts a prefix in front of the id if given
	*/
	GenId : function (prefix) {
		randomId = Math.ceil((Math.random() * 1000000)) * 1;
		randomId = parseInt(randomId);
		id = prefix + randomId;
		return id;	
	},
	
	/*
	FinishSpinner - Finishes up and kills a spinner animation
	id - The spinner animations id
	msg - The saved! confirmation msg if not 'saved!'
	*/
	FinishSpinner : function (id, msg) {
		if (!msg) { msg = ' - Saved!'; }
		obj = $(id);
		spanId = this.GenId('spanMsg');
		obj.parentNode.insertBefore(this.AddSpanText(spanId, msg), obj.nextSibling);	
		removeNode($(id));
		this.FadeOutItem(spanId, 3);
	},
	
	AddSpanText : function (id, msg) {
		spinner = document.createElement('span');
		spinner.className = 'spanMsg';
		spinner.innerHTML = msg;
		spinner.setAttribute('id', id);
		return spinner;
	},
	
	FadeOutItem : function (spanId, time) {
		cEffect.opacity(spanId, 100, 0, time*1000);
		setTimeout('removeNode($("'+spanId+'"))', time*1000*1.01);
	}
};

function AjaxFieldClass() {
	this.field = '';
	this.name = '';
	this.required = false;
	this.btnTxt = 'Save';
	this.url = '';
	this.vars = '';
	this.animType = 'spinner';
};

AjaxFieldClass.prototype = {
	
	/*
	setVars - adds additional vars attached to the ajax button
	array - array of values (key is var name, value is value)
	*/
	setVars : function (str) {
		if (str) {
			array = str.split(',');
			for(key in array) {
				if (array[key]) {
					subarray = array[key].split('=>');
					if (this.vars.length>0) {
						this.vars += '&';	
					}
					this.vars += subarray[0] + '=' + subarray[1];
				}
			}
		}
	},
	
	/*
	create - Creates the button next to the field
	*/
	create : function() {
		newBtn = document.createElement('input');
		newBtn.setAttribute('id','ajaxfieldsavebtn');
		newBtn.setAttribute('type','button');
		newBtn.setAttribute('value',this.btnTxt);
		newBtn.onclick = this.save;
		this.field.onchange = this.save;
		this.field.parentNode.insertBefore(newBtn, this.field.nextSibling);
	},
	
	/*
	save - Saves the field
	*/
	save : function(e) {
		obj = GetTarget(e);
		id = obj.id;
		if (AjaxField.required && obj.value.length < 1) { 
			alert('This field cannot be left empty, please complete it.');
		} else {
			OnComplete = AjaxField.animate();
			cProcess.process(AjaxField.url + '?'+AjaxField.vars+'&'+AjaxField.name+'='+obj.value, 'Saving Field: '+AjaxField.name, OnComplete);
		}
	},
	
	/*
	animate - Figure out what type of animation to use, and then run it
	*/
	animate : function() { 
		if (AjaxField.animType) {
			switch(AjaxField.animType) {
				case('spinner'): 
					spinner = AjaxFields.addSpinner(AjaxField.field);
					return "AjaxFields.FinishSpinner('"+spinner+"')";
			}
		}
	},
	
	/*
	remove - Removes the button next to the field
	*/
	remove : function() {
		btn = $('ajaxfieldsavebtn');
		if (btn) {
			removeNode(btn);	
		}
	}

};



AjaxFields = new AjaxFields;


///New Ajax Model///
function Ajax() {}
Ajax.prototype = {
	
	GetXmlHttp : function () {
		{var ajax=null;try{ajax=new XMLHttpRequest();}
		catch(e){ajax=null;}
		try{if(!ajax)ajax=new ActiveXObject("Msxml2.XMLHTTP");}
		catch(e){ajax=null;}
		try{if(!ajax)ajax=new ActiveXObject("Microsoft.XMLHTTP");}
		catch(e){ajax=null;}
		return ajax;}
	},
	
	fire : function (uri, callback, parameters) {
		this.callback = callback;
		this.transport = this.GetXmlHttp();
		this.osc(this);
		this.transport.open("POST",uri,true);
		this.transport.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
		this.transport.send(parameters);
	},
	
	osc : function (ajaxObj) {
		ajaxObj.transport.onreadystatechange = function() {
			try {
				switch(ajaxObj.transport.readyState) {
					case(4): ajaxObj.callback(ajaxObj.transport.responseText);
				}
			}
			catch( e ) { //server error
			}
		}	
	},
	 
	fireForm : function (id, callback) {
		if ($(id).getAttribute('enctype') == 'multipart/form-data') {
			alert('Use cProcess for file transfers');
		} else {
			this.fire( $(id).action, callback, this.formParse( id ) );		
		}
	},
	
	formParse : function (id) {
		str = '';
		ins = $(id).getElementsByTagName('input');
		for(i=0; i<ins.length; i++) {
			if(ins[i].type == 'checkbox'|'radio') {
				if (ins[i].checked) { str += ins[i].name + '=' + ins[i].value + '&'; }				
			} else {
				str += ins[i].name + '=' + ins[i].value + '&';
			}
		}
		ins = $(id).getElementsByTagName('textarea');
		for(i=0; i<ins.length; i++) {
			str += ins[i].name + '=' + ins[i].value + '&';
		}	
		ins = $(id).getElementsByTagName('select');
		for(i=0; i<ins.length; i++) {
			if (ins[i].getAttribute('multiple')) { 
				str += ins[i].name + '='
				for(j=0; j<ins[i].options.length; j++) {
					ins[i].options[j].value + '|';
				}
				str.substr(0,str.length-1);
				str += '&';
			} else {
				str += ins[i].name + '=' + ins[i].options[ins[i].selectedIndex].value + '&';
			}
		} 
		return str.substr(0,str.length-1);	
	}
	
}
