
var jsTools = new objJsTools();
var page = new objPage();
var theForm = new objValidatingForm();

function objPage(){}
objPage.prototype.onload = function() {}

function linkToHREF(href){
	document.location=href;
}

function onloadPage() {
    var browser = navigator.appName;
    var b_version = navigator.appVersion;
    var version = parseFloat(b_version);

	page = choosePageManager();
	if (page) if (page.onload) page.onload();
}

function MM_reloadPage(init) {  
  if (init==true) with (navigator) {if ((appName=="Netscape")&&(parseInt(appVersion)==4)) {
    document.MM_pgW=innerWidth; document.MM_pgH=innerHeight; onresize=MM_reloadPage; }}
  else if (innerWidth!=document.MM_pgW || innerHeight!=document.MM_pgH) location.reload();
}
MM_reloadPage(true);
	
function MM_jumpMenu(targ,selObj,restore){ 
  eval(targ+".location='"+selObj.options[selObj.selectedIndex].value+"'");
  if (restore) selObj.selectedIndex=0;
}





/* timetable ############################################## */



function paint(){
	paintTimeTable()
}
function paintTimeTable(){
	var e,td
	for(var day=1;day<=7;day++) 
		for(var time=1;time<=4;time++) {
			e=document.getElementById('dd'+day+''+time)
			td=document.getElementById('tdd'+day+''+time)
			if (e && td){
			 td.className= e.checked?"on":"off"
			}
		}
}

function toggle(day,time,setting){
	var e
	if (day==null && time==null) {
		for(day=1;day<=7;day++) 
		for(time=1;time<=4;time++) {
			e=document.getElementById('dd'+day+''+time)
			e.checked=setting 
			}
	}
	else if (day==null) {
		day=1;
		if (setting==null) setting=!(document.getElementById('dd'+day+''+time).checked)
		for(day=1;day<=7;day++) {
			e=document.getElementById('dd'+day+''+time)
			e.checked=setting 
			}
	}
	else if (time==null) {
		time=1;
		if (setting==null) setting = !(document.getElementById('dd'+day+''+time).checked)
		for(time=1;time<=4;time++) {
			e=document.getElementById('dd'+day+''+time)
			e.checked=setting 
			}
		}
	else {}
	paintTimeTable();
}


/******************************************************/
/******************************************************/

////
//
// objJsTools
// 	 objJsTools.addClosureListener( element, type, object, methodName, bubbling)
//		objJsTools.addListener(element, type, expression, bubbling) 
//		objJsTools.createEventListenerMethodReference(object, methodName) 

function objJsTools(){}

////
//
// trim leading and trailing whitespace from a string
objJsTools.prototype.trim = function (str){
   str+='';
   return str.replace(/^\s*|\s*$/g,"");
}
/* 
	addClosureListener - adds a listener using a closure
	This allows you to add a listener that calls back to the instance of the appropiate object.
	When adding a listener you pass it a function to call when the event is triggered.
	This function is a global function and does not have an automatic connection to any object instances.
	It you design a class that can be dynamically instantiated to manage various items in the page then it
	is hard to get events to call methods of teh appropriate instances.  
	The solution is to use a closure.  This is a function created at runtime for each listener that contains
	an explicit call to the object.
	So in an object class the following line will add a listener to in effect call this.func(e) when 'thing' is clicked.
	addClosureListener( document.getElementById('thing'), 'click', this, func, false)
	
	Dependancies
		addListener
		createEventListenerMethodReference
*/
objJsTools.prototype.addClosureListener = function ( element, type, object, methodName, bubbling){
	return this.addListener(element, type, this.createEventListenerMethodReference(object, methodName), bubbling)
}

/*
	adds a generic function as a listener
*/
objJsTools.prototype.addListener = function (element, type, expression, bubbling){
	if(window.addEventListener)	{ // Standard
		element.addEventListener(type, expression, bubbling || false);
		return true;
	} else if(window.attachEvent) { // IE
		element.attachEvent('on' + type, expression);
		return true;
	} else return false;
}

/*
	createEventListenerMethodReference 
	creates a function that can be passed out from an object but that will callback the correct instance of the object (if object is set to this)
*/
objJsTools.prototype.createEventListenerMethodReference = function (object, methodName) {
    return function (event) {
        object[methodName].call(object, event || window.event);
    };
}


/******************************************************/

	function objFormManager(formId){
		//if (formId) this.setup(formId);
	}

	objFormManager.prototype.setup = function (formId){
		this.theForm = document.getElementById(formId) ? document.getElementById(formId) : document.forms[0];
		this.fields = new Array();
		this.showDirty = true;
		this.getFields();
	}

	
	objFormManager.prototype.validate = function (){
		for(var i=0;i<this.fields.length;i++) this.fields[i].validationWarning = '';
		var isValid=( this.beforeValidate ) ? this.beforeValidate() : true;
		for(var i=0;i<this.fields.length;i++){
			var msg= this.fields[i].validationWarning ;

			var inputIsValid=true;
			var val = (this.fields[i].vwType=='select-one') ? this.fields[i].el[this.fields[i].el.selectedIndex].value : jsTools.trim( this.fields[i].el.value );
		
			if (this.fields[i].vwType && this.fields[i].vwEl && val>'') {
				switch(this.fields[i].vwType){
					case 'select-one':
						break; 
					case 'email':
						if (!val.match(/^([0-9a-zA-Z]([-.\w]*[0-9a-zA-Z])*@(([0-9a-zA-Z])+([-\w]*[0-9a-zA-Z])*\.)+[a-zA-Z]{2,9})$/i)){
							msg='Please enter a valid email address.';
							inputIsValid=false;
						}
						break;									
					case 'int': 
						if (!val.match(/^\d*$/)) {
							msg+=(msg>''?', ':'')+'This needs to be a whole number; please correct it.'
							inputIsValid=false;
						}
						break;
					case 'password': 
						if (val.length<7) {
							msg='Passwords must be at least 7 characters long, with a mixture of letters and digits'
							inputIsValid=false;
						}else{
							if (!val.match(/\d/)) {
								msg+=(msg>''?', ':'')+'Passwords must contain at least one digit [0,1,2,3,4,5,6,7,8 or 9]'
								inputIsValid=false;
							}
							if (!val.match(/[a-z]/i)) {
								msg+=(msg>''?', ':'')+'Passwords must contain at least one letter'
								inputIsValid=false;
							}
						}
						break;
					case 'text': break;
					default:
						alert('Validation type not recognised: ' + this.fields[i].vwType)
				}
			}
			this.fields[i].isValid = inputIsValid;
			this.fields[i].validationWarning = msg;
			isValid = isValid && inputIsValid;
		}
		var afterValidation = (this.afterValidate ) ? this.afterValidate() :true;
		isValid = isValid && afterValidation;
		this.isValid = isValid;
	}
	
	objFormManager.prototype.paint = function (){
		this.validate();
		if (this.beforePaint) this.beforePaint();
		for(var i=0; i<this.fields.length; i++){
			var val = (this.fields[i].vwType=='select-one') ? this.fields[i].el[this.fields[i].el.selectedIndex].value : jsTools.trim( this.fields[i].el.value );
			var isDirty = (val != this.fields[i].originalValue);
			if (this.fields[i].vwEl) this.fields[i].vwEl.innerHTML = this.fields[i].el.disabled ? '' : (this.fields[i].validationWarning) ? this.fields[i].validationWarning: '';
			this.fields[i].el.className = this.fields[i].el.disabled ? 'disabled' : (this.fields[i].isValid ? (( !this.showDirty || !isDirty) ? '' : 'dirty') : 'invalid');
		}
	}	
	
	objFormManager.prototype.getFieldIndexFromId = function (id){
		for (var i=0; i<this.fields.length; i++) if (this.fields[i].el.id == id) return i;
		return null;
	}

	objFormManager.prototype.getFields = function (){
		var msg='';
		for (var i=0; i<this.theForm.length; i++){
			var el = this.theForm.elements[i];
			var vwEl = document.getElementById('vw_'+el.id)
			var vwOriginalValue = (vwEl) ? vwEl.value : '';
			var vwType= (vwEl) ? (vwEl.type ? vwEl.type : el.type): null;
			this.fields.push({  name:el.name, vwType:vwType, maxLength:el.maxLength<2000 ? el.maxLength : -1, el:el, vwEl:vwEl, originalValue:el.value, vwOriginalValue:vwOriginalValue })			
			jsTools.addClosureListener( el, 'change', this, 'paint', false)
			jsTools.addClosureListener( el, 'keyup', this, 'paint', false)
			jsTools.addClosureListener( el, 'click', this, 'paint', false)
			//msg += ((msg>'') ? ' ':'') + ((vwEl) ? (vwEl.type):'.');
		}
		if (msg>'')	alert(msg)
	}
		
/******************************************************/
// http://en.wikipedia.org/wiki/Luhn_algorithm
// http://en.wikipedia.org/wiki/Credit_card_number#_note-VISA

	function objCreditCard(ct, s){
		this.cardtype = ct;
		this.characterString = s;
		this.digits = '';
		this.passesLuhnTest = false;
		this.passesPrefixTest = false;
		this.passesLengthTest = false;
		this.msg = '';
		//
		this.cardTypeInfo = new Array();
		this.cardTypeInfo[1]  = { cardType:1,  name:'Visa',			lengths:[13,16],	 prefixes: ['4'] };
		this.cardTypeInfo[2]  = { cardType:2,  name:'MasterCard',	lengths:[16],		 prefixes: ['51','52','53','54','55'] };
		this.cardTypeInfo[10] = { cardType:10, name:'Switch/Maestro',lengths:[16,18,19], prefixes: ['4903', '4905', '4911', '4936', '564182', '633110', '6333', '6759'] };
		this.cardTypeInfo[11] = { cardType:11, name:'Electron',		lengths:[16],		 prefixes: ['417500', '4917', '4913'] };
		  
		return this.execute();
	}
	  
	objCreditCard.prototype.execute = function (){
		this.getDigitsFromString();
		this.performLuhnTest();
		this.performTypeTests();
		//
		if (!this.passesLengthTest) this.msg='Please enter your credit card number';
		else if (!this.passesLuhnTest) this.msg='Invalid credit card number.  Please correct the number.  ';
		else if (!this.passesPrefixTest) this.msg='Wrong number.  Please check the credit type is correct. ';
		else this.msg=""
		return this.passesLuhnTest && this.passesPrefixTest;
	}
	
	objCreditCard.prototype.getDigitsFromString = function (){
	  	this.digits = '';
	  	for (var i=0; i < this.characterString.length; i++) {
	  		var ch = this.characterString.substring(i,i+1); 
	  		if (ch>='0' && ch<='9') this.digits += ch;
	  	}
	  	return this.digits;
	}
	
	objCreditCard.prototype.performLuhnTest = function (){
		var sum = 0;
		for (var i = this.digits.length-1; i >= 0; --i) {
		    var value = 1 * this.digits.substring(i,i+1);
		    sum += (!(i % 2)) ?  ((value >= 5) ? value * 2 - 9 : value * 2) : value;
		}
		this.passesLuhnTest = ((sum % 10) == 0);
		return this.passesLuhnTest;
	}
	
	objCreditCard.prototype.performTypeTests = function (){
		if (this.cardtype>0){
			var cti = this.cardTypeInfo[this.cardtype];
			this.passesLengthTest = false;
			for (var i=0; i<cti.lengths.length; i++)  {
				this.passesLengthTest =  this.passesLengthTest || (this.digits.length == cti.lengths[i]); 
			}
			this.passesPrefixTest = false;
			for (var i=0; i<cti.prefixes.length; i++)  {
				this.passesPrefixTest =  this.passesPrefixTest || (this.digits.substring(0,cti.prefixes[i].length) == cti.prefixes[i]); 
			}
		}else{
			this.passesLengthTest = false;
			this.passesPrefixTest = false;
		}
		return this.passesPrefixTest && this.passesLengthTest;
	}  
	
/******************************************************/
function choosePageManager(){
	return;
	var el = document.getElementsByTagName('form')
	if (el.length>0) {
		switch (el[0].id ){
			case 'paymentForm':
				setUpPaymentForm(theForm) 
				return null; //new objPaymentPage();
				break;
			case 'signinForm':
				return new objSignInPage();
				break;
			case 'activationForm':
				//return new objActivatePage();
				break;
		}
	}
}

/******************************************************/

function objSignInPage(){
	this.name='signInPage';
	this.formManager = new objFormManager(); 
	this.formManager.showDirty = false;
	this.formManager.beforePaint = function (){	
			this.theForm.email.disabled = (this.theForm.userIdentityMode[1].checked);
			this.theForm.learnerId.disabled = !this.theForm.email.disabled;
			this.theForm.password.disabled = !(this.theForm.passwordMode[1].checked);
		}
	this.formManager.beforeValidate = function (){	
			var isValid = true;	
			isValid = isValid && ((this.theForm.userIdentityMode[1].checked && this.theForm.learnerId.value.length>1 ) 
						  	  || (!this.theForm.userIdentityMode[1].checked && this.theForm.email.value.length>1 ) );
			isValid = isValid && (this.theForm.passwordMode[1].checked && this.theForm.password.value.length>1   );
			return isValid;
		}
}

objSignInPage.prototype.onload = function (){
	this.formManager.setup('signinForm');		
	this.formManager.paint()
}

/******************************************************/

function objActivatePage(){
	this.name='activatePage';
	this.formManager = new objFormManager(); 
	this.formManager.showDirty = false;
	this.formManager.beforePaint = function (){	
			this.theForm.btnSignin.disabled = !this.isValid
		}
	this.formManager.beforeValidate = function (){	
			var isValid = true;	
			isValid = this.theForm.password.value>'' && this.theForm.password.value == this.theForm.password2.value ;
			document.getElementById('vFormActivateMsg').innerHTML = (this.theForm.password.value != this.theForm.password2.value) ? 'Enter exactly the same password in both boxes.' :'' ;
			return isValid;
		}
}
objActivatePage.prototype.onload = function (){
	this.formManager.setup('d');		
	this.formManager.paint()
}

/******************************************************/


function objValidatingForm(){
	this.theForm = null; document.getElementById('paymentForm')
}
objValidatingForm.prototype.submit = function(el){ 
	var valid = this.validate();
	el.disabled = valid;
	if (valid) this.theForm.submit();
	return valid;
}
objValidatingForm.prototype.validate = function(){ alert('no local validation function supplied - set the validate member to a function'); return true;}
objValidatingForm.prototype.paint = function(){ return true;}

objValidatingForm.prototype.onLoad = function(){
	this.theForm = document.getElementById('paymentForm')
	this.paint();
}

/******************************************************/

function setUpPaymentForm(theForm){
	theForm.validate = paymentFormValidate;
	theForm.paint = paymentFormPaint;
	theForm.onLoad();
}





function paymentFormValidate(){
	var now = new Date();
	var valid = true;
	var theForm = document.getElementById('paymentForm');
	var cc = new objCreditCard(this.theForm.cardtype.value, this.theForm.cardno.value);
	var nameCheckMsg = '';
	var cardExpiryMsg ='';
	var cardStartMsg = '';
	// correct in form
	this.theForm.cardname.value = jsTools.trim(this.theForm.cardname.value.toUpperCase()).replace(/[^a-z|A-Z|\s|.|'|0-9]/mgi,'')
	// test form
	if (jsTools.trim(this.theForm.cardname.value).length<5) nameCheckMsg='The name is too short. Please enter the exact name as it appears on the card.'
	if (jsTools.trim(this.theForm.cardname.value)=='') nameCheckMsg='Please enter the exact name as it appears on the card.'
	if ( (this.theForm.cardExpiryYY.value * 12 + 1 * this.theForm.cardExpiryMM.value) <  (now.getFullYear() * 12 + now.getMonth()) ) 
		cardExpiryMsg = 'This card has expired.  Please correct the date or use a valid card.';
	if (this.theForm.cardtype.value==10 && ((this.theForm.cardStartYY.value * 12 + 1*this.theForm.cardStartMM.value) > (now.getFullYear() * 12 + now.getMonth()))) 
		cardStartMsg='This card has not started yet. Please correct the date or use a valid card.';
	//
	document.getElementById('vw_' + 'cardtype').innerText = (theForm.cardtype.value>0) ? (cc.passesPrefixTest || !this.theForm.cardno.value.length) ? '':'Check the card type is correct; it does not match the number.': 'You must select the type of card you are using';
	document.getElementById('vw_' + 'cardname').innerText = nameCheckMsg;
	document.getElementById('vw_' + 'cardno').innerText = cc.msg
	document.getElementById('vw_cardExpiry').innerText = cardExpiryMsg
	document.getElementById('vw_cardStart').innerText = cardStartMsg
	//
	valid = valid && (nameCheckMsg=='') && (cardExpiryMsg=='') && (cardStartMsg=='') && cc.passesLuhnTest && cc.passesPrefixTest && cc.passesLengthTest;
	if (!valid) alert('There are problems with this form.  Please correct them and then try again.  Thank you.');
	return valid;
}

/******************************************************/

function newAccountFormSubmit(){
	document.getElementById('command').value='create'; 
	document.getElementById('submitBtn').disabled=true; 
	document.getElementById('newAccountForm').submit();
}

/******************************************************/

function signInFormSubmit(){
	document.getElementById('signInFormCommand').value='signIn'; 
	document.getElementById('signInFormSubmitBtn').disabled=true; 
	document.getElementById('signInFormSignUpBtn').disabled=true; 
	document.getElementById('signInForm').submit();
}
function signInFormSignUp(){
	window.location.search='page=newaccount';
}

function paymentFormPaint(){
	var theForm = document.getElementById('paymentForm')
	document.getElementById('issuenumber').disabled = theForm.cardStartYY.disabled = theForm.cardStartMM.disabled = ( theForm.cardtype.value!=10 && theForm.cardtype.value!=11);
	document.getElementById('issuenumber').className = theForm.cardStartYY.className = theForm.cardStartMM.className = ( theForm.cardtype.value!=10 && theForm.cardtype.value!=11) ? "disabled" :"enabled";
}


function paymentFormSubmit(){
	paymentFormPaint();
	document.getElementById('btnPay').disabled=true; 
	document.getElementById('paymentForm').submit();
}