/*
    SLim - Slimbox Improved
    by Marco Süß
    - Support for HTML strings as "open" argument, like Modalbox
    - Support for HTML Elements as "open" argument, like Modalbox
    - Support for displaying an error message after a timeout, if the
      image is not loaded.
    - The user may close the box at any time by clicking on the overlay,
      even if the content is not loaded yet. The original Slimbox just
      keeps displaying the spinner and the user is not able to close it
      as long as $('lbImage') == undef; This error will be catched in my
      version, so that the teardown can continue as if the image was loaded.
    - The next and prev-Links will be blurred after clicking. This prevents the
      ugly 'active'-border around it from cluttering the image/content.
    - MIT-style License, of course
    
    Based on:
	Slimbox (Extended Version 1.3.1, 2007-02-21)
	by Yukio Arita (http://homepage.mac.com/yukikun/software/slimbox_ex/)
	- 	Support to show external content using iframe.
	- 	Support to set content size. You can add width/height parameters 
		in rev attribute of the anchor url.
		ex1) <a href="image.jpg" rev="width=50%, height=50%" rel="lightbox">
				<img src="image_thumb.jpg" alt="image"></a>
		ex2) <a href="text.html" rev="width=500, height=300" rel="lightbox">
				some text here</a>
	- 	Some rendering problem with IE6 is fixed. Now you can use Slimbox in 
		valid XHTML document with XML prolog.
	- Of course, license is same as original.

	Which in turn is based on:
	Slimbox v1.3 - The ultimate lightweight Lightbox clone
	by Christophe Beyls (http://www.digitalia.be) - MIT-style license.
	Inspired by the original Lightbox v2 by Lokesh Dhakar.
*/

var Lightbox = {
	init: function(options){
		this.options = $extend({
			resizeDuration: 400,
			resizeTransition: Fx.Transitions.Sine.easeInOut,
			initialWidth: 250,
			initialHeight: 250,
			animateCaption: true,
			defaultIframeWidth : 500, 
			defaultIframeHeight: 300,
            defaultCaption: 'Klicken Sie in die linke oder rechte Hälfte des Bildes um zu blättern',
            defaultErrorMessage: 'Image could not be loaded.'
		}, options || {});

		// IE 6 - XML prolog problem
		if(window.ie6 && document.compatMode=="BackCompat"){
			this.options.animateCaption = false;
		}

		this.anchors = [];
		$each(document.links, function(el){
			if (el.rel && el.rel.test(/^lightbox/i)){
				el.onclick = this.click.pass(el, this);
				this.anchors.push(el);
			}
		}, this);
		this.eventKeyDown = this.keyboardListener.bindWithEvent(this);
		this.eventPosition = this.position.bind(this);

		/*	Build float panel
			<div id="lbOverlay"></div>
			<div id="lbCenter">
				<div id="lbCanvas">
					<a id="lbPrevLink"></a>
					<a id="lbNextLink"></a>
					<!-- img or iframe element is inserted here -->
				</div>
			</div>
			<div id="lbBottomContainer">
				<div id="lbBottom">
					<a id="lbCloseLink"></a>
					<div id="lbCaption"></div>
					<div id="lbNumber"></div>
					<div style="clear:both;"></div>
				</div>
			</div>
		*/

		this.overlay = new Element('div').setProperty('id', 'lbOverlay').injectInside(document.body);

		this.center = new Element('div').setProperty('id', 'lbCenter').setStyles({width: this.options.initialWidth+'px', height: this.options.initialHeight+'px', marginLeft: '-'+(this.options.initialWidth/2)+'px', display: 'none'}).injectInside(document.body);
		this.canvas = new Element('div').setProperty('id', 'lbCanvas').injectInside(this.center);
		this.prevLink = new Element('a').setProperties({id: 'lbPrevLink', href: '#'}).setStyle('display', 'none').injectInside(this.canvas);
		this.nextLink = this.prevLink.clone().setProperty('id', 'lbNextLink').injectInside(this.canvas);
		this.prevLink.onclick = this.previous.bind(this);
		this.nextLink.onclick = this.next.bind(this);

		this.bottomContainer = new Element('div').setProperty('id', 'lbBottomContainer').setStyle('display', 'none').injectInside(document.body);
		this.bottom = new Element('div').setProperty('id', 'lbBottom').injectInside(this.bottomContainer);
		new Element('a').setProperties({id: 'lbCloseLink', href: '#'}).injectInside(this.bottom).onclick = this.overlay.onclick = this.close.bind(this);
		this.caption = new Element('div').setProperty('id', 'lbCaption').injectInside(this.bottom);
		this.number = new Element('div').setProperty('id', 'lbNumber').injectInside(this.bottom);
		new Element('div').setStyle('clear', 'both').injectInside(this.bottom);

		/* Build effects */
		var nextEffect = this.nextEffect.bind(this);
		this.fx = {
			overlay: new Fx.Tween(this.overlay, { property: 'opacity', duration: 500 }),
			resizeCenter: new Fx.Morph( this.center, {duration: this.options.resizeDuration, transition: this.options.resizeTransition, onComplete: nextEffect}),
			image: new Fx.Tween( this.canvas, { property: 'opacity', duration: 500, onComplete: nextEffect}),
			bottom: new Fx.Tween( this.bottomContainer, { property: 'height', duration: 400, onComplete: nextEffect})
		};
                this.overlay.setStyle('display', 'none');

		this.preloadPrev = new Image();
		this.preloadNext = new Image();
	},

	click: function(link){
		if (link.rel.length == 8) return this.show(link.href, link.title, link.rev);

		var j, itemNumber, items = [];
		this.anchors.each(function(el){
			if (el.rel == link.rel){
				for (j = 0; j < items.length; j++) if(items[j][0] == el.href && items[j][2] == el.rev) break;
				if (j == items.length){
					items.push([el.href, el.title, el.rev]);
					if (el.href == link.href && el.rev == link.rev) itemNumber = j;
				}
			}
		}, this);
		return this.open(items, itemNumber);
	},

	show: function(url, title, rev){
		return this.open([[url, title, rev]], 0);
	},

	open: function(items, itemNumber){
		this.items = items;
		this.position();
		this.setup(true);
		var wh = (window.getHeight() == 0) ? window.getScrollHeight() : window.getHeight();
		var st = document.body.scrollTop  || document.documentElement.scrollTop;
		this.top = st + (wh / 15);
		this.center.setStyles({top: this.top+'px', display: ''});
		this.fx.overlay.start(0.8);
		return this.changeItem(itemNumber);
	},

	position: function(){
		//IE6 - XML prolog problem.
		var ww = (window.getWidth() == 0) ? window.getScrollWidth()-22 : window.getWidth();
		var wh = (window.getHeight() == 0) ? window.getScrollHeight() : window.getHeight();
		var st = document.body.scrollTop  || document.documentElement.scrollTop;
		this.overlay.setStyles({top: st+'px', height: wh+'px', width:ww+'px'});
	},

	setup: function(open){
		var elements = $A(document.getElementsByTagName('object'));
		if (window.ie) elements.extend(document.getElementsByTagName('select'));
		elements.each(function(el){ el.style.visibility = open ? 'hidden' : ''; });
		var fn = open ? 'addEvent' : 'removeEvent';
		window[fn]('scroll', this.eventPosition)[fn]('resize', this.eventPosition);
		document[fn]('keydown', this.eventKeyDown);
		this.step = 0;
	},

	keyboardListener: function(event){
		switch (event.keyCode){
			case 27: case 88: case 67: this.close(); break;
			case 37: case 80: this.previous(); break;	
			case 39: case 78: this.next();
		}
	},

	previous: function(){
        this.prevLink.blur();
		return this.changeItem(this.activeItem-1);
	},

	next: function(){
        this.nextLink.blur();
		return this.changeItem(this.activeItem+1);
	},
    
    onImageError: function() {
        /*
         * Return if the user has already closed the box
         * and the preload object is not available anymore.
         */
        if(null == this.preload) return;
        /*
         * We assume that the image is loaded if the width
         * of the image is not 0 anymore.
         */
        var oldUserHeight = this.preload.h != -1 ? this.preload.h : this.options.defaultIframeHeight;
        this.removeCurrentItem();
        
        var rev = this.items[this.activeItem][2];
        this.step = 1;
        this.preload = new Object();
        this.preload.datatype = 'htmlElement';
        this.preload.w = this.matchOrDefault( rev, new RegExp("width=(\\d+)", "i"),  this.options.defaultIframeWidth  );
		this.preload.h = this.matchOrDefault( rev, new RegExp("height=(\\d+)", "i"), this.options.defaultIframeHeight );
        this.preload.data = new Element('p').setStyle('line-height', oldUserHeight).setStyle('text-align', 'center').setText(this.matchOrDefault( rev, new RegExp("error_message=(\\w+)", "i"),  this.options.defaultErrorMessage ) );
        this.nextEffect();
    },

	changeItem: function(itemNumber){
		if (this.step || (itemNumber < 0) || (itemNumber >= this.items.length)) return false;
		this.step = 1;
		this.activeItem = itemNumber;

		this.bottomContainer.style.display = this.prevLink.style.display = this.nextLink.style.display = 'none';
		//this.fx.image.hide();
		this.center.className = 'lbLoading';

		// discard previous content by clicking
		this.removeCurrentItem();
		
		// check item type
		var url = this.items[this.activeItem][0];
		var rev = this.items[this.activeItem][2];
		
		var re_imageURL   = /\.(jpe?g|png|gif|bmp)/i;
        var re_htmlString = /[<>]/i;
        // Is it a Mootools HTML Element? Then we insert that directly into the canvas
        if( $type(url) == 'element' ){
            this.preload = new Object();
            this.preload.datatype = 'htmlElement';
            this.preload.w = this.matchOrDefault( rev, new RegExp("width=(\\d+)", "i"),  this.options.defaultIframeWidth  );
			this.preload.h = this.matchOrDefault( rev, new RegExp("height=(\\d+)", "i"), this.options.defaultIframeHeight );
            this.preload.data = url;
            this.nextEffect(); //go on and render the element
		}
        // Is it a url matching re_imageURL? Then its an image
		else if( url.match(re_imageURL) ) {
			this.preload = new Image();	// JavaScript native Object
			this.preload.datatype = 'image';
			this.preload.w = this.matchOrDefault(rev, new RegExp("width=(\\d+%?)", "i"), -1); //-1 if use original size.
			this.preload.h = this.matchOrDefault(rev, new RegExp("height=(\\d+%?)", "i"), -1);
			this.preload.onload = this.nextEffect.bind(this);
			this.preload.src = url;
            
            /*
             * Set an error handler. An error message reading
             * "Image not found" will be displayed if a request
             * error occurs.
             */
            this.preload.onerror = this.onImageError.bind(this);
        }
        // Is it a string matching < and >? Then its a html string
        else if( url.match(re_htmlString) ) {
            this.preload = new Object();
            this.preload.datatype = 'htmlString';
            this.preload.w =  this.matchOrDefault(rev, new RegExp("width=(\\d+)", "i"), this.options.defaultIframeWidth);
			this.preload.h = this.matchOrDefault(rev, new RegExp("height=(\\d+)", "i"), this.options.defaultIframeHeight);
            this.preload.data = url;
            this.nextEffect(); //go on and render the html
        }
        // It has to be some stuff to be loaded in an iframe
        else{
			this.preload = new Object ();	// JavaScript native Object
			this.preload.datatype = 'iframe';
			this.preload.w =  this.matchOrDefault(rev, new RegExp("width=(\\d+)", "i"), this.options.defaultIframeWidth);
			this.preload.h = this.matchOrDefault(rev, new RegExp("height=(\\d+)", "i"), this.options.defaultIframeHeight);
			this.preload.src = url;
			this.nextEffect(); //asynchronous loading
		}
        
		return false;
	},

	nextEffect: function(){
		switch (this.step++){
		case 1:
			this.center.className = '';

			// create HTML element
			if( this.preload.datatype == 'image' ) {
				var ws = (this.preload.w == -1) ? this.preload.width.toString() : this.preload.w.toString();
				var hs = (this.preload.h == -1) ? this.preload.height.toString() : this.preload.h.toString();
				this.p_width = ( q = ws.match(/(\d+)%/) ) ? q[1] * this.preload.width * 0.01 : ws;
				this.p_height = ( q = hs.match(/(\d+)%/) ) ? q[1] * this.preload.height * 0.01 : hs;
				new Element('img').setProperties({id: 'lbImage', src:this.preload.src, width:this.p_width, height:this.p_height}).injectInside(this.canvas);
				this.nextLink.style.right = '';
            } else if( this.preload.datatype == 'htmlString' ) {
                this.p_width  = this.preload.w;
                this.p_height = this.preload.h;
				this.canvas.set( 'html', this.canvas.getHTML + this.preload.data );
                this.nextLink.style.right = '25px';
            } else if( this.preload.datatype == 'htmlElement' ) {
                this.p_width  = this.preload.w;
                this.p_height = this.preload.h;
				this.preload.data.injectInside(this.canvas);
                this.nextLink.style.right = '25px';
			}else{
				this.p_width = this.preload.w;
				this.p_height = this.preload.h;
				// Safari would not update iframe content that has static id.
				this.iframeId = "lbFrame_"+new Date().getTime();
				new Element('iframe').setProperties({id: this.iframeId, width: this.p_width, height: this.p_height, frameBorder:0, scrolling:'yes', src:this.preload.src}).injectInside(this.canvas);
				this.nextLink.style.right = '25px';
			}
			this.canvas.style.width = this.bottom.style.width = this.p_width+'px';
			this.canvas.style.height = this.prevLink.style.height = this.nextLink.style.height = this.p_height+'px';

			this.caption.set( 'html', this.items[this.activeItem][1] || this.options.defaultCaption);
			this.number.set( 'html', (this.items.length == 1) ? '' : 'Page ' + (this.activeItem+1) + ' of ' + this.items.length );

			if (this.activeItem) this.preloadPrev.src = this.items[this.activeItem-1][0];
			if (this.activeItem != (this.items.length - 1)) this.preloadNext.src = this.items[this.activeItem+1][0];
			if (this.center.clientHeight != this.canvas.offsetHeight){
				var oh = (this.p_height == this.canvas.clientHeight) ? this.canvas.offsetHeight : eval(this.p_height)+18; // fix for ie
				this.fx.resizeCenter.start({height: oh});
				break;
			}

			this.step++;
		case 2:
			if (this.center.clientWidth != this.canvas.offsetWidth){
				var ow = (this.p_width == this.canvas.clientWidth) ? this.canvas.offsetWidth : eval(this.p_width)+18; // fix for ie
				this.fx.resizeCenter.start({width: ow, marginLeft: -ow/2});
				break;
			}
			this.step++;
		case 3:
			this.bottomContainer.setStyles({top: (this.top + this.center.clientHeight)+'px', height:'0px', marginLeft: this.center.style.marginLeft, width:this.center.style.width, display: ''});
			this.fx.image.start(1);
			break;
		case 4:
			if (this.options.animateCaption){
				// This is not smooth animation in IE 6 with XML prolog.
				// If your site is XHTML strict with XML prolog, disable this option.
				this.fx.bottom.start(0,this.bottom.offsetHeight+10);
				break;
			}
			this.bottomContainer.style.height = (this.bottom.offsetHeight+10)+'px';
		case 5:
			if (this.activeItem){
				this.prevLink.style.display = '';
			}
			if (this.activeItem != (this.items.length - 1)){
				this.nextLink.style.display = '';
			}
			this.step = 0;
		}
	},

	close: function(){
		if (this.step < 0) return;
		this.step = -1;
        this.removeCurrentItem(); // discard content
		for (var f in this.fx) this.fx[f].cancel();
		this.center.style.display = this.bottomContainer.style.display = 'none';
		this.fx.overlay.chain(this.setup.pass(false, this)).start(0);
		return false;
	},

	removeCurrentItem: function(){
		if (this.preload){
			if( this.preload.datatype == 'image' ) {
                /*
                 * checking for null equivalence before running the method so
                 * that the thing closes even if the content is not loaded yet.
                 * Useful for 404s, for example.
                 */
				if( $('lbImage') != null ) $('lbImage').dispose();
				this.preload.onload = Class.empty;
            } else if( this.preload.datatype == 'htmlString' || this.preload.datatype == 'htmlElement' ) {
                this.canvas.getChildren().each(function(canvasChild){
                    if( canvasChild.id == 'lbNextLink' || canvasChild.id == 'lbPrevLink' ) return;
                    $(canvasChild).remove();
                });
                //this.canvas.setHTML('');
			}else{
				$(this.iframeId).remove();
			}
			this.preload = null;
		}		
	},

	matchOrDefault: function(str, re, val){
		var hasQuery = str.match(re);
		return hasQuery ? hasQuery[1] : val;
	}

};

window.addEvent('domready', Lightbox.init.bind(Lightbox));
