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

	ImageBox v1.0

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

// Initialize the Imagebox variables & constants
// holds the image to be shown next, used to get the dimensions of the image
var tempImg = new Image();
// holds the overlay div, used to get the dimensions of the viewport
var overlay,
// used to tell if the next or previous buttons should be shown for gallery images
showNext, showPrev,
// holds the thumbnail image
thumbImg,
// these hold the current dimensions of the imagebox
curLeft, curTop, curWidth, curHeight,
// these hold the new dimensions of the imagebox (what to resize the box to)
newLeft, newTop, newWidth, newHeight,
// these hold the dimensions of the thumnail image
thumbWidth, thumbHeight,
// if true, the current image being viewed has a title
hasTitle = false,
// detects if the browser is Internet Explorer (to fix the png alpha problem)
isIE = navigator.userAgent.toLowerCase().indexOf('msie') > -1? true : false;
// -----------------------------------------------------------------------------------

// Create the imagebox object
var ib = {
	
	/**
	* imagebox options 
	* modify these to change the speed and style of animations, as well as other options
	*/	
	options: {

		openDuration: 0.35,          // the duration (in seconds) of the zooming animation. Defaults to 0.35.
		resizeDuration: 0.35,        // the duration (in seconds) of the resizing animation. Defaults to 0.35.
		fadeDuration: 0.40,          // the duration (in seconds) of all the fading animations. Defaults to 0.40.
		
		animSequence: 'sync',        // the sequence of the resizing animation 'sync' resizes both width and height at the 
		                             // same time. 'wh' resizes the width then the height. 'hw' resizes the height
		                             // then the width. Defaults to 'sync'
		animOpen: 'zoom',            // the animation to use when imagebox opens and closes. 'zoom' enlarges the image from
		                             // the thumbnail size, then shrinks back to the thumbnail size
		                             // 'fade' fades the image in then out. Defaults to 'zoom'
		easingOpen: 'ease-in-out',   // the easing to use for the opening and closing animation (only for animOpen = 'zoom')
		                             // choices are: 'none', 'ease-in', 'ease-out', 'ease-in-out', and 'bounce'
		                             // defaults to 'ease-in-out'
		easingResize: 'ease-in-out', // the easing to use for the resizing animation
		                             // choices are: 'none', 'ease-in', 'ease-out', 'ease-in-out', and 'bounce'
		                             // defaults to 'ease-in-out'
		galleryCounter: 'default',   // the method to use in displaying the gallery counter.
		                             // choices are: 'default' and 'skip'. Default displays "Image 1 of 3", and skip displays
		                             // "Gallery: 1 2 3" allowing the user to skip to any image in the current gallery
		continuousGalleries: false,  // if true, clicking next on the last image in the gallery will loop to the first image
		                             // and the clicking previous on the first image will loop to the last image
		                             // defaults to false
		viewportPadding: 20          // the amount padding (in pixels) around the browser window
		                             // defaults to 20
	
	},
	
	/**
	* settings to customize how imagebox looks
	*/	
	skin: {
		
		background: '#FFF',                // the background color of imagebox (in hex format e.g. #fff)
		
		title: {
			
			background: '#444',            // the background color of the title (in hex format e.g. #fff)
			color: '#FFF',                 // the text color of the title (in hex format e.g. #fff)
			font: 'Gill Sans MT, Verdana'                // the font of the title
			
		},
		
		pngs: {
			
			close: 'images/imagebox/closebox.png',  // the image of the 'close' button
			next: 'images/imagebox/nextImage.png',  // the image of the 'next' button
			prev: 'images/imagebox/prevImage.png'   // the image of the 'previous' button
			
		}
		
	},

	/* The box that contains all the elements of imagebox */
	box: null,
	
	/* The imagebox's close button */
	closeBtn: null,
	
	/* The imagebox's next button */
	nextBtn: null,
	
	/* The imagebox's previous button */
	prevBtn: null,
	
	/* The current image being viewed in ImageBox */
	image: null,
	
	/* The current image's title */
	title: null,
	
	/* An array to preload the imagebox ready images */
	preloader: [],
	
	/* An array to hold all of the images in the current gallery */	
	gallery: [],
	
	/* The current image being displayed in the gallery */
	current: -1,	
	
	/* An array to hold all of the imagebox ready images */	
	cache: [],	
	
	/**
	* regex patterns
	*/
	regex: {
		
		// Matches the rel attribute of the image
		rel: /(image|light)box/i,
		
		// Matches if the image is part of a gallery
		gallery: /(?:image|light)box\[(.*?)\]/i
		
	},
	
	/**
	* Sets the default options and sets up the thumbnail images to work with imagebox
	*/
	init: function() {

		/**
		* Sets the resizing animation sequence to 'sync'(default) if it is not set to one of the available options
		*/
		if(ib.options.animSequence != 'hw' && ib.options.animSequence != 'wh' && ib.options.animSequence != 'sync') {
			ib.options.animSequence = 'sync';
		}
		
		/**
		* Sets the opening and closing animation easing to 'ease-in-out'(default) if it is not set to one of the available options
		*/
		if(ib.options.easingOpen != 'none' && ib.options.easingOpen != 'ease-in' && ib.options.easingOpen != 'ease-out' && ib.options.easingOpen != 'ease-in-out' && ib.options.easingOpen != 'bounce') {
			ib.options.easingOpen = 'ease-in-out';
		}	
		
		/**
		* Sets the resizing animation easing to 'ease-in-out'(default) if it is not set to one of the available options
		*/
		if(ib.options.easingResize != 'none' && ib.options.easingResize != 'ease-in' && ib.options.easingResize != 'ease-out' && ib.options.easingResize != 'ease-in-out' && ib.options.easingResize != 'bounce') {
			ib.options.easingResize = 'ease-in-out';
		}
		
		/**
		* Sets the opening animation default
		*/
		if(ib.options.animOpen != 'fade' && ib.options.animOpen != 'zoom') {
			ib.options.animOpen = 'zoom';
		}			
		
		/**
		* Converts the animation durations from time(seconds) to steps
		*/	
		ib.options.openDuration = (ib.options.openDuration > 0)? Math.ceil(50*ib.options.openDuration) : 18;
		ib.options.resizeDuration = (ib.options.resizeDuration > 0)? Math.ceil(50*ib.options.resizeDuration) : 18;
		ib.options.fadeDuration = (ib.options.fadeDuration > 0)? Math.ceil(50*ib.options.fadeDuration) : 20;	
		
		// create an array of all the <img> tags in the document and loops through them
		var imgs = document.getElementsByTagName('img'), rel;
		for(var i=0; i<imgs.length; i++) {	
			
			rel = imgs[i].getAttribute('rel');
			// if the 'rel' attribute of the tag matches the imagebox format then..
			if(ib.regex.rel.test(rel) && imgs[i].getAttribute('src')) {
				
				// preload the image from the tag
				ib.preloader[i] = new Image();
				if(imgs[i].getAttribute('href'))
					ib.preloader[i].src = imgs[i].getAttribute('href');
				else
					ib.preloader[i].src = imgs[i].getAttribute('src');
				
				imgs[i].style.cursor = "pointer";
				// open imagebox when the thumbnail is clicked
				imgs[i].onclick = function() { ib.open( this, true ); return false; }
				// set the tags xid (used to retrieve the preloaded image)
				imgs[i].xid = i;
				// set the tags gallery
				if(ib.regex.gallery.test(rel)) imgs[i].gallery = rel.match(ib.regex.gallery)[1];
				// add the <img> tag to the cache
				ib.cache.push(imgs[i]);
				
			}
			
		}
		
	},
	
	/**
	 * Populates the gallery array with images from the cache that are in the current gallery
	 *
	 * @param   HTMLElement          img      The image object that is currently being viewed	 
	 * @return  void
	 * @public
	 */	
	populateGallery: function( img ) {
		var n = 0;
		if(!img.gallery) {
			ib.gallery = [img];
			ib.current = 0;
		} else {
			ib.gallery = [];			
			for(var i=0; i<ib.cache.length; i++) {
				if(ib.cache[i].gallery && ib.cache[i].gallery == img.gallery) {
					ib.gallery.push(ib.cache[i]);
					if(ib.cache[i] == img) ib.current = n;
					n++;
				}
			}
		}
	},
	
	/**
	 * Checks to see if there is an image before or after the current image in the gallery
	 * and displays the 'next' and 'previous' buttons accordingly
	 * 
	 * @return  void
	 * @public
	 */	
	toggleNavigation: function() {
		if(thumbImg.gallery) {
			if(ib.options.continuousGalleries) {
				showNext = true;
				showPrev = true;
			} else {
				showNext = (ib.gallery[ib.current+1])? true : false;
				showPrev = (ib.gallery[ib.current-1])? true : false;
			}
			ib.nextBtn.style.display = (showNext)? 'block' : 'none';
			ib.prevBtn.style.display = (showPrev)? 'block' : 'none';
			ib.nextBtn.style.width = newWidth/3 + 'px';
			ib.prevBtn.style.width = newWidth/3 + 'px';
		} else {
			showNext = false;
			showPrev = false;
			ib.nextBtn.style.display = 'none';
			ib.prevBtn.style.display = 'none';
		}
	},
	
	/**
	 * Jumps to a specified image in the gallery
	 *
	 * @param   Number     index      The position of the image in the gallery	 
	 * @return  void
	 * @public
	 */	
	jumpto: function( index ) {
		if( ib.gallery[index] && !ib.animating() ) {
			ib.current = index;
			ib.open(ib.gallery[ib.current], false);
		}
	},
	
	/**
	 * Moves to the next image in the gallery
	 *	 
	 * @return  void
	 * @public
	 */		
	next: function() {
		if(ib.options.continuousGalleries) {
			if(ib.current == ib.gallery.length-1) {
				ib.jumpto(0);
			} else {
				ib.jumpto(ib.current+1);
			}
		} else {
			if(ib.current < ib.gallery.length) {
				ib.jumpto(ib.current+1);
			}
		}
	},
	
	/**
	 * Moves to the previous image in the gallery
	 *	 
	 * @return  void
	 * @public
	 */		
	previous: function() {
		if(ib.options.continuousGalleries) {
			if(ib.current == 0) {
				ib.jumpto(ib.gallery.length-1);
			} else {
				ib.jumpto(ib.current-1);
			}
		} else {
			if(ib.current > 0) {
				ib.jumpto(ib.current-1);
			}
		}
	},	
	
	/**
	 * Creates a tween between two numbers with a designated number of steps (for the animations) 
	 *
	 * @param   Number          start           the Starting value of the tween
	 * @param   Number          end             the Ending value of the tween
	 * @param   Number          total           the Total number of steps in the tween
	 * @param   Number          current         the Current step of the tween
	 * @param	Number			easing			the type of easing to use. The options are:
	 *											'none', 'ease-in', 'ease-out', 'ease-in-out'
	 * @param   Boolean         rnd      		if true the tween will round the step values	 
	 * @return  Number
	 * @public
	 */
	ease: function( start, end, total, current, easing, rnd ) {
		var delta = end - start,
		value = start;
		
		switch(easing) {
			case 'ease-in':
				value = delta * (current /= total) * current + start;
				break;
				
			case 'ease-out':
				value = -delta * (current /= total) * (current - 2) + start;
				break;
				
			case 'ease-in-out':
				if ((current /= total / 2) < 1) {
					value = delta / 2 * current * current + start;
				} else {
					value = -delta / 2 * ((--current) * (current - 2) - 1) + start;
				}
				break;
				
			case 'bounce':
				if ((current /= total) < (1 / 2.75)) {
					value = delta * (7.5625 * current * current) + start;
				} else if (current < (2 / 2.75)) {
					value = delta * (7.5625 * (current -= (1.5 / 2.75)) * current + .75) + start;
				} else if (current < (2.5 / 2.75)) {
					value = delta * (7.5625 * (current -= (2.25 / 2.75)) * current + .9375) + start;
				} else {
					value = delta * (7.5625 * (current -= (2.625 / 2.75)) * current + .984375) + start;
				}
				break;
				
			default:
				value = delta * current / total + start;
				break;
		}
		
		if(rnd) value = value.toFixed(0);
		
		return value;
	},	
	
	// Creates an animation object to apply different animations to an element
	animation: {
		
		/**
         * fades an element in/out
         *
         * @param   HTMLElement     el      the element to fade
         * @param   String          type    the type of fade e.g. 'in' or 'out'
         * @param   Function        cb      a callback function to call when the animation completes
         * @return  void
         * @public
         */
		fade: function( el, type, cb ) {
			var step = 0,
			from = (type == 'in')? 0 : 1,
			to = (type == 'in')? 1 : 0;
			setOpacity(el, from);
			el.style.visibility = "visible";
			el.fade = setInterval(function() {
				setOpacity(el, ib.ease(from, to, ib.options.fadeDuration, step, 'ease-in-out'));
				step++;
				if(step > ib.options.fadeDuration) {
					clearInterval(el.fade);
					el.fade = null;
					if(type != 'in') el.style.visibility = 'hidden';
					if(cb) cb();
				}					
			},20);
		},
		
		/**
         * smoothly slides an element
         *
         * @param   HTMLElement     el      The element to slide
		 * @param   String          p       The style property to animate e.g. top, right, bottom, or left
         * @param   Number          to      The value to animate to
         * @param   Function        cb      A callback function to call when the animation completes
         * @return  void
         * @public
         */
		slide: function( el, p, to, cb ) {
			if(p != 'top' && p != 'right' && p != 'bottom' && p != 'left') p = 'top';
			var step = 0,
			from = parseFloat(el.style[p]);
			el.slide = setInterval(function() {
				el.style[p] = ib.ease(from, to, 15, step, 'ease-out') + 'px';
				step++;
				if(step > 15) {
					clearInterval(el.slide);
					el.slide = null;
					if(cb) cb();
				}					
			},20);
		},
		
		/**
         * smoothly resizes the imagebox element
         *
         * @param   Number          left        the left position of the imagebox
		 * @param   Number          top         the top position of the imagebox
         * @param   Number          width       the width of the imagebox
         * @param   Number          height      the height of the imagebox
         * @param   String          sequence    the sequence in which to resize the imagebox
         * @param   Number          steps       the number of steps it takes to resize the imagebox
         * @param   String          easing      the type of easing to use in the resizing animation
         * @param   Function        cb          the callback function to run when the animation finishes
         * @return  void
         * @public
         */
		resize: function(left, top, width, height, sequence, steps, easing, cb) {
			
			var step = 0, timer, timerW, timerH;
			
			switch(sequence) {
				case 'hw':
					if(curHeight == height) {
						adjustWidth(cb);
					} else if(curWidth == width) {
						adjustHeight(cb);
					} else {
					adjustHeight(function() {adjustWidth(cb);});
					}
				break;
				case 'wh':
					if(curWidth == width) {
						adjustHeight(cb);
					} else if(curHeight == height) {
						adjustWidth(cb);
					} else {
					adjustWidth(function() {adjustHeight(cb);});
					}
				break;
				default:
					adjustBoth(cb);
				break;
			}
			
			/* resizes the width of the imagebox */
			function adjustWidth(Ncb) {
				ib.box.resize = true;
				timerW = setInterval(function() {
					ib.setDimensions(ib.ease(curLeft, left, steps, step, easing), null, ib.ease(curWidth, width, steps, step, easing), null);
					step++;
					if(step > steps) {
						step = 0;
						clearInterval(timerW);
						ib.box.resize = null;
						if(Ncb) Ncb();
					}					
				},20);				
			}
			
			/* resizes the height of the imagebox */
			function adjustHeight(Ncb) {
				ib.box.resize = true;
				timerH = setInterval(function() {
					ib.setDimensions(null, ib.ease(curTop, top, steps, step, easing), null, ib.ease(curHeight, height, steps, step, easing));
					step++;
					if(step > steps) {
						step = 0;
						clearInterval(timerH);
						ib.box.resize = null;
						if(Ncb) Ncb();
					}					
				},20);
			}
			
			/* resizes the width and height of the imagebox */
			function adjustBoth(Ncb) {
				ib.box.resize = true;
				timer = setInterval(function() {
					ib.setDimensions(ib.ease(curLeft, left, steps, step, easing), ib.ease(curTop, top, steps, step, easing), ib.ease(curWidth, width, steps, step, easing), ib.ease(curHeight, height, steps, step, easing));
					step++;
					if(step > steps) {
						step = 0;
						clearInterval(timer);
						ib.box.resize = null;
						if(Ncb) Ncb();
					}					
				},20);
			}
			
		}

	},
	
	/**
	 * Checks to see if imagebox currently has an animation running
	 * 
	 * @return  void
	 * @public
	 */
	animating: function() {
		var anim = true;
		if(($('imagebox') && !ib.box.resize && !ib.box.fade && !ib.image.fade && !ib.title.slide && !ib.closeBtn.fade) || !$('imagebox')) { anim = false; }
		return anim;
	},
	
	/**
	 * Sets the title to be shown in imagebox, and displays the gallery navigation
	 * 
	 * @param   String     newTitle      the string to set the new title to	 
	 * @return  void
	 * @public
	 */
	setTitle: function( newTitle ) {
		
		hasTitle = (newTitle)? true : false;
		
		if(thumbImg.gallery)
		{
				var margin = (newTitle)? "5px" : "2px", text;
				text = (newTitle)? newTitle : "";
				text += "<div style='font-weight:normal; font-size:11px; margin-top:"+margin+";'>";
				if(ib.options.galleryCounter == 'skip') {
					text += "Gallery: ";
					for(var i=0; i<ib.gallery.length; i++) {
						if(i == ib.current) {
							text += "<span style='margin:0 2px; font-weight:bold;'>" + (i+1) + "</span><wbr>";
						} else {
							text += "<a href='javascript:void(0);' onclick='ib.jumpto(" + i + ")' style='margin:0 2px; color:" + ib.skin.title.color + ";'>" + (i+1) + "</a><wbr>";
						}
					}
				} else {
					text += "Image " + (ib.current+1) + " of " + ib.gallery.length;
				}
				text += "</div>";
				ib.title.innerHTML = text;
				hasTitle = true;
		}
		else if(newTitle) {
			ib.title.innerHTML = newTitle;
		}		
		
	},	
	
	/**
	 * Sets the dimensions of the imagebox. Set value to null to not change it
	 * 
	 * @param   Number     l      the value to set imagebox's left position to
	 * @param   Number     t      the value to set imagebox's top position to
	 * @param   Number     w      the value to set imagebox's width to
	 * @param   Number     h      the value to set imagebox's height to	 
	 * @return  void
	 * @public
	 */
	setDimensions: function( l, t, w, h ) {
		
		if(l) ib.box.style.left = l + 'px';
		if(t) ib.box.style.top = t + 'px';
		if(w) ib.box.style.width = w + 'px';
		if(h) ib.box.style.height = h + 'px';		
		
	},
	
	/**
	 * Creates the HTML elements that make up imagebox and inserts them into the DOM
	 * 
	 * @return  void
	 * @public
	 */
	create: function() {
			
		/**
		* Create the imagebox container
		*/
		ib.box = document.createElement("div");
			ib.box.setAttribute("id", "imagebox");
			ib.box.style.background = ib.skin.background;
			
		/**
		* Create the image to be displayed
		*/
		ib.image = document.createElement("img");
			ib.image.setAttribute("id", "imagebox-image");
			ib.image.setAttribute("class","imagebox");
			
		/**
		* Create the title box
		*/
		ib.title = document.createElement("div");
			ib.title.setAttribute("id", "imagebox-title");
			ib.title.style.background = ib.skin.title.background + " url('images/imagebox-shadow-s.png') repeat-x";
			ib.title.style.color = ib.skin.title.color;
			ib.title.style.fontFamily = ib.skin.title.font;
			ib.title.style.display = 'none';
			
			
		/**
		* Create the imagebox close button
		*/
		ib.closeBtn = document.createElement("div");
			ib.closeBtn.setAttribute("id", "imagebox-close");
			ib.closeBtn.style.background = "url('" + ib.skin.pngs.close + "') no-repeat";
			ib.closeBtn.style.visibility = "hidden";
			ib.closeBtn.onclick = ib.close;

		/**
		* Create the imagebox next button
		*/
		ib.nextBtn = document.createElement("div");
			ib.nextBtn.setAttribute("id", "imagebox-next");
			ib.nextBtn.bgOn = "url('" + ib.skin.pngs.next + "') no-repeat right center";
			ib.nextBtn.bgOff = "url('" + ib.skin.pngs.next + "') no-repeat -500px -500px";
			ib.nextBtn.style.background = ib.nextBtn.bgOff;
			ib.nextBtn.style.display = 'none';
			ib.nextBtn.onclick = ib.next;
			ib.nextBtn.onmouseover = function() { this.style.background = this.bgOn; }
			ib.nextBtn.onmouseout = function() {this.style.background = this.bgOff; }

		/**
		* Create the imagebox previous button
		*/
		ib.prevBtn = document.createElement("div");
			ib.prevBtn.setAttribute("id", "imagebox-prev");
			ib.prevBtn.bgOn = "url('" + ib.skin.pngs.prev + "') no-repeat left center";
			ib.prevBtn.bgOff = "url('" + ib.skin.pngs.prev + "') no-repeat -500px -500px";
			ib.prevBtn.style.background = ib.prevBtn.bgOff;
			ib.prevBtn.style.display = 'none';
			ib.prevBtn.onclick = ib.previous;
			ib.prevBtn.onmouseover = function() {this.style.background = this.bgOn; }
			ib.prevBtn.onmouseout = function() {this.style.background = this.bgOff; }		
			
		/**
		* Insert imagebox into the DOM
		*/
		document.body.appendChild( ib.box );
			ib.box.appendChild( ib.image );
			ib.box.appendChild( ib.title );
			ib.box.appendChild( ib.closeBtn );
			ib.box.appendChild( ib.nextBtn );
			ib.box.appendChild( ib.prevBtn );
			
		/**
		* Creates the imagebox drop-shadow
		*/
		var shdwDirs = new Array("n","e","s","w","nw","ne","se","sw");
		var shdw;
		for(var i=0; i<8; i++) {
			shdw = document.createElement("div");
			shdw.className = "imagebox-shadow imagebox-shadow-"+shdwDirs[i];
			ib.box.appendChild( shdw );
		}
	},
	
	/**
	 * opens or changes the image in imagebox
	 *
	 * @param   HTMLElement     img      		The <img /> element to be enlarged
	 * @param   Boolean         resetGallery    if true the current gallery will be repopulated	 
	 * @return  void
	 * @public
	 */
	open: function( img, resetGallery ) {

		if(!ib.animating()) {
			
			/**
			* Create the page overlay
			*/
			if(!overlay) {
			overlay = document.createElement("div");
			overlay.setAttribute('id', 'imagebox-overlay');
			document.body.appendChild( overlay );
			}
			
			/**
			* Populate the gallery with the images in the same gallery as the current image
			*/
			if(resetGallery) ib.populateGallery(img);		
			
			/**
			* Set tempImg to the image being displayed next
			* and set the new width and height to tempImg's width and height
			*/			
			tempImg = ib.preloader[img.xid];
			newWidth = tempImg.width;
			newHeight = tempImg.height;
			
			/**
			* If the image size is bigger than the page, then set the new size to fit the screen
			* otherwise set the new size to the size of the image
			*/			
			if(newWidth > page.width()-(ib.options.viewportPadding*2) || newHeight > page.height()-(ib.options.viewportPadding*2)) {
				newSize = resizeProportions(newWidth, newHeight, page.width()-(ib.options.viewportPadding*2), page.height()-(ib.options.viewportPadding*2));
				newWidth = newSize[0];
				newHeight = newSize[1];
			}
			
			/**
			* Setup variables to hold the position and size of the thumbnail image
			*/
			var thumbPos = getElementPosition( img );
			thumbWidth = img.width;
			thumbHeight = img.height;
			
			/**
			* Set baseImg to the current thumbnail image
			*/			
			thumbImg = img;			

		}
		
		if(!$('imagebox')) {
			
			/**
			* Create the imagebox
			*/			
			ib.create();
			
			/**
			* Set the imagebox to the size and position of the thumbnail image
			*/				
			ib.setDimensions(thumbPos[0], thumbPos[1], thumbWidth, thumbHeight);
			
			/**
			* Display the chosen image in the imagebox
			*/				
			ib.image.setAttribute("src", tempImg.src);
			
			/**
			* Set the imagebox title to the title attribute of the thumbnail image tag
			*/			
			ib.setTitle(thumbImg.title);
			
			/**
			* Set the width of the title box to the new width of the imagebox minus the title box's horizontal padding
			*/	
			ib.title.style.width = newWidth - 12 + "px";
			
			/**
			* Declare the current and new dimensions of the imagebox for a starting and ending point of the animation
			*/				
			curLeft = parseFloat(ib.box.style.left);
			curTop = parseFloat(ib.box.style.top);
			curWidth = parseFloat(ib.box.style.width);
			curHeight = parseFloat(ib.box.style.height);			
			newLeft = Math.max(Math.ceil((page.width()/2) - (newWidth/2) + page.scrollX()), ib.options.viewportPadding);
			newTop = Math.max(Math.ceil((page.height()/2) - (newHeight/2) + page.scrollY()), ib.options.viewportPadding);
			
			/**
			* Set a function to run after imagebox has openned
			*/			
			afterOpen = function() {
				
				if(isIE) {
					ib.closeBtn.style.visibility = "visible";
				} else {
					ib.animation.fade(ib.closeBtn, 'in');
				}
				ib.title.style.display = 'block';
				ib.title.style.top = newHeight - ib.title.offsetHeight + "px";
				ib.title.style.display = 'none';
				ib.toggleNavigation();
				if(hasTitle) {
					ib.title.style.display = 'block';
					ib.animation.slide(ib.title, 'top', newHeight);
				}
				
			}
			
			/**
			* Open imagebox with the animation set in the options
			*/				
			if(ib.options.animOpen == 'zoom') {
				
				ib.animation.resize(newLeft, newTop, newWidth, newHeight, 'sync', ib.options.openDuration, ib.options.easingOpen, afterOpen);							
				
			} else if(ib.options.animOpen == 'fade') {
				
				ib.setDimensions(newLeft, newTop, newWidth, newHeight);
				ib.animation.fade(ib.box, 'in', afterOpen);
				
			}
			
		} else {
			
			if(!ib.animating()) {
				
				changeImage = function() {

					if(isIE) {
						ib.closeBtn.style.visibility = "hidden";
					} else {
						ib.animation.fade( ib.closeBtn, 'out' );
					}
					ib.animation.fade( ib.image, 'out', function() { ib.change(tempImg.src, img.title); } );
					ib.title.style.display = 'none';					
				}
				
				if(hasTitle) {
					
					ib.animation.slide(ib.title, 'top', parseFloat(ib.title.style.top) - ib.title.offsetHeight, changeImage);
					
				} else {
					
					changeImage();
					
				}
				
				/**
				* Hide the navigation buttons while animating
				*/
				ib.nextBtn.style.display = "none";
				ib.prevBtn.style.display = "none";
				
			}
			
		}
		
	},
	
	/**
	 * Changes the image and resizes the currently open imagebox
	 *
	 * @param   String     newSrc      The url of the new image
	 * @param   String     newTitle    The title of the new image	 
	 * @return  void
	 * @public
	 */
	change: function(newSrc, newTitle) {
		
		if(!ib.animating()) {
		
			/**
			* Set the imagebox title to the title attribute of the thumbnail image tag
			*/			
			ib.setTitle(newTitle);
		
			/**
			* create a function to slide the title out if the image has a title
			*/			
			var titleFunc = null;
			if(hasTitle) {
				titleFunc = function() {
					ib.title.style.display = 'block';
					ib.title.style.top = newHeight - ib.title.offsetHeight + 'px';
					ib.animation.slide(ib.title, 'top', parseFloat(ib.title.style.top)+ib.title.offsetHeight);
				}
			}
			
			/**
			* create a function to run after imagebox has finished resizing
			*/				
			function afterChange() {
				ib.image.setAttribute('src', newSrc);
				ib.title.style.width = newWidth - 12 + 'px';
				if(isIE) {
					ib.closeBtn.style.visibility = "visible";
				} else {
					ib.animation.fade( ib.closeBtn, 'in' );
				}
				ib.animation.fade( ib.image,  'in', function() { titleFunc(); ib.toggleNavigation(); });
			}
			
			/**
			* Declare the current and new dimensions of the imagebox for a starting and ending point of the animation
			*/				
			curLeft = parseFloat(ib.box.style.left);
			curTop = parseFloat(ib.box.style.top);
			curWidth = parseFloat(ib.box.style.width);
			curHeight = parseFloat(ib.box.style.height);
			newLeft = Math.max(Math.ceil((page.width()/2) - (newWidth/2) + page.scrollX()), ib.options.viewportPadding);
			newTop = Math.max(Math.ceil((page.height()/2) - (newHeight/2) + page.scrollY()), ib.options.viewportPadding);
			
			/**
			* Resize imagebox with the animation sequence set in the options
			*/				
			ib.animation.resize(newLeft, newTop, newWidth, newHeight, ib.options.animSequence, ib.options.resizeDuration, ib.options.easingResize, afterChange);
		}
	},
	
	/**
	 * Closes imagebox
	 *	 
	 * @return  void
	 * @public
	 */
	close: function() {
		
		/**
		* create a function that closes the imagebox
		*/			
		function doClose() {
			ib.box.removeChild(ib.title);
			if(ib.options.animOpen == 'zoom') {
				ib.animation.resize(newLeft, newTop, newWidth, newHeight, 'sync', ib.options.openDuration, ib.options.easingOpen, function() {document.body.removeChild( ib.box );});
			} else if(ib.options.animOpen == 'fade') {
				ib.animation.fade(ib.box, 'out', function() {document.body.removeChild( ib.box );});			
			}
		}
			
		if(!ib.animating()) {
		
			ib.box.removeChild(ib.closeBtn);
			ib.box.removeChild(ib.nextBtn);
			ib.box.removeChild(ib.prevBtn);
			var pos = getElementPosition( thumbImg );
			
			/**
			* Declare the current and new dimensions of the imagebox for a starting and ending point of the animation
			*/	
			curLeft = parseFloat(ib.box.style.left);
			curTop = parseFloat(ib.box.style.top);
			curWidth = parseFloat(ib.box.style.width);
			curHeight = parseFloat(ib.box.style.height);			
			newLeft = pos[0];
			newTop = pos[1];
			newWidth = thumbImg.width;
			newHeight = thumbImg.height;
			
			/**
			* Run the function to close the imagebox
			* If the image has a title, then wait for the title to finish animating
			*/
			if(hasTitle)
				ib.animation.slide(ib.title, 'top', parseFloat(ib.title.style.top)-ib.title.offsetHeight, doClose);
			else
				doClose();
			
		}
		
	}
	
	

};
// -----------------------------------------------------------------------------------

// Creates a page object to get info about the browser page
var page = {
	
	// the position of the horizontal scrollbar
	scrollX: function() {
		var pos = 0;
		if(window.pageXOffset) { pos = window.pageXOffset; }
		else if(document.body.scrollLeft) { pos = document.body.scrollLeft; }
		else if(document.documentElement && document.documentElement.scrollLeft){ pos = document.documentElement.scrollLeft; }
		return pos;
	},
	
	// the position of the vertical scrollbar
	scrollY: function() {
		var pos = 0;
		if(window.pageYOffset) { pos = window.pageYOffset; }
		else if(document.body.scrollTop) { pos = document.body.scrollTop; }
		else if(document.documentElement && document.documentElement.scrollTop){ pos = document.documentElement.scrollTop; }
		return pos;
	},
	
	// the width of the viewport
	width: function() {
		overlay.style.display = 'block';
		var width = overlay.offsetWidth;
		overlay.style.display = 'none';
		return width;
	},
	
	// the height of the viewport
	height: function() {
		var height;
		if( typeof( window.innerHeight ) == 'number' ) { height = window.innerHeight; }
		else if(document.documentElement && document.documentElement.clientHeight) { height = document.documentElement.clientHeight; }
		else {
		overlay.style.display = 'block';
		height = overlay.offsetHeight;
		overlay.style.display = 'none';
		}
		return height;
	}

};

/**
 * Resizes a rectangle proportionally to fit inside of another rectangle
 *
 * @param   Number     recWidth          the current width of the rectangle to be resized
 * @param   Number     recHeight         the current height of the rectangle to be resized
 * @param   Number     boundWidth        the width of the bounding rectangle
 * @param   Number     boundHeight       the height of the bounding rectangle
 * @return  Array
 * @public
 */
function resizeProportions( recWidth, recHeight, boundWidth, boundHeight ) {
	var tempWidth = recWidth, tempHeight = recHeight;
	if(recWidth/boundWidth >= recHeight/boundHeight) {
		recWidth = Math.floor(boundWidth);
		recHeight = Math.floor(recHeight * (recWidth / tempWidth));
	} else {
		recHeight = Math.floor(boundHeight);
		recWidth = Math.floor(recWidth * (recHeight / tempHeight));
	}
	return [recWidth, recHeight];
}

/**
 * Gets an HTML element by its ID (short-hand)
 *
 * @param   String     id          the ID of the HTML element 
 * @return  HTMLElement
 * @public
 */
function $( id ) { return document.getElementById( id ); }

/**
 * Sets the opacity of an element to a chosen value
 *
 * @param   HTMLElement   el       the element whose opacity will be set
 * @param   Number        op       the value to set the opacity to (min=0, max=1)
 * @return  void
 * @public
 */
function setOpacity( el, op ) {
	el.style.opacity = op;
	el.style.filter = 'progid:DXImageTransform.Microsoft.Alpha(Opacity=' + op*100 + ')';
}

/**
 * Gets the opacity of an element
 *
 * @param   HTMLElement   el       the element to get the opacity from
 * @return  Number
 * @public
 */
function getOpacity( el ) { 
	var op = 1;
	if(el.style.opacity) { op = el.style.opacity; } 
	else 
	if(el.style.filter) { op = el.style.filter; }
	return parseFloat(op);
}

/**
 * Gets the left and top positions of non-absolutely positioned elements
 *
 * @param   HTMLElement   el       the element to get the position from
 * @return  Array
 * @public
 */
function getElementPosition( el ) {
	var left = 0;
	var top = 0;
	if(el.offsetParent) {
		left = el.offsetLeft;
		top = el.offsetTop;
		while(el = el.offsetParent) {
			left += el.offsetLeft;
			top += el.offsetTop;
		}
	}
	return [left, top];
}

/**
 * Adds a function to the window.onload event
 *
 * @param   Function   func       the function to be added to window.onload
 * @return  void
 * @public
 */
function addLoadEvent( func ) {	
	var oldLoad = window.onload;
	if(typeof window.onload != 'function') {
		window.onload = func;
	} else {
		window.onload = function() {
			oldLoad();
			func();
		}
	}
}

// Initialize imagebox onload
addLoadEvent( ib.init );
