// 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';
    }
    
}


/**
 *  Show/Hide Billing Information
 *
 *  Last modified: 2008-01-29 {pf}
 */

function showHideDeliveryAddress() {
    
    //  End function if required elements not present in page
    if (!document.getElementById('differentAddress') && !document.getElementById('deliveryInfo')) {
        return false;
    }
    
    var differentAddress = document.getElementById('differentAddress');
    var deliveryInfo = document.getElementById('deliveryInfo');
    
    if(differentAddress.checked) {
        deliveryInfo.style.display = 'block';
    }
    
    differentAddress.onclick = function() {
        showHide(deliveryInfo);
    }
    
}

addLoadHandler(
    function() {
        return new showHideDeliveryAddress();
    }
);


/**
 *  Show Relevant Payment Form
 *
 *  Last modified: 2008-01-29 {pf}
*/

function showHidePaymentForm() {
    
    //  End function if required elements not present in page
    if (!document.getElementById('paymentInformation') && !document.getElementById('subscriptionType')) {
        return false;
    }
    
    var subscriptionType = document.getElementById('subscriptionType');
    var creditCardDetails = document.getElementById('creditCardDetails');
    var directDebitMandate = document.getElementById('directDebitMandate');
    
    //  Reveal correct form on init
    switchPaymentInfo(subscriptionType.options[subscriptionType.selectedIndex].className, creditCardDetails, directDebitMandate);
    
    //  Reveal correct form on change
    subscriptionType.onchange = function() {
        switchPaymentInfo(subscriptionType.options[subscriptionType.selectedIndex].className, creditCardDetails, directDebitMandate);
    }
    
}


function switchPaymentInfo(selectedSubsType, creditCardDetails, directDebitMandate) {
    
    switch(selectedSubsType) {
        
        case 'creditCard':
            creditCardDetails.style.display = 'block';
            directDebitMandate.style.display = 'none';
            break;
        
        case 'directDebit':
            directDebitMandate.style.display = 'block';
            creditCardDetails.style.display = 'none';
            break;
        
        default:
            creditCardDetails.style.display = 'none';
            directDebitMandate.style.display = 'none';
        
    }
    
}

addLoadHandler(
    function() {
        return new showHidePaymentForm();
    }
);


/**
 *  Show Post Code Search Results
 *
 *  Last modified: 2008-01-29 {pf}
 */

function showPostcodeResults() {
    
    //  End function if required elements not present in page
    if (!document.getElementById('billingPostcode') && !document.getElementById('billingPostcodeResults') && !document.getElementById('deliveryPostcode') && !document.getElementById('deliveryPostcodeResults')) {
        return false;
    }
    
    var billingPostcodeLink = document.getElementById('billingPostcodeLink');
    var billingPostcodeResults = document.getElementById('billingPostcodeResults');
    var billingPostcodeCancel = document.getElementById('billingPostcodeCancel');
    var deliveryPostcodeLink = document.getElementById('deliveryPostcodeLink');
    var deliveryPostcodeResults = document.getElementById('deliveryPostcodeResults');
    var deliveryPostcodeCancel = document.getElementById('deliveryPostcodeCancel');
    
    billingPostcodeLink.onclick = function() {
        billingPostcodeResults.style.display = 'block';
        return false; // prevent default hyperlink behaviour
    }
    
    billingPostcodeCancel.onclick = function() {
        billingPostcodeResults.style.display = 'none';
        return false; // prevent default hyperlink behaviour
    }
    
    deliveryPostcodeLink.onclick = function() {
        deliveryPostcodeResults.style.display = 'block';
        return false; // prevent default hyperlink behaviour
    }
    
    deliveryPostcodeCancel.onclick = function() {
        deliveryPostcodeResults.style.display = 'none';
        return false; // prevent default hyperlink behaviour
    }
    
}

addLoadHandler(
    function() {
        return new showPostcodeResults();
    }
);


/**
 * "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: 2006-11-22 {pf}
 */

function validateField(field) {
    
	switch(field.type) {
        
        case 'select':
		case 'select-one':
			if (field.options[field.selectedIndex].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}
 */
function checkRequireds(form) {

	var errorFlag = false;

	//	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)) {
                
				//	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);
					if(getAttr(clonedRequired, 'class').indexOf('first') != -1) {
						//setAttr(clonedRequired, 'class', 'required');
						clonedRequired.className = 'required';
						setAttr(requiredWrapper, 'class', 'requiredWrapper small');
					} else {
						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');
                }
                
			}
            
			//	Field completed
			else {
                
				//	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');
                
			}
            
		}
        
	}
	if (errorFlag) {
        
        //	Scroll to warning's parent
        window.scroll(0, form.offsetTop);
        
		//	Prevent form submitting
		return false;
        
	}
	else {
        
		//	Submit form
		return true;
        
	}

    
}


/**
 * 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() {
		//CH-dropped: findRequireds();
		return true;
	}
);


/**
 *	Title As Value [jQuery]
 *
 *	Looks for any classed INPUT text elements that have a title
 *	attribute and pre-populates the value with it.
 */

function titleAsValue() {
	
	$(':input.titleAsValue').each( function() {
		
		//	Safety check, in case 'class' has been used by 'title' not set
		if ( $(this).attr('title') && $(this).attr('title').length > 0 ) {
			
			//	Populate value with title on load (if empty; respects browser auto-population)
			if ( $(this).val() == '' ) {
				$(this).val( $(this).attr('title') );
			}
			
			//	Empty title from value (if same)
			$(this).focus( function(){
				if ( $(this).val() == $(this).attr('title') ) {
					$(this).val('');
				}
			});
			
			//	Repopulate value with title (if empty)
			$(this).blur( function() {
				if ( $(this).val() == '' ) {
					$(this).val( $(this).attr('title') );
				}
			});
			
		}
		
	});
	
}

//	Bind titles as values on page load
$(document).ready( function() {
	if ( $(':input.titleAsValue').length > 0 ) {
		titleAsValue();
	}
});


//  'The markets' Buttons for the homepage
function highlightMarketsButton(showID) {
    $('li.marketsButton a').each( function() {
        if ( $(this).attr('href').substring(1) == showID ) {
            $(this).parent().addClass('current');
        }
        else {
            $(this).parent().removeClass('current');
        }
    });
}

function showMarketsContent(showID) {
    highlightMarketsButton(showID);
    $('#marketsContent>li').each( function() {
        if ( $(this).attr('id') == showID ) {
            $(this).show();
        }
        else {
            $(this).hide();
        }
    });
}

function marketsButtons() {
    $('li.marketsButton a').each( function() {
        $(this).click( function() {
            var thisID = $(this).attr('href');
                thisID = thisID.substring(1);//strips off the leading hash (#) char from the href
            showMarketsContent(thisID);
            return false;
        });
    });
}

$(document).ready( function() {
    if ( $('#theMarkets') ) {
        marketsButtons();
    }
});
