// Useful extra methods on native objects

// -- string trim function
String.prototype.trim = String.prototype.trim || function () {
	return this.replace(/[\s\0\n\t\r\f\v]+/g," ").replace(/(^\s*|\s*$)/g,"");
};

// -- remove all blank characters from a string
String.prototype.cleanse = String.prototype.cleanse || function () {
	return this.replace(/([\s\t\ ]{2,}|[\0\n\r\f\v])/g," ");
};

// -- replace non alphanumeric chars
String.prototype.toAlphaNumeric = String.prototype.toAlphaNumeric || function(){
	return this.replace(/[^\w+-]+ */g, "-")
};

// -- insert substrings into a string
String.prototype.format = String.prototype.format || function(){
	var	str = this, args = arguments, i = args.length;
	while(i--) { str = str.replace(new RegExp("\\{"+i+"\\}","g"),args[i]) };
	return str.toString();
};

(function(){
	var html_regex = /<\/?\w+((\s+\w+(\s*=\s*(?:".*?"|'.*?'|[^'">\s]+))?)+\s*|\s*)\/?>/g;
	// find and return all html tags in a string
	String.prototype.findHTML = String.prototype.findHTML || function(){
		return this.match(html_regex);
	};
	// remove all html tags from string
	String.prototype.stripHTML = String.prototype.stripHTML || function(){
		return this.replace(html_regex,"");
	};
})();

// -- format prices [ e.g. var formattedPrice = rawPrice.toPrice() ]
Number.prototype.toPrice = Number.prototype.toPrice || function(i){
    var $n = (Math.round(parseFloat(this)*100)/100).toString();
    return i == 0 ? $n : $n.toPrice();
};

String.prototype.toPrice = String.prototype.toPrice || function(i){
    if (i == 0) { return this };
    var a = parseFloat(this).toPrice(0).split("."),
    b = a[0].split(""), x = b.length, c = 1;
    while(x--) { if(c % 3 == 0 && x !== 0) { b.splice(x,0,",") }; c ++; };
    return b.join("") + (a[1] ? "." + (a[1].length == 1 ? a[1] + "0" : a[1]) : ".00")
};


// FAT JS HELPER CLASS
(function(){
	var FatJS = function(){},
	Pbl = FatJS.prototype = {};
	
	// -- quick reference to document.getElementById	
	Pbl.get = function(str){ return document.getElementById(str); };
	
	// -- quick reference to document.createElement	
	Pbl.make = function(str){ return document.createElement(str); };
	
	// -- hasOwnProperty reference ( use "Fat.hasOwnProperty.call(object,key)" )
	Pbl.hasOwnProp = {}.hasOwnProperty;
	
	// -- set the value of an element to be an alphanumeric-only string
	Pbl.setNonAlpha = function(elm, str){
		try {
			if(typeof elm.value === "string") {
				elm.value = str.toAlphaNumeric();
			} else {
				throw "Element has no useable value attribute";
			};
		} catch(err) {
			console.log(err);
		};
	};
	
	// -- insert substrings into a string
	Pbl.stringFormat = function(){
		var args = Pbl.makeArray(arguments),
		str = args.splice(0,1)[0];
		return str.format.apply(str, [].slice.call(arguments, 1));
	};
	
	// -- check if an object is empty. returns false if you pass in something that isn't an object/array
	Pbl.isEmpty = function(b) {
		var a = {};
		for (var x in b) { if(a[x] !== b[x]) { return false; }; }
		return true;
	};
	
	// -- convert object to array
	Pbl.makeArray = function(b){
		var a = [], i = 0;
		for(var n in b){ a[i] = b[n]; i++; };
		return a;
	};
	
	// -- strip all html from a string
	Pbl.stripHTML = function(str){
		return str.stripHTML();
	};
	
	// -- find the index of an item in an array (use arrayVariable.indexOf(item) in newer browsers)
	Pbl.indexOfArray = function(name,array) {
		var i = 0;
		for(var x in array) {
			if(array[x] === name) { return i; };
			i++;
		};
		return -1;
	};
	
	// -- run through a twitter post and link stuff up
	Pbl.parseTweet = (function(){
		var rex = {
			shout: /@[\w-]*/,
			hash: /#[\w-]*/,
			link: /(http:\/\/).*/
		};
		return function(str){
			var strArr = str.split(" ");
			for(var x in strArr) {
				var word = strArr[x];
				if(rex.shout.test(word)) { // shoutouts
					var name = word.match(rex.shout)[0];
					name = "<a target='_blank' href='http://www.twitter.com/" + name.substring(1,name.length)+"'>" + name + "</a>"
					strArr[x] = word.replace(rex.shout,name);				
				} else if(rex.hash.test(word)) { // hashtags
					var hash = word.match(rex.hash)[0];
					hash = "<a target='_blank' href='http://search.twitter.com/search?q=" + encodeURIComponent(hash) + "'>" + hash + "</a>"
					strArr[x] = word.replace(rex.hash,hash);	
				} else if (rex.link.test(word)) { // links
					var link = word.match(rex.link)[0];
					link = "<a target='_blank' href='" + link +"'>" + link + "</a>"
					strArr[x] = word.replace(rex.link,link);		
				};
			};
			return strArr.join(" ");
		};
	})();
	
	// convert XML document to javascript object
	Pbl.parseXML = function(xml) {
		if( typeof xml === "string" ) return xml;
		var obj = {},
		children = xml.children || xml.childNodes,
		len = children.length,
		i = 0,
		// ignore a node if it's #text, #comment or xml
		disallowedNodes = /(\#(text|comment)|xml)/gi,
		idx = 0;
		while(i<len) {
			var child = children[i],
			prop = {},
			propName = child.nodeName;
			if(!propName.match(disallowedNodes)) {
				if(obj[propName]) { propName += (idx+1) };
				obj[propName] = {};
				var subChildren = child.children || child.childNodes,
				atts = child.attributes, aI = atts.length;
				if(subChildren.length === 0) {
					obj[propName]._nodeText = child.textContent || child.text;
				} else if(subChildren.length === 1 && subChildren[0].nodeName === "#text"){
					obj[propName]._nodeText = subChildren[0].textContent || subChildren[0].text;
				} else {
					obj[propName] = parseXML(child);
				};
				if(aI > 0) {
					obj[propName]._nodeAttributes = {};
					while(aI--) {
						var att = atts[aI];
						obj[propName]._nodeAttributes[att.name] = att.value;
					};
				};
				idx++;
			};
			i++;
		};	 
		return obj;
	};
	
	// -- asynchronously convert XML to JS, return js in callback
	// -- eg Fat.jsFromXML("/xml/test/xml",function(js){ alert(js.length) });
	Pbl.jsFromXML = (function(){
		// -- xhr object creation
		var XHR = (function(){
			var xhr = (window.XMLHttpRequest ?
				XMLHttpRequest :
				ActiveXObject("Msxml2.XMLHTTP"));
				
			return function() {
				return new xhr;
			};
		})(),
		// check if an xhr is successful
		xhrSuccess = function(status){
			var isValid = false;
				isValid = (!status && window.location.protocol === "file:") ||
						  (status >= 200 && status < 300) || status === 304;
				if(!isValid) throw "Bad request: status " + status;
			return isValid;
		};
		// return function opens up request, calls parse function if it's a good status and real XML
		return function(url, fn){
			var ts = "ts=" + new Date().getTime(),
			xhr = new XHR();
			xhr.onreadystatechange = function(){
				if(xhr.readyState === 4) {
					try {
						if(xhrSuccess(xhr.status) && xhr.responseXML){ 
							fn(Pbl.parseXML(xhr.responseXML));
						} else throw "No XML returned";
					} catch(err) {
						console.log(err + " from " + url);
						fn(err + " from " + url);
					};
				};
			};
			url += (url.indexOf("?") !== -1 ? "&" : "?") + ts;
			xhr.open("GET",url,true);
			xhr.send("");
		};
	})();
		
	// -- add a function to the window load event
	Pbl.addLoadEvent = function (fn) {
		var loadFunc = window.onload;
		if (typeof loadFunc !== "function") {
			window.onload = fn;	
		} else {
			window.onload = function() {
				loadFunc();
				fn();
			};
		};
	};
	
	// -- add a twitter feed to an element
	Pbl.twitter = function(elm,config){
		return new TwitterFeed(elm,config);
	};
	
	// -- setup faqs section in an element
	Pbl.setFaqs = function(elem){
		var elem = elem.nodeName ? elem : document.getElementById(elem);
		if(!elem) return;
		
		var faqs = $(elem).find(".faq"),
		answers = new Array(faqs.length),
		i = faqs.length;
		while(i--) {
			var faq = $(faqs[i]),
			answer = faq.find(".answer")[0],
			btn = faq.find("h3")[0],
			height = answer.clientHeight;
			answer.style.height = "0px";
			answer.opened = false;
			answers[i] = answer;
			btn.targetHeight = height;
			btn.clickIdx = i;
			btn.onclick = function(){
				var theAnswer = answers[this.clickIdx],
				theHeight = theAnswer.opened ? 0 : this.targetHeight;
				$(theAnswer).stop().animate({"height":theHeight});
				theAnswer.opened = !theAnswer.opened;
			};
		};
	};
	
	// -- show a google map
	Pbl.showMap = (function(mapDiv){
		if(mapDiv) {
			return function(lat,lng, zoom){
				var myOptions = {
					zoom: zoom,
					center: new google.maps.LatLng(lat,lng),
					mapTypeId: google.maps.MapTypeId.ROADMAP,
					scrollwheel:false
				},
				shape = {
					coord: [1, 1, 1, 20, 18, 20, 18 , 1],
					type: 'poly'
				},
				image = new google.maps.MarkerImage('/images/mapMarker.png'),
				map = new google.maps.Map(mapDiv,myOptions),
				marker = new google.maps.Marker({
					position: myOptions.center,
					map: map,
					icon: image,
					shape: shape,
					title: "",
					html: ""
				});				
			};
		} else return false;
	})(document.getElementById("googleMap"));
	
	// -- main class for a header banner	
	Pbl.Banner = function(selector,json,template){
		var _b = this,
		wrap = $(selector),
		controls = document.createElement("div"),
		slides = [],
		links = [],
		auto = true,
		timer,
		curSlide = 0;
		this.makeSlide = function(data){
			var slide = document.createElement("div");
			slide.innerHTML = template.format(data.title,data.subtitle,data.link,data.target);
			navLink = document.createElement("a");
			slide["className"] = "slide";
			slide.style.backgroundImage = "url("+data.image+")";
			slides.push(slide);
			links.push(navLink);
			navLink.innerHTML = "&nbsp;"
			navLink.onclick = function(){
				var idx = (links.indexOf  && links.indexOf(this)) || Pbl.indexOfArray(this,links);
				auto = false;
				clearTimeout(timer);
				_b.showSlide(idx);
			};
			wrap.append(slide);
			controls.appendChild(navLink);
		};
		controls.setAttribute("id","bannerControls");
		wrap.append(controls);
		this.showSlide = function(i){
			if( i >= slides.length)  i = 0 ;
			$(slides[curSlide]).stop().animate({"opacity":"0"},function(){this.style.display = "none"});
			$(slides[i]).stop().css("display","block").animate({"opacity":"1"});
			links[curSlide].removeAttribute("class")
			links[i]["className"] = "on";
			curSlide = i;
			if(auto) timer = setTimeout(function(){
				_b.showSlide(curSlide+1);
			},4000);
		};
		for(var x in json) {
			json.hasOwnProperty(x) && this.makeSlide(json[x]);
		};
		slides[0] && this.showSlide(0);
	};
	
	
	// -- reference MediaGallery class from MediaGallery.js
	Pbl.MediaGallery = function(elm,type,id,vids){
		return new MediaGallery(elm,type,id,vids,6);
	};
	
	// -- property for placeholder attributes
	Pbl.hasPlaceholders = (function(Modernizr){
		return ((Modernizr && Modernizr.input.placeholder) || false);
	})(window.Modernizr);
	
	// -- set placeholders for non-html5 browsers
	Pbl.setPlaceholders = function(){
		if(!Pbl.hasPlaceholders) {
			$(document.forms).find('input').each(function() {
				var text = this.getAttribute("placeholder");
				if(!!text && text != "" && this.value == "") {
					this.value = text;
					this.onfocus = function(){
						if(this.value == text) { this.value = '' };
					};
					this.onblur = function(){
						if(this.value == '') { this.value = text };
					};
				};
			});
		};
	};
	
	// -- remove faked placholders from elems
	Pbl.clearPlaceholders = function(){
		$(document.forms).find('input').each(function() {
			var text = this.getAttribute("placeholder");
			if(this.value === text) {
				this.value = '';
			};
		});
	};
	
	// -- error types for validations
	Pbl.validationConfig = {
		errors: true,
		verboseErrors: true,
		elmCallback: null,
		formCallback: null
	};
	
	// -- form validation
	Pbl.validate = (function(){
		var classes = {
			required: /required/i,
			email: /email/i,
			phone: /phone/i,
			postcode: /postcode/i,
			group: /group/i,
			numeric: /numeric/i,
			max: /max/i,
			min: /min/i,
			match: /match/i
		},
		msgs = {
			defaultMsg: "This field cannot be left blank",
			radioGroup: "Please choose one option",
			radio: "You have not chosen the correct option",
			check: "Please make sure to check this box",
			checkGroup: "Please choose at least one option",
			select: "Please choose an option",

			
			phone: "This field must contain a valid phone number",
			postcode: "This field must contain a valid postcode",
			email: "This field must contain a valid email address",
			
			numeric: "This field must contain a number",
			min: "This field has too few characters",
			max: "This field has too many characters",
			match: "This field must match the other"
		},
		regs = {
			email: /([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})/,
			phone: /[0-9\+]/,
			postcode: /(^[A-Z]{1,2}[0-9]{1,2})(\s|)([0-9][A-Z]{2}$)/i,
			non_number: /[\D]/gi,
			dots: /\./gi
		},
		types = {
			text: /(text|email|password)/,
			ignore: /(hidden|submit|image|button|reset)/i
		},
		hasValue = function(elm) { // -- basic value test
			if(typeof elm.value !== "undefined" && elm.value !== "") {
				return true;
			};
			return false;
		},
		hasEmail = function(elm){ // -- email test
			return regs.email.test(elm.value);
		},
		hasPostcode = function(elm){ // -- postcode test
			return regs.postcode.test(elm.value);
		},
		hasPhone = function(elm){ // -- phone test
			return regs.phone.test(elm.value);
		},
		hasGroup = function(elm) { // -- test that 1 radio button in a group is checked
			var group = elm.form.elements[elm.name],
			hasOneChecked = false,
			o = {}.hasOwnProperty;
			for(var name in group) {
				if(o.call(group,name) && group[name].checked) {
					return true;
				};
			};
			return false;
		},
		hasChecked = function(elm) { //  -- test for checked item
			return (typeof elm.checked !== "undefined" ? elm.checked : false);
		},
		hasOption = function(elm) { // -- test select box for option
			return (typeof elm.selectedIndex !== "undefined" ? elm.selectedIndex > 0 : false);
		},
		hasNumber = function(elm) { // -- test for a valid number
		// -- (test for non-number characters  - if more non-numbers than dots, NaN. if more than 1 dot, NaN)
			var nans = elm.value.match(regs.non_number) || "",
				dots = elm.value.match(regs.dots) || "";
			return (nans.length === dots.length) && dots.length < 2;
		},
		hasMax = function(elm,str){ // -- test for a maximum number of characters
			var limit = parseInt(str.split("-")[1].split(" ")[0]);
			return (elm.value.length <= limit);
		},
		hasMin = function(elm,str){ // -- test for a minimum number of characters
			var limit = parseInt(str.split("-")[1].split(" ")[0]);
			return (elm.value.length >= limit);
		},
		hasMatch = function(elm,str){ // -- test value matches value of another element
			var targetName = str.split("-")[1].split(" ")[0],
			target = elm.form.elements[targetName];
			return elm.value === target.value;
		},
		createError = function(elm,errStr){
			if(Pbl.validationConfig.elmCallback) {
				Pbl.validationConfig.elmCallback(elm, elm.form);
			};
			if(Pbl.validationConfig.errors) {
				removeError(elm);
				var customError = elm.getAttribute("data-error"),
				error;
				elm.style.color = "#c00";
				elm.style.fontWeight = "bold";
				if(Pbl.validationConfig.verboseErrors) {
					error =  document.createElement("span");
					error.innerHTML = customError || errStr;
					error["className"] = "errorStyle";
					elm.formError = error;
					$(elm).after(elm.formError);
				};
			};
		},
		removeError = function(elm){
			elm.removeAttribute("style");
			if(elm.formError) {
				$(elm.formError).remove();
				elm.formError = null;
			};
		};
		return function(formName,handler) {
			if (typeof formName === "undefined") {
				for ( var len = document.forms.length, i = 0; i < len; i++ ) {
					Fat.validate(document.forms[i])
				};
				return false;
			} else if(formName.jquery) {
				formName.each(function(){
					Fat.validate(this);
				});
				return false;
			};
			var form;
			if(formName.nodeName) {
				form = formName
			} else if(typeof document.forms[formName] !== "undefined") {
				form = document.forms[formName];
			} else {
				return Fat.validate($(formName),handler);
			};
			if(typeof form !== "undefined" && form.nodeName.toLowerCase() === "form") {
				form.noValidate = true;
				var elemList = form.elements || $(form).find("input,select,textarea"),
				doValidation = function(elms) {
					var isValidForm = true,
					isValidElem,
					name = elms.length;
					while ( name-- ) { 
						isValidElem = true;
						var elem = elms[name],
						inpValue = typeof elem.value !== "undefined" ? elem.value  : "",
						nodeName = elem.nodeName.toLowerCase(),
						nodeType = typeof elem.type !== "undefined" ? elem.type.toLowerCase()  : "",
						className = elem["className"],
						required = classes.required.test(className),
						errorStr = "";
						
						if(required && !types.ignore.test(nodeType)) {
							if(nodeName === "input" || nodeName === "textarea") { // -- test all inputs/textareas
								elem.onkeyup = elem.onkeyup || function(){
									doValidation([this])
								};
								if(types.text.test(nodeType) || nodeName === "textarea") { // -- test "typeable" inputs
									
									if(!hasValue(elem)) { // -- basic value test
										isValidElem = false;
										errorStr = msgs.defaultMsg;
									} else if(classes.email.test(className) && !hasEmail(elem)) { // email test
										isValidElem = false;
										errorStr = msgs.email;
									} else if(classes.postcode.test(className) && !hasPostcode(elem)) { // postcode test
										isValidElem = false;
										errorStr = msgs.postcode;
									} else if(classes.phone.test(className) && !hasPhone(elem)) { // phone no. test
										isValidElem = false;
										errorStr = msgs.phone;
									} else if(classes.numeric.test(className) && !hasNumber(elem)) { // phone no. test
										isValidElem = false;
										errorStr = msgs.numeric;
									} else if(classes.max.test(className) && !hasMax(elem,className)) { // -- test for too many chars
										isValidElem = false;
										errorStr = msgs.max;
									} else if(classes.min.test(className) && !hasMin(elem,className)) { // -- test for too few chars
										isValidElem = false;
										errorStr = msgs.min;
									} else if(classes.match.test(className) && !hasMatch(elem,className)) { // -- test for matching values
										isValidElem = false;
										errorStr = msgs.match;
									};
									
								} else if(nodeType === "radio" || nodeType === "checkbox") { // test radio/checkbox groups
									elem.onclick = elem.onclick || function(){
										doValidation([this]);
									};
									
									if(classes.group.test(className)) {
										if(!hasGroup(elem)) { // any is checked?
											isValidElem = false;
											errorStr = nodeType === "checkbox" ? msgs.checkGroup : msgs.radioGroup;
										};
									} else {
										if(!hasChecked(elem)) { // specific is checked
											isValidElem = false;
											errorStr = nodeType === "checkbox" ? msgs.check : msgs.radio;
										};
									};
									
								};
							} else if(nodeName === "select" && !hasOption(elem)) { // -- test select boxes
								elem.onchange = elem.onchange || function(){
									doValidation([this])
								};
								errorStr = msgs.select;
								isValidElem = false;
							};
						};
						if(!isValidElem) {
							createError(elem, errorStr);
							isValidForm = false;
						} else {
							removeError(elem);
						};
					};
					if(!isValidForm && Pbl.validationConfig.formCallback) {
						Pbl.validationConfig.formCallback(elem.form);
					};
					if(!Pbl.hasPlaceholders) {
						Pbl.setPlaceholders();
					};
					return isValidForm;
				},
				evtFunc = function(oldFunc){
					if(!Pbl.hasPlaceholders) {
						Pbl.clearPlaceholders();
					};
					var isValid = doValidation(elemList);
					if(isValid) {
						oldFunc && oldFunc();
					} else {
						return false;
					};
				};
				if(typeof handler !== "undefined") {
					$(handler)[0].onclick = evtFunc;
				} else {
					var oldSubmit = form.onsubmit;
					form.onsubmit = function() {
						return evtFunc(oldSubmit);
					};
				};
			};
		};
	})();
	
	window.Fat = new FatJS();
	
})();
