Class.prototype.toSingleton = function(){
    var p = this.prototype;
    var instance = undefined;
    
    if ($defined(p.initialize) && $type(p.initialize) == 'function') 
        var init = p.initialize;
    
    p.initialize = function(){
        if (!$defined(instance)) {
            if ($defined(init) && $type(init) == 'function') 
                init.apply(this, arguments);
            instance = this;
        }
        return instance;
    }
};

var MoodalBox = new Class({
    Implements: [Options],
    
    // Зададимся двумя опциями.
    options: {
        // Прозрачность оверлея после показа всплывающего окна.
        destinationOverlayOpacity: 0.7,
        
        // Возможность спрятать окно кликом по оверлею.
        allowManualClose: true
    },
    
    // В конструктор передаем два обязательных аргумента и необязательный аргумент с опциями.
    //   element — идентификатор элемента окна.
    //   overlay — идентификатор элемента оверлея.
    initialize: function(element, overlay, content, options){
        this.setOptions(options);
        
        this.element = this.createPopup(element);
        this.overlay = this.createOverlay(overlay);
        var a = getPageSize();
        if (navigator.userAgent.toLowerCase().indexOf('msie') > -1) {
            this.overlay.setStyles({
                'height': a[1]
            });
        }
        // Bonch: Внедряем контент
        if ($type(content) == 'string') {
            this.element.appendText(content);
        }
        else 
            if ($type(content) == 'element') {
                content.inject(this.element);
            }
        
        // Проверяем опцию возможности скрытия окна по клику.
        if (this.options.allowManualClose) 
            // Цепляем на клик по оверлею метод скрытия.
            // Примечание: функция bind(param) возвращает метод this.hide, внутри которого переменная this
            // привязана к param. Если опустить вызов bind, тогда this внутри this.hide будет привязан к элементу,
            // событие в котором мы обрабатываем, т.е. к this.overlay.
            this.overlay.addEvent("click", this.hide.bind(this));
        
        // Получаем размеры окна для последующей его центровки.
        this.targetCoords = this.element.getCoordinates();
        
        // Эффекты для показывания/скрытия окна и оверлея.
        // Используется эффект, изменяющий заданное CSS-свойство во времени, в данном случае — это прозрачность.
        this.fx = {
            overlayAnimation: new Fx.Tween(this.overlay, {
                property: "opacity"
            }),
            elementAnimation: new Fx.Tween(this.element, {
                property: "opacity"
            })
        }
    },
    
    // Bonch: Создаем оверлей
    createOverlay: function(id){
        return new Element('div', {
            'id': id
        }).inject(document.body, 'top');
    },
    
    // Bonch: Создаем окно
    createPopup: function(id){
        return new Element('div', {
            'id': id
        }).inject(document.body, 'top');
    },
    
    // Вызов этого метода покажет всплывающее окно.
    show: function(){
        // Выставляем элементы в нужные места.
        this.setPosition();
        
        // Запускаем анимацию показа (изменение прозрачности до видимых значений).
        this.fx.overlayAnimation.start(0, this.options.destinationOverlayOpacity);
        this.fx.elementAnimation.start(0, 1);
    },
    
    // Вызов этого метода скроет всплывающее окно.
    hide: function(){
        // Запускаем анимацию скрытия (изменение прозрачности до невидимых значений).
        this.fx.overlayAnimation.start(this.options.destinationOverlayOpacity, 0);
        this.fx.elementAnimation.start(1, 0);
    },
    
    // Здесь мы корректируем позицию всплывающего окна на странице и
    // размеры оверлея.
    setPosition: function(){
        if (navigator.userAgent.toLowerCase().indexOf('msie') > -1) {
            this.element.setStyles({
                "marginLeft": -(this.targetCoords.width / 2),
                "marginTop": -parseInt(this.targetCoords.height / 2 - window.getScrollTop())
            });
        }
        else {
            this.element.setStyles({
                "marginLeft": -(this.targetCoords.width / 2),
                "marginTop": -parseInt(this.targetCoords.height / 2)
            });
        }
    }
});

function getPageSize(){
    var xScroll, yScroll;
    if (window.innerHeight && window.scrollMaxY) {
        xScroll = document.body.scrollWidth;
        yScroll = window.innerHeight + window.scrollMaxY;
    }
    else 
        if (document.body.scrollHeight > document.body.offsetHeight) { // all but Explorer Mac
            xScroll = document.body.scrollWidth;
            yScroll = document.body.scrollHeight;
        }
        else { // Explorer Mac...would also work in Explorer 6 Strict, Mozilla and Safari
            xScroll = document.body.offsetWidth;
            yScroll = document.body.offsetHeight;
        }
    var windowWidth, windowHeight;
    if (self.innerHeight) { // all except Explorer
        windowWidth = self.innerWidth;
        windowHeight = self.innerHeight;
    }
    else 
        if (document.documentElement && document.documentElement.clientHeight) { // Explorer 6 Strict Mode
            windowWidth = document.documentElement.clientWidth;
            windowHeight = document.documentElement.clientHeight;
        }
        else 
            if (document.body) { // other Explorers
                windowWidth = document.body.clientWidth;
                windowHeight = document.body.clientHeight;
            }
    // for small pages with total height less then height of the viewport
    if (yScroll < windowHeight) {
        pageHeight = windowHeight;
    }
    else {
        pageHeight = yScroll;
    }
    
    // for small pages with total width less then width of the viewport
    if (xScroll < windowWidth) {
        pageWidth = windowWidth;
    }
    else {
        pageWidth = xScroll;
    }
    
    arrayPageSize = new Array(pageWidth, pageHeight, windowWidth, windowHeight);
    return arrayPageSize;
}

// Bonch: делаем MoodalBox синглетоном, чтобы гарантированно
// был только один объект класса MoodalBox
MoodalBox.toSingleton();
