var Modal = new Class({
	Implements: [Events, Options],
	options: {
		overlayId: 'modal-overlay',
		modalId: 'modal-window',
		hasClose: true,
		closeClass: 'close',
		closeHtml: '',
		clickToClose: true,
		transitionDuration: 250,
		onShowComplete: $empty,
		onHideComplete: $empty,
		onUpdateComplate: $empty
	},
	initialize: function(options){
		this.setOptions(options);
		
		// create overlay element
		this.overlay = new Element('div').setProperty('id', this.options.overlayId).inject(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).set('html', this.options.closeHtml).inject(this.modal);
		}
		this.modalContent = new Element('div').addClass('content').inject(this.modal);
		
		// trash to dump old content to
		this.contentTrash = new Element('div', {'id':'modal-trash'}).setStyle('display', 'none').injectAfter(this.modal);
		
		// create effects objects
		this.modal.fx = new Fx.Tween(this.modal, {
			property: 'opacity',
			duration: this.options.transitionDuration
		});
		this.overlay.fx = new Fx.Tween(this.overlay, {
			property: '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.positionModal();
				self.positionOverlay();
			}
		});
		
		// 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.inject(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.8).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.getChildren().inject(this.contentTrash);
				content.inject(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',
					'top': '-9999px',
					'left': '-9999px'
				});
				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.getChildren().inject(this.contentTrash);
					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);
		if(top < 0)
			top = 0;
		this.modal.setStyles({
			'top': top,
			'left': left
		});
	}
});


var Paginator = new Class({
	Implements: [Events, Options],
	options: {
		slideTransitionDuration: 400,
		navOpenClass: '.paginator .nav-expanded',
		navCloseClass: '.paginator .nav-hidden',
		navTransitionDuration: 150,
		pageWidth: 832
	},
	initialize: function(container, options){
		this.setOptions(options);
		
		// collect the containing element
		this.container = $(container);
		
		// collect pages and their wrapper
		this.scroller = this.container.getElement('.scroller');
		this.pageContainer = this.container.getElement('.page-container');
		this.pages = this.container.getElements('.page');
		
		// set up effect for paginating
		this.fx = this.pageContainer.get('tween', {
			property: 'margin-left',
			link: 'chain',
			duration: this.options.slideTransitionDuration
		});		
				
		// starting index is 0
		this.currentIndex = 0;
		
		this.attach();
	},
	attach: function(){
		var self = this;
		
		// event delegation
		this.container.addEvent('click', function(event){
			var target = $(event.target);

			if(target.hasClass('next') || target.getParent().hasClass('next')){
				event.preventDefault();
				self.next();
			}
			else if(target.hasClass('previous') || target.getParent().hasClass('previous')){
				event.preventDefault();
				self.previous();
			}
			else if(target.hasClass('skip-to')){
				event.preventDefault();
				// pulls the index from the rel attribute
				self.toIndex(target.get('rel'));
			}
		});
	},
	next: function(instantUpdate){
		if(this.currentIndex < this.pages.length - 1)
			this.currentIndex++;
		this.update(instantUpdate);
	},
	previous: function(instantUpdate){
		if(this.currentIndex > 0)
			this.currentIndex--;
		this.update(instantUpdate);
	},
	toIndex: function(index, instantUpdate){
		this.currentIndex = index;
		this.update(instantUpdate);
	},
	update: function(instantUpdate){
		if(!instantUpdate){
			this.fx.start(-this.options.pageWidth * this.currentIndex);
		}
		else{
			this.fx.set(-this.options.pageWidth * this.currentIndex);
		}
	}
});


var Pins = new Class({
	Implements: [Events, Options],
	options: {
	},
	initialize: function(pinSelector, options){
		this.setOptions(options);
		
		this.pins = $$(pinSelector);

		this.attach();
	},
	attach: function(){
		this.pins.addEvents({
			'mouseenter': this.showPin,
			'mouseleave': this.hidePin
		});
	},
	showPin: function(){
		// change pin to expanded state
		this.addClass('pin-expand');
		this.removeClass('pin');
		
		// fade in content
		var content = this.getElement('.content');
		// ie can't do opacity transitions on bolded text properly
		if(Browser.Engine.trident && Browser.Engine.version <= 5){
			content.setStyle('display', 'block');
		}
		else{
			content.setStyles({
				'opacity': 0,
				'display': 'block'
			});
			content.get('tween', {
				duration: 250,
				property: 'opacity'
			}).start(1);
		}
	},
	hidePin: function(){
		// remove expanded state from pin
		this.removeClass('pin-expand');
		this.addClass('pin');
		
		// hide content
		this.getElement('.content').setStyle('display', 'none');
	}
});


var ScrollBar = new Class({
	Implements: [Events, Options],
	options: {
		trackDimension: 18,
		wheel: 16,
		steps: 100
	},
	initialize: function(wrapper, options){
		this.setOptions(options);
		
		this.wrapper = $(wrapper).setStyle('position', 'relative');
		var wrapperSize = this.wrapper.getSize();
		
		this.contents = new Element('div').addClass('contents').setStyles({
			'width': wrapperSize.x - this.options.trackDimension,
			'height': wrapperSize.y
		});
		this.contents.adopt(this.wrapper.getChildren());
		this.contents.inject(this.wrapper);
		this.wrapper.removeClass('scroll-wrapper');
		
		this.vTrack = new Element('div').addClass('vTrack').setStyle('height', wrapperSize.y);
		this.vThumb = new Element('div').addClass('vThumb').inject(this.vTrack);
		this.vTrack.injectInside(this.wrapper);
		
		this.slider = new Slider(this.vTrack, this.vThumb,{
			mode: 'vertical',
			steps: this.options.steps
		});
		
		this.update();
		this.attach();
	},
	attach: function(){
		// onchange for when the thumb is dragged
		this.slider.addEvent('onChange', function(step){
			this.contents.scrollTop = this.scrollRatio * (step / this.options.steps);
		}.bind(this));
		
		// mousewheel event for scrolling
		this.contents.addEvent('mousewheel', function(e){
			this.contents.scrollTop -= e.wheel * this.options.wheel;
			this.vUpdateThumbContentScroll();
			e.stop();
		}.bind(this));
	},
	update: function(){
		var wrapperSize = this.wrapper.getSize();
		
		// calculate how much of the area needs to be scrolled
		this.scrollRatio =  this.contents.scrollHeight - wrapperSize.y;
		
		// hide or show the thumb depending on if it is needed or not
		if(this.scrollRatio <= 0){
			this.vThumb.setStyle('display', 'none');
			this.vTrack.setStyle('display', 'none');
		}
		else{
			this.vThumb.setStyle('display', 'block');
			this.vTrack.setStyle('display', 'block');
		}
	},
	vUpdateThumbContentScroll: function(){
		// calculate the current scroll position as a value between 0 - 100
		var value = (this.contents.scrollTop * this.options.steps) / this.scrollRatio;
		
		// set the thumb position 0 - 100
		this.slider.set(value);
	},
	vSnapTo: function(position){
		this.contents.scrollTop = position;
		this.vUpdateThumbContentScroll();
	}
});