/* ==================================================================
 *The JavaScript Validation objects to be used in form validation.
 * Copyright (c) 2001 by Blackboard, Inc.,
 * 1899 L Street, NW, 5th Floor
 * Washington, DC, 20036, U.S.A.
 * All rights reserved.
 * Submit RFC & bugs report to: aklimenko@blackboard.com
 * This software is the confidential and proprietary information
 * of Blackboard, Inc. ("Confidential Information").  You
 * shall not disclose such Confidential Information and shall use
 * it only in accordance with the terms of the license agreement
 * you entered into with Blackboard.
 * ==================================================================*/

/************************************************************
* Object formCheckList. Use this object to hold form objects
* to be validated and perform form validation
************************************************************/

var formCheckList = new formCheckList();
var dblSubmit = false;

function formCheckList(){
	this.checkList  = new Array();
	//this.superGroups= new Array(); // Create empty array for objects representing groups of radio/checkbox groups
	this.addElement = addElement;
	this.check      = checkForm;
}

function addElement(element){
	if (typeof element.group != 'undefined'){
		for (var i=0; i < this.checkList.length;i++){
			if (this.checkList[i].name == element.group){
				this.checkList[i].addElement(element);
				return;
			}
		}
		var grp = new CheckGroup(element);
		grp.addElement(element);
		this.checkList[this.checkList.length] = grp;
		return;
	}
	this.checkList[this.checkList.length] = element;
}

function checkForm(){
	var valid =true;
	for (var i=0;i<this.checkList.length;i++){
		if (!this.checkList[i].check()){
		  if(this.checkList[i].answerChk){valid=false;}
		  else {return false};
		}
	}
	return valid;
}
///////////////////End of object formCheckList////////////////

/************************************************************
* Object: inputText. Use this object to validate text input in
* your form (for input type == text|password|textarea|file)
************************************************************/
function inputText(h){
	this.element        = 'document.forms[0]["'+h.name+'"]';
	this.formatElement  = 'document.forms[0]["'+h.display_format+'"]';
	this.disable_script = h.disable_script;
	this.ref_label      = h.ref_label;
	this.custom_alert   = h.custom_alert;
	this.minlength      = h.minlength;
	this.maxlength      = h.maxlength;
	this.trim           = h.trim;
	this.regex          = h.regex;
	this.regex_msg      = h.regex_msg;
	this.regex_match    = h.regex_match;
	this.verify         = h.verify;
	this.check          = inputTextCheck;
	this.valid_email    = h.valid_email;
	this.invalid_chars  = h.invalid_chars;
	this.cmp_element	= 'document.forms[0]["'+h.cmp_field+'"]';
	this.cmp_ref_label	= h.cmp_ref_label;	
	this.xor			= h.xor;
	this.cmp_required   = h.cmp_required;
	// Add here anything you need to validate
}


// Do actual check here
function inputTextCheck(){
	var element = eval(this.element);
	var cmp_element = eval(this.cmp_element);
	if (typeof element != 'undefined'){
		this.custom_alert = (typeof this.custom_alert != 'undefined') ? this.custom_alert : '';
		this.ref_label = (typeof this.ref_label != 'undefined') ? this.ref_label :"'"+element.name + "' input field";
		var val    = element.value;      
		if (typeof eval(this.formatElement) != "undefined"){       
			//Check if it is a mathml where;
			if(/<APPLET ID="(\d+)" NAME="(\w+)"/.test(element.value)){
				if (getRadioValue(eval(this.formatElement)) == 'P'){
					if(!confirm('To display equations correctly in your document you must select \'Smart Text\' or\'HTML\' format.\nClick \'OK\' to save in selected \'Plain Text\' format or click \'Cancel\' to select new format.')){
						element.focus();return false;
					}
				}
			}
		}
		if (this.trim) {val = val.trim(); element.value = val;} //Remove leading & trailing spaces if needed
		
		if (typeof cmp_element != 'undefined'){
			if(this.xor){
				if(val.trim()=='' ^ cmp_element.value.trim()==''){
				  if(val.trim()==''){
				    alert('You must provide value for '+this.ref_label+'\nwhen '+this.cmp_ref_label+' field is not empty');
				    element.focus();
				  }else{
				    alert('You must provide value for '+this.cmp_ref_label+'\nwhen '+this.ref_label+' field is not empty');
				    cmp_element.focus(); 
				  }					
					return false;
				}
			}
		}
		
		if (this.disable_script){
			if (typeof eval(this.formatElement) == "undefined" || getRadioValue(eval(this.formatElement)) != 'P'){
				var re	= /<\s*script/ig;
				var re1 = /<\s*\/\s*script\s*>/ig;
				val     = val.replace(re,'<disabled-script');
				val		= val.replace(re1,'</disabled-script>');
				var re2 = /href\s*=\s*(['"]*)\s*javascript\s*:/ig;
				val     = val.replace(re2,"href=$1disabled-javascript:");
				element.value = val;
			}
		}
		if (this.valid_email){
			re = /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9])+$/; 
			if (!re.test(val)){
				alert('You must enter a fully qualified email address \n(e.g. info@blackboard.com)  for '+this.ref_label+'.');
				element.focus();
				return false;
			}     
		}      
		if (typeof(this.regex) == 'object' || typeof(this.regex) == 'function'){
			re =this.regex;        
			if (this.regex_match && val.search(re) == -1){
				alert(this.regex_msg + this.ref_label + '.');
				element.focus();
				return false;
			}
			if (!this.regex_match && re.test(val)){
				alert(this.regex_msg + this.ref_label + '.');
				element.focus();
				return false;
			}
		}

		if (this.invalid_chars){
			var arr = val.invalidChars(this.invalid_chars);
        
			if (arr && arr.length){
				alert(this.ref_label+' contains illegal characters.\nPlease remove this characters:\n'+arr.join('\n'));
				element.focus();
				return false;       
			}
		}
		if (val.length < this.minlength){
			if (this.minlength == 1){
				alert(this.custom_alert ? this.custom_alert : "You must provide a value for "+this.ref_label+".");
			}else{
				alert(this.custom_alert ? this.custom_alert : "You must enter minimum of "+this.minlength+" characters for the "+this.ref_label+".");
			}
			element.focus();
			return false;
		} 
		if (this.maxlength < val.length){
		    if ((val.length - this.maxlength) > 1) {
	    		alert("The "+this.ref_label+" may not contain more than "+this.maxlength+" characters.\nPlease reduce the size of your input by "+ (val.length-this.maxlength)+" characters.");
			} else {
		    	alert("The "+this.ref_label+" may not contain more than "+this.maxlength+" characters.\nPlease reduce the size of your input by 1 character.");
			}
			element.focus();
			return false;
		}
		if (this.verify){
			var chk_field = document.forms[0][element.name.replace(/_inp$/,'_chk')];
			var field     = document.forms[0][element.name.replace(/_inp$/,'')]; 

			if (chk_field.value != val){
				alert("The "+this.ref_label+"s you have entered do not match.\nPlease confirm "+this.ref_label);
				chk_field.focus();
				return false;
			}        
			// Encode password
			if (element.type == 'password'){
				element.value = element.value.trim();
				if (element.value != ''){
					element.value = field.value = chk_field.value = calcMD5(element.value);
				}else{
				  alert('Password can not be empty or contain only spaces');
				  element.value = field.value ='';
				  element.focus();
				  return false;
				}          
			}         
		}
		
		if (this.cmp_required && element.value.trim()!=''){
		    if (!cmp_element.value.trim().length){
		        alert('The '+this.ref_label+' can not be used without a corresponding '+this.cmp_ref_label+' value.');
		        cmp_element.focus();
		        return false;
		    }
		}     
	}
	return true;
}

///////////////////End of object inputText///////////////////

/************************************************************
*    Object: Check_EventTime. Use this object to make sure
*    that the end time is not before the start time, confirm pastdue time,
*    check duration of the event. 
*************************************************************/

function Check_EventTime(obj){
	this.start      = "document.forms[0]['"+obj.name+"']";
	this.ref_lbl    = obj.ref_label;
	this.end        = "document.forms[0]['"+obj.cmp_field+"']";
	this.cmp_ref_lbl= obj.cmp_ref_label;
	this.notEqual   = obj.duration;
	this.pastDue    = obj.past_due;
	//restrict flags fields
	this.restrict   = "document.forms[0]['"+obj.restrict_flag+"']";
	this.cmp_restrict="document.forms[0]['"+obj.cmp_restrict_flag+"']";
	this.show_end_time = obj.show_end_time;
	// define method
	this.check      = Check_EventTime_check;
}

function Check_EventTime_check(){
	var start, end, restr, cmp_restr;
	start = eval(this.start);		// first datetime field
	end   = eval(this.end);			// second datetime field to be compared with first
	restr = eval(this.restrict);	// Restrict checkbox field
	cmp_restr = eval(this.cmp_restrict);  // Restrict checkbox field to compare to

	restr     = (typeof(restr)     != 'undefined') ? restr.checked : true;      // True if restrict checkbox
	cmp_restr = (typeof(cmp_restr) != 'undefined') ? cmp_restr.checked : true;  // is checked or not defined

	// Update time in hidden field
	// Set time to empty string if it is not restricted
	if (!restr){start.value = '';}
	if (!cmp_restr || (this.show_end_time && !restr)){end.value   = '';} // Second field has to be set also
	start = start.value;
	if (typeof end != 'undefined'){
		end = end.value;
	}
	// Do not compare fields if at least one checkbox is unchecked
	if(!restr || !cmp_restr){
		this.notEqual = 0;
	}
	// Do not test for past due if restiction is not applied

	if (!restr){this.pastDue = 0;}
	if (this.pastDue){
		var confirm;
		var start_ms = Date.parse(start.replace(/-/g,'/'));      
		if (start_ms < Date.parse(new Date())-this.pastDue*1000*60){
			if (!window.confirm(this.ref_lbl+" is in the past.\nWould you like to continue with this "+this.ref_lbl+"?")){
				return false;
			}
		}
	}
	if (start > end && this.notEqual){
		alert("The "+this.cmp_ref_lbl+" can not be earlier than the "+this.ref_lbl+".");
		return false;
	}
	else if (end == start && this.notEqual){
		alert("The "+this.cmp_ref_lbl+" can not be equal to the "+this.ref_lbl+".");
		return false;
	}    
	return true;
}

/*We always need time in our favorite format
*/
function sql_datetime(dat){
	var year = dat.getFullYear();
	var mon  = dat.getMonth();
	mon++;						mon = (mon<10)?'0'+mon:mon;
	var day = dat.getDate();	day = (day<10)?'0'+day:day;
	hh		= dat.getHours();	hh  = (hh<10)?'0'+hh:hh;
	mi		= dat.getMinutes();	mi  = (mi<10)?'0'+mi:mi;
	ss		= dat.getSeconds();	ss  = (ss<10)?'0'+ss:ss;
	return  year+'-'+mon+'-'+day+' '+hh+':'+mi+':'+ss;
}
///////////////////End of object Check_EventTime/////////////

/********************************************************************************
* Object DoubleSubmit():
* Use this object to prevent multiple times form submission
* Second argument is wait image source; first argument is image name (string) or
* instance of ImageSwapper object created for submit button (if it has rollovers)
*********************************************************************************/

// Prevent multiple form submission
function DoubleSubmit(){


	this.submitted = 0;
	this.check = checkDoubleSubmit;
}

function checkDoubleSubmit(){






	if (this.submitted > 0){
	  alert ("You have already submitted this action.\nPlease wait until the action is complete.");
		return false;
	}else{
		this.submitted++;
	}
	return true;
}
///////////////////End of Object DoubleSubmit()//////////////////

/********************************************************************************
* Object CheckRadioBox():
* Use this object to make sure that at least one item is selected from the group of
* radio/checkbox groups. Just attach this code to checkbox/radio group (refered below as 'element'):
* formCheckList.addElement(new CheckRadioBox({name:'element name',group:'group name',ref_label:"element ref in alerts"}));
*********************************************************************************/
// Constructor function
function RadioCheckBox(h){
	return h;
}

function CheckGroup(h){
	this.name       = h.group;
	this.ref_label  = new Array();
	this.elements   = new Array();
	this.addElement = groupAddElement;
	this.check      = checkGroupChecked;
}

function groupAddElement(h){
	this.elements[this.elements.length]   = h.name;
	this.ref_label[this.ref_label.length] = h.ref_label;
}

function checkGroupChecked(){
	var list = this.elements;
	var chk  = false;
	for (var i = 0; i < list.length; i++){
		if (groupIsChecked(list[i])){return true;};
	}
	var msg = 'Please select at least one '+this.ref_label.join(' or ')+'.';
	var group = eval('document.forms[0]["'+list[0]+'"]');
	if (group[0].type == "radio"){
		msg = 'You need to select a ' + this.ref_label + ' to continue';
	}
	alert(msg);
	group[0].focus();
	return false;
}

function groupIsChecked(groupName){
	var group = eval('document.forms[0]["'+groupName+'"]');
	var checked = false;
	if (typeof group != 'undefined'){
		if (group.length  > 1){
			for (var i=0;i< group.length; i++){
				if (group[i].checked){
					checked = true;
					return checked;
				}
			}
		}else{
			if (group.checked){
				checked = true;
				return checked;
			}
		}
	}
	return checked;
}
///////////////////End of Object CheckGroup()////////////////////


//////////////// Start some useful generic functions ////////////

/* FUNCTION  strip() : removes all white spaces from the provided string:
   Usage: strippedString = originalString.strip();
*/
function strip(){
	return this.replace( /\s+/g,'');
}
String.prototype.strip= strip;


/* 	Function ltrim(): Remove leading  spaces in strings:
	Usage:trimmedString = originalString.ltrim();
*/
function ltrim(){
	return this.replace( /^\s+/g,'');
}
String.prototype.ltrim = ltrim;

/* 	Function rtrim(): Remove trailing spaces in strings:
	Usage:trimmedString = originalString.rtrim();
*/
function rtrim(){
	return this.replace( /\s+$/g,'');
}
String.prototype.rtrim = rtrim;


/* 	Function trim(): Remove leading and trailing spaces in strings:
	Usage:trimmedString = originalString.trim();
*/
function trim(){
	return this.rtrim().ltrim();;
}
String.prototype.trim = trim;

/*  
// Function trim_all is same as trim() but it treats string as multiple lines
function trim_all() {
	return this.replace( /^\s+|\s+$/gm,'');
}

//Function nice_striper will remove all whitespaces except new line from the string 
function nice_striper {
	return this.trim_all().replace(/(.)+\s+/gm,\1);
}  
*/


/* Function invalidChars(): Returns an array of illegal chars 
   Usage: var listOfChars = myStringToSearch.invalidChars(regularExpression); 
   regularExpression = /[illegal chars]/g; Sample re = /[! &^$#]/g
*/  
function invalidChars (re){  
	var chrs = this.match(re);
	if (chrs){
		for (j=0;j<chrs.length;j++){
			if(chrs[j]==' '){chrs[j]='space';}
			if(chrs[j]==','){chrs[j]='comma';}    
		}       
	}
	return chrs; 
}
String.prototype.invalidChars = invalidChars;

/** Function getRadioValue(): Returns selected value for group of radio buttons
* Usage: var selectedValue = getRadioValue(radio); radio - reference to radio group
*/
function getRadioValue(radio){
	for (var i=0;i< radio.length;i++){
		if (radio[i].checked){
			return radio[i].value;
		}
	}  
}

/*Function submitForm()
  Call this function to validate and submit form
*/
function submitForm(){
	if (validateForm()){      
		document.forms[0].submit();
	}
}

/*Function validateForm()
  Call this function onSubmit inside <form> tag
*/
function validateForm(){
	var ismath = (typeof(api) !='undefined'); //True if webeq is there
	/* Transform equations place holders into html before validation*/
	if (ismath){api.setHtml();}
	/* Validate form*/
	var valid = formCheckList.check();	
	/*Check for invalid answers if any present*/
	var list='';
	if (typeof(invalidAnswers) == 'object' && invalidAnswers.length > 0){list += invalidAnswers.join('\n')+'\n'; }
	if (typeof(invalidAnswersTmp) == 'object' && invalidAnswersTmp.length > 0){list += invalidAnswersTmp.join('\n'); }
	if (list != '' && valid){
	  if(!confirm('The following questions may be incomplete:\n'+list+'\nDo you want to continue?')){
	    valid = false; //User decided not to submit
	    invalidAnswersTmp = []; //Clearing up
	  }
	}
	/* Go back to placeholders if validation failed (valid == false)*/
	if (ismath && !valid){api.setMathmlBoxes();}
	/* If everything is OK and ready to go,check that the form was not already submitted */
	if (valid && dblSubmit != false){	  
	  valid = dblSubmit.check();
	}	
	return valid;
}

/*Function boxSelector()
* Use this function to select, unselect or invert selection for specified checkbox groups
* Call: boxSelector(['name1','name2',...,'namen'],action), here action is 'select', or 'unselect', or 'invert'
*/
function boxSelector(list,action){
	var action = (action == 'select') ? true : (action == 'unselect') ? false : action;
	for (var i=0;i<list.length;i++){
		var group = 'document.forms[0]["'+list[i]+'"]';
		if (typeof (group = eval(group)) != 'undefined'){
			if (action == 'invert'){
				for (var j=0;j<group.length;j++){
					group[j].checked = !group[j].checked;
				}
			} else {
				for (var j=0;j<group.length;j++){
					group[j].checked = action;
				}
			}
		}
	}
}


function setHidden (from,to){
	var hide = eval(to);
	hide.value = from.value;
}


//////////////////////////////////////////////////////////////////
/**
* Check_Answer object was added by request specified in mscr 524
* to provide validation to student answers
* Variable invalidAnswers has to be added to the page where assessment is submitted
* It should contain the list of unfinished questions excluding  question(s) on current page
* Check_Answer object will perform final validation and display all unfinished questions in confirm box
*/

var invalidAnswers = new Array(); // the java code will populate this array on final QbyQ page
/** Object constructor for answers walidation
* 
*/

function Check_Answer (vobj){ 
  if (typeof(window.invalidAnswersTmp)=='undefined'){window.invalidAnswersTmp=[];}
  this.element    = 'document.forms[0]["'+vobj.name+'"]';
  this.name       = vobj.name;
  this.answerChk  = true; //Check_Answer is special check, it makes a list of unfinished questions and always return true
  this.ref_label  = vobj.ref_label;  
  this.check      = Check_Answer_check;  
}

function Check_Answer_check(){
  
  //create form element object
  el = eval(this.element); 
  
  //Extract question type information from element name  
  var qtype =  /^(\w+)-/.exec(this.name);
  qtype = qtype[1];  
  if (qtype == 'ma'){
    qtype = /-\d+$/.test(this.name) ? 'mat' : 'ma';
  }
  
  // Perform actual check-up
  if (qtype == 'tf' || qtype == 'mc' || qtype == 'ma'){
    if(!isChecked(el)) {invalidAnswersTmp[invalidAnswersTmp.length]=this.ref_label;}  
  }  
  else if (qtype == 'ord' || qtype == 'mat'){
    if(el.selectedIndex == 0 && this.ref_label != invalidAnswersTmp[invalidAnswersTmp.length-1]){
        invalidAnswersTmp[invalidAnswersTmp.length]= this.ref_label;
      }  
  }
  else if (qtype == 'fitb' || qtype == 'essay'){
    if (el.value.trim() == ''){
      invalidAnswersTmp[invalidAnswersTmp.length]= this.ref_label; 
    }
  } 
  return true; //Always true, we can make decision later through confirm
}

// Test if at least one member of radio or checkbox group is selected
function isChecked(grp){
	for (var i=0;i< grp.length;i++){
		if (grp[i].checked){
			return true;
		}
	}
	return false; 
}


/* This is a sample code that has to be added to every corresponding form element in take assessment page,
where you perform question validation for completness; 
ref_lablel value is used to refer to element, name is field full name:

<script type="text/javascript">
formCheckList.addElement(new Check_Answer({ref_label:"Question 3",name:"tf-ans-_190_1"}));
</script>

*/
