/**
* COPYRIGHTED SOFTWARE
* PLEASE READ THE LICENSE AGREEMENT
*
* @copyright		Copyright 2008-2010 Dilltree Inc.
* @package			tPantherPro
* @license 			http://dilltree.com/license/tPantherPro/2.0
* @author			Jeremy Dill
*
**/
// JCALL - AJAX CALL AND RESPONSE HANDLER
// @author : Jeremy Dill
// @ver : 1.6
// @param [str] func : php jCall function to execute
// @param [array] params : array of parameters ( "value1","value2" )  to be passed to the jCall function
// @param [obj/selector] opt : set of options for the ajax call.  Supported options include:
// 			[str] url : path to ajax.php, the entry point for ajax call
//			[bool] setWait (true) : if true, _PI.wait will be set to true before call, and will be set to false on response.  Global synchronous calling if you check _PI.wait before each request.
//			[string] mode - (assign) OPTIONAL FUNCTION CALL INSTEAD OF ASSIGNING NEW CONTENT TO TARGET, YOU CAN 'append','prepend,'after','before' OR 'replaceWith' ON THE TARGET SELECTOR
//			[bool] rapidFire (false) : if false, subsequent duplicate requests to the same func & params will be prevented and allowed only after response is completed.  Singular synchronous calling.
//			[bool] jsonly (false) : only return javascript...no xhtml, no target
//			[bool] scall (false) : pass session _PI.sess with request
//			[bool] layers (false) : pass _PI.layerid with request (current active layer)
//			[bool] noloader (false) : disable ajax loader (spinner)
//			[bool] persistLoader (false) :  keep loader (spinner) after returning response.
//			[int] timeBeforeGivingUp (10000): ms time to wait for response before giving up and throwing an error.
//			[func] timeoutAction (alert): function to run on timeout
//			[func] errorAction (alert): function to run on error
//			[func] precall (): function to run before ajax call.
//			[func] onSuccess (): function to run on on ajax success but before processing responses.  Return true to continue processing response or false to prevent futher processing.
//			[func] callback (): function to run on ajax success and after processing responses.
//			[func] onComplete (): final function to run on on ajax complete after all callbacks and response processing.
//			[obj] loaderOpts : set of options for loader.  Can clone loader object and reuse.
//				[int] delay (200) : ms time to wait after request before showing loader.
//				[int] transSpeed (10) : ms time for fadin and fadeout of loader.
//				[selector] sel ("#smallLoader") : ID selector for ajax loader graphic that gets placed next to target anchorpoint on page (don't use a class selector!)
//				[obj/selector] ap ("body") : specifies the object to place the loaderSel next to.  Optionally, use "where" to use the 'where' param[1] of servUp. Can also be a function returning a selector.
//				[str] loc ("top-right") : where to mount loader on ap.  options are based on placer plugin.	 Accepted presently: top-left, top-right, bottom-left, bottom-right, off-left, top-center
//				[obj] offset ({0,0}) :  x and y offsets for loaders.
// OPTIONALLY, opt can just be selector that then gets the apLoader treatment. This will use the default jcallLoader opts and build a 'spinner' next to the selector.
// @RESPONSE - JSON MULTI DIMENSIONAL ASSOC ARRAY, EACH DIMENSION MAY HAVE 4 PRIMARY KEYS :
//  'target' - A LOCAL JQUERY OBJECT "target" WHICH IS GOING TO GET THE RETURNED XHTML RESPONSE.
//			   TYPICALLY A SINGLE DIV OR BLOCK ELEMENT BUT CAN BE ANY ELEMENT OR JQUERY COLLECTION OF ELEMENTS (EVEN TABLE ROWS!)
//	'xhtml' - ANY HTML TO BE RENDERED TO EACH RESULT OF "target"
//	'js' - (REQUIRED) ANY JS TO BE EVAL AFTER XHMTL HAS BEEN RENEDERED.
// 	'linkJs' - ARRAY OF JS FILES TO BE LINKED TO THE PAGE (IF NOT ALREADY LINKED).
// 	'mode' - OPTIONAL FUNCTION CALL INSTEAD OF ASSIGNING NEW CONTENT TO TARGET, YOU CAN 'append','prepend,'after','before' OR 'replaceWith' ON THE TARGET SELECTOR
//  'kldr' - USE ALONG WITH 'persistLoader' OPTION TO CONTROL ON/OFF LOADER.  SET $resp['kldr']=1 IN FIRST RESPONSE TO KILL PERSISTANT LOADER
//
//  NOTE: FOR XHTML TO BE USED, YOU MUST RETURN THE 'target' JQUERY OBJECT.
//		 FOR EXAMPLE, IN PHP - $response['target']="target=jQuery("#mydiv");";
//		 IF NO TARGET IS INCLUDED IN THE RESPONSE, ONLY THE 'js' WILL EXECUTE.
//
// SET _PI.DEBUG=TRUE FOR DEBUG MESSAGES IN THE CONSOLE.
// SET OF DEFAULT OPTIONS FOR LOADER.  I RECOMMEND THAT YOU CUSTOMIZE THIS OBJ IN YOUR OWN SCRIPT. THEN FOR ALL CALLS, CLONE THE OBJECT, EXTEND THE CLONE, THEN PASS TO JCALL
(function($){
panther.prototype.wait=false;
panther.prototype.jcallLoader = makeClass();
panther.prototype.jcallLoader.prototype={
		delay:200,
		transSpeed:10,
		sel:'#smallLoader',
		ap:"body",
		loc : "TLCR",
		offset:{offsetX:0,offsetY:0},
		init:function(options){
			jQuery.extend(this,options);
		}
};
// SET OF DEFAULT OPTIONS FOR JCALL.  I RECOMMEND THAT YOU CUSTOMIZE THIS OBJ IN YOUR OWN SCRIPT. THEN FOR ALL CALLS, CLONE THE OBJECT, EXTEND THE CLONE, THEN PASS TO JCALL
panther.prototype.jcallOpts = makeClass();
panther.prototype.jcallOpts.prototype={
		url:"/",
		setWait:true,
		rapidFire:false,
		mode:'assign',
		jsonly:false,
		scall:false,
		layers:false,
		noloader: false,
		persistLoader: false,
		timeBeforeGivingUp:10000,
		precall:function(){},
		callback:function(){},
		onSuccess:function(){return true},
		onComplete:function(){},
		timeoutAction: function(reqestObj){alert("Sorry, it is taking too long for a response.  Please try again");},
		errorAction: function(reqestObj){if(!_PI.debug) alert("Sorry, an error has occurred.  Please contact an administrator.");},
		init:function(options){
			this.loaderOpts=new _PI.jcallLoader();
			jQuery.extend(this,options);
		}
};
// SET UP ANCHOR POINT JCALL LOADER OPTIONS
panther.prototype.apLoader=function(apsel,o) {
		var jpl=_PI.jcallLoader();
		o = jQuery.extend({
			delay:jpl.delay,
			transSpeed:jpl.transSpeed,
			sel:jpl.sel,
			loc:jpl.loc,
			offset:jpl.offset,
			offsetX:null,
			offsetY:null
		}, o);
		if (o.offsetX) o.offset.offsetX=o.offsetX;
		if (o.offsetY) o.offset.offsetY=o.offsetY;
		if (o.offset==jpl.offset){
			o.offset={offsetX:5}
		}
        aLoad = $.extend({},_PI.jLoader,{ offset: o.offset, loc: o.loc, ap: apsel, sel: o.sel, transSpeed:o.transSpeed , delay: o.delay });
        return $.extend({},_PI.jOpts,{ loaderOpts: aLoad });
};
panther.prototype.jcallHandleResp=function(resp,func,params){
	if(undefined==func) func="";
	if(undefined==params) params=[];
	for ( var key in resp ) {
		if (_PI.debugResp) {
			_PI.cs("RESPONSE #"+key+" "+func+" "+params[0]+" "+params[1]);
			if(resp[key]['target']!=undefined) _PI.cs(key+"->TARGET\n"+resp[key]['target']);
			if(resp[key]['mode']!=undefined) _PI.cs(key+"->MODE\n"+resp[key]['mode']);
			if(resp[key]['kldr']!=undefined) _PI.cs(key+"->KILL LOADER\n"+resp[key]['kldr']);
			if(resp[key]['linkJs']!=undefined) _PI.cs(key+"->LINKS\n"+resp[key]['linkJs']);
			_PI.cs(key+"->XHTML\n"+resp[key]['xhtml']);
			_PI.cs(key+"->SCRIPT\n"+resp[key]['js']);
		}
		var r=resp[key];
		if(resp[key]) {

			try{
				if ("undefined"!=typeof(r['jsBeforeResponse'])){
					eval(r['jsBeforeResponse']); //return to avoid response process
				}
				var target=$(r['target']);
				var evaljs=true;
				if(undefined!=target && undefined!=r['xhtml'] && r['xhtml']!=null && target!=null) {
					target.each(function(){
							switch(r['mode']){
								case "appendReplace":
									var ins=$(r['xhtml']).andSelf().attr('id');
									var ex=$("#"+ins);
									if(ex[0]) ex.replaceWith(r['xhtml']);
									else $(this).append(r['xhtml']);
								break;
								case "appendUpdate":
									var ins=$(r['xhtml']);
									var ex=$("#"+ins.attr('id'));
									if(ex[0]) ex.html(ins.contents());
									else $(this).append(r['xhtml']);
								break;
								case "appendOnce":
									var ins=$(r['xhtml']).andSelf().attr('id');
									var ex=$("#"+ins);
									if(ex[0]) evaljs=false;
									else $(this).append(r['xhtml']);
								break;
								case "append":
									$(this).append(r['xhtml']);
								break;
								case "prepend":
									$(this).prepend(r['xhtml']);
								break;
								case "before":
									$(this).before(r['xhtml']);
								break;
								case "after":
									$(this).after(r['xhtml']);
								break;
								case "replaceWith":
									$(this).replaceWith(r['xhtml']);
								break;
								default:
									if(this.nodeName=="TR") $(this).trHtml(r['xhtml']);
									else if(this.innerHTML!=undefined) this.innerHTML=r['xhtml'];
								break;
							}
					});
				}
				if ("undefined"!=typeof(r['linkJs'])){
					for (var i=0;i<r['linkJs'].length;i++) {
					 $('head').append("<script type='text/javascript' src='"+r['linkJs']+"'></script>");
					}
				}
				//todo, linkJsOnce
			} catch(e){
				_PI.cs("ERR-Error processing the jcall xhtml response.  This could be due to innerHTML rule being broken such as nesting p tags, etc.");
				_PI.cs(e);
			}
			if(_PI.stats) console.time("jcall eval process time:");
			try {
				if (evaljs) eval(r['js']);
			} catch(e){
				_PI.cs("ERR-Error evaluting jcall js response.");
				_PI.cs(e);
				_PI.cs("CODE-"+r['js']);
			}
			if(_PI.stats) console.timeEnd("jcall eval process time:");

		} else {
			if (_PI.debug) _PI.cs("INFO-An empty response was ignored");
		}
	}
};
panther.prototype.jcall=function(func,params,opt){
	if(undefined==_PI.jOpts) _PI.jOpts=new _PI.jcallOpts();
	if(undefined==_PI.pingOpts) _PI.pingOpts=new _PI.jcallOpts({jsonly:true,noloader:true,setWait:false});
	if(undefined===opt) opt=_PI.jOpts;
	if($(opt).selector) opt=this.apLoader(opt);
	if(typeof opt.loaderOpts.ap=='function') opt.loaderOpts.ap=opt.loaderOpts.ap();
	if(undefined==params) params=[];
	var settings={};
	if(opt.scall) $.extend(settings,{sess:_PI.sess});
	if(opt.layers) $.extend(settings,{layerid:_PI.layerid});
	if(opt.mode!="assign") $.extend(settings,{mode:opt.mode});
	if(opt.setWait) _PI.wait=true;
		else $.extend(settings,{setWait:false});
	var dobj={func:func};
	if(params.length) $.extend(dobj,{parameters:JSON.stringify(params)});
	var rid=dobj.func+dobj.parameters;
	if(!opt.rapidFire && _PI.stash[rid]) return false;
	_PI.stash[rid]=true;
	if(opt.jsonly) $.extend(settings,{jsonly:true});
	if(!opt.noloader){
		var loaderID=_PI.guid('ldr');
		var goLoader=function(){
				if(!_PI.stash[rid]) return false;
				var ap=opt.loaderOpts.ap;
				if(opt.loaderOpts.ap=="where") {
					if(_PI.debug) _PI.cs("INFO- 'where' for loader specified. Using "+params[1]);
					ap=params[1];
				}
				if($(ap).length<1 && _PI.debug) {
					_PI.cs("WRN-Can't display loader. Anchor point 'ap="+ap+"' is unselectable for loaderOpts.");
					return false;
				}
				if($(opt.loaderOpts.sel).length<1 && _PI.debug) {
					_PI.cs("WRN-Can't display loader. Loader image/element selector 'sel="+opt.loaderOpts.sel+"' is unselectable for loaderOpts.","error");
					return false;
				}
				$(opt.loaderOpts.sel).clone().attr("id",loaderID).placer(ap, { offsetX : opt.loaderOpts.offset.offsetX , offsetY : opt.loaderOpts.offset.offsetY , where : opt.loaderOpts.loc } ).fadeIn(opt.loaderOpts.transSpeed);
		}
		setTimeout(goLoader, opt.loaderOpts.delay);
	}

	var sj=JSON.stringify(settings);
	if(sj.length>2) $.extend(dobj,{settings:sj});
	opt.precall();
	$.ajax({
		type: "POST",
		url: opt.url,
		dataType: "json",
		data: dobj,
		timeout: opt.timeBeforeGivingUp,
		success: function(resp){
			if(opt.setWait) _PI.wait=false;
			if(_PI.stash[rid]) delete _PI.stash[rid];
			if(_PI.stats) console.time("jcall response process time:");
			if(_PI.stats) console.time("onsucess:");
			var process=opt.onSuccess(resp);
			if(_PI.stats) console.timeEnd("onsucess:");
			if ((!opt.noloader && !opt.persistLoader) || resp[0]['kldr']) $("#" + loaderID).fadeOut(opt.loaderOpts.transSpeed, function() {
				while ($("#"+loaderID).length>0) $("#"+loaderID).remove();
			});
			if(process && undefined!=resp && resp!==null){
				_PI.jcallHandleResp(resp,func,params);
			}
			opt.callback(resp);
			if(_PI.stats) console.timeEnd("jcall response process time:");
		},
		error: function(XMLHttpRequest, textStatus, errorThrown) {
				if(opt.setWait) _PI.wait=false;
				if(!opt.rapidFire) delete _PI.stash[rid];
		        if (textStatus!=undefined && _PI.debug) _PI.cs("ERR-There was ajax call error:"+textStatus,"error");
		        if (errorThrown!=undefined && _PI.debug) _PI.cs("ERR-There was ajax call exception:"+errorThrown,"error");
				if (!opt.noloader) $("#"+loaderID).fadeOut(opt.loaderOpts.transSpeed, function(){
					while ($("#"+loaderID).length>0) $("#"+loaderID).remove();
				});
				if (textStatus=="timeout") {
					opt.timeoutAction(XMLHttpRequest);
				} else {
					if (_PI.debug) _PI.cs("RESP-"+XMLHttpRequest.responseText);
					opt.errorAction(XMLHttpRequest);
				}
				return false;
		},
		complete: opt.onComplete
	 });
};
panther.prototype.servUp=function(what,where,vars,jOpts,async){
	if(!jOpts) jOpts=_PI.jOpts;
	if (!vars) vars={};
	if(_PI.wait&&!async) return false;
	_PI.jcall('servUp', [what,where,vars] ,jOpts);
	return false;
};

// plugin servUp - JEREMY DILL - March 2009
// @selector : target
// @param selector what : what to servUp />
// @param object settings : override default settings.
// @example: $("#mydiv").servUp("/shop/order");
// @ver .1
$.fn.servUp = function (what,settings) {
		settings = $.extend({
				opts : null,
				params : {},
				async : false
				}, settings);
		if(_PI.wait&&!settings.async) return false;
		_PI.jcall('servUp', [what,$(this).selector,settings.params],settings.opts||_PI.jOpts);
		return this.each(function(){});
};
//TRHTML - JEREMY DILL - AUG 2008
//CHANGE OUT CONTENTS OF TR (SAFE FOR IE)
// V .01 BETA
$.fn.trHtml = function (strContent) {
	return this.each(function(){
		row=$(this);
		rowid=row.attr('id');
		rowindex=row.index();
		row.before("<tr id="+rowid+">"+strContent+"</tr>")
		row.remove();
	});
};
$.fn.cloneObj = function (newOpts,deep) {
	if(deep==undefined) deep = 0;
	if(this.length>0){
		var newObj=$.extend(deep,{},this[0]);
		return $.extend(deep,newObj,newOpts);
	} else return this;
};
// UNIQUE IDENITFIER GENERATOR
$.uid=function(obj){
	if(_PI.stash.uid===undefined) _PI.stash.uid=1;
	var jobj=$(obj);
	var uid=$(obj).data('uid');
	if(uid>0) return uid;
	else uid=_PI.stash['uid']++;
	jobj.data('uid',uid);
	return uid;
};
})(jQuery);
