(function () {
    if (typeof mcd === 'undefined') {
        throw "mcd.dom and mcd.event are required libraries.";
    }

    /**
     * The MCD Obtrusive Overlay Library
     * 
     * Forces user interaction with a dialog box by darkening the page and 
     * displaying a standard overlay on top.
     * 
     * @namespace   mcd
     * @class       mcd.ObtrusiveOverlay
     * @author      Michael Girouard (mgirouard@mcdpartners.com)
     * @author      Michael T. Smith (msmith@mcdpartners.com)
     * @constructor
     */ 
    mcd.ObtrusiveOverlay = function (id) {
        
        this.iframe = document.createElement('iframe');
        this.iframe.setAttribute('id',id + '-shim');
        this.iframe.setAttribute('src', 'javascript:false');
        this.iframe.setAttribute('class', 'overlay-shim');
        this.iframe.frameBorder = '0';
        this.iframe.style.position = 'absolute';
        this.iframe.style.zIndex = '1';
        this.iframe.style.display = 'none';
        
        this.terminators = {};
        
        this.parent = document.getElementById(id);
        
        if (!this.parent) {
            throw 'Unable to initialize obtrusive overlay: ' + id;
        }
        
        mcd.event.add(id, 'click', function (event) {
            var target = mcd.event.getTarget(event);
                    
            if (this.terminators[id]) {
                while (target !== null) {
                    if (target.id in this.terminators[this.parent.id]) {
                        
                        mcd.event.preventDefault(event);
                        
                        if (this.terminators[this.parent.id][target.id](event)) {
                            this.hide();
                            
                            mcd.ObtrusiveOverlay.manager.hideBackdrop();
                        }
                        
                        break;
                    }
                    target = target.parentNode;
                }
            } else {
                // This branch is for overlays that do not have on-page triggers, and do not use the manager
                if (target.nodeName == "IMG") target = target.parentNode;
                if (this.terminators[target.id]) {
                    if (this.terminators[target.id](event)) {
                        this.hide();

                        mcd.ObtrusiveOverlay.manager.hideBackdrop();
                    }

                }
            }
        }, this);
        
        mcd.event.add(window, 'resize', function(event) {
			if (!mcd.dom.hasClass(document.body, 'in-obtrusive')) return;
			if (!document.getElementById('backdrop')) return;
			
			this.updatePosition();
		}, this);
    };
    
    mcd.ObtrusiveOverlay.prototype.updatePosition = function () {
		var viewportHeight = mcd.dom.getViewportHeight();
		var viewportWidth = mcd.dom.getViewportWidth();
					
		if ((viewportHeight < 600) || (viewportWidth < 900)) {
			if (navigator.userAgent.match(/KHTML/)) {
				// Safari needs to have HTML scrolling
				document.getElementsByTagName('HTML')[0].style.overflow = 'scroll';
			}
			else {
        		document.documentElement.style.overflow = 'scroll';
			}
			this.parent.style.top = 0;
			this.parent.style.left = 0;
			this.parent.style.marginLeft = 0;
			this.parent.style.marginTop = 0;
		}
		else {
			document.body.style.overflow = 'hidden';
			this.parent.style.top = '50%';
			this.parent.style.left = '50%';
			this.parent.style.marginLeft = '-' + (this.parent.offsetWidth / 2) + 'px';
			this.parent.style.marginTop  = '-' + (this.parent.offsetHeight / 2) + 'px';
    		document.documentElement.style.overflow = 'auto';
		}
		
		document.body.style.width = viewportWidth + 'px';
		document.body.style.height = viewportHeight + 'px';
		
		
	    document.getElementById('backdrop').style.height = document.body.scrollHeight + 'px';
	    document.getElementById('backdrop').style.width = document.body.scrollWidth + 'px';
    };
    
    /**
     * Shows an obtrusive overlay
     */
    mcd.ObtrusiveOverlay.prototype.show = function (el) {
        if (this.beforeShow) this.beforeShow(el);
        
        mcd.dom.removeClass(this.parent, 'hide');
        if (this.iframe){
            this.iframe.style.display = '';
        }

        // this.updatePosition();
        
        if (this.afterShow) this.afterShow();   
    };
    
    /**
     * Hides an obtrusive overlay
     */
    mcd.ObtrusiveOverlay.prototype.hide = function () {
        if (this.beforeHide) this.beforeHide();
        
        mcd.dom.addClass(this.parent, 'hide');
        if (this.iframe){
            this.iframe.style.display = 'none';
        }
        
        if (this.afterHide) this.afterHide();
    };
    
    /**
     * Toggles the visibility of an obtrusive overlay
     */
    mcd.ObtrusiveOverlay.prototype.toggle = function () {
        mcd.dom.toggleClass(this.parent, 'hide');
        if (this.iframe){
            this.iframe.style.display = this.iframe.style.display == '' ? 'none' : '';
        }
    };
    
    /**
     * @deprecated
     */
    mcd.ObtrusiveOverlay.prototype.attachOverlay = function (parent,yx) {
        var div = mcd.dom.getElement(parent);
        div.appendChild(this.parent);
        if (this.iframe){
            div.appendChild(this.iframe);
        }
    };
    
    /**
     * @deprecated
     */
    mcd.ObtrusiveOverlay.prototype.removeOverlay = function (parent) {
        var div = mcd.dom.getElement(parent);
        div.removeChild(this.parent)
        if (this.iframe){
            div.removeChild(this.iframe)
        }
    };
    
    /**
     * Registers a terminator to an overlay instace
     * 
     * @param {String} id 
     * @param {Function} callback 
     */
    mcd.ObtrusiveOverlay.prototype.registerTerminator = function (id, callback) {
        this.terminators[id] = callback;
    };
    
    /**
     * The MCD Obtrusive Overlay Manager
     * 
     * @namespace   mcd.ObtrusiveOverlay
     * @class       mcd.ObtrusiveOverlay.manager
     * @author      Michael Girouar (mgirouard@mcdpartners.com)
     * @author      Michael T. Smith (msmith@mcdpartners.com)
     * @constructor
     */
    mcd.ObtrusiveOverlay.manager = {
        overlays : {},
        
        init : function (terminators) {
            var triggerList = mcd.dom.getElementsByAttribute('class', 'obtrusive-trigger', document.body, 'a', true);
            
            
            for (var i = 0; i < triggerList.length; i++) {
                var id = triggerList[i].getAttribute('rel');
                var nodeName = triggerList[i].nodeName.toLowerCase();
                
                if (id) {
                    if (!(id in this.overlays)) {
                        this.overlays[id] = new mcd.ObtrusiveOverlay(id);
                        
                        var terminatorName;
                        for (terminatorName in terminators) {
                            this.overlays[id].registerTerminator(terminatorName, terminators[terminatorName]);
                        }
                    }
                        
                        if (nodeName === 'a') {
                            mcd.event.add(triggerList[i], 'click', function (event) {
                                
                                var rel = this.getAttribute('rel');
                                var overlayParent = mcd.dom.getElement(rel).parentNode;
                                var xy  = mcd.dom.getPosition(this);
                                
                                
                                if (rel) {
                                    
                                    mcd.ObtrusiveOverlay.manager.getOverlay(rel).activator = this;
                                    
                                    mcd.event.preventDefault(event);
                                    
                                    if (!mcd.dom.getElement('backdrop')) {
                                        mcd.ObtrusiveOverlay.manager.createBackdrop();
                                    }
                                    else {
                                        mcd.ObtrusiveOverlay.manager.showBackdrop();
                                    }
        
                                    mcd.ObtrusiveOverlay.manager.getOverlay(rel).toggle();
                                    mcd.ObtrusiveOverlay.manager.getOverlay(rel).iframe.style.display = 'block';
                                    mcd.ObtrusiveOverlay.manager.getOverlay(rel).iframe.style.top    = mcd.ObtrusiveOverlay.manager.getOverlay(rel).parent.offsetTop + 'px';
                                    mcd.ObtrusiveOverlay.manager.getOverlay(rel).iframe.style.left   = mcd.ObtrusiveOverlay.manager.getOverlay(rel).parent.offsetLeft + 'px';
                                    mcd.ObtrusiveOverlay.manager.getOverlay(rel).iframe.style.width  = mcd.ObtrusiveOverlay.manager.getOverlay(rel).parent.offsetWidth + 'px';
                                    mcd.ObtrusiveOverlay.manager.getOverlay(rel).iframe.style.height = mcd.ObtrusiveOverlay.manager.getOverlay(rel).parent.offsetHeight + 'px';
                                    overlayParent.appendChild(mcd.ObtrusiveOverlay.manager.getOverlay(rel).iframe);
                                };
                            });
                        }
                }
            }
            
            return this;
        },
        
        /**
         * Creates the backdrop element
         */
        createBackdrop : function () {
            mcd.dom.addClass(document.getElementsByTagName('html')[0], 'in-obtrusive');
            mcd.dom.addClass(document.body, 'in-obtrusive');
            this.backdrop = document.createElement('div');
            this.backdrop.className = 'show';
            window.scrollTo(0,0);
            this.backdrop.setAttribute('id', 'backdrop');
            document.body.appendChild(this.backdrop);
        },
        
        /**
         * Shows the backdrop element
         */
        showBackdrop : function () {
            mcd.dom.addClass(document.getElementsByTagName('html')[0], 'in-obtrusive');
            mcd.dom.addClass(document.body, 'in-obtrusive');
            window.scrollTo(0,0);
            var backdrop = mcd.dom.getElement('backdrop');
            mcd.dom.toggleClass(backdrop, 'show');
        },
        
        /**
         * Toggles the backdrop element and creates it if necessary
         */
        displayBackdrop : function () {
            mcd.dom.addClass(document.getElementsByTagName('html')[0], 'in-obtrusive');
            mcd.dom.addClass(document.body, 'in-obtrusive');
            if (!document.getElementById('backdrop')){
                // No backdrop exists; create it.
                this.backdrop = document.createElement('div');
                this.backdrop.className = 'show';
                this.backdrop.setAttribute('id', 'backdrop');
                document.body.appendChild(this.backdrop);
            } 
            else {
                this.backdrop = mcd.dom.getElement('backdrop');
                mcd.dom.addClass(this.backdrop, 'show');
            }
            window.scrollTo(0,0);
            
        },
        
        /**
         * Hide the backdrop element
         */
        hideBackdrop : function () {
            mcd.dom.removeClass(document.getElementsByTagName('html')[0], 'in-obtrusive');
            mcd.dom.removeClass(document.body, 'in-obtrusive');
            var backdrop = mcd.dom.getElement('backdrop');
            if (backdrop) mcd.dom.removeClass(backdrop, 'show');
        },
    
        /**
         * Gets an overlay instance
         */
        getOverlay : function (id) {
            if (id in this.overlays) {
                return this.overlays[id];
            }
        },
        
        /**
         * Exits from an obtrusive state
         */
        exitObtrusive : function () {
            for (var overlay in this.overlays) {
                mcd.ObtrusiveOverlay.manager.getOverlay(overlay).hide();
                mcd.ObtrusiveOverlay.manager.hideBackdrop();
            }
        },
        
        /**
         * Enters into an obtrusive state
         * 
         * @param {String} id [, id...]
         */
        enterObtrusive : function () {
            for (var i = 0; i < arguments.length; i++) {
                var overlay = arguments[i];
                
                if(overlay in mcd.ObtrusiveOverlay.manager.overlays) {
                    mcd.ObtrusiveOverlay.manager.getOverlay(overlay).show();
                    mcd.ObtrusiveOverlay.manager.displayBackdrop();
                }
            }
        }
    };
})();