// ADD IMAGE SWAP -------------------------------------------------
// adds a JS mouse over swap to all image inputs and image links
// in the document with '-off' or '-on' as the last bit of the
// filename before the extension
function AddSwap() {
//	declare variables
	var	inputs,
		hyperlinks,
		numElements,
		childImages,
		numImages,
		i,
		j;
//	test for DOM Methods and get all input elements
	if(	(typeof document.getElementById != 'undefined') &&
		(typeof document.getElementsByTagName != 'undefined') &&
		(inputs = document.getElementsByTagName('input')) &&
		(hyperlinks = document.getElementsByTagName('a'))
		) {
//		loop through the inputs
		numElements = inputs.length;
		for (i = 0; i < numElements; i++) {
//			find the ones that are of type 'image'
			if (inputs[i].type.toLowerCase() == 'image') {
//				pass image inputs to the function that adds the swap
				this.makeSwap(inputs[i]);
			}
		}
//		loop through the anchors
		numElements = hyperlinks.length;
		for (i = 0; i < numElements; i++) {
//			find the ones that have image children
			childImages = hyperlinks[i].getElementsByTagName('img');
			numImages = childImages.length;
			if (numImages > 0) {
//				pass the images to the function that adds the swap
				for (j = 0; j < numImages; j++) {
					this.makeSwap(childImages[j]);
				}
			}
		}
		addUnloadHandler(this.cleanUp);
		return true;
	}
	return false;
}
AddSwap.prototype.makeSwap = function (el) {
	if ((typeof el.src == 'string') && (el.src.indexOf('-off.') != -1)) {
//		if the image name contains 'off' set the src of off state img to the current
//		src and swap the 'off' with 'on' to get the src for the on state
		el.offImg = new Image();
		el.offImg.src = el.src;
		el.onImg = new Image();
		el.onImg.src = el.src.replace(/-off\./,'-on.');
	} else if ((typeof el.src == 'string') && (el.src.indexOf('-on.') != -1)) {
//		otherwise, if the image name contains 'on' set the src of on state img to the
//		current src and swap the 'on' with 'ooff' to get the src for the on state
		el.offImg = new Image();
		el.offImg.src = el.src;
		el.onImg = new Image();
		el.onImg.src = el.src.replace(/-on\./,'-off.');
//	if neither of the state slugs is present, return false
	} else return false;
//	add mouseover and mouseout functions
	el.onmouseover = function() { this.src = this.onImg.src; return true; }
	el.onmouseout = function() { this.src = this.offImg.src; return true; }
	return true
}
AddSwap.prototype.cleanUp = function () {
	var	inputs = document.getElementsByTagName('input'),
		hyperlinks = document.getElementsByTagName('a'),
		numElements = inputs.length,
		childImages,
		numImages,
		i,
		j;
//	loop through the inputs
	for (i = 0; i < numElements; i++) {
//		find the ones that are of type 'image'
		if (inputs[i].type.toLowerCase() == 'image') {
//			remove handlers & expandos
			inputs[i].offImg = null;
			inputs[i].onImg = null;
			inputs[i].onmouseover = null;
			inputs[i].onmouseout = null;
		}
	}
//	loop through the anchors
	numElements = hyperlinks.length;
	for (i = 0; i < numElements; i++) {
//		find the ones that have image children
		childImages = hyperlinks[i].getElementsByTagName('img');
		numImages = childImages.length;
		if (numImages > 0) {
//			pass the images to the function that adds the swap
			for (j = 0; j < numImages; j++) {
				childImages[j].offImg = null;
				childImages[j].onImg = null;
				childImages[j].onmouseover = null;
				childImages[j].onmouseout = null;
			}
		}
	}
	return true;
}
addLoadHandler(function() { return new AddSwap();});


// UTILITIES ======================================================


function attachEventListener(target, eventType, functionRef, capture) {
	if (typeof target.addEventListener != "undefined") {
		target.addEventListener(eventType, functionRef, capture);
	} else if (typeof target.attachEvent != "undefined") {
		target.attachEvent("on" + eventType, functionRef);
	} else {
		eventType = "on" + eventType;
		if (typeof target[eventType] == "function") {
			var oldListener = target[eventType];
			target[eventType] = function() {
				oldListener();
				return functionRef();
			}
		} else {
			target[eventType] = functionRef;
		}
	}
	return true;
}

function detachEventListener(target, eventType, functionRef, capture) {
	if (typeof target.removeEventListener != "undefined") {
		target.removeEventListener(eventType, functionRef, capture);
	} else if (typeof target.detachEvent != "undefined") {
		target.detachEvent("on" + eventType, functionRef);
	} else {
		target["on" + eventType] = null;
	}
	return true;
}

function getEventTarget(event) {
	var targetElement = null;
	if (typeof event.target != "undefined") {
		targetElement = event.target;
	} else {
		targetElement = event.srcElement;
	}
	while (targetElement.nodeType == 3 && targetElement.parentNode != null) {
		targetElement = targetElement.parentNode;
	}
	return targetElement;
}

function stopDefaultAction(event) {
	event.returnValue = false;
	if (typeof event.preventDefault != "undefined") {
		event.preventDefault();
	}
	return true;
}

/*	ADD LOAD HANDLER -----------------------------------------------
	allows adding more than one function to the onload event
	based on Simon Willison's discussion of closures at:
	http://simon.incutio.com/archive/2004/05/26/addLoadEvent
*/
function addLoadHandler(func) {
  var oldonload = window.onload;
  if (typeof window.onload != 'function') {
	window.onload = func;
  } else {
	window.onload = function() {
	  oldonload();
	  func();
	}
  }
}
/*	ADD ONUNLOAD HANDLER -------------------------------------------
	allows adding more than one function to the window's onlunload
	event based on Simon Willison's closure demo
*/
function addUnloadHandler(func) {
	var oldonunload = window.onunload;
	if (typeof window.onunload != 'function') {
		window.onunload = function() {
			func();
			window.onload = null;
			window.onunload = null;
		}
	} else {
		window.onunload = function() {
			func();
			oldonunload();
		}
	}
}

/*	ADD HANDLER ----------------------------------------------------
	http://simon.incutio.com/archive/2004/05/26/addLoadEvent
*/
function addHandler(obj,evt,func) {
	var oldhandler = obj[evt];
	if (typeof obj[evt] != 'function') {
		obj[evt] = func;
	} else {
		obj[evt] = function() {
			oldhandler();
			func();
		}
	}
}

/*	GET ATTR -------------------------------------------------------
	gets an attribute value; works around the IE class/className,
	for/htmlfor & camel-case debacles

	PARAMETERS
	- o = object; HTML element on which the attribute resides
	- a = string; attribute name

	REQUIRED SCRIPTS
	- translateAttrName; utility to translate from standard to
	  MS-specific attribute names

	LIMITATIONS
	- may not have exhaustive list of case-sensitive attributes
*/
function getAttr(o,a) {
	var attr;
//	if the.getAttributeNode method exists, use it
	if (o.getAttributeNode) return (attr = o.getAttributeNode(a))?attr.nodeValue:'';
//	otherwise, try getAttribute with translation
	else if (o.getAttribute) return o.getAttribute(translateAttrName(a));
//	if all else fails, try just accessing it as a property
	else return o[a];
}

/*	SET ATTR -------------------------------------------------------
	sets an attribute value; works around the IE class/className,
	for/htmlfor & camel-case debacles

	PARAMETERS
	- o = object; HTML element on which the attribute resides
	- a = string; attribute name
	- v = string; attribute value

	REQUIRED SCRIPTS
	- translateAttrName; utility to translate from standard to
	  MS-specific attribute names

	LIMITATIONS
	- may not have exhaustive list of case-sensitive attributes
*/
function setAttr(o,a,v) {
	var attr;
//	if the getAttributeNode method exists, use it
	if (o.getAttributeNode) {
//		if the attribute already exists, we can just set the nodeValue and have done
		if (attr = o.getAttributeNode(a)) return attr.nodeValue = v;
//		otherwise...
		else {
//			create a new attribute
			attr = document.createAttribute(a);
//			set it's nodeValue
			attr.nodeValue = v;
//			and finally add it to the element
			return o.setAttributeNode(attr);
		}
//	if there's no getAttribute node, try using setAttribute with IE translation
	} else if (o.setAttribute) return o.setAttribute(translateAttrName(a),v);
//	and if all else fails, just set it as a property
	else return o[a] = v;
}

/*	TRANSLATE ATTR NAME --------------------------------------------
	translatest from standard to IE-specific attribute names

	PARAMETERS
	- n = string; attribute name

	LIMITATIONS
	- may not have exhaustive list of case-sensitive attributes
*/
function translateAttrName(n) {
	switch(n) {
		case 'class'		:	return 'className';
		case 'for'			:	return 'htmlFor';
		case 'accesskey'	:	return 'accessKey';
		case 'colspan'		:	return 'colSpan';
		case 'rowspan'		:	return 'rowSpan';
		case 'maxlength'	:	return 'maxLength';
		case 'usemap'		:	return 'useMap';
		default				:	return n;
	}
}


/*
    JavaScript CSS Class Manipulator
    http://onlinetools.org/articles/unobtrusivejavascript/cssjsseparation.html
    
    This example function takes four parameters:

    a
        defines the action you want the function to perform.
    o
        the object in question.
    c1
        the name of the first class
    c2
        the name of the second class

    Possible actions are:

    swap
        replaces class c1 with class c2 in object o.
    add
        adds class c1 to the object o.
    remove
        removes class c1 from the object o.
    check
        test if class c1 is already applied to object o and returns true or false.
    
*/
function jscss(a, o, c1, c2) {
    
    switch (a){
        
        case 'swap':
            o.className = !jscss('check', o, c1) ? o.className.replace(c2,c1) : o.className.replace(c1, c2);
            break;
        
        case 'add':
            if (!jscss('check', o, c1)) {
                o.className += o.className ? ' '+c1 : c1;
            }
            break;
        
        case 'remove':
            var rep = o.className.match(' '+c1) ? ' '+c1 : c1;
            o.className = o.className.replace(rep,'');
            break;
        
        case 'check':
            return new RegExp('\\b'+c1+'\\b').test(o.className);
            break;
        
    } // end switch
    
}


/**
 * Simple Show/Hide
 *
 * Modifies an element's DOM CSS attributes to show or hide that
 * element.
 *
 * Last modified: 2007-11-06 {pf}
 */

function showHide(element) {
    
    if (element.style.display != 'none') {
        element.style.display = 'none';
    }
    else if (element.style.display != 'block') {
        element.style.display = 'block';
    }
    
}


/**
 * "getElementsByClassName"
 * http://www.snook.ca/archives/javascript/your_favourite_1/
 *
 * Finds all elements with a specific class, within a parent ('node'),
 * and returns them in an array.
 *
 * NOTE: Improved structure, regexp and readability. {pf}
 *
 * Last modified: 2006-11-22 {pf}
 */

function getElementsByClassName(classname, node) {

	var a = [];
	var re = new RegExp('(^| )' + classname + '( |$)');
	var els = node.getElementsByTagName("*");

	for (var i = 0, j = els.length; i < j; i++) {
		if (re.test(els[i].className)) {
			a.push(els[i]);
		}
	}

	return a;

}


/**
 * Validate Field
 *
 * Intelligently checks a form field to make sure either a selection has
 * been made or a value entered (as appropriate to the field type).
 *
 * Last modified: 2008-06-04 {sd}
*
 */

function validateField(field) {
	
	var set = false;
	
	switch(field.type) {
        case 'select':
		case 'select-one':
			if (field.options[field.selectedIndex].value == '') {
				return false;
			}
			break;
        case 'checkbox':
		case 'radio':
			if(!field.checked)
				return false;
			break;
		case 'password': //password confirmation added 04 June 2008. Requires having two password field on the submitted form. 
            return field;
            /*
			if(field.id == "password") {
				psswd = field;
			}
			//Grab the 'confirm' password field
			if(field.id == "confirm") {
				psswd_confirm = field;
			}
			//if both field variables fail to equal each other OR this field value is blank then fail.
			if((psswd.value != psswd_confirm.value) || field.value == '') {
				return false;
			}
            */
			break;
		default: // text/password
			if (getAttr(field, 'class').indexOf('validateEmail') >= 0 ) {
				var emailPattern = /^([a-zA-Z0-9_\-\.\']+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$/;
				var regExp = new RegExp(emailPattern);
				var validEmailAddr = regExp.test(field.value);
				if (!validEmailAddr) {
					return false;
				}
			}
			else if (field.value == '') {
				return false;
			}
        
	}
    
	return true;
    
}


/**
 * Look Up Label
 *
 * A relatively simple function that finds the label for the current
 * form element.
 *
 * Last modified: 2006-11-22 {pf}
 */

function lookUpLabel(field, form) {
    
	var htmlLabels = form.getElementsByTagName('label');
    
	for (var l = 0, htmlLabel; htmlLabel = htmlLabels[l]; l++) {
		if (getAttr(htmlLabel, 'for') == field.id) {
			return htmlLabel;
		}
	}
    
	return null;
    
}


/**
 * Get Parent By ClassName
 *
 * Last modified: 2008-01-31 {pf}
 */

function getParentByClassName(child, cssClass) {
    
    var parent = child.parentNode;
    
    if (parent.className == cssClass) {
        return parent;
    }
    
    return getParentByClassName(parent, cssClass);
    
}


/**
 * Check Requireds
 *
 * Alerts the user to any 'required' inputs, in the target form, which
 * have not been completed.
 *
 * Last modified: 2006-11-21 {pf}
 * Notes on use: 
 *	Each field that is intended to be checked must include a value of 'required' in their class definition
 * 	Make sure every required input field has an id otherwise this function will bug out and fail
 *	Try and make sure every field has an associated label, it wont break if it doesn't but types checkbox and radiobutton can only indicate their requirements through how the label changes.
 *	There needs to be a single 'requiredWarning' div within the <form> being checked
 *	For password comparison/validation the ids of the password fields need to be: password and confirm
 */
function checkRequireds(form) {

	var errorFlag = false;
    var objPasswordField = 0;

	//	Find the hidden warning box-out in our form
    //  TODO: probably needs defensive checks here in case no requiredWarning found so that it fails gracefully.
	var requiredWarning = getElementsByClassName('requiredWarning', form);
		requiredWarning = requiredWarning[0]; // only ever one per form

	//	Find all required elements that haven't been completed
	var requireds = getElementsByClassName('required', form);
	for (var r = 0, required; required = requireds[r]; r++) {
        
		if (required.type != undefined) {
			//	Field incomplete
            if(validateField(required) == required){ //password types return the field object for comparisson at the form 
                if(objPasswordField == 0) {
                    objPasswordField = required;
                }
                if(objPasswordField && (objPasswordField.value !=  required.value) || (required.value == '')) {
                    errorFlag = true;
                    errorField(required, form);
                    if((objPasswordField != required) && (required.value != '')) {
                        errorField(objPasswordField, form); 
                    }
                }
                if(((objPasswordField.value == required.value) && (objPasswordField != required) && required.value != '')) {
                    if (getAttr(required.parentNode, 'class') == 'requiredWrapper') {
                        removeErrorField(required, form);
                    }
                    if (getAttr(objPasswordField.parentNode, 'class') == 'requiredWrapper') {
                        removeErrorField(objPasswordField, form);
                    }
                }
            }
			if (validateField(required) == false) {
				//	Set error flag
				if (!errorFlag) {
					errorFlag = true;
				}
                
				//	Display warning message
				if (requiredWarning.style.display != 'block') {
					requiredWarning.style.display = 'block';
				}
     
				//	Apply warning wrapper if not already in place
				if (getAttr(required.parentNode, 'class').indexOf('requiredWrapper') != 0) {
					//	Highlight empty required field
					var clonedRequired = required.cloneNode(true);
					var requiredWrapper = document.createElement('div');
					var container = required.parentNode;
					container.replaceChild(requiredWrapper, required);
					setAttr(requiredWrapper, 'class', 'requiredWrapper');
					requiredWrapper.appendChild(clonedRequired);
				}
                
                //  Find the label element for the required item
                var requiredLabel = lookUpLabel(required, form);
                //  Highlight label
                //if statement added 2008 May 19 as a defensive check to fail gracefully when no label found - Sam Dwyer
                if(requiredLabel) {
                    jscss('add', requiredLabel, 'requiredError');
					// Change the bullet colour
					if(requiredLabel.getElementsByTagName('img').length)
						requiredLabel.getElementsByTagName('img')[0].src='/img/bul-required-on.gif';
                }
                
			}
            
			//	Field completed
			else {
                if(!errorFlag) {
    				//	Remove warning wrapper if already applied
    				if (getAttr(required.parentNode, 'class') == 'requiredWrapper') {
    					var requiredWrapper = required.parentNode;
    					var container = requiredWrapper.parentNode;
    					var clonedRequired = required.cloneNode(true);
    					container.replaceChild(required, requiredWrapper);
    				}
                    
                    //  Find the label element for the required item
                    var requiredLabel = lookUpLabel(required, form);
                    //  Remove highlight (if present)
                    jscss('remove', requiredLabel, 'requiredError');
    				// Change the bullet colour
    				if(requiredLabel.getElementsByTagName('img').length)
    					requiredLabel.getElementsByTagName('img')[0].src='/img/bul-required.gif';
                }
                    
			}
            
		}
        
	}
	if (errorFlag) {
        
        //	Scroll to warning's parent
        window.scroll(0, form.offsetTop);
        
		//	Prevent form submitting
		return false;
        
	}
	else {
		//	Submit form
		return true;
        
	}
	
}

function errorField(required, form) {
    var clonedRequired = required.cloneNode(true);
    var requiredWrapper = document.createElement('div');
    var container = required.parentNode;
    container.replaceChild(requiredWrapper, required);
    setAttr(requiredWrapper, 'class', 'requiredWrapper');
    requiredWrapper.appendChild(clonedRequired);
    var requiredLabel = lookUpLabel(required, form);
    //  Highlight label
    //if statement added 2008 May 19 as a defensive check to fail gracefully when no label found - Sam Dwyer
    if(requiredLabel) {
        jscss('add', requiredLabel, 'requiredError');
        // Change the bullet colour
        if(requiredLabel.getElementsByTagName('img').length)
            requiredLabel.getElementsByTagName('img')[0].src='/img/bul-required-on.gif';
    }
}

function removeErrorField(required, form) {
    var requiredWrapper = required.parentNode;
    var container = requiredWrapper.parentNode;
    var clonedRequired = required.cloneNode(true);
    container.replaceChild(required, requiredWrapper);
    //  Find the label element for the required item
    var requiredLabel = lookUpLabel(required, form);
    //  Remove highlight (if present)
    jscss('remove', requiredLabel, 'requiredError');
    // Change the bullet colour
    if(requiredLabel.getElementsByTagName('img').length)
        requiredLabel.getElementsByTagName('img')[0].src='/img/bul-required.gif';
}

/**
 * Find Requireds
 *
 * Automatically scans the page for forms, then adds an 'onsubmit'
 * function to any forms which contain input elements classed as
 * 'required'.
 *
 * Last modified: 2006-11-21 {pf}
 */

function findRequireds() {

	//	Find all forms on the page and identify those that need validating upon submission
	var forms = document.getElementsByTagName('form');
	if (forms.length >= 1) {
		for (var f = 0, form; form = forms[f]; f++) {
			var inputs = form.getElementsByTagName('input');
			for (var i = 0, input; input = inputs[i]; i++) {
				if (getAttr(input, 'class')
					&& getAttr(input, 'class').indexOf('required') >= 0) {
					form.onsubmit = function() {
						return checkRequireds(this);
					};
					break;
				}
			}
		}
	}

}

addLoadHandler(
	function() {
		findRequireds();
		return true;
	}
);

