﻿/*
 * Popup - jQuery plugin 1.1
 * Copyright (c) 2011 Copenhagen Software ApS
 *
 * Slides in a popup from a predetermined position. Can be used for more than one popup on the same page.
 * Only one popup can be shown at a time!
 * 
 * It needs the folowing structure to exist in the DOM:
 *	<div id="popup_background"></div>
 *	<div id="global_popup">
 *		<div class="close"></div>
 *		<div id="global_popup_content">
 *			<div id="global_popup_loading" style="display:none;">text</div>
 *			<div id="global_popup_error" style="display:none;">text</div>
 *		</div>
 *	</div>
 *
 * Functions:
 * $(selector).popup(); - prepares (moves) the selector to the popup area.
 * $(selector).popup("show", [onShown], [onHidden], [width]); - shows the popup with the selector content. callbacks are called when animations are finished.
 * $(selector).popup("hide", [callback]); - hides the popup. callback is called when animation is finished. This function is called before onHidden.
 * jQuery.fn.popup("hide", [callback]); - hides the popup. callback is called when animation is finished. This function is called before onHidden.
 * jQuery.fn.popup("showAjax", url, [data], [onData(container)], [onShown], [onHidden], [width]); - Shows a popup with loading animation. An ajax call is made to url.
 * Data is automatically inserted into the popup and onData is called with jQuery container for the popup data.
 * jQuery.fn.popup("updateBg"); - tell the background to resize itselves to fit the content.
 */

(function ($) {
	// Constants
	var POPUP_DIV_ID = "#global_popup";
	var POPUP_CONTENT_DIV_ID = "#global_popup_content";
	var POPUP_BACKGROUND_DIV_ID = "#popup_background";
	var POPUP_LOADING_DIV_ID = "#global_popup_loading";
	var POPUP_ERROR_DIV_ID = "#global_popup_error";

	// Attribute for the currently shown popup.
	var onHiddenCallback;
	var ajaxContainer;

	var hideCurrent = function (callback) {
		// Remove the pop-up close button handler.
		$(POPUP_DIV_ID).find(".close").unbind("click");

		// Fade out background.
		$(POPUP_BACKGROUND_DIV_ID).fadeOut("fast");

		// Call both callbacks if they are defined.
		function hideCallbacks() {
			if (typeof (callback) === "function") {
				callback();
			}

			if (typeof (onHiddenCallback) === "function") {
				onHiddenCallback();
				onHiddenCallback = undefined;
			}

			// Destroy any data received from ajax.
			if (ajaxContainer) {
				ajaxContainer.remove();
			}

			// TODO: We might need to form validate here!
			//jQuery.validator.unobtrusive.parse(document);
		}

		// Slide out popup and when done, call callback.
		$(POPUP_DIV_ID).hide("blind", hideCallbacks, 400);
	};

	var initShow = function (width, onHidden) {
		if (!width) {
			width = 950;
		}

		// Set the size of the popup
		if (width > 1200 || width < 30) {
			throw "popup.init: specified width is out of bounds (30-1200)";
		}

		// Set width and left (to center).
		var left = (1200 - width) / 2;
		$(POPUP_DIV_ID).css({ "width": width, "left": left });

		// Store onHidden callback
		onHiddenCallback = onHidden;

		// Bind the pop-up close button.
		$(POPUP_DIV_ID).find(".close").click(hideCurrent);

		// Show only the selected content.
		$(POPUP_CONTENT_DIV_ID).children().hide();
	};

	var showCommon = function (showFunction) {
		if ($(POPUP_CONTENT_DIV_ID + ":visible").length > 0) {
			hideCurrent(showFunction);
		}
		else {
			showFunction();
		}
	};

	var updateBackground = function () {
		// Reset the bg height, so that it doesn't influence the measured height.
		$(POPUP_BACKGROUND_DIV_ID).height(0);

		// Get the y offset of the pop-up top.
		var popupTop = $(POPUP_BACKGROUND_DIV_ID).offset().top;

		// Get the page height minus the top offset.
		var pageHeight = $(document).height() - popupTop;

		// Get the height of the current content of the popup.
		var contentHeight = $(POPUP_DIV_ID).height();

		// Set background height so it matches the page
		var height = Math.max(pageHeight, contentHeight);
		$(POPUP_BACKGROUND_DIV_ID).css("height", height);
	};

	var methods = {
		init: function () {
			return this.each(function () {
				var popupContent = $(POPUP_CONTENT_DIV_ID);

				// Move the given content to the popup container.
				$(this).appendTo(popupContent);
			});
		},
		show: function (onShown, onHidden, width) {
			// Only show the first.
			if (this.length > 0) {
				var content = this.first();

				showCommon(function () {
					initShow(width, onHidden);

					content.show();

					// Fade in background.
					$(POPUP_BACKGROUND_DIV_ID).fadeTo("fast", 0.7);

					// Slide in popup.
					$(POPUP_DIV_ID).show("blind", onShown, 400);

					// Set the height of the background. This is done AFTER the popup is show, to get the correct height/offset values.
					updateBackground();

					// Because the popup is shown in the top of the page, we need to scroll to the top.
					$(window).scrollTop(0);
				});
			}

			return this;
		},
		showAjax: function (url, data, onData, onShown, onHidden, width) {
			if (url === undefined || url === null) {
				throw "popup.showAjax Exception: url argument not specified";
			}

			showCommon(function () {
				initShow(width, onHidden);

				// Show loading icon
				$(POPUP_LOADING_DIV_ID).show();

				// Fade in background.
				$(POPUP_BACKGROUND_DIV_ID).fadeTo("fast", 0.7);

				// Slide in popup.
				$(POPUP_DIV_ID).show("blind", onShown, 400);

				// Set the height of the background. This is done AFTER the popup is show, to get the correct height/offset values.
				updateBackground();

				// Because the popup is shown in the top of the page, we need to scroll to the top.
				$(window).scrollTop(0);

				// Start loading data
				function onDataReady(aData, statusText, jqXHR) {
					// Hide loading
					$(POPUP_LOADING_DIV_ID).hide();

					// Create node for data and show.
					ajaxContainer = $("<div></div>");
					$(POPUP_CONTENT_DIV_ID).append(ajaxContainer);
					ajaxContainer.html(aData);

					// Update the height of the background.
					updateBackground();

					// Validate data
					jQuery.validator.unobtrusive.parse(ajaxContainer);

					// Notify caller that data is ready and give the container to the data.
					if (typeof (onData) === "function") {
						onData(ajaxContainer);
					}
				}

				function onDataError(aData, statusText, jqXHR) {
					// Hide loading
					$(POPUP_LOADING_DIV_ID).hide();

					// Show error
					$(POPUP_ERROR_DIV_ID).show();
				}

				jQuery.ajax({ type: "POST", url: url, data: data, success: onDataReady, error: onDataError });
			});
		},
		hide: function (callback) {
			hideCurrent(callback);

			return this;
		},
		updateBg: function () {
			updateBackground();

			return this;
		}
	};

	$.fn.popup = function (method) {
		// Method calling logic.
		if (methods[method]) {
			return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
		}
		else if (typeof method === 'object' || !method) {
			return methods.init.apply(this, arguments);
		}
		else {
			$.error('Method ' + method + ' does not exist on jQuery.popup');
		}
	};

})(jQuery);

