/**
 * Liteshow JavaScript library, version 1.0beta3
 * (c) 2006-2007 Benjamin Mack <www.xnos.org>
 *
 * Liteshow is freely distributable under the terms of an MIT-style license.
 * For details, see the Liteshow web site at http://www.xnos.org/
 */

function getpix (pos){
    if (typeof(pageTracker) != "undefined" && pageTracker != null) {
        if (typeof(gaid) == "undefined" || gaid == null) {
            aid = 'null';
        }
        else{
            aid = gaid;
        }
        path = 'imageshow/aid_' + aid.toString() + '/pos_' + (pos+1).toString();
        pageTracker._trackPageview(path);
    //alert(path);
    }   
}

// ==== Extending the window object ==== //
Object.extend(window, {
    getInnerWidth: function() {
        // Non-IE
        if (typeof(window.innerWidth) == 'number') return window.innerWidth;
        // IE 6+ in 'standards compliant mode
        if (document.documentElement && document.documentElement.clientWidth) return document.documentElement.clientWidth;
        // IE 4 compatible
        if (document.body && document.body.clientWidth) return document.body.clientWidth;
    },

    getInnerHeight: function() {
        if (typeof(window.innerHeight) == 'number') return window.innerHeight;
        if (document.documentElement && document.documentElement.clientHeight) return document.documentElement.clientHeight;
        if (document.body && document.body.clientHeight) return document.body.clientHeight;
    }
});

Event.KEY_SPACE = 32;


Object.extend(Element, {
    // get maximum dimensions with considering maxHeight / maxWidth option
    getMaxDimensions: function(el) {
        var dims = Element.getDimensions(el);
        if (LiteConfig.maxSize) {
            while (dims.width > window.getInnerWidth() || dims.height > window.getInnerHeight()*LiteConfig.maxSize) {
                dims.width = dims.width*0.95;
                dims.height = dims.height*0.95;
            }
        }
        return {
            width: dims.width,
            height: dims.height
        };
    }
});


// ==== Global config object ==== //
var LiteConfig = {
    dom: {},	// information about the state of the DOM will be saved in there
    view: 'HorizontalSlide',	// the running slideshow, will be overwritten with every init() of every slideshow

    maxSize: 0.7,	// screen height in percent that the image can fil out max
    cacheNum: 4,	// number of (previous / next) images that should be loaded into cache

    useBookmarks: false,	// whether to use the bookmarking system or not
	
    xmlPath: '',	// not used when displaying a single image

    throbber: '/images/g/loading.gif',
    isLoading: false,	// internal state variable

    // div markers
    idContainer: 'liteShow',
    idCacheContainer: 'liteCache',
    idOverlay: 'liteOverlay',
    idThrobber: 'liteLoading',
    idBrowser: 'liteBrowser'
}


// ==== Main Liteshow object ==== //
var Liteshow = {
    browser: {},
    pos: 0,
	
    // == XML / resource functions == //
    xml: null,
    loadXML: function(file) {
        new Ajax.Request(LiteConfig.xmlPath + file, {
            method: 'get',
            onComplete: function(xhr) {
                this.xml = xhr.responseXML;
                LiteEvent.run('postXML');
            }.bind(this)
        });
    },

    // returns the information from the XML file of a image in an array with two elements
    getImageInfo: function(pos) {
        if (this.xml == null) {	// single image mode
            var src = this.options.resourceId;
            var title = this.options.singleImageTitle;
        } else {
            if (!this.isValidImagePos(pos)) return [];
            var node  = this.xml.getElementsByTagName('image')[pos];
            var src   = node.getAttribute('src');
            var title = node.getElementsByTagName('title')[0].firstChild.nodeValue;
        }
        return [src, title.replace(/"/g, '\'')];
    },

    isLastImage:     function(pos) {
        return (pos == this.numImages()-1)          ? true : false;
    },
    isValidImagePos: function(pos) {
        return (pos < 0 || pos >= this.numImages()) ? false : true;
    },
    numImages:       function()    {
        return (this.xml == null) ? 1 : (this.xml.getElementsByTagName('image').length || 0);
    },


    // instantiates the object with the xml, creates the DOM elements, loads the XML and then starts the slide show
    init: function(resourceId) {
        var defaultOptions = {
            resourceId: resourceId,
            startPos: 0,
            transitionTime: 2,
            timer: false,
            timerDelay: 5,
            automaticRewind: false
        };
        this.options = Object.extend(defaultOptions, (arguments[1] || {}) );
        this.pos = this.options.startPos;

        if (this.localInit) this.localInit();

        // initialize timer
        LiteTimer.setOptions(this.options.timer, this.options.timerDelay, this.options.transitionTime);

        // -- DOM manipulation --

        // set the HTML and BODY tag overflow to "hidden"
        LiteConfig.dom.htmlObj = document.body.parentNode;
        LiteConfig.dom.bodyObj = document.body;
        LiteConfig.dom.htmlOverflow = LiteConfig.dom.htmlObj.style.overflow;
        LiteConfig.dom.bodyOverflow = LiteConfig.dom.bodyObj.style.overflow;
        LiteConfig.dom.htmlObj.style.overflow = 'hidden';
        LiteConfig.dom.bodyObj.style.overflow = 'hidden';
		
        // creating the container, the overlay and the cacheContainer, then show the overlay
        if (!$(LiteConfig.idOverlay))   new Insertion.Top(LiteConfig.dom.bodyObj, '<div id="' + LiteConfig.idOverlay + '" style="display: none;"/>');
        if (!$(LiteConfig.idContainer)) new Insertion.Top(LiteConfig.dom.bodyObj, '<div id="' + LiteConfig.idContainer + '"/>');
        else {
            // reset to default styles when starting a new show
            $(LiteConfig.idContainer).innerHTML = '';
            $(LiteConfig.idContainer).setStyle({
                display: 'none',
                top: '0px',
                left: '0px',
                width: '100%',
                height: '100%'
            });
        }
        if (!$(LiteConfig.idCacheContainer)) new Insertion.Bottom(LiteConfig.idContainer, '<div id="' + LiteConfig.idCacheContainer + '"/>');
        new Effect.Appear(LiteConfig.idOverlay, {
            duration: 0.2,
            from: 0.0,
            to: 0.7
        });
        LiteThrobber.show();

        // load resource file or XML file via AJAX
        var ext = resourceId.substring(resourceId.length-4).toLowerCase();
        if (ext != '.jpg' && ext != '.png' && ext != '.gif') {
            if (ext != '.xml') {
                resourceId += '.xml';
            }
            LiteEvent.add('postXML', 'setupShow');
            this.loadXML(resourceId);
        } else {
            // run the setup without delay
            this.setupShow();
        }
    },
	
    setupShow: function() {
        // start in browser mode
        if (this.pos == 'browser') {
            this.browserInit();
        }
        // start with caching and rendering single images (default)
        else {
            var waitUntilCached = this.pos + LiteConfig.cacheNum - 1;
            while (waitUntilCached >= this.numImages()) waitUntilCached--;
            if (this.isImageCached(waitUntilCached)) {
                this.firstImage();
            } else {
                LiteEvent.add(waitUntilCached, 'firstImage');
                this.cacheImages(this.pos);
            }
        }

        // adding control mechanisms
        Event.observe(document, 'keypress', this.keyboardControl.bindAsEventListener(this), true);
    },

    // clean up show elements
    endShow: function() {
        LiteThrobber.hide();
        LiteTimer.stop();
        new Effect.SlideUp(LiteConfig.idContainer, {
            delay: 0.2,
            duration: 0.7
        } );
        new Effect.Fade(LiteConfig.idOverlay, {
            duration: 0.8,
            delay: 1,
            afterFinish: function() {
                LiteConfig.dom.htmlObj.style.overflow = LiteConfig.dom.htmlOverflow;
                LiteConfig.dom.bodyObj.style.overflow = LiteConfig.dom.bodyOverflow;
            }
        });
        Event.stopObserving(document, 'keypress', this.keyboardControl.bindAsEventListener(this), true);
        LiteBookmark.clear();
    },


    rewindShow: function() {
        var timer = LiteTimer.state;
        if (timer) LiteTimer.stop();
        if (this._rewindShowHook)
            this._rewindShowHook();
        else {
            this.pos = 0;
            this.viewImage(this.pos);
        }
        if (timer) LiteTimer.start();
    },


    // function that checks if an arrow key is pressed, then slides the images
    keyboardControl: function(e) {
        var key = (e.which) ? e.which : e.keyCode;
        if (e.altKey || e.shiftKey || e.ctrlKey)  {
            return true;
        }  // passthrough
        Event.stop(e);

        LiteTimer.stop();
        if (key == Event.KEY_ESC) {
            this.endShow(); return false;
        }  // end show when pressing escape

        if (LiteConfig.isLoading) return true; // do nothing when loading
        if (key == Event.KEY_RIGHT || key == Event.KEY_DOWN || key == Event.KEY_SPACE) {
            this.nextImage(); return false;
        }
        if (key == Event.KEY_LEFT  || key == Event.KEY_UP) {
            this.previousImage(); return false;
        }
        return true;
    },


    // ============== Image Caching Functions =========== //

    isImageCached: function(pos) {
        return (this.imageCached(pos) && this.imageCached(pos).complete) ? true : false;
    },

    // caches the next X and the previous X image(s)
    cacheImages: function(pos) {
        if (!this.isValidImagePos(pos)) return false;
        var startPreloadPos = (pos-LiteConfig.cacheNum > 0) ? pos-LiteConfig.cacheNum : 0;
        var stopPreloadPos  = pos+LiteConfig.cacheNum;
        for (var i = startPreloadPos; i < stopPreloadPos; i++) {
            this._cacheImage(i);
            if (this.isLastImage(i)) break;
        }
    },

    // internal function to create a cache image and set an event when loaded
    _cacheImage: function(pos) {
        var id = 'ls_cache_img_' + pos;
        // no need to load an already cached image
        if ($(id)) return;
        var info = this.getImageInfo(pos);
        new Insertion.Bottom(LiteConfig.idCacheContainer, '<img src="' + info[0] + '" id="' + id + '"/>');
        $(id).onload = function() {
            LiteEvent.run(pos);
        };
    },


    // == Image Loading Functions == //

    // takes the preloaded image and uses it as a real image
    _createImage: function(pos) {
        if (!this.isValidImagePos(pos)) return false;
        if (this.image(pos)) 			return true;
        if (!this.isImageCached(pos))   this._cacheImage(pos);
        var info = this.getImageInfo(pos);
        var browserButton = '';
        if (this.numImages() > 1) {
            browserButton = '<a class="browse" href="javascript:void(0);" onclick="getpix(998);Liteshow[LiteConfig.view].browserInit();"></a>';
        }
        new Insertion.Bottom(LiteConfig.idContainer, '<div id="ls_'+pos+'" style="display: none;" class="liteItem"><img src="'+info[0]+'" alt="'+info[1]+'" id="ls_img_'+pos+'" /><div class="liteControls" style="display: none;"><h4>'+info[1]+'</h4><a class="close" href="javascript:void(0);" onclick="Liteshow[LiteConfig.view].endShow();"></a>'+browserButton+'</div></div>');
    },


    // the next four functions are general functions that take care
    // of the loading images. Usually this is called from
    // the "firstImage"... function of the implementing views
    _firstImage: function() {
        Element.show(LiteConfig.idContainer);
        this.__addDefaultImageActions(this.pos);
        LiteBookmark.set(this, this.pos);
        if (LiteTimer.state) LiteTimer.start();
        LiteThrobber.hide();
    },

    _previousImage: function() {
        this.pos = this.pos-1;
        this.__addDefaultImageActions(this.pos);
        LiteBookmark.set(this, this.pos);
    },

    _nextImage: function() {
        this.pos = this.pos+1;
        if (this.isLastImage(this.pos)) {
            if (this.options.automaticRewind)
                LiteTimer.rewindShow();
            else
                LiteTimer.stop();
        }
        this.__addDefaultImageActions(this.pos);
        LiteBookmark.set(this, this.pos);
    },
	
    _viewImage: function(pos) {
        if (!pos) var pos = this.pos;
        this.__addDefaultImageActions(pos);
        LiteBookmark.set(this, this.pos);
    },

    __addDefaultImageActions: function(pos) {
        this.cacheImages(pos);
        if (!this.image(pos))   this._createImage(pos);
        if (!this.image(pos-1)) this._createImage(pos-1);
        if (!this.image(pos+1)) this._createImage(pos+1);

        this.image(pos).onclick                          = this.nextImage.bind(this);
        if (this.image(pos+1)) this.image(pos+1).onclick = this.nextImage.bind(this);
        if (this.image(pos-1)) this.image(pos-1).onclick = this.previousImage.bind(this);
    },


    // == Peripheral Functions == //
    lockNavigation: function(pos) {
        LiteConfig.isLoading = true;
        this.image(pos).onclick = null;
        if (this.image(pos+1)) this.image(pos+1).onclick = null;
        if (this.image(pos-1)) this.image(pos-1).onclick = null;
    },

    unlockNavigation: function(pos) {
        LiteConfig.isLoading = false;
        this.__addDefaultImageActions(pos);
    },


    // -- default title functions, can be overridden by any view --
    showInfo: function(pos) {
        if (this.pos != pos) return;
        Element.hide(this.controls(pos));
        this.controls(pos).style.width = (this.image(pos).getWidth()) + 'px';
        new Effect.BlindDown(this.controls(pos), {
            duration: 0.2
        } );
        getpix(this.pos);
    },

    hideInfo: function(pos) {
        if (Element.visible(this.controls(pos)))
            new Effect.SlideUp(this.controls(pos), {
                duration: 0.2
            } );
    },


    // == Liteshow Browser == //
    browserInit: function() {
        this.browser.images = 0;
        this.browser.tnSize = 250;
        this.browser.tnPadding = 20;
        this.browser.wasTimerOn = LiteTimer.state;
        LiteTimer.stop();
        if (this.item(this.pos)) this.hideInfo(this.pos);
		

        if (!$(LiteConfig.idBrowser)) new Insertion.After(LiteConfig.idContainer, '<div id="' + LiteConfig.idBrowser + '" style="display: none;"></div>');
        else $(LiteConfig.idBrowser).innerHTML = '';

        var styles = {
            top: '0px',
            left: '0px',
            width:  window.getInnerWidth() + 'px',
            height: window.getInnerHeight() + 'px',
            display: 'block',
            opacity: '1'
        };
        $(LiteConfig.idBrowser).setStyle(styles);
        LiteThrobber.show();
		
        $(LiteConfig.idContainer).style.zIndex = 5;

        // calculate how many images we can bring on the screen
        var numHorz = -1;
        var maxHorzSize = window.getInnerWidth();
        var tmp = maxHorzSize;
        while (tmp > numHorz) {
            numHorz += 1;
            tmp -= this.browser.tnSize+(this.browser.tnPadding*2);
        }
        var horzSize = this.browser.tnSize*numHorz + this.browser.tnPadding*numHorz*2;

        // creating the table datas
        var tbldata = "";
        for (var td = 0; td < this.numImages(); td++) {
            if (td % numHorz == 0) tbldata += '<tr>';
            tbldata += '<td id="browse_td_' + td + '"></td>';
            if (td % numHorz == numHorz-1) tbldata += '</tr>';
        }
        if (td % numHorz != numHorz-1) tbldata += '</tr>';
        $(LiteConfig.idBrowser).innerHTML = '<table cellspacing="0" cellpadding="' + this.browser.tnPadding + '">' + tbldata + '</table>';

        // putting the images in the tds
        for (var pos = 0; pos < this.numImages(); pos++) {
            if (!this.isImageCached(pos)) this._cacheImage(pos);
            this._preloadBrowserImage(pos);
        }
    },


    _preloadBrowserImage: function(pos) {
        var info = this.getImageInfo(pos);
        // TODO: replace this with DOM internal functions so we can get rid of the Builder dependency
        var browseImg  = Builder.node('img', {
            id: 'ls_browse_img_' + pos,
            style: 'display: none;',
            src: info[0],
            alt: info[1]
        });
        $('browse_td_' + pos).appendChild(browseImg);
        var reqId = 'browse_' + pos;
        if (!browseImg.complete) {
            LiteEvent.add(reqId, '_renderBrowserImage');
            browseImg.onload = function() {
                LiteEvent.run(reqId);
            };
        } else {
            this._renderBrowserImage(reqId);
        }
    },

    _renderBrowserImage: function(pos) {
        pos = pos.split('_')[1];
        var dims = Element.getDimensions(this.imageCached(pos));
        if (dims.width > dims.height) this.imageBrowser(pos).style.width  = this.browser.tnSize + 'px';
        else                          this.imageBrowser(pos).style.height = this.browser.tnSize + 'px';

        Event.observe(this.imageBrowser(pos), 'click', this.selectBrowserImage.bind(this, pos), false);
        this.browser.images += 1;
		
        var finishUp = function(pos) {
            var imgWidth = this.imageBrowser(pos).style.width;
            if (!imgWidth) {
                var dims = Element.getDimensions(this.imageCached(pos));
                imgWidth = Math.round(dims.width/dims.height * parseInt(this.imageBrowser(pos).style.height)) + 'px';
            }

            new Insertion.After(this.imageBrowser(pos), '<span style="width: '+imgWidth+';">' + this.imageBrowser(pos).alt + '</span>');
            this._checkBrowserImages();
        };
        new Effect.Appear(this.imageBrowser(pos), {
            duration: 0.4,
            queue: {
                position: 'end',
                scope: 'browserimages'
            },
            afterFinish: finishUp.bind(this, pos)
        } );
    },
	
    _checkBrowserImages: function() {
        if (this.browser.images < this.numImages()) return;
        LiteThrobber.hide();
    },
	
    selectBrowserImage: function(pos) {
        if (this.item(this.pos)) Element.hide(this.item(this.pos));
        pos = parseInt(pos);
        LiteThrobber.hide();

        // hook after selecting an image from the browser
        if (this._selectBrowserImageHook)
            this._selectBrowserImageHook(pos);

        if (!this.image(pos)) this._createImage(pos);
        Position.clone(this.imageBrowser(pos), this.item(pos), {
            setWidth: false,
            setHeight: false
        } );

        this.pos = pos;
        this.lockNavigation(pos);
        $(LiteConfig.idContainer).style.zIndex = 20;
        this.item(pos).style.zIndex = 50;
        this.image(pos).style.width  = this.imageBrowser(pos).style.width;
        this.image(pos).style.height = this.imageBrowser(pos).style.height;

        // IE width bug
        if(/MSIE/.test(navigator.userAgent)) this.item(pos).style.width = this.imageBrowser(pos).style.width;

        Element.show(this.item(pos));
        Element.hide(LiteConfig.idBrowser);
        if (this.browser.wasTimerOn) LiteTimer.start();
        this.viewImage(pos);
    },


    // == helper functions for accessing the images, titles and items   == //
    item:			function(pos) {
        return $('ls_' + pos);
    },
    controls:       function(pos) {
        return this.item(pos).lastChild;
    },
    title:          function(pos) {
        return this.item(pos).lastChild.firstChild.firstChild;
    },
    image:			function(pos) {
        return $('ls_img_' + pos);
    },
    imageCached: 	function(pos) {
        return $('ls_cache_img_' + pos);
    },
    imageBrowser: 	function(pos) {
        return $('ls_browse_img_' + pos);
    },


    // == stub functions - these need to be implemented by the extension classes == //
    firstImage:    function() {
        this._firstImage();
    },
    previousImage: function() {
        this._previousImage();
    },
    nextImage:     function() {
        this._nextImage();
    },
    viewImage:     function(pos) {
        if (!pos) var pos = this.pos;
        this._viewImage(pos);
        var dims    = Element.getDimensions(this.imageCached(pos));
        var dimsMax = Element.getMaxDimensions(this.imageCached(pos));
        var dimsNow = Element.getDimensions(this.image(pos));

        var scaleFrom = (100 * dimsNow.width / dims.width);
        var scaleTo = 100;
        if (dims.width  > dimsMax.width)  scaleTo = dimsMax.width  / dims.width * 100;
        if (dims.height > dimsMax.height) scaleTo = dimsMax.height / dims.height * 100;

        var x = Math.floor((window.getInnerWidth() - dimsMax.width)/2);
        var y = Math.floor((window.getInnerHeight() - dimsMax.height)/2 - 20);

        new Effect.Scale(this.image(pos), scaleTo, {
            delay: 0.3,
            duration: 0.5,
            scaleFrom: scaleFrom,
            mode: 'absolute',
            scaleFromCenter: true,
            scaleMode: {
                originalHeight: dims.height,
                originalWidth: dims.width
            },
            afterFinish: function () {
                this.showInfo(pos);
                this.unlockNavigation(pos);
                this._createImage(pos+2);
            }.bind(this)
        });
        new Effect.Move(this.item(pos), {
            delay: 0.3,
            duration: 0.5,
            x: x,
            y: y,
            mode: 'absolute'
        });
    }
};




// ==== Timer class ==== //
var LiteTimer = {
    state: null,
    delay: null,
    duration: null,

    id: null,
    clickOff: null,

    setOptions: function(state, delay, duration) {
        this.state = state;
        this.delay = delay;
        this.duration = duration;
    },
	
    start: function(delay) {
        if (!delay) var delay = this.delay;
        else this.delay = parseInt(delay);
        this.state = true;
        this.reset();
    },

    reset: function() {
        if (!this.state) return;
        window.clearTimeout(this.id);
        this.id = window.setTimeout('Liteshow.' + LiteConfig.view + '.nextImage(); LiteTimer.reset();', (this.delay+this.duration) * 1000);
        this.clickOff = this.stop.bind(this);
        window.onmousedown = this.clickOff;
    },

    stop: function() {
        this.state = false;
        window.clearTimeout(this.id);
        if (this.clickOff) {
            window.onmousedown = '';
            this.clickOff = null;
        }
    },
	
    rewindShow: function() {
        this.id = window.setTimeout('Liteshow.' + LiteConfig.view + '.rewindShow(); LiteTimer.reset();', this.delay * 2 * 1000);
    }
};



// ==== Bookmarker class ==== //
// 0: View, 1: GalleryID, 2: Position

var LiteBookmark = {
    recover: function() {
        var hash = window.location.hash;
        if (hash) {
            hash = hash.substring(1, hash.length);
            var details = hash.split('_');
            if (details.length != 3) return;
            Liteshow[details[0]].init(details[1], {
                startPos: parseInt(details[2])
            });
        }
    },
    set:   function(show, pos) {
        if (LiteConfig.useBookmarks) window.location.hash = '#' + LiteConfig.view + '_' + show.options.resourceId + '_' + pos;
    },
    clear: function() {
        if (LiteConfig.useBookmarks) window.location.hash = '#';
    }
};

Event.observe(window, 'load', LiteBookmark.recover, false);



// == Event framework class == //
var LiteEvent = {
    events: {},
    add: function(key, func) {
        this.events[key] = func;
    },
    run: function(key) {
        if (this.events[key]) {
            Liteshow[LiteConfig.view][this.events[key]](key)
            this.events[key] = null
        }
    }
};
	
var LiteThrobber = {
    show: function() {
        LiteConfig.isLoading = true;
        if (!$(LiteConfig.idThrobber)) new Insertion.Top(document.body, '<img id="'+LiteConfig.idThrobber+'" src="'+LiteConfig.throbber+'" style="display: none;"/>');
        if (!Element.visible(LiteConfig.idThrobber)) {
            $(LiteConfig.idThrobber).setStyle({
                top: Math.round((window.getInnerHeight()/2)-40) + 'px',
                left: Math.round((window.getInnerWidth()/2)-40) + 'px'
            });
            new Effect.Appear(LiteConfig.idThrobber, {
                duration: 0.2,
                queue: 'throbber'
            } );
        }
    },
    hide: function() {
        new Effect.Fade(LiteConfig.idThrobber, {
            duration: 0.2,
            queue: 'throbber'
        } );
        LiteConfig.isLoading = true;
    }
};

// ==== VIEW IMPLEMENTATIONS ==== //

// == Object for view that slides images from the right to the left == //
Liteshow.HorizontalSlide = Class.create();
Object.extend(Object.extend(Liteshow.HorizontalSlide, Liteshow), {

    // implementing default methods
    localInit: function() {
        LiteConfig.view = 'HorizontalSlide';
    },

    firstImage: function() {
        this._firstImage();
		
        // fix safari rendering bug
        var dimsMax = Element.getMaxDimensions(this.imageCached(this.pos));
        this.image(this.pos).style.width  = Math.floor(dimsMax.width/2)  + 'px';
        this.image(this.pos).style.height = Math.floor(dimsMax.height/2) + 'px';

        this.moveIn(this.pos);
        this.prepareNext(this.pos+1);
        if (this.pos > 0) this.preparePrevious(this.pos-1);
    },

    previousImage: function() {
        if (!this.isValidImagePos(this.pos-1)) return;
        this._previousImage();

        this.moveOutToRight(this.pos+1);
        this.moveIn(this.pos);
        this.preparePrevious(this.pos-1);
    },

    nextImage: function() {
        if (!this.isValidImagePos(this.pos+1)) return;
        this._nextImage();

        this.moveOutToLeft(this.pos-1);
        this.moveIn(this.pos);
        this.prepareNext(this.pos+1);
    },


    // moves image from the center to the left border of the window
    moveOutToLeft: function(pos) {
        if (!this.item(pos)) return;
        this.hideInfo(pos);
        this.item(pos).style.zIndex = 900;

        var dims = Element.getMaxDimensions(this.imageCached(pos));
        var x = Math.floor(0 - dims.width/4);
        var y = Math.floor(window.getInnerHeight()/2 - dims.height/4);

        new Effect.Move(this.item(pos), {
            x: x,
            y: y,
            mode: 'absolute',
            duration: this.options.transitionTime
        });
        new Effect.Scale(this.image(pos), 50, {
            scaleFromCenter: true,
            duration: this.options.transitionTime
        });
        this.unpreparePrevious(pos-1);
    },

    // moves image from the center to the right border of the window
    moveOutToRight: function(pos) {
        if (!this.item(pos)) return;
        this.hideInfo(pos);
        this.item(pos).style.zIndex = 900;

        var dims = Element.getMaxDimensions(this.imageCached(pos));
        var x = Math.floor(window.getInnerWidth() - dims.width/4);
        var y = Math.floor(window.getInnerHeight()/2 - dims.height/4);

        new Effect.Move(this.item(pos), {
            x: x,
            y: y,
            mode: 'absolute',
            duration: this.options.transitionTime
        });
        new Effect.Scale(this.image(pos), 50, {
            scaleFromCenter: true,
            duration: this.options.transitionTime
        });
        this.unprepareNext(pos+1);
    },

    // moves image to the center
    moveIn: function(pos) {
        if (!this.item(pos)) return;
        this.item(pos).style.zIndex = 1000;

        var dims    = Element.getDimensions(this.imageCached(pos));
        var dimsMax = Element.getMaxDimensions(this.imageCached(pos));
        var dimsNow = Element.getDimensions(this.image(pos));

        var scaleFrom = Math.floor(100 * dimsNow.width / dims.width);
        var scaleTo   = 100;
        if (dims.width  > dimsMax.width)  scaleTo = dimsMax.width  / dims.width * 100;
        if (dims.height > dimsMax.height) scaleTo = dimsMax.height / dims.height * 100;
        if (isNaN(scaleFrom)) scaleFrom = 50;

        var x = Math.floor((window.getInnerWidth()  - dimsMax.width)/2);
        var y = Math.floor((window.getInnerHeight() - dimsMax.height)/2 - 20);

        if (!Element.visible(this.item(pos))) {
            this.item(pos).style.left = Math.floor(window.getInnerWidth() * 1.5) + 'px';
            this.item(pos).style.top  = Math.floor(window.getInnerHeight()/ 2 - dimsMax.height/4) + 'px';
            this.item(pos).style.display = 'block';
        }

        new Effect.Scale(this.image(pos), scaleTo, {
            duration: this.options.transitionTime,
            scaleFromCenter: true,
            scaleFrom: scaleFrom,
            mode: 'absolute',
            scaleMode: {
                originalHeight: dims.height,
                originalWidth: dims.width
            },
            afterFinish: function () {
                this.showInfo(pos);
                this.unlockNavigation(pos);
                this._createImage(pos+2);
            }.bind(this)
        });
        new Effect.Move(this.item(pos), {
            x: x,
            y: y,
            mode: 'absolute',
            duration: this.options.transitionTime
        });
    },


    // slides image half into the window from the right
    prepareNext: function(pos) {
        if (!this.item(pos)) this._createImage(pos);
        if (!this.item(pos)) return false;

        var dimsMax = Element.getMaxDimensions(this.imageCached(pos));
        var x = Math.floor(window.getInnerWidth() - dimsMax.width*1/8);
        var y = Math.floor(window.getInnerHeight()/2 - dimsMax.height/4);

        this.image(pos).onclick = this.nextImage.bind(this);
        this.image(pos).style.width  = Math.floor(dimsMax.width/2)  + 'px';
        this.image(pos).style.height = Math.floor(dimsMax.height/2) + 'px';

        this.item(pos).style.left = window.getInnerWidth()*1.2 + 'px';
        this.item(pos).style.top  = y + 'px';
        this.item(pos).style.display = 'block';

        new Effect.Move(this.item(pos), {
            x: x,
            y: y,
            mode: 'absolute',
            duration: this.options.transitionTime
        });
    },

    // slides image half into the window from the left
    preparePrevious: function(pos) {
        if (!this.item(pos)) this._createImage(pos);
        if (!this.item(pos)) return false;

        var dimsMax = Element.getMaxDimensions(this.imageCached(pos));
        var x = Math.floor(0 - dimsMax.width*3/8);
        var y = Math.floor(window.getInnerHeight()/2 - dimsMax.height/4);

        this.image(pos).onclick = this.previousImage.bind(this);
        this.image(pos).style.width  = Math.floor(dimsMax.width/2)  + 'px';
        this.image(pos).style.height = Math.floor(dimsMax.height/2) + 'px';

        this.item(pos).style.left = '-' + (dimsMax.width*2) + 'px';
        this.item(pos).style.top  = y + 'px';
        this.item(pos).style.display = 'block';

        new Effect.Move(this.item(pos), {
            x: x,
            y: y,
            mode: 'absolute',
            duration: this.options.transitionTime
        });
    },
	
    // slides item out to the right
    unprepareNext: function(pos) {
        if (!this.item(pos)) return;
        var dims = Element.getDimensions(this.imageCached(pos));
        var x = Math.floor(window.getInnerWidth() + dims.width*1.2);
        var y = parseInt(this.item(pos).style.top);
        new Effect.Move(this.item(pos), {
            x: x,
            y: y,
            mode: 'absolute',
            duration: this.options.transitionTime
        });
    },
	
    // slides item out to the left
    unpreparePrevious: function(pos) {
        if (!this.item(pos)) return;
        var dims = Element.getDimensions(this.imageCached(pos));
        var x = -(dims.width*1.2);
        var y = parseInt(this.item(pos).style.top);
        new Effect.Move(this.item(pos), {
            x: x,
            y: y,
            mode: 'absolute',
            duration: this.options.transitionTime
        });
    },
	
    // adding hook functions
    _selectBrowserImageHook: function(pos) {
        // hide every visible image
        for (var i = 0; i < this.numImages(); i++) {
            if (this.item(i) && Element.visible(this.item(i)))
                Element.hide(this.item(i));
        }
        this.prepareNext(pos+1);
        this.preparePrevious(pos-1);
    },

    _rewindShowHook: function() {
        if (this.pos == 0) return;
        if (this.pos == 1) return this.previousImage();
        this.unprepareNext(this.pos+1);
        this.unprepareNext(this.pos);
        if (this.pos != 2) this.unprepareNext(this.pos-1);

        this.pos = 0;
        this.__addDefaultImageActions(this.pos);
        var dimsMax = Element.getMaxDimensions(this.imageCached(this.pos));
        var x = Math.floor(0 - dimsMax.width*2);
        var y = Math.floor(window.getInnerHeight()/2 - dimsMax.height/4);

        this.image(this.pos).style.width  = Math.floor(dimsMax.width/2)  + 'px';
        this.image(this.pos).style.height = Math.floor(dimsMax.height/2) + 'px';
        this.item(this.pos).style.left = x + 'px';
        this.item(this.pos).style.top  = y + 'px';
        this.item(this.pos).style.display = 'block';
        this.viewImage(this.pos);
        this.prepareNext(this.pos+1);
    }

});


Liteshow.Fader = Class.create();
Object.extend(Object.extend(Liteshow.Fader, Liteshow), {

    localInit: function() {
        LiteConfig.view = 'Fader';
    },

    firstImage: function() {
        this._firstImage();
        this.appear();
    },

    previousImage: function() {
        if (!this.isValidImagePos(this.pos-1)) return;
        this._previousImage();

        this.hideVisibleImage(this.pos+1);
        this.hideVisibleImage(this.pos+2);
        this.appear();
    },

    nextImage: function() {
        if (!this.isValidImagePos(this.pos+1)) return;
        this._nextImage();

        this.hideVisibleImage(this.pos-1);
        this.hideVisibleImage(this.pos-2);
        this.appear();
    },
	
    hideVisibleImage: function(pos) {
        if (!this.item(pos)) return false;
        if (Element.visible(this.item(pos))) {
            this.hideInfo(pos);
            new Effect.Fade(this.item(pos), {
                queue: {
                    position: 'front',
                    scope: 'image'
                },
                transition: Effect.Transitions.linear,
                duration: 0.5
            } );
        }
    },
	
    appear: function() {
        this.adjustItem(this.pos);
        new Effect.Appear(this.item(this.pos), {
            queue: {
                position: 'end',
                scope: 'image'
            },
            duration: 0.5,
            afterFinish: function() {
                this.showInfo(this.pos);
            }.bind(this)
        } );
    },

    adjustItem: function(pos) {
        var dims = Element.getMaxDimensions(this.imageCached(pos));
        this.item(pos).style.left = Math.floor((window.getInnerWidth()-dims.width)/2) + 'px';
        this.item(pos).style.top  = Math.floor((window.getInnerHeight()-dims.height)/2)-20 + 'px';
        this.item(pos).style.width   = dims.width + 'px';
        this.image(pos).style.width  = dims.width + 'px';
        this.image(pos).style.height = dims.height + 'px';
    },
	
    _rewindShowHook: function() {
        this.hideVisibleImage(this.pos);
        this.pos = 1;
        this._previousImage();
        this.appear();
    }
});


Liteshow.SlideDown = Class.create();
Object.extend(Object.extend(Liteshow.SlideDown, Liteshow), {
    effectInProgress: null,

    localInit: function() {
        LiteConfig.view = 'SlideDown';
    },

    firstImage: function() {
        this._firstImage();
        this.appear();
    },

    previousImage: function() {
        if (!this.isValidImagePos(this.pos-1)) return;
        this._previousImage();

        this.hideVisibleImage(this.pos+1);
        this.hideVisibleImage(this.pos+2);
        this.appear();
    },

    nextImage: function() {
        if (!this.isValidImagePos(this.pos+1)) return;
        this._nextImage();

        this.hideVisibleImage(this.pos-1);
        this.hideVisibleImage(this.pos-2);
        this.appear();
    },
	
    hideVisibleImage: function(pos) {
        if (!this.item(pos)) return false;
        if (Element.visible(this.item(pos))) {
            this.hideInfo(pos);
            new Effect.SlideUp(this.item(pos), {
                queue: {
                    position: 'front',
                    scope: 'image'
                },
                transition: Effect.Transitions.linear,
                duration: this.options.transitionTime,
                delay: 0.3
            } );
        }
    },
	
    appear: function() {
        this.adjustItem(this.pos);
        if (this.effectInProgress) {
            this.effectInProgress.cancel();
        }
        this.effectInProgress = new Effect.SlideDown(this.item(this.pos), {
            queue: {
                position: 'end',
                scope: 'image'
            },
            duration: this.options.transitionTime,
            delay: 0.3,
            afterFinish: this.showInfo.bind(this, this.pos)
        } );
    },

    adjustItem: function(pos) {
        var dims = Element.getMaxDimensions(this.imageCached(pos));
        this.item(pos).style.left = Math.floor((window.getInnerWidth()-dims.width)/2) + 'px';
        this.item(pos).style.top  = Math.floor((window.getInnerHeight()-dims.height)/2)-20 + 'px';
        this.item(pos).style.width   = dims.width + 'px';
        this.image(pos).style.width  = dims.width + 'px';
        this.image(pos).style.height = dims.height + 'px';
    },
	
    _rewindShowHook: function() {
        this.hideVisibleImage(this.pos);
        this.pos = 1;
        this._previousImage();
        this.appear();
    }
});
