function jbox(opts) {
	var _this = this;
	
	// set up our jbox
	this.init = function() {
		this.options = null;
		var defaults = {
			overlayColor: 'black',
			overlayOpacity: .7,
			overlayFade: 300,
			contentfadeIn: 200,
			contentfadeOut: 400,
			overlayContentTop: '150px',
			closeButtonPath: 'images/close.png',
			closeButtonPathIE: false,
			closeElementClass: 'closebox'
		};
		
		this.options = jQuery.extend(defaults, opts);
		
		this.overlay = null;
		this.overlayLiner = null;
		this.overlayContentHolder = null;
		
		this.currentContent = false;
		
		this.visible = false;
		this.mouseIn = false;
		this.nextPrevButtons = false;
		
		this.fadeSpeed = 300;
		this.jboxGroups = Array();
		
		if (jQuery('a[rel^=jbox]').length) {
			this.buildOverlay();
			this.addJboxHandler();
			this.addWindowHandler();
		}
	}
	
	
	
	// build overlay
	this.buildOverlay = function() {
		jQuery('body').append(
			jQuery('<div></div>').attr({ 'id': 'Overlay'}).css({ display: 'none', position: 'absolute', top: '0', left: '0', 'z-index': '25', opacity: this.options.overlayOpacity, "background-color": this.options.overlayColor, padding: '0', margin: '0' }),
			jQuery('<div></div>').attr({ 'id': 'OverlayLiner' }).css({ display: 'none', position: 'absolute', top: '0', left: '0', 'z-index': '26', width: this.overlayWidth, height: this.overlayHeight, padding: '0', margin: '0' }).append(
				jQuery('<div></div>').attr({ 'id': 'OverlayContentHolder' }).css({ position: 'relative', 'z-index': '50' })
			)
		);
		this.overlay = jQuery('#Overlay');
		this.overlayLiner = jQuery('#OverlayLiner');
		this.overlayContentHolder = jQuery('#OverlayContentHolder');
	}
	
	
	
	// display the overlay
	this.showOverlay = function() {
		this.resizeOverlay();
		this.updateModalLocation();
		var that = this;
		if (!this.visible) {
			this.overlay.fadeIn(this.options.overlayFade);
			this.overlayLiner.fadeIn(this.options.overlayFade, function() {
				that.resizeOverlay(); // resize again just incase the gallery popup was bigger than the window size and created scroll bars (overlay would be cut off below the fold)
			});
			this.visible = true;
		}
	}
	
	
	
	// hide the overlay
	this.hideOverlay = function() {
		if (this.visible) {
			this.overlay.fadeOut(this.options.overlayFade);
			this.overlayLiner.fadeOut(this.options.overlayFade);
			this.visible = false;
		}
	}
	
	
	
	// load content into the overlay (ajax request if doesn't exist, just display if already loaded)
	this.loadContent = function(href, group) {
		httpRegExp = /^(http:)/;
		jpgRegExp = /(.jpg)$/;
		gifRegExp  = /(.gif)$/;
		pngRegExp = /(.png)$/;
		
		// weird ie bug where the whole address comes from the href (rather than the actual contents of the href attribute) so we need to substr it away
		if (httpRegExp.test(href)) {
			href = this.fixIE(href);
		}

		// generate the id to attach to the div that this content will be loaded into
		var loadID = this.generateLoadID(href);

		if (jpgRegExp.test(href) || gifRegExp.test(href) || pngRegExp.test(href)) {
			// load image
			this.loadImage(loadID, href);
		} else {
			// load html
			this.loadHTML(loadID, href);
		}
		
		// check to see if this content is part of a group
		this.checkForGroup(group);
	}
	
	
	
	this.fixIE = function(href) {
		pos = href.indexOf("/");
		var count = 0;
		while (pos != -1 && count < 3) {
			count++;
			href = href.substr(pos + 1);
			pos = href.indexOf("/");
		}
		return href;
	}
	
	
	
	this.generateLoadID = function(href) {
		var loadID = href;
		
		pos = loadID.indexOf("/");
		
		while (pos != -1) {
			loadID = loadID.substr(0, pos) + loadID.substr(pos + 1);
			pos = loadID.indexOf("/");
		}
		
		pos = loadID.indexOf('.');
		if (pos != -1) {
			loadID = loadID.substr(0, pos) + loadID.substr(pos + 1);
		}
		
		// handle get variables in the href
		var quesPos = loadID.indexOf("?");
		var eqPos   = -1;
		if (quesPos != -1) {
			loadID = loadID.substr(0, quesPos) + loadID.substr(quesPos + 1);
			eqPos  = loadID.indexOf("=");
			loadID = loadID.substr(0, eqPos) + loadID.substr(eqPos + 1);
			var ampPos = loadID.indexOf("&");
			while (ampPos != -1) {
				loadID = loadID.substr(0, ampPos) + loadID.substr(ampPos + 1);
				eqPos  = loadID.indexOf("=");
				loadID = loadID.substr(0, eqPos) + loadID.substr(eqPos + 1);
				ampPos = loadID.indexOf("&");
			}
		}
		
		return loadID;
	}
	
	
	
	// load an image into overlay
	this.loadImage = function(loadID, href) {
		if (jQuery('#' + loadID).length) {
			if (this.currentContent != loadID) {
				jQuery('#' + this.currentContent).fadeOut(this.options.contentFadeOut);
				this.currentContent = loadID;
				jQuery('#' + this.currentContent).fadeIn(this.options.contentFadeIn);
			}
		} else {
			var img = new Image();
			jQuery(img).load(function() {
				jQuery('#OverlayContentHolder').prepend(
					// create div with loadID to hold new content
					jQuery('<div></div>').attr({ 'id': loadID, className: 'overlayContent imageContent' }).css({ display: 'none', position: 'absolute', top: _this.options.overlayContentTop }).append(
						this,
						_this.addCloseButton()
					)
				);
				if (_this.currentContent) {
					jQuery('#' + _this.currentContent).fadeOut(_this.options.contentFadeOut);
				}
				_this.currentContent = loadID;
				jQuery('<img/>').load(function() {
					jQuery('#' + _this.currentContent).fadeIn(_this.options.contentFadeIn);
				}).attr('src', href);
				_this.centreContent();
				_this.addCloseHandler();
			}).attr('src', href);
			if (img[0].complete) {
				img.trigger('load');
			}
		}
	}
	
	
	
	this.loadHTML = function(loadID, href) {
		// check to see if content has already been loaded
		if (jQuery('#' + loadID).length) {
			// check to see if this content is already the current content
			if (this.currentContent != loadID) {
				// show loading animation
				jQuery('#loader').css({ display: 'block' });
				// hide current content
				jQuery('#' + this.currentContent).fadeOut(this.options.contentFadeOut);
				// show new content
				jQuery('#' + loadID).fadeIn(this.options.contentFadeIn);
				// hide loading animation
				jQuery('#loader').css({ display: 'none' });
				// store new current content id
				this.currentContent = loadID;
			}
			this.centreContent();
			jQuery('#' + loadID).css({ display: 'block' });
		} else {
			// load content using ajax
			jQuery.ajax({ cache: false, url: href, complete: function(XMLHttpRequest, textStatus) {
				jQuery('#loader').css({ display: 'none' });
				jQuery('#OverlayContentHolder').prepend(
					// create div with loadID to hold new content
					jQuery('<div></div>').attr({ 'id': loadID, className: 'overlayContent htmlContent' }).css({ display: 'none', position: 'absolute', top: _this.options.overlayContentTop }).append(XMLHttpRequest.responseText).fadeIn(_this.options.contentFadeIn)
				);
				if (_this.currentContent) {
					jQuery('#' + _this.currentContent).fadeOut(_this.options.contentFadeOut);
				}
				_this.currentContent = loadID;
				_this.centreContent();
				_this.addJboxHandler();
				_this.addCloseHandler();
			}});
		}
	}
	
	
	
	this.centreContent = function() {
		var contentWidth = jQuery('#' + this.currentContent).outerWidth();
		var offset = (this.overlayWidth / 2) - (contentWidth / 2);
		jQuery('#' + this.currentContent).css({ 'left': offset });
	}
	
	
	
	// check to see if this content is part of a group
	this.checkForGroup = function(group) {
		if (group && _this.jboxGroups[group.group].length > 1) {
			// create buttons when needed
			if (!this.nextPrevButtons) {
				this.addNextPrevButtons();
			}
			
			// display next/prev buttons and add new click events
			jQuery('#next').css({ display: 'block' });
			jQuery('#prev').css({ display: 'block' });
			// unbind any current click events
			jQuery('#next').unbind('click');
			jQuery('#prev').unbind('click');
			// bind new click events
			jQuery('#next').bind('click', function() {
				var next = group.current+1;
				if (next > _this.jboxGroups[group.group].length-1) {
					next = 0;
				}
				_this.loadContent(_this.jboxGroups[group.group][next], { group: group.group, current: next });
			});
			jQuery('#prev').bind('click', function() {
				var prev = group.current-1;
				if (prev < 0) {
					prev = _this.jboxGroups[group.group].length-1;
				}
				_this.loadContent(_this.jboxGroups[group.group][prev], { group: group.group, current: prev });
			});
		} else {
			// make sure buttons are hidden if they have been created
			if (this.nextPrevButtons) {
				jQuery('#next').css({ 'display': 'none' });
				jQuery('#prev').css({ 'display': 'none' });
			}
		}
	}
	
	
	
	this.addNextPrevButtons = function() {
		this.nextPrevButtons = true;
		this.overlayContentHolder.append(
			jQuery('<div></div>').attr('id', 'next'),
			jQuery('<div></div>').attr('id', 'prev')
		);
	}
	
	
	
	this.addCloseButton = function() {
		var closeButton = null;
		if (this.options.closeButtonPathIE && jQuery.browser.msie && jQuery.browser.version == "6.0") {
			closeButton = "<img src='" + this.options.closeButtonPathIE + "' />";
		} else {	
			closeButton = "<img src='" + this.options.closeButtonPath + "' />";
		}
		return jQuery('<div></div>').addClass(this.options.closeElementClass).css({ position: 'absolute', top: '13px', right: '13px', cursor: 'pointer', 'z-index': '100'}).append(
			closeButton
		);
	}
	
	
	
	// add click events to all anchor tags with rel = 'jbox'
	this.addJboxHandler = function() {
		jQuery("a[rel^='jbox']").each(function(i) {
			var rel = jQuery(this).attr('rel');
			var href = jQuery(this).attr('href');
			var position = href.indexOf("/");
	
			// check for groups (if length of rel is > 4 it means we have 'jbox[...]' which means it is in a group
			if (rel.length > 4) {
				// get the group name (the string between the [ ])
				rel = rel.substr(5, (rel.length-6));
				// create a new entry in the group array if it isn't already there
				if (!_this.jboxGroups[rel]) {
					_this.jboxGroups[rel] = Array();
				}
				if (jQuery.inArray(href, _this.jboxGroups[rel]) == -1) {
					// add this link to the group
					_this.jboxGroups[rel].push(href);
				}
				// store group and current info for loadContent() call
				rel = { group: rel, current: jQuery.inArray(href, _this.jboxGroups[rel]) };
			} else {
				// not part of a group so set rel to false
				rel = false;
			}
			// add click event
			jQuery(this).unbind("click");
			jQuery(this).bind("click", function(event) {
				event.preventDefault();
				_this.showOverlay();
				_this.loadContent(href, rel);
				return false;
			});
		});
	}
	
	
	
	// add events to handle closing the overlay
	this.addCloseHandler = function() {
		this.overlayContentHolder.unbind('mouseenter');
		this.overlayContentHolder.unbind('mouseleave');
		this.overlayContentHolder.bind('mouseenter', function() {
			_this.mouseIn = true;
		}).bind('mouseleave', function() {
			_this.mouseIn = false;
		});
		jQuery('div.' + this.options.closeElementClass).unbind('click');
		jQuery('div.' + this.options.closeElementClass).bind('click', function(event) {
			event.preventDefault();
			_this.hideOverlay();
		});
		this.overlayLiner.unbind('click');
		this.overlayLiner.bind('click', function() {
			if (!_this.mouseIn) {
				_this.hideOverlay();
			}
		});
	}
	
	
	
	// add a resize event to the window so the overlay stays the right size
	this.addWindowHandler = function() {
		jQuery(window).resize(function() {
			_this.resizeOverlay(true);
		});
	}
	
	
	
	// keep the modal window in the viewport
	this.updateModalLocation = function() {
		this.overlayContentHolder.css({ top: jQuery(window).scrollTop() + 'px' });
	}
	
	
	
	// get the current width and height of the window/document and apply this to the overlay and liner
	this.resizeOverlay = function(windowChange) {
		var newWidth = this.getViewWidth(windowChange);
		var newHeight = this.getViewHeight();
	
		// check that the new height and width is different to the current
		if (this.overlayWidth != newWidth || this.overlayHeight != newHeight) {
			// set the new heights and widths
			
			// take into account the scrollbar in IE 6
			if (jQuery.browser.msie && jQuery.browser.version == "6.0") {
				newWidth = newWidth - 21;
			}
			
			this.overlay.css({ width: newWidth, height: newHeight });
			this.overlayLiner.css({ width: newWidth, height: newHeight });
			
			// store the new height and widths
			this.overlayWidth = newWidth;
			this.overlayHeight = newHeight;
			
			this.centreContent();
		}
	}
	
	
	
	// get width of the view
	this.getViewWidth = function(windowChange) {
		if (windowChange) {
			return jQuery(window).width();
		}
		return jQuery(document).width();
	}
	
	
	
	// get height of the view
	this.getViewHeight = function() {
		return jQuery(document).height();
	}
	
	
	
	this.init();
}

var box = null;

jQuery(window).load(function() {
	box = new jbox({
		overlayColor: 'white',
		overlayContentTop: '50px',
		closeButtonPath: 'mysite/themes/gib/images/overlay-nav-close.png',
		closeButtonPathIE: 'mysite/themes/gib/images/overlay-nav-close.gif'
	});
});