var QD = { version: '1.0 rc1' }
/* Luis :: Functionality altered to grab html text in the same way as images - 03/09/08 */

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * Class: QD.Preloader
 * Class for preloading images with callbacks and progress reporting
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
QD.Preload = new Class({
	
	Implements: [Events, Options],

	options: {
		root       : '',
		period     : 100,
		onLoad     : $empty,
		onError    : $empty,
		onStart    : $empty,
		onProgress : $empty,
		onComplete : $empty
	},
	
	initialize: function(options){
		this.setOptions(options);
	},
	
	load: function(sources){
		this.index = 0;
		this.images = [];
		this.sources = $splat(sources);
		this.total = this.sources.length;
		
		this.fireEvent('onStart', this.total);
		this.timer = this.progress.periodical(this.options.period, this);
		this.sources.each(function(source, index){
			var isHtml = source.test(/\.(html|htm)$/);
			var isThumb = this.options.root.test(/thumbs\/$/);
			
			if (!isHtml || (isHtml && isThumb)) {
				source = source.replace(/\.(html|htm)$/, '.gif');
				this.images[index] = new Asset.image(this.options.root + source, {
					onload  : function(){ this.index++; this.fireEvent('onLoad',  [index, source, this.images[index]]); }.bind(this),
					onerror : function(){ this.index++; this.fireEvent('onError', [index, source, this.images.splice(index, 1)]); }.bind(this),
					onabort : function(){ this.index++; this.fireEvent('onError', [index, source, this.images.splice(index, 1)]); }.bind(this)
				});
			} else {
				this.request = new Request({ method: 'post', url: this.options.root + source,
					onSuccess : function(text){ this.index++; this.images[index] = text; this.fireEvent('onLoad', [index, source, this.images[index]]); }.bind(this),
					onFailure : function(){ this.index++; this.fireEvent('onError', [index, source, this.images.splice(index, 1)]); }.bind(this),
					onCancel : function(){ this.index++; this.fireEvent('onError',  [index, source, this.images.splice(index, 1)]); }.bind(this)
				}).send();
			}
		}, this);
	},
	
	progress: function() {
		this.fireEvent('onProgress', [Math.min(this.index, this.total), this.total]);
		if(this.index >= this.total) this.complete();
	},
	
	complete: function(){
		$clear(this.timer);
		this.fireEvent('onComplete', [this.images]);
	},
	
	cancel: function(){
		$clear(this.timer);
	}
	
});

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * Class: QD.Gallery
 * Base gallery class written for Nathan Querido
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
QD.Gallery = new Class({
	
	Implements: [Events, Options],
	
	options: {
		width      : 677,
		height     : 428,
		delay      : 3000,
		prefetch   : true,
		root       : 'projects/',
		imagePath  : 'images/',
		thumbsPath : 'thumbs/',
		imageDiv   : 'gallery_image',
		thumbsDiv  : 'gallery_thumbs',
		controlDiv : 'gallery_control',
		fade       : { wait: false, duration: 600 },
		onImageChanged : $empty,
		onImageLoading : $empty,
		onImageLoaded  : $empty
	},
	
	getOptions: function(){
		var options = $merge(this.options);
		options.initialize = null;
		return options;
	},
	
	initialize: function(data, options){
		this.setOptions(options);
		this.data = data;
		
		this.styles = { width : this.options.width, height : this.options.height };
		this.timeout = null;
		
		this.image  = new Element('div', { styles: this.styles });
		this.buffer = new Element('div', { styles: this.styles });
		this.buffer.get('tween', 'opacity', this.options.fade).addEvent('onComplete', this.resetImage.bind(this));
		
		this.container  = $(this.options.imageDiv).setStyles(this.styles).adopt(this.image, this.buffer);
		this.preloader  = new QD.Preload({ onLoad: this.showImage.bind(this) });
		this.prefetch   = new QD.Preload();
		this.thumbnails = new QD.Thumbnails(this);
		this.control    = new QD.Control(this);
	},
	
	loadGallery: function(category, gallery){
		var data = this.data[category].galleries[gallery];
		this.index = 0;
		this.idle = true;
		this.images = data.images;
		this.preloader.options.root = this.prefetch.options.root = this.options.root + data.path + this.options.imagePath;
		this.preloader.load(data.images[0].filename);
		this.thumbnails.loadGallery(data);
	},
	
	loadImage: function(source, index){
		if(!this.idle || this.index == index) return;
		this.idle = false;
		this.timeout = (function(){ this.fireEvent('onImageLoading'); }).delay(100, this);
		this.index = index;
		this.preloader.load(source);
		this.fireEvent('onImageChanged');
	},
	
	showImage: function(index, source, image){
		$clear(this.timeout);
		if ($defined(image) && image.src) {
			this.image.set('html', '');
			this.image.setStyle('background-image', 'url(' + image.src + ')');
			['gallery_image_prev', 'gallery_image_next'].each(function(button){ $(button).setStyle('display', ''); });
		} else {
			this.image.set('html', image);
			this.image.setStyle('background-image', '');
			['gallery_image_prev', 'gallery_image_next'].each(function(button){ $(button).setStyle('display', 'none'); });
		}
		this.buffer.fade(0);
		this.fireEvent('onImageLoaded', this.images[this.index]);
	},
	
	resetImage: function(){
		if (this.image.getStyle('background-image')) {
			this.buffer.set('html', this.image.get('html'));
			this.buffer.setStyle('background-image', this.image.getStyle('background-image'));
		} else {
			this.buffer.setStyle('background-image', '');
			this.buffer.set('html', '');
		}
		this.buffer.fade(1);
		this.idle = this.thumbnails.idle = true;
	},
	
	prefetchImage: function(source){
		this.prefetch.load(source);
	},
	
	startSlideshow: function(){
		this.timer = this.thumbnails.nextImage.periodical(this.options.delay, this.thumbnails);
	},
	
	stopSlideshow: function(){
		$clear(this.timer);
	}
	
});

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * Class: QD.Control
 * Navigation generating class for QD.Gallery
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
QD.Control = new Class({
	
	Implements: Options,
	
	options: {
		initial   : [0, 0],
		accordion : { duration: 300, opacity: false }
	},
	
	initialize: function(gallery){
		this.setOptions(gallery.getOptions());
		this.gallery = gallery;
		this.cindex = this.options.initial[0];
		this.gindex = this.options.initial[1];
		
		this.controlDiv = $(this.options.controlDiv);
		this.controlDiv.adopt(new Element('ul', { 'class': 'main' }).adopt(this.gallery.data.map(function(category, cindex){
			return new Element('li', { 'class': 'main' }).adopt(
				new Element('a', { 'href': '#' }).set('html', category.title).addEvent('click', function(event){ event.stop(); }),
				new Element('ul', { 'class': 'sub' }).adopt(category.galleries.map(function(gallery, gindex){
					return new Element('li').adopt(new Element('a', { 'href': '#' }).set('html', gallery.title).addEvent('click', function(event){ event.stop(); this.loadGallery(cindex, gindex); }.bind(this)));
				}, this), new Element('li')));
		}, this)));
		
		this.lists      = this.controlDiv.getElements('ul.sub');
		this.categories = this.controlDiv.getElements('li.main').getFirst();
		this.galleries  = this.categories.getNext().getElements('a');
		
		if(this.options.accordion) this.accordion = new Accordion(this.categories, this.lists, $merge(this.options.accordion, { show: this.cindex }));
		this.controlDiv.show();
		
		this.loadGallery(this.cindex, this.gindex, true);
	},
	
	loadGallery: function(category, gallery, force){
		if(!force && category == this.cindex && gallery == this.gindex) return;
		
		this.categories[this.cindex].removeClass('active');
		this.categories[category].addClass('active');
		this.galleries[this.cindex][this.gindex].removeClass('active');
		this.galleries[category][gallery].addClass('active');
		
		this.gallery.loadGallery(category, gallery);
		this.cindex = category;
		this.gindex = gallery;
	},
	
	reloadGallery: function(){
		this.loadGallery(this.cindex, this.gindex, true);
	}
	
});

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * Class: QD.Thumbnails
 * Class which creates a preloading paginated photo viewer
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
QD.Thumbnails = new Class({
	
	Implements: [Events, Options],

	options: {
		prevPage       : false,
		nextPage       : false,
		prevImage      : false,
		nextImage      : false,
		trayWidth      : 695,
		activeClass    : 'active',
		fadeOpacity    : 0.55,
		imagesPerPage  : 6,
		displayThumbs  : true,
		displayNumbers : true,
		slide          : { wait: false, duration: 500, transition: Fx.Transitions.Sine.easeOut },
		onSlide        : $empty
	},
	
	initialize: function(gallery){
		this.setOptions(gallery.getOptions());
		this.gallery = gallery;
		
		this.buttons = {};
		this.container = $(this.options.thumbsDiv);
		this.wrap = new Element('div').setStyles({ 'left': 0 }).inject(this.container);
		this.wrap.set('tween', this.options.fade);
		this.preloader = new QD.Preload({
			onLoad: this.addImage.bind(this),
			onStart: function(total){ total.times(this.initImage.bind(this)); }.bind(this),
			onComplete: function(){ this.scroller.toLeft(); this.loadImage(0, true); }.bind(this)
		});
		this.scroller = new Fx.Scroll(this.container, this.options.slide).addEvents({
			onComplete: function(){ this.idle = true; }.bind(this),
			onStart: function(){ this.resetButtons(); }.bind(this)
		});
		
		['prevPage', 'nextPage', 'prevImage', 'nextImage'].each(function(button){
			var element = $(this.options[button]);
			if(element) element.addEvent('click', function(event){ event.stop(); this[button](); }.bind(this)).get('tween', 'opacity', {
				wait: false,
				duration: 300
			}).set(0.001);
			if(button.test(/Image/)) element.addEvents({
				mouseover: function(){ (Browser.Engine.trident5) ? this.set('opacity', 1) : this.fade(0.7); },
				mouseout: function(){ (Browser.Engine.trident5) ? this.set('opacity', 0.001) : this.fade(0.001); }
			});
			this.buttons[button] = element;
		}, this);
		
		document.addEvent('keydown', function(event){
			if(event.key == 'left') this.prevImage();
			else if(event.key == 'right') this.nextImage();
		}.bind(this));
		
		this.reset();
	},
	
	reset: function(){
		this.idle = true;
		this.active = null;
		this.page = this.index = this.numPages = this.numImages = 0;
		this.pages = [];
		this.thumbs = [];
		this.sources = [];
	},
	
	resetButtons: function(){
		if(this.buttons['prevPage']) (Browser.Engine.trident5) ? (this.page == 0) ? this.buttons['prevPage'].set('opacity', 0) : this.buttons['prevPage'].set('opacity', 0.999) : this.buttons['prevPage'].fade(this.page == 0 ? 0 : 0.999);
		if(this.buttons['nextPage']) (Browser.Engine.trident5) ? (this.page == 0) ? this.buttons['nextPage'].set('opacity', 0) : this.buttons['nextPage'].set('opacity', 0.999) : this.buttons['nextPage'].fade(this.page == 0 ? 0 : 0.999);
	},
	
	loadGallery: function(data){
		this.reset();
		this.wrap.get('tween', 'opacity').start(0).chain(function(){
			this.wrap.empty();
			this.wrap.fade('show');
			
			this.sources = data.images.map(function(image){ return image.filename; });
			this.numImages = this.sources.length;
			this.wrap.setStyle('width', Math.ceil(this.numImages / this.options.imagesPerPage) * this.options.trayWidth);

			this.preloader.options.root = this.options.root + data.path + this.options.thumbsPath;
			this.preloader.load(this.sources);
		}.bind(this));
	},
	
	initImage: function(index, source){
		if(index % this.options.imagesPerPage == 0){
			this.pages[this.numPages++] = new Element('ul').setStyles({
				'width' : this.options.trayWidth,
				'left'  : (this.numPages - 1) * this.options.trayWidth
			}).inject(this.wrap);
		}
		
		var elements = [];
		if(this.options.displayThumbs) elements.push(new Element('img'));
		if(this.options.displayNumbers) elements.push(new Element('span').set('html', (index + 1).zeroPad(2)));
		
		var li = new Element('li', {
			'events': {
				mouseenter: function(){
					if(li.hasClass('active')) return;
					li.fade(0.999);
				}.bind(this),
				mouseleave: function(){
					if(li.hasClass('active')) return;
					li.fade(this.options.fadeOpacity);
				}.bind(this),
				mousedown: function(){ this.loadImage(index); }.bind(this)
			}
		});
		this.thumbs[index] = li;
		li.set('tween', { wait: false, duration: 200 }).fade('hide').adopt(elements).inject(this.pages[this.numPages - 1]);
	},
	
	addImage: function(index, source, image){
		var li = this.thumbs[index];
		var img = li.getElement('img');
		
		if(img) img.replaceWith(image);
		li.fade(this.options.fadeOpacity);
	},
	
	loadImage: function(index, force){
		if(!this.idle || (index == this.index && !force)) return;
		else if(index < 0) index = this.numImages - 1;
		else if(index > this.numImages - 1) index = 0;
		this.index = index;
		this.idle = false;
		
		if(this.active) this.active.removeClass(this.options.activeClass).fireEvent('mouseleave');
		this.active = this.thumbs[index].fireEvent('mouseenter').addClass(this.options.activeClass);
		this.gallery.loadImage(this.sources[index], index);
		if(this.options.prefetch && index < this.numImages - 1) this.gallery.prefetchImage(this.sources[index + 1]);
		this.loadPage((index / this.options.imagesPerPage).toInt());
	},
	
	nextImage: function(){
		this.loadImage(this.index + 1);
	},
	
	prevImage: function(){
		this.loadImage(this.index - 1);
	},
	
	loadPage: function(page){
		if(page < 0 || page >= this.numPages) return;
		this.page = page;
		this.idle = false;
		this.scroller.toElement(this.pages[this.page]);
		this.fireEvent('onSlide', this.page);
	},
	
	nextPage: function(){
		this.loadPage(this.page + 1);
	},
	
	prevPage: function(){
		this.loadPage(this.page - 1);
	}
	
});
