/*
-------------------
#=js1 - Gallery Slideshow
-------------------
   Name:	js1
Version:	1.0.0
 Author:	Joey Fowler <joey@d3corp.com>
Details:	General JS/CSS gallery slideshow that is hopelessly uncustomizable.
-------------------
*/

var js1 = {};

// reduce the amount of "."'s used and just call get_element()
function get_element(elem) {
	return document.getElementById(elem);
}

// get all "child" tags of the specified type from the parent element
function get_tags(tag, parent) {
	parent = (parent || document);
	return parent.getElementsByTagName(tag);
}

// setup the default values of the slideshow object
js1.slideshow = function(name) {
	this.allow_nav_buttons			= true;
	this.autoplay					= true;
	this.hide_descr_when_empty		= true;
	this.hilight_border_color		= '#FF0000';
	this.name						= name;
	this.nav_hover					= 70;
	this.nav_opacity				= 25;
	this.playing					= false;
	this.play_speed					= 5;
	this.position					= 0;
	this.scroll_page				= true;
	this.scroll_speed				= 12;
	this.thumbnail					= Array();
	this.thumbnail_spacing			= 5;
	this.thumb_opacity				= 70;
}

// setup the main slideshow prototype
js1.slideshow.prototype = {
	// initialize the slideshow object
	init: function() {
		var list	= get_tags('li', get_element(this.name)),	// UL->LI that contains all of the thumbnail images and their titles/descriptions
			i		= 0,										// incrementer
			width	= 0;										// holds the current width of the thumbnail container

		this.length		= list.length;										// holder for the length of the list
		this.full_div	= get_element(this.name + '_full_image');			// reference to the "large" image div
		this.full_width	= parseInt(js1.style.val(this.full_div, 'width'));	// gets the width of the "large" image
		this.thumbs_div	= get_element(this.name + '_slider');				// reference to the thumbnail "sliding" div
		this.descr_div	= get_element(this.name + '_description');			// reference to the "description" div

		// setup the left/right slider buttons
		var left_slider				= get_element(this.name + '_slide_left'),
			right_slider			= get_element(this.name + '_slide_right');
		if (left_slider && right_slider) {
			if (this.scroll_page) {
				// we will scroll a full "page" at a time
				left_slider.onclick		= new Function('js1.scroll.init("' + this.name + '_slider", -1, ' + this.scroll_speed + ', true)');
				right_slider.onclick	= new Function('js1.scroll.init("' + this.name + '_slider", 1, ' + this.scroll_speed + ', true)');
			} else {
				// we will scroll "pixel-by-pixel"
				left_slider.onmousedown		= new Function('js1.scroll.init("' + this.name + '_slider", -1, ' + this.scroll_speed + ', false)');
				right_slider.onmousedown	= new Function('js1.scroll.init("' + this.name + '_slider", 1, ' + this.scroll_speed + ', false)');
				left_slider.onmouseup		=
				right_slider.onmouseup		= new Function('js1.scroll.clear_interval("' + this.name + '_slider")');
			}
		} else {
			// one/both of the sliders are "missing", make sure both are hidden
			if (left_slider) left_slider.style.display = 'none';
			if (right_slider) right_slider.style.display = 'none';
		}

		// loop thru all of the thumbnail images and add them to the slider (while also getting their title/descriptions)
		for (i; i<this.length; i++) {
			this.thumbnail[i]	= {};
			var	li				= list[i],
			thumbnail			= this.thumbnail[i];
			
			// get the (first) image in the LI tag
			var thumb = get_tags('img', li)[0];
			
			if ((thumb == null) || (thumb.getAttribute('src') == '')) {
				// there ain't no picture!!! ahhhhhhh...wait, it's ok =P ...just remove this "block" from
				// the thumbnails array and move on..heh
				list.splice(i, 1);
				i--;
				continue; 
			}
			thumbnail.large = thumb.getAttribute('large'); // get the "large" version of the image (set as an attribute on the img tag)
			if (thumbnail.large == '') {
				// there ain't a "large" picture specified, so we have to force to use the thumbnail =/
				thumbnail.large = thumb.getAttribute('src');
			}
			
			thumbnail.descr		= get_tags('p', li)[0].innerHTML;			// image description
			thumbnail.title 	= get_tags('h1', li)[0].innerHTML;			// image title
			thumbnail.url		= (url = get_tags('a', li)[0]) ? url : '';	// any available URL
			if (thumb.getAttribute('default_image') == 'yes') {
				// found a "default" image to start on
				this.position = i;
			}

			// add the thumbnail to the "slider"
			this.thumbs_div.appendChild(thumb);
			
			// add the "alt" attribute to the image
			thumb.setAttribute('alt', thumbnail.title);
			thumb.setAttribute('title', thumbnail.title);
			
			// setup the width / offset of the "slider" and new thumbnail
			width += parseInt(thumb.offsetWidth);
			if (i != (this.length - 1)) {
				thumb.style.marginRight = this.thumbnail_spacing + 'px';
				width += this.thumbnail_spacing;
			}
			this.thumbs_div.style.width = width + 'px';
			
			// setup the faded effect on the thumbnail
			thumb.style.opacity	= this.thumb_opacity / 100;
			thumb.style.filter	= 'alpha(opacity=' + this.thumb_opacity + ')';
			
			// setup the mouse-over effects and the "make me large" clickable do-hicky
			thumb.onmouseover	= new Function('js1.alpha.set(this, 100, 5)');
			thumb.onmouseout	= new Function('js1.alpha.set(this, ' + this.thumb_opacity + ', 5)');
			thumb.onclick		= new Function(this.name + '.load_image(' + i + ', 1)');
		}

		// setup the next/previous buttons (if they exist)
		previous_btn	= get_element(this.name + '_previous');
		next_btn		= get_element(this.name + '_next');
		if (this.allow_nav_buttons && previous_btn && next_btn) {
			// setup the fade styles (and rollover events)
			previous_btn.style.opacity	= next_btn.style.opacity	= this.nav_opacity / 100;
			previous_btn.style.filter	= next_btn.style.filter		= 'alpha(opacity=' + this.nav_opacity + ')';
			previous_btn.onmouseover	= next_btn.onmouseover		= new Function('js1.alpha.set(this, ' + this.nav_hover + ', 5)');
			previous_btn.onmouseout		= next_btn.onmouseout		= new Function('js1.alpha.set(this, ' + this.nav_opacity + ', 5)');
			// setup the navigation click events
			previous_btn.onclick		= new Function(this.name + '.display(-1, 1)');
			next_btn.onclick			= new Function(this.name + '.display(1, 1)');
		} else if (!this.allow_nav_buttons && (previous_btn || next_btn)) {
			// the navigation buttons aren't allowed, hide them (if they exist)
			if (previous_btn) previous_btn.style.display = 'none';
			if (next_btn) next_btn.style.display = 'none';
		}
		
		// setup the play/stop toggle button (if it exists)
		toggle_btn = get_element(this.name + '_toggle');
		if (toggle_btn && (toggle_btn.getAttribute('play_btn') != '') && (toggle_btn.getAttribute('stop_btn') != '')) {
			var toggle_play = document.createElement('img');
			toggle_play.setAttribute('id', this.name+'_toggle_play');
			toggle_play.src = toggle_btn.getAttribute('play_btn');
			toggle_play.onclick = new Function("get_element('" + this.name + "_toggle_play').style.display = 'none'; get_element('" + this.name + "_toggle_stop').style.display = ''; " + this.name + ".playing = true; " + this.name + ".display(0, 0);");

			var toggle_stop = document.createElement('img');
			toggle_stop.setAttribute('id', this.name+'_toggle_stop');
			toggle_stop.src = toggle_btn.getAttribute('stop_btn');
			toggle_stop.onclick = new Function("get_element('" + this.name + "_toggle_stop').style.display = 'none'; get_element('" + this.name + "_toggle_play').style.display = ''; " + this.name + ".playing = false; " + this.name + ".stop_play();");
			
			toggle_btn.appendChild(toggle_play);
			toggle_btn.appendChild(toggle_stop);
			if (this.autoplay) {
				toggle_play.style.display = 'none';
				toggle_stop.style.display = '';
			} else {
				toggle_play.style.display = '';
				toggle_stop.style.display = 'none';
			}
		} else if (toggle_btn) {
			toggle_btn.style.display = 'none';
		}

		// if autoplay is turned on, well, start playing!
		this.display(0, (this.autoplay ? 0 : 1));
		this.playing = this.autoplay;
	},
	
	// display the next image in the specified direction
	display: function(dir, clicked) {
		//console.log(arguments.callee.caller.toString());
		var tmp_pos		= (this.position + dir);
		this.position	= (tmp_pos < 0) ? (this.length - 1) : ((tmp_pos > (this.length - 1)) ? 0 : tmp_pos);
		this.load_image(this.position, clicked);
	},
	
	// setup and load the specified image
	load_image: function(pos, clicked) {

		//console.log(arguments.callee.caller.toString());

		if (clicked) this.stop_play();
		this.position = pos;

		// setup the "temp" image to load the large version of
		this.thumb			= new Image();
		this.thumb.onload	= new Function(this.name + '.image_loaded(' + pos + ', ' + clicked + ')');
		this.thumb.src		= this.thumbnail[pos].large;
		
		// reset all of the borders for all of the images and "highlight" the selected one
		var img = get_tags('img', this.thumbs_div), length = img.length, i = 0, width_pos = 0;
		for (i; i<length; i++) {
			img[i].style.borderColor = (i != pos) ? '' : this.hilight_border_color;
			if (i <= pos) width_pos += parseInt(img[i].offsetWidth) + parseInt(this.thumbnail_spacing);
		}
		
		// determine if the "selected" thumbnail is "out of view" and re-scroll the thumbnails to re-focus it
		var left_pos		= parseInt(js1.style.clean_pixels((this.thumbs_div.style.left || js1.style.val(this.thumbs_div, 'left')))),
			slider_width	= parseInt(this.thumbs_div.parentNode.offsetWidth);
		if ((width_pos + left_pos) > slider_width) {
			// we reached a thumbnail that is semi-(or completely)-out-of-view, scroll the slider a bit to bring it in
			js1.scroll.init(this.name + '_slider', 1, this.scroll_speed, false, (left_pos - ((width_pos + left_pos) - slider_width)));
		} else if ((left_pos < 0) && (this.position == 0)) {
			// we've already scrolled and the "first" image is reached, scroll back
			js1.scroll.init(this.name + '_slider', -1, this.scroll_speed, false, 0);
		}
	},

	// handles when the "large" image finishes loading
	image_loaded: function(pos, clicked) {
		this.full_div.appendChild(this.thumb);
		this.thumb.style.left = parseInt((this.full_div.offsetWidth - this.thumb.offsetWidth) / 2) + 'px';
		
		// if a user hasn't clicked the image (autoplay), reset the timeout to continue the playback
		if (this.playing) this.autoplay_timeout = setTimeout(new Function(this.name + '.display(1, 0)'), (this.play_speed * 1000));
		
		// set the large image's "alt" attribute to be the image's title
		this.thumb.setAttribute('alt', this.thumbnail[pos].title);
		this.thumb.setAttribute('title', this.thumbnail[pos].title);
		
		// check for a URL
		if (this.thumbnail[pos].url != '') {
			// a URL exists for this image, make the URL div "linkable"
			this.full_div.onclick		= new Function('window.location="' + this.thumbnail[pos].url + '"');
			this.full_div.style.cursor	= 'pointer';
		} else {
			// no link, do nothing (for now)
			this.full_div.onclick = null;
			this.full_div.style.cursor = 'default';
		}
		
		// check for a description
		if ((this.thumbnail[pos].descr != '') && this.descr_div) {
			this.descr_div.innerHTML = this.thumbnail[pos].descr;
			if (this.hide_descr_when_empty) this.descr_div.style.display = '';
		} else if (this.descr_div) {
			this.descr_div.innerHTML = '';
			if (this.hide_descr_when_empty) this.descr_div.style.display = 'none';
		}
		
		// remove any "additional" images in the "large image" container
		var img = get_tags('img', this.full_div);
		if (img.length > 1) this.full_div.removeChild(img[0]);
	},
	
	// stop playing by stopping the autoplay stuff, as well as any current 'scrolling'
	stop_play: function() {
		clearTimeout(this.autoplay_timeout);
		js1.scroll.clear_interval(this.name + '_slider');
	}
};

// handles scrolling the thumbnails horizontally
// plans to implement vertical scrolling are on my list =]
js1.scroll = function() {
	return {
		// initialize the scrolling function
		init: function(thumbs, dir, speed, full_page, end_left) {
			// if the thumbs element passed is just the ID, make sure to get the actual thumbnails before proceeding
			thumbs = (typeof(thumbs) == 'object') ? thumbs : get_element(thumbs);

			if (typeof(end_left) == 'undefined') {
				// get the leftmost position of the thumbnails and hard-set it as an attribute
				var left_pos = parseInt(js1.style.clean_pixels((thumbs.style.left || js1.style.val(thumbs, 'left'))));
				thumbs.style.left = left_pos + 'px';
	
				// determine the ending destination / left position and setup the sliding interval
				end_left, thumbs_width = parseInt(thumbs.offsetWidth), slider_width = parseInt(thumbs.parentNode.offsetWidth);
				if (full_page) {
					// we're gonna scroll a full "page" of thumbnails at a time, so make the "end position" the next "page", or whatever's closest
					end_left = (dir == 1) ? ((((thumbs_width + left_pos) - slider_width) < slider_width) ? (left_pos - ((thumbs_width + left_pos) - slider_width)) : (left_pos - slider_width)) : ((left_pos >= -slider_width) ? 0 : (left_pos + slider_width));
				} else {
					// we're gonna scroll 1 pixel at a time so make the "end position" the leftmost/rightmost points
					end_left = (dir == 1) ? -(thumbs_width - slider_width) : 0;
				}
			}
			thumbs.slide_interval = setInterval(function() { js1.scroll.slide(thumbs, end_left, dir, speed); }, 20);
		},

		// perform the "sliding" transition
		slide: function(thumbs, end_left, dir, speed) {
			// if the thumbs element passed is just the ID, make sure to get the actual thumbnails before proceeding
			thumbs = (typeof(thumbs) == 'object') ? thumbs : get_element(thumbs);
			
			// get the current "leftmost" position of the thumbnails
			var left_pos = parseInt(js1.style.clean_pixels((thumbs.style.left || js1.style.val(thumbs, 'left'))));
			if (((dir == 1) && (left_pos <= end_left)) || ((dir == -1) && (left_pos >= end_left))) {
				// we have reached our "destination", kill the slide
				js1.scroll.clear_interval(thumbs);
			} else {
				// we have more sliding to do
				var i = Math.abs(end_left + left_pos);
				i = (i < speed) ? i : speed;
				var n = (left_pos - i * dir);
				thumbs.style.left = n + 'px';
			}
		},

		// clear the "sliding" interval to stop the sliding animation
		clear_interval: function(thumbs) {
			// if the thumbs element passed is just the ID, make sure to get the actual thumbnails before proceeding
			thumbs = (typeof(thumbs) == 'object') ? thumbs : get_element(thumbs);
			clearInterval(thumbs.slide_interval); // clear the "sliding" interval
		}
	}
} ();

// handles the alpha fading in/out of any requested element =]
js1.alpha = function() {
	return {
		// set the specified elements alpha/opacity
		set: function(elem, alpha, steps) {
			// if the element passed is just the ID, make sure to get the actual element before proceeding
			elem = (typeof(elem) == 'object') ? elem : get_element(elem);
			
			// get the current opacity of the element and determine the direction to proceed in
			var opacity = (elem.style.opacity || js1.style.val(elem, 'opacity')),
				dir		= ((alpha > (opacity * 100)) ? 1 : -1);

			// set the elements opacity
			elem.style.opacity = opacity;
			
			// clear any current interval set and then reset it with all of the new settings
			clearInterval(elem.alpha_interval);
			elem.alpha_interval = setInterval(function() { js1.alpha.transition(elem, alpha, dir, steps); }, 20);
		},
		
		// performs the alpha transition to fade in/out
		transition: function(elem, alpha, dir, steps) {
			// calc the new opacity
			var opacity = Math.round(elem.style.opacity * 100);
			
			if (opacity == alpha) {
				// we have reached the ending alpha, clear the interval!
				clearInterval(elem.alpha_interval);
			} else {
				// we still have stuffs to do, calc the new opacity and set both the style and
				// filter so all browsers will register it
				var i = (opacity + Math.ceil(Math.abs(alpha - opacity) / steps) * dir);
				elem.style.opacity	= (i / 100);
				elem.style.filter	= 'alpha(opacity=' + i + ')';
			}
		}
	}
} ();

// we need to pull different style attributes from elements, but browsers (specifically IE) do it different from others
// document.defaultView.getComputedStyle - gets the "computed" style for all browsers, except IE
// elem.currentStyle[*] - gets the current style for IE
js1.style = function() {
	return {
		// return the requested style on the requested element
		val: function(elem, style) {
			// if the element passed is just the ID, make sure to get the actual element before proceeding
			elem = (typeof(elem) == 'object') ? elem : get_element(elem);
			return (document.defaultView && document.defaultView.getComputedStyle) ? document.defaultView.getComputedStyle(elem, null).getPropertyValue(style) : elem.currentStyle[style.replace(/\-(\w)/g, function (str, p1) { return p1.toUpperCase(); })];
		},
		
		// check for, and remove if found, any "px" on a css style
		clean_pixels: function(css) {
			return (((typeof(css) == 'string') && (css.substr((css.length - 2), 2) == 'px')) ? css.substr(0, (css.length - 2)) : css);
		}
	}
} ();
