// 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,"");
};

// -- 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 args = arguments, i = args.length, str = this, rex;
	if(i > 0) {
		while(i--) { str = str.replace(new RegExp("\\{"+i+"\\}"),args[i]);};
	} else {
		throw new Error("No string found for formatting");
	};
	return str;
};

// find and return all html tags in a string
String.prototype.findHTML = String.prototype.findHTML || function(){
	return this.match(/<\/?\w+((\s+\w+(\s*=\s*(?:".*?"|'.*?'|[^'">\s]+))?)+\s*|\s*)\/?>/g);
};

// remove all html tags from string
String.prototype.stripHTML = String.prototype.stripHTML || function(){
	return this.replace(/<\/?\w+((\s+\w+(\s*=\s*(?:".*?"|'.*?'|[^'">\s]+))?)+\s*|\s*)\/?>/g,"");
};

// -- 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")
};

// -- very basic date formatting
Date.prototype.format = Date.prototype.format || function(str){
	var date = this.toUTCString().split(" "),
	dayName = date[0].replace(",",""),
	day = date[1],
	monthName = date[2],
	month = this.getMonth() + 1,
	year = date[3];
	str = str.replace(/(D-dd-M-)/,dayName + ", " + day + " " + monthName + " ")
	.replace(/(dd)/,day)
	.replace(/D+\W/,dayName + " ")
	.replace(/(mm)/,(month > 9 ? month : "0" + month))
	.replace(/M+\W/,monthName + " ")
	.replace(/(yyyy)/,year)
	.replace(/(yy)/,year.substring(2,4));
	return str;
};

// FAT JS HELPER CLASS
(function(){
	var FatJS = function(){},
	Pbl = FatJS.prototype = {};
	
	// -- set the value of an element to be an alphanumeric-only string
	Pbl.setNonAlpha = function(elm, str){
		if(typeof elm.value === "string") {
			elm.value = str.toAlphaNumeric();
		} else {
			throw new Error("Element has no useable value attribute");
		};
	};
	
	// -- 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));
	};
	
	// -- basic date formatting	
	Pbl.dateFormat = function(date,str){
		date = (arguments.length > 1 && date.constructor === Date) ? date : new Date();
		return date.format(str);
	};
	
	// -- 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 array.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(" ");
		};
	})();
	
	// -- asynchronously convert XML to JS, return js in callback
	// -- eg Fat.jsFromXML("/xml/test/xml",function(js){ alert(js.length) });
	Pbl.jsFromXML = function(url,fn) {
		var o = $.ajax({
			type: 'GET',
			url: url,
			contentType: 'text/xml',
			success: function(data) {
				var jsObj = parseXML(data.documentElement);
				fn(jsObj);
			},
			error: function(e){window.ajaxResponseError = e;}
		}),
		parseXML = function(xml) {
			var obj = {},
			children = xml.children || xml.childNodes,
			len = children.length,
			i = 0,
			idx = 0;
			while(i<len) {
				var child = children[i],
				prop = {},
				propName = child.nodeName;
				if(child.nodeName !== "#text" && child.nodeName !== "#comment" && child.nodeName !== "xml") {
					propName = child.nodeName;
					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].children = parseXML(child);
					};
					if(aI > 0) {
						while(aI--) {
							var att = atts[aI];
							obj[propName][att.name] = att.value;
						};
					};
					idx++;
				};
				i++;
			};	 
			return obj;
		};
	};

	// -- get className instead of class for old IE
	Pbl.classAttribute = (function(){
		var ie7 = /MSIE 7.0/g.test(navigator.userAgent),
		ie6 = /MSIE 6.0/g.test(navigator.userAgent),
		docMode = document.documentMode ? document.documentMode.toString() : "",
		classAttr = "class";
		if(docMode === "7" || docMode === "5") {
			classAttr = "className";
		} else if(docMode === "") {
			classAttr = (ie7 || ie6) ? "className" : "class";
		};
		return classAttr;
	})();
	
	// -- 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){
		$(elm).twitter(config);
	};
	
	// -- setup faqs section in an element
	Pbl.setFaqs = function(elem){
		var elem = elem.nodeName ? elem : document.getElementById(elem);
		if(elem) {
			var faqs = $(elem).find(".faq"), len = faqs.length, i = 0,
			answers = [];
			while(i < len) {
				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.push(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;
				};
				i++;
			};
		};
	};
	
	// -- 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){
		var _b = this,
		wrap = $(selector),
		controls = document.createElement("div"),
		slides = [],
		links = [],
		curSlide = 0;
		this.makeSlide = function(data){
			var slide = document.createElement("div"),
			title = "<h1>" + data.title + "</h1>",
			subtitle = "<p>" + data.subtitle + "</p>",
			link = "<a href='"+data.link+"' target='"+data.target+"' class='styledLink'>Read more &raquo;</a>",
			text = "<div class='slideText'>" + title + subtitle + link + "</div>",
			navLink = document.createElement("a");
			slide.setAttribute(Pbl.classAttribute,"slide");
			slide.style.backgroundImage = "url("+data.image+")";
			slide.innerHTML = text;
			slides.push(slide);
			links.push(navLink);
			navLink.innerHTML = "&nbsp;"
			navLink.onclick = function(){
				var idx = (links.indexOf  && links.indexOf(this)) || Pbl.indexOfArray(this,links);
				_b.showSlide(idx);
			};
			wrap.append(slide);
			controls.appendChild(navLink);
		};
		controls.setAttribute("id","bannerControls");
		wrap.append(controls);
		this.showSlide = function(i){
			$(slides[curSlide]).stop().animate({"opacity":"0"},function(){
				this.style.display = "none";
			});
			$(slides[i]).stop().css("display","block").animate({"opacity":"1"});
			links[curSlide].removeAttribute(Pbl.classAttribute)
			links[i].setAttribute(Pbl.classAttribute,"on");
			curSlide = i;
		};
		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){
		try {
			var y = $.ajax({
				type: 'GET',
				url: '/json/generate_Gallery.asp?GAL_ID=' + id,
				dataType: 'json',
				success: function(data) {
					return new MediaGallery(elm,5,type,data);
				},
				error: function(e){window.ajaxResponseError = e;}
			});
		} catch(e) {
			window.mediaGalleryError = e;
		};
	};
	
	// -- property for placeholder attributes
	Pbl.hasPlaceholders = (function(){
		return ((Modernizr && Modernizr.input.placeholder) || false);
	})();
	
	// -- set placeholders for non-html5 browsers
	Pbl.setPlaceholders = function(){
		if(!Pbl.hasPlaceholders) {
			$(document.forms).find('input').each(function() {
				var text = $(this).attr("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 };
					};
				};
			});
		};
	};
	
	// -- 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,
			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",
			
			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
		},
		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;
			for(var name in group) {
				if(group.hasOwnProperty(name)) {
					if(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);
		},
		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(elm.formError) {
				removeError(elm);
			};
			if(Pbl.validationConfig.elmCallback) {
				Pbl.validationConfig.elmCallback(elm, elm.form);
			};
			if(Pbl.validationConfig.errors) {
				var customError = elm.getAttribute("data-error"),
				error;
				elm.style.border = "2px solid #c00";
				if(Pbl.validationConfig.verboseErrors) {
					error =  document.createElement("span");
					error.innerHTML = customError || errStr;
					error.setAttribute(Pbl.classAttribute,"errorStyle");
					$(elm).after(error);
					elm.formError = error;
				};
			};
		},
		removeError = function(elm){
			elm.removeAttribute("style");
			if(elm.formError) {
				$(elm.formError).remove();
			};
		};
		return function(formName,handler) {
						
			if(formName.jquery) {
				formName.each(function(){
					Pbl.validate(this,handler);
				});
				return false;
			} else if(formName === document.forms) {
				var len = formName.length, i = 0;
				while(i < len) {
					Pbl.validate(formName[i],handler)
					i++;
				};
				return false;
			};
			var form;
			if(formName.nodeName) {
				form = formName
			} else if(typeof document.forms[formName] !== "undefined") {
				form = document.forms[formName];
			} else {
				return Pbl.validate($(formName),handler);
			};
			if(typeof form !== "undefined") {
				var elemList = form.elements || $(form).find("input,select,textarea").toArray(),
				doValidation = function(elms) {
					var elms = elms.children || elms,
					len = elms.length,
					name = 0,
					isValidForm = true,
					isValidElem;
					while(name < len) {
						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.getAttribute(Pbl.classAttribute),
						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.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) && !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);
						};
						name++;
					};
					if(!isValidForm && Pbl.validationConfig.formCallback) {
						Pbl.validationConfig.formCallback(elem.form);
					};
					return isValidForm;
				};
				if(typeof handler !== "undefined") {
					$(form).find(handler)[0].onclick = function(){
						return doValidation(elemList);
					};
				} else {
					form.onsubmit = function(){
						return doValidation(elemList);
					}
				};
			};
		};
	})();
	
	window.Fat = new FatJS();
	
})();
