/* Global Domready Event
------------------------------------------------------------------------------------*/
window.addEvent('domready', function(){
	$(document.body).addClass('script');
	
	// provide a modal window on all pages
	modal = new Modal();
	
	
	// event delegation vegas-favorites
	$(document.body).addEvent('click', function(e){
		var event = new Event(e);
		var target = $(event.target);
		if(target.getTag() == 'a' && target.hasClass('vegas-favorites')){
			event.preventDefault();
			if(!isLoggedIn){
				// user is not logged in yet so do it
				new Ajax('/vegas/play/music/includes/login.jsp',{
					method: 'get',
					onComplete: showLogin
				}).request();
			}
			else{
				var form = target.getParent();
				form.set('send', {
					url: form.get('action'), 
					method: 'post',
					onComplete: function(response){
						var element = new Element('a').addClass('vegas-favorites-saved').setProperty('href', '/vegas/my-vegas/index.jsp').setText('Saved to My Vegas Favorites');
						target.replaceWith(element);
					}
				});
				form.send();
				/*
				In Moo 1.2 Element:send no longer takes options as an argument, but rather, takes the url to send the request to. In order to use special options for the internal Request instance, you must use Element:set.
				form.send({
					onComplete: function(response){
						var element = new Element('a').addClass('vegas-favorites-saved').setProperty('href', '/vegas/my-vegas/index.jsp').setText('Saved to My Vegas Favorites');
						target.replaceWith(element);
					}
				});*/
			}
		}
	});
	function showLogin(text){
		// test response for login window
		
		if(text.contains('login-form-window')){
			// strip the html we need from the response
			var responseText = text.split('<body>')[1].split('</body>')[0];
			var responseElement = new Element('div').setHTML(responseText);
			var form = responseElement.getElement('form');
			
			if(form != null){
				form.addEvent('submit', function(e){
					new Event(e).stop();
					
					this.set('send', {
						url: form.get('action'), 
						method: 'post',
						onComplete: showLogin
					});
					this.send();					
				});
			}
			
			modal.show(responseElement.getFirst());
		}
		// if the response does not contain the login form login must have been sucessfull
		else{
			// refresh the page
			window.location.href = window.location.href;
		}
		
	}
	
	// send to a friend
	$$('a.send-to-a-friend').addEvent('click', function(e){
		new Event(e).preventDefault();
		new Ajax(this.getProperty('href'),{
			method: 'get',
			onComplete: updateForm
		}).request();
	});
	function updateForm(text){
		// strip the html we need from the response
		var responseText = text.split('<body>')[1].split('</body>')[0];
		var responseElement = new Element('div').setHTML(responseText);
		var form = responseElement.getElement('form');
		if(form != null){
			form.addEvent('submit', function(e){
				new Event(e).stop();
				
				this.set('send', {
					url: form.get('action'), 
					method: 'post',
					onComplete: updateForm
				});
				this.send();				
			});
		}
		modal.show(responseElement.getFirst());
	}
	
	
	// interactive maps links need to open in a window like they do on the rest of the site
	$(document.body).addEvent('click', function(e){
		var event = new Event(e);
		var target = $(event.target);
		if(target.getTag() == 'a' && target.hasClass('interactive-map')){
			event.preventDefault();
			window.open(target.getProperty('href'),null,'toolbar=0,scrollbars=1,location=0,status=0,menubar=0,resizable=0,width='+screen.availWidth+',height='+screen.availHeight+',left=0,top=0');
		}
	});
});


/* Global Functions
------------------------------------------------------------------------------------*/
function ajaxUpdate(elementId, url){
	var element = $(elementId);
	var elementHeight = element.getSize().y;
	element.empty();
	new Element('div').addClass('ajax-update').setStyle('height', elementHeight).injectInside(element);
	new Ajax(url,{
		method: 'get',
		update: elementId
	}).request();
}

function getParam(url, name){
	name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
	var regexS = "[\\?&]"+name+"=([^&#]*)";
	var regex = new RegExp(regexS);
	var results = regex.exec(url);
	if(results == null){
		return "";
	}
	else{
		return results[1];
	}
}


/* Global Classes
------------------------------------------------------------------------------------*/
var PlayLouder = new Class({
	options:{
		onUpdateComplete: Class.empty,
		url: '/vegas/play/music/includes/play_louder.jsp',
		defaultUrl: '/vegas/play/music/includes/play_louder_default.jsp',
		useDelegation: true,
		showFirst: false,
		duration: 300,
		positionModifier: 128,
		topMargin: 0,
		bottomMargin: 0
	},
	initialize: function(element, boundingElement, options){
		this.setOptions(options);
		
		// store some elements
		this.element = $(element);
		this.boundingElement = $(boundingElement);
		this.mask = this.element.getLast();
		this.content = this.mask.getFirst();
		this.mask.setStyle('height', this.content.getSize().y);
		
		// create required effects for later use
		this.element.fx = new Fx.Style(this.element, 'top', { duration: this.options.duration });
		this.mask.fx = new Fx.Style(this.mask, 'height', { duration: this.options.duration })
		this.content.fx = new Fx.Style(this.content, 'opacity', { duration: this.options.duration });
		
		// store ajax object for updating
		this.ajaxUpdater = new Ajax(this.options.defaultUrl ,{
			method: 'get',
			update: this.content
		});
		
		// attach events
		this.attach();
		
		if(this.options.showFirst)
			this.showFirst();
	},
	attach: function(){
		// sneaky way to catch events
		// attach click event to body and look for clicks from play louder elements
		if(this.options.useDelegation){
			document.body.addEvent('click', function(e){
				var event = new Event(e);
				if($(event.target).hasClass('play-louder') && $(event.target).getTag() == 'a'){
					event.preventDefault();
					if(!this.updateDisabled){
						this.updateDisabled = true;
						var target = $(event.target);
						this.detachFromElement();
						this.attachToElement(target.getParent());
						var id = getParam(target.getProperty('href'), 'playLouder');
						this.update(id);
					}
				}
			}.bind(this));
		}
		
		// after update
		this.ajaxUpdater.addEvent('onSuccess', function(){
			// fire an event after ajax update
			this.fireEvent('onUpdateComplete');
			
			// if attached to another element position next to it
			if($defined(this.attached))
				this.position();
			// fade in new content
			this.content.fx.start(1);
			// resize play louder to fit the new content
			this.mask.fx.start(this.content.getScrollSize().y).chain(function(){
				this.updateDisabled = false;
				// if attached to another element highlight it
				if($defined(this.attached))
					this.attached.addClass('highlight');
			}.bind(this));
		}.bind(this));
		
		// update failure
		this.ajaxUpdater.addEvent('onFailure', function(){
			// insert error message
			this.content.empty();
			this.content.setHTML('<p>There was an error processing your request. Please try again.');
			// fade in new content
			this.content.fx.start(1);
			// resize play louder to fit the new content
			this.mask.fx.start(this.content.getScrollSize().y);
		}.bind(this));
	},
	attachToElement: function(element){
		if($defined(this.attached))
			this.attached.removeClass('highlight');
		this.attached = element;
	},
	detachFromElement: function(){
		if($defined(this.attached))
			this.attached.removeClass('highlight');
		this.attached = null;
	},
	position: function(){
		if($defined(this.attached)){
			// dimensions
			var boundsCoordinates = this.boundingElement.getCoordinates();
			var triggerCoordinates = this.attached.getCoordinates();
			var elementCoordinates = this.element.getCoordinates();
			
			// play louder just got new content and we need to calculate what the new height will be once the content is fully shown
			elementCoordinates.height = (elementCoordinates.height - this.mask.getSize().y) + this.content.getScrollSize().y;
			
			var newTop = (triggerCoordinates.bottom - (triggerCoordinates.height/2)) - (elementCoordinates.height/2) + this.options.topMargin;
			
			// test lower bounds
			if((newTop + elementCoordinates.height) > boundsCoordinates.bottom){
				var temp = (newTop + elementCoordinates.height) - boundsCoordinates.bottom;
				newTop = newTop - temp - this.options.bottomMargin;
			}
			// test upper bounds
			if(newTop < boundsCoordinates.top){
				newTop = boundsCoordinates.top + this.options.topMargin;
			}

			// set position
			this.element.fx.start(newTop - this.options.positionModifier);
		}
	},
	update: function(id){
		// fix the size of play louder before updating content
		this.mask.fx.set(this.content.getSize().y);
		// fade out content
		this.content.fx.start(0).chain(function(){
			// make ajax request
			this.ajaxUpdater.url = this.options.url;
			var data = {eventId: id};
			this.ajaxUpdater.options.data = data;
			this.ajaxUpdater.request();
		}.bind(this));
	},
	defaultState: function(){
		// only restore default state if we are not already there
		if(this.ajaxUpdater.url != this.options.defaultUrl){
			this.detachFromElement();
			
			this.mask.fx.set(this.content.getSize().y);
			this.content.fx.start(0).chain(function(){
				// move to top
				this.element.fx.start(this.options.positionModifier + this.options.topMargin);
				// make ajax request for default content
				this.ajaxUpdater.url = this.options.defaultUrl;
				this.ajaxUpdater.options.date = {};
				this.ajaxUpdater.request();
			}.bind(this));
		}
	},
	showFirst: function(){
		if($$('.play-louder').length > 0){
			var first = $$('.play-louder')[0];
			this.attachToElement(first.getParent());
			var id = getParam(first.getProperty('href'), 'playLouder');
			this.update(id);
		}
	}
});
PlayLouder.implement(new Options, new Events);


var EventDate = new Class({
	options: {
		startOpen: true,
		onToggle: Class.empty,
		onShow: Class.empty,
		onHide: Class.empty
	},
	initialize: function(element, options){
		this.setOptions(options);
		
		this.element = $(element);
		this.events = this.element.getElements('.event');
		
		// show the toggler in the right corner
		this.toggleElement = this.element.getElement('.toggle').setStyle('display', 'inline');
		this.header = this.element.getElement('.header');
		
		// start close if option set
		if(this.options.startOpen)
			this.show();
		else
			this.hide();
				
		// attach events
		this.attach();
	},
	attach: function(){
		// toggle event
		this.header.addEvent('click', function(){
			this.toggle();
		}.bind(this));
	},
	toggle: function(){
		if(this.hidden)
			this.show();
		else
			this.hide();
		this.fireEvent('onToggle', this);
	},
	show: function(){
		this.hidden = false;
		this.toggleElement.removeClass('expand');
		this.update();
		this.fireEvent('onShow', this);
	},
	hide: function(){
		this.hidden = true;
		this.toggleElement.addClass('expand');
		this.update();
		this.fireEvent('onHide', this);
	},
	update: function(){
		if(this.hidden)
			this.events.setStyle('display', 'none');
		else
			this.events.setStyle('display', 'block');
	}
});
EventDate.implement(new Options, new Events);

var EventGroup = new Class({
	options: {
		onUpdateComplete: Class.empty
	},
	initialize: function(element, previous, next, options){
		this.setOptions(options);
		
		// store elements to work with later
		this.container = $(element);
		this.previous = $(previous);
		this.next = $(next);
		
		// store values used for ajax requests for more events
		this.venueId = getParam(window.location.href, 'venueId');
		this.currentPage = 1;
		
		// store ajax object for later use
		this.ajaxUpdate = new Ajax('/vegas/play/music/includes/event-group.jsp',{
			method: 'get'
		});
		
		// get states of the previous and next controls
		this.hasPrevious = false;
		if(this.next.hasClass('hidden'))
			this.hasNext = false;
		else
			this.hasNext = true;
		this.next.removeClass('hidden');
		this.previous.removeClass('hidden');
		
		// set the controls visual appearance
		this.setControls();

		// attach events
		this.attach();
		
		// first update
		this.update();
	},
	attach: function(){
		this.next.addEvent('click', function(e){
			var event = new Event(e);
			event.preventDefault();
			// do nothing if there are no more events to display
			if(this.hasNext){
				this.currentPage++;
				this.get();
			}
		}.bind(this));
		this.previous.addEvent('click', function(e){
			var event = new Event(e);
			event.preventDefault();
			// do nothing if there are no more events to display
			if(this.hasPrevious){
				this.currentPage--;
				this.get();
			}
		}.bind(this));
		
		// after ajax request
		this.ajaxUpdate.addEvent('onSuccess', function(response){
			// store the response in a element so I can work on it.
			var tempElement = new Element('div').setHTML(response);
			
			// grab the previous element from the response to examine it
			// this is how I know if there are more events in line
			var tempControl = tempElement.getFirst();
			if(tempControl.hasClass('hidden'))
				this.hasPrevious = false;
			else
				this.hasPrevious = true;
			
			// grab the next element from the response to examine it
			// this is how I know if there are more events in line
			tempControl = tempElement.getLast();
			if(tempControl.hasClass('hidden'))
				this.hasNext = false;
			else
				this.hasNext = true;
			
			// empty the container that holds the events
			this.container.empty();
			// dump the events from the request in to the dom
			this.container.adopt(tempElement.getChildren()[1].getChildren());
			tempElement = null;
			
			// set the state of the previous and next controls
			this.setControls();
			
			this.fireEvent('onUpdateComplete');
			
			this.update();
		}.bind(this));
	},
	setControls: function(){
		// previous
		if(this.hasPrevious)
			this.previous.setStyles({
				visibility: 'visible',
				cursor: 'pointer'
			});
		else
			this.previous.setStyles({
				visibility: 'hidden',
				cursor: 'default'
			});
		
		// next
		if(this.hasNext)
			this.next.setStyles({
				visibility: 'visible',
				cursor: 'pointer'
			});
		else
			this.next.setStyles({
				visibility: 'hidden',
				cursor: 'default'
			});
	},
	get: function(){
		playLouder.detachFromElement();
		
		// build a data object for ajax to parse in to param string
		var state = {
			id: this.venueId,
			page: this.currentPage
		}
		this.ajaxUpdate.options.data = state;
		this.ajaxUpdate.request();
	},
	update: function(){
		this.dates = new Array();
		var temp = this.container.getChildren();
		for(var i = 0; i < temp.length; i++){
			this.dates[i] = new EventDate(temp[i], {
				startOpen: false,
				onToggle: function(obj){
					// if we are hiding the day
					if(obj.hidden){
						// no play louder is visible so lets reset to default
						playLouder.defaultState();
					}
					else{
						// if the day is open and has a play louder link
						var firstPlay = obj.element.getElement('.play-louder');
						if(firstPlay != null){
							// detach play louder from any previous elements
							playLouder.detachFromElement();
							// attach to the first element in the toggled date
							playLouder.attachToElement(firstPlay.getParent());
							var id = getParam(firstPlay.getProperty('href'), 'playLouder');
							// update play louder
							playLouder.update(id);
						}
						else{
							// if the day is open and has no play louder link reset
							playLouder.defaultState();
						}
					}
					if(obj != this.lastDate){
						this.lastDate.hide();
						this.lastDate = obj;
					}
				}.bind(this)
			});
		}
		this.lastDate = this.dates[0];
		
		// open up the first date
		this.dates[0].toggle();
	}
});
EventGroup.implement(new Options, new Events);


var Gallery = new Class({
	options: {
		slideDuration: 1000,
		thumbnailsPerPage: 4,
		autoPlay: false,
		autoPlayInterval: 9000
	},
	initialize: function(element, options){
		this.setOptions(options);
		
		// grab and store some usefull elements
		this.element = $(element);
		this.mask = this.element.getElement('.gallery-mask');
		this.status = this.element.getElement('.gallery-status');
		this.thumbnailContainer = this.element.getElement('.gallery-thumbnails');
		this.thumbnails = this.thumbnailContainer.getElements('img');
		this.images = this.thumbnailContainer.getElements('a').getProperty('href');
		
		// grab and store gallery control elements
		this.controls = {
			container: this.element.getElement('.gallery-controls'),
			previous: this.element.getElement('.gallery-previous'),
			next: this.element.getElement('.gallery-next'),
			viewAll: this.element.getElement('.gallery-all')
		}
		// hide controls if all images fit on screen at one time
		if(this.images.length <= this.options.thumbnailsPerPage)
			this.controls.container.setStyle('display', 'none');
		
		// set up scroller that will cycle thumbnail pages
		this.mask.scroll = new Fx.Scroll(this.mask, {
			duration: this.options.slideDuration,
			wait: false
		})
		
		// create and store modal window for image zoom and view all
		try{
			// try to use the global modal
			this.modalWindow = modal
		}catch(e){
			// if global is not around make one
			this.modalWindow = new Modal();
		}
		
		// set starting state
		this.pageCount = Math.floor(this.images.length / this.options.thumbnailsPerPage);
		this.currentPage = 0;
		
		// attach events
		this.attach();
				
		// do the first update
		this.walk();
	},
	attach: function(){
		// set property that can be used to disable user interaction
		this.clickDisabled = false;
		
		
		// event delegation for thumbnail images
		this.thumbnailContainer.addEvent('click', function(e){
			var event = new Event(e);
			var target = $(event.target);
			event.preventDefault();
			if(target.getTag() == 'img'){
				if(!this.clickDisabled)
					this.showImage(this.thumbnails.indexOf(target), false);
			}
			else if(target.getTag() == 'a'){
				if(!this.clickDisabled)
					this.showImage(this.thumbnails.indexOf(target.getFirst()), false);
			}
		}.bind(this));
		
		
		// attach events to controls
		this.controls.next.addEvent('click', function(e){
			new Event(e).preventDefault();
			if(!this.clickDisabled)
				this.nextPage();
		}.bind(this));
		this.controls.previous.addEvent('click', function(e){
			new Event(e).preventDefault();
			if(!this.clickDisabled)
				this.previousPage();
		}.bind(this));
		this.controls.viewAll.addEvent('click', function(e){
			new Event(e).preventDefault();
			if(!this.clickDisabled)
				this.showAll();
		}.bind(this));
	},
	nextPage: function(){
		if(this.currentPage < this.pageCount){
			this.currentPage++;
			this.walk();
		}
	},
	previousPage: function(){
		if(this.currentPage != 0){
			this.currentPage--;
			this.walk();
		}
	},
	updateStatus: function(){
		this.status.setText('Page ' + (this.currentPage + 1) + ' of ' + (this.pageCount + 1));
	},
	walk: function(){
		var element = this.thumbnails[this.currentPage * this.options.thumbnailsPerPage].getParent();
		this.mask.scroll.toElement(element);
		this.updateStatus();
	},
	showImage: function(index, toShowAll){
		// disable the users ability to start another action while in the process of loading a imge
		this.clickDisabled = true;
		
		var thumbnail = this.thumbnails[index];
				
		// show loading indicator
		var thumbnailOpacity = new Fx.Style(thumbnail, 'opacity', {
			duration: 200,
			wait: false
		});
		thumbnailOpacity.start(0);
		thumbnail.getParent().addClass('loading');
		
		// begin pre loading
		new Asset.images([this.images[index]],{
			onComplete: function(){
				// hide loading indicator
				thumbnailOpacity.start(1).chain(function(){
					thumbnail.getParent().removeClass('loading');
				}.bind(this));
				// show the image
				this.modalWindow.show(this.buildSingleImage(index, toShowAll));
				// allow the user to click again
				this.clickDisabled = false;
			}.bind(this)
		});
	},
	showAll: function(){
		this.modalWindow.show(this.buildViewAll());		
	},
	buildSingleImage: function(index, toShowAll){
		// collect the data we want to display
		var thumbnail = this.thumbnails[index];
		var title = thumbnail.getProperty('title');
		var caption = thumbnail.getProperty('alt');
		var imageSrc = this.images[index];
		
		// view will be dumped in to this container
		var container = new Element('div').setProperty('id', 'gallery-single');
	
		// create main view elements
		var left = new Element('div').addClass('left').injectInside(container);
		var center = new Element('div').addClass('center').injectInside(container);
		var right = new Element('div').addClass('right').injectInside(container);
		
		// add click events to arrows
		left.addEvent('click', function(e){
			this.showImage(index - 1, toShowAll);
		}.bind(this));
		right.addEvent('click', function(e){
			this.showImage(index + 1, toShowAll);
		}.bind(this));
		
		// image and information - center column
		new Element('div').addClass('image-index').setHTML(index + 1 + ' of ' + this.thumbnails.length).injectInside(center);
		if(title != null)
			new Element('h2').setText(title).injectInside(center);
		new Element('img').setProperty('src', imageSrc).injectInside(center);
		if(caption != null)
			new Element('div').addClass('image-caption').setText(caption).injectInside(center);
			
		//hide/show required arrows
		if(index == 0){
			if(this.images.length > 1){
				right.setStyle('visibility', 'visible');
				left.setStyle('visibility', 'hidden');
			}
			else{
				right.setStyle('visibility', 'hidden');
				left.setStyle('visibility', 'hidden');
			}
		}
		else if(index == this.images.length - 1){
			right.setStyle('visibility', 'hidden');
			left.setStyle('visibility', 'visible');
		}
		else{
			right.setStyle('visibility', 'visible');
			left.setStyle('visibility', 'visible');
		}
		
		// to properly calculate heights the element needs to be inserted in the dom
		// clone and insert in page -> calculate arrow center -> remove temporary element
		var temporaryElement = container.cloneNode(true);
		temporaryElement.setStyle('visibility', 'hidden');
		temporaryElement.injectInside(document.body);
		centerY = temporaryElement.getElement('.center').getScrollSize().y;
		rightY = temporaryElement.getElement('.right').getScrollSize().y;
		var arrowTop = (centerY/2) - (rightY/2);
		temporaryElement.remove();
		
		// set arrows to proper height
		right.setStyle('margin-top', arrowTop);
		left.setStyle('margin-top', arrowTop);
		
		return container;
	},
	buildViewAll: function(){
		// create container
		var container = new Element('div').addClass('gallery-view-all');
		
		// inject a image count in to the view
		new Element('div').addClass('count').setText(this.images.length + ' photos').injectInside(container);
		
		// clone thumbnails from main gallery view	
		var copy = this.thumbnailContainer.cloneNode(true);
		copy.injectInside(container);
		
		// attach event event to thumbnails
		copy.addEvent('click', function(e){
			var event = new Event(e);
			var target = $(event.target);
			event.preventDefault();
			if(target.getTag() == 'img'){
				if(!this.clickDisabled){
					var index = this.images.indexOf(target.getParent().getProperty('href'));
					this.showImage(index, true);
				}
			}
			else if(target.getTag() == 'a'){
				if(!this.clickDisabled){
					var index = this.images.indexOf(target.getProperty('href'));
					this.showImage(target, true);
				}
			}
		}.bind(this));	
		return container;
	}
});
Gallery.implement(new Options, new Events);


var Modal = new Class({
	options: {
		overlayId: 'modal-overlay',
		modalId: 'modal-window',
		hasClose: true,
		closeClass: 'close',
		closeHtml: '',
		clickToClose: true,
		transitionDuration: 250,
		onShowComplete: Class.empty,
		onHideComplete: Class.empty,
		onUpdateComplate: Class.empty
	},
	initialize: function(options){
		this.setOptions(options);
		
		// create overlay element
		this.overlay = new Element('div').setProperty('id', this.options.overlayId).injectInside(document.body);
		if(this.options.clickToClose){
			// add a pointer on the overlay if click to close is set
			this.overlay.setStyle('cursor', 'pointer');
		}
		
		// create modal window
		this.modal = new Element('div').setProperty('id', this.options.modalId).injectAfter(this.overlay);
		if(this.options.hasClose){
			// create close button if option set
			this.modalClose = new Element('div').addClass(this.options.closeClass).setHTML(this.options.closeHtml).injectInside(this.modal);
		}
		this.modalContent = new Element('div').addClass('content').injectInside(this.modal);
		
		// create effects objects
		this.modal.fx = new Fx.Style(this.modal, 'opacity', {duration: this.options.transitionDuration});
		this.overlay.fx = new Fx.Style(this.overlay, 'opacity', {duration: this.options.transitionDuration});
		this.overlay.fx.set(0);
		
		this.isOpen = false;
		
		this.attach();
	},
	attach: function(){
		var self = this;
		
		if(this.options.clickToClose){
			this.overlay.addEvent('click', function(){
				self.hide();
			});
		}
		
		window.addEvent('resize', function(){
			if(self.isOpen){
				self.positionOverlay();
				self.positionModal();
			}
		});
		
		// attach event if option set for close
		if(this.options.hasClose){
			this.modalClose.addEvent('click', function(){
				self.hide();
			});
		}
	},
	show: function(content){
		// modal is not open yet
		if(!this.isOpen){
			this.isOpen = true;
			
			this.modal.fx.set(0);
			this.modal.setStyle('display', 'block');
			// inject content in to modal window
			content.injectInside(this.modalContent);
			
			// fix for ie6 select boxes showing through and safari 2 flash showing through
			if(window.ie6)
				$$('select').setStyle('visibility', 'hidden');
			else if(window.webkit419)
				$$('embed').setStyle('visibility', 'hidden');
			
			// show/position and fade in overlay
			this.overlay.setStyle('display', 'block');
			this.positionOverlay();
			this.overlay.fx.start(0.6).chain(function(){
				this.positionModal();
				this.modal.fx.start(1).chain(function(){
					this.fireEvent('onShowComplete');
				}.bind(this));
			}.bind(this));
		}
		// modal is already open so we just need to update and reposition
		else{
			this.modal.fx.start(0).chain(function(){
				this.modalContent.empty();
				content.injectInside(this.modalContent);
				this.positionModal();
				this.modal.fx.start(1).chain(function(){
					this.fireEvent('onShowComplete');
					this.fireEvent('onUpdateComplete');
				}.bind(this));
			}.bind(this));
		}
	},
	hide: function(){
		if(this.isOpen){
			this.isOpen = false;
			this.modal.fx.start(0).chain(function(){
				this.modal.setStyles('display', 'none');
				this.overlay.fx.start(0).chain(function(){
					// fix for ie6 select boxes showing through and safari 2 flash showing through
					if(window.ie6)
						$$('select').setStyle('visibility', 'visible');
					else if(window.webkit419)
						$$('embed').setStyle('visibility', 'visible');
					this.overlay.setStyle('display', 'none');
					this.modalContent.empty();
					this.fireEvent('onHideComplete');
				}.bind(this));
			}.bind(this));
		}
	},
	positionOverlay: function(){
		this.overlay.setStyles({
			'height': window.getScrollHeight(),
			'width': window.getWidth()
		});
	},
	positionModal: function(){
		var modalSize = this.modal.getSize();
		var left = (window.getScrollLeft() + (window.getWidth() - modalSize.x)/2);
		var top = (window.getScrollTop() + (window.getHeight() - modalSize.y)/2);
		this.modal.setStyles({
			'top': top,
			'left': left
		});
	}
});
Modal.implement(new Options, new Events);

/*
 * This is a patch for Mootools Request.HTML
 *
 * If you use Request.HTML to load some data containing any HTML special chars
 * like &nbsp; or &laquo; it won't work with Webkit based browsers.
 */
Request.HTML.implement({
	processHTML: function(text){
		var match = text.match(/<body[^>]*>([\s\S]*?)<\/body>/i);
		text = (match) ? match[1] : text;
		
		var container = new Element('div');
		
		return $try(function(){
			var root = '<root>' + text + '</root>', doc;
			if(Browser.Engine.trident){
				doc = new ActiveXObject('Microsoft.XMLDOM');
				doc.async = false;
				doc.loadXML(root);
			}else{
				doc = new DOMParser().parseFromString(root, 'text/html');
			}
			root = doc.getElementsByTagName('root')[0];
			for(var i = 0, k = root.childNodes.length; i < k; i++){
				var child = Element.clone(root.childNodes[i], true, true);
				if(child) container.grab(child);
			}
			return container;
		}) || container.set('html', text);
	}
});