var ContentFetcher = new Class({
	overlay: null,
	outer: null,
	inner: null,
	loading: null,
	elements: [],
	animating: false,
	req: null,
	visible: false,
	imageload: {
		images: [],
		loaded: 0
	},
	fx: {
		overlay: null,
		outer: null,
		inner: null,
		loading: null
	},
	options: {
		append: '', //append this to the url to be fetched
		update: null, //update this element instead of displaying a lightbox
		opacity: 0.7,
		duration: 250,
		fixFlash: false, //hide flash when displaying the lightbox
		adoptOnly: '', //adopt only this element from the fetched contents
		onlyAdoptChildren: false, //only adopt children from options.adoptOnly
		classPrefix: 'jplb-',
		onReady: null
	},
	initialize: function (elements, options) {
		this.setOptions(options);
		this.elements = elements;

		this.setup();
		
		if (this.options.onReady) {
			this.addEvent('ready', function (evt) {this.options.onReady}.bindWithEvent(this));
		}

		this.elements.each(function (el) {
			el.addEvent('click', this.clicked.bindWithEvent(this, el));
		}, this);
	},
	setup: function () {
		this.options.update = $(this.options.update);
		
		if (this.isAnimated()) {
			var size = $(window).getScrollSize();
			this.overlay = new Element('div', {
				id: this.options.classPrefix + 'overlay',
				styles: {
					height: size.y,
					opacity: this.options.opacity,
					visibility: 'hidden'
				}
			}).injectInside(document.body);
			this.overlay.addEvent('click', function (evt) {
				evt.stop();
				this.hide();
			}.bind(this));
	
			this.outer = new Element('div', {
				id: this.options.classPrefix + 'outer',
				styles: {
					opacity: 1,
					visibility: 'hidden'
				}
			}).injectInside(document.body);
			this.outer.addEvent('click', function (evt) {
				evt.stop();
				this.hide();
			}.bind(this));
	
			this.inner = new Element('div', {
				id: this.options.classPrefix + 'inner'
			}).injectInside(this.outer);
			this.inner.addEvent('click', function (evt) {
				evt.stopPropagation();
			}.bind(this));
	
			this.content = new Element('div', {
				id: this.options.classPrefix + 'content'
			}).injectInside(this.inner);
	
			this.loading = new Element('div', {
				id: this.options.classPrefix + 'loading',
				styles: {
					opacity: 1,
					visibility: 'hidden'
				}
			}).injectInside(this.inner);
	
			this.fx.overlay = new Fx.Tween(this.overlay, {
				duration: this.options.duration
			})
			this.fx.overlay.addEvent('start', this.start.bind(this));
			this.fx.overlay.addEvent('complete', this.completed.bind(this));
	
			this.fx.outer = new Fx.Tween(this.outer, {
				duration: this.options.duration
			})
			this.fx.outer.addEvent('start', this.start.bind(this));
			this.fx.outer.addEvent('complete', this.completed.bind(this));
	
			this.fx.loading = new Fx.Tween(this.loading, {
				duration: this.options.duration
			})
			this.fx.loading.addEvent('complete', this.completed.bind(this));
			this.fx.loading.addEvent('start', this.start.bind(this));
		}
	},
	start: function (el) {
		switch (el) {
			case this.overlay:
				if (this.visible) {
					el.setStyle('display', 'block');
					this.fixFlash(true);
				}
				break;
			case this.outer:
				if (this.visible) {
					el.setStyle('display', 'block');
				}
				break;
		}
	},
	completed: function (el) {
		var visible = el.getStyle('visibility') == 'hidden' ? false : true;
		switch (el) {
			case this.overlay:
				if (!this.visible) {
					el.setStyle('display', 'none');
					this.fixFlash();
				}
				break;
			case this.outer:
				if (!this.visible) {
					el.setStyle('display', 'none');
				}
				if (visible && this.req) {
					this.req.send();
				}
				break;
		}
	},
	imageLoaded: function () {
		this.imageload.loaded++;
		if (this.imageload.loaded == this.imageload.images.length) {
			this.show();
		}
	},
	loaded: function (tree, elements, html) {
		this.content.setStyles({
			visibility: 'hidden'
		});
		
		if (this.options.adoptOnly) {
			var numberOfFetchedElements = elements.length, i = 0;
			do {
				if (elements[i].get('id') == this.options.adoptOnly) {
					if (!this.options.onlyAdoptChildren) {
						this.content.adopt(elements[i]);
					}
					else {
						this.content.innerHTML = elements[i].innerHTML;
					}
					break;
				}
			}
			while (i++ < numberOfFetchedElements);
		}
		else {
			this.content.adopt(tree);
		}
		
		var container = this.options.update ? this.options.update : this.inner;
		this.content.injectInside(container);

		var images = [];
		images.extend(this.content.getElements('img'));
		//get background images as well
		images.extend(this.getBackgroundImages());

		// load images
		this.imageload = {
			images: images,
			loaded: 0
		};
		images.each(function (el) {
			if (el.complete) {
				this.imageload.loaded++;
			}
			else {
				el.addEvent('load', this.imageLoaded.bind(this));
			}
		}, this);

		if (this.imageload.images.length >= this.imageload.loaded) {
			this.show();
		}
	},
	show: function () {
//		this.fx.loading.start('opacity', 1, 0);
		this.loading.setStyle('display', 'none');
		
		this.fireEvent('ready'); //ready to show
		
		var closes = this.content.getElements('.close');
		closes.each(function (el) {
			el.addEvent('click', function (evt) {
				this.hide();
			}.bind(this));
		}, this);

//		this.content.setStyle('visibility', 'visible');
		new Fx.Tween(this.content, {duration: this.options.duration}).start('opacity', 0, 1);
	},
	hide: function () {
		this.visible = false;
		this.fx.overlay.start('opacity', 0)
		this.fx.outer.start('opacity', 0)
	},
	clicked: function (evt, el) {
		if (evt) {
			evt.stop();
		}
		this.toggleTriggersClass();
		if (!this.visible) {
			var url = new String(el.get('href'));
			if (url && url != '#') {
				if (this.options.append) {
					url += url.contains('?') ? '&' : '?';
					url += this.options.append;
				}
				this.load(url)
			}
		}
		else {
			this.hide();
		}
	},
	load: function (url) {
		this.req = new Request.HTML({
			url: url,
			evalScripts: false,
			onSuccess: this.loaded.bind(this),
			onFailure: function () {
				alert('The page request failed, please refresh the page and try again.');
			},
			method: 'get'
		});

		if (this.isAnimated()) {
			// show loading
			this.visible = true;
			this.inner.empty();
			this.fx.loading.start('opacity', 1);
			this.loading.injectInside(this.inner);
	
			// fade in
			this.fx.overlay.start('opacity', 0, this.options.opacity)
			this.fx.outer.start('opacity', 0, 1)
		}
		else {
			this.req.send();
		}
	},
	fixFlash: function (disable) {
		if (this.options.fixFlash) {
			["object", /MSIE/.test(navigator.userAgent) ? "select" : "embed"].forEach(function(tag){
				Array.forEach(document.getElementsByTagName(tag), function(el){
//					el.setStyle('visibility', disable ? 'hidden' : 'visible');
					el.style.visibility = disable ? 'hidden' : 'visible'; //meh
				});
			});
		}
	},
	toggleTriggersClass: function(hide) {
		this.elements.each(function (el) {
			if (hide) {
				el.removeClass(this.options.triggerActiveClass);
			}
			else {
				el.addClass(this.options.triggerActiveClass);
			}
		}, this);
	},
	isAnimated: function() {
		//if the element to update is specified, then we won't have any
		//animation. Otherwise, animate with fading background.
		return !this.options.update;
	},
	//get the background images for all 
	getBackgroundImages: function(e) {
		e = $(e);
		if (!e) e = this.content;
		var images = [];
		
		//has the element any background image?
		var bg = e.getStyle('background-image');
		if (bg && bg != 'none') {
			bg = bg.replace(/url\("?([^"\)]+)"?\)/i, '$1');
			var img = $(new Image());
			img.src = bg;
			images.push(img);
		}
		
		//recurse down to check the children
		e.getChildren().each(function(c) {
			images.extend(this.getBackgroundImages(c));
		}, this);
		
		return images;
	}
});
ContentFetcher.implement(new Options, new Events);
