$(document).ready(function() {
	
	jQuery.fn.animate.defaults.avoidTransforms = true;
	jQuery.fn.animate.defaults.useTranslate3d = true;
	
	if ($.browser.SafariMobile) {
		$('body').addClass('ios');
	}
	
	if ($('#product-list').length > 0) { initProductList(); }
	if ($('#product-info').length > 0 && isSet(ProductPageConfig.variations) && isSet(ProductPageConfig.prices)) { initProductPage(); }
	if ($('#basket').length > 0) { initBasketPage(); }
	if ($('#checkout').length > 0) { initCheckoutPage(); }
	if ($('#banner .slides').length > 0) { initBannerSlides(); }
    
});

var Config = {
	clickEvent: ($.browser.touch) ? 'touchstart' : 'click'
}

//////////////////////////// BANNER SLIDES ///////////////////////////////

function initBannerSlides() {
	bannerSlides = new BannerSlides($('#banner'), 7000, 800);
}

function BannerSlides(wrap, period, animationDuration) {
	
	var self = this,
	nextBut = $('.arrow.next', wrap),
	prevBut = $('.arrow.prev', wrap).css({ 'display':'block' }).hide(),
	slidesWrap = $('.slides', wrap),
	$imgs = $('img', slidesWrap),
	pImg = -1, cImg = 0, imgs = {}, numSlides = $imgs.length, loading = false, loader = null, cX = 0, w = 920, moving = false, timeout = null, scrolling = true;
	
	function init() {
		
		if (numSlides < 2) { return; }
		
		period = period + animationDuration;
		slidesWrap.css({ 'width':$imgs.length*w });
		nextBut.click(function() {
			
			scrolling = false;
			nextSlide();
			return false;
			
		});
		prevBut.click(function() {
			
			scrolling = false;
			prevSlide();
			return false;
			
		});
		imgs[0] = $imgs.eq(0);
		
		timeout = setTimeout(nextSlide, period);
		
	}
	
	function nextSlide() {
		if (loading || moving) { return false; }
		clearTimeout(timeout);
		pImg = cImg;
		cImg = (cImg >= numSlides-1) ? 0 : cImg+1;
		cX += w;
		var newImg = imgs[cImg];
		if (!isSet(newImg)) {
			newImg = imgs[cImg] = $imgs.eq(cImg).css({ 'position':'absolute', 'top': 0, 'display':'block', 'left': cX }).load(onLoad).attr({ 'src':$imgs.eq(cImg).attr('url') });
		} else {
			newImg.show().css({ 'left': cX });
			gotoCurrentSlide();
		}
		return false;
	}
	
	function prevSlide() {
		if (loading || moving) { return false; }
		clearTimeout(timeout);
		pImg = cImg;
		cImg = (cImg > 0) ? cImg-1 : numSlides-1;
		cX -= w;
		var newImg = imgs[cImg];
		if (!isSet(newImg)) {
			newImg = imgs[cImg] = $imgs.eq(cImg).css({ 'position':'absolute', 'top': 0, 'display':'block', 'left': cX }).load(onLoad).attr({ 'src':$imgs.eq(cImg).attr('url') });
		} else {
			newImg.show().css({ 'left': cX });
			gotoCurrentSlide();
		}
		return false;
	}
	
	function onLoad() {
		loading = false;
		// Hide loader?
		gotoCurrentSlide();
	}
	
	function gotoCurrentSlide() {
		moving = true;
		slidesWrap.animate({ 'left': -cX }, { 'duration': 600, 'easing': 'easeInOutCubic', 'queue': false, 'complete': cleanUp });
		if (scrolling) {
			timeout = setTimeout(nextSlide, period);
		}
		checkNextPrev();
	}
	
	function cleanUp() {
		for (var p in imgs) {
			if (p != cImg) {
				imgs[p].hide();
			} else {
				imgs[p].show().css({ 'left': 0 });
			}
		}
		slidesWrap.stop(true, false).css({ 'left': 0 });
		cX = 0;
		moving = false;
	}
	
	function checkNextPrev() {
		// Nothing yet
		prevBut.show();
	}
	
	init();
	
}

//////////////////////////// CHECKOUT PAGE ///////////////////////////////

function initCheckoutPage() {
	
	var checkoutPage = new CheckoutPage();
	
}

function CheckoutPage() {
	
	var self = this, billingCheckbox = $('#BillingAddressTheSame').change(billingCheckboxChange), billingDetails = $('#billingHide'), billingDetailsHeight = 0, billingInputs = $('input, textarea, select', billingDetails), countrySelect = $('select#Country').change(countrySelectChange), stateWrap = $('#stateWrap').css({ 'display':'block' }).hide(), billingCountrySelect = $('select#BillingCountry').change(billingCountrySelectChange), billingStateWrap = $('#billingStateWrap').css({ 'display':'block' }).hide(), isSame = false;
	
	function init() {
		
		$('.formHelp').each(function(i) {
			
			var el = $(this);
			var popup = $('.popup', el).css({ 'display':'block', 'opacity':0 });
			
			el.hover(function() {
				el.addClass('hover');
				popup.animate({ 'opacity':1 }, { 'duration':200, 'queue':false });
			}, function() {
				popup.animate({ 'opacity':0 }, { 'duration':200, 'queue':false, 'complete': outComplete });
			});
			
			function outComplete() {
				el.removeClass('hover');
			}
			
		});
		
		billingCheckboxChange(false);
		countrySelectChange();
		billingCountrySelectChange();
		
	}
	
	function billingCountrySelectChange() {
		
		if (isSame) { return; }
		
		var countryId = billingCountrySelect.val();
		var selectedOption = $('option[value="' + countryId + '"]', billingCountrySelect);
		
		if (selectedOption.text().toLowerCase() == "united states") {
			billingStateWrap.show();
		} else {
			billingStateWrap.hide();
		}
		
	}
	
	function countrySelectChange() {
		
		var countryId = countrySelect.val();
		var selectedOption = $('option[value="' + countryId + '"]', countrySelect);
		
		if (selectedOption.text().toLowerCase() == "united states") {
			stateWrap.show();
		} else {
			stateWrap.hide();
		}
		
	}
	
	function billingCheckboxChange(animate) {
		
		if (billingDetailsHeight == 0) {
			billingDetailsHeight = billingDetails.height();
		}
		
		isSame = billingCheckbox.prop('checked');
		
		if (animate === false) {
			if (isSame) {
				billingDetails.stop(true, false).css({ 'opacity': 0 }).hide();
			} else {
				billingDetails.stop(true, false).css({ 'opacity':1 }).show();
			}
			billingCountrySelectChange();
		} else {
			if (isSame) {
				billingDetails.animate({ 'opacity':0 }, { 'duration':250, 'easing':'easeInOutCubic', 'complete':billingCloseComplete });
			} else {
				billingInputs.prop('disabled', false);
				billingDetails.stop(true,false).show().css({ 'opacity':0 }).animate({ 'opacity':1 }, { 'duration':250, 'easing':'easeInOutCubic' });
				billingCountrySelectChange();
			}
		}
		
	}
	
	function billingCloseComplete() {
		
		billingInputs.prop('disabled', true);
		billingCountrySelectChange();
		billingDetails.hide();
		
	}
	
	init();
	
}

//////////////////////////// BASKET PAGE ///////////////////////////////

function initBasketPage() {
	
	var basketPage = new BasketPage();
	
}

function Shaker(wrap, shakeLength) {
	
	var self = this, mainInt = null, shakeInc = 0;
	
	function init() {
		
	}
	
	self.start = function() {
		self.stop();
		shakeInc = 0;
		mainInt = setInterval(enterFrame, 50);
	}
	
	self.stop = function() {
		wrap.css({ 'margin-left': 0 });
		clearInterval(mainInt);
	}
	
	function enterFrame() {
		if (shakeInc == shakeLength) { self.stop(); }
		
		if (shakeInc % 2 == 0) {
			wrap.css({ 'margin-left': 1 });
		} else {
			wrap.css({ 'margin-left': -1 });
		}
		
		shakeInc++;
	}
	
	init();
	
}

function BasketPage() {
	
	var self = this,
	basketTable = $('#basket table').eq(0),
	basketLinkTotal = $('#basket-link .total'),
	deliveryCost = $('#delivery-cost span'),
	basketTotal = $('#basket-total'),
	deliverySelect = $('#delivery-select').change(updateDelivery),
	cDeliveryCost = parseFloat(deliveryCost.text().substr(1)),
	cDeliveryId = deliverySelect.val(),
	deliveryUpdateURL = deliverySelect.attr('data-url'),
	subtotal = parseFloat($('#basket-subtotal').text().substr(1)),
	deliveryLoader = $('<span class="loader"></span>').css({ 'opacity': 0 }).appendTo($('#delivery-cost')),
	discountRow = $('#basket-totals .discount'),
	discountValue = $('#discount-value span'),
	discountInput = $('#basket-proms input.t'),
	discountForm = $('#basket-proms form'),
	discountURL = discountForm.attr('action'),
	cDiscount = 0,
	cDiscountCode = discountInput.val(),
	cRequest = null,
	cDiscountRequest = null,
	checkoutButs = $('.but.checkout-but, .but.paypal-but').click(checkoutClick),
	deliveryErrorMsg = $('<span class="err">Please select a delivery region...</span>').hide().appendTo('#basket-delivery .desc'),
	deliveryShaker = new Shaker($('#delivery-select, #basket-delivery span.err'), 10),
	discountErrorMsg = $('<span class="err">That code is invalid...</span>').hide().appendTo('#basket-proms .desc'),
	promsContent = $('#basket-proms form, #basket-proms p.desc'),
	codeApplied = false,
	codeAppliedMsg = null,
	hasErrors = false;
	
	function init() {
		
		if (!discountRow.eq(0).hasClass('none')) {
			cDiscount = parseFloat(discountValue.text());
			if (isNaN(cDiscount)) { cDiscount = 0; }
		} else {
			discountRow.hide();
		}
		
		discountForm.submit(updateDiscount);
		
		if (isNaN(cDeliveryCost)) { cDeliveryCost = 0; }
		if (isNaN(subtotal)) { subtotal = 0; }
		updateDelivery();
		
	}
	
	function checkoutClick() {
		
		if (!isSet(cDeliveryId) || cDeliveryId == '') {
			//if (hasErrors) {
				deliveryShaker.start();
			//}
			deliverySelect.addClass('err');
			deliveryErrorMsg.stop(true, false).fadeIn(200);
			hasErrors = true;
			return false;
		} else {
			
		}
		
	}
	
	function updateDiscount() {
		
		var newCode = discountInput.val();
		if (newCode == cDiscountCode || codeApplied) { return false; }
		
		cDiscountCode = newCode;
		
		// Abort the current request if it's not complete already
		if (cDiscountRequest != null && cDiscountRequest.readyState != 4) {
			cDiscountRequest.abort();
		}
		
		cDiscountRequest = $.ajax({
			'url': discountURL,
			'dataType': 'json',
			'data': { 'voucherCode':cDiscountCode },
			'async': true,
			'success': onDiscountLoad,
			'cache': false
		});
		
		return false;
		
	}

	function onDiscountLoad(data) {
	
		if (data['invalid'] == "true") {
			discountErrorMsg.stop(true, false).fadeIn(200);
			discountInput.addClass('err');
		} else {
			discountErrorMsg.stop(true, false).fadeOut(200);
			discountInput.removeClass('err');
		}
		
		var newDiscount = parseFloat(data["discountTotal"]);
		if (newDiscount == cDiscount) { return; }
		cDiscount = newDiscount;
		if (isNaN(cDiscount)) { cDiscount = 0; }
		
		if (cDiscount == 0) {
			discountRow.stop(true, false).fadeOut(200);
		} else {
			discountRow.removeClass('none').stop(true, false).fadeIn(200);
			discountValue.text(cDiscount.toFixed(2));
		}
		
		codeApplied = true;
		promsContent.stop(true, false).fadeOut(200, showAppliedMsg);
		
		$('span.discounted', basketTable).remove();
		$('td', basketTable).removeClass('disc');
		
		if (isSet(data['discounted']) && data['discounted'].length > 0) {
			
			for (var i = 0, len = data['discounted'].length; i < len; i++) {
				
				var cRow = $('#basket table tr#' + data['discounted'][i]['id']);
				if (cRow.length > 0) {
					
					var priceTd = $('td.price', cRow).addClass('disc');
					var disc = $('<span class="discounted">&pound;' + data['discounted'][i]['price'] + '</span>').appendTo(priceTd);
					
				}
				
			}
			
		}
		
		updateTotals();
		
	}
	
	function showAppliedMsg() {
		
		if (codeAppliedMsg == null) {
			codeAppliedMsg = $('<p class="desc">Voucher code has been applied: <span class="code">' + cDiscountCode + '</span></p>').hide().insertAfter(discountForm);
		}
		codeAppliedMsg.fadeIn(200);
		
	}
	
	function updateDelivery() {
		
		var newVal = deliverySelect.val();
		if (newVal == cDeliveryId) { return; }
		
		cDeliveryId = newVal;
		
		// Abort the current request if it's not complete already
		if (cRequest != null && cRequest.readyState != 4) {
			cRequest.abort();
		}
		
		deliveryLoader.animate({ 'opacity': 1 }, { 'duration': 200, 'queue': false });
		
		cRequest = $.ajax({
			'url': deliveryUpdateURL,
			'data': { 'id':cDeliveryId },
			'async': true,
			'success': onDeliveryLoad,
			'cache': false
		});
		
	}
	
	function onDeliveryLoad(data) {
		
		deliveryLoader.stop(true, false).animate({ 'opacity': 0 }, { 'duration': 200, 'queue': false });
		
		var newDeliveryCost = parseFloat(data);
		if (newDeliveryCost == cDeliveryCost) { return; }
		cDeliveryCost = newDeliveryCost;
		
		if (isSet(cDeliveryId) && cDeliveryId != '') {
			deliverySelect.removeClass('err');
			deliveryErrorMsg.stop(true, false).fadeOut(200);
			hasErrors = false;
		}
		
		updateTotals();
		
	}
	
	function updateTotals() {
		
		var newTotal = (cDeliveryCost + subtotal) - cDiscount;
		
		basketTotal.text('£' + newTotal.toFixed(2));
		basketLinkTotal.text(newTotal.toFixed(2));
		if (cDeliveryCost == 0) {
			deliveryCost.text('---');
		} else {
			deliveryCost.text('£' + cDeliveryCost.toFixed(2));
		}
		
	}
	
	init();
	
}

//////////////////////////// PRODUCT PAGE ///////////////////////////////

var ProductPageConfig = {
	// Nothing!
}

function initProductPage() {
	
	var productPage = new ProductPage();
	
}

function ProductPage() {
	
	var self = this,
	priceWrap = $('#product-info .price'),
	price = $('.normal-price', priceWrap),
	from = $('.from', priceWrap),
	salePrice = $('.sale-price', priceWrap),
	colourSelect = $('#product-colour').change(colourChange),
	sizeSelect = $('#product-size').change(sizeChange),
	cColour = null, cSize = null,
	emptyOption = $('option', colourSelect).eq(0), origPrice = price.text(), origSalePrice = salePrice.text(), isSale = priceWrap.hasClass('sale'),
	errorMsg = $('<span class="err"></span>').hide().appendTo('#product-info .options'),
	form = $('#product-info form').submit(onSubmit);
	
	function init() {
		update();
	}
	
	function colourChange() {
		update();
	}
	
	function sizeChange() {
		cSize = sizeSelect.val();
		if (cSize == "") { cSize = null; }
		else { sizeSelect.removeClass('err'); }
	}
	
	function update() {
		
		var newColour = colourSelect.val();
		if (!isNaN(parseInt(newColour))) { newColour = parseInt(newColour); }
		else if (newColour == "") { newColour = null; }
		if (newColour == cColour) { return; }
		cColour = newColour;
		
		if (cColour != null) {
			colourSelect.removeClass('err');
		}
		
		populateSizes();
		updatePrice();
		
	}
	
	function populateSizes() {
		
		$('option', sizeSelect).remove();
		
		var sizes = ProductPageConfig.variations[cColour];
		if (!isSet(sizes)) {
			sizeSelect.append(emptyOption.clone());
			sizeSelect.prop('disabled', true);
			cSize = null;
			return;
		}
		
		if (sizes.length == 1) {
			//sizeSelect.prop('disabled', true);
			sizeSelect.append($('<option value="' + sizes[0] + '" selected>' + sizes[0] + '</option>'));
			cSize = sizeSelect.val();
			sizeSelect.removeClass('err');
		} else {
			sizeSelect.prop('disabled', false);
			sizeSelect.append(emptyOption.clone());
			for (var i = 0, len = sizes.length; i < len; i++) {
				sizeSelect.append($('<option value="' + sizes[i] + '"' + ((sizes[i] == cSize) ? ' selected' : '') + '>' + sizes[i] + '</option>'));
			}
			if ($.inArray(parseInt(cSize), sizes) == -1) {
				cSize = null;
			} else {
				sizeSelect.removeClass('err');
			}
		}
		
	}
	
	function updatePrice() {
		
		var newPrice = ProductPageConfig.prices[cColour];
		if (!isSet(newPrice)) {
			price.text(origPrice);
			if (isSale) {
				salePrice.text(origSalePrice);
				priceWrap.addClass('sale');
				from.show();
			}
			return;
		}
		newPrice = newPrice.split('|');
		
		if (newPrice.length > 1) {
			// It's a sale price
			salePrice.html('£' + newPrice[1]);
			priceWrap.addClass('sale');
		} else {
			// It's a normal price
			salePrice.html('');
			priceWrap.removeClass('sale');
		}
		
		price.text('£' + newPrice[0]);
		from.hide();
		
	}
	
	function onSubmit() {
		
		var msg = '';
		if (cColour == null) {
			msg = 'Please select a colour and size...';
			colourSelect.addClass('err');
			sizeSelect.addClass('err');
			errorMsg.text(msg).fadeIn(200);
			return false;
		} else if (cSize == null) {
			msg = 'Please select a size...';
			colourSelect.removeClass('err');
			sizeSelect.addClass('err');
			errorMsg.text(msg).fadeIn(200);
			return false;
		} else {
			errorMsg.fadeOut(200);
			colourSelect.removeClass('err');
			sizeSelect.removeClass('err');
		}
		
	}
	
	init();
	
}

//////////////////////////// PRODUCT LIST ///////////////////////////////

var ProductListConfig = {
	useJSCache: true
}

function initProductList() {
	
	var productList = new ProductList();
	
}

function ProductList() {
	
	var self = this,
	filtersWrap = $('#list-filter'),
	filters = [],
	filtersByName = {},
	form = $('form', filtersWrap),
	numFilters = 0,
	clearAllBut = $('<a href="#" class="clear-all">clear all<em></em></a>').hide().appendTo($('.h-main', filtersWrap)),
	values = {},
	qs = null,
	History = window.History,
	cRequest = null,
	inc = 0,
	url = form.attr('action'),
	list = new ProductResults(),
	pgn1 = new PaginationFilter(onFormChange),
	pgn2 = new PaginationFilter(onFormChange),
	firstLoad = true,
	cache = {},
	fakeLoadTimer = null,
	catID = $('input[name="catID"]', form).val(),
	isSale = $('input[name="isSale"]', form).val(), 
	isAll = $('input[name="isAll"]', form).val();
	
	
	function init() {
		
		pgn1.copy = pgn2;
		pgn2.copy = pgn1;
		
		addFilter(pgn1);
		addFilter(pgn2);
		
		pgn1.wrap.prependTo($('#product-list'))
		pgn2.wrap.appendTo($('#product-list')).addClass('bot');
		
		$('.item', filtersWrap).each(function(i) {
			var wrap = $(this);
			cItem = new Filter(wrap, onFormChange);
			addFilter(cItem);
		});
		
		numFilters = filters.length;
		
		$('.clear-all', filtersWrap).bind(Config.clickEvent, function() {
			self.clearAll();
			return false;
		});
		
		History.Adapter.bind(window, 'statechange', onStateChange);
		onStateChange(false);
		onFormChange(false);
		
	}
	
	self.clearAll = function() {
		for (var i = 0; i < numFilters; i++) {
			filters[i].clear();
		}
	}
	
	function addFilter(filter) {
		filters.push(filter);
		filtersByName[filter.name] = filter;
	}
	
	function onStateChange(react) {
		var State = History.getState();
		var data = $.deserialize(State.url.split('?')[1]);
		for (var p in filtersByName) {
			if (isSet(data[p])) {
				filtersByName[p].setValue(data[p], react)
			} else {
				filtersByName[p].clear(react)
			}
		}
		showHideClearAllButton();
	}
	
	function onFormChange(createHistory, triggeredBy) {
		values = { 'catID': catID, 'isSale': isSale, 'isAll': isAll, 'p': ((triggeredBy !== 'p') ? 1 : null) };
		for (var i = 0; i < numFilters; i++) {
			var cValue = filters[i].getValue();
			if (cValue == null || isSet(values[filters[i].name])) continue;
			values[filters[i].name] = cValue;
		}
		qs = decodeURIComponent($.param(values));
		if (createHistory != false) {
			History.pushState(null, null, '?'+qs);
		}
		startLoad();
	}
	
	function showHideClearAllButton() {
		var isDefault = true;
		for (var i = 0; i < numFilters; i++) {
			if (!filters[i].isDefault() && filters[i].clearable != false) isDefault = false;
		}
		if (isDefault) {
			clearAllBut.hide();
		} else {
			clearAllBut.show();
		}
	}
	
	function startLoad() {
		
		clearTimeout(fakeLoadTimer);
		
		// Abort the current request if it's not complete already
		if (cRequest != null && cRequest.readyState != 4) {
			cRequest.abort();
		}
		
		list.startLoading();
		
		// If the results for this query string have been stored in the cache object, use that instead
		if (isSet(cache[qs])) {
			fakeLoadTimer = setTimeout(function() {
				onLoad(cache[qs]);
			}, 300);
			return;
		}
		
		cRequest = $.ajax({
			'url': url,
			'data': values,
			'dataType': 'json',
			'async': true,
			'success': onLoad,
			'cache': false
		});
	}
	
	function onLoad(data) {
		if (ProductListConfig.useJSCache == true) {	cache[qs] = data; }
		list.update(data['products']);
		pgn1.update(data['numPages'], values['p']);
		pgn2.update(data['numPages'], values['p']);
		if (firstLoad) {
			onStateChange(false);
			firstLoad = false;
		}
		//console.log('loaded ' + data.inc + ' (current is ' + inc + ')');
	}
	
	init();
	
	/////////// Filter Objects ///////////
	
	function Filter(wrap, onChange) {
		
		var self = this, h = $('.h', wrap), c = $('.c', wrap), itemsHeight = c.height(), items = [], control = null, clearBut = $('<a href="#" class="clear-item">clear<em></em></a>').hide().appendTo(wrap);
		
		// Exposing some properties as public here, for the control to see
		self.wrap = wrap;
		self.isOpen = true;
		self.clearBut = clearBut;
		self.type = 'none';
		self.name = 'noname';
		self.c = c;
		
		function init() {
			control = getControl();
			if (isSet(control) && isFunction(control.getName)) self.name = control.getName();
			clearBut.bind(Config.clickEvent, function() { self.clear(); return false; });
			// If it has a heading and a container, enable it to open and close
			if (c.length && h.length) {
				h.bind(Config.clickEvent, function() {
					if (self.isOpen) { self.close(); } else { self.open(); }
					return false;
				});
				h.select(function() {
					return false;
				});
				h.disableTextSelect();
			}
			showHideClearButton();
		}
		
		self.open = function() {
			if (self.isOpen) { return; }
			wrap.removeClass('closed');
			c.animate({ 'height':itemsHeight, avoidTransforms: false }, { duration: 250, easing: 'easeOutCubic', queue: false });
			self.isOpen = true;
		}
		
		self.close = function() {
			if (!self.isOpen) { return; }
			if (itemsHeight == 0) { itemsHeight = c.height(); }
			wrap.addClass('closed');
			c.height(itemsHeight).animate({ 'height':0, avoidTransforms: false }, { duration: 250, easing: 'easeOutQuint', queue: false });
			self.isOpen = false;
		}
		
		self.setValue = function(val, react) {
			if (control == null || !isFunction(control.setValue)) { return false; }
			return control.setValue(val, react);
		}
		
		self.getValue = function() {
			if (control == null || !isFunction(control.getValue)) { return null; }
			return control.getValue();
		}
		
		self.clear = function(react) {
			if (control == null || !isFunction(control.clear) || control.isDefault == true) { return false; }
			control.clear(react)
			return true;
		}
		
		self.onChange = function(react) {
			showHideClearButton();
			if (isFunction(onChange) && react != false) { onChange(); }
		}
		
		self.isDefault = function() {
			if (isSet(control.isDefault)) { return control.isDefault; }
			return true;
		}
		
		function showHideClearButton() {
			if (isSet(control.isDefault) && !control.isDefault && control.useClearButton != false) {
				clearBut.show();
			} else {
				clearBut.hide();
			}
		}
		
		function getControl() {
			// Returns the custom control for a particular type of filter. Also sets the type for quick future reference
			if (wrap.hasClass('cb')) {
				self.type = 'checkbox';
				return new CheckboxFilter(self);
			} else if (wrap.hasClass('range')) {
				self.type = 'range';
				return new RangeFilter(self);
			} else if (wrap.hasClass('dd')) {
				self.type = 'dropdown';
				return new DropdownFilter(self);
			}
			return null;
		}
		
		init();
		
	}
	
	/////////// Custom Filter Objects - Each one takes one parameter, which is an instance of the above 'Filter' object ///////////
	
	function CheckboxFilter(filter) {
		
		var self = this, wrap = filter.wrap, inputs = $('input[type="checkbox"]', wrap).checkbox(), values = [];
		self.isDefault = true;
		
		function init() {
			inputs.each(function(i) {
				var el = $(this), row = el.parent();
				el.change(function() {
					onChange(true);
				});
				row.disableTextSelect();
			});
		}
		
		self.getName = function() { return inputs.attr('name'); }
		self.getValue = function() { return (values.length > 0) ? values.join(',') : null; }
		
		self.setValue = function(val, react) {
			if (val == self.getValue()) { return false; }
			var newValues = val.split(',');
			inputs.each(function(i) {
				var el = $(this), num = $.inArray(el.val(), newValues);
				if (num > -1) {
					el.prop('checked', true);
				} else {
					el.prop('checked', false);
				}
			});
			onChange(react);
			return true;
		}
		
		self.clear = function(react) {
			inputs.prop('checked', false);
			onChange(react);
		}
		
		function onChange(react) {
			values = [];
			inputs.each(function(i) {
				var el = $(this), row = el.parent().parent();
				if (el.is(':checked')) {
					values.push(el.attr('value'));
					row.addClass('on');
				} else {
					row.removeClass('on');
				}
				el.data().updateCheckbox();
			});
			self.isDefault = values.length == 0;
			filter.onChange(react);
		}
		
		init();
		
	}
	
	function RangeFilter(filter) {
		
		var self = this, wrap = filter.wrap, slider = $('.slider', wrap), max = $('.max', wrap), min = $('.min', wrap), values = [0,0], rangeMin = parseInt(slider.attr('data-min')), rangeMax = parseInt(slider.attr('data-max'));
		
		self.isDefault = true;
		slider.slider({ range: true, min: rangeMin, max: rangeMax, values: [rangeMin, rangeMax], slide: onSliderChange, stop: onSliderStop, animate: true }).addTouch();
		$('.ui-slider-handle').each(function() { $(this).append('<span></span>'); });
		
		self.getName = function() { return slider.attr('data-name'); }
		self.getValue = function() { return (values.length > 0 && !self.isDefault) ? values.join(',') : null; }
		
		self.setValue = function(val, react) {
			val = val.split(',');
			val[0] = parseInt(val[0]);
			val[1] = parseInt(val[1]);
			if (val[1] == values[1] && val[2] == values[2]) { return false; }
			slider.slider('values', [val[0],val[1]]);
			onSliderStop(null, null, react);
			return true;
		}
		
		self.clear = function(react) {
			slider.slider('values', [rangeMin,rangeMax]);
			onSliderStop(null, null, react);
		}
		
		function onSliderChange(e, ui) {
			var minValue = (isSet(ui)) ? ui.values[0] : slider.slider('values', 0);
			var maxValue = (isSet(ui)) ? ui.values[1] : slider.slider('values', 1);
			values[0] = minValue;
			values[1] = maxValue;
			min.val('£' + minValue);
			max.val('£' + maxValue);
			//self.isDefault = (values[0] == rangeMin && values[1] == rangeMax);
		}
		
		function onSliderStop(e, ui, react) {
			onSliderChange(e, ui);
			self.isDefault = (values[0] == rangeMin && values[1] == rangeMax);
			filter.onChange(react);
		}

		onSliderChange();
		
	}
	
	function DropdownFilter(filter) {
		
		var self = this, wrap = filter.wrap, select = $('select', wrap), value = select.val(), defaultVal = $('option:first', select).attr('value');
		self.isDefault = true;
		self.useClearButton = false;
		
		select.change(onChange);
		
		self.getName = function() { return select.attr('name'); }
		self.getValue = function() { return select.val(); }
		
		self.setValue = function(val, react) {
			if (val == value) { return false; }
			select.val(val);
			onChange(react);
			return true;
		}
		
		function onChange(react) {
			value = select.val();
			//self.isDefault = value == defaultVal;
			filter.onChange(react);
		}
		
		self.clear = function() {
			
		}
		
	}
	
	function PaginationFilter(onChange) {
		
		var self = this, value = null, wrap = $('<ul class="pagination clearfix"></ul>').hide(),
		all = $('<li class="all"><a href="#">all</a></li>').appendTo(wrap),
		next = $('<li class="next"><a href="#">&gt;</a></li>').appendTo(wrap),
		prev = $('<li class="prev"><a href="#">&lt;</a></li>').appendTo(wrap),
		nums = null,
		value = null,
		cNum = -1,
		cTotal = 0;
		
		self.wrap = wrap;
		self.type = 'pagination';
		self.name = 'p';
		self.copy = null;
		self.clearable = false;
		
		function init() {
			all.bind(Config.clickEvent, function() {
				if (value == 'all') {
					self.setValue('1');
				} else {
					self.setValue('all');
				}
				return false;
			});
			next.bind(Config.clickEvent, nextPage);
			prev.bind(Config.clickEvent, prevPage);
			if ($.browser.touch) {
				all.click(function() { return false; });
				next.click(function() { return false; });
				prev.click(function() { return false; });
			}
		}
		
		self.setValue = function(val, react) {
			if (nums == null || val == value) return;
			
			nums.removeClass('on');
			all.removeClass('on');
			if (!isNaN(val)) {
				cNum = parseInt(val)-1;
				nums.eq(cTotal-(cNum+1)).addClass('on');
			} else if (val == 'all') {
				all.addClass('on');
			}
			value = val + '';
			checkNextPrev();
			if (self.copy != null) { self.copy.setValue(value, false); }
			_onChange(react);
		}
		
		self.getValue = function() { return (value == null) ? 1 : value; }
		
		self.clear = function(react) {
			self.setValue('1', false);
		}
		
		self.isDefault = function() { return value == '1'; }
		
		self.update = function(total, currentPage) {
			
			if (nums != null && total == cTotal) { return; }
			if (nums != null) { nums.remove(); }
			if (total < 2) { total = 0 }
			if (total == 0 && currentPage !== 'all') {
				wrap.hide();
				cTotal = -1;
				return;
			} else {
				cNum = 0;
			}
			nums = $();
			for (var i = total-1; i >= 0; i--) {
				var cNumItem = $('<li' + ((i === cNum) ? ' class="on"' : '') + '><a href="#">' + (i+1) + '</a></li>').bind('click', { num: i }, numClick);
				nums = nums.add(cNumItem);
			}
			nums.insertAfter(next);
			wrap.show();
			cTotal = total;
		}
		
		function nextPage() {
			if (value == "all" || cNum >= nums.length-1) { return false; }
			self.setValue((cNum+2) + '');
			return false;
		}
		
		function prevPage() {
			if (value == "all" || cNum <= 0) { return false; }
			self.setValue(cNum + '');
			return false;
		}
		
		function checkNextPrev() {
			
			if (value == 'all') {
				nums.hide();
				next.hide();
				prev.hide();
			} else {
				nums.show();
				next.show();
				prev.show();
			}
			
			var disableNext = cNum >= nums.length-1;
			var disablePrev = value == 'all' || cNum <= 0;
			
			if (disableNext) {
				next.addClass('disabled').css({ opacity: .2 });
			} else {
				next.removeClass('disabled').css({ opacity: 1 });
			}
			if (disablePrev) {
				prev.addClass('disabled').css({ opacity: .2 });
			} else {
				prev.removeClass('disabled').css({ opacity: 1 });
			}
			
		}
		
		function numClick(e) {
			self.setValue((e.data.num+1) + '');
			return false;
		}
		
		function _onChange(react) {
			if (react != false && isFunction(onChange)) { onChange(true, self.name); }
		}
		
		init();
		
	}
	
	/////////// Product List ///////////
	
	function ProductResults() {
		
		var self = this,
		wrap = $('#products'),
		loaderWrap = $('<div class="loader-overlay"><span></span></div>').hide().appendTo(wrap),
		loader = $('span', loaderWrap),
		products = [],
		numProducts = -1,
		topOffset = 0,
		$window = $(window),
		htmlBody = $('html,body'),
		wH = 500, 
		cListHeight = 400,
		emptyMsg = $('<p id="no-results">Sorry, there were no shoes<br /> found that matched your search.</p>');
		
		self.wrap = wrap;
		
		$(window).load(function() {
			topOffset = wrap.offset().top;
			$window.scroll(onScroll);
			$window.resize(onResize);
			onResize();
			onScroll();
		});
		
		self.update = function(data) {
			
			destroyCurrentProducts();
			loaderWrap.stop(true, false).fadeOut(200);
			numProducts = parseInt(data.length);
			
			if (numProducts > 0) {
				cListHeight = Math.ceil(numProducts/2)*322;
				emptyMsg.stop(true, false).hide();
			} else {
				cListHeight = 500;
				emptyMsg.insertBefore(loaderWrap).stop(true, false).fadeIn(400);
			}
			wrap.stop(true, false).animate({ height: cListHeight, avoidTransforms: false }, { duration: 400 });
			
			for (var i = 0; i < numProducts; i++) {
				data[i].num = i;
				var cProduct = new Product(data[i]);
				wrap.append(cProduct.wrap);
				products.push(cProduct);
			}
			
		}
		
		self.startLoading = function() {
			onScroll();
			loaderWrap.appendTo(wrap).stop(true, false).fadeTo(200, .85);
		}
		
		function destroyCurrentProducts() {
			while (products.length > 0) {
				var cProduct = products.pop();
				cProduct.destroy();
			}
		}
		
		function onResize() {
			wH = $window.height();
			onScroll();
		}
		
		function onScroll() {
			var scrollTop = $window.scrollTop();
			newPosY = (scrollTop - topOffset) + (Math.round(wH/2) - 40);
			if (newPosY < 40) { newPosY = 40; }
			if (newPosY > cListHeight-250) { newPosY = cListHeight-250; }
			var newPosStr = '50% '+newPosY+'px';
			loaderWrap.css({ 'background-position': newPosStr });
		}
		
	}
	
	function Product(data) {
		
		var self = this, tpl = [
			'<div class="item"><a href="',
			data.href,
			'" class="main"><span class="img"><img src="',
			data.thumb[0],
			'" alt="',
			data.name,
			'" /></span><span class="p"><span class="name">',
			data.name,
			'</span><span class="price">' + ((data.priceIsVariable == "true") ? 'from ' : '') + '<span>&pound;',
			data.price,
			'</span></span></span></a></div>'
		], wrap = $(tpl.join('')), colourBut = null, imgWrap = $('.img', wrap), cImg = 0, imgs = {}, numColours = data.thumb.length, loading = false, loader = null, cX = 0, w = 323, price = $('.price', wrap);
		self.wrap = wrap;
		
		if (data.num % 2 == 1) { wrap.addClass('r'); }
		if (numColours > 1) { initColours(); }
		
		if (isSet(data.salePrice)) {
			price.append($('<span class="sale-price">' + data.salePrice + '</span>')).addClass('sale');
		}
		
		self.destroy = function() {
			wrap.remove();
		}
		
		function initColours() {
			imgWrap.width(w*6);
			colourBut = $('<a href="#" class="colour">change colour +</a>').appendTo(wrap).click(changeColour);
			loader = $('<span class="imgLoader"></span>').insertAfter(imgWrap);
		}
		
		function changeColour() {
			if (loading) { return false; }
			cImg = (cImg >= data.thumb.length-1) ? 0 : cImg+1;
			cX += w;
			var newImg = imgs[cImg];
			if (!isSet(newImg)) {
				newImg = imgs[cImg] = $('<img src="' + data.thumb[cImg] + '" />').appendTo(imgWrap).css({ 'left': cX }).load(onLoad);
			} else {
				newImg.appendTo(imgWrap).css({ 'left': cX });
				gotoCurrentColour();
			}
			return false;
		}
		
		function onLoad() {
			loading = false;
			// Hide loader?
			gotoCurrentColour();
		}
		
		function gotoCurrentColour() {
			imgWrap.animate({ 'left': -cX }, { 'duration': 300, 'easing': 'easeInOutCubic', 'queue': false, 'complete': cleanUp });
		}
		
		function cleanUp() {
			for (var p in imgs) {
				if (p != cImg) {
					imgs[p].detach();
				} else {
					imgs[p].css({ 'left': 0 });
				}
			}
			imgWrap.stop(true, false).css({ 'left': 0 });
			cX = 0;
		}
		
	}
	
}

//////////////////////////// END PRODUCT PAGE ///////////////////////////////



