/*
 * jQuery Co3 Modal Window
 *
 * Version 1.0.8
 * 20. september 2011
 *
 * Co3 Web Agency
 * www.co3.dk
 *
 * Developer
 * Jesper Holm Damgaard
 * jd@co3.dk
 * Rune Øllgaard Grønkjær
 * rg@co3.dk
 *
 * Created with jQuery v1.4.2
 * Tested with jQuery v1.6.3
 *
 * Usage:
 * //Shows the selected elements as modal popups in front of a background content block
 * jQuery('#selector').Co3ModalWindowOpen(settings);
 *
 * Default settings:
 *		onOpen: function() {},// The modal content is sent as a parameter
 *		onOpened: function() {},// The modal content is sent as a parameter
 *		onClose: function() {},// The modal content is sent as a parameter
 *		onClosed: function() {},// The modal content is sent as a parameter
 *		closeOnBlockClick: true,
 *		closeModalSelector: '',
 *		addCloseButton: false,
 *		animateTime: 500,
 *		background: '#000',
 *		opacity: 0.8,
 *		zIndex: 999,
 *		minDistanceToViewport: 0,
 *		fixedTop: null,
 *		fixedLeft: null
 *
 * Changelog:
 * Version v1.0:
 * Created: 21. june 2010
 *
 * Version v1.0.1:
 * Created: 28. june 2010
 * Fixed modal position bug in IE6
 *
 * Version v1.0.2:
 * Created: 2. july 2010
 * Fixed problem with the onClosed event firering twice.
 *
 * Version v1.0.3:
 * Created: 11. marts 2011
 * Fixed problem with firefox reloading flash content on modal resize.
 *
 * Version v1.0.4:
 * Created: 28. marts 2011
 * Fixed problem with jQuery(line 7909) assigning invalid arguments to top and left css props when animating.
 *
 * Version v1.0.5:
 * Created: 29. marts 2011
 * Add the stop() function before animating modal appearance props, and raised resize control timeout from 100 to 500 milisecs
 *
 * Version v1.0.6:
 * Created: 6. june 2011
 * Workaround for incorrect return value for initial display prop in IE
 *
 * Version v1.0.7:
 * Created: 20. september 2011
 * New functions for handling initial hidden content
 *
 * Version v1.0.8:
 * Created: 28. september 2011
 * New settings 'closeModalSelector' and 'addCloseButton' added and modalContent positioning css trimmed
 *
 */

(function(jQuery) {
	/*
	 * Constructor for the Co3ModalWindow class.
	 * initiates the modal window.
	 */
	jQuery.Co3ModalWindow = function(modalContent, settings) {
		var base = this;
		base.init = function() {
			base.settings = jQuery.extend({}, jQuery.Co3ModalWindow.defaultSettings, settings);
			//The onOpen event is fired
			base.settings.onOpen(modalContent);
			base.blockID = new Date().getTime();
			base.jQueryModalContent = jQuery(modalContent);
			base.insertMarker();
			base.buildMarkup();
			base.updateModalAppearance();
			base.showModal();
			base.aboutToResize = null;
			base.resizeControl = true;
			base.block.data('Co3ModalWindowBase', base);
			jQuery(window).bind('resize', function() {
				base.resizeControlFunction();
			});
		};
		/*
		 * Deconstructs the modal window.
		 * Will reset the location and css settings of the modal content.
		 */
		base.deconstruct = function() {
			// Fire the onClose event
			base.settings.onClose(modalContent);
			// If IE6 the overflow of the html element must be reset.
			if (jQuery.browser.msie && parseInt(jQuery.browser.version, 10) === 6) {
				jQuery('html').css(jQuery('html').data('overflowcontrol'));
			}

			// Animate the modal window and modal background block to opacity 0
			base.modal.add(base.background).animate({
				opacity: 0
			}, base.settings.animateTime, null, function() {
				// Remove filter attribute to render the fonts correctly in IE
				if (!jQuery.support.opacity && this.style.filter) {
					try {
						this.style.removeAttribute('filter');
					}
					catch (ex) {}
				}
				// Unsubscribe the click event from
				// the element(s) specified in the settings.
				if (base.settings.closeModalSelector !== '') {
					jQuery('#_block_' + base.blockID + ' ' + base.settings.closeModalSelector).die('click');
				}
				// If hidden onOpen hide it again
				if(base.jQueryModalContent.data('isHidden') === 'true'){
					base.jQueryModalContent.hide();
				}
				// Move the modal content back to it's marker and reset the contents css.
				jQuery('#_block_marker_' + base.blockID).replaceWith(base.jQueryModalContent.css(base.jQueryModalContent.data('Co3ModalWindowCssShow'))); 
				// Remove the modal block wich envelopes both modal window and modal background block
				base.block.remove();
				if (jQuery(this).is('._block_modal')) {
					// Fire the onClosed event
					base.settings.onClosed(modalContent);
				}
			});
		};
		/*
		 * Displays the modal window and the modal background block
		 */
		base.showModal = function() {
			// Animate the modal window to opacity 1
			base.modal.animate({
				opacity: 1
			}, base.settings.animateTime, null, function() {
				// Remove filter attribute to render the fonts correctly in IE
				if (!jQuery.support.opacity && this.style.filter) {
					try {
						this.style.removeAttribute('filter');
					}
					catch (ex) {}
				}
				// Subscribe the modal window close method to the click event
				// of the modal background block.
				if (base.settings.closeOnBlockClick) {
					base.background.bind('click', function() {
						jQuery(this).Co3ModalWindowClose();
					});
				}
				// Subscribe the modal window close method to the click event
				// of the element(s) of the selector specified in the settings.
				if (base.settings.closeModalSelector !== '') {
					jQuery('#_block_' + base.blockID + ' ' + base.settings.closeModalSelector).live('click', function() {
						jQuery(this).Co3ModalWindowClose();
					});
				}
				// Fire the onOpened event
				base.settings.onOpened(modalContent);
			});
			// Animate the modal background block to opacity 1
			base.background.animate({
				opacity: base.settings.opacity
			}, base.settings.animateTime);
		};
		/*
		 * Inserts a marker after the modal content to remember
		 * it's original position
		 */
		base.insertMarker = function() {
			// Insert a hidden hr in where the modal content is currently placed in the DOM
			base.jQueryModalContent.after(jQuery('<hr>').css({
				display: 'none'
			}).attr('id', '_block_marker_' + base.blockID));
		};
		/*
		 * If the modal content is somehow hidden, this method shows the content.
		 * It will save the original settings of the content, to be reenabled
		 * when the modal window is closed.
		 */
		base.hiddenModalContentDetection = function() {
			// Create an object to contain all default css settings of the modal content.
			var Co3ModalWindowCssShow = {};
			// If hidden, save display prop to apply on hide
			if (base.jQueryModalContent.css('display') === 'none') {
				Co3ModalWindowCssShow.display = 'none';
			}
			// Set the modal content to visibility visible and save the old value
			if (base.jQueryModalContent.css('visibility') === 'hidden') {
				Co3ModalWindowCssShow.visibility = base.jQueryModalContent.css('visibility');
			}
			// Save the old opacity value
			if (base.jQueryModalContent.css('opacity') === '0') {
				Co3ModalWindowCssShow.opacity = base.jQueryModalContent.css('opacity');
			}
			// Save the old position values
			if (base.jQueryModalContent.css('position') !== 'static') {
				Co3ModalWindowCssShow.position = base.jQueryModalContent.css('position');
				/*
				Co3ModalWindowCssShow.left = base.jQueryModalContent.css('left');
				Co3ModalWindowCssShow.top = base.jQueryModalContent.css('top');
				Co3ModalWindowCssShow.right = base.jQueryModalContent.css('right');
				Co3ModalWindowCssShow.bottom = base.jQueryModalContent.css('bottom');
				*/
			}
			// Save the modal contents default css settings in its own data.
			base.jQueryModalContent.data('Co3ModalWindowCssShow', Co3ModalWindowCssShow);
			if(Co3ModalWindowCssShow.display !== 'none' && Co3ModalWindowCssShow.visibility !== 'hidden' && Co3ModalWindowCssShow.opacity !== '0' && base.jQueryModalContent.is(':hidden')){
				base.jQueryModalContent.data('isHidden','true');
			}
			else{
				base.jQueryModalContent.data('isHidden','false');			
			}
		};
		/*
		 * If the modal content is somehow hidden, this method shows the content.
		 */
		base.hiddenModalContentShow = function() {
			if(typeof base.jQueryModalContent.data('Co3ModalWindowCssShow') !== 'object'){
				return false;
			}
			// If hidden, show the modal content
			if (base.jQueryModalContent.data('isHidden') === 'true') {
				base.jQueryModalContent.show();
			}
			if (base.jQueryModalContent.data('Co3ModalWindowCssShow').display === 'none') {
				base.jQueryModalContent.css('display', 'block');
			}
			// Set the modal content to visibility visible
			if (base.jQueryModalContent.data('Co3ModalWindowCssShow').visibility === 'hidden') {
				base.jQueryModalContent.css('visibility', 'visible');
			}
			// Set the modal content to opacity 1
			if (base.jQueryModalContent.data('Co3ModalWindowCssShow').opacity === '0') {
				base.jQueryModalContent.css('opacity', '1');
				if (!jQuery.support.opacity && base.jQueryModalContent[0].style.filter) {
					try {
						base.jQueryModalContent[0].style.removeAttribute('filter');
					}
					catch (ex) {}
				}
			}
			// Set the modal content to position static and 0
			if (base.jQueryModalContent.data('Co3ModalWindowCssShow').position !== 'static') {
				base.jQueryModalContent.css({
					position: 'static'
					/*,
					left: 0,
					top: 0,
					right: 0,
					bottom: 0
					*/
				});
			}
		};
		/*
		 * Builds the markup for the modal window and the modal background block.
		 */
		base.buildMarkup = function() {
			// Build the block wrapper html element
			base.block = jQuery('<div></div>').addClass('_block').attr('id', '_block_' + base.blockID).css({
				position: 'fixed',
				left: 0,
				top: 0,
				width: '100%',
				height: '100%',
				zIndex: base.settings.zIndex
			});
			if (jQuery.browser.msie && parseInt(jQuery.browser.version, 10) === 6) {
				// Remove the scrollbars in IE6
				jQuery('html').data('overflowcontrol', {
					'overflow-x': jQuery('html').css('overflow-x'),
					'overflow-y': jQuery('html').css('overflow-y')
				}).css({
					'overflow-x': 'hidden',
					'overflow-y': 'hidden'
				});
			}
			// Create the modal background block element
			base.background = jQuery('<div></div>').addClass('_block_background').css({
				position: 'absolute',
				left: 0,
				top: 0,
				width: '100%',
				height: '100%',
				opacity: 0,
				background: base.settings.background
			});
			base.hiddenModalContentDetection();
			// Create the modal window element and append the modal content
			base.modal = jQuery('<div></div>').addClass('_block_modal').css({
				position: 'absolute',
				opacity: 0
			}).append(base.jQueryModalContent);
			// Create and add a basic close button to modal according to settings
			if(base.settings.addCloseButton){
				jQuery('<button>&#215;</button>').addClass('_close_modal').bind('click',function(){
					jQuery(this).Co3ModalWindowClose();
				}).appendTo(base.modal);
			}
			base.hiddenModalContentShow();
			// Append the modal block wrapper to the body of the document
			base.block.append(base.background).append(base.modal).appendTo('body');
		};
		/*
		 * Sets the size and position of the modal window.
		 */
		base.updateModalAppearance = function(animate) {
			// Calculate the width's and height's of the modal window
			var modalContentHeight = base.jQueryModalContent.outerHeight(true),
				modalContentWidth = base.jQueryModalContent.outerWidth(true),
				availableViewportHeight = (((window.innerHeight) ? window.innerHeight : jQuery(window).height()) - ((base.settings.fixedTop === null) ? 0 : base.settings.fixedTop)) - (base.settings.minDistanceToViewport * ((base.settings.fixedTop === null) ? 2 : 1)),
				availableViewportWidth = (jQuery(window).width() - ((base.settings.fixedLeft === null) ? 0 : base.settings.fixedLeft)) - (base.settings.minDistanceToViewport * ((base.settings.fixedLeft === null) ? 2 : 1)),
				heightExceeded = (modalContentHeight > availableViewportHeight),
				widthExceeded = (modalContentWidth > availableViewportWidth),
				calculatedModalWidth = ((widthExceeded) ? availableViewportWidth : modalContentWidth),
				calculatedModalHeight = ((heightExceeded) ? availableViewportHeight : modalContentHeight),
				scrollBarCorectionAmount = jQuery.Co3ModalWindow.getScrollBarWidth();
			if (heightExceeded) {
					calculatedModalHeight = availableViewportHeight;
					calculatedModalWidth = calculatedModalWidth + scrollBarCorectionAmount;
					widthExceeded = calculatedModalWidth > availableViewportWidth;
				}
			if (widthExceeded) {
					calculatedModalWidth = availableViewportWidth;
					if (!heightExceeded) {
						calculatedModalHeight = calculatedModalHeight + scrollBarCorectionAmount;
						heightExceeded = calculatedModalHeight > availableViewportHeight;
						if (heightExceeded) {
							calculatedModalHeight = availableViewportHeight;
						}
					}
				}
			// Create a css objects for the modal window
			var appearanceCss = {
					left: ((base.settings.fixedLeft === null) ? ((widthExceeded) ? base.settings.minDistanceToViewport : '50%') : base.settings.fixedLeft),
					top: ((base.settings.fixedTop === null) ? ((heightExceeded) ? base.settings.minDistanceToViewport : '50%') : base.settings.fixedTop),
					overflow: ((heightExceeded || widthExceeded) ? 'auto' : 'hidden')
				};
			// css object suited for animation
			var appearanceAnimateCss = {
					width: calculatedModalWidth,
					height: calculatedModalHeight,
					marginLeft: ((base.settings.fixedLeft === null && !widthExceeded) ? calculatedModalWidth / 2 * -1 : 0),
					marginTop: ((base.settings.fixedTop === null && !heightExceeded) ? calculatedModalHeight / 2 * -1 : 0)
				};
			// If the height or width of the modal content exeeds that of the
			// modal window the overflow of the modal window is set to auto.
			// This is done to create the scroll posibility to let the user
			// see all content.
			// After that the modal window is animated to it's new size
			base.modal.stop().css(appearanceCss).animate(appearanceAnimateCss, base.settings.animateTime / 2);
			// IE6 cannot handle position fixed, so instead we use position absolute
			if (jQuery.browser.msie && parseInt(jQuery.browser.version, 10) === 6) {
					base.block.css({
						position: 'absolute',
						height: jQuery(window).height(),
						top: jQuery('html').scrollTop()
					});
				}
		};
		base.resizeControlFunction = function() {
			if (base.resizeControl) {
				clearTimeout(base.aboutToResize);
				base.aboutToResize = setTimeout(function() {
					base.updateModalAppearance();
					base.aboutToResize = null;
				}, 500);
			}
		};
		base.init();
	};
	/*
	 * Default settings for the module.
	 * Is used if the user does not specify the settings
	 */
	jQuery.Co3ModalWindow.defaultSettings = {
		onOpen: function() {},// The modal content is sent as a parameter
		onOpened: function() {},// The modal content is sent as a parameter
		onClose: function() {},// The modal content is sent as a parameter
		onClosed: function() {},// The modal content is sent as a parameter
		closeOnBlockClick: true,
		closeModalSelector: '',
		addCloseButton: false,
		animateTime: 500,
		background: '#000',
		opacity: 0.8,
		zIndex: 999,
		minDistanceToViewport: 0,
		fixedTop: null,
		fixedLeft: null
	};
	/*
	 * Calculates the width of the browsers scrollbar.
	 */
	jQuery.Co3ModalWindow.getScrollBarWidth = function() {
		// create a div from wich to calculate the scrollbar width
		var scrollBarParent = jQuery('<div></div>').css({
			width: 50,
			height: 50,
			overflow: 'auto',
			position: 'absolute',
			top: 0,
			left: 0
		}).appendTo('body'),
			scrollBarChild = jQuery('<div></div>').appendTo(scrollBarParent),
			scrollBarWidth = scrollBarChild.innerWidth() - scrollBarChild.css({
				height: 500
			}).innerWidth();
		scrollBarParent.remove();
		return scrollBarWidth;
	};
	/*
	 * Åbner et modal vindue hvis indhold er det selectede jQuery element.
	 * Der kan kun vælges og åbnes et element. Skal der åbnes flere må man
	 * wrappe dem alle i en container.
	 */
	jQuery.fn.Co3ModalWindowOpen = function(settings) {
		var Co3ModalWindow = new jQuery.Co3ModalWindow(this[0], settings); //tager kun det første i en eventuel række
		return this;
	};
	/*
	 * Lukker et modal vindue.
	 * Denne funktion kan kaldes på ALLE elementer inde i modal vinduet.
	 */
	jQuery.fn.Co3ModalWindowClose = function() {
		this.closest('._block').data('Co3ModalWindowBase').deconstruct(); //find block og luk den
		return this;
	};
	/*
	 * Opdaterer positionen af modal vinduet.
	 * Denne funktion kan kaldes på ALLE elementer inde i modal vinduet.
	 */
	jQuery.fn.Co3ModalWindowUpdate = function() {
		this.closest('._block').data('Co3ModalWindowBase').updateModalAppearance(); //find block og opdater size og pos
		return this;
	};
})(jQuery);