/* Gallery JavaScript
 *
 * Copyright (c) Aaron Bieber, 2009
 * http://blog.aaronbieber.com
 *
 * This script is provided AS IS with no warranty expressed or implied.
 */
/* Class: Gallery {{{
 *
 * Display an interactive gallery or a slideshow.
 */
var Gallery = Class.create({
	/* Function: initialize() {{{
	 *
	 */
	initialize: function() {
		/* Check dependencies! */
		if(typeof Prototype == 'undefined') alert('Gallery requires the Prototype library (http://prototypejs.org).');
		if(typeof Effect == 'undefined') alert('Gallery requires the Scriptaculous Effects library (http://scriptaculous.org).');
		/* Options and defaults {{{ */
		this.options = Object.extend({
			stage: null,
			imagelist: null,
			width: 300,
			height: 300,
			prefix: 'ss',
			duration: 0.5,
			delay: 4,
			filmstrip: true,
			autorun: true
		}, arguments[0] || {});

		this.options.delay = this.options.delay * 1000;
		this.running = this.options.autorun;
		this.prefix = this.options.prefix;
		this.uid = this.prefix+this.generateId();
		this.stage = $(this.options.stage);
		this.imagelist = $(this.options.imagelist);
		this.index = 1;
		this.timer;
		this.items = [];
		this.thumbs = [];
		this.images = {
			loading: null,
			loaded: null
		}
		// }}}
		/* Generate the filmstrip {{{ */
		if(this.options.filmstrip) {
			this.filmstrip = new Element('div', { id: this.uid+'_filmstrip', 'class': this.prefix+'_filmstrip' });

			this.slideleft = new Element('div', {
				id: this.uid+'_filmstrip_left',
				'class': this.prefix+'_filmstrip_button '+this.prefix+'_filmstrip_left'
			}).setStyle({
				float: 'left'
			});

			this.slider = new Element('div', { id: this.uid+'_slider', 'class': this.prefix+'_slider' }).setStyle({
				float: 'left',
				overflow: 'hidden',
				width: '1%',
				position: 'relative'
			});
			this.slider_mover = new Element('div', { id: this.uid+'_slider_mover', 'class': this.prefix+'_slider_mover' }).setStyle({ paddingLeft: '1px' });
			//.setStyle({
			//	position: 'relative'
			//});
			this.slider.insert({ top: this.slider_mover });

			this.slideright = new Element('div', {
				id: this.uid+'_filmstrip_right',
				'class': this.prefix+'_filmstrip_button '+this.prefix+'_filmstrip_right'
			}).setStyle({
				float: 'right'
			});

			this.filmstrip.insert({ bottom: this.slideleft });
			this.filmstrip.insert({ bottom: this.slider });
			this.filmstrip.insert({ bottom: this.slideright });
			this.stage.insert({ bottom: this.filmstrip });

			this.slider.setStyle({
				width: this.stage.getWidth() - this.slideleft.getWidth() - this.slideright.getWidth() + 'px'
			});
		}
		// }}}
		// Configure base elements {{{
		this.stage.setStyle({
			width: this.options.width + 'px',
			overflow: 'hidden'
		});
		this.image_container = new Element('div', { id: this.uid+'_image_container' }).setStyle({
			width: this.options.width + 'px',
			height: this.options.height + 'px',
			backgroundColor: 'white',
			textAlign: 'center',
			position: 'relative'
		});
		this.stage.insert({ top: this.image_container });
		this.loading = new Element('div', { id: this.uid+'_loading', 'class': this.prefix+'_loading' }).setStyle({
			display: 'none',
			position: 'absolute',
			zIndex: 200
		});
		this.stage.insert({ bottom: this.loading });
		this.loading.setStyle({
			left: ((this.image_container.getWidth() - this.loading.getWidth()) / 2) + 'px',
			top: ((this.image_container.getHeight() - this.loading.getHeight()) / 2) + 'px'
		});
		// }}}
		/* Read image data from the list {{{ */
		var source_items = $A(this.imagelist.getElementsByTagName('LI'));
		source_items.each(function(item) {
			item = $(item);
			tempitem = {};
			tempitem.title = (item.down('h3') || new Element('h3')).innerHTML;
			tempitem.src = (item.down('span') || new Element('span')).innerHTML;
			tempitem.caption = (item.down('p') || new Element('p')).innerHTML;
			tempitem.thumb = (item.down('img') || new Element('img'));
			this.items.push(tempitem);
		}.bind(this));
		// }}}
		/* Configure event handlers {{{ */
		this.swapHandler		= this.swap.bind(this);
		this.loadEventHandler	= this.loadEvent.bind(this);
		this.overHandler		= this.over.bindAsEventListener(this);
		this.outHandler			= this.out.bindAsEventListener(this);
		this.slideHandler		= this.slide.bindAsEventListener(this);
		this.selectHandler		= this.select.bindAsEventListener(this);
		// }}}
		/* Populate the filmstrip {{{ */
		if(this.filmstrip) {
			var sm_width = 0;
			var idx = 1;
			for(idx=0;idx<this.items.length;idx++) {
				var item = this.items[idx];
				if(item.thumb && item.thumb.src) {
					img_class = this.prefix+'_thumb' + 
						((idx==0) ? ' '+this.prefix+'_thumb_first' : '') +
						((idx==this.items.length-1) ? ' '+this.prefix+'_thumb_last' : '');

					timg = new Element('img', {
						id: this.uid+'_thumb_'+idx,
						'class': img_class,
						src: item.thumb.src
					});
					this.slider_mover.insert({ bottom: timg });
					this.thumbs.push(timg);
					sm_width += timg.getWidth() +
								parseInt(timg.getStyle('marginLeft') || 0) +
								parseInt(timg.getStyle('marginRight') || 0);

					Event.observe(timg, 'mouseover', this.overHandler);
					Event.observe(timg, 'mouseout', this.outHandler);
					Event.observe(timg, 'click', this.selectHandler);
				}
			}
			sm_width += parseInt(this.slider_mover.getStyle('paddingLeft') || 0) +
						parseInt(this.slider_mover.getStyle('paddingRight') || 0);

			this.slider_mover.setStyle({ width: sm_width + 'px' });
		} // }}}
		/* Configure event observers {{{ */
		Event.observe(this.slideleft, 'mouseover', this.overHandler);
		Event.observe(this.slideright, 'mouseover', this.overHandler);
		Event.observe(this.slideleft, 'mouseout', this.outHandler);
		Event.observe(this.slideright, 'mouseout', this.outHandler);
		Event.observe(this.slideleft, 'click', this.slideHandler);
		Event.observe(this.slideright, 'click', this.slideHandler);
		// }}}
		if(this.items.length) this.load();
	}, // }}}
	/* Function: select() {{{
	 *
	 */
	select: function(e) {
		var elm = Event.element(e);
		var index = (elm.id.match(/_thumb_(\d+)/)[1]*1) + 1;
		try { clearTimeout(this.timer) } catch(e) { }
		this.running = false;
		this.load(index);
	}, // }}}
	/* Function: slide() {{{
	 *
	 */
	slide: function(e) {
		var elm = Event.element(e);
		if(this.slider_mover.getWidth() <= this.slider.getWidth()) return;

		var sm_pos = this.slider_mover.cumulativeOffset();
		var s_pos = this.slider.cumulativeOffset();
		if(elm.className.match(/_left/)) this.slideRight();
		else this.slideLeft();
	}, // }}}
	/* Function: slideLeft() {{{
	 *
	 */
	slideLeft: function() {
		var sm_pos = this.slider_mover.cumulativeOffset();
		var s_pos = this.slider.cumulativeOffset();
		var min = s_pos.left - (this.slider_mover.getWidth() - this.slider.getWidth());
		if(sm_pos.left <= min) return;
		var newleft = Math.ceil(sm_pos.left - (this.slider.getWidth() / 2));
		if(newleft < min) newleft = min;
		new Effect.Move(this.slider_mover, { x: -Math.abs(sm_pos.left-newleft), mode: 'relative' });
	}, // }}}
	/* Function: slideRight() {{{
	 *
	 */
	slideRight: function() {
		var sm_pos = this.slider_mover.cumulativeOffset();
		var s_pos = this.slider.cumulativeOffset();
		var max = s_pos.left;
		if(sm_pos.left >= max) return;
		var newleft = Math.ceil(sm_pos.left + (this.slider.getWidth() / 2));
		if(newleft > max) newleft = max;
		new Effect.Move(this.slider_mover, { x: Math.abs(sm_pos.left-newleft), mode: 'relative' });
	}, // }}}
	/* Function: slideMaxLeft() {{{
	 *
	 */
	slideMaxLeft: function() {
		var sm_pos = this.slider_mover.cumulativeOffset();
		var s_pos = this.slider.cumulativeOffset();
		var min = s_pos.left - (this.slider_mover.getWidth() - this.slider.getWidth());
		if(sm_pos.left <= min) return;
		new Effect.Move(this.slider_mover, { x: -Math.abs(sm_pos.left-min), mode: 'relative' });
	}, // }}}
	/* Function: slideMaxRight() {{{
	 *
	 */
	slideMaxRight: function() {
		var sm_pos = this.slider_mover.cumulativeOffset();
		var s_pos = this.slider.cumulativeOffset();
		var max = s_pos.left;
		if(sm_pos.left >= max) return;
		new Effect.Move(this.slider_mover, { x: Math.abs(sm_pos.left-max), mode: 'relative' });
	}, // }}}
	/* Function: over() {{{
	 *
	 */
	over: function(e) {
		var elm = Event.element(e);
		if(elm.className.match(/_thumb/)) {
			elm.className += ' '+this.prefix+'_thumb_over';
		} else if(elm.className.match(/_filmstrip_button/)) {
			elm.className += ' '+this.prefix+'_filmstrip_button_over';
		}
	}, // }}}
	/* Function: out() {{{
	 *
	 */
	out: function(e) {
		var elm = Event.element(e);
		if(elm.className.match(/_thumb/)) {
			var pat = new RegExp('\s*'+this.prefix+'_thumb_over\s*');
			elm.className = elm.className.gsub(pat, ' ');
		} else if(elm.className.match(/_filmstrip_button/)) {
			var pat = new RegExp('\s*'+this.prefix+'_filmstrip_button_over\s*');
			elm.className = elm.className.gsub(pat, ' ');
		}
	}, // }}}
	/* Function: getImageObject() {{{
	 *
	 */
	getImageObject: function() {
		var img = new Element('img', { alt: '' }).setStyle({
			display: 'none',
			position: 'absolute',
      top: 0,
      left: 0,
			zIndex: 50
		});
		this.image_container.insert({ top: img });
		Event.observe(img, 'load', this.loadEventHandler);
		return img;
	}, // }}}
	/* Function: load() {{{
	 *
	 */
	load: function() {
		var index = arguments[0] || this.index;
		var item = this.items[index-1];
		
		if(this.options.filmstrip) {
			this.thumbs[index-1].className += ' '+this.prefix+'_thumb_active';
			if(index != this.index) {
				var pat = new RegExp('\s*'+this.prefix+'_thumb_active\s*');
				this.thumbs[this.index-1].className =
					this.thumbs[this.index-1].className.gsub(pat, ' ');
			}
			var thumb_right = (thumb_left = this.thumbs[index-1].cumulativeOffset().left) +
				this.thumbs[index-1].getWidth();
			var slide_right = (slide_left = this.slider.cumulativeOffset().left) +
				this.slider.getWidth();
			if(thumb_right > slide_right) this.slideLeft();
			else if(thumb_left < slide_left)
				if(index == 1) this.slideMaxRight();
				else this.slideRight();
		}

		this.index = index;

		this.images.loading = this.getImageObject();
		this.images.loading.src = item.src;
		if(!this.images.loading.complete) this.showLoading();
	}, // }}}
	/* Function: loadNext() {{{
	 *
	 */
	loadNext: function() {
		try { clearTimeout(this.timer) } catch(e) { }
		var index = (this.index == this.items.length) ? 1 : this.index + 1;
		this.load(index);
	}, // }}}
	/* Function: loadEvent() {{{
	 *
	 */
	loadEvent: function() {
		this.hideLoading();
		this.swap();
		if(this.running) this.queueNext();
	}, // }}}
	/* Function: queueNext() {{{
	 *
	 */
	queueNext: function() {
		this.timer = setTimeout(this.loadNext.bind(this), this.options.delay);
	}, // }}}
	/* Function: showLoading() {{{
	 *
	 */
	showLoading: function() {
		new Effect.Appear(this.loading, { duration: 0.3, queue: { position: 'end', scope: this.uid+'_loading' } });
	}, // }}}
	/* Function: hideLoading() {{{
	 *
	 */
	hideLoading: function() {
		if(this.loading.visible()) {
			new Effect.Fade(this.loading, { duration: 1, queue: { position: 'end', scope: this.uid+'_loading' } });
			//new Effect.Invoke(function() { this.loading.hide() }.bind(this));
		}
	}, // }}}
	/* Function: swap() {{{
	 *
	 */
	swap: function() {
		if(!this.images.loaded) {
			this.images.loaded = this.images.loading;
			this.images.loading = null;
      this.images.loaded.setStyle({
        borderLeft: (this.options.width - this.images.loaded.getWidth()) / 2 + 'px solid white',
        borderRight: (this.options.width - this.images.loaded.getWidth()) / 2 + 'px solid white'
      });
			this.images.loaded.appear({ duration: this.options.duration });
			new Effect.Invoke(this.swap.bind(this));
		} else {
			if(this.images.loading) {
				if(this.images.loaded.visible()) {
					this.images.loading.show();
					this.images.loading.setOpacity(1);
          this.images.loading.setStyle({
            borderLeft: (this.options.width - this.images.loading.getWidth()) / 2 + 'px solid white',
            borderRight: (this.options.width - this.images.loading.getWidth()) / 2 + 'px solid white'
          });
					new Effect.Fade(this.images.loaded, { duration: this.options.duration });
					new Effect.Invoke(this.swap.bind(this), { delay: 0.1 });
				} else {
					this.images.loaded.remove();
					this.images.loaded = this.images.loading;
					this.images.loading = null;
					this.images.loaded.setStyle({ zIndex: 100 });
					this.images.loaded.setOpacity(1);
				}
			} else {
				this.images.loaded.setStyle({ zIndex: 100 });
				this.images.loaded.setOpacity(1);
			}
		}
	}, // }}}
	/* Function: generateId() {{{
	 *
	 */
	generateId: function() {
		var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz";
		var randomstring = '';
		for (var i=0; i<4; i++) {
			var rnum = Math.floor(Math.random() * chars.length);
			randomstring += chars.substring(rnum,rnum+1);
		}
		return randomstring;
	}, // }}}
	/* Function: d() {{{ */
	d: function() {
		var msg = arguments[0] || '';
		$('d').innerHTML += msg + "<br />";
	} // }}}
}); // }}}
/* Class: Effect.Invoke {{{
 *
 * A mix-in to Scriptaculous that gives you the ability to piggyback any
 * function call onto their event queues. Create the Effect.Invoke object
 * the way you would create any Scriptaculous object, and take advantage
 * of all of the available queue settings.
 *
 * Fade out a box called "box" and then pop up an alert:
 *
 * new Effect.Fade('box');
 * new Effect.Invoke(function() { alert('Done!') });
 */
Effect.Invoke = Class.create({
	initialize: function() {
		this.options = Object.extend({
			queue:	'end',
			from:	0.0,
			to:		1.0,
			delay:	0.0
		}, arguments[1] || {});
		this.func = arguments[0] || function() {};
		this.startOn = this.options.delay*1000;
		this.finishOn = 0;

		Effect.Queues.get(Object.isString(this.options.queue) ?
			'global' : this.options.queue.scope).add(this);
	},

	loop: function(timePos) {
		if (timePos >= this.startOn) {
			this.func();
			Effect.Queues.get(Object.isString(this.options.queue) ?
				'global' : this.options.queue.scope).remove(this);
		}
	}
}); // }}}
