/*
A simple css popup window. 
The content can be an hidden content already present in the page or get via Ajax.
 
A- Changelog :
 3.1
 	- overlay style params
	- center works as expected on big contents
 3.0
 	- use of ContentPane
 2.1
 	- use of Prototype.js Class framework
 	- upgrade to prototype 1.6.0.3
 	- some cleaning 
 2.0 
 	- support of page already contained hidden content to be opened in the popup
 	- ajax update separated from show()
 	- cssHiderClass facility removed (it wasn't that clean)
 1.0 Created by D.Saussac 21/10/2008


B- Sample usage :
	1- simple case : content already in page
	var popup = new CssPopup({container : 'myContainer', content : 'myContent'});  // this will be centered in container with ID 'myContainer'
	popup.show(); // shows the popup containing content with ID 'myContent'
	
	2- Creation then Ajax update + show popup when done
	var popup = new CssPopup({container : 'myContainer'});  // this will be centered in container with ID 'myContainer'
	popup.contentPane.ajaxUpdate({url : "url/of/popup/content/to/be/loaded/by/ajax.html", {onComplete : function() {popup.show();}}}); // updates then shows the popup

	3- Or the same in one single action 
	var popup = new CssPopup({container : 'myContainer', contentPane : {
		ajaxParams : {
		 url : "url/of/popup/content/to/be/loaded/by/ajax.html",
		 {onComplete : function() {popup.show();}}
		}
	}});
	

C- Constructor :
	 * 'CssPopup([options])' creates a popup and attaches it to its container element. 
		 <options> is an object with the following properties :
		 	 container : this is the popup container whithin which the popup will be centered.
	 	 			If nothing is passed, container defaults as document body.
	 	 			Can be a DOM element or an Id as for prototype '$' function. 
		 	 contentPane : this is the inner ContentPane params including initial popup content. 
	 	 			If nothing is passed, an empty ContentPane is created.
		 	 		IMPORTANT : The content is used as is and should be cloned if used many times (see ContentPane).
		 	 content : this is a simpler way to provide initial popup content from an in-page content IF 'contentPane' parameters do not provide such an element.
	 	 			Can be a DOM element or an Id as for prototype '$' function. 
	 	 
	 	 NOTE : a 'hidePopup' callback is added to the pane for in-popup scripting purpose

D- Methods : 
	 * 'show([<url>][, <ajaxOptions>][, <event>])' shows popup centered in the container depending on its size. 
	 	If optionnal <url> parameter is provided, an ajax update is done after the overlay is displayed as with 'ajaxUpdate(<url>, <ajaxOptions>)'.
	 	The <event> param is the originating event causing the hide to happen.
	 	
	 	'show' sends events before and after being shown with an event parameter built as follows :
	 	event = {
	 		type : 'beforeShow'|'show',
	 		origin : <event>
	 	}
	 	If one of the event callbacks return false on a 'beforeShow', popup open is canceled.

	 * 'hide(<event>)' method can be explicitely called to close the popup.
		The <event> param is the originating event causing the hide to happen.
		Example : catch click events from inside an Ajax loaded popup content to hide it
			  Event.observe(this.element.down("button"), "click", function(e)
			  {
			    this.hidePopup(e);
			  }.bindAsEventListener(this));
			  
		'hide' sends events before and after update with an event parameter built as follows :
		event = {
			type : 'beforeHide'|'hide',
			origin : <event>
		} 
	 	If one of the event callbacks return false on a 'beforeHide', popup hide is canceled.
	
	 * 'visible()' returns a boolean telling wether the popup is visible or not.
	 
	 * Use 'addListener(<listenerCallbackFunction>)' and 'removeListener(<listenerCallbackFunction>)' to be warned of events sent by 
	 	'show', 'hide' and 'updateAjax'. 
		 This takes a callback function as parameter to which an event is passed :
		 { type : 'show'|'hide'|'update', origin : <event at source of hide event>}

E- Properties
	* contentPane : the ContentPane object extending the content element
	* content : the content element

See ContentPane for more details on script usage in popups.

*/

if (typeof Prototype == 'undefined' || !Prototype.Version.match("1.6.0.*"))
	throw ("cssPopup.js requires Prototype library v1.6.0");
if (typeof Effect == 'undefined')
	throw ("cssPopup.js requires scriptaculous library / Effects");
if (typeof EventManager == 'undefined')
	throw ("cssPopup.js requires eventManager.js");
if (typeof ContentPane == 'undefined')
	throw ("cssPopup.js requires contentPane.js");

var CssPopup = {
	//Static stuff
	Version : '3.1'
};

(function() {
	//Instance stuff
	var k = Class.create(EventManager,
		{
			initialize : function($super, _params) {
				$super();
				_params = Object.extend(
						{
							overlay : {},
							top: "15%"
						},
						_params || {});
				
				this._params = _params;
		
				var container = $(this._params.container || document.body);
				
				// Create overlay div and insert in in the container
				var overlay = new Element("div", {
					style : this._params.overlay.style||"background-color:#000000; position:absolute; z-index:9997;",
					className : this._params.overlay.className||""
				});
				overlay.setOpacity(this._params.overlay.opacity||0.35);
				overlay.hide();
				container.insert(overlay);
		
				// Create content div or use source content and insert in in the container
				var _contentPaneParams = Object.extend({element : this._params.content}, this._params.contentPane||{});
				this.contentPane = new ContentPane(_contentPaneParams);
				var content = this.contentPane.element;
				content.scriptContext.hidePopup = this.hide.bind(this);
				
				content.setStyle("position:absolute; z-index:9998; overflow : auto;");
				content.setOpacity(0);
				content.hide();
				container.insert(content);
		
				this.container = container;
				this.overlay = overlay;
				this.content = content;
				
				// IE<v7 select bug fix
				if (Prototype.Browser.IE && Prototype.Browser.Version < 7)
				{
					var iframe = new Element("iframe", { src : "/blank.html", style : "position: absolute;z-index: -1;filter: mask();"
						+"border: 0;margin: 0;padding: 0;top: 0;left: 0;width: 100%;height: "+this.container.getHeight()+";overflow: hidden;",scrolling:"no"});
					overlay.insert(iframe);
				}
			},
			
			center : function()
			{
				var dims = this.content.getDimensions();
				var targetZoneDims = this.container.getDimensions();
	
				// update overlay height to content's
				/*if (targetZoneDims.height<dims.height)
				{
					targetZoneDims.height = dims.height;
					this.overlay.morph('height:'+targetZoneDims.height+'px;');
				}*/
				
				// This forces dims on IE
				//this.content.style.width=dims.width;
				//this.content.style.height=dims.height;

				// center on target zone
				var viewPortOffsets = document.viewport.getScrollOffsets();
				this.content.clonePosition(this.container, {
					setWidth :false,
					setHeight :false,
					//offsetTop :Math.max(0, (viewPortOffsets.top+targetZoneDims.height - dims.height) / 2 | 0),
					offsetLeft :Math.max(0, (viewPortOffsets.left+targetZoneDims.width - dims.width) / 2 | 0)
				});
				this.content.setStyle({top:this._params.top});
			},
			
			// shows the popup
			show : function(_url, _ajaxOptions, e) {
				var event = {
						type :'beforeShow',
						origin : e
					};
				if (this.fireEvent(event, false)) return;
		
				// Match overlay to container
				this.overlay.clonePosition(this.container);
				//this.overlay.setStyle({height:this.container.clientHeight+'px',width:'100%'});
				//this.overlay.setStyle({height:'100%',width:'100%'});
				this.overlay.setStyle(this.getPageDimensions());
				this.overlay.show();
				
				var doShow = function() {
					// center popup
					this.center();
					
					// show content
					this.content.setOpacity(0);
					this.content.show();
					new Effect.Opacity(this.content, {
						from :0,
						to :1,
						duration :0.5
					});
		
					// warn listeners
					var event = {
						type :'show',
						origin : e
					};
					this.fireEvent(event);
				}.bind(this);
				
				// perform ajax update first if needed then show the content
				if(_url)
				{
					_ajaxOptions = _ajaxOptions || {};
					_ajaxOptions.onComplete = (_ajaxOptions.onComplete || Prototype.emptyFunction).wrap(
								function(proceed, transport) {proceed(transport); doShow();}
							);
					this.contentPane.ajaxUpdate({url : _url, ajaxOptions :_ajaxOptions});
				}
				else doShow();
			},
		
			visible : function() {
				return (Object.isElement(this.overlay) && this.overlay.visible())
					|| (Object.isElement(this.content) && this.content.visible());
			},
			
			hide : function(e) {
				if (!this.visible())
					return;
		
				// warn listeners
				var event = {
					type :'beforeHide',
					origin :e
				};
				if (this.fireEvent(event, false)) return;
		
				var localRef = this;
		
				// Hide the popup
				new Effect.Opacity(this.content, {
					from :1,
					to :0,
					duration :0.3,
					afterFinish : function() {
						localRef.content.hide();
						localRef.overlay.hide();
					}
				});
		
				// warn listeners
				event = {
					type :'hide',
					origin :e
				};
				this.fireEvent(event);
		
			},
			
			getPageDimensions: function() {
				var dim = this.getPageSize();
			    return {width: dim[0], height: dim[1]};
			},
			
			getPageSize: function() {
				var xScroll, yScroll;
			
			    if (window.innerHeight && window.scrollMaxY) {	
			      xScroll = document.body.scrollWidth;
			      yScroll = window.innerHeight + window.scrollMaxY;
			    } else if (document.body.scrollHeight > document.body.offsetHeight){ // all but Explorer Mac
			      xScroll = document.body.scrollWidth;
			      yScroll = document.body.scrollHeight;
			    } else { // Explorer Mac...would also work in Explorer 6 Strict, Mozilla and Safari
			      xScroll = document.body.offsetWidth;
			      yScroll = document.body.offsetHeight;
			    }
			
			    var windowWidth, windowHeight;
			    if (self.innerHeight) {	// all except Explorer
			      windowWidth = self.innerWidth;
			      windowHeight = self.innerHeight;
			    } else if (document.documentElement && document.documentElement.clientHeight) { // Explorer 6 Strict Mode
			      windowWidth = document.documentElement.clientWidth;
			      windowHeight = document.documentElement.clientHeight;
			    } else if (document.body) { // other Explorers
			      windowWidth = document.body.clientWidth;
			      windowHeight = document.body.clientHeight;
			    }	
			    
			    // for small pages with total height less then height of the viewport
			    if(yScroll < windowHeight){
			      pageHeight = windowHeight;
			    } else { 
			      pageHeight = yScroll;
			    }
			
			    // for small pages with total width less then width of the viewport
			    if(xScroll < windowWidth){	
			      pageWidth = windowWidth;
			    } else {
			      pageWidth = xScroll;
			    }
			
			    arrayPageSize = new Array(pageWidth,pageHeight,windowWidth,windowHeight);
			    return arrayPageSize;
			}
		
		});// Class.create()

	Object.extend (k, CssPopup);
	CssPopup = k;
})();


