/*
 * jQuery-based CBQ/CBA/Sequential form plugin
 * 
 * Copyright (c) 2008-09 QuinStreet Inc.
 * 
 * Created and maintained by sbhimavarapu@quinstreet.com
 * 
 */
;(function($) {
	var cSeqAttr = undefined, $backBtn = undefined, $nextBtn = undefined, progressIndex = 0;
	var selectData = {};
	$.cbq = function(_cbqS){
		//This is to stop any data modifications enroute ;-)
		$.cbq.data = eval('({'+_cbqS+'})');
		var seqOrd = {}, progressTotal=1, pMin = 0, pBrd = 100; 
			isSequential = ($("[name='ApplySequentialFormLogic']").val() == 'True');
		var submitBtn = $("#submit");
		if(isSequential){
			submitBtn.hide();
			$.each($.cbq.data, function($attr, $this){
				if($this.q != undefined){
					seqOrd[$this.q] = $attr; progressTotal++;
				} else
					cSeqAttr = $attr;
			});
			//add only if progress bar is not there in the form object	
			var progress;
			if($('#progress') == undefined){
				progress = $("form").prepend("<div id='progress'><label>Progress</label><h6>"
					+ "<span style='width:"
					+ ((progressIndex/progressTotal)*100)+"%'>&nbsp;</span></h6></div>");
			}else {
				progress=$('#progress');
			}
			pMin = parseInt((progress.find('span').css('min-width').replace(/px/g,'')
					/progress.find('h6').css('width').replace(/px/g,''))*100);
			if(isNaN(pMin)) pMin = 0;
			pBrd = parseInt((progress.find('span').css('max-width').replace(/px/g,'')
					/progress.find('h6').css('width').replace(/px/g,''))*100) - pMin;
			if(isNaN(pBrd)) pBrd = 100;
			//if backBtn is defined at site level then don't add it again.
			if($('#backBtn').html() == null){
				$backBtn = $("<a id='backBtn' href='#'>&lt; Back</a>").click(function(){
				toggleQuestion(cSeqAttr, false, undefined, false);
				cSeqAttr = $.cbq.data[cSeqAttr].q;
				if($.cbq.data[cSeqAttr].q == undefined) 
					$backBtn.hide();
				toggleQuestion(cSeqAttr, true);
				$("#progress").find('span').width((pMin+(--progressIndex/progressTotal)*pBrd)+'%');
				$nextBtn.show();
				submitBtn.hide();
				if(readFormValues(cSeqAttr).length==0) $nextBtn.hide();
				else $nextBtn.show();
				return false;
			});
			}else {
				$backBtn = $('#backBtn').click(function(){
				toggleQuestion(cSeqAttr, false, undefined, false);
				cSeqAttr = $.cbq.data[cSeqAttr].q;
				if($.cbq.data[cSeqAttr].q == undefined) 
					$backBtn.hide();
				toggleQuestion(cSeqAttr, true);
				$("#progress").find('span').width((pMin+(--progressIndex/progressTotal)*pBrd)+'%');
				$nextBtn.show();
				submitBtn.hide();
				if(readFormValues(cSeqAttr).length==0) $nextBtn.hide();
				else $nextBtn.show();
				return false;
			});
			}
			
			//if nextBtn is defined at site level then don't add it again.
			if($('#nextBtn').html() == null){
				$nextBtn = $("<a id='nextBtn' href='#'>Next &gt;</a>").click(function(){
					if(readFormValues(cSeqAttr).length==0){ showInputError(cSeqAttr);return false;}
					toggleQuestion(cSeqAttr, false, undefined, false);
					cSeqAttr = seqOrd[cSeqAttr];
					if(seqOrd[cSeqAttr] == undefined){
						submitBtn.show();
						$nextBtn.hide();
					}
					if(cSeqAttr == undefined) return;
					toggleQuestion(cSeqAttr, true);
					$("#progress").find('span').width((pMin+(++progressIndex/progressTotal)*pBrd)+'%');
					$backBtn.show();
					if(readFormValues(cSeqAttr).length==0) $nextBtn.hide();
					else if(seqOrd[cSeqAttr] != undefined) $nextBtn.show();
					return false;
				});
			}else {
				$nextBtn = $('#nextBtn').click(function(){
						if(readFormValues(cSeqAttr).length==0){ showInputError(cSeqAttr);return false;}
						toggleQuestion(cSeqAttr, false, undefined, false);
						cSeqAttr = seqOrd[cSeqAttr];
						if(seqOrd[cSeqAttr] == undefined){
							submitBtn.show();
							$nextBtn.hide();
						}
						if(cSeqAttr == undefined) return;
						toggleQuestion(cSeqAttr, true);
						$("#progress").find('span').width((pMin+(++progressIndex/progressTotal)*pBrd)+'%');
						$backBtn.show();
						if(readFormValues(cSeqAttr).length==0) $nextBtn.hide();
						else if(seqOrd[cSeqAttr] != undefined) $nextBtn.show();
						return false;
					});
			
			}
			submitBtn.after($nextBtn);
			$nextBtn.hide();
			if(readFormValues(cSeqAttr).length > 0)
				$nextBtn.show();
			if($('#backBtn').html() == null){	
				submitBtn.after($backBtn);
			}
			$backBtn.hide();
		}
		
		//Loop through each configuration
		$.each($.cbq.data, function($attr, $this){
			//if the server made a 'hard-decision' about showing/not showing a question, honor it.
			if($this.s != undefined)
				toggleQuestion($attr, $this.s);
			//if it is not sure yet, then ask it everytime a parent question on this form changes value
			else{
				//Show the question if the question is not criteria-based, but answers are!
				//These questions come as CBQ's, because we need to hide the question if all answers are invalid
				if($this.k == undefined && $this.a != undefined)
					if(!isSequential)
						toggleQuestion($attr, true);
				//This happens for sequential forms only
				var qBound = false;
				if($this.q != undefined){
					bindTrigger($this.q, function(){
						if(readFormValues($this.q).length==0){showInputError($this.q); return false;};
						cSeqAttr = seqOrd[$this.q];
						if(seqOrd[cSeqAttr] == undefined){
							submitBtn.show();
							$nextBtn.hide();
						}
						if(cSeqAttr == undefined) return;
						toggleQuestion($this.q, false, undefined, false);
						toggleQuestion(cSeqAttr, true);
						$("#progress").find('span').width((pMin+(++progressIndex/progressTotal)*pBrd)+'%');
						if(readFormValues(cSeqAttr).length==0) $nextBtn.hide();
						else if(seqOrd[cSeqAttr]!=undefined) $nextBtn.show();
						$backBtn.show();
						qBound = true;
					});
				}
				//Setup 'change handlers' for only the parent questions
				if($this.p != undefined && !qBound)
					$.each($this.p, function(){
						bindTrigger(this, function(){
							valueChanged($attr);
						});
					});
				if($this.a != undefined){
					//If this is a select (single or multi) save the 'option' into array
					if($("[name='"+$attr+"']")[0].type.indexOf('select')>-1){
						selectData[$attr] = $("[name='"+$attr+"']").find('option');
						//We don't need this, but just to make sure 'append' works as expected
//						$("[name='"+$attr+"']").empty();
//						$("[name='"+$attr+"']").append(selectData[$attr]);
//						$("[name='"+$attr+"']").selectedIndex = 0;
					}
					$.each($this.a, function(){
						var $$this = this;
						//Setup 'change handlers' for only the parent questions
						if($$this.p != undefined){
							$.each($$this.p, function(){
								bindTrigger(this, function(){
									valueChanged($attr, $$this.k, $$this.p, $$this.s, $$this.e);
								});
							});
							valueChanged($attr, $$this.k, $$this.p, $$this.s, $$this.e);
						}
					});
				}
				//Ask it one more time before displaying the form as the values might have changed 
				//on the client end. usually happens during a 'page refresh' or 'hit back button'
				if($this.p != undefined)
					valueChanged($attr);
			}
		});
		
		$("form").submit(function(){
			$.each($.cbq.data, function($attr, $this){
				if($this.r==true)
					//"Not asked required CBQs" shouldn't be used for form validation. So, set a 
					//value to notify the server to take it out of validations
					if($("[id$='html_"+$attr+"']").css('display') == 'none')
						$("form").prepend("<input type='hidden' name='"+$attr+"' value='CBQ_NOT_SHOWN' />");
					//"Asked, but not answered, required CBQs" should make the form validation fail 
					else if(readFormValues($attr).length==0)
						$("form").prepend("<input type='hidden' name='"+$attr+"' value='CBQ_NOT_ANSWERED' />");
			});
		});
    };
	$.cbq.version = 2.1; //Criteria based answer filtering
	$.cbq.data = {};
	
	function bindTrigger(name, func){
		var prnt;
		//Absolutely unnecessary conversions b/w Criteria and Form notations
		if(''+name == 'Static09')
			prnt = $("[name='SP']"); 
		else if(''+name == 'Static11')
			prnt = $("[name='CN']"); 
		else
			prnt = $("[name='"+name+"']"); 
		//The click vs change is for IE which doesn't trigger 'change' until you 'blur'
		var type = prnt.attr('type');
		//Somehow the normal binding process is acting up when we 
		//attach multiple handlers on the same field
		//adding a new clause if the field type is text field then it should be submit not onclick
		if(type=='text'){
			prnt.bind('submit', func);
		}else {
			prnt.bind((type=='radio'||type=='checkbox')?'click':'change', func);
		}
	}
	function readFormValues(fld){
		var flds = $("[name='"+fld+"']");
		//If the field is not on the form return 'undefined'
		if(flds.length == 0) return undefined;
		//If the field type is radio or checkbox, read only checked values
		if(flds.attr('type')=='radio' || flds.attr('type')=='checkbox')
			flds = flds.filter(":checked");
		var vals = [];
		flds.each(function(){
			if(flds.attr('type')=='select-one'){
				if(!(/select/i.test($(this).val())))
					vals.push($(this).val());
			}else vals.push($(this).val());
		});
		return vals;
	}
	function valueChanged(attr, key, prnts, show, empty){
		var cbqDt = $.cbq.data[attr];
		var context =cbqDt.c; 
		var postdata = "key=" + (key?key:cbqDt.k);
		//Track the number of parents that have a valid question
		var parentsAnswered = 0;
		if(prnts == undefined) prnts = cbqDt.p;
		if(prnts == undefined) prnts = [];
		$.each(prnts, function(){
			var vals;
			//Unceremonious conversion b/w Criteria and Form notations
			if(''+this == 'Static09')
				vals = readFormValues("SP");
			else if(''+this == 'Static11')
				vals = readFormValues("CN");
			else
				vals = readFormValues(''+this);
			//If the field exists in the form
			if(vals != undefined){
				//Count if a valid answer is chosen
				if(vals.length > 0) parentsAnswered++;
				//Add it to the posting string
				if(context)
					postdata += "&"+(''+this).replace(context, '')+"="+vals.join(',');
				else
					postdata += "&"+this+"="+vals.join(',');
			}
		});
		//if the server did not make a 'hard-decision' about displaying this question yet,
		//ask it again with current form field values
		if(show == undefined) show = cbqDt.s;
		if(empty == undefined) empty = cbqDt.e;
		if(!show)
			//If all parents need to be answered, but some are unanswered, the criteria 
			//will eventually fail. So save the call and hide the question
			if(empty==false && parentsAnswered < prnts.length)
				toggleQuestion(attr, false, key);
			//If none of the parents are answered, the criteria won't make sense
			//So, hide the question
			else if(prnts.length == 0)
				toggleQuestion(attr, true, key);
			else if(prnts.length > 0 && parentsAnswered == 0)
				toggleQuestion(attr, false, key);
			else
			//Since 'at least one' or 'all' parents are answered, validate the criteria
				$.getJSON("../CBQValidator.jsp", postdata, function(data){
					toggleQuestion(attr, data, key);
				});
		return true;
	}
	function toggleQuestion(attr, show, key, resetAnswer){
		if(attr == undefined) return false;
		$('body').trigger('cbqToggleQuestion', [attr, show]);
		if(resetAnswer == undefined) resetAnswer = true;
    	//this is for answer filtering
    	if(key != undefined){
    		//search for the 'i' of that key - syam
    		$.each($.cbq.data, function($attr, $this){
    			if(attr == $attr && $this.a != undefined){
					$.each($this.a, function(){
						if(key == this.k){
							var flds = $("[name='"+attr+"']");
							var type = flds[0].type;
							if(type=='radio' || type=='checkbox'){
								var p = flds.filter(":enabled").length;
								for(var x=0;x<flds.length;x++){
									if((Math.pow(2, x) & this.i) > 0)
										if(show==true)
											$(flds[x]).parent().andSelf().removeAttr('disabled');
										else{
											$(flds[x]).parent().andSelf().attr('disabled','disabled');
											if(resetAnswer == true)
												$(flds[x]).parent().andSelf().removeAttr('checked');
										}
								}
								//if all the answers are invalid, hide the entire question
								if(flds.filter(":enabled").length == 0)
									toggleQuestion(attr, false);
								//If the question has been previously been hidden on 
								//account of all answers disabled, then show it
								else if(p == 0)
									valueChanged(attr);
							}else if(type.indexOf('select') > -1){
								var selectCount = /select/i.test(selectData[$attr][0].value)?1:0;
								for(var x = 0;x<selectData[$attr].length;x++){
									if(x == 0 && /select/i.test(selectData[$attr][x].value)){
										flds.prepend(selectData[$attr][x]);
										continue;
									}
									var oldOpt = flds.find("option[value='"+selectData[$attr][x].value+"']");
									//If this option belongs to this criteria group, apply 'show' flag
									if((Math.pow(2, x-selectCount) & this.i) > 0){
										if(show == true)
											flds.append(selectData[$attr][x]);
										else if(oldOpt != undefined)
											oldOpt.replaceWith();
									}
								}
								//If the only option left is 'Select One', hide the entire question
								//or If there is no select and no options are left
								if($("[name='"+attr+"'] option").length == selectCount)
									toggleQuestion(attr, false);
								else if($("[name='"+attr+"'] option").length == selectCount)
									toggleQuestion(attr, false);
								//If, before this change, all the answers have been hidden, then show the question
								else
									valueChanged(attr);
							}
						}
					});
    			}
    		});
    	}else{
	    	//This is for the questions
	    	//And no, this is not obvious as the response can be 'invalid' and the 'if' will pass
	    	var elems = $("[id$='_"+attr+"']");
	    	if(elems.length == 0) return false;
	        if (show == true) {
	        	//check if at least one answer is valid. if not, hide the question
				var flds = $("[name='"+attr+"']");
				var type = flds[0].type;
				if(type == 'radio' || type == 'checkbox'){
					if(flds.filter(":enabled").length > 0)
						elems.show();
				}else{
					elems.show();
				}
	        } else if(show == false){
	            elems.hide();
	            //Reset values while hiding the question, as though it was never answered)
	            var $elems = $("[id='html_"+attr+"'] [name='"+attr+"']");
	            var type = $elems.attr('type');
				if(resetAnswer == true)
		            if(type == 'radio' || type == 'checkbox')
		            	$elems.removeAttr('checked');
		            else{
		            	//If the value is still not empty (select with no empty value)
		            	//try to select the first option
		            	$elems.find('select').each(function(){
		            		this.selectedIndex = 0;
		            	});
		            }
	            cascadeToggle(attr);
	        }
    	}
    }
	function cascadeToggle(parent){
		//Find out if there are CBQs or CBAs based off of this CBQ and cascade the toggle effect
		$.each($.cbq.data, function($attr, $this){
			if($this.p != undefined)
				//Cascade the CBQ toggle for the children
				$.each($this.p, function(){
					if(this == parent) valueChanged($attr);
				});
			if($this.a != undefined)
				//Cascade the CBA toggle for children
				$.each($this.a, function(){
					if(this.p == parent)
						valueChanged($attr, this.k, this.p, this.s, this.e);
				});
		});
	}
})(jQuery);
$(document).ready(function() {

	var cbqS;
    $("input[name='CBQ']").each(function(){
	   if(cbqS == null)
       	cbqS = $(this).val();
       else 
       	cbqS = cbqS+","+$(this).val();
    });

    if(cbqS) $.cbq(cbqS);
       else if($("[name='ApplySequentialFormLogic']").val() == 'True')
	       $("form").prepend("<div id='progress'><label>Progress</label><h6>"
           + "<span>&nbsp;</span></h6></div>");
});
function showInputError(attr){
	//if(console!=undefined && console.log!=undefined)
		//console.log('Error: attr='+attr);
}
