/**
 * 
 * Basic popup functions - loading, show, hide.
 * 
 * @require prototype.js
 * @require scriptaculous/effects.js
 */

/**
 * класс контроллера попапов. 
 * Выполняет функции создания, показа - скрытия, хранения попапов
 */

var PPControlClass = Class.create();
PPControlClass.prototype = {
    
    initialize: function() {
        this.popup = {};
        this.zIndex = 100;
    }, 
    /**
     * Запрос на вывод попапа.
     * @param Obj event_obj обект страницы, который вызвал попап
     * @param str key id попапа
     * @param Obj options доп. опции
     */
    runPopup: function(event_obj, key, options) {

        var popup = this.createPopup(event_obj, key, options);
        popup.setEventTimer(event_obj);
        this.zIndex ++;
        popup.setZIndex(this.zIndex);
    },
    /**
     * Скрытие попапа с id = key
     */
    hidePopup: function(key) {
        if (!this.popup[key])
            return;
        this.popup[key].hide();
    },
    /**
     * Скрыть попап, в котором находится элемент event
     * Ищется родительский ел-т с классом PopUp. Он и скрывается.
     */
    hideCurrentPopup: function(event) {
        var pp = $(event).up('.PopUp');
        if (pp.id)
            this.hidePopup(pp.id);
    },
    
    /**
     * Обновить попап
     */
    reloadPopup: function(key) {
        if (!this.popup[key])
            return;
        this.popup[key].reload();
    },
    
    createPopup: function(event_obj, key, options) {
        if (this.popup[key])
            return this.popup[key];

        var popup = new PPClass(event_obj, key, options);
        this.popup[key] = popup;
        return popup;
    }
};

var pp_control = new PPControlClass();

/**
 * Класс попапа
 * 
 * Доступные event: 
 *      onCreate - создание попапа
 *      onFirstShow - первый вывод попапа
 *      onShow - вывод попапа
 */

var PPClass = Class.create();
PPClass.prototype = {

    event_obj: null,

    show_timer: null,
    hide_timer: null,

    transaction: false,
    // кол-во коллизий на ожидание завершения транзакции показа попапа
    count_coll: 0,

    /**
     * Создание попапа.
     * @param Obj event_obj обект страницы, который вызвал попап
     * @param str key id попапа
     * @param Obj options доп. опции
     */
    initialize: function(event_obj, div_id, options) {
        event_obj = $(event_obj);
        this.options = {
            showTimeout : 1000, // таймаут при выводе
            hideTimeout : 1000, // таймаут при скрытии
            offsetx : 0, // смещение попапа x
            offsety : 20 // смещение попапа y
        };
        options = options || {};
        if (options.timeout !== undefined)
        {
            options.showTimeout = options.showTimeout ? options.showTimeout : options.timeout;
            options.hideTimeout = options.hideTimeout ? options.hideTimeout : options.timeout;
        }

        this.options = Object.extend(this.options, options);
        // если есть элемент с id, то в качестве попапа берем его. Иначе генерим див
        if ($(div_id))
            this.popup = $(div_id);
        else
            this.popup = this.createDiv(div_id);
        this.id = div_id;
        this.isLoad = false;
        if (!options.noMouseEvent)
            this.setOnEvent(event_obj);

        this.event('onCreate');
    },

    setOnEvent: function(event_obj)
    {
        var self = this;
        
        this.popup.observe('mouseover', self.outHideTimer.bind(self));

        this.popup.observe('mouseout', self.onHideTimer.bind(self));
    },

    onHideTimer: function ()
    {
        var self = this;
        if (self.show_timer) 
            window.clearTimeout(self.show_timer);
        if (self.hide_timer) 
            window.clearTimeout(self.hide_timer);
        self.hide_timer = window.setTimeout(self.hide.bind(self), self.options.hideTimeout);
    },

    outHideTimer: function()
    {
        var self = this;
        if (self.hide_timer) 
            window.clearTimeout(self.hide_timer);
    },

    /**
     * Создание дива для попапа
     */
    createDiv: function(div_id) {
        var div = document.createElement('div');
        div = $(div);
        div.addClassName('PopUp');

        if (this.options.ppClass)
            div.addClassName(this.options.ppClass);

        div.id = div_id;
        div.setStyle({display: 'none'});
        div.update('Loading...<br />Загрузка...');
        div.hide();
        document.body.appendChild(div);
        return div;
    },

    setEventTimer: function(event_obj)
    {
        var self = this;
        if (!this.options.noMouseEvent)
        {
            $(event_obj).observe('mouseout', self.onHideTimer.bind(self));

            $(event_obj).observe('mouseover', self.outHideTimer.bind(self));
        }
        if (self.show_timer) window.clearTimeout(self.show_timer);
        self.show_timer = window.setTimeout(self.show.bind(self, event_obj), self.options.showTimeout);
    },

    show: function(event_obj)
    {
        var self = this;
        var changeEvent = false;
        if (this.event_obj && this.event_obj != event_obj)
            changeEvent = true;

        if (changeEvent && this.popup.visible())
            this.hide({
                afterFinish: function() { 
                    self.show.bind(self, event_obj);  
                    self.transaction_end();
                }
            });
        else if (!this.popup.visible())
        {
            this.transaction_start();;
            this.event_obj = event_obj;
            new Effect.toggle(this.popup, 'appear', {
                beforeStart: self.position.bind(self),
                afterFinish: function () { self.transaction_end(); }
            });
        }
        if (!this.isLoad)
            this.event('onFirstShow');
        this.event('onShow');
        this.isLoad = true;
    },

    hide: function(onEvent)
    {
        if (this.popup.visible())
        {
            if (this.transaction && this.count_coll < 4)
            {
                this.count_coll ++;
                this.onHideTimer();
            }
            else
            {
                this.count_coll = 0;
                var self = this;
                this.transaction_start();
                var onFinishEvent = {  };
                new Effect.toggle(this.popup, 'appear', onEvent || {
                    afterFinish: function() {
                        self.transaction_end();
                    }
                });
            }
        }
    },

    position: function()
    {
        // @TODO: после выхода версии prototype 1.6.1 будет пофикшен баг с clonePosition
        // и надо будет перевести вывод попапа на clonePosition - т.е. раскоментить нижележащий код.
        var pos = this.event_obj.cumulativeOffset();
        this.popup.setStyle({
            left: pos[0] + this.options.offsetx + 'px',
            top: pos[1] + this.options.offsety + 'px'
        });
        /*this.popup.clonePosition($(this.event_obj), {
            setWidth: false,
            setHeight: false,
            offsetLeft: this.options.offsetx,
            offsetTop: this.options.offsety
        });*/
    },

    reload: function()
    {
        this.isLoad = false;
        this.hide();
    },

    event: function(eventName) {
        if(this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this);
        if(this.options[eventName]) this.options[eventName](this);
    },
    
    setZIndex: function(ind) {
        this.popup.setStyle({zIndex: ind});
    },

    transaction_start: function()
    {
        this.transaction = true;
    },

    transaction_end: function()
    {
        this.transaction = false;
    }

};
