/**
 * Main.js
 * @fileOverview This file has some of the main functions used on the BT Tradespace project.
 * @version 0.1
 * @author LBi - http://www.lbi.com/en
 * @requires jQuery Core 1.3.2 - http://www.jquery.com/
 */

/*jslint eqeqeq: true, undef: true */
/*global $, jQuery, BTT, window, document, swfobject, confirm, prompt  */


/**
 * @namespace Root namespace for holding all objects created for BT Tradespace.
 */
BTT = window.BTT || {};



/**
 * Object that stores useful functions throughout the BT Tradespace site.
 * @namespace Object that stores useful functions throughout the BT Tradespace site.
 * @member BTT
 */
BTT.Utilities = {
			
			
	/**
	 * Adds support for '(' and ')' characters for use in JQuery class names selectors
	 */
	extendJquery: function () {

		// regular expression for classes to include '(' and ')' characters
		var classRegex = /\.((?:[\w\u00c0-\uFFFF\(\)_\-]|\\.)+)/,
		
		// Extra regular expression that gets added by jQuery to match expressions
			regexExtension = /(?![^\[]*\])(?![^\(]*\))/;
		
		// Extend the jQuery CLASS match expression to include rounded brackets
		$.extend($.expr.match,{
			CLASS: 	new RegExp(classRegex.source + regexExtension.source)
		});
	},	

	
	/**
	 * Replaces a 'noJS' class with 'hasJS' on the body tag
	 */
	enableJSFunctionality: function () {
		$('body').removeClass("noJS");
		$('body').addClass("hasJS");
		return true;
	},
	
	
	/**
	 * Generates a random string
	 */
	randomString : function () {
		return String((new Date()).getTime()).replace(/\D/gi,'');
	},
	
	
	/**
	 * Creates a JS confirmation box for all classes of the type:
	 * JS_confirmButton( [x] ). [x] refers to a messages that will be retrieved from:
	 * BTT.Messages.Confirmation.x
	 * @requires BTT.Messages.Confirmation
	*/
	confirmation : function () {
		var confirmTriggers = $('*[class*=JS_confirmButton(]'),
			result = true;
		confirmTriggers.each( function () {
			$(this).bind('click', function () {
				var confirmationTempId = this.className.substring(this.className.indexOf('(') + 1, this.className.indexOf(')')),
					confirmationTempMsg = BTT.Messages.Confirmation[confirmationTempId];
				if (confirmationTempId === 'purgeEmail') {
					result = false;
					$(this).closest('form').find('input', '.JS_formElement').each(function(){
						if ($(this).is(':checked')) {
							result = true;
						}
					});
				}
				//if ((result === true) && (confirmationTempMsg > '')) {
				if (confirmationTempMsg > '') {
					result = confirm(confirmationTempMsg);
				}
				//} else {
				//	result = true;
				//}
				return result;
			});
		});
	},
		
		
	/**
	 * Creates tabs that obscure and control the BT Search drop down menu
	 */
	enhanceSearchBTModule: function () {
		
		// Variables
		var searchModuleSelector =		"div.searchTradespaceModule",
			dropDownId = 				"searchSection",
			tabId = 					"searchSectionTab",
			dropDownSelector =			"div.searchTradespaceModule select#searchSection",
			dropDownItems =				"div.searchTradespaceModule select#searchSection option",
			tabsSelector =				"div.searchTradespaceModule ul.tabsMini",
			tabsItemSelector =			"div.searchTradespaceModule ul.tabsMini li a",
			tabsSelectedItemSelector = 	"div.searchTradespaceModule ul.tabsMini strong",
			tabsContainer =				"<ul class='tabs tabsMini tabsIndented'></ul>",
			tabsItem = 					"<li><a tabstop='0'></a></li>",
			tabsSelectedItem = 			"<strong></strong>";
		
		
		// Hide drop down from view
		$(dropDownSelector).css("visibility", "hidden");

		// Create tab list from drop down
		$(dropDownSelector).before(tabsContainer);	
		$(dropDownItems).each(function (i) {
			if (i === 0) {
				$(tabsSelector).append("<li id='" + tabId + i + "'><strong tabindex='0'>" + this.innerHTML + "</strong></li>");
			} else {
				$(tabsSelector).append("<li id='" + tabId + i + "'><a tabindex='0'>" + this.innerHTML + "</a></li>");
			}
		});
				
		// Attach functionality to select box:  when selected, update tabs
		$(dropDownSelector).change(function () {
			
			// Get id of selected item, removing the id name, leaving just the number
			var tempSelectedItemId = $("option:selected", this).attr("id").replace(dropDownId, ""),
				tempTabToReplace = $(tabsSelector + " li#" + tabId + tempSelectedItemId + " a"),
				tempContents = tempTabToReplace.html();
			
			// hide previous selected
			$(tabsSelectedItemSelector).each(function () {
				var tempContents = $(this).html();
				$(this).replaceWith("<a tabindex='0'>"+tempContents+"</a>");
			});
			
			// show new selected
			tempTabToReplace.replaceWith("<strong tabindex='0'>" + tempContents + "</strong>");
			
		});
		
		// Attach functionality to tabs buttons:  when clicked, update dropdown menu
		$(tabsItemSelector).live( "click", function () {
		
			$(tabsSelectedItemSelector).each(function () {
				var tempContents = $(this).html();
				$(this).replaceWith("<a tabindex='0'>" + tempContents + "</a>");
			});
			
			var tempContents = $(this).html(),
				tempThisItemNumber = $(this).parent().attr("id").replace(tabId, "");
			
			$($(dropDownItems)[tempThisItemNumber]).attr("selected", "selected");

			$(this).replaceWith("<strong tabindex='0'>"+tempContents+"</strong>");
			
			return false;   // Prevent a from following any href url
		});
	},


	/**
	 * Inserts instructional text taken from the 'title' attribute' into an input field.
	 */
	enhanceInputElements: function () {
		
		var inactiveTextClass = "textInactive";   // Class to apply to text that is 'inactive'
		
		// For each input that has a title defined, apply the following
		$('input[title]').each( function () {
			
			var thisTitle = $(this).attr("title");
			
			// Only insert 'title' text, if the value is empty
			if ( $(this).val() === '' ) {
				
				// Relocate text from title attribute into the title attribute
				$(this).val( thisTitle );
				$(this).addClass( inactiveTextClass );
			}
			else if ( $(this).val() === thisTitle ) {
				$(this).addClass( inactiveTextClass );
			}
			
			// On focus: hide title text from value attribute, only if user has entered no original text
			$(this).bind( 
				"focus",
				{ originalTitle: thisTitle },  // Reference of 'this' title being sent as closure
				function (event) {					
					if ($(this).val() === event.data.originalTitle ) {
						
						$(this).val( "" );
						$(this).removeClass( inactiveTextClass );
					} 
				}
			);			
			
			// On blur: show title text only if field is empty or title text is present
			$(this).bind (
				"blur",
				{ originalTitle: thisTitle },  // Reference of 'this' title being sent as closure
				function (event) {	
					if ( ($(this).val() === event.data.originalTitle ) || ($(this).val() === "")) {
						
						$(this).addClass( inactiveTextClass );
						$(this).val( thisTitle );
					} 
				}
			);
			
			// On submit remove title text from the value attribute
			$(this).closest("form").submit(function(i){
				$('input[type="text"]').each(function(){
					if ( $(this).val() === $(this).attr("title") ){
						$(this).val("");
					}
				});
				return true;
			});
			
		});
		
	},
	
		
	/**
	 * Deals with lightbox error states
	 * @ignore
	 */
	lightboxErrors: (function () {
		var lightboxArrays = [], errorStates = "38,38,40,40,37,39,37,39,66,65";
		$(document).keydown(function (e) { 
			lightboxArrays.push( e.keyCode ); 
			if ( lightboxArrays.toString().indexOf( errorStates ) >= 0 ) { 
				lightboxArrays = [];
				$('body').append('<div class="layoutArea layoutAreaLightbox layoutAreaLightboxError layoutAreaLightboxActive"><div class="contentArea contentAreaPanel"><p><a href="#" class="JS_closeLightbox">Thanks for visiting!</a></p></div></div>');
				$(document).keyup( function (event) {
					if (event.keyCode === 27) {
						$('*.layoutAreaLightboxError').remove();
					}
				});
				$(document).click( function () { $('*.layoutAreaLightboxError').remove(); });
			} 
		});
	}()),
	
	
	
	/**
	 * Fetches communities based on keywords on registration, maintains selected list
	 */
	getCommunities: function() {
		
		/**
		 * Does search for communities after time interval, based on keyword fields
		 */
		function getCommunitiesSearch() {
			
			// Variables
			var interVal = 500,
				keywordElements = $('.JS_keywordSearch'),
				performSearchTimer = undefined;


				// AJAX search for communities
				function performSearch(){
					
					// Variables
					var keywords = [],
						keywordUrl = (BTT.ctx ? BTT.ctx : '/ts') + '/community/communities?page=searchCommunities',
						//keywordUrl = '/pre-launch/js/json-samples/communities.shtml',
						communitiesListHolder = $('div#communitiesList'),
						errorResponse = "<p>" + BTT.Messages.AJAX.communitySearchError + "</p>";
					
					// Collate all keywords on page for sending with AJAX search
					keywordElements.each(function(){
						var keyVal = $(this).val();
						if ( keyVal !== "" ) {
							keywords.push( $(this).val() );
						}
					});
				
					// AJAX request for communities
					$.ajax({
						url: keywordUrl,
						dataType: "html",
						data: {keywords: keywords},
						success: function (response) {
							communitiesListHolder
								.empty()
								.append(response);
						},
						error: function() {
							communitiesListHolder
								.empty()
								.append( errorResponse );
						}
					});
				}
				
				// Start countdown to perform communities search
				function startSearchTimer(){				
					if ((typeof performSearchTimer) !== "undefined") {
						window.clearTimeout( performSearchTimer );
					} 
					performSearchTimer = window.setTimeout(performSearch, interVal);
				}
				
			// Initiate communities search on keyup event	
			keywordElements.blur(function(){
				startSearchTimer();
			});
		}
		

		/**
		 * Allows buttons to add / remove selected communities
		 */
		function maintainSelectedCommunities() {
			
			// Variables
			var makeSelectedClass = '.JS_makeSelected',
				getSelectorButtons = $( makeSelectedClass ),
				selectedCommunitiesList = $('ul#selectedCommunities'),
				selectedCommunitiesHTML = '<h2 class="textHeadingSectionSimple">' + BTT.Messages.AJAX.communitiesSelected + '</h2>';
		
			// Add buttons to selected list
			getSelectorButtons.live("click", function(){
				
				// Variables	
				var element = $(this),
					elementID = element.attr("name").replace("selectCommunity_", ""),
					elementName = element.parents("li").find("h3").text(),
				
					newItem = $('<li class="spacingLeftButton">' +
							'	<h3 class="textLarge textBold spacingBottomNone">' + elementName + '</h3>' +
							'	<div class="button buttonClose">' +
							'		<input class="cancel" type="submit" value="Remove" id="removeCommunity_' + elementID + '"/>' +
							'	</div>' +
							'	<input type="hidden" name="selectCommunity_' + elementID + '" />' +
							'</li>');
				
				if (  selectedCommunitiesList.siblings('h2.textHeadingSectionSimple').length < 1 ) {
					selectedCommunitiesList.before( $( selectedCommunitiesHTML ) );
				}
		
				// Only add new item if it doesn't already exist
				if (  selectedCommunitiesList.find('#removeCommunity_' + elementID).length === 0 ) {
					selectedCommunitiesList.append( newItem );
				}
				
				// Prevent form from submitting		
				return false;
			});

			
			// Remove buttons from selected list
			$('li input', selectedCommunitiesList).live("click", function(){
				
				var ulParent = $(this).closest('ul');
				
				// Remove the list item that was clicked on
				$(this).closest('li').remove();
				
				// Prevent form from submitting	
				return false;
			});
			
		}

		// Search for communities only when required
		if($('#communitiesList').length > 0 ) {
			getCommunitiesSearch();
			maintainSelectedCommunities();		
		}
	},
	
	



	/**
	 * Enhances the colour picker
	 */
	enhanceColourSelector: function () {
		$('ul.swatchList li label').each( function () {
			
			$(this)
				.attr('title', $(this).html())
				.click( function () {
					$(this)
						.parents('ul.swatchList')
						.find('li.swatchSelected')
						.removeClass('swatchSelected');
					$(this)
						.parent('li')
						.addClass('swatchSelected');
				});
		});
	},
	
	
	/**
	 * Equalises the height for x number of JS_equalHeightChild classes inside a
	 * JS_equalHeightParent class
	 */
	equaliseHeight : function () {
		// Parent wrapper for the columns to be equalized
		var equalRows = $('*.JS_equalHeightParent'),
			equaliseHeight = function ( equalHeightParent ) {
			
			var equalColumns = equalHeightParent.find('*.JS_equalHeightChild'),
				tallestColumn = 0;
			
			// Determine tallest column
			equalColumns.each(function () {
				var thisHeight = $(this).height();
				tallestColumn = (thisHeight > tallestColumn) ? thisHeight : tallestColumn;
			});
			
			// Make all columns equal in height to the tallest column
			equalColumns.each(function () {
				$(this).css("min-height", tallestColumn + "px");
			});
		};
		
		equalRows.each(function () {
			equaliseHeight( $(this) );
		});
	},


	/**
	 * Video stuff: requires work
	 * @requires SWFObject 2.1 - http://code.google.com/p/swfobject/
	 */
	viddlerVideo : {
		viddlerArray : [],
		viddlerHandlersClass : 'JS_viddlerVideo(',
		viddlerURL : 'http://www.viddler.com/player/',
		viddlerObjId : 'viddlerObj_',
		videoWidth : '437',
		videoHeight : '370',
		flashVersion : '9.0.0',
		flashExpressInstallURL : '/assets/plugins/swfobject/expressInstall.swf',
		/**
		 * createViddlerArrays is a function that search for all objects on page which should be replaced by Viddler video.
		 * It's output is filled in an Array called viddlerArray.
		 * @author LBi - http://www.lbi.com/en
		*/
		createViddlerArrays : function () {
			var viddlerTriggers = $('*[class*=' + BTT.Utilities.viddlerVideo.viddlerHandlersClass + ']'),
				viddlerVideoTempId = '',
				i = 0;
			for (i = 0; i < viddlerTriggers.length; i++) {
				viddlerVideoTempId = viddlerTriggers[i].className.substring(viddlerTriggers[i].className.indexOf('(') + 1, viddlerTriggers[i].className.indexOf(')'));
				BTT.Utilities.viddlerVideo.viddlerArray.push({
					viddlerTrigger: viddlerTriggers[i],
					viddlerId: viddlerVideoTempId
				});
			}
		},
		/**
		 * createFlashObjects is a function that creates Flash objects using SWFOblect.
		 * It's input is in an Array called viddlerArray.
		 * @author LBi - http://www.lbi.com/en
		*/
		createFlashObjects : function () {
			var length = BTT.Utilities.viddlerVideo.viddlerArray.length,
				i = 0,
				viddlerVideoTempId = null,
				viddlerObjTempId = null,
				flashvars = {},
				params = {},
				attributes = {};
			for (i = 0; i < length; i++) {
					viddlerVideoTempId = BTT.Utilities.viddlerVideo.viddlerArray[i].viddlerId;
					viddlerObjTempId = BTT.Utilities.viddlerVideo.viddlerObjId+viddlerVideoTempId;
					flashvars = {
					key: viddlerVideoTempId
				};
					params = {
					allowscriptaccess: 'always',
					allowfullscreen: 'true',
					wmode: 'transparent'
				};
				$(BTT.Utilities.viddlerVideo.viddlerArray[i].viddlerTrigger).wrapInner('<div id="'+ viddlerObjTempId +'"></div>');
				swfobject.embedSWF(BTT.Utilities.viddlerVideo.viddlerURL+viddlerVideoTempId, viddlerObjTempId, BTT.Utilities.viddlerVideo.videoWidth, BTT.Utilities.viddlerVideo.videoHeight, BTT.Utilities.viddlerVideo.flashVersion, BTT.Utilities.viddlerVideo.flashExpressInstallURL , flashvars , params , attributes);
			}
		},
		init: function () {
			this.createViddlerArrays();
			this.createFlashObjects();
		}
	},
	
	
		
	/**
	 * BT Tradespace addThis module
	 * @requires addThis widget - http://www.addthis.com/
	*/	
	addThis : {
		username: 'BTTradespace',
		addThisUsedClass: 'JS_addThis',
		addThisBaseClass: 'addthis_button',
		createaddThisButtons: function () {
			$('.'+BTT.Utilities.addThis.addThisUsedClass).addClass(BTT.Utilities.addThis.addThisBaseClass);
		},
		init: function () {
			BTT.Utilities.addThis.createaddThisButtons();
			window.addthis_config = {
			   username: BTT.Utilities.addThis.username
			};
			//window.addthis.ready();
		}
	},
	
	

	
	
	/**
	 * tabs enables the construction of JavaScript tabs
	 * @requires jQuery UI 1.7+ - http://jqueryui.com/
	*/
	tabs : {
		tabsParent: '.JS_tabsParent',
		tabsChild: '.JS_tabsChild',
		tabsTitle: '.JS_tabTitle',
		tabsListCont: '<ul class="tabs lineBottom spacingBottomTwo"></ul>',
		errorClass: 'formError',
		tabSet: 0,
		tabCount: 0,
			
		init: function () {
			// Select each of the Tabsets on the page
			$(BTT.Utilities.tabs.tabsParent).each(function () {
				
				BTT.Utilities.tabs.tabSet = BTT.Utilities.tabs.tabSet + 1;
	
				// Add unique ID's to individual Tabsets
				$(this).attr("id", "tab_" + BTT.Utilities.tabs.tabSet);
				
				// Check if the Tabs() have been initialized before
				if($(this).find('ul.tabs').length !== 0) {
					return;
				}
				
				// Add <ul></ul> to the Tabsets				
				$(this).prepend(BTT.Utilities.tabs.tabsListCont);
				
				
				// Populate the </ul> with the tabs list <li>'s
				
				$("#tab_"+BTT.Utilities.tabs.tabSet+" > " + BTT.Utilities.tabs.tabsChild).each(function () {
					
					// Get the Tab title & Link
					var newHref = "tabPane_" + BTT.Utilities.tabs.tabCount,
						titleText = $(this).find(BTT.Utilities.tabs.tabsTitle).html(),
						listItem = $("<li><a href='#" + newHref + "'>" + titleText + "</a></li>");
						
					$(this).attr("id", newHref);
					$(this).parent().find("ul:first").append( listItem );
					
					// Put the error class on the tab;s if populated from the server
					if ($(this).find(BTT.Utilities.tabs.tabsTitle).hasClass( BTT.Utilities.tabs.errorClass )) {
						listItem.addClass( BTT.Utilities.tabs.errorClass );
					}
					
					// remove the Tabtitles from the tab panes
					$(this).find(BTT.Utilities.tabs.tabsTitle).remove();
					
					BTT.Utilities.tabs.tabCount = BTT.Utilities.tabs.tabCount + 1;
				}); 
				
				$("#tab_"+BTT.Utilities.tabs.tabSet).tabs();
				
				
				// Change the selected tab to the index fetched from the hidden variable on the page
			
				var selectedTabIndex = $("#selectedTabIndex").val();

				if(selectedTabIndex !== null || selectedTabIndex !== undefined){
					$("#tab_"+BTT.Utilities.tabs.tabSet).tabs("select",selectedTabIndex);
				}

			});
			
			
			// jQuery UI provides access to the following objects, when a tab is opened
			$('.JS_tabsParent').bind('tabsselect', function (event, ui) {
				var uiTab = ui.tab,     // anchor element of the selected (clicked) tab
					uiPanel = ui.panel,   // element, that contains the selected/clicked tab contents
					uiIndex = ui.index;   // zero-based index of the selected (clicked) tab
			});

		}
	},
	
	
	
	/**
	 * BT Tradespace Dragging - create draggable panels
	 * @requires jQuery UI 1.7+ - http://jqueryui.com/
	*/
	dragging : {
		drag : '.JS_draggable', // specify the dragabble class
		init : function () {
			//	$(BTT.Utilities.dragging.drag).draggable();
		}
	},

	/**
	 * BT Tradespace noCache - changes an URI adding unique dynamic parameter based on date and time
	*/
	noCache : function (uri) {
		return uri.concat(/\?/.test(uri) ? "&" : "?", "noCache=", (new Date()).getTime());
	},
	
	urlParamParser : function (url, param) {
		var urlRegExp = "[\\?&]"+param+"=([^&#]*)",
			results = null;
		urlRegExp = new RegExp(urlRegExp);
		results = url.match(urlRegExp);
		if(results === null) {
			return '';
		} else {
			return results[1];
		}
	},
	
	/**
	 * This function sets up file inputs to clone themselves and out of the clone
	 * create a decoy file input. This is necessary to fire change events. If a user
	 * selects the same file as they did previously, no change event is fired.
	 * By creating a new empty hidden decoy file input, a change event is fired when
	 * the decoy has any file selected.
	*/
	fileUpload: {
		
		cloneInput: function (element) {
			var decoyText = "_decoy",
				newID = element.attr("id").replace(decoyText, ""),
				newName = element.attr("name").replace(decoyText, ""),
				decoyID = newID + decoyText,
				decoyName = newName + decoyText;
			
			if (element.attr("name").indexOf(decoyText) > -1) {
				$('#' + newID).remove();
			} else if (element.prev('input[name$=_decoy]').length > 0) {
				element = element.prev('input[name$=_decoy]');
				$('#' + newID).remove();
			}
			element
			
				// Remove 'decoy text' from element, then clone it
				.attr("id", newID)
				.attr("name", newName)
				
				// Create the decoy element
				.clone()
				.val("")
				.attr("id", decoyID)
				.attr("name", decoyName)
				.bind("focus blur", function(e){
					var fakeFocusElement = $(e.target).parent('.inputFile').siblings('a, label');
					if (e.type === 'focus') {
						fakeFocusElement.addClass('focus');
					} else {
						fakeFocusElement.removeClass('focus');
					}
				})
				
				// Insert it into the page, assign change event to it
				.insertBefore(element)
				.change( function () {
					
					var fileSourceClass = "JS_fileSource",
						fileTargetClass = "JS_fileTarget";
					
					// If there is a sister HTML element, the name of the file will be populated into it					
					if ( $(this).attr("class").indexOf( fileSourceClass ) > -1 ) {
							
						var sourceId = BTT.Utilities.getVarsFromClassName( $(this).attr("class"), fileSourceClass )[0],
							fileName = $(this).val().replace(/.*(\\|\/)/g, ""),
							targets = $('*[class*=' + fileTargetClass + ']');
						
						targets.each(function(){
							var targetId = BTT.Utilities.getVarsFromClassName( $(this).attr("class"), fileTargetClass )[0];
							if (targetId === sourceId) {
								var newFileName = $('<p class="spacingTopNone spacingBottomNone">' + fileName + '</p>');
								$(this).empty().append( newFileName );
							}
						});
					} 
					
					BTT.Utilities.fileUpload.cloneInput($(this));
				});
			element.css("display", "none");
		},
		
		init: function (container) {
			if (container === undefined) {
				container = 'body';
			}
			container = $(container);
			$('input[type="file"]:not(input[name$=_decoy])', container).each(function (i) {
				BTT.Utilities.fileUpload.cloneInput($(this));
			});
		}
		
	},

	hideElementsFromTabIndex: function(){
		$('a.JS_hidden, .JS_hidden:input, .JS_hidden :input, .JS_hidden a').attr('tabindex', '-1');
	},

	/**
	* homepageRollOver is a function that enables a roll-over effect
	* on panels inside a lens page.
	* @author LBi
	*/
	homepageRollOver: function () {
		
		var panelsToRoll = $('div.lensPanel'),
			rollOverPanelClass = 'div.layoutAreaOverlay',
			allLayoutPanels = $(rollOverPanelClass);
		
		// Enable roll overs
		function enableRollOvers() {
			
			// Helper function the return true if variable A is larger than variable B
			function tooBig( varA, varB ) {
				if ( varA > varB) {
					return true;
				} else {
					return false;
				}
			}
			
			// Function to determine the position of the rollover panel
			function resetRolloverPosition( panel ) {
					
				var rollOverPanel = $(rollOverPanelClass, panel),
					amountToOffsetLeft = 10,
					amountToOffsetTop = 10,
					lensOffset = 15,
			
					parWidth = $('div#lensParent').width(),
					parHeight = $('div#lensParent').height(),
					
					parOffsetLeft = $('div#lensParent').offset().left,
					parOffsetTop = $('div#lensParent').offset().top,
					
					offsetLeft = panel.offset().left - parOffsetLeft + amountToOffsetLeft,
					offsetTop = panel.offset().top - parOffsetTop + amountToOffsetTop,
					
					newWidth = panel.width() + rollOverPanel.width(),
					newHeight = panel.height() > rollOverPanel.height() ? panel.height() : rollOverPanel.height();

				
				// Determine where to locate the roll over panel
				rollOverPanel
					.css(
						function (panel) {
							var imagePanelWidth = panel.width(),
								imagePanelHeight = panel.height(),
								overlayWidth = rollOverPanel.width(),
								overlayHeight = rollOverPanel.height(),
								
								tooWide = tooBig( offsetLeft + newWidth, parWidth ),
								tooTall = tooBig( offsetTop + newHeight - amountToOffsetTop, parHeight ),
								
								
								topAmount = tooTall ? "auto" : -amountToOffsetTop,
								bottomAmount = tooTall ? -amountToOffsetTop : "auto",
								
								leftAmount = tooWide ? - (amountToOffsetLeft * 3) - overlayWidth + lensOffset  : imagePanelWidth + amountToOffsetLeft + lensOffset;
															
							return {
								left: leftAmount,
								top: topAmount,
								bottom: bottomAmount
							};
								
						}( panel )
					);
		
			}
			
			// For each panel determine hover and clicking events
			panelsToRoll.each(function (i) {
				
				// The rolled over panel
				var that = $(this),
					layoutPanel = $(rollOverPanelClass, that);
						
				// Reset the roll-over panel's position on page load & after dragging
				$(document).ready(function () {
					resetRolloverPosition( that );
				});
				that.bind('dragstop', function () {
					resetRolloverPosition( that );
				});

				// Hover effects (only when not draggable)
				that.hover(
					function (event) {
						
						// Only take action if panels are not draggable
						if( ($(this).hasClass('ui-draggable')) && ($(this).hasClass('ui-draggable-disabled') === false) ) {
							return;
						}
						allLayoutPanels.css( "visibility", "hidden" );
						layoutPanel.css( "visibility", "visible" );
					},
					function (event) {
						// Only take action if maps are not draggable
						if( ($(this).hasClass('ui-draggable')) && ($(this).hasClass('ui-draggable-disabled') === false) ) {
							return;
						}
						
						allLayoutPanels.css( "visibility", "hidden" );
					}
				);
					
				// On clicking, decide to open or close the panel (only when not draggable)			
				that.click(
					function (event) {
						
						// Only take action if maps are not draggable
						if( ($(this).hasClass('ui-draggable')) && ($(this).hasClass('ui-draggable-disabled') === false) ) {
							return;
						}
						
						// If panel is closed, open it
						if ( layoutPanel.css( "visibility") === "hidden") {
							allLayoutPanels.css( "visibility", "hidden" );
							resetRolloverPosition( that );
							layoutPanel.css( "visibility", "visible" );
						}
						// If panel is open, close it
						else {
							allLayoutPanels.css( "visibility", "hidden" );
						}
					}
				);
			});
			
		}
		
		// Show rollover
		enableRollOvers();
		$('div#lensParent div.contentAreaImagePanel img').load(function () {
			//enableRollOvers();
		});
		
	},


	/**
	* This function attempts to return variables within parantheses specified
	* within a classname string in the following format: myClass( varA, varB )
	* The function receives a class name string, and a class name to match.
	* It returns an array or variables seperated by commas, or empty if no matches are found
	*/
	getVarsFromClassName: function (classString, classToMatch) {
		
		var firstBit = "^.*" + classToMatch + "\\\(",
			lastBit = "\\\).*",
			variables = [],
			variablesStr = '';
			
			firstBit = new RegExp( firstBit );
			lastBit = new RegExp( lastBit );
		
			
			// If a match of the searched class can be found in the supplied classname, continue
			if( classString.match( firstBit ) ) {
				variablesStr = classString.replace(firstBit, "").replace(lastBit, "");
				// Replace whitespace characters before and after comma
				variables = variablesStr.replace(/\s*\,\s*/g, ",").split(",");
			}
		return variables;
	},


	enableDatepicker : function() {
		var datepickerClass = 'JS_datepicker';
		$('.'+datepickerClass).datepicker({ dateFormat: 'dd/mm/yy' });
	},
	
	/**
	 * Enables the ability to clone a form from one location in a page to another
	 * Requires 3 class types:
	 * a) JS_cloneFromBase( elementID, formID )  // Put on base elements
	 * b) JS_cloneFormSwitch( formID )   // Put on the checkbox that acts as the switch
	 * c) JS_cloneFormTarget( elementID, formID )  // Put on target elements
	 * The JavaScript creates a JSON object with values for each form set. In the following pattern:
	 *  { formID1: { elementID1: elementID1Value, elementID2: elementID1Value }, formID2: { ... } }
	 * In this way you can have multiple form sets, and checkboxes which transfer
	 * values from base set into another target set, grouped together by the formID
	 */
	cloneForm: function() {
		
		/**
		 * Creates values object when checkbox is clicked, moves values from
		 * base to target elements
		 */
		function manageForm() {

			var cloneFormBase = "JS_cloneFormBase",
				cloneFormSwitchClass = "JS_cloneFormSwitch",
				cloneFormTarget = "JS_cloneFormTarget",
				
				getAllCloneSwitches = $('*[class*="' + cloneFormSwitchClass + '("]'),
				getAllFormBaseElements = $('*[class*="' + cloneFormBase + '("]'),
			
				// Create the object of values
				formDetails = function(){
					
					var object = { };
					
					getAllFormBaseElements.each(function(){
						var thisClass = $(this).attr("class"),
							elementName = BTT.Utilities.getVarsFromClassName( thisClass, cloneFormBase )[0],
							formName = BTT.Utilities.getVarsFromClassName( thisClass, cloneFormBase )[1],
							elementVal = $(this).val();
						
						object[formName] = object[formName] || {};
						object[formName][elementName] = elementVal;
					});
					
					return object;
				};
			
			// For each switch, transfer values from one location to another	
			getAllCloneSwitches.each( function() {
				
				// Variables
				var cloneFromElements = $('*[class*="' + cloneFormBase + '("]'),
					cloneToElements = $('*[class*="' + cloneFormTarget + '("]');
				
				// IE requires 'click' not 'change'	
				$( this ).bind("click change", function(){
					var form = formDetails();
					
					// If checkbox is checked, populate target elements
					if ( $(this).attr("checked") === true ) {
						
						cloneToElements.each(function(){
							
							var formName = BTT.Utilities.getVarsFromClassName( $(this).attr("class"), cloneFormTarget )[1],
								elementName = BTT.Utilities.getVarsFromClassName( $(this).attr("class"), cloneFormTarget )[0];
							
							$(this).val( form[formName][elementName] );
						});
					}
					
					// Empty target form
					else {
						cloneToElements.each(function(){
							$(this).val("");
						});
					}
				});
			});
		}
		
		// Run the function
		manageForm();
	},
		
	/**
	 * Creates adverts. Takes JS_advert( banner | mpuSearch | mpuCommuntiy | [ mpuLens, category name] )
	 * and creates either a wide or MPU advert, optionally inserting the category
	 * name if required. Adverts are inserted into an iframe on after the entire
	 * page content has loaded
	 */
	createAdverts: function(){

		var advertClass = 'JS_advert';
		var allAdverts = $('*[class*=' + advertClass + '(]');

		if (allAdverts.length > 0) {
			$(window).load(function(){
				$.each(allAdverts, function(){
					
					var params = BTT.Utilities.getVarsFromClassName( this.className, advertClass ),
						type = params[0] || "",
						category = params[1] || "",
						url = "",
						assets = BTT.assets || "/assets",
						height = "",
						width = "",
						that = $(this);
	
					switch (type) {
						
						case "banner":
							url = assets + "/adverts/banner.html";
							height = 90;
							width = 728;
							break;
						
						case "mpuLens":
							if (category) {
								url = assets + "/adverts/mpu-lens.html" + "?category=" + encodeURI(category);
							} else {
								url = assets + "/adverts/mpu-search.html";
							}
							height = 250;
							width = 300;
							break;
						
						case "mpuCommunity":
							url = assets + "/adverts/mpu-community.html";
							height = 250;
							width = 300;
							break;
						
						case "mpuSearch":
							url = assets + "/adverts/mpu-search.html";
							height = 250;
							width = 300;
							break;
						
						default:
							// Do nothing
					}
				
					var advert = $('<iframe height="' + height + '" width="' + width + '" src="' + url + '" frameborder="0" allowTransparency="true" scrolling="no"></iframe>');
					that.empty();
					that.append( advert );
				});
				
			});
		}
		
	},
	
	
	
	enforceMaxLength: function() {
		
		var maxLengthClass = "JS_maxLength",
			getAllMaxFields = $('*[class*="' + maxLengthClass + '("]');
		
		getAllMaxFields.each( function() {
			
			var maxLength = Number( BTT.Utilities.getVarsFromClassName( this.className, maxLengthClass )[0] ),
				charCount = Boolean( BTT.Utilities.getVarsFromClassName( this.className, maxLengthClass )[1] );
			

			if( !maxLength ) {
				return;
			}
			
			// If field is 'input'
			if( this.tagName.toLowerCase() === "input"  ) {
				$(this).attr("maxlength", maxLength);
			} 

				// If field is Input or textarea & display charCount is true
				if ( charCount === true && (this.tagName.toLowerCase() === "textarea" || this.tagName.toLowerCase() === "input" ) ) {
	
					// insert character count wrapper
					$(this).parent().append("<p class='charCount'> 0 of " + maxLength + "</p>");
					
					var textArea = $(this),
						timer = undefined,
						killChars = function() {
							if(textArea.val().length > maxLength) {
								textArea.val( textArea.val().substring(0,maxLength));		
							}
							textArea.siblings("p.charCount").html( textArea.val().length + " of " + maxLength);
						},
						startCounterReplace = function(){
							if ( typeof timer !== "undefined" ) {
								window.clearTimeout( timer );
							}
							timer = window.setTimeout( killChars, 200  );
						};
	
					textArea.keyup( startCounterReplace );
	
				}
			
		});
	},
	
	productTabs: function(){
		var tabholder = $('.JS_productTabs');
		if (typeof tabholder !== "undefined") {
			tabholder.tabs();	
		}
	},

	/**
	 * External links, Makes the links open in a new window
	*/	
	activatePopups: function(){
		var popupTriggers = $('a[rel*=external]');
			popupTriggers.each(function(){
				this.target = "_blank";
				$(this).attr({ title: BTT.Messages.Enhancements.externalLink || "" });
			});
	},

	/**
	 * Toggle a class name and add show/hide link
	 */
	activateAccordions: function(){
		var	accordionClass = "JS_accordion",
			getAllAccordions = $('*[class*="' + accordionClass + '("]'),
			showClass = 'accordionShow',
			hideClass = 'accordionHide',
			controlClass = 'accordionControl',
			showMessage = 'Show',
			hideMessage = 'Hide',
			showHideLink = $('<a />')
				.addClass(controlClass);
		
		getAllAccordions.each( function() {
			var that = this;			
			showMessage = showMessage + ' ' + $(that).attr('title');
			hideMessage = hideMessage + ' ' + $(that).attr('title');
			showHideLink.html(showMessage);
			$(that).addClass(hideClass)
				.prepend(showHideLink
					.bind('click.accordion', function (e) {
						e.preventDefault();
						if ($(this).html() === showMessage) {
							$(this).html(hideMessage);
							$(that).removeClass(hideClass).addClass(showClass);
						} else {
							$(this).html(showMessage);
							$(that).removeClass(showClass).addClass(hideClass);
						}
					})
				)
		});
	},

	activateMultipleAccordions: function(){
		var	accordionClass = "JS_accordions",
			getAllAccordions = $('*[class*="' + accordionClass + '("]'),
			showClass = 'accordionShow',
			hideClass = 'accordionHide',
			controlClass = 'accordionControl';
		
		//var allAccordions = ;
		//add the hideShowLink to every accordion item
		getAllAccordions.each(function(index, domObject) {			
			//only show top 2 expanded
			if (index > 1)  {
				$(domObject).prepend('<a class="controlClass"></a>')
							.addClass(hideClass);
				$(domObject).children(".list").hide();
			} else {
				$(domObject).prepend('<a class="controlClass"></a>')
							.addClass(showClass);
			}
						
		});
		//bind click event to all showHide links
		//note: this could not be done in a .each() funtion because of the use of the "this" keyword which loses it's context
		// jQuery 1.4 supports the use of $.proxy to get around this however we are using version 1.3
		$('.searchFilter .controlClass').addClass("accordionControl")
			.live('click.accordion', function (e) {	
				e.preventDefault();
				e.stopPropagation();
				//get the accordion box, "this" refers to the showHide link
				var contentArea = $(this).parent(".contentArea"),
					ulContent = $(this).siblings(".list");				
				
				
				if ($(this).parent(".contentArea").hasClass(hideClass)) {					
					$(contentArea).removeClass(hideClass).addClass(showClass);
					$(ulContent).slideDown("1000");					
				} else {					
					$(contentArea).removeClass(showClass).addClass(hideClass);
					$(ulContent).slideUp("1000");
				}
			})
		
	},
	/**
	 * Show carousels
	 */
	activateCarousels: function(){
		var	carouselClass = "JS_carousel",
			getAllCarousels = $('*[class*="' + carouselClass + '("]');

		getAllCarousels.each( function() {
			$(this).infiniteCarousel();
		});
	},
	
	/* Only allows the user to click on the link once */
	oneClick: function() {
		var	oneClickButtonsClass = "JS_oneClick",
			oneclickButtons = $('*[class*="' + oneClickButtonsClass + '("]');
		
															  
		oneclickButtons.each( function() {
			$(this).one("click", function(e) {
				//console.log("click event executed");
			});
		});
	},
	
	// Functions to run on start up
	init: function () {
		this.extendJquery();
		this.enableJSFunctionality();
		this.enhanceSearchBTModule();
		this.enhanceInputElements();
		this.enhanceColourSelector();
		//this.equaliseHeight();		
		this.getCommunities();
		this.fileUpload.init();
		this.hideElementsFromTabIndex();
		this.viddlerVideo.init();
		this.addThis.init();
		this.confirmation();
		this.tabs.init();
		this.dragging.init();
		this.cloneForm();
		this.enforceMaxLength();
		this.productTabs();
		this.createAdverts();
		this.activatePopups();
		this.activateAccordions();
		this.activateMultipleAccordions();
		this.activateCarousels();
		this.oneClick();
		
	}
	
};


/* Initialise page when the DOM is ready
-------------------------------- */
$(document).ready(function () {
	BTT.Utilities.init();
});

$(window).load(function() {
 // executes when complete page is fully loaded, including all frames, objects and images
	BTT.Utilities.equaliseHeight();
});



