/**
Ajax library, to be used in conjunction with Ajax_Dispatcher

This script has two main purposes.
1) Be able to send out ajax requests in an orderly fashion [queuing],
	with dynamic parameters, and error/status message capability
2) Receive Ajax responses [specially formatted according to framework
	specs, that is, receive Ajax_Actions] and parse them, allowing
	custom handlers to come into play.

Features of ajax.js:
- Ajax queue which supports any feasible use
- XHTML/Ajax binding: bind ajax events to elements seamlessly
- Custom Ajax Handlers: you can define your own ajax handling functions
  and still get all the perks of the framework
- Debugging and error handling, to keep javascript running

Right now, uses JQuery to send and receive the AJAX... can be changed to use whatever.
Depends on JQuery for XHTML/Ajax binding, and some of the default handlers

Author: Sam Mati
Date: 6/06 - 4/07
*/

ajaxQueue = new Array();	//global ajax queue
ajaxErrors = new Array();	//global ajax error holder
ajaxHandlers = new Array();

/**
  Bind ajax to events on certain elements, and set up ajax status.
*/
$( function(){
	bindAjaxEvents();



	//status.
	$(document).ajaxStart(function(){
		ajaxErrors = new Array();

		//todo: change this.
		if ($('#ajax_status').length){
			//empty the ajax status message to prepare it for the next call
			$('#ajax_status').empty();

			//add an ajax message TODO: make this dynamic based on ajax call
			$('#ajax_status').append('zootoo is updating your page...');

			//center ajax status element width based on body size
			centerElem("#ajax_status");

			//center ajax status element height based on 1/2 page height - 1/2 status element height plus the amount scrolled down the page
			$('#ajax_status').css('top', ((getPageH() / 2) - ($('#ajax_status').height() / 2) + getScrollXY()[1]) + 'px');

			//display ajax status element (cannot use jquery show() method, as we need the width to center the item before display)
			//$.dimScreen(400, 0.01);
			$("#ajax_status").css('visibility','visible');

		}
	}).ajaxStop(function(){
		//what errors have we collected from ajax since we started?
		for (var i=0; i<ajaxErrors.length; i++){
			var error = ajaxErrors[i];
			var exception = error.exception;
			var message = error.message;

			if (showErrors) {
				if (exception) {
					alert(message + "\n" + exception.name + "\n" + exception.message);
				}else{
					alert(message + "\n (There was no exception associated with this error.)");
				}
			}
			//do whatever you want with the error here
		}

		if ($('#ajax_status').length){
			//hide the ajax status message and hide the dimmed screen display
			//$.dimScreenStop();
			$("#ajax_status").css('visibility','hidden');
		}
	});
});


var bindAjaxHandler = function(name, fn){
	ajaxHandlers[name] = fn;
}

var unbindAjaxHandler = function(name){
	ajaxHandlers[name] = null;
}

/**
  Binds events to any elements with "ajaxcall" attribute defined.
  Also makes sure that elements arent double bound.
*/
var bindAjaxEvents = function(){
	//function that gets run when an element wants to execute ajax.
	var doAjax = function(element){
		var me = $(element);

		//get the ajaxcall, if any
		var ajaxcall = me.attr("ajaxcall");
		if (!ajaxcall) return;	//leave 'this' alone.

		//evaluate the parameters
		var ajaxparams = me.attr("ajaxparams");
		var ajaxasync = me.attr("ajaxasync");
		var ajaxid = me.attr("ajaxid");

		ajax(ajaxcall, ajaxparams, ajaxasync, ajaxid);
	}


	//bind events to each element that needs it.
	$('[@ajaxcall]').each(function(){
		//clear all Ajax binds to prevent double calls.
		$(this).unbind(function(){ doAjax(this); });

		var ajaxevents = $(this).attr("ajaxevents");
		if (ajaxevents) ajaxevents = ajaxevents.split(",");
		else ajaxevents = ["click"];

		for (var i=0; i<ajaxevents.length; i++) $(this).bind(ajaxevents[i], function(){ doAjax(this); } );
	});
}


/**
  Use this method to call ajax, instead of JQuerie's $.ajax() --
  This uses the queue and handles other stuff thats important.
  This is also the function to be called from "stray" calls, that is,
  calls that weren't automatically bound to elements.
*/
var ajax = function(call, params, id, async){
	params = "ajaxcall=" + call + "&" + params;
	enqueueAjax({
		id: id,
		data: params,
		async: async,
		url: "/ajax.php",
		type: "POST",
		success: function(response) {
			handleAjax(response);
			shiftAjaxQueue();
		},
		error: function(a,b,c) {
			ajaxErrors.push({
				"exception" : c,
				"message": "Ajax call failed. (non 200 response)"
			});
			shiftAjaxQueue();
		}
	});
}



/**
	Handles JSO that is returned from server.
	Here is where the commands are interpreted and executed.
*/
var handleAjax = function(response){
	//try to eval the response (it might not be a valid JSON object)
	try {
		eval("response = eval(" + response + ");");  //JSON?
		response["commands"].length; //roughly check formatting
	}catch (e){
		if (response.match("Fatal error:")) response = "\n--FATAL ERROR DETECTED! LOOK AT BOTTOM OF RESPONSE--\n\n" + response;
		ajaxErrors.push({
			"exception" : e,
			"message" : "Invalid response received from server (not a JSON object, or incorrect formatting)\n" + response
		});
		return;
	}

	var processCommandQueue = function(commands){
		//shift the queue.
		var command = commands.shift();
		if (!command) return;

		try {//Trap any errors and continue to next command.
		 //TODO:  put try/catch block around each different command type
		 //		  for maximal error control and debugging.  this is fine
			switch (command.type) {
				case "pause":
					var time = command.time;
				    d = new Date() //today's date
				    while (1){
				        mill=new Date() // Date Now
				        diff = mill-d //difference in milliseconds
				        if( diff > time ) {break;}
				    }
					break;
				case "replace":
					var id = command.id;
					var temp = parseOutJS(command.html);
					var html = temp[0];
					var js = temp[1];
					var animate = command.animate;

					var oldObj = $("#"+id);
					if (!oldObj.size()) return;
					//var newObj = $(html);  too slow in IE -- old skool:
					var newObj = $(createElement(html));
					oldObj[0].id = null;  //we do this so safari maps id to newObj

					if (animate){
						oldObj.DropOutLeft(500, function(){
						   newObj.css('opacity','.01');
						   oldObj.after( newObj ).remove();
						   newObj.DropInRight(500);
						});
					}else{
						oldObj.after( newObj ).remove();
					}

					//execute the javascripts
					executeJS(js);

					try { initialize(newObj); } catch (e) {}
					break;
				case "executejs":
					var js = command.js;
					eval(js);
					break;
				case "forward":
					var url = command.url;
					window.location.href = url;
					break;
				case "refresh":
					var regexp = /\?lbox=[0-9a-z]*/ig;
					if (regexp.test(window.location.href)) {
						window.location.href = window.location.href.replace(regexp, '?');
					} else {
						window.location.reload(true);												
					}
					break;
				case "showmodal":
					var modalWidth = command.modalWidth;
					var showCloseButton = command.showCloseButton;
					var title = command.title;
					var temp = parseOutJS(command.body);
					var body = temp[0];
					var js = temp[1];

					showPopUp(title, body, modalWidth, showCloseButton);
					executeJS(js);

					//this is important, it runs any jquery and js
					//needed on the DOM inside the TB
					try { initialize($('#AjaxModalBody')); } catch (e) {}
					break;
				case "closemodal":
				    killPopUp();
					break;
				case "alert":
					var content = command.content;
					alert(content);
					break;
				case "notify":	// inline notification
					var content = command.content;
					var sticky = command.sticky;
					$('#notice').html(content);	// update notice div on chrome
					displayNotice(sticky); //display the posted notice
					break;
				case "handler":	//pass jso to callback
					var jso = command.jso;
					var handlerName = command.handlerName;
					command.type = handlerName;
					ajaxHandlers[handlerName](jso);
					break;
				case "nothing":
					break;
				case "gooanal":
					pageTracker._trackPageview(command.path);
					break;
				default:
					ajaxErrors.push({
						"exception" : new Error("Command not yet implemented"),
						"message" : "Command type: " + command.type + " not yet implemented."
					});
			}
		}catch (e){
			ajaxErrors.push({
				"exception" : e,
				"message" : "Error parsing command of type: " + command.type
			})
		}
		setTimeout(function(){ processCommandQueue(commands); }, "0");
	}

	processCommandQueue(response["commands"]);
}

function showPopUp(title, body, modalWidth, showCloseButton){
//for now set up to only make 1 popup
//could be setup to make as many as you want
	killPopUp();

	$('object').css('visibility', 'hidden');
	$('embed').css('visibility', 'hidden');
	$('select').css('visibility', 'hidden');

	//this causes jquery error if no matches
	//try{ $('#col2map #complete_list .search-results').css('overflow-y', 'hidden'); }catch(e){}

	var MrBox = document.createElement("div");
	MrBox.id = "AjaxModal";
	MrBox.innerHTML = "<div id='AjaxModalTitle'>" + title + "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</div>";
	if (showCloseButton) MrBox.innerHTML += "<div id='AjaxModalCloseButton' onclick='killPopUp()'>&nbsp;</div>";
	MrBox.innerHTML += "<div id='AjaxModalBody'>" + body + "</div>";
	document.body.appendChild(MrBox);

	//center popup display (sort of)
	var width = document.body.offsetWidth;
	var height = document.body.offsetHeight;
	MrBox.style.visibility = "hidden";
	MrBox.style.zIndex = 30102;
	MrBox.style.width = modalWidth;
	MrBox.style.display = "block";
	MrBox.style.position = "absolute";

	if(self != top){ // if in an iframe
		MrBox.style.left = 0;
		var scrollTop = document.body.offsetHeight / 2;
		MrBox.style.top = scrollTop - 100 + "px";
		$('#AjaxModal').ready(function(){
			document.location = document.location + '#' + MrBox.id;
		});
	}else{
		MrBox.style.left = (width - MrBox.offsetWidth)/2 + "px";
		var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
		MrBox.style.top = scrollTop + 100 + "px";
	}
	
	MrBox.style.background = 'white';
	MrBox.style.visibility = 'visible';

        var MrCover = $("<div style='background: black;' id='MrCover'></div>");
        MrCover.css("opacity", .75);
        MrCover.width("100%");
        MrCover.css("height", document.documentElement.scrollHeight || document.body.scrollHeight)
        MrCover.css("position", "absolute");
        MrCover.css("top", "0px");
        MrCover.css("left","0px");
        MrCover.css("z-index","30000");

	MrCover.appendTo(document.body);
	
	if (showCloseButton) {
		jQuery('#AjaxModal').bind('keypress', function(event) {
			var code = event.charCode || event.keyCode;
			if(code && code == 27) {// if escape is pressed
	  			killPopUp();
			};
		});
	}

	var form = $("form", MrBox).get(0);
	if (form){
		var elements = form.elements;
		for (var i=0; i<elements.length; i++){//for each element
			var e = elements[i];
			if (e.type=="text" || e.type=="textarea") {
				$(e).focus();
				break;
			}
		}
	}
}

function killPopUp(){
	var AjaxModal = document.getElementById('AjaxModal');
	if (AjaxModal) document.body.removeChild(AjaxModal);

	var MrCover = document.getElementById('MrCover');
	if (MrCover) document.body.removeChild(MrCover);

	$('object').css('visibility', 'visible');
        $('embed').css('visibility', 'visible');
	$('select').css('visibility', 'visible');

	//this causes jquery error if no matches
	//try { $('#col2map #complete_list .search-results').css('overflow-y', 'scroll'); }catch(e){}
}

function keyPressHandler(e) {
	var kC  = (window.event) ? event.keyCode : e.keyCode;
	var Esc = (window.event) ? 27 : e.DOM_VK_ESCAPE // MSIE : Firefox
	if(kC==Esc) {
		killPopUp();
	}
}

//evaluates any javascript in a string, then returns the string without js.
//this is for cross browser compat when inserting strings into DOM.
var prepareForDOM = function(s){
	var re = /(?:<script.*?>)((\n|.|\r)*?)(?:<\/script>)/img;
	var scripts = s.match(re);
	s = s.replace(re,'');
	re = /(?:<script.*?>)((\n|.|\r)*?)(?:<\/script>)/im;
	if (scripts) for (var i=0; i<scripts.length; i++) {
		var script = scripts[i].match(re)[1];
		if (window.execScript) window.execScript( script );
		else window.setTimeout( script, 0 )
	}
	return s;
}

//removes all javascript from a string
//returns an array of the scripts, and the string without the script
var parseOutJS = function(s){
	var re = /(?:<script.*?>)((\n|.|\r)*?)(?:<\/script>)/img;
	var scripts = s.match(re);
	s = s.replace(re,'');
	re = /(?:<script.*?>)((\n|.|\r)*?)(?:<\/script>)/im;
	if (scripts) for (var i=0; i<scripts.length; i++) scripts[i] = scripts[i].match(re)[1];
	return [s, scripts];
}

//evals an array of javascript strings in cross browser way.
var executeJS = function(scripts){
	if (scripts) for (var i=0; i<scripts.length; i++) {
		var script = scripts[i];
		if (window.execScript) window.execScript( script );
		else window.setTimeout( script, 0 )
	}
}

//create element
var createElement = function(html){
	var newObj = document.createElement("div");
	newObj.innerHTML = html;
	return newObj.firstChild;
}

/*
	Use these functions to ensure ajax happens in order
*/
var enqueueAjax = function(ajax){
	//do it asynchronously if required
	if (("" + ajax.async).toLowerCase()=="true".toLowerCase()) {
		$.ajax(ajax);
		return;
	}else ajax.async=true;

	//if it has an id, find and replace
	if (ajax.id) for (var i=1; i<ajaxQueue.length; i++){
		if(ajaxQueue[i].id==ajax.id){
			ajaxQueue[i] = ajax;
			return;
	}}

	//push onto queue and jumpstart if needed
	ajaxQueue.push(ajax);
	if (ajaxQueue.length==1) $.ajax(ajaxQueue[0]);
}
var shiftAjaxQueue = function(){
	//remove head of queue and continue if possible
	ajaxQueue.shift();
	if (ajaxQueue.length>0) $.ajax(ajaxQueue[0]);
}


//parseForm
function parseForm(form){
	if (typeof form == 'string') form = document.getElementById(form);
	var str = "";
	var elements = form.elements;
	var names = new Array();
	var values = new Array();
	var index = 0;

	var getValue = function(e){
		if (e.type=="text" || e.type=="hidden" ||
		e.type=="password" || e.type=="textarea" ||
		e.type=="select-one") return e.value;
		else if (e.type=="checkbox" && e.checked) return e.value;
		else if (e.type=="radio" && e.checked) return e.value;
		else return false;
	}

	for (var i=0; i<elements.length; i++){//for each element
		var e = elements[i];
		if (!e.name) continue;
		var val = getValue(e);
		if (val===false) continue;
		names[index] = e.name;
		values[index++] = encodeURIComponent(val);
	}

	for (var i=0; i<names.length; i++){
		str = str + "&" + names[i] + "=" + values[i];
	}

	return str.substring(1, str.length);
}