/*
	SmoodalBox v0.1
	By A S Jones
	Displays content from inline, iframe, another element, remote HTML, JSON or XML sources.
	No image gallery (yet)
	Heavily influenced, inspired and derived from lightbox, slimbox, thickbox, smoothbox, shadowbox, squeezebox, etc
	Started life as ModalBox -> MoodalBox -> SmoodalBox, curse those lightbox derivatives taking all the cool names!
	
	TODO:
		Replace getContent with external content handlers. This can enable slide shows, processor for json results etc
		iFrame code needs refactoring.
	
	Licence: Open Source MIT Licence
		
*/

var SmoodalBox = new Class({
	Implements: [Options, Events],
	
	overlay: null,
	box: null,
	
	options: {
		boxWidth: 600,
		boxHeight: 400,
		boxDuration: 200,
		bindRegEx: /^smoodal/i,
		overlayOpacity: 0.75,
		overlayDuration: 75,
		remoteRegEx: /remote\[(.+)\]/i,
		elementRegEx: /element\[(.+)\]/i,
		zIndex: 65555
	},
	
	initialize: function(options) {
		this.setOptions(options);		
		// create overlay, box etc
		this.createElements();
		// bind links
		this.bindElements();
		this.bindEvents = {
			window: this.positionElements.bind(this),
			key: this.keyDown.bind(this),
			scroll: this.checkTarget.bind(this)
		}
	},	
	
	createElements: function(){		
		this.overlay = new Element('div', {id: 'sbOverlay'}).setStyles({display: 'none', zIndex: this.options.zIndex});
		this.box = new Element('div', {id: 'sbBox'}).setStyles({display: 'none', zIndex: this.options.zIndex + 2});
		this.box.closeLink = new Element('a', {id: 'sbCloseLink', href: '#'}).setStyles({display: 'none', zIndex: this.options.zIndex + 3});			
		this.box.content = new Element('div', {id: 'sbContent'}).injectInside(this.box).set('opacity', 0);
		this.box.adopt(this.box.closeLink,this.box.content);
		if(Browser.Engine.trident4) this.box.addClass('sbBox-ie6');
		$(document.body).adopt(this.overlay, this.box);
		$$([this.box.closeLink, this.overlay]).addEvent('click', function(event){
			event.preventDefault();
			this.hide();
		}.bind(this));
		this.overlay.set('tween', {duration:this.options.overlayDuration, unit:'px'}).set('opacity', 0);
		this.box.set('morph',{duration: this.options.boxDuration, unit:'px', link: 'chain'});
	},
	
	bindElement: function(el){
		if(!el._smoodalboxBound){
			el._smoodalboxBound = true;
			el.addEvent('click', function(event){
				event.preventDefault(); 
				this.show(el);
			}.bind(this));
		}
	},
	
	bindElements: function(){
		$$("a").filter(function(el){
			if (el.rel && el.rel.test(this.options.bindRegEx)){				
				this.bindElement(el);
			};			
		}, this);		
	},
		
	show: function(el) {
		
		// hide link's selection box
		el.blur();
		
		this.positionElements();
		this.resetBox();
		this.setEventHandlers(true);
		this.toggleFloaters(true);		
		
		this.overlay.setStyle("display", "block").tween('opacity', 0, this.options.overlayOpacity);
		
		this.box.setStyle("display", "block").morph({
			width: this.options.boxWidth.toInt(),
			left: (window.getScrollLeft() + (window.getWidth() - this.options.boxWidth) / 2).toInt()
		});
		
		this.box.morph({
			height: this.options.boxHeight.toInt(),
			top: (window.getScrollTop() + (window.getHeight() - this.options.boxHeight) / 2).toInt()
		});		
		
		this.box.morph({
			onComplete: function(){
				this.box.closeLink.setStyle('display','block');
				this.getContent(el);
			}.bind(this)
		});
				
	},
	
	hide: function(){		
		this.overlay.tween('opacity', this.options.overlayOpacity, 0);
		$$([this.overlay, this.box]).setStyle("display", "none");		
		this.box.content.empty();
		this.setEventHandlers(false);
		this.toggleFloaters(false);
	},	
	
	getSubType: function(src, regex){
		var matches = src.match(regex);
		return (matches) ? ((matches.length > 0) ? matches[1] : '') : '';
	},
	
	getContent: function(el){		
		
		/* 
		
			todo: parse rel attribute for semicolon separated parameters
			e.g.: rel="smoodal;iframe"
						rel="smoodal;element[someId]"
						rel="smoodal;ajax[json]"
						rel="smoodal;ajax[html]"
			
			currently only checking from existance of above strings in rel attribute
		
		*/
		
		if(el.rel.test(/iframe/i)){
			// content wrapped in iFrame
			// todo: iframe height calculation vs. css
			this.showContent('<iframe src="' + el.href + '" style="height:' + this.options.boxHeight + 'px;width:' + this.options.boxWidth + '"></iframe>');
		}
		else
		{
			
			if(el.rel.test(this.options.remoteRegEx)){
				// content from remote request	
				var req;
				var subType = this.getSubType(el.rel, this.options.remoteRegEx);				
				switch(subType){
					case 'json':
						req = new Request.JSON({
							onComplete: function(responseJSON, responseText){
								if(responseText){
									var cb = this.showContent.bind(this);
									cb(responseText);
								}
							}.bind(this)
						});
						break;
						
					case 'html':
						req = new Request.HTML({
							onComplete: function(responseTree, responseElements, responseHTML, responseJavaScript){
								if(responseHTML){
									var cb = this.showContent.bind(this);
									cb(responseHTML);
								}
							}.bind(this)
						});					
						break;
						
					default:
						req = new Request({
							onComplete: function(responseHTML, responseXML){
								if(responseHTML){
									var cb = this.showContent.bind(this);
									cb(responseHTML);
								}
							}.bind(this)
						});
						break;
						
				}
				// send remote request
				req.send({url: el.href, method: 'get', data: ''});				
			}
			else
			{
				
				if(el.rel.test(this.options.elementRegEx)){
					// content from element
					var elId = $(this.getSubType(el.rel, this.options.elementRegEx));
					if(elId){
						this.showContent(elId.get('html'));
					}
				}
				else{
					// inline content from element's title attribute
					this.showContent(el.title);
				}
			}
		}
	},

	showContent: function(someContent){		
		this.box.content.setStyle('overflow', 'auto').set('html', someContent).tween('opacity', 0, 1)
	},

	setEventHandlers: function(show){		
		var fn = show ? "addEvent" : "removeEvent";		
		window[fn]("scroll", this.bindEvents.window);
		window[fn]("resize", this.bindEvents.window);
		document[fn]("keydown", this.bindEvents.key);
		//document[fn]("mousewheel", this.bindEvents.scroll);
	},
	
	toggleFloaters: function(show){
		// taken from slimbox
		["object", Browser.Engine.trident4 ? "select" : "embed"].forEach(function(tag) {
			$each(document.getElementsByTagName(tag), function(el) {
				if(show) el._smoodalboxState = el.style.visibility;
				el.style.visibility = show ? "hidden" : el._smoodalboxState;
			});
		});		
	},	
	
	positionElements: function(){
		this.overlay.setStyles({top: window.getScrollTop(), height: window.getHeight()});
		this.box.setStyles({			
			left: (window.getScrollLeft() + (window.getWidth() - this.options.boxWidth) / 2).toInt(),
			top: (window.getScrollTop() + (window.getHeight() - this.options.boxHeight) / 2).toInt(),
			width: this.options.boxWidth.toInt(),
			height: this.options.boxHeight.toInt()
		});
	},	
	
	resetBox: function(){
		this.box.closeLink.setStyle('display','none');
		this.box.setStyles({			
			left: (window.getScrollLeft() + (window.getWidth()) / 2).toInt(),
			top: (window.getScrollTop() + (window.getHeight()) / 2).toInt(),
			width: 0,
			height: 0
		});
		this.box.content.setStyle('overflow', 'hidden');
		this.box.content.set('opacity',0);
	},
	
	keyDown: function(event){		
		var e = Event(event);
		e.preventDefault();	
		if((e.key == 'esc') || (e.key == 'backspace')){
				this.hide();
		}
	},
	
	checkTarget: function(event){
		return this.content.hasChild(event.target);
	}
	
});
