/**
 * modal.js
 * @author Brian Crescimanno <brian.crescimanno@autotrader.com>
 *
 * Creates a modal dialog box on the page.
 *
 */

if(typeof Prototype == undefined) throw("Modal requires the Prototype library");
if(typeof Effect == undefined) throw("Modal requires the Scriptaculous library");

var Modal = Class.create({
    initialize: function(options){
        this.options = this._extendOptions(options);
        this.docHeight = this._getDocHeight();
        this.newDiv = this._cacheDiv();
        this.overlay = this._cacheOverlay();
        this.modal = null;
        this.isShown = false;

        document.observe("modal:triggerhide", this._handleHideTrigger.bindAsEventListener(this));
    },

    hide: function(){
        this.isShown = false;
        this._hideOverlay();
    },

    resize: function(width, height){
        new Effect.Morph(this.modal, {
            style:'height: '+height+'px;',
            duration: 0.5,
            transition: Effect.Transitions.sinoidal
        });
        new Effect.Morph(this.modal, {
            style:'width: '+width+'px; left: ' + this._getNewLeftOffset(width)+ 'px',
            duration: 0.5,
            delay: 0.4,
            transition: Effect.Transitions.sinoidal
        });
    },

    scale: function(width, height){
        this.options.startWidth = width;
        this.options.startHeight = height;
    },

    show: function(el, parent){
        if(!this.isShown){
            this._showOverlay(el, parent);
            this.isShown = true;
        }
    },

    triggerHide: function(delay){
        //console.debug("triggering hide in " + delay + " seconds");
        if(!(delay>0)) delay = 2;
        delay = delay * 1000;
        setTimeout(function(){ this.hide(); }.bind(this), delay);
    },

    _cacheDiv: function() {
        return new Element('div');
    },

    _createModal: function(el, parent){
        var checkHeightForAuto = this.options.startHeight === "auto" ? this.options.startHeight : this.options.startHeight +"px";
        return new Element('div', {'class': "modal"}).setStyle({
                        height: checkHeightForAuto,
                        width: this.options.startWidth+"px",
                        top: this._getTopOffset(parent)+"px",
                        left: this._getLeftOffset(parent)+"px"
                    }).setOpacity(0).update(el);
    },

    _cacheOverlay: function(){
        return new Element('div', {'class': this.options.overlayClass}).setOpacity(0).setStyle({
            height: this.docHeight});
    },

    _extendOptions: function(options){
        return Object.extend({
            dynamic: false,
            startHeight: 500,
            startWidth: 500,
            fps: 75,
            overlaySpeed: 0.5,
            overlayClass: 'modal-overlay',
            overlayOpacity: 0.6,
            modalSpeed: 0.5
        }, options || {});
    },

    _getDocHeight: function(){
        var vH = document.viewport.getHeight();
        var dH = document.height;
        //Exploder always needs body.getHeight; IE7 is too stupid to understand docHeight and viewport.getHeight returns only the current viewable area. 100% is also viewable area.
        if (Prototype.Browser.IE){ return $(document.body).getHeight(); }
        else if(dH > vH && typeof dH != "undefined"){return dH+"px";}
        else {return "100%";}
    },

    _getTopOffset: function(parent){
        var pOffset = 0;
        if(parent){
            //make sure it's got Prototype methods
           // Element.extend(parent);
            //pOffset = parent.cumulativeOffset().top; 
        }
        var scroll = document.viewport.getScrollOffsets().top;
        var checkHeightForAuto = this.options.startHeight === "auto" ? 250 : this.options.startHeight; // chosing an arbitrary number here b/c getHeight nor getDimensions works on the created modal node. - CK
        var vOffset = Math.floor((document.viewport.getHeight() - checkHeightForAuto) / 2);
        return (scroll + vOffset - pOffset > 0) ?  (scroll + vOffset - pOffset) : 60;
    },

    _getLeftOffset: function(parent){
        var pOffset = 0;
        if(parent) pOffset = parent.cumulativeOffset().left;
        return Math.floor((document.viewport.getWidth() - this.options.startWidth - pOffset) / 2);
    },

    _getNewLeftOffset: function(width){
        return Math.floor((document.viewport.getWidth() - width) / 2);
    },

    _handleHideTrigger: function(e){
        if(this.isShown){
            this.triggerHide(2.5);
        }
    },

    _hideOverlay: function(){
        new Effect.Opacity(this.modal,{
            from: 1.0,
            to: 0.0,
            fps: this.options.fps,
            duration: this.options.modalSpeed
        });

        new Effect.Opacity(this.overlay, {
            from: this.options.overlayOpacity,
            to: 0.0,
            fps: this.options.fps,
            duration: this.options.overlaySpeed,
            delay: (this.options.modalSpeed /2),
            afterFinish: function(){
                this._modalCleanup();
            }.bind(this)
        });
    },

    _modalCleanup: function(){
        this.overlay.remove();
        this.newDiv.remove();
        this.modal.remove();
        this.modal = null;
    },
    
    _showOverlay: function(el, parent){
        //defect 5283 - in IE pages with video need to recalculate body heights due to dom injection
        if (Prototype.Browser.IE && this.docHeight != $(document.body).getHeight()){
            this.docHeight = this._getDocHeight();
            this.overlay = this._cacheOverlay();
        }
        if ((parent == null) || parent == ""){
            parent = Element.extend(document.body);
        } else  {
            parent = $(parent);
        }
        parent.appendChild(this.newDiv);
        document.body.insert(this.overlay);
        this.modal = this._createModal(el, parent);
        new Effect.Opacity(this.overlay, {
            from: 0.0,
            to: this.options.overlayOpacity,
            fps: this.options.fps,
            duration: this.options.overlaySpeed,
            afterFinish: function(){
                parent.appendChild(this.modal);
                new Effect.Opacity(this.modal,{
                    from: 0.0,
                    to: 1.0,
                    fps: this.options.fps,
                    duration: 0.5
                });
            }.bind(this)
        });
    }
});

/**
 * atxModal.js
 *
 * Creates a modal dialog box on the page in the ATX Style.
 *
 * @author Brian Crescimanno <brian.crescimanno@autotrader.com>
 * @require Prototype.js, Script.aculo.us, Modal.js, atxModal.css, modal.css
 * @extends Modal
 *
 */

var AtxModal = Class.create(Modal, {

    initialize: function($super, options){
        $super(options);
    },

    catchLogin: function(id){
        document.observe("login:catch", function(e){
            this.show($(id));
        }.bind(this));
    },

    populate: function(el){
        this.content.update(el.hide());
        new Effect.Appear(el, {
            from: 0.0,
            to: 1.0,
            duration: 0.4,
            delay: 0.7,
            fps: 75
        });
    },

    show: function(el, parent){
        //defect 1966 - Safari erroronously registering Click event twice - causes flicker
        if (this.isShown) {return false;}
        this.isShown = true;
        this.el = el;
        var tc = new Element("div", {'class': 'modal-tc'});
        var tb = new Element("div", {'class': 'modal-top'}).update(tc);
        var close = new Element("div", {'class': 'modal-close'}).observe("click", this.hide.bind(this));
        tb.insert({bottom: close});

        this.content = new Element("div", {'class': 'modal-content-r'}).update(el.show());
        var cr = new Element("div", {'class': 'modal-content'}).update(this.content);

        var bc = new Element("div", {'class': 'modal-bc'});
        var bb = new Element("div", {'class': 'modal-bottom'}).update(bc);

        var styledModal = new Element("div", {'class': 'atx-modal'}).insert({
            bottom: tb}).insert({bottom: cr}).insert({bottom: bb});

        this._showOverlay(styledModal, parent);        
    },

    isDisplaying: function(){
        return this.isShown;
    },

    /* for ATX implementation, we need to not destroy the modal's contents */
    _modalCleanup: function($super){
        document.body.appendChild(this.el.hide());
        $super();
    }

});

var ModalManager = Class.create({
    initialize: function(){
        this.modalObjs = [];
        this.modal = new AtxModal();
        this.watchClicks();
    },

    addModal: function(id, width, height, parentId){
        parentId = (parentId.length > 1) ? parentId : null;
        var tmpModal = {
            mId: id,
            mWidth: width,
            mHeight: height,
            mParent: parentId
        };
        this.modalObjs.push(tmpModal);
    },

    fetchAndShow: function(id){
        var toShow = this.findModal(id);
        this.modal.scale(toShow.mWidth, toShow.mHeight);
        this.modal.show($(toShow.mId), $(toShow.mParent));
    },

    findModal: function(id){
        var numModals = this.modalObjs.length;
        for(var i=0; i < numModals; i++){
            if(this.modalObjs[i].mId === id){
                return this.modalObjs[i];
            }
        }
        return null;
    },

    hide: function(){
        this.modal.hide();
    },

    watchClicks: function(){
        document.observe("click", function(e){
            var rel = e.element().getAttribute("rel");
            if(rel){
                if(rel.indexOf("modalLink") != -1){
                    e.stop();
                    this.fetchAndShow(rel.split(" ")[1]);
                } else if(rel.indexOf("closeModal") > -1){
                    e.stop();
                    this.modal.hide();
                }
            }
        }.bind(this));
    }
});