/*- **************************************** EXTRA REQUIRED MOOTOOLS STUFF **************************************************************** --*/

MooTools.More={version:"1.2.2.2"};var Log=new Class({log:function(){Log.logger.call(this,arguments);}});Log.logged=[];Log.logger=function(){if(window.console&&console.log){console.log.apply(console,arguments);
}else{Log.logged.push(arguments);}};var Asset={javascript:function(f,d){d=$extend({onload:$empty,document:document,check:$lambda(true)},d);var b=new Element("script",{src:f,type:"text/javascript"});
var e=d.onload.bind(b),a=d.check,g=d.document;delete d.onload;delete d.check;delete d.document;b.addEvents({load:e,readystatechange:function(){if(["loaded","complete"].contains(this.readyState)){e();
}}}).set(d);if(Browser.Engine.webkit419){var c=(function(){if(!$try(a)){return;}$clear(c);e();}).periodical(50);}return b.inject(g.head);},css:function(b,a){return new Element("link",$merge({rel:"stylesheet",media:"screen",type:"text/css",href:b},a)).inject(document.head);
},image:function(c,b){b=$merge({onload:$empty,onabort:$empty,onerror:$empty},b);var d=new Image();var a=$(d)||new Element("img");["load","abort","error"].each(function(e){var f="on"+e;
var g=b[f];delete b[f];d[f]=function(){if(!d){return;}if(!a.parentNode){a.width=d.width;a.height=d.height;}d=d.onload=d.onabort=d.onerror=null;g.delay(1,a,a);
a.fireEvent(e,a,1);};});d.src=a.src=c;if(d&&d.complete){d.onload.delay(1);}return a.set(b);},images:function(d,c){c=$merge({onComplete:$empty,onProgress:$empty,onError:$empty},c);
d=$splat(d);var a=[];var b=0;return new Elements(d.map(function(e){return Asset.image(e,{onload:function(){c.onProgress.call(this,b,d.indexOf(e));b++;if(b==d.length){c.onComplete();
}},onerror:function(){c.onError.call(this,b,d.indexOf(e));b++;if(b==d.length){c.onComplete();}}});}));}};


/*-------------------------------------------------------------------------------------------------------------------------

Gallery - A Javascript-powered photo gallery solution

Current Version 0.7

Copyright (c) 2008 Paul Wilson

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.



******************************* GALLERY OBJECT SYNTAX ****************************************

{
name[string]  
description[string] -  optional
image[string] - optional
thumb[string] - optional
data[object|array_of_objects]
}

************************************* GALLERY DATA OBJECT SYNTAX*********************************

{
uri[string] - the path to the image
title[string] - optional
caption[string] - optional
thumb[string] - path to the thumbnail - optional
size[int] - sizer of file - optional
}


******************************* OBJECTS**********************************************

Gallery.Core
Gallery.Resize
Gallery.Transitions
Gallery.XML
Gallery.Schema
Gallery.Components

***************************COMPONENTS***************************************

Slideshow
Button
CaptionBar











*/





var Gallery = {}
/*-------------------------------------------Gallery.Core---------------------------------------------------------------------------

contains the core functionality only - loads in gallery data and provides simple API for moving between gallerires - has no direct interaction with users, this is done with components


*/



Gallery.Core = new Class({
/*------------------------- class properties*-------------------------------------------------------------*/
	container : '', // holds reference to slideshow container
	galleries : [], // holds gallery objects (see above)
	imagesToLoad : [], // used for prelaoder
	loadedImages : [], // used by preloader
	currentlyLoadingGallery : '', // used by preloader
	currentGallery : '', // id of gallery currently showing
	currentImage : '', // reference to image tag currently showing
	oldImage : '', // reference to previous image tag
	currentIndex : '', // id of currently displayed image
	transitions : [], // array of avialable transition effects loaded in at start
	delayIndex : '', // used internally
	holdingSlide : '', // the image in slideshow container initially
	resize: '', //holds resizing logic
	
	Implements:Events,
	
	
	options : {
/*------------------------------------options--------------------------------------------*/
		transition : 'fadeOut', // default transition between images 
		url: false,
		datatype: 'json',
		schema: 'standard',
		firstGallery: 0,
		transitionDuration: 1000,
		holdingSlide: true,
		buffer : 0.3,
		stream : false,
		resize: 'scaleAndCrop', // determines what to do if picture is bigger than container - can be 'scaleAndCrop', 'crop' or 'scale'
		debug : false, // if set to true will show alert boxes from time to time detailing internal state off object
		// EVENTS
		onGalleryDataLoad : $empty,  // fired when Gallery data is loaded in
		onImageLoadBegin : $empty, // fired when image preloading begins
		onImageLoaded : $empty, // fired when each image has loaded
		onImageLoadingComplete : $empty, //fired when preloading is complete
		onGalleryChange: $empty, //fired when galleries are switched
		onTransitionBegin: $empty, //fired before picture changes
		onTransitionComplete: $empty // fired when picture has changed
	},
	
	
	/* *Public Method CONSTRUCTOR
	
	Arguments - 
	containerId [string] - the id of the container element
	options [object] -  optional settings - see above
	**/
	
	
	initialize : function(containerId, options){
		this.setOptions(options);
		this.container = $(containerId);
		this.container.setStyles({
			'position' : 'relative',
			'overflow' : 'hidden'
		});
		
		//load in transition effects and holding slide
		for(prop in Gallery.Transitions){
			this.transitions.push( prop );
		}
		// load in resize functions
		for(prop in Gallery.Resize){
			Gallery.Resize[prop] = Gallery.Resize[prop].bind(this);
		}
		this.holdingSlide = this.container.getElement('img');
		this.currentImage = this.holdingSlide;
		// load gallery data, if present
		if(this.options.url) this.loadGallery(this.options.url, this.options.datatype);
		this.addEvent('galleryDataLoad', this.goToGallery.bind(this, [this.options.firstGallery, true] ) );
		this.addConsole();
	},
	
	/*-------------------------UTILITY METHODS-----------------------------------*/
	
	/* private method DEBUG
	
	used to provide debugging functionality whilst testing
	
	*/
	
	trace: function(msg){
		if(this.options.debug){
			console.log(msg);
		}
	},
	
	/* private method addConsole
	
	tests for support of console.log - if not, and if debugging is on, adds popup window
	
	*/
	
	addConsole: function(){
		try{
			console.log();
		}catch(e){
			if( $chk(this.options.debug) ){
				var console = {};
					console.win = window.open('', 'Debugger', 'width=250,height=500,scrollbars=1,resizable=1');
					if( $chk(console.win) ){
						console.win.document.write('<h2>Debugging Console</h2>');
						console.log = function(m){
							try{console.win.document.write(m + '<br />');}catch(e){}
						}
					}
			}
		}
	},

	
	/* private method getTransition 
	
	checks gallery data object and options object to determine which transition effect to use - returns random transition if required
	
	*/
	
	getTransition: function(){
		var tran = this.options.transition;
		if(tran == 'random'){
			tran = this.transitions.getRandom();
		}else if($type(tran) == 'array'){
			tran = tran.getRandom();
		}
		return tran;
	},
	
	/* private methods whenImageLoads, whenImageLoadsPlay, whenImageLoadingCompletePlay, whenImageLoadingCompletePlay
	
	Event handlers used by preloadGallery method
	ADD STREAMING HERE
	*/

	
	whenImageLoads: function(counter, index){
		this.galleries[this.currentlyLoadingGallery].data[index].tag = this.loadedImages[index];
		this.fireEvent('imageLoaded', [counter, index]);
	},
	
	whenImageLoadingComplete: function(){
		this.galleries[this.currentlyLoadingGallery]['loaded'] = true;
		this.currentlyLoadingGallery = '';
		this.loadedImages = '';
		this.imagesToLoad.empty();
		this.fireEvent('imageLoadingComplete');
	},
	 
	whenImageLoadsPlay:function(counter, index){
		this.whenImageLoads(counter, index);
		if( this.options.stream && (  this.loadedImages.length / (this.imagesToLoad.length/100)) >=  this.options.buffer){
			this.currentGallery = this.currentlyLoadingGallery;
			this.goTo('first');
		}
	},
	
	whenImageLoadingCompletePlay: function(){
		this.currentGallery = this.currentlyLoadingGallery;
		this.whenImageLoadingComplete();
	//	if( this.options.holdingSlide ){
			this.goTo('first');
		//}
		
	},
	
	/* private method preloadGallery
	
	preloads selected gallery and streams depending on settings 
	
	*/
	
	preloadGallery: function(index, autoplay){
		
		while(this.currentlyLoadingGallery != ''){
			this.preloadGallery.delay(1000);
			return;
		}
		this.imagesToLoad.empty();
		this.galleries[index].data.each(function(i){
			this.imagesToLoad.push(i.uri);
		}, this);
		this.currentlyLoadingGallery = index;
		
		if(autoplay == true){
			this.currentGallery = index;
			this.goToHoldingSlide();
			this.loadedImages = new Asset.images(this.imagesToLoad, {onProgress:this.whenImageLoadsPlay.bind(this), onComplete:this.whenImageLoadingCompletePlay.bind(this) });
			this.fireEvent( 'imageLoadBegin' );
		}else{
			this.loadedImages = new Asset.images(this.imagesToLoad, {onProgress:this.whenImageLoads.bind(this), onComplete:this.whenImageLoadingComplete.bind(this) });
		}
	},
	/*
		parseJSON
			used when loading in gallery data via JSON
	*/
	
	parseJSON: function(obj){
		obj.each(function(o){ this.addGallery(o); }, this);
		this.fireEvent('galleryDataLoad');
	},
	/*
		parseXML()
		- used to parse xml gallery data
	*/
	
	parseXML: function(txt, xml){
		var fn = Gallery.XML.parse.bind(this);
		fn(xml, this.options.schema);
	},
	
	/* private method afterTransition
	
	cleans up DOM after switching images
	
	*/
	
	afterTransition: function(index, newImg, oldImg){
		this.currentIndex = index;
		oldImg.setOpacity(1);
		oldImg.setStyle('visibility', 'visible');
        oldImg.dispose();
		this.currentImage = newImg;
		this.currentImage.setOpacity(1);
		this.fireEvent('transitionComplete');
        this.working = false; 
	},
	
	/***************************LOW LEVEL METHODS *******************************************************************/

     /*
	     goToPic(index:int):void
		 does the grunt work of moving to the next picture
		 Called By: goToAndStop(), GoToAndPlay()
	*/
	
	goToPic: function(index){
        this.working = true; 
		if(index > (this.galleries[this.currentGallery].data.length - 1) ) index = 0;
		if(index < 0 ) index = this.galleries[this.currentGallery].data.length - 1;
		if( this.galleries[this.currentGallery].data[index].tag){
			var newImg = this.galleries[this.currentGallery].data[index].tag;
			if(newImg == this.currentImage){
				this.working = false;
				return;
			}
			if( !$chk(newImg) ){ 
				this.working = false;
				return;
			}else{
				newImg.setStyles({'position':'absolute'});
				newImg = Gallery.Resize[this.options.resize](newImg);
				var tran = this.getTransition();
				if(!Gallery.Transitions[tran]) this.trace('transition "' + tran + '" does not exist');
				this.fireEvent('transitionBegin', index);
				Gallery.Transitions[tran].attempt([this.currentImage, newImg, this.options.transitionDuration], this);
				this.afterTransition.delay(this.options.transitionDuration, this, [index, newImg, this.currentImage] );
			}
		}else{
			this.goTo('next');
		}
		
	},
	
	/*
		goToHoldingSlide():void
			same as above but returns to holding slide (the original image in slideshow div)
			Called By: preloadGallery()
	*/
	
	goToHoldingSlide: function(){
		if(this.holdingSlide == this.currentImage) return;
		var newImg = this.holdingSlide
		Gallery.Transitions['none'].attempt([this.currentImage, newImg, this.options.transitionDuration], this);
		this.afterTransition(0, newImg, this.currentImage );
		this.container.getElements('img').each(function(el){
			if(el != this.holdingSlide) el.dispose();
		}, this);
	},
	/*
		getCurrentImageData():object
			used by compenents to retrieve currentimage
	*/
	
	getCurrrentImageData: function(){
		return this.galleries[this.currentGallery].data[this.currentIndex];
	},
	
	resetImage: function(img, styles){
		styles.each( function(style){
			if( style == 'clip' ){	
				
				img.setStyle(style, 'rect(0px ' + img.width + 'px ' + img.height + 'px 0px)' );
			}else{
				img.setStyle(style, '');
			}
			
		});
	},
	
	
		
/****************************************************** THE API******************************************************************/
	
	// playback methods
	
	/*
		goTo(position:string|integer):void
			Moves slideshow to given picture and stops - used for user-controller galleries
			Arguments:
				position: can be 'first', 'last', 'next' or 'previous' or an integer representing the gallery position of the pic you want
	*/
	goTo: function(position){
                if(this.working) return;
		var index;
		$clear(this.delayIndex);
		switch(position){
			case 'first' : index = 0; break;
			case 'last' : index = this.galleries[this.currentGallery].data.length - 1; break;
			case 'next' : index = this.currentIndex + 1; break;
			case 'previous' : index = this.currentIndex - 1; break;
			default : index = position; break;
		}
		this.goToPic(index);
	},
		
	// gallery control methods
	
	/*
		goToGallery(gallName:string|integer):void
			switches to given gallery - will fire the preloader if necessary
			Arguments:
				gallName: either the name of the gallery as a string, or an integer representing it's position in the galleries object
	*/
	
	goToGallery: function(gallName, autoplay){
		var found = false;
		if( $type(gallName) == 'number'){
			if(gallName < this.galleries.length){
				this.currentGallery = gallName;
			}else{
				this.currentGallery = 0;
			}
			found = true;
		}else{
			this.galleries.each(function(gal, index){
				if(gal.name == gallName){
					found = true;
					this.currentGallery = index;
				}
			}, this);
		}
		if(found == false){
			this.debug("Gallery '" + gallName + "' does not exist");
			return;
		}
		this.fireEvent('galleryChange');
		this.goToHoldingSlide();
		if(this.galleries[this.currentGallery].loaded && autoplay == true){
			if(this.options.holdingSlide){
				this.goTo('first');
			}else{
				this.goTo(1);
			}
		}else{
			this.preloadGallery(this.currentGallery, autoplay);
		}
	},
	
	/*
		addGallery(obj:object)
			used to add a gallery via javascript
			Arguments:
				obj: a gallery object - see top of page for definintion
	*/
	
	addGallery: function(obj){
		this.galleries.push(obj);
	
	},
	
	/*
		loadGallery(url:string):void
			loads in gallery data from the given url (must be same domain)
			Arguments:
				url: the url of the file / processing script
				type: the dataType to use (can be used to override the value given in options) - either 'json' or 'xml'
				type: the datatype used, either xml, or json - if not given options.datatype will be used
	*/
		
	loadGallery: function(url, type){
		var r;
		if(arguments.length < 2) type = this.options.datatype;
		if(type == 'json'){
			r = new Request.JSON({ url:url, onComplete:this.parseJSON.bind(this)}).get();
		}else{
			r = new Request( { url:url, method:'get', onComplete:this.parseXML.bind(this)}).send();
		}
	}
});	

Gallery.Core.implement(new Options);

/*--------------------GALLERY.RESIZE------------------------------------------------*/

/*


Placeholder for photo resizing logic

*/

Gallery.Resize = {

	none: function(img){
		img.setStyles({'top':0,'left':0});
		return img;
	},

	crop: function(img){
		var containerSize = this.container.getSize();
		var left, top;
		if(img.width > containerSize.x){
			left = (img.width - containerSize.x) / 2;
			left = left - (left * 2);
		}else{
			left = (containerSize.x - img.width) / 2;
		}
		if(img.height > containerSize.y){
			top = (img.height - containerSize.y) / 2;
			top = top - (top * 2);
		}else{
			top = (containerSize.y - img.height) / 2;
		}
		img.store('left', left);
		img.store('top', top);
		img.setStyles({'top':top,'left':left});
		return img;
	},
	
	center: function(img){
		var containerSize = this.container.getSize();
		var left, top;
		if( containerSize.x > img.width ){
			img.setStyle( 'left', (containerSize.x - img.width) / 2 );
		}
		if( containerSize.y > img.height ){
			img.setStyle( 'top', (containerSize.y - img.width) / 2 );
		}
	},
		
	
	scaleByX: function(img, x){
		var ratio = img.height / img.width;
		if(x < img.width) img.width = x;
		if(ratio < 1) img.height = img.width * ratio;
		return img;
	},
	
	scaleByY: function(img, y){
		var ratio = img.width / img.height;
		if(y < img.height) img.height = y;
		if(ratio < 1) img.width = img.height * ratio;
		return img;
	},
	
	scale: function(img){
		var containerSize = this.container.getSize();
		if(img.width > img.height){
			img = Gallery.Resize.scaleByX(img, containerSize.x);
		}else{
			img = Gallery.Resize.scaleByY(img, containerSize.y);
		}
		return Gallery.Resize.crop(img);
	},
	
	scaleAndCrop: function(img){
		var containerSize = this.container.getSize();
		if(img.width > img.height){
			img = Gallery.Resize.scaleByY(img, containerSize.y);
		}else{
			img = Gallery.Resize.scaleByX(img, containerSize.x);
		}
		return Gallery.Resize.crop(img);
	},
	
	resizeContainer: function(img){
		var containerSize = this.container.getSize();
		Gallery.Resize.crop(img);
		var dimensions = {};
		dimensions.width = img.width;
		dimensions.height = img.height;
		//dimensions.top = img.getStyle('top');
		//dimensions.left = img.getStyle('left');
		this.container.morph(dimensions);
		return Gallery.Resize.none(img);
	}
}

/*-------------------------------------Gallery.Transistions-----------------------------------------------------

Contains all transition logic.

By default newImg has position property set to 'absolute',.

'top' and 'left' properties are set by Gallery.Resize.  If you need to change these the original values can be be found by newImg.retrieve('left') and newImg.retrieve('top')


*/

Gallery.Transitions = {
	
	none:function(oldImg, newImg, dur){
		newImg.setOpacity(1);
		newImg.setStyle('visibility', 'visible');
		newImg.inject(this.container, 'bottom');
		oldImg.setStyle('visibility', 'hidden');
	},
	
	fadeOut: function(oldImg, newImg, dur){
		newImg.setOpacity(1);
        newImg.setStyle('visibility', 'visible');
		newImg.inject(this.container, 'top');
		oldImg.set('tween', {duration:dur, transition:'linear'} );
		oldImg.tween('opacity', 0);
	},
	
	fadeIn: function(oldImg, newImg, dur){
		newImg.setOpacity(0);
		this.resetImage( newImg, ['left', 'top', 'clip', 'visibility'] );
		newImg.inject(oldImg, 'after');
		newImg.set('tween', {duration:dur, transition:'linear'} );
		newImg.tween('opacity', 1);
	},
	
	fadeInOut: function(oldImg, newImg, dur){
		newImg.setOpacity(0);
        newImg.setStyle('visibility', 'visible');
		newImg.inject(this.container, 'top');
		oldImg.set('tween', {duration:dur, transition:'linear'} );
		newImg.set('tween', {duration:dur, transition:'linear'} );
		oldImg.tween('opacity', 0);
		newImg.tween('opacity', 1);
	},
	
	fadeOutThenIn: function(oldImg, newImg, dur){
		newImg.setOpacity(0);
		this.resetImage( newImg, ['left', 'top', 'clip', 'visibility'] );
		newImg.inject(oldImg, 'before');
		newImg.set('tween', {duration:dur / 2, transition:'linear'} );
		oldImg.set('tween', {duration:dur / 2, transition:'linear', onComplete:function(){ newImg.tween('opacity', 1); } } );
		oldImg.tween('opacity', 0);
	},
		
	
	slideLeft: function(oldImg, newImg, dur){
		newImg.setOpacity(1);
		var l = this.container.getSize().x;
		var swapl = l - (l*2);
		newImg.setStyles({
			'visibility': 'visible',
			'left' : l
		});
		newImg.inject(this.container, 'bottom');
		oldImg.set('tween', {duration:dur, transition:'quad:in:out'} );
		newImg.set('tween', {duration:dur, transition:'quad:in:out'} );
		oldImg.tween('left', swapl);
		newImg.tween('left', 0);
	},
	
	slideRight: function(oldImg, newImg, dur){
		newImg.setOpacity(1);
		var l = this.container.getSize().x;
		var swapl = l - (l*2);
		newImg.setStyles({
			'visibility': 'visible',
			'left' : swapl
		});
		newImg.inject(this.container, 'bottom');
		oldImg.set('tween', {duration:dur, transition:'quad:in:out'} );
		newImg.set('tween', {duration:dur, transition:'quad:in:out'} );
		oldImg.tween('left', l);
		newImg.tween('left', 0);
	},
	
	slideDown: function(oldImg, newImg, dur){
		newImg.setOpacity(1);
		var l = this.container.getSize().y;
		var swapl = l - (l*2);
		newImg.setStyles({
			'visibility': 'visible',
			'top' : swapl
		});
		newImg.inject(this.container, 'bottom');
		oldImg.set('tween', {duration:dur, transition:'quad:in:out'} );
		newImg.set('tween', {duration:dur, transition:'quad:in:out'} );
		oldImg.tween('top', l);
		newImg.tween('top', 0);
	},
	
	bounceDown: function(oldImg, newImg, dur){
		newImg.setOpacity(1);
		var l = this.container.getSize().y;
		var swapl = l - (l*2);
		newImg.setStyles({
			'visibility': 'visible',
			'top' : swapl
		});
		newImg.inject(this.container, 'bottom');
		oldImg.set('tween', {duration:dur, transition:'bounce:out'} );
		newImg.set('tween', {duration:dur, transition:'bounce:out'} );
		oldImg.tween('top', l);
		newImg.tween('top', 0);
	},
	
	wipeRight: function(oldImg, newImg, dur){
		var s = this.container.getSize();
		newImg.setOpacity(1);
		this.resetImage( newImg, ['left', 'top', 'clip'] );
		newImg.inject(this.container, 'top');
		oldImg.set('tween', {duration:dur, transition:'quad:in:out'} );
		oldImg.tween('clip', [ [ 0, s.x, s.x, 0 ].join(' '), [0, s.x, s.y, s.x].join(' ') ]);
		
	},
	
	wipeLeft: function(oldImg, newImg, dur){
		var s = this.container.getSize();
		newImg.setOpacity(1);
		this.resetImage( newImg, ['left', 'top', 'clip'] );
		newImg.inject(this.container, 'top');
		oldImg.set('tween', {duration:dur, transition:'quad:in:out'} );
		oldImg.tween('clip', [ [ 0, s.x, s.y, 0 ].join(' '), [0, 0, s.y, 0].join(' ') ]);
		
	},
	
	wipeDown: function(oldImg, newImg, dur){
		var s = this.container.getSize();
		newImg.setOpacity(1);
		this.resetImage( newImg, ['left', 'top', 'clip'] );
		newImg.inject(this.container, 'top');
		oldImg.set('tween', {duration:dur, transition:'quad:in:out'} );
		oldImg.tween('clip', [ [ 0, s.x, s.y, 0 ].join(' '), [s.y, s.x, s.y, 0].join(' ') ]);
		
	},
	
	wipeUp: function(oldImg, newImg, dur){
		var s = this.container.getSize();
		newImg.setOpacity(1);
		this.resetImage( newImg, ['left', 'top', 'clip'] );
		newImg.inject(this.container, 'top');
		oldImg.set('tween', {duration:dur, transition:'quad:in:out'} );
		oldImg.tween('clip', [ [ 0, s.x, s.y, 0 ].join(' '), [0, s.x, 0, 0].join(' ') ]);
		
	},
	
	wipeRightFade: function(oldImg, newImg, dur){
		var s = this.container.getSize();
		newImg.setOpacity(1);
		this.resetImage( newImg, ['left', 'top', 'clip'] );
		newImg.inject(this.container, 'top');
		oldImg.set('morph', {duration:dur, transition:'quad:in:out'} );
		oldImg.morph({
			'clip' : [ [ 0, s.x, s.x, 0 ].join(' '), [0, s.x, s.y, s.x].join(' ') ],
			'opacity' : 0.5
					 });
	},
	
	wipeLeftFade: function(oldImg, newImg, dur){
		var s = this.container.getSize();
		newImg.setOpacity(1);
		this.resetImage( newImg, ['left', 'top', 'clip'] );
		newImg.inject(this.container, 'top');
		oldImg.set('morph', {duration:dur, transition:'quad:in:out'} );
		oldImg.morph({
			'clip' : [ [ 0, s.x, s.x, 0 ].join(' '), [0, 0, s.y, 0].join(' ') ],
			'opacity' : 0.5
					 });
	},
		
	wipeDownFade: function(oldImg, newImg, dur){
		var s = this.container.getSize();
		newImg.setOpacity(1);
		this.resetImage( newImg, ['left', 'top', 'clip'] );
		newImg.inject(this.container, 'top');
		oldImg.set('morph', {duration:dur, transition:'quad:in:out'} );
		oldImg.morph({
			'clip' : [ [ 0, s.x, s.x, 0 ].join(' '), [s.y, s.x, s.y, 0].join(' ') ],
			'opacity' : 0.5
					 });
	},
	
	wipeUpFade: function(oldImg, newImg, dur){
		var s = this.container.getSize();
		newImg.setOpacity(1);
		this.resetImage( newImg, ['left', 'top', 'clip'] );
		newImg.inject(this.container, 'top');
		oldImg.set('morph', {duration:dur, transition:'quad:in:out'} );
		oldImg.morph({
			'clip' : [ [ 0, s.x, s.x, 0 ].join(' '), [0, s.x, 0, 0].join(' ') ],
			'opacity' : 0.5
					 });
	}
		
};

/*-------------------------------------Gallery.XML-----------------------------------------------------

Contains functions for parsing xml data


*/

Gallery.XML = {
/*
	getText()
	 - retreives text value of element
	
*/

	getText:function(node){
		var c = node.childNodes;
		var r = false;
		for(var i=0; i<c.length; i++){
			if(c[i].nodeType == 3){
				r = c[i].nodeValue;
			}
		}
		return r;
	},
	
/*
	getElements()
	- gets all direcect descendants of node with given name
*/

	getElements:function(node, name){
		var c = node.childNodes;
		var r = [];
		for(var i=0; i<c.length; i++){
			if(c[i].nodeName == name){
				r.push(c[i]);
			}
		}
		return r;
	},
	
/*
	getElements()
	- same as above but only returns 1st element
*/

	getElement:function(node, name){
		return Gallery.XML.getElements(node, name)[0];
	},

	obj : {},
	data : [],
	parseElement: function(key, el, name, obj){
		try{
			obj[key] = Gallery.XML.getText(Gallery.XML.getElement(el, name));
			var c = 0;
		}catch(e){
			obj[key] = false;
		}
		return obj;
	},
/*
	parse()
	- the function used to parse the standard xml file
*/

	parse: function(xml, activeSchema){
		var s = Gallery.Schema[activeSchema];
		var g = Gallery.XML.getElements(xml.documentElement, s.galleryContainer);
		var data;
		
		for(var i=0; i<g.length; i++){
			Gallery.XML.obj = {};
			Gallery.XML.obj['data'] = [];
			Gallery.XML.obj = Gallery.XML.parseElement('name', g[i], s.galleryName, Gallery.XML.obj);
			Gallery.XML.obj = Gallery.XML.parseElement('description', g[i], s.galleryDesciption, Gallery.XML.obj);
			Gallery.XML.obj = Gallery.XML.parseElement('image', g[i], s.galleryImage, Gallery.XML.obj);
			Gallery.XML.obj = Gallery.XML.parseElement('thumb', g[i], s.galleryThumb, Gallery.XML.obj);
			if(s.dataContainer){			
				Gallery.XML.getElements(g[i], s.dataContainer).each(function(d){
					data = {};
					data = Gallery.XML.parseElement('uri', d, s.image, data);
					data = Gallery.XML.parseElement('thumb', d, s.thumb, data);
					data = Gallery.XML.parseElement('title', d, s.title, data);
					data = Gallery.XML.parseElement('caption', d, s.caption, data);
					data = Gallery.XML.parseElement('size', d, s.size, data);
					Gallery.XML.obj['data'].push(data);
				});
			}else{
				Gallery.XML.getElements(g[i], s.image).each(function(f){
					data = {};
					data = Gallery.XML.parseElement('uri', f, s.image);
					Gallery.XML.obj['data'].push(data);
				});
			}
			this.addGallery(Gallery.XML.obj);
		}
		if( Browser.Engine.trident){
			this.goToGallery(this.options.firstGallery, true);
		}else{
			this.fireEvent('galleryDataLoad');
		}
		
	}
}

Gallery.Schema = {

	standard : {
		galleryContainer : 'gallery',
		galleryName : 'galleryName',
		galleryDescription : 'galleryDescription',
		galleryImage : 'galleryImage',
		galleryThumb : 'galleryThumb',
		dataContainer : 'data',
		image : 'uri',
		thumb : 'thumb',
		title : 'title',
		caption : 'caption',
		size : 'size'
	},
	
	alternative: {
		galleryContainer : 'gallery',
		galleryName : 'name',
		galleryDescription : 'galleryDescription',
		galleryImage : 'galleryImage',
		galleryThumb : 'galleryThumb',
		dataContainer : 'image',
		image : 'uri',
		thumb : 'thumb',
		title : 'title',
		caption : 'caption',
		size : 'size'
	}
}

/*------------------------------------------Gallery.Components-------------------------------------------------

container for all additional components - user interfaces

*/

Gallery.Components = {};

/*------------------------------------Gallery.Components.Slideshow---------------------------------------

Automatically changes to the next image in given timframe

*/


Gallery.Components.Slideshow = new Class({

	gallery : '', // holds gallery instance
	delayIndex : '', // holds delay time
	holder: '', //holds repeat function
	
/*--------------------------OPTIONS-------------------------------*/
	options : {
		atEnd: 'return', // what to do when reaching the ned of a gallery - can be 'return' (go back to beginning) 'next' (go to next gallery) or 'stop' (fires stop method)
		whenStopped: 'holding slide', // controls what to do when slideshow is stopped, either 'holding slide' (go to holding slide) or 'first' go to first image
		autostart: true // whether to call play() method immediately or not
	},
	
	/* 
		Constructor
			new Gallery.Components.Slideshow(gallery:object, delay:integer, [options:object]):object
		Arguments:
		- gallery : the gallery instance to use
		- integer: the delay between images, in miliseconds
		- options: optional - see above
	*/
	
	initialize: function(gallery, delay, options){
		this.setOptions(options);
		this.gallery = gallery;
		this.delayIndex = delay + gallery.options.transitionDuration;
		
		this.gallery.addEvent('galleryChange', this.stop.bind(this));
		if(this.options.autostart) this.gallery.addEvent('imageLoadingComplete', this.play.bind(this) );
	},
	
	/* atEnd()
	unsed internally to determine if we've reached the last pic in the gallery
	*/
	
	atEnd: function(){
		return this.gallery.currentIndex >= (this.gallery.galleries[this.gallery.currentGallery].data.length - 1) ? true : false;
	},
	
	/* iterator
	used internalley - called at each iteration
	*/
	iterator: function(){
		if( this.atEnd() ){
			switch(this.options.atEnd){
				case 'next':
					this.gallery.goToGallery(this.gallery.currentGallery + 1);
					break;
				case 'stop':
					this.stop();
					break;
				case 'pause' :
					this.pause();
					break;
				default:
					this.gallery.goTo('first');
			}
		}else{
			this.gallery.goTo('next');
		}
	},
	
	/*
	play(), pause() and stop()
	obvious, really.  No arguments required. pause remains on current image, stop will go to either the holding slide or the first image, depending on this.options.shenStopped
		*/
	
	play: function(){
		this.holder = this.iterator.periodical(this.delayIndex, this);
	},
	
	stop: function(){
		this.pause();
		if(this.options.whenStopped == 'holding slide'){
			this.gallery.goToHoldingSlide();
		}else{
			this.gallery.goTo('first');
		}
	},
	
	pause: function(){
		this.holder = $clear(this.holder);
	},
	
	unpause: function(){
		this.gallery.goTo('next');
		this.play();
	}
	
});
Gallery.Components.Slideshow.implement(new Options);	

		
/*-------------------------- Gallery.Components.CaptionBar-----------------------------------------------------

Displays a caption / title info

-----------------------------------------------------------------------------------------------------------------*/		
			
Gallery.Components.CaptionBar = new Class({
										  
	// variables
	gallery : '', // holds the gallery instance
	element : '', // holds the caption bar element
	duration: '', // the duration of hide / show effect
	/*
		CONSTRUCTOR
		
		new Gallery.Components.CaptionBar(gallery:object, elementId:string, options:object):object
		
		Arguments
		- gallery: the gallery instance to control
		- object: the id of the button element
		- options: the otions object
		
		Options
		- showAlways: (boolean, defalt:false) if true, captionw will always be visible, if false it will show only when the user mouses over the images
		- useHTML: (boolean, default:false)  if true, the caption will accept HTML input, if false only text can be used
		- showCaption: (function|boolean, default:false) - custom function used to show caption
		- hideCaption: (function|boolean, default:false) - custom function used to hide caption	
	*/
	
	options: {
		showAlways: false,
		useHTML : false,
		showCaption : false,
		hideCaption : false
	},
	
	initialize: function( gallery, elementId, options){
		this.setOptions(options);
		this.gallery = gallery;
		this.element = $(elementId);
		
		this.fadeDuration = this.gallery.options.transitionDuration / 2;
		this.element.set( 'fade', { duration: this.duration } );
		this.gallery.addEvent( 'transitionComplete', this.setCaption.bind(this) );
		
		if( !this.options.showAlways ){
			this.hideCaption();
			this.gallery.container.addEvent('mouseout', this.hideCaption.bind(this) );
			this.gallery.container.addEvent('mouseover', this.showCaption.bind(this) );
		}else{
			this.gallery.addEvent( 'transitionBegin', this.hideCaption.bind(this) );
			this.gallery.addEvent( 'transitionComplete', this.showCaption.bind(this) );
		}
	},
	
	setCaption: function(){
		var obj = this.gallery.getCurrrentImageData();
		var caption = obj.caption;
		if( !$chk(caption) ) caption = '';
		if( this.options.useHTML ){
			this.element.set( 'html', caption );
		}else{
			this.element.set( 'text', caption );
		}
	},
	
	showCaption: function(){
		if(this.options.showCaption){
			this.options.showCaption.call(this, this.element);
		}else{
			this.element.tween('opacity', 1);
		}
	},
	
	hideCaption: function(){
		if(this.options.hideCaption){
			this.options.hideCaption.call(this, this.element);
		}else{
			this.element.tween('opacity', 0);
		}
	}
	
	
								
});
Gallery.Components.CaptionBar.implement(new Options);	
	
/*-------------------------- Gallery.Components.Preloader-----------------------------------------------------

helps to provide visual feedback whilst gallery photos are loaded - does not do very much, just provides callbacks

-----------------------------------------------------------------------------------------------------------------*/
	
	
Gallery.Components.Preloader = new Class({

	//variables
	gallery: '', // holds the gallery instance
	timer: '',// hold the preloader time
	current : '', // holds the current gallery data
	beginTime : 0, // hold the time that preloading begins
	loadedSize : 0,
	/*
		CONSTRUCTOR
		
		new Gallery.Components.Preloader( gallery:object, options:object )
		
		Arguments
		- gallery: the gallery instance to control
		- options: the otions object
		
		Options
		- onStart: (function, defalt:$empty) function to fire when preloading begins
				onStart: function( galleryName, number ){
					galleryName:string - name of currently loading gallery
					number:integer - number of pictures to load
				}
				
		- onTick: (function, default:$empty) function fired each time and image loads
				onTick: function( obj ){
					obj:object{
						current:object{
							name: the name of the currently loading image
							size : the size of the currently loading image (if applicable)
							number: the index number of the curent image
						}
						gallery:object{
							name: the name of the current gallery
							size: the total size of the current gallery (if applicable)
							number: the total number of images in this gallery
						}
						loaded:object{
							number: the number of images loaded
							percentage : the percentage loaded
						}
						time:object{
							taken:object{
								minutes: minutes taken
								seconds: seconds taken
								string: rough text string of above
							}
							remaining
								minutes: minutes remaining
								seconds: seconds remaining
								string: rough text string of aboce
							}
				}
		- onFinish: (function, default:empty
	*/		
	
	options: {
		onStart: $empty,
		onTick: $empty,
		onFinish: $empty
	},
	
	getTotalSize: function(){
		var size = 0;;
		this.current.data.each( function(img){
			if(img.size){
				size += parseInt(img.size);
			}
		});
		return size > 0 ? size : false;
	},
				
	
	initialize: function( gallery, options){
		this.setOptions(options);
		this.gallery = gallery;
		this.gallery.addEvent('imageLoadBegin', this.begin.bind(this) );
		this.gallery.addEvent('imageLoaded', this.tick.bind(this) );
		this.gallery.addEvent('imageLoadingComplete', this.finish.bind(this) );
	},
	
	begin: function(){
		var d = new Date();
		this.current = this.gallery.galleries[this.gallery.currentlyLoadingGallery];
		this.beginTime = d.getTime();
		
		this.current.size = this.getTotalSize();
		this.loadedSize = 0;
		this.options.onStart( this.current.name, this.current.data.length );
		
	},
	
	tick: function(counter, index){
	
		var d = new Date();
		if( !this.current ){
			this.current = this.gallery.galleries[this.gallery.currentlyLoadingGallery];
		}
		var name = this.current.data[index].uri.split('/');
		
		name = name[name.length - 1];
		
		var totalSize = this.current.size ? this.current.size : this.current.data.length;
		var add = this.current.data[index].size ? this.current.data[index].size : 1;
		
		this.loadedSize = this.loadedSize + add;
		var perc = this.loadedSize / (totalSize / 100) || 0;
		var millisecs = d.getTime() - this.beginTime;
		var takenMins = ( millisecs / 1000)  / 60;
		takenMins = Math.floor(takenMins);
		var takenSecs = ( millisecs / 1000)  - (takenMins * 60);
		var takenString = takenMins > 1 ? takenMins.round() + ' minutes elapsed' : takenSecs.round() + ' seconds elapsed';
		var remMil = (100 * (millisecs / perc)) - millisecs;
			if( remMil == 'Infinity' ) remMil = 0;
		var remMins = ( remMil / 1000)  / 60;
		remMins = Math.floor(remMins);
		
		var remSecs = (remMil - (remMins * 60) ) / 1000;
		var remString = remMins > 1 ? remMins.round() + ' minutes remaining' : remSecs.round() + ' seconds remaining';
	    
		var obj = {
			current : {
				name : name,
				size : this.current.data[index].size,
				number : index + 1
			},
			gallery : {
				name : this.current.name,
				size : totalSize,
				number : this.current.data.length
			},
			loaded : {
				number : counter,
				percentage : perc
			},
			time : {
				taken : {
					minutes :takenMins,
					seconds : takenSecs,
					string : takenString
				},
				
				remaining : {
					minutes :remMins,
					seconds : remSecs,
					string : remString
				}
			}
				
		}
		this.options.onTick( obj );
	},
	
	finish: function(){
		this.current = {}
		this.beginTime = 0;
		this.options.onFinish(  );
	}
		
		
		
});
		
Gallery.Components.Preloader.implement(new Options);	

				 
										
			
	
	
			
	
