argos.require("classes");
argos.check("widgets");

argos.widgets.popups = new (function() {
	var _popups = this;
	var _created = new Array();


	// External Classes
	var _classes = {
		Element : argos.classes.Element
	}

	// External Helpers
	var _helpers = {
	}



	// PUBLIC

	// Return Array of created popup objects (if created by _popups.create).
	this.get = function(i) {
		return arguments.length > 0 ? _created[i] : _created;
	}

	// Loop over all popups to close each.
	this.close = function() {
		var popups = _popups.get();
		for(var i=0; i<popups.length; i++) {
			popups[i].$node.close();
		}
	}

	// Create new popup (and append to _created array).
	this.create = function(id, options) {
		var p = new Popup(id, options ? options : {});
		_created.push(p);
		return p;
	}


	// Classes
	function Popup(id, options) {
		var _popup = this;


		// Externally configurable options.
		var _options = _updateOptions.call(new Object({	
			"popupButtonText" : "close",
			"popupClassHidden" : "closed",
			"popupClassVisible" : "open"
		}), options);


		// Initialise script.	
		(function init() {
			_classes.Element.call(this, "<div id=\"" + id + "\"></div>");
			this.$node.hide().addClass(_options.popupClassHidden);
			$(document.body).append(this.$node);
		}).call(this);


		// Private
		function _updateOptions(customisations) {
			for(var c in customisations) {
				this[c] = customisations[c];
			}
			return this;
		}


		// Priviledged
		this.element = this.$node; // Left in for backwards compat.
		this.active = false; // true when popup open.
	
		this.open = function (effect, f) {
			var effect = arguments.length > 0 ? effect : "show";
			var f = arguments.length > 1 ? f : function(){}; // note: f can also be int instead of func.

			_popup.$node
				.append(_popupClose())
				.removeClass(_options.popupClassHidden)
				.addClass(_options.popupClassVisible);

			switch(effect) {
				case "fade" : _popup.$node.fadeIn(f); 
					break;
				default : _popup.$node.show(f);  
			}

			_popup.active = true;
		};

		this.close = function () {
			_popup.$node.fadeOut(_onPopupClosed);
		};

		this.closeImmediately = function() {
			_popup.$node.stop(false, true);
			_onPopupClosed();
		}

		this.positionNear = function (target, hOffset, vOffset, vSpacer) {
			var viewportHeight = $(window).height();
			var scrolledAmount = $(document).scrollTop();
			var targetOffset = target.offset();
			var targetTopToViewportTop = targetOffset.top - scrolledAmount;
			var targetBaseToViewportBase = viewportHeight - (targetTopToViewportTop + target.height());
			var targetTop = 0;
	
			if(targetTopToViewportTop > _popup.$node.height()) {
				targetTop = scrolledAmount + (targetTopToViewportTop - (_popup.$node.height() + vSpacer));
			}
			else {
				targetTop = scrolledAmount;
			}
	
			// Make sure targetTop is not less than desired minimum
			targetTop = (targetTop > vOffset) ? targetTop : vOffset;
	
			_popup.$node.css({"left" : hOffset + "px", "top" : targetTop + "px"});
	
			return _popup.$node; // allow jQuery style chaining.
		};

		this.postLoadPositionAdjustment = function(vOffset) {
			// Sometimes content pushes out the height ruining our previous calculations.
			// This attempts to avoid unnecessary popup clipping where we have viewport space.
			var viewportHeight = $(window).height();
			var scrolledAmount = $(document).scrollTop();
			var popupOffset = _popup.$node.offset();
			var popupTopToViewportTop = popupOffset.top - scrolledAmount;
			var popupBaseToViewportBase = viewportHeight - (popupTopToViewportTop + _popup.$node.outerHeight());
			var adjustment = popupOffset.top - (popupBaseToViewportBase * -1);
			var currentTop = _popup.$node.css("top").replace("px","");
	
			if(popupBaseToViewportBase < 0 && popupTopToViewportTop > (popupBaseToViewportBase * -1)) {
				_popup.$node.css("top", (currentTop <= vOffset) ? vOffset : adjustment);
			}
		}
	
		// Private
		function _popupClose() {
			var $bttnClose = $("<button class=\"close\">" + _options.popupButtonText + "</button>");
			$bttnClose.click(_popup.close);
			return $bttnClose;
		}
		
		function _onPopupClosed() {
			_popup.$node.addClass(_options.popupClassHidden);
			_popup.$node.removeClass(_options.popupClassVisible);
	        _popup.$node.empty();
			_popup.active = false;
		}

	
		//==================== START: argos.widgets.popup IE fixes ====================================
		// Throw in some fixes for IE6 & 7 bugs (note: delete this code when dropping support for IE6 & 7).
		if(jQuery.browser.msie && Math.floor(jQuery.browser.version) <= 7) {
			var oldArgosPopupOpen = this.open;
			var oldArgosPopupPositionNear = this.positionNear;
			var oldArgosPopupClose = this.close;
	
			this.open = function() {
				var $popup = _popup.$node;
				if(jQuery.browser.msie && Math.floor(jQuery.browser.version) <= 6) {
					// We need to compensate for IE6 rendering select fields in their own special window.
					$popup.append("<iframe frameborder=\"0\" src=\"javascript:document.bgColor='rgb(150,150,150)';document.fgColor='rgb(150,150,150)';\"></iframe>");
				}
		
				// We also need to move the popup element to sort out a z-index issues with the dropdownmenu.
				$("#dropdownmenus").before($popup);
	
				// Call to existing open function to do the rest.
				oldArgosPopupOpen(); 
			};
	
			// We need to correct the left position after moving the popup element (see above).
			this.positionNear = function (target, hOffset, vOffset, vSpacer) {
				oldArgosPopupPositionNear(target, hOffset, vOffset, vSpacer);
				_popup.$node.css("left","320");
			};
	 
			this.close = function() {
				// Remove the inserted iframe before closing.
				_popup.$node.contents("iframe").remove();
				oldArgosPopupClose();
			}
		}
		//==================== END: argos.widgets.popup IE fixes =====================================
	}
	Popup.prototype = new _classes.Element();
	Popup.prototype.loading = function(bool) {
		if(bool) {
			this.$node.addClass("loading");
		}
		else {
			this.$node.removeClass("loading");
		}
	}
	Popup.prototype.empty = function() {
		this.$node.empty();
	}
	Popup.prototype.append = function (content) {
		this.$node.append(content);
	};
	Popup.prototype.positionAt = function (x, y) {
		this.$node.css({"left": x + "px", "top": y + "px"});
		return this.$node; // allow jQuery style chaining.
	};
	
});



