/*
 *
 * FBox JavaScript Library v 0.2
 * require jQuery library
 *
 * @author Alex Malshev
 * @tester Andrey Akulov
 * @copyright Flexites Ltd. (http://flexites.org/)
 * @license GPL
 * @requires jQuery (http://jquery.com/)
 *
 Пример:
    new FBox({
        animation: true, 
        imagesList: false,
        imagesListWidth: 80,
        showOverlay: true,
        useLoaderAnim: true,
        loaderParams: {
            animatioLength: 400,
            animationStep: 50,
            animationTime: 100
        },
        navigationButtons: true,
        navigationButtonsHover: true,
        constraints: {
            minWidth: 470,
            minHeight: 465,
            maxWidth: 750,
            maxHeight: 465
        },
        data: {
            type: 'gallery',
            gallery: pictures
        }
    })
    Параметры:
        animation - показывать/не показывать анимацию появления галереи (true/false)
        imagesList - показывать/не показывать превью картинок в области галереи (true/false),
        imagesListWidth - ширина области превьюшек (integer),
        showOverlay - показывать/не показывать затемнение под галереей/картинкой (true/false)
        useLoaderAnim - использовать внутреннюю анимацию для загрузчика (true/false)
        loaderParams - параметры анимации
            animatioLength - длина анимации (высота файла с картинкой загрузчика), должна быть кратна шагу анимации (integer) По умолчанию: 400
            animationStep - шаг анимации (минимальная высота кадра анимации) (integer) По умолчанию: 50
            animationTime - временной шаг анимации (integer) По умолчанию: 50
        navigationButtons - показывать/не показывать навигационные кнопки в галерее (true/false)
        navigationButtonsHover - использовать эффект появления кнопок навигации при наведении (true/false)
        constraints - кое-какие-размеры
            minWidth - минимальная ширина области галереи (integer)
            minHeight - минимальная высота области галереи (integer)
            maxWidth - максимальная ширина области галереи (integer)
            maxHeight - максимальная высота области галереи (integer)
        data - тип данных для галереи     // если используется ajax (см. ниже), указывать не нужно
            type - тип данных (галерея, картинка, HTML-код) ('gallery'/'image'/'html')
            Если указан тип 'gallery',то обязательны параметры:
                gallery - массив хешей вида:
                      src - url изображения
                      thumbnail - url превьюшки //не обязателен, если imagesList указано false
                      comment - комментарий 
            Если указан тип 'image', то обязательны параметры:
                image - хеш вида:
                      src - url изображения
                      comment - комментарий
            Если указан тип 'html', то обязательны параметры:
                html - html-код
        ajax - использование AJAX для получения источника данных.(хеш параметров запроса)
            url - адрес, на который нужно отправлять запрос
            ... - произвольные параметры запроса
            Шаблоны ответа должны содержать теги
                <fbox type="тип данных (gallery/image/html)">
                    Структура данных аналогичная хешу, то в тегах (значения тегов должны быть в CDATA).
                    Например:
                    <image>
                        <src><![CDATA[ url изображения ]]></src>
                        <thumbnail><![CDATA[ url превью ]]></thumbnail>
                        <comment><![CDATA[ комментарий ]]></comment>
                    </image>
                </fbox>
        Алтернативные возможности инициализации без указания data (краткая запись. Параметры подставляются ВМЕСТО data):
        image: {
            src: link.href,
            comment: link.title
        } - тоже самое, что и data с типом 'image'
        html: {
            html
        } - тоже самое, что и data с типом 'html'
    Есть вариант инициализации, как плагина к jQuery:
        $("a img").FBox({ параметры }) - используется если, необходимо сделать галерею, по готовым превьюшкам.
        Инициализируеся на конструкциях вида: <a href=""><img src="" title="" /></a>.
        Пока так, в дальнейшем возможны улучшения.
 */

(function() {
    
    jQuery.fn.extend({
        FBox: function(params) {
            var pictures = new Array();
            var gallery = new FBox(params);
            this.each(function(i) {
                pictures.push({thumbnail: this.src,
                               comment: this.title,
                               src: this.parentNode.href});
                $(this.parentNode).click(function(){
                    gallery.setImageIndex(i);
                    gallery.show();
                    return false;
                    });
                });
            gallery.params.data = {type: 'gallery', gallery: pictures };
        }
    });
    
var OLD_IE = ((jQuery.browser.msie) && (jQuery.browser.version < 7));
var IE = jQuery.browser.msie;
var FBox = function(params) {
    this.params = params;
    this.setup();
}

FBox.prototype = {
    setup: function() {
        this.layout = this.params.layout || document.documentElement;
        this.$layout = $(this.layout);
        this._layout = this.layout;
        if (this._layout == document.documentElement) this._layout = document.getElementsByTagName('body')[0];
        if (!this.params.padding) this.params.padding = 10;
        var defaultConstraints = { minWidth: 300, minHeight: 300, maxWidth: 640, maxHeight: 480, offsetTop: 20, offsetLeft: 0 };
        if (!this.params.constraints)
            this.params.constraints = {};
        for (var k in defaultConstraints) {
            if (!this.params.constraints[k]) this.params.constraints[k] = defaultConstraints[k];
        }
        if (this.params.imagesList) {
            if (!this.params.inactiveImageFadeRatio) this.params.inactiveImageFadeRatio = .65;
            if (!this.params.inactiveImageFadeDuration) this.params.inactiveImageFadeDuration = 200;
            if (!this.params.imagesListWidth) this.params.imagesListWidth = 100;
        }
        if (OLD_IE) this.params.animation = false;
    },
    show: function() {
        var self = this;
        if (this.params.showOverlay) {
            this.overlayD = $el('div')
                .addClass('fb-overlay')
                .css({ opacity: this.params.overlayOpacity || 0.3  })
                .appendTo(this._layout);
            if (OLD_IE)
                this.overlayD.css({ position: 'absolute', height: this.layout.scrollHeight });
            window.setTimeout(function() { self.overlayD.click(function() { self.hide() }) }, 800);
        }
        this.wrapD = $el('div').addClass('fb-wrap').appendTo(this._layout);
        var cLeft = this.params.padding;
        if ((!this.width) || (!this.height)) {
            this.width = (this.params.constraints.minWidth || defaultConstraints.minWidth) + 2*this.params.padding;
            this.height = (this.params.constraints.minHeight || defaultConstraints.minHeight)  + 2*this.params.padding;
            if (this.params.imagesList) {
                this.width += this.params.imagesListWidth + this.params.padding;
            }
        }
        if (this.params.imagesList) {
            cLeft += this.params.imagesListWidth + this.params.padding;
        }
        this.outerD = $el('div').addClass('fb-outer').css({ display: 'none', width: this.width, height: this.height }).appendTo(this.wrapD);
        if (OLD_IE) this.outerD.css({ position: 'absolute' });
        this._placeCenter();
        var _bg = $el('div').addClass('fb-bg').html('<div class="bg-n" /><div class="bg-s" /><div class="bg-e" /><div class="bg-w" /><div class="bg-ne" /><div class="bg-se" /><div class="bg-nw" /><div class="bg-sw" />').appendTo(this.outerD);
        this.innerD = $el('div').addClass('fb-inner').appendTo(this.outerD);
        var _close = $el('div').addClass('fb-close').click(function() { self.hide() }).appendTo(this.innerD);
        this.contentD = $el('div').addClass('fb-content').css({ left: cLeft, top: this.params.padding, right: this.params.padding, bottom: this.params.padding }).appendTo(this.innerD);
        this.loaderD = $el('div').addClass('fb-loader').appendTo(this.contentD);
        if (this.params.contentOverflow) this.contentD.css({ overflow: this.params.contentOverflow });
        if (this.params.imagesList) {
            this.listD = $el('div').addClass('fb-list').css({ left: this.params.padding, top: this.params.padding, width: this.params.imagesListWidth, bottom: this.params.padding }).appendTo(this.innerD);
        }
        if (this.loaded) {
            this.restoreContent();
        } else {
            this.loadContent();
        }
        if (this.params.animation) {
            this.outerD.fadeIn(300);
        } else {
            this.outerD.show();
        }
        this._bindWindowEvents();
        if (IE) __iebgfix(_bg.find('div'));
        if (OLD_IE) __iebgfix(_close);
    },
    hide: function() {
        var self = this;
        function _hide() {
            if (self.overlayD) {
                self.overlayD.remove();
                self.overlayD = null;
            }
            self.wrapD.remove();
            self.wrapD = null;
        }
        this._unbindWindowEvents();
        this.saveState();
        if (this.params.animation) {
            this.outerD.animate({ opacity: 0 }, 300, null, function() {
                _hide();
            });
        } else {
            _hide();
        }
    },
    saveState: function() {
        // dummy
    },
    restoreContent: function() {
        this.drawContent();
    },
    loadContent: function() {
        this.data = {};
        if (this.params.data) {
            this.data = this.params.data;
        } else if (this.params.ajax) {
            this.loadAjax(this.params.ajax);
        } else if (this.params.image) {
            this.data.image = {
                src: this.params.image.src,
                comment: this.params.image.comment
            };
            this.data.type = 'image';
        } else if (this.params.html) {
            this.data.type = 'html';
            this.data.html = this.params.html;
        }
        if (!this.params.ajax) {
            this.loaded = true;
            this.drawContent();
        }
    },
    drawContent: function() {
        this.currentImage = null;
        switch (this.data.type) {
            case 'image':
                this.loadImage(this.data.image);
                break;
            case 'gallery':
                this.drawGallery(this.data.gallery);
                break;
            case 'html':
                this.contentD.html(this.data.html);
                break;
        }
    },
    loadImage: function(image) {
        if (this.currentImage != image) {
            this._loaderShow();
            if (this.data.type == 'gallery') {
                for (var i=0; i<this.data.gallery.length; i++) {
                    if (this.data.gallery[i] == image) {
                        this.imageIndex = i;
                        break;
                    }
                }
            }
            var self = this;
            var img = new Image();
            img.onload = function() {
                self.currentImage = image;
                self.drawImage(this, image.comment, image._li);
            }
            img.src = image.src;
        }
    },
    loadPrevImage: function() {
        if ((this.data.type == 'gallery') && (this.imageIndex > 0)) {
            this.loadImage(this.data.gallery[this.imageIndex - 1]);
        }
    },
    loadNextImage: function() {
        if ((this.data.type == 'gallery') && (this.imageIndex+1 < this.data.gallery.length)) {
            this.loadImage(this.data.gallery[this.imageIndex + 1]);
        }
    },
    drawImage: function(image, comment, listItem) {
        this._loaderHide();
        this._resizeContentArea(image.width, image.height);
        if (this.currentD) {
            if (this.params.animation) {
                this.currentD.fadeOut(300, function() { $(this).remove(); });
            } else {
                this.currentD.remove();
            }
        }
        var left = Math.floor((this._cwidth - image.width) / 2);
        var top = Math.floor((this._cheight - image.height) / 2);
        this.currentD = $el('div')
            .addClass('fb-img').css({ display: 'none', width: image.width, height: image.height, left: left, top: top })
            .append(image)
            .appendTo(this.contentD);

        if (comment) {
            var bottom = (top < 0) ? -top-1: 0;
            this.currentD.append($el('div').addClass('fb-comment').css({ bottom: bottom }).html('<p>'+comment+'</p>').append($el('ins').css('opacity', .6)));
        }
        if (this.params.animation) {
            this.currentD.fadeIn(300);
        } else {
            this.currentD.show();
        }
        if ((this.params.imagesList) && (listItem)) {
            $('li.active', this.listD).css({ opacity: this.params.inactiveImageFadeRatio }).removeClass('active');
            $(listItem).addClass('active').css({ opacity: 1 });
            this._scrollTo(listItem.parentNode, listItem);
        }
        if (this.params.navigationButtons)
            this._checkNavigationButtons();
    },
    drawGallery: function() {
        var self = this;
        var overF;
        var outF;
        var fadeValue = this.params.inactiveImageFadeRatio;
        var fadeDuration = this.params.inactiveImageFadeDuration;
        
        if (!this.imageIndex) this.setImageIndex(0);
        self._checkImageIndex();
        if (this.params.animation) {
            overF = function() { $(this).animate({ opacity: 1 }, fadeDuration) };
            outF = function() { if (this.className != 'active') $(this).animate({ opacity: fadeValue }, fadeDuration) };
        } else {
            overF = function() { $(this).css({ opacity: 1 }) };
            outF = function() { if (this.className != 'active') $(this).css({ opacity: fadeValue }) };
        }
        var clickF = function() {
            self.loadImage(this._imageObject);
        }
        if (this.params.imagesList) {
            var ul = $el('ul')
                .bind("mousewheel", function(e) { self._mouseWheel(this, e); return false; })
                .bind("DOMMouseScroll", function(e) { self._mouseWheel(this, e); return false; })
                .appendTo(this.listD);
            for (var i=0; i<this.data.gallery.length; i++) {
                var li = document.createElement('li');
                li._imageObject = this.data.gallery[i];
                this.data.gallery[i]._li = li;
                ul.append($(li).hover(overF, outF).click(clickF).css({ opacity: .8 }).append($el('img').attr({ src: this.data.gallery[i].thumbnail, title: this.data.gallery[i].comment })));
            }
            var list = ul.get(0);
            this.btnUD = $el('div').addClass('fb-btn-up').css({ left: this.params.imagesListWidth/2 }).mousedown(function() { self._startScroll(list, -1); return false; }).mouseup(function() { self._stopScroll() }).appendTo(this.listD);
            this.btnDD = $el('div').addClass('fb-btn-down').css({ left: this.params.imagesListWidth/2 }).mousedown(function() { self._startScroll(list, 1); return false; }).mouseup(function() { self._stopScroll() }).appendTo(this.listD);
        }
        if (this.params.navigationButtons) {
            var navD = $el('div').addClass('fb-nav').appendTo(this.contentD);
            this.btnLD = $el('div').css({ display: 'none' }).addClass('fb-nav-prev').click(function() { self.loadPrevImage() }).html('<div class="fb-btn-left" />').appendTo(navD);
            this.btnRD = $el('div').css({ display: 'none' }).addClass('fb-nav-next').click(function() { self.loadNextImage() }).html('<div class="fb-btn-right" />').appendTo(navD);
            if (this.params.navigationButtonsHover) {
                this.btnLD.addClass('fb-nav-h').hover(function() { $(this).removeClass('fb-nav-h') }, function() { $(this).addClass('fb-nav-h') });
                this.btnRD.addClass('fb-nav-h').hover(function() { $(this).removeClass('fb-nav-h') }, function() { $(this).addClass('fb-nav-h') });
            }
        }
        if (OLD_IE) __iebgfix($('.fb-btn-up, .fb-btn-down, .fb-btn-left, .fb-btn-right'), this.innerD);
        this.loadImage(this.data.gallery[this.imageIndex]);
    },
    loadAjax: function(params) {
        var self = this;
        var action = params.url;
        this._loaderShow();
        jQuery.post(action, params, function(data) { self._parseAjaxResponse(data); }, 'xml');
    },
    setImageIndex: function(idx){
        var self = this;
        this.imageIndex = parseInt(idx);
     },
    _checkImageIndex: function(){
        var self = this;
        if(this.imageIndex<0){
            this.imageIndex = 0;
        }else{
            if(this.imageIndex>=this.data.gallery.length){
                this.imageIndex = this.data.gallery.length - 1;
            }
        }
    },
    _parseAjaxResponse: function(data) {
        this._loaderHide();
        this.loaded = true;
        var fboxData = data.getElementsByTagName('fbox')[0];
        if (fboxData) {
            this.data.type = fboxData.getAttribute('type');
            switch (this.data.type) {
                case 'image':
                    this.data.image = _parseBlock(fboxData);
                    break;
                case 'gallery':
                    this.data.gallery = [];
                    var images = fboxData.getElementsByTagName('image');
                    for (var i=0; i<images.length; i++) {
                        this.data.gallery.push(_parseBlock(images[i]));
                    }
                    break;
                case 'html':
                    this.data.html = _getCDATA(fboxData);
                    break;
                default:
                    this.hide();
                    return;
            }
            this.drawContent();
        } else {
            this.hide();
        }
    },
    _loaderShow: function() {
        this.loaderD.show();
        if(this.params.useLoaderAnim){
            var anim_length = this.params.loaderParams.animationLength || 400;
            var anim_step = this.params.loaderParams.animationStep || 50;
            var anim_time = this.params.loaderParams.animationTime || 50;
            var l = this.loaderD.get(0);
            var p = 0;
            if (this._loaderTimer) window.clearInterval(this._loaderTimer);
            this._loaderTimer = window.setInterval(function() {
                p += anim_step;
                if (p == anim_length) p = 0;
                l.style.backgroundPosition = '0 -'+p+'px';
            }, anim_time)    
        }
        
    },
    _loaderHide: function() {
        if (this._loaderTimer) window.clearInterval(this._loaderTimer);
        this.loaderD.hide();
    },
    _checkNavigationButtons: function() {
        this.btnLD.css({ display: (this.imageIndex > 0) ? 'block' : 'none' });
        this.btnRD.css({ display: (this.imageIndex+1 < this.data.gallery.length) ? 'block' : 'none' });
    },
    _scrollTo: function(list, item) {
        var top = item.offsetTop - ((list.clientHeight - item.clientHeight) / 2);
        if (this.params.animation) {
            $(list).animate({ scrollTop: top }, 300);
        } else {
            list.scrollTop = top;
        }
        this._checkScrollBtns(list);
    },
    _mouseWheel: function(list, e) {
        var delta = e.detail ? e.detail : -e.wheelDelta;
        delta /= Math.abs(delta);
        this._startScroll(list, delta, true);
    },
    _startScroll: function(list, k, wheel) {
        var self = this;
        if (this.scrollTimer) window.clearInterval(this.scrollTimer);
        this.scrollTimer = window.setInterval(function() { self._scroll(list, k) }, 10);
        if (wheel) {
            if (this._stopTimer) window.clearInterval(this._stopTimer);
            this._stopTimer = window.setTimeout(function() { self._stopScroll() }, 250);
        }
    },
    _stopScroll: function() {
        if (this.scrollTimer) window.clearInterval(this.scrollTimer);
    },
    _scroll: function(list, k) {
        list.scrollTop += k*5;
        this._checkScrollBtns(list);
    },
    _checkScrollBtns: function(list) {
        if (!IE) {
            this.btnUD.css({ opacity: (list.scrollTop > 0) ? 1 : .3 });
            this.btnDD.css({ opacity: (list.scrollTop < list.scrollHeight - list.clientHeight) ? 1 : .3 });
        }
    },
    _resizeContentArea: function(width, height) {
        if (width < this.params.constraints.minWidth) width = this.params.constraints.minWidth;
        else if (width > this.params.constraints.maxWidth) width = this.params.constraints.maxWidth;
        if (height < this.params.constraints.minHeight) height = this.params.constraints.minHeight;
        else if (height > this.params.constraints.maxHeight) height = this.params.constraints.maxHeight;
        this._cwidth = width;
        this._cheight = height;
        width += 2*this.params.padding;
        height += 2*this.params.padding;
        if (this.params.imagesList)
            width += this.params.imagesListWidth + this.params.padding
        if ((this.width != width) || (this.height != height)) {
            this.width = width;
            this.height = height;
            var c = this._getCenterCoords();
            if (this.params.animation) {
                this.outerD.animate({ left: c.left, top: c.top, width: this.width, height: this.height }, 300);
            } else {
                this.outerD.css({ left: c.left, top: c.top, width: this.width, height: this.height });
            }
        }
    },
    _placeCenter: function() {
        var c = this._getCenterCoords();
        this.outerD.css({
            left: c.left,
            top: c.top
        });
    },
    _getCenterCoords: function() {
        var r = {};
        r.top = (this.layout.clientHeight - this.height) / 2;
        if (r.top < this.params.constraints.offsetTop) r.top = this.params.constraints.offsetTop;
        if (OLD_IE) r.top += this.layout.scrollTop;
        r.left = (this.layout.clientWidth - this.width) / 2;
        if (r.left < this.params.constraints.offsetLeft) r.left = this.params.constraints.offsetLeft;
        if (OLD_IE) r.left += this.layout.scrollLeft;
        return r;
    },
    _bindWindowEvents: function() {
        var self = this;
        this._windowOnScrollResize = function() {
            self._placeCenter();
        }
        $(window).bind('resize', this._windowOnScrollResize);
        if (OLD_IE) $(window).bind('scroll', this._windowOnScrollResize);
    },
    _unbindWindowEvents: function() {
        $(window).unbind('resize', this._windowOnScrollResize);
        if (OLD_IE) $(window).unbind('scroll', this._windowOnScrollResize);
    }
}

window.FBox = FBox;

__iebgfix = function(object) {
    object.each(function() {
        this.style.background = 'none';
        var fix = $el('div').addClass('bgfix').appendTo(this);
        var imageURL = fix.css('background-image');
        var bPos = fix.css('background-position');
        var sizingMethod = 'scale';
        imageURL = imageURL.substr(5, imageURL.length-7);
        fix.css({
            background: 'none',
            filter: 'progid:DXImageTransform.Microsoft.AlphaImageLoader(src="' + imageURL + '", sizingMethod="' + sizingMethod + '")'
        });
    });
}

function _getCDATA(xmlElement) {
    if (xmlElement.text != undefined) return xmlElement.text;
    if (xmlElement.textContent != undefined) return xmlElement.textContent;
    if (xmlElement.firstChild != undefined) return xmlElement.firstChild.nodeValue;
    return '';
}
function _parseBlock(xmlElement) {
    var elements = xmlElement.childNodes;
    var ret = {};
    for (var i=0; i<elements.length; i++) {
        if (elements[i].tagName)
            ret[elements[i].tagName] = _getCDATA(elements[i]);
    }
    return ret;
}
function $el(tagName) {
    return $(document.createElement(tagName));
}
})();
