/*
   Javascript form validation routines.
   Author: Stephen Poley

   Simple routines to quickly pick up obvious typos.
   All validation routines return true if executed by an older browser:
   in this case validation must be left to the server.

   Update Aug 2004: have tested that IE 5.0 and IE 5.5 both support DOM model
   sufficiently well, so innerHTML option removed (redundant).
*/

emptyString = /^\s*$/

var nbsp = 160;    // non-breaking space char
var node_text = 3; // DOM text node-type
var proceed = 2;   // Flag for markign it ok to proceed

/* Trim leading/trailing whitespace off string
*/
function trim(str) {
  return str.replace(/^\s+|\s+$/g, '')
};

/* msg
   Display warn/error message in HTML element
   commonCheck routine must have previously been called.
   Setting an empty string can give problems if later set to a 
   non-empty string, so ensure a space present. (For Mozilla and Opera one could 
   simply use a space, but IE demands something more, like a non-breaking space.)
   @param fld      id of element to display message in
   @param msgtype  class to give element ("warn" or "error")
   @param message  string to display
*/

function msg(fld,msgtype,message) {
  var dispmessage;
  if (emptyString.test(message)) 
    dispmessage = String.fromCharCode(nbsp);    
  else  
    dispmessage = message;

  var elem = document.getElementById(fld);
  elem.firstChild.nodeValue = dispmessage;  
  
  elem.className = msgtype;
};

/*
            commonCheck
    Common code for all validation routines to:
    (a) check for older / less-equipped browsers
    (b) check if empty fields are required
    Returns    true (validation passed), 
               false (validation failed) or 
               proceed (don't know yet)
   @param vfld      element to be validated
   @param ifld      id of element to receive info/error msg
   @param reqd      true if required
*/
function commonCheck(vfld,ifld,reqd)
{
  if (!document.getElementById) return true;  // not available - leave validation to the server
  
  var elem = document.getElementById(ifld);
  if (!elem.firstChild)
    return true;  // not available on this browser 
  if (elem.firstChild.nodeType != node_text)
    return true;  // ifld is wrong type of node  

  if (emptyString.test(vfld.value)) {
    if (reqd) {
      msg (ifld, "error", "Sorry: that's a required field");  
      vfld.focus();
      return false;
    }
    else {
      msg (ifld, "warn", "");   // OK
      return true;  
    }
  }
  return proceed;
}


/*
	commonCheck2
	Common code for checkbox validation routines to
	check for older / less-equipped browsers
	
	
vfld,   // element to be validated
ifld)   // id of element to receive info/error msg

	
	Returns true (validation passed) or
	proceed (don't know yet)
*/

function commonCheck2(vfld, ifld)
{
  if (!document.getElementById) 
    return true;  // not available on this browser - leave validation to the server
  var elem = document.getElementById(ifld);
  if (!elem.firstChild)
    return true;  // not available on this browser 
  if (elem.firstChild.nodeType != node_text)
    return true;  // ifld is wrong type of node  

  msg (ifld, "warn", "");  // clear any previous error message
  return proceed;
}



/* validatePresent
   Validate if something has been entered
    @param vfld     element to be validated
    @param ifld     id of element to receive info/error msg
   Returns true if so 
*/
function validatePresent(vfld, ifld )
{
  var stat = commonCheck (vfld, ifld, true);
  if (stat != proceed) return stat;

  msg (ifld, "warn", "");  
  return true;
};


/* Completed
   Validate if something has been entered
    @param vfld     element to be validated
    @param ifld     id of element to receive info/error msg
   Returns true if so 
*/
function completed(vfld, ifld )
{
  var stat = commonCheck (vfld, ifld, true);
  if (stat != proceed) return stat;

  msg (ifld, "warn", "");  
  return true;
};


/*  validateEmail
    Validate if e-mail address
    Returns true if so (and also if could not be executed because of old browser)
    @param vfld     element to be validated
    @param ifld     id of element to receive info/error msg
    @param reqd     true if required
*/
function validEmail(vfld, ifld, reqd)  
{
  var stat = commonCheck (vfld, ifld, reqd);
  if (stat != proceed) return stat;

  var tfld = trim(vfld.value);  // value of field with whitespace trimmed off
  var email = /^[^@]+@[^@.]+\.[^@]*\w\w$/
  if (!email.test(tfld)) {
    msg (ifld, "error", "ERROR: not a valid e-mail address");
    vfld.focus();
    return false;
  }

  var email2 = /^[A-Za-z][\w.-]+@\w[\w.-]+\.[\w.-]*[A-Za-z][A-Za-z]$/
  if (!email2.test(tfld)) 
    msg (ifld, "warn", "Unusual e-mail address - check if correct");
  else
    msg (ifld, "warn", "");
  return true;
};


/*  validPostcode
    Validate a uk postcode address
    Returns true if so (and also if could not be executed because of old browser)
    @param vfld     element to be validated
    @param ifld     id of element to receive info/error msg
    @param reqd     true if required
*/
function validPostcode(vfld, ifld, reqd)  
{
  var stat = commonCheck (vfld, ifld, reqd);
  if (stat != proceed) return stat;

  var tfld = trim(vfld.value);  // value of field with whitespace trimmed off
  tfld = tfld.toUpperCase();
  var postcode = /^[0-9]{5}-[0-9]{4}|[0-9]{5}|[A-Z][0-9][A-Z] [0-9][A-Z][0-9]|[A-Z]{1,2}[0-9]{1,2}[A-Z]? [0-9][A-Z]{2}$/
  if (!postcode.test(tfld)) {
    msg (ifld, "warn", "Check: doesn't appear to be a valid postcode");
    //vfld.focus();
    return true;
  } else {
    msg (ifld, "warn", "");
  }
  vfld.value = tfld;
  return true;
};

// 



/*  validPhone
    Validate telephone number
    Returns true if so (and also if could not be executed because of old browser)
    Permits spaces, hyphens, brackets and leading +
    @param vfld    element to be validated
    @param ifld    id of element to receive info/error msg
    @param reqd    true if required
*/

function validPhone(vfld, ifld, reqd)
{
  var stat = commonCheck (vfld, ifld, reqd);
  if (stat != proceed) return stat;

  var tfld = trim(vfld.value);  // value of field with whitespace trimmed off
  var telnr = /^\+?[0-9 ()-]+[0-9]$/
  if (!telnr.test(tfld)) {
    msg (ifld, "error", "Sorry: That's not a valid telephone number. "+
                        "Please use just digits, spaces, '(', ')', '-', "+
                        "and if needed one '+'. E.g. +1 (418) 333 5555");
    vfld.focus();
    return false;
  }

  var numdigits = 0;
  for (var j=0; j<tfld.length; j++)
    if (tfld.charAt(j)>='0' && tfld.charAt(j)<='9') numdigits++;

  if (numdigits<6) {
    msg (ifld, "error", "ERROR: " + numdigits + " digits - too short");
    vfld.focus();
    return false;
  }

  if (numdigits>14)
    msg (ifld, "warn", numdigits + " digits - check if correct");
  else { 
    if (numdigits<10)
      msg (ifld, "warn", "Only " + numdigits + " digits - check if correct");
    else
      msg (ifld, "warn", "");
  }
  return true;
};

/*
validateAge
Validate person's age
Returns true if OK 

vfld,   // element to be validated
ifld,   // id of element to receive info/error msg
reqd)   // true if required


*/
function validAge(vfld, ifld, reqd) 
{
  var stat = commonCheck (vfld, ifld, reqd);
  if (stat != proceed) return stat;

  var tfld = trim(vfld.value);
  var ageRE = /^[0-9]{1,3}$/
  if (!ageRE.test(tfld)) {
    msg (ifld, "error", "ERROR: not a valid age");
    vfld.focus();
    return false;
  }

  if (tfld>=200) {
    msg (ifld, "error", "ERROR: not a valid age");
    vfld.focus();
    return false;
  }

  if (tfld>110) msg (ifld, "warn", "Older than 110: check correct");
  else {
    if (tfld<7) msg (ifld, "warn", "Bit young for this, aren't you?");
    else        msg (ifld, "warn", "");
  }
  return true;
};


/*
            validateCheckbox
 Validate that the correct number of checkboxes has been checked.
 Returns true if valid (and also if could not be executed because 
 of old browser)
 
 vfld,    checkboxes to be validated
ifld,    id of element to receive info/error msg
nr,      number of checkboxes to be checked. >=2
cond)    condition: -1 = less than or equal to nr
                     0 = equal to nr (default)
                     1 = greater than or equal to nr
*/

function validCheckbox(vfld,ifld,nr,cond) 
{
  if (!nr || nr<2) {
    alert('Programming error in validateCheckbox: nr<2'); 
       // for nr=1 use radio buttons or validateConfirm
    return true;
  }
  if (!cond) cond = 0;

  var stat = commonCheck2(vfld, ifld);
  if (stat != proceed) return stat;

  // count how many boxes have been checked by the reader
  var count = 0;
  for (var j=0; j<vfld.length; j++)
     if (vfld[j].checked) count++;

  if (count==nr) return true;
  if (count<nr && cond==-1) return true;
  if (count>nr && cond==1)  return true;

  // if we get here then the validation has failed

  var suffix='';
  if (count>1) suffix='es';

  var errorMsg;

  if (count<nr) errorMsg = 'Only ' + count + ' box' + suffix + ' checked: ' + nr + ' required';
  if (count>nr) errorMsg = '' + count + ' boxes checked: maximum ' + nr + ' allowed';
  if (count==0) errorMsg = 'No boxes checked: ' + nr + ' required';

  msg (ifld, "error", errorMsg);
  vfld[0].focus();
  return false;
}


/*
 validateConfirm 
 Usually one doesn't want to validate if 1 checkbox of a set has been
 checked, because in this case one would use radio buttons instead.
 But sometimes one wants a reader to check a single box to confirm that 
 he or she agrees to something. That is covered by this routine.

vfld,   // checkbox to be validated
ifld)   // id of element to receive info/error msg


 Returns true if valid (and also if could not be executed because 
 of old browser)
*/
function validConfirm(vfld, ifld)
{
  var stat = commonCheck2(vfld, ifld);
  if (stat != proceed) return stat;

  if (vfld.checked) return true;

  // if we get here then the validation has failed

  var errorMsg = 'Please confirm that you agree to this.';

  msg (ifld, "error", errorMsg);
  vfld.focus();
  return false;
}


/* validateDate
   Validate a date
   Returns true if OK 

    @param vfld  element to be validated
    @param ifld  id of element to receive info/error msg
    @param reqd  true if required
*/

function validDate(vfld, ifld, reqd)
{
  
  var stat = commonCheck (vfld, ifld, reqd);
  if (stat != proceed) return stat;

  var tfld = trim(vfld.value);  // value of field with whitespace trimmed off
  
  // check for null MySQL Values for dates
  if(tfld == '0000-00-00') {
  	vfld.value = '';
  	msg (ifld, "info", '');
  	return true;
  } else {
	var parts;
	var myYear;
	var myMonth;
	var myDay;

  // test tfld as a date value
  
	parts = tfld.split("/");
	
	if (parts.length == 3) { // we may have a properly formatted date yyyy-mm-dd 
		myYear = parts[2];
		myMonth = parts[1];
		myDay = parts[0];				
		//alert(myYear+'-'+pad(myMonth)+'-'+pad(myDay));
		
		
		if (!isDate(myYear, myMonth, myDay)) {
			msg (ifld, "error", "Invalid date - Please use YYYY-MM-DD or DD/MM/YYYY only.");
			return false;
		} else {
			vfld.value = myYear+'-'+pad(myMonth)+'-'+pad(myDay);
			var returndate = new Date(myYear, myMonth-1, myDay);
			msg (ifld, "info", "("+returndate.toDateString()+")");
			return true;
		}
	}  else {
		parts = tfld.split("-");
		if (parts.length == 3) { // we may have a properly formatted date yyyy-mm-dd 
			myYear = parts[0];
			myMonth = parts[1];
			myDay = parts[2];				
			
			
			
			if (!isDate(myYear, myMonth, myDay)) {
				msg (ifld, "error", "Invalid date - Please use YYYY-MM-DD or DD/MM/YYYY only.");
				return false;
			} else {
				vfld.value = myYear+'-'+pad(myMonth)+'-'+pad(myDay);
				var returndate = new Date(myYear, myMonth-1, myDay);
				msg (ifld, "info", "("+returndate.toDateString()+")");
				return true;
			}
		} else {
   		 msg (ifld, "error", "Invalid date - Please use YYYY-MM-DD or DD/MM/YYYY only.");
		 return false;
		}
	}
  }
}

function isBlank(s){
	for(var i=0; i<s.length; i++) {
		var c = s.charAt(i);
		if ((c !=' ') && (c !='n') && (c !='t')) return false;
	}
	return true;
}

function isDate(myYear, myMonth, myDay) {
	var testDate = new Date(myYear, myMonth - 1, myDay);
	if ((testDate.getDate() != myDay) || 
	(testDate.getMonth() != myMonth - 1) || 
	(testDate.getFullYear() != myYear))  return false;
	return true;
}

function pad(my_num) { // requires a date object
	return (my_num < 10 ? "0" + (my_num * 1) : my_num);	
}

function ordinal(my_num) { // requires a date object
	suffix =  my_num % 10 + ((my_num > 10) & (my_num < 20) ? 4 : 0);
	switch (suffix) {
		case 1:
			return my_num+"st";
			break;
		case 2:
			return my_num+"nd";
			break;
		case 3:
			return my_num+"rd";
			break;
		default :
			return my_num+"th";
			break;
	}
}

var months=new Array("Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec");
var days=new Array("Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday");
var monthlengths = new Array( 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);

function fullYear(my_date) { // requires a date object
	var theYear = my_date.getYear();
	return (theYear < 1900 ? theYear + 1900 : theYear);	
}

function leapyear(my_year) { // requires a four digit year
	return ( ((my_year%4==0)&(my_year%100!=0))|(my_year%400==0) );
}

function monthlength(my_month, my_year) { // requires a four digit year and a month 00-11
	return (monthlengths[my_month] + (leapyear(my_year) & (my_month==1)? 1:0));
}

function dayname(my_date) { // requires a date object
	return days[my_date.getDay()];
}

function monthname(my_date) { // requires a date object
	return months[my_date.getMonth()];
}

function monthyear(my_date) { // requires a date object
	return(monthname(my_date))+" "+(fullYear(my_date));
}

function getSelectValue(selectObject) {
	return selectObject.options[selectObject.selectedIndex].value;
}
		
function buildList(yearMonthMenu, dateMenu)
{
	
	var thisMonthYear = getSelectValue(yearMonthMenu);
	var thisYear = thisMonthYear.substring(0,4)*1; // get the year, and make sure it's a number
	var thisMonth = (thisMonthYear.substring(5,7))-1;  // remember months start: 0 Jan, 1 Feb, etc.
	var thisMonthStart = new Date(thisYear, thisMonth, 1, 0, 0, 0, 0); // create the date for 1st of the following month
	var distanceFromThursday = (thisMonthStart.getDay()+3)%7; // days from Thursday - Thu = 0, Fri = 1, Sat = 2 etc
	var startDate = new Date(thisMonthStart - (24*60*60*1000*distanceFromThursday));
	var firstDay = days[thisMonthStart.getDay()];
	
	if (distanceFromThursday) { // the month doesn't start with a Thursday
		var lastMonth = (thisMonth == 0 ? 11 : thisMonth-1);
		var lastMonthsYear = ((thisMonth == 0) ? thisYear -1 : thisYear);
		var currentDay = monthlength(lastMonth, lastMonthsYear)-distanceFromThursday+1;
		var currentMonth = lastMonth;
		var currentYear = lastMonthsYear;
	} else { // the month does start with a Thursday
		var currentDay = 1;
		var currentMonth = thisMonth;
		var currentYear = thisYear;
	}
	
	// empty the menu of days first - must come back and see if can just trim an extra days off - for speed ;-)
	for (var thisOption = 0; thisOption < dateMenu.length+1; thisOption++) {
		dateMenu.options[0] = null;	// setting an option array element to null removes it from the list
									// effectively we're cutting the menu off at the bottom!
	}
	
	var maxDays = monthlength(thisMonth, thisYear)+distanceFromThursday+7;
	var startDays = new Array("Thu","Fri","Sat");
	var theResult = "";
	var thisItem = 0;
	
	for (i=0; i< maxDays ; i = i+7) {
		
		for (j=0; j<3 ; j++) {
			currentMonthLength = monthlength(currentMonth, currentYear);
			if (currentDay > currentMonthLength) {
				currentDay = currentDay - currentMonthLength;
				currentMonth = currentMonth+1;
				if (currentMonth==12) {
					currentMonth = 0;
					currentYear = currentYear+1;
				}
			}
			if (j==0) {
				theValue = currentYear+"-"+pad(currentMonth+1)+"-"+pad(currentDay);
			}
			
			theResult = theResult+" "+ordinal(currentDay)+" "+months[currentMonth]+((currentYear != thisYear) ? " "+currentYear:"");
			if (j<2) {theResult = theResult+", ";};
			currentDay++;
		}
		
		dateMenu.options[thisItem] = new Option(theResult);					
			
		// option value e.g. 2003-12-01
		dateMenu.options[thisItem++].value =  theValue;
		
		currentDay++;// ... skip Sunday
		currentDay++;// ... skip Monday
		currentDay++;// ... skip Tuesday
		currentDay++;// ... skip Wednesday
		theResult = "";
	}
	
	return theResult;
}

function monthYearList(monthLimit) {
	// create an array of month names
	month = new Array("December", "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November");
	
	now = new Date();
	currentYear = fullYear(now);			
	currentMonth = now.getMonth()+1;			
	
	var theResult = "";
	
	for (i=0; i<monthLimit; i++) {
		
		thisYear = currentYear + Math.floor((currentMonth + i)/12.01);
		
		thisMonthIndex = ((currentMonth + i) % 12) ;
		thisMonthDisplay = ((thisMonthIndex == 0) ? 12:pad(thisMonthIndex));
		sqlDate = thisYear+"-"+thisMonthDisplay;	
		displayDate = month[thisMonthIndex]+" "+thisYear;	
		
		theResult = theResult + '<option label="'+displayDate+'" value="'+sqlDate+'">'+displayDate+'</option>\n';		
	}
	
	return theResult;
}

function buildDateMenu(dayMenu, yearMonthMenu, showOnlyForward)
{
	// this works, assuming that the values in the yearMonthMenu are of the form YYYY-MM
	// which is set up to mimic SQL date values yyyy-mm-dd
	// showOnlyForward let us specify whether or not to begin the day calendar from Today if it's the current month!
	
	var thisMonthYear = yearMonthMenu.value; // for easier reading, store the passed value
	var days = new Array ("Sun","Mon","Tue","Wed","Thu","Fri","Sat"); // make an array fo day names
	var thisYear = thisMonthYear.substring(0,4)*1; // get the year, and make sure it's a number
	var thisMonth = (thisMonthYear.substring(5,7))-1;  // remember months start: 0 Jan, 1 Feb, etc.
	
	var now = new Date(); // get the date today
	
	var nextMonthStart = new Date(thisYear, thisMonth + 1, 1, 0, 0, 0, 0); // create the date for 1st of the following month
	var thisMonthStart = new Date(thisYear, thisMonth, 1, 0, 0, 0, 0); // create the date for 1st of this month
	var thisMonthEnd = new Date(nextMonthStart - 60000); // 60 * 1000 milliseconds, i.e. 1 minute
	

	// empty the menu of days first - must come back and see if can just trim an extra days off - for speed ;-)
	for (var thisOption = 0; thisOption < dayMenu.length+1; thisOption++) {
		dayMenu.options[0] = null;	// setting an option array element to null removes it from the list
									// effectively we're cutting the menu off at the bottom!
	}

	var numDays = thisMonthEnd.getDate();// return the day part of the date
	var theDay = thisMonthStart.getDay();// return the day index part of the date
	var theDate = 1;// return the day index part of the date
	
	// menu needs to have numDays items in it.
	// start day for menu is startDay.
	// start date for menu is 1.
	
	if ((thisMonthStart < now) && showOnlyForward ) {
		// oops! The day menu needs to start from today onwards!
		theDate = now.getDate(); // we need to start from item (today-1)
		theDay = now.getDay();// return the day index part of the date
		numDays = numDays - theDate + 1;
	}
	
	for (var thisItem = 0; thisItem < numDays; thisItem++) {
		
		// option e.g. Fri 6
		dayMenu.options[thisItem] = new Option( days[theDay] + " " + ( theDate ));
		
		// option value e.g. 06
		dayMenu.options[thisItem].value =  theDate < 10 ? "0" + theDate : theDate;
		
		theDate++; // set the next day...
		theDay = (theDay+1)%7; // and next dayname index values for the next loop
	}
	dayMenu.options[0].selected = true; // select the first item in the menu				
}



var newWindow = null;

function closeWin(){
    if (newWindow != null){
        if(!newWindow.closed)
            newWindow.close();
    }
}

function popUpWin(url, type, strWidth, strHeight){

    closeWin();

     if (type == "fullScreen"){

        strWidth = screen.availWidth - 10;
        strHeight = screen.availHeight - 160;
    }

    var tools="";
    if (type == "standard" || type == "fullScreen") tools = "resizable,toolbar=yes,location=yes,scrollbars=yes,menubar=yes,width="+strWidth+",height="+strHeight+",top=0,left=0";
    if (type == "console") tools = "resizable,toolbar=no,location=no,scrollbars=no,width="+strWidth+",height="+strHeight+",left=0,top=0";
    newWindow = window.open(url, 'newWin', tools);
    newWindow.focus();
}

function isUndefined(v) {
	var undef;
	return v===undef;
}

// These defaults should be changed the way it best fits your site
var _POPUP_FEATURES = 'location=0,statusbar=0,menubar=0,width=615,height=540';

function raw_popup(url, target, features) {
	// pops up a window containing url optionally named target, optionally having features
	if (isUndefined(features)) features = _POPUP_FEATURES;
	if (isUndefined(target  )) target   = '_blank';
	var theWindow = window.open(url, target, features);
	theWindow.focus();
	return theWindow;
}

function link_popup(src, features) {
	// to be used in an html event handler as in: <a href="..." onclick="link_popup(this,...)" ...
	// pops up a window grabbing the url from the event source's href
	return raw_popup(src.getAttribute('href'), src.getAttribute('target') || '_blank', features);
}