﻿(function ($) {
	$.fn.modal = function (options) {
		var _self = this;
		var _modal, _mask, _btnClose, _windows = null;

		// Remember if this is IE6 so we can hide windowed elements
		// Alternatively, could explore using an iframe layer as the mask
		var _isIE6 = (-1 < window.navigator.appVersion.indexOf('MSIE 6'));

		// Default options
		var opt = {
			closeHandler: null,
			openHandler: null,
			selectorForPosition: null,
			horizPosition: 'left',
			vertPosition: 'top',
			horizOffset: 0,
			vertOffset: 0,
			fadeIn: 600,
			fadeOut: 300,
			mask: true,
			modalStyle: { display: 'none', position: 'absolute', zIndex: 10002, backgroundColor: 'transparent' },
			innerMaskStyle: { width: '100%', height: '100%', backgroundColor: '#000', opacity: '.75', filter: 'progid:DXImageTransform.Microsoft.Alpha(opacity=75)' },
			outerMaskStyle: { width: '100%', top: 0, left: 0, position: (_isIE6 ? 'absolute' : 'fixed'), zIndex: 10000 }
		};
		$.extend(opt, options);

		// Init content style for dimensioning and construct modal elements
		_self.css({ visibility: 'hidden', display: 'block' });
		_buildModal();

		//
		// Generates modal dom dependency elements
		//
		function _buildModal() {

			// Capture windowed elements for IE6
			if (_isIE6) {
				_windows = $('select, iframe').not(_self.find("select, iframe"));
			}

			// Create the modal wrapper
			_modal = $('<div class="modal_outer"><div class="modal_inner"></div></div>').css(opt.modalStyle).appendTo($('body'));

			// Create the mask
			// Requires outer/inner elements for transparency to work correctly in all browsers
			if (opt.mask) {
				_mask = $('<div class="modal_mask_outer"><div class="modal_mask_inner"></div></div>').css(opt.outerMaskStyle).appendTo($('body'));
				$('.modal_mask_inner', _mask).css(opt.innerMaskStyle);
			}
		}

		//
		// Detaches content from modal and removes modal elements from the DOM
		//
		function _destroy() {

			// Hide content and move it to body so it isn't lost
			_self.hide();
			$('body').append(_self);

			// Remove modal elements we created
			if (_modal) {
				_modal.remove();
			}
			if (_mask) {
				_mask.remove();
			}
		}

		//
		// Keeps the modal in the center of the window pane.  Optionally enforces
		// vertical centering.
		//
		function _positionModal(vertically) {
			var css;
			if (opt.selectorForPosition) {
				var relWidth = $(opt.selectorForPosition).width();
				var relHeight = $(opt.selectorForPosition).height();
				var relLeft = $(opt.selectorForPosition).position().left;
				var relTop = $(opt.selectorForPosition).position().top;
				var posLeft;
				var posTop;
				if ('left' == opt.horizPosition && 'top' == opt.vertPosition) {
					posLeft = (relLeft + opt.horizOffset) + 'px';
					posTop = (relTop + opt.vertOffset) + 'px';
				}
				else if ('right' == opt.horizPosition && 'top' == opt.vertPosition) {
					posLeft = (relLeft + relWidth + opt.horizOffset) + 'px';
					posTop = (relTop + opt.vertOffset) + 'px';
				}
				else if ('left' == opt.horizPosition && 'bottom' == opt.vertPosition) {
					posLeft = (relLeft + opt.horizOffset) + 'px';
					posTop = (relTop + relHeight + opt.vertOffset) + 'px';
				}
				else if ('right' == opt.horizPosition && 'bottom' == opt.vertPosition) {
					posLeft = (relLeft + relWidth + opt.horizOffset) + 'px';
					posTop = (relTop + relHeight + opt.vertOffset) + 'px';
				}

				css = {
					left: posLeft,
					top: posTop
				}

			}
			else {
				var pane = $(window);
				css = {
					left: (((pane.width() - _modal.outerWidth()) / 2) + pane.scrollLeft()) + 'px'
				}
				if (true === vertically) {
					css.top = (((pane.height() - _modal.outerHeight()) / 2) + pane.scrollTop()) + 'px'
				}
			}

			_modal.css(css);
		}

		//
		// Ensures mask always covers entire page
		//
		function _sizeMask() {
			_mask.css({ height: $(document).height() + 'px' });
		}

		//
		// Hides/shows windowed elements (for IE6)
		//
		function _setWindowedElements(visible) {
			if (_windows) {
				visible ? _windows.show() : _windows.hide();
			}
		}

		//
		// Closes modal when ESC is pressed
		//
		function _onKeyDown(e) {
			if (27 == e.which) {
				_close();
			}
		}

		function _closeHoverOver() {
			$(this).addClass('modal_close_hover');
		}
		function _closeHoverOut() {
			$(this).removeClass('modal_close_hover');
		}

		//
		// Open the modal window.
		//
		function _open() {

			// If a handler was provided, call the handler.  If the handler returns true, stop execution.
			if (opt.openHandler) {
				if (!opt.openHandler.call(this, _modal)) {
					return;
				}
			}

			// Position the modal
			_positionModal(true);

			// Hide windowed elements that aren't in the modal
			_setWindowedElements(false);

			// Show the modal and content
			_self.css('visibility', 'visible').show();
			if (opt.fadeIn) {
				_modal.fadeIn(opt.fadeIn);
			} else {
				_modal.show();
			}

			// Show the mask
			if (_mask) {
				$(window).bind('resize', _sizeMask);
				_sizeMask();
				if (opt.fadeIn) {
					_mask.fadeIn(opt.fadeIn);
				} else {
					_mask.show();
				}
			}

			// Bind events
			$(window).bind('resize', _positionModal);
			$('.modal_close', _modal).click(_close).hover(_closeHoverOver, _closeHoverOut);
			$(document).keydown(_onKeyDown);
		}

		//
		// Close the modal window
		//
		function _close() {

			// If a handler was provided, call the handler.  If the handler returns true, stop execution.
			if (opt.closeHandler) {
				if (true == opt.closeHandler.call(this)) {
					return;
				}
			}

			// Stop listening to events
			$(window).unbind('resize', _positionModal);
			$('.modal_close', _modal)
				.unbind('click', _close)
				.unbind('mouseover', _closeHoverOver)
				.unbind('mouseout', _closeHoverOut);
			$(document).unbind('keydown', _onKeyDown);

			// Hide the modal
			if (opt.fadeOut) {
				_modal.fadeOut(opt.fadeOut);
			} else {
				_modal.hide();
			}

			// Hide the mask
			if (_mask) {
				$(window).unbind('resize', _sizeMask);
				if (opt.fadeOut) {
					_mask.fadeOut(opt.fadeOut);
				} else {
					_mask.hide();
				}
			}

			// Restore windowed elements and remove dom elements we created
			setTimeout(function () {
				_setWindowedElements(true);
				_destroy();
			}, opt.fadeOut + 100);
		}

		// Move content into modal and show
		$('.modal_inner', _modal).append(this);
		_open();

		return this;
	}
})(jQuery);
