/**
 * Popup class for jQuery
 *
 * @author              khaehlen <khaehlen@zigwebsoftware.nl>
 * @version     1.0
 * @copyright   Copyright (c) 2011, Zig Websoftware
 *
 *
 * TODO: Documentation - of course ;)
 */


/** TYPICAL USAGE
 
   // EXAMPLE 1: Attaching a Popup to a DOM element
 
   var oLettertypePopup = new Popup({
                        sContent:               '<img src="fileadmin/templates/skins/elanwonen/images/lettergrootte.png" />'
                }, '#fontSizeButton');
       
        // EXAMPLE 2: Using Popup.open (directly opens the popup). It takes a Popup as parameter
       
        var oPopup = Popup.open(new Popup({sContentId: 'c53', sStyle: Popup.style.SIMPLE }));
       
        // EXAMPLE 3: Using Popup.open again. You can also give a config array as parameter
       
        var oPopup = Popup.open({sContent:'Hello world'});
       
        // EXAMPLE 4: Calling the run() function on the popup. Note that all DOM nodes with class .inside-a-popup will become popups (see grab())
       
        var aPopups = Popup.grab('.inside-a-popup');
        aPopups[1].run();
 */

function Popup(oConfig, sTriggeringElSelector)
{
        // constructor
        this.oConfig = {
                sContainerId:                   'popup',
                sContent:'Inhoud van de popup',
                sTitle:  null,
                sContentId: null,           // this will OVERRIDE sContent. If set, the element with this id will
                                                                                        // be inserted into the popup. You can also give a node.
                fnShow:     null,
                fnHide:     null,
                sHideSelector: null,           // an empty string will result in the popup closing when clicked
                                                                                        // Note this is an SELECTOR, you should enter something like
                                                                                        // "#hide-button", OR "#hide-button1,#hide-button2" OR
                                                                                        // "#button-bar .hide-button". Do NOT forget the # before an id.
                sStyle:                                 null,
                sSkinId:                                null,           // custom skin for the popup,
                bLazyContentLoading:    false,          // if true, the given content will be placed inside the popup when
                                                                                        // the run() method is called. If false it will be placed there
                                                                                        // immediately
                sButtonsHtml:                   null,           // HTML that will be inserted at the bottom of the popup. You can
                                                                                        // use specific classes to trigger specific functionality:
                                                                                        //              .close-button: Closes the popup
                bBlocking:                              false           // if true, the popup cannot be closed by clicking the overlay or
                                                                                        // pressing the ESC key
        };
       
        this.sTriggeringElSelector      = (typeof sTriggeringElSelector != 'undefined') ? sTriggeringElSelector : null; // see above about SELECTORS
        this.sContainerId                       = '';           // will be set later
        this.oContainer                         = null;         // the jQuery container element with the above id
        this.sBaseClass                         = 'Popup';      // do not change!

        this.initialize(oConfig);
       
        return true;
}

// STATIC PROPERTIES
Popup.oOverlay                                  = null;
Popup.iActivePopups                     = 0;
Popup.iPopupIndex                               = 0;
Popup.aStack                                    = [];
Popup.style                                     = new Object();         // see constants

// CONSTANTS

// our skin
Popup.sDefaultSkin = '                                                                                                                             \
           <div id="popup-skin" class="popup" style="display: none">       \
                   <div class="popup-header">                                                              \
                           <div class="popup-title"></div>                                         \
                           <div class="popup-header-buttons">                                      \
                                   <div class="close-button"><span>X</span></div>  \
                           </div>                                                                                          \
                   </div>                                                                                                  \
                   <div class="popup-content"></div>                                               \
                   <div class="popup-buttons"></div>                                               \
           </div>';
Popup.oDefaultSkinNode = null;

// prefab stylings
Popup.style.SIMPLE = 'background-color: white; width: 300px; min-height: 40px; border: 1px solid black;';


// METHODS

Popup.prototype = {
               
                initialize: function(oConfig)
                {
       
                        // override default settings
                        if (typeof oConfig == 'object')
                        {
                                for (var i in oConfig)
                                {
                                        this.oConfig[i] = oConfig[i];
                                }
                        }
                       
                        // initialize observer
                        if (this.sTriggeringElSelector)
                        {
                                var oThis = this;
                                jQuery(this.sTriggeringElSelector).bind('click', function(oEvent){
                                        oThis.run();
                                }, false);
                        }
                       
                        var oSkinNode = this.loadSkin();
                       
                        var sContainerId = this.oConfig.sContainerId + parseInt(++Popup.iPopupIndex);
                       
                        var oPopupNode = oSkinNode.cloneNode(true);
                        jQuery(oPopupNode).attr('id', sContainerId);
                       
                        // additional styling
                        if (this.oConfig.sStyle)
                        {
                                // add the additional styling. And ALWAYS ADD display:none!!
                                jQuery(oPopupNode).attr('style', this.oConfig.sStyle+';display:none;');
                        }
                       
                       
                        jQuery('body').append(oPopupNode);
                       
                        this.sContainerId = '#' + sContainerId;
                       
                        this.oContainer = jQuery(this.sContainerId);
                       
                        // install the content (if not done already)
                        // if this.oConfig.sContentId is given the DOM node with the given id will be placed in the popup,
                        // else the popup will make use of this.oConfig.sContent
                       
                        if (!this.oConfig.bLazyContentLoading)
                        {
                                this.installContent();
                        }
                       
                        if (this.oConfig.sTitle)
                        {
                                this.setTitle(this.oConfig.sTitle);
                        }
                       
                        if (this.oConfig.sButtonsHtml)
                        {
                                this.setButtons(this.oConfig.sButtonsHtml);
                        }
                       
                        // make the necessary arrangements for the overlay
                        Popup.initOverlay();
                       
                },
               
                /**
                 * Our entry point
                 */
                run: function()
                {
                        var oThis = this;
                       
                        // install the content (if not done already)
                        // if this.oConfig.sContentId is given the DOM node with the given id will be placed in the popup,
                        // else the popup will make use of this.oConfig.sContent
               
                        if (this.oConfig.bLazyContentLoading)
                        {
                                this.installContent();
                        }
                       
                        // wait for the content to load (images etc.), then do a callback to the given function
                        this.onContentLoaded(function()
                        {
                                // set the listeners
                                oThis.initObservers();
                               
                                // and open the popup
                                oThis.openPopup();
                               
                        }, this);
                       
                },
               
                installContent: function()
                {
                        var mContent = null;
                       
                        if (this.oConfig.sContentId)
                        {
                                if (typeof this.oConfig.sContentId == 'object')
                                {
                                        // sContentId is actually a DOM node
                                        mContent = jQuery(this.oConfig.sContentId);
                                }
                                else
                                {
                                        // sContentId is a string designating a DOM node-id
                                        mContent = jQuery('#' + this.oConfig.sContentId);
                                }
                               
                                mContent.css('display','block');
                        }
                        else
                        {
                                mContent = this.oConfig.sContent;
                        }
                       
                        this.setContent(mContent);
                },
               
                show: function(oThis)
                {
                        // Hi extenders! Make sure you do a callback to oThis.afterShow(oThis) when you're done animating
                        oThis.oContainer.css('z-index','20000');
                        oThis.center();
                        oThis.oContainer.fadeIn(400, oThis.afterShow(oThis));
                },
               
                hide: function(oThis)
                {
                        // Hi extenders! Make sure you do a callback to oThis.afterHide(oThis) when you're done animating
                        oThis.oContainer.hide(400, oThis.afterHide(oThis));
                },
               
                center: function()
                {      
                        var oContainer = this.oContainer;
                       
                        oContainer.css("position","absolute");
                        oContainer.css("top","-10000px");
                        oContainer.css("left","-10000px");
                       
                        oContainer.css("top", ( jQuery(window).height() - oContainer.height() ) / 2+jQuery(window).scrollTop() + "px");
                        oContainer.css("left", ( jQuery(window).width() - oContainer.width() ) / 2+jQuery(window).scrollLeft() + "px");
                },
               
                initHidingObserver: function()
                {
                        var oThis = this;
                       
                        // initialize observer
                        if (this.oConfig.sHideSelector !== null)
                        {
                               
                                // an empty string results in: The popup will close itself if it is clicked
                                var sHideString = this.oConfig.sHideSelector == '' ? this.sContainerId : this.oConfig.sHideSelector;
                               
                                jQuery(sHideString).bind('click', function(oEvent){
                                        oEvent.preventDefault();
                                        oThis.closePopup();
                                }, false);
                        }
                       
                        this.oContainer.find('.close-button').bind('click', function(oEvent){
                                oEvent.preventDefault();
                                oThis.closePopup();
                        }, false);
                       
                },
               
                initObservers: function()
                {
                        this.initHidingObserver();
                        this.initViewportObservers();
                },
               
                removeObservers: function()
                {
                        this.removeHidingObserver();
                        this.removeViewportObservers();
                },
               
                removeHidingObserver: function()
                {
                        if (this.oConfig.sHideSelector !== null)
                        {
                               
                                // an empty string results in: The popup will close itself if it is clicked
                                var sHideString = this.oConfig.sHideSelector == '' ? this.sContainerId : this.oConfig.sHideSelector;
                               
                                jQuery(sHideString).unbind('click');
                        }
                       
                        this.oContainer.find('.close-button').unbind('click');
                       
                },
               
                removeViewportObservers: function()
                {
                        // TODO
                },
               
                initViewportObservers: function()
                {
                        var oThis = this;
                       
                        jQuery(window).scroll(function() {
                                // allow for a hook (oConfig.fnOnMove)
                                var fnMove = typeof oThis.oConfig.fnOnMove == 'function' ? oThis.oConfig.fnOnMove : oThis.onMove;
                                fnMove(oThis);
                        });
                       
                        jQuery(window).resize(function() {
                                // allow for a hook (oConfig.fnOnMove)
                                var fnMove = typeof oThis.oConfig.fnOnMove == 'function' ? oThis.oConfig.fnOnMove : oThis.onMove;
                                fnMove(oThis);
                        });
                },
               
                openPopup: function()
                {
                        // tell the popup manager we are going to open a popup. It will display the overlay if necessary
                        Popup.notifyOpen(this);
                       
                        // allow for a hook (oConfig.fnShow)
                        var fnShowFunction = typeof this.oConfig.fnShow == 'function' ? this.oConfig.fnShow : this.show;
                        fnShowFunction(this);
                },
               
                closePopup: function()
                {
                        // tell the popup manager we are going to close a popup. It will hide the overlay if necessary
                        Popup.notifyClose(this);
                       
                        // allow for a hook (oConfig.fnHide)
                        var fnHideFunction = typeof this.oConfig.fnHide == 'function' ? this.oConfig.fnHide : this.hide;
                        fnHideFunction(this);
                       
                        // if the popup is not dependent of an triggering element, remove the DOM node
                        if (this.sTriggeringElSelector == null)
                        {
                                this.oContainer.remove();
                        }
                },
               
                onContentLoaded: function(fnCallback, oThis, iTimesToCall)
                {
                        if (typeof iTimesToCall == 'undefined') iTimesToCall = 10;
                       
                        if ((oThis.oContainer.find('.popup-content').height() != 0) || iTimesToCall <= 0)
                        {
                                fnCallback();
                        }
                        else
                        {
                                setTimeout(function() { oThis.onContentLoaded(fnCallback, oThis, iTimesToCall-1); }, 10);
                        }
                },
               
                setContent: function(mContent)
                {
                        var oContentContainer = this.oContainer.find('.popup-content');
                       
                        // install the content
                        oContentContainer.empty();
                        oContentContainer.append(mContent);
                       
                },
               
                loadSkin: function()
                {
                        if (this.oConfig.sSkinId)
                        {
                                // custom skin
                                return document.getElementById(this.oConfig.sSkinId);
                        }
                        else
                        {
                                // default skin
                                if (!Popup.oDefaultSkinNode)
                                {
                                        Popup.oDefaultSkinNode = jQuery(Popup.sDefaultSkin).appendTo('body').get(0);
                                        jQuery(Popup.oDefaultSkinNode).detach();
                                }
                               
                                return Popup.oDefaultSkinNode;
                        }
                       
                       
                },
               
                forceArray: function(mStringOrArray)
                {
                        if (typeof(mStringOrArray) == 'object')
                        {
                                return mStringOrArray;
                        }
                        else
                        {
                                return [mStringOrArray];
                        }
                },
               
                setTitle: function(sTitle)
                {
                        this.oContainer.find('.popup-title').html(sTitle);
                },
               
                setButtons: function(sButtonsHtml)
                {
                        this.oContainer.find('.popup-buttons').html(sButtonsHtml);
                },
               
                afterShow: function()
                {
                        // this function is called after the popup is shown
                },
               
                afterHide: function()
                {
                        // this function is called after the popup is hidden
                        this.removeObservers();
                },
               
                onMove: function(oThis)
                {
                        oThis.center();
                }
};


// static functions, these are the 'manager'-type functions

Popup.initOverlay = function()
{      
        if (Popup.oOverlay == null)
        {
                var sOverlayId = 'popupOverlay';
               
                if(document.getElementById(sOverlayId))
                {
                Popup.oOverlay = document.getElementById(sOverlayId);
                }
                else                          
                {    
                        Popup.oOverlay = document.createElement('div');
                        document.body.appendChild(Popup.oOverlay);
                }
               
                Popup.oOverlay.style.background = '#000';
                Popup.oOverlay.style.display = 'none';
                Popup.oOverlay.style.position = 'absolute';
                Popup.oOverlay.style.zIndex = "900";
               
               
                if (jQuery.browser.msie)
                {
                        Popup.oOverlay.style.filter = 'alpha(opacity=60)';
                }
                else
                {
                        Popup.oOverlay.style.opacity = 0.6;            
                }
        }
       
       
};

Popup.positionOverlay = function()
{
        // if active
        if (Popup.isActive())
        {
                Popup.oOverlay.style.top = jQuery(window).scrollTop() + "px";
                Popup.oOverlay.style.left = jQuery(window).scrollLeft() + "px";
               
                Popup.oOverlay.style.width = jQuery(window).width()+'px';
                Popup.oOverlay.style.height = jQuery(window).height()+'px';
        }
};

Popup.showOverlay = function()
{
        Popup.positionOverlay();
        jQuery(Popup.oOverlay).css({display: 'block'});
};

Popup.hideOverlay = function()
{
        jQuery(Popup.oOverlay).css({display: 'none'});
};

Popup.notifyOpen = function(oPopup)
{
        Popup.aStack.push(oPopup);
       
        if (Popup.iActivePopups++ == 0)
        {
                Popup.addObservers();
                Popup.showOverlay();
        }
};

Popup.notifyClose = function(oPopup)
{
        if (iIndex = Popup.aStack.indexOf(oPopup))
        {
                Popup.aStack.splice(iIndex, 1);
        }
       
       
        if (--Popup.iActivePopups <= 0)
        {
                Popup.iActivePopups = 0;
               
                this.hideOverlay();
                this.removeObservers();
        }
       
};

Popup.forceClose = function()
{
        if (Popup.aStack.length > 0)
        {
                var oPopup = Popup.aStack[Popup.aStack.length - 1];
               
                if (!oPopup.oConfig.bBlocking)
                {
                        oPopup.closePopup();
                }
        }
};

Popup.addObservers = function()
{
        jQuery(window).bind('scroll', Popup.moveHandler);
        jQuery(window).bind('resize', Popup.moveHandler);
        jQuery(Popup.oOverlay).bind('click', Popup.forceClose);
        jQuery(document).bind('keyup',function(e)
        {
                if (e.keyCode == 27)
                {
                        Popup.forceClose();
                }
        });
};

Popup.removeObservers = function()
{
        jQuery(window).unbind('scroll', Popup.moveHandler);
        jQuery(window).unbind('resize', Popup.moveHandler);
        jQuery(Popup.oOverlay).unbind('click');
        jQuery(document).unbind('keyup');
};

Popup.moveHandler = function()
{
        Popup.positionOverlay();
};

Popup.isActive = function()
{
        return (Popup.iActivePopups > 0);
};

/**
 * oPopupData can be either one of these:
 * 1. config. This config will be used to create the Popup. e.g. Popup.open( { sContent: 'test' } );
 * 2. new Popup(). An instance of a popup. e.g. Popup.open( new Popup({ sContent: 'test' }) );
 *
 * The second method has the advantage that one can instantiate subclasses of the Popup class, and still
 * open that popup with one command (Popup.open())
 */

Popup.open = function(oPopupData, bPreventClickEvent)
{
        if (typeof oPopupData != 'undefined')
        {
                if (typeof oPopupData.sBaseClass == 'string')
                {
                        if (oPopupData.sBaseClass == 'Popup')
                        {
                                // our parameter is actually a popup!
                                // run the thing...
                               
                                oPopupData.run();
                                return (typeof bPreventClickEvent == 'boolean' && bPreventClickEvent == true) ? false : oPopupData;
                        }
                }
        }
       
        var oPopup = new Popup(oPopupData);
        oPopup.run();
        return (typeof bPreventClickEvent == 'boolean' && bPreventClickEvent == true) ? false : oPopup;
       
};

Popup.grab = function(sPopupContentSelector, oPopupConfig)
{
        if (typeof oPopupConfig == 'undefined')
        {
                oPopupConfig = {};
        }
       
        var aPopups = [];
       
        jQuery(sPopupContentSelector).each(function(iCounter, oPopupContent)
        {
                var sIndex = oPopupContent.id ? oPopupContent.id : iCounter;
                oPopupConfig.sContentId = oPopupContent;
                aPopups[sIndex] = new Popup(oPopupConfig);
        });
       
        return aPopups;

};


/* ------------- EXTRA LIBRARIES ------------- */

/**
 * IE does not always support indexOf
 */

if(!Array.indexOf){
    Array.prototype.indexOf = function(obj){
        for(var i=0; i<this.length; i++){
            if(this[i]==obj){
                return i;
            }
        }
        return -1;
    }
}
