Object.extend(Math, {
	formatNumber:function(n, options){
		var options = Object.extend({
			decimals:0,
			currency:false,
			currencySymbol: '&pound; ',
			formatWhole:true,
			wholeDelimiter:',',
			decimalDelimiter:'.'
		},options);

		var nArr = new Array();
		nArr = String(n).split('.');
		
		var whole = (typeof nArr[0]!='undefined')?nArr[0]:'0';
		if(options.formatWhole){
			var exp = /(\d+)(\d{3})/;
			while (exp.test(whole)) {
				whole = whole.replace(exp, '$1' + options.wholeDelimiter + '$2');
			}
		}

		if(typeof nArr[1]!='undefined'){
			var remainder = nArr[1];
		}else{
			var remainder = '';
			for(var i=0;i<options.decimals;i++){remainder += '0'}
		}
		
		var pfix = options.currency?options.currencySymbol:'';
		
		if(options.decimals<=0) return pfix + whole;
		
		var a = new Array();
		for(var i = 0; i < options.decimals; i++){
			if(remainder.charAt(i) != '') a[i] = remainder.charAt(i);
			else a[i] = '0';
		}
		
		return pfix + whole + options.decimalDelimiter + a.join("");
	}
});

/*
Extend the Element class to contain an appendText() method which adds text to a given element
*/
Element.addMethods({
	appendText: function(element, text) {
		text = document.createTextNode(text);
		element.appendChild(text);
		return $(element);
	}
});

/*
ImageCaptionizer

This class places a caption underneath specified images, or images within a specified container. The caption text is
read from the image alt tag. Images with a class="caption" will be captioned, or images within a container with 
class="caption_container" will be captioned.

The generated caption will be in the following format:
<div class="caption"><p>image alt text</p></div>

Add CSS for .caption to style the caption text/background.

Note that it is important to call this function once all images have been downloaded so that the image dimensions are known.

Examples:

> <div class="caption_container">
> 	<img src="images/image.jpg" alt="This is the alt text 1" />
> 	<img src="images/image.jpg" alt="This is the alt text 2" />
> </div>
> 
> <img class="caption" src="images/image.jpg" alt="This is the alt text" />
> 
> <script>
> 	Event.observe(window, 'load', function(event) {
> 		new ImageCaptionizer();
> 	});
> </script>	
*/
var gCaptionClass = 'caption'; // css class to use for captioning an image
var gCaptionContainerClass = 'caption_container'; // css class to use for captioning a set of images within a container
var gMinimumCaptionedImageWidth = 75; // minimum width that an image must be to be captioned

var ImageCaptionizer = Class.create({
	initialize: function() {
		var self = this;
		// Captionize container images
		$$("." + gCaptionContainerClass + " img").each(function(imageElement) {  
		    self.applyCaptionToImageElement(imageElement);
		});
		// Captionize specific images
		$$("." + gCaptionClass).each(function(imageElement) {  
			self.applyCaptionToImageElement(imageElement);
		});
	},
	applyCaptionToImageElement: function(imageElement) {
		var title = Element.readAttribute(imageElement, "alt");
	    var imageWidth = Element.getWidth(imageElement);
		if(imageWidth >= gMinimumCaptionedImageWidth && title != null) {
			
	    	var divCaption = new Element(
				'div', 
				{ className: gCaptionClass, style: 'width:' + imageWidth + 'px' }
			);
			divCaption.appendChild(new Element('p').appendText(title));
		
			var divContainer = new Element('div', {style: imageElement.readAttribute('style')});
			divContainer.setStyle({height: ''});
			imageElement.setStyle({margin:'',padding:'',cssFloat:''});
	
			imageElement.parentNode.replaceChild(divContainer, imageElement); 
			divContainer.appendChild(imageElement);
			divContainer.appendChild(divCaption);
		}
	}
});
	
/*
Tabstrip
*/
var gTabPanelDivSelectedClass = "tab_panel_shown"; // css class to use on all containing divs when tab panel is shown
var gTabPanelDivUnselectedClass = "tab_panel_hidden"; // css class to use on all containing divs when tab panel is hidden

var TabStrip = Class.create({
	initialize: function(tabs) {

		// Select the first tab by default
		this.deselectAll(tabs);
		$(tabs.first().panelElementId).className =  gTabPanelDivSelectedClass;

		var self = this;
		tabs.each(function(tab) {

			// Observe the click event on the action button
			$(tab.buttonElementId).observe("click", function(event) {
				event.stop();
				// Deselect all tabs
				self.deselectAll(tabs);
				// Select the clicked tab
				self.selectTab(tabs, tab.buttonElementId); 
			});

			var forwardButtonElement = $$("#" + tab.panelElementId + " .forward_link")[0];
			if(forwardButtonElement)
				forwardButtonElement.observe("click", function(event) {
					event.stop();
					// Deselect all tabs
					self.deselectAll(tabs);
					// Select the next tab
					self.selectTab(tabs, tabs[tabs.indexOf(tab) + 1].buttonElementId);
				});

			var backButtonElement = $$("#" + tab.panelElementId + " .back_link")[0];
			if(backButtonElement)
				backButtonElement.observe("click", function(event) {
					event.stop();
					// Deselect all tabs
					self.deselectAll(tabs);
					// Select the next tab
					self.selectTab(tabs, tabs[tabs.indexOf(tab) - 1].buttonElementId);
				});
		});
	},
	selectTab: function(tabs, buttonElementId) {
		tabs.each(function(tab) {
			if(tab.buttonElementId == buttonElementId)
				$(tab.panelElementId).className = gTabPanelDivSelectedClass;
		});
	},
	deselectAll: function(tabs) {
		tabs.each(function(tab) {
			$(tab.panelElementId).className = gTabPanelDivUnselectedClass;
		});
	}
});

/*
	Applies the given class to all child elements of a specified type
*/
function applyCssClassToChildren(parentElementId, tagName, className) {
	var elements = $(parentElementId).childNodes;
	for(var elementIndex = 0; elementIndex < elements.length; elementIndex++) {
		if(elements[elementIndex].tagName == tagName.toUpperCase())
			$(elements[elementIndex]).className = className;
	}
}

/*
	Makes the given element clickable, and when clicked will activate a given tab panel within a container
*/
function makeTabButton(clickableTabElementId, tabPanelElementId) {

	applyCssClassToChildren($(tabPanelElementId).parentNode.id, "DIV", gTabPanelDivUnselectedClass);
	$(tabPanelElementId).className = gTabPanelDivSelectedClass;	
	
	// Observe the click event
	$(clickableTabElementId).observe("click", function (event) {
		applyCssClassToChildren($(tabPanelElementId).parentNode.id, "DIV", gTabPanelDivUnselectedClass);
		$(tabPanelElementId).className = gTabPanelDivSelectedClass;	
	});
}

/*
Makes the given element a rollover image.
The element needs to have a background image assigned to it, for example: <div style="background:url(image.gif) #f4f4f4 0px 0px no-repeat;width:141px;height:124px">
The image itself must contain both states within the same canvas, with the rollover state directly underneath the normal image state.
*/
function makeRolloverImage(elementId) {
// Set the initial height of the element to be half the image height, so we only show half of the image composite
var originalHeight = $(elementId).getStyle('height').replace('px','');
var startHeight = originalHeight / 2;
//$(elementId).setStyle('overflow','hidden');
$(elementId).setStyle({height:startHeight + 'px'});

// Shift the background image position on rollover to the rollover image
$(elementId).observe('mouseover', function (event) {
	$(elementId).setStyle({backgroundPosition: '0px -' + startHeight  + 'px',borderColor: '#006432'});
});

// Shift the background image position to the top on rollout
$(elementId).observe('mouseout', function(event) {
	$(elementId).setStyle({backgroundPosition: '0px 0px',borderColor: '#666666'});
});
}


/*
RolloverImage

Makes a given element a two-state image rollover. The image file must have the first view state at the top and the second
view state directly beneath it. An optional hyperlink may be specified for when the image is clicked.

Note: the height of the image must be set explicitly in style attribute. It must be set to the full height of the image. 

Example:

> <div>
> 	<div id="image1" style="background:url(images/mens-glasses.gif) #f4f4f4 0px 0px no-repeat;width:137px;height:56px"></div>
> </div>
> 	
> <script type="text/javascript">
> 	new RolloverImage("image1", "http://www.google.co.uk");
> </script>
*/
var RolloverImage = Class.create({
	initialize: function(elementId, linkUrl) {
		// Set the initial height of the element to be half the image height, so we only show half of the image composite
		var originalHeight = $(elementId).getStyle('height').replace('px','');
		var startHeight = originalHeight / 2;

		$(elementId).setStyle({height:startHeight + 'px'});
		
		// Shift the background image position on rollover to the rollover image
		$(elementId).observe('mouseover', function (event) {
			$(elementId).setStyle({backgroundPosition: '0px -' + startHeight  + 'px',borderColor: '#006432'});
		});
		
		// Shift the background image position to the top on rollout
		$(elementId).observe('mouseout', function(event) {
			$(elementId).setStyle({backgroundPosition: '0px 0px',borderColor: '#666666'});
		});
		
		// Add a hyperlink if required
		if(linkUrl != null)
			$(elementId).update('<a href="' + linkUrl + '" style="width:137px;height:' + startHeight + 'px;display:block"></a>');
	}
});

/*
Accordion

Creates an accordion UI, which consists of a number of panels - each of which can be shown hidden. Only one panel can
be shown at a time. Selecting a panel when another is open will close the first panel. Selecting a panel which is open
will close it.

Example:

> <div>
> 	<div id="button1">Button 1</div>
> 	<div id="button2">Button 2</div>
> </div>
> 
> <div id="button1_panel" style="display: none;">
> 	<p>Button 1 Content</p>   
> </div>
> <div id="button2_panel" style="display: none;">
> 	<p>Button 2 Content</p> 
> </div>

> <script type="text/javascript">
> 	var panelGroup = [
> 	  	{clickableElementId:'button1', panelElementId:'button1_panel'},
> 	  	{clickableElementId:'button2', panelElementId:'button2_panel'}
> 	  	];
> 	new Accordion(panelGroup);
> </script>
*/
var gSlidingPanelDuration = 0.5; // How long the panel takes to show/hide in seconds
var gSlidingPanelDivClass = "arrow"; // css class to use on all containing divs when panel is hidden
var gSlidingPanelDivClickedClass = "arrow_clicked"; // css class to use on all containing divs when panel is shown

var Accordion = Class.create({
	initialize: function(panelGroup) {

		var self = this;
		panelGroup.each(function(panelItem) {
			
			// Apply the default css class
			self.applyCssClassToChildrenRecursively(panelItem.clickableElementId, "div", gSlidingPanelDivClass);
			$(panelItem.panelElementId).style.display = "none";
		
			// Observe the click event
			$(panelItem.clickableElementId).observe("click", function (event) {
			
				var clickedElementId = Event.findElement(event, 'div').id;
				var closeClickedElement = ($(panelItem.panelElementId).style.display != "none");
				
				// Close panels
				panelGroup.each(function(otherPanelItem) {
				
					var closePanel = (otherPanelItem.panelElementId != panelItem.panelElementId && $(otherPanelItem.panelElementId).style.display != "none");
					closePanel |= (otherPanelItem.clickableElementId == clickedElementId && closeClickedElement);

					if(closePanel) {
						new Effect.SlideUp(otherPanelItem.panelElementId, {duration: gSlidingPanelDuration});
						self.applyCssClassToChildrenRecursively(otherPanelItem.clickableElementId, "div", gSlidingPanelDivClass);
					}
				});
				
				// Open this panel
				if(!closeClickedElement) {
					new Effect.SlideDown(panelItem.panelElementId, {duration: gSlidingPanelDuration});
					self.applyCssClassToChildrenRecursively(panelItem.clickableElementId, "div", gSlidingPanelDivClickedClass);
				}
			});
		});
	},
	/* Applies the given class to all child elements of a specified type */
	applyCssClassToChildrenRecursively: function (parentElementId, tagName, className) {
		var elements = $(parentElementId).getElementsByTagName(tagName);
		for(var elementIndex = 0; elementIndex < elements.length; elementIndex++)
			elements[elementIndex].setAttribute("class", className);
	}
});

var HiddenPanelList = Class.create({
	initialize: function(panelGroup, clickableElementId) {

		var self = this;
		panelGroup.each(function(panelItem) {
			$(panelItem.panelElementId).style.display = "none";
		});
			
		// Observe the change event
		$(clickableElementId).observe("change", function (event) {
			
			var selectedPanel = panelGroup[$(clickableElementId).selectedIndex];
			if($(selectedPanel.panelElementId).style.display != "none")
				return;
			var panelsToClose = panelGroup.without(selectedPanel);
				
			// Close panels
			panelsToClose.each(function(panelItem) {
				
				var closePanel = ($(panelItem.panelElementId).style.display != "none");
				if(closePanel) {
					new Effect.SlideUp(panelItem.panelElementId, {duration: gSlidingPanelDuration});
				}
			});
				
			// Open this panel
			if(selectedPanel) {
				new Effect.SlideDown(selectedPanel.panelElementId, {duration: gSlidingPanelDuration});
			}
		});
	}
});

/*
HiddenPanel

Constructs a panel which can be expanded/hidden on the click of a button.
Specify the ID of the button element, and the ID of the panel in the constructor.

The classname specified in gSlidingPanelDivClickedClass is applied to the button when the
panel is shown. gSlidingPanelDivClass is applied when the panel is hidden.

Example:

> <div>
> 	<div id="button">Button</div>
> </div>
> <div id="panel" style="display: none;">
> 	<p>Hidden content</p>   
> </div>
> 
> <script type="text/javascript">
> 	new HiddenPanel('button', 'panel');
> </script>
*/
var HiddenPanel = Class.create({
	initialize: function(clickableElementId, panelElementId) {

		// Apply the default css class
		this.applyCssClassToChildrenRecursively(clickableElementId, "div", gSlidingPanelDivClass);
		$(panelElementId).style.display = "none";
	
		// Observe the click event
		var self = this;
		$(clickableElementId).observe("click", function (event) {
			if ($(panelElementId).style.display == "none") {
				new Effect.SlideDown(panelElementId, {duration: gSlidingPanelDuration});
				self.applyCssClassToChildrenRecursively(clickableElementId, "div", gSlidingPanelDivClickedClass);
			}
			else {
				new Effect.SlideUp(panelElementId, {duration: gSlidingPanelDuration});
				self.applyCssClassToChildrenRecursively(clickableElementId, "div", gSlidingPanelDivClass);
			}		
		});
	},
	/*
	Applies the given class to all child elements of a specified type
	*/
	applyCssClassToChildrenRecursively: function (parentElementId, tagName, className) {
		var elements = $(parentElementId).getElementsByTagName(tagName);
		for(var elementIndex = 0; elementIndex < elements.length; elementIndex++)
			elements[elementIndex].setAttribute("class", className);
	}
});

/*
FlashAnimation

Uses the SWFObject v2.2 library to add a Flash Animation to the page within the specified element id.
Check the SWFObject documentation (http://code.google.com/p/swfobject/wiki/documentation) for supported 
params and attributes.

Example:

<div id="flash_animation">
	Noflash content goes here
</div>
	
<script>
	var movie = "animation.swf";
	var width = "500";
	var height = "200";
	new FlashAnimation("flash_animation", movie, width, height);
</script>
*/
var FlashAnimation = Class.create({
	initialize: function(noFlashElementId, movieUrl, width, height, flashVars, params, attributes) {
		swfobject.embedSWF(movieUrl, noFlashElementId, width, height, "9.0.0", "expressInstall.swf", flashVars, params, attributes, this.callbackFn.bind(this));
	},
	callbackFn: function(e) {
		if(e.success) {
		} else {
			alert("error loading movie");
		}
	}
});

//Image Rotator Script
var ImageRotator = Class.create();

ImageRotator.prototype = {
  // Arguments:
  //   images - A list of the ids of elements to rotate
  //   duration - the length of time an element to should show for
  initialize: function(args) {
    this.images = args['images'];
    this.duration = args['duration'] || 7000
  },
  
  start: function() {
    this.show(imageRotator.images[0]);
    setTimeout("imageRotator.crossFade(0)", this.duration);
  },

  show: function(image) {
    new Effect.Appear(image);
  },

  hide: function(image) {
    new Effect.Fade(image);
  },

  crossFade: function(current_index) {

    if(current_index+1 == imageRotator.images.length) {
      next_index = 0
    }
    else {
      next_index = current_index + 1
    }

    this.hide(imageRotator.images[current_index]);
    this.show(imageRotator.images[next_index]);
    setTimeout("imageRotator.crossFade("+next_index+")", this.duration);
  }
}

var VideoCarousel = Class.create({
	initialize: function(containerElementId) {
	
		$$("#" + containerElementId + " .video_link").each(function(videoLinkElement) {
			
			videoLinkElement.observe("mouseover", function() {
				videoLinkElement.down(".overlay").show();
			});
			videoLinkElement.observe("mouseout", function() {
				videoLinkElement.down(".overlay").hide();
			});
			
		});

		new UI.Carousel(containerElementId);
	}
});

var DayDropdown = Class.create({
	initialize: function(elementId, defaultLabel, startDay, endDay) {
		var self = this;
		
		var validatedStartDay = startDay;
		if(validatedStartDay == null) validatedStartDay = 1;
		
		var validatedEndDay = startDay;
		if(validatedEndDay == null) validatedEndDay = 31;
		
		var validatedDefaultLabel = defaultLabel;
		if(validatedDefaultLabel == null) validatedDefaultLabel = "Day";

		var defaultOption = document.createElement('option');
		defaultOption.text = validatedDefaultLabel;
		$(elementId).options.add(defaultOption);
		
		for(var dayNumber = validatedStartDay ; dayNumber <= validatedEndDay ; dayNumber++) {
			var option = document.createElement('option');
			option.value = dayNumber;
			option.text = dayNumber;
			$(elementId).options.add(option);
		}
	}
});

var YearDropdown = Class.create({
	initialize: function(elementId, defaultLabel, startYear, endYear) {
		var self = this;
		
		var validatedStartYear = startYear;
		if(validatedStartYear == null) validatedStartYear = 1900;
		
		var validatedEndYear = startYear;
		if(validatedEndYear == null) validatedEndYear = new Date().getFullYear();
		
		var validatedDefaultLabel = defaultLabel;
		if(validatedDefaultLabel == null) validatedDefaultLabel = "Year";

		var defaultOption = document.createElement('option');
		defaultOption.text = validatedDefaultLabel;
		$(elementId).options.add(defaultOption);
		
		for(var yearNumber = validatedEndYear ; yearNumber >= validatedStartYear ; yearNumber--) {
			var option = document.createElement('option');
			option.value = yearNumber;
			option.text = yearNumber;
			$(elementId).options.add(option);
		}
	}
});

var BackButton = Class.create({
	initialize: function(clickableElementId, destinationUrl) {
		var self = this;

		if($(clickableElementId))
			$(clickableElementId).observe("click", function(event) {
				event.stop();
				if(destinationUrl == null || destinationUrl == "")
					history.go(-1);
				else
					window.location = destinationUrl;
			});
	}
});

/*
ElementRotator

Fades in/out a series of elements within a container. Elements to be crossfaded should include
the classname "rotating_item".

Example:

<div id="container" style="width:785px;height:245px;">
	<div class="rotating_item" style="position: absolute;">
		<img style="width: 785px; height: 245px; border:0" src="images/image1.jpg" />
	</div>
	<div class="rotating_item" style="position: absolute;">
		<img  style="width: 785px; height: 245px; border:0" src="images/image2.jpg" />
	</div>
</div>	
<script>
	var rotateDuration = 5; // seconds
	new ElementRotator(rotateDuration, "container");
</script>
*/
var ElementRotator = Class.create({
	initialize: function(rotateDuration, containerElementId) {

		this.rotateDuration = rotateDuration;
		this.elementList = [];

		var self = this;
		$(containerElementId).descendants().each(function(descendantElement) {
			if(descendantElement.hasClassName("rotating_item"))
				self.elementList.push(descendantElement);
		});

		// Hide all items and show the first
		this.elementList.each(function(element) {
			element.hide();
		});
		this.elementList.first().show();
		
		$(containerElementId).setStyle({
			width:this.elementList.first().getWidth() + 'px', 
			height:this.elementList.first().getHeight() + 'px'
		});

		// Set up a timer to crossfade the elements
		if(this.elementList.length > 1)
			new PeriodicalExecuter(this.onCrossFade.bind(this), this.rotateDuration);
	},
	onCrossFade: function() {
		var currentElement = this.getCurrentlyVisibleElement();
		var nextElement = this.getNextElement(currentElement);

		new Effect.Appear(nextElement);
		new Effect.Fade(currentElement);
	},
	getCurrentlyVisibleElement: function() {
		var currentlyVisibleElement = null;
		this.elementList.each(function(element) {
			if(element.visible() == true)
				currentlyVisibleElement = element;
		});

		return currentlyVisibleElement;
	},
	getNextElement: function(currentElement) {
		var nextElement = null;
		var rotatingElementIndex = 0;
		var self = this;
		this.elementList.each(function(element) {

			if(element == currentElement) {
				if(rotatingElementIndex + 1 < self.elementList.length)
					nextElement = self.elementList[rotatingElementIndex + 1];
				else
					nextElement = self.elementList[0];
			}
			rotatingElementIndex++;
		});
		
		return nextElement;
	}
});


var Print = Class.create({
	initialize: function(printOnWindowLoad) {
		if(printOnWindowLoad)
			Event.observe(window, 'load', function(event) {
				window.print();
			});
	}
});

/*
 * Standard window load events
 */
Event.observe(window, 'load', function(event) {
	// Captionize any images
	new ImageCaptionizer();
});

