/*
A simple css popup window. 
The content can be an hidden content already present in the page or get via Ajax.
 
A- Changelog :
 1.0 Created by D.Saussac 03/12/2008

B- Sample usage :
	var myPane = new ContentPane({
		element : "myElementID"
	});

	// update via Ajax
	myPane.ajaxUpdate({url : "url/of/popup/content/to/be/loaded/by/ajax.html", {onComplete : function() {...}}});

	// perform a periodical update
	myPane.ajaxUpdate({
		url : "url/of/popup/content/to/be/loaded/by/ajax.html",
		period : 10, 
		{onComplete : function() {...}}
		});

C- ContentPane constructor :
	* <params> is an object with the following properties :
		- element: this is the element to be extended with content pane functions. 
		 	Can be a DOM element or an Id as for prototype '$' function. 
			The content is used as is and should be cloned (see Prototype Element.clone() function) before use if used somewhere else in the page.
			If no element info is passed, an empty div element is created.
			
		- ajaxParams : an object with the following properties. 
			- url : the ajax target URL.
			- period : request will be periodicaly done with this period (in seconds).
			- ajaxOptions : the same object as prototypes.js Ajax.Request options.
					 	One more property can be set in these options :
					 		evalScripts : true(default)|false. Activates or prevents content script 
					 					evaluation. 
			NOTE : If this object if set, an Ajax request will be sent at the end of the initialization process. 

D- Methods : 
 	* 'ajaxUpdate(ajaxParams[, <setParamsAsDefault>])' updates content from an URL.
	 	- <ajaxParams> : same as ajaxParams in constructor. Automaticaly stored as default params if none are set in constructor.
	 	- <setParamsAsDefault> : optional. If true, the ajaxParams are stored as the new default params. 
	* 'suspend()' : suspend the periodical update
	* 'resume([<afterPeriod>])' : resumes the periodical update and starts the request immediately or after the period if <afterPeriod> is true.

E- Properties :
	* element : the paned element. It is extendend itself with two properties :
		- scriptContext : the script context object (see G below)
		- ContentPane : the ContentPane object
	* loaded : tells if content from Ajax has been loaded yet

F- Events :
	These are fired on the element
	* "ContentPane:initialize" : at the end of initialization. Usefull for scripts that should be runned only once the script context is available
	* "ContentPane:unload" : before an ajax request.
	* "ContentPane:load" : after a successfull ajax request.

G- Usage of scripts in Ajax updated ContentPanes : 
	 Scripts are evaled within the context of the ContentPane'd element script context after it has been inserted in the page. 
	 Which means that : 
	
	 - functions and variable defined using a global scope are global to the window.
	 As this causes a potential data overwrite problem, this should be avoided whenever possible. 
	    example :
	      myVar = "test";
	      myFunc = function(...) {...};
	    are accessible from anywhere in the page.
	
	 - functions and variable defined using a local scope are discarded at the end of eval of the script tag
	    example :
	      var myLocalVar = "test";
	      function myLocalFunc(...) {...};
	    are inaccessible from anywhere but the current evaled script tag.
	
	 - functions and variable defined using 'this' are attached to the element script context.
	 This is the prefered way of defining persistent variables and functions in content panes since it will not interfere with global scope 
	 (no overwrite) while still being accessible from inside or outside the pane. 
	    example :
	      this.myVar = "test";
	      this.myFunc = function(...) {...};
	    are accessible though myPane.scriptContext.myVar and myPane.scriptContext.myFunc(...).
	
	 Script context object is accessible as 'this' in the evaled scripts. 
	 It is valued with a default object 'element' containing a reference to the paned element. 
 */
if (typeof Prototype == 'undefined')
	throw ("multiAjaxUpdater.js requires Prototype library");

if (!ContentPane)
{
	var ContentPane = {
		Version : 1.0
	};
} //if (!ContentPane)

if (!ContentPane.prototype)
(function() {

	var inTagScripts = "scriptContext";
	var k = Class.create (
	{
		initialize : function(_params, _ajaxParams)
		{
			_params = _params||{};
			
			if (Object.isElement(_params) || Object.isString(_params))
				_params = {element : $(_params)};
			
			this.element = $(_params.element||new Element("div"));

			if (Object.isString(_ajaxParams))
				this.ajaxParams = {url : _ajaxParams};
			else this.ajaxParams = _ajaxParams||_params.ajaxParams;
			
			this.loaded = false;
			this.element.contentPane = this;
			this.scriptContext = {element : this.element};
			this.element.scriptContext = this.scriptContext;
			
			var debugLogger;
			if (window.PageLogger && (debugLogger=_params.logger||window.PageLogger.getInstance("debug")))
				this.element.observe("ContentPane:ajaxError", function(e){
					debugLogger.log("ContentPane:ajaxError :"+e.memo);
				});
			
			if(_params.element)
				this.element.fire("ContentPane:initialize", this);
			
			if(this.ajaxParams)
				this.ajaxUpdate();
		},
		ajaxUpdate : function(_ajaxParams, setParamsAsDefault) {
			this._stop();
			
			setParamsAsDefault = setParamsAsDefault||!this.ajaxParams;
			if (Object.isUndefined(_ajaxParams))
				_ajaxParams = this.ajaxParams; // instance params if no specific params
			else if (Object.isString(_ajaxParams))
				_ajaxParams = {url : _ajaxParams};

			if (_ajaxParams && setParamsAsDefault) this.ajaxParams = _ajaxParams; // save params when needed
			
			if(!_ajaxParams || !_ajaxParams.url) return;
			
			var _ajaxOptions = Object.clone(_ajaxParams.ajaxOptions || {});
			
			var evalScripts = Object.isUndefined(_ajaxOptions.evalScripts) || _ajaxOptions.evalScripts;
	
			// wrap onSsuccess with default actions before calling user action
			_ajaxOptions.onSuccess = (_ajaxOptions.onSuccess || Prototype.emptyFunction).wrap(
					function(proceed, transport) { 
							this.contentPane.loaded = false;
							this.fire("ContentPane:unload");
							
							var scripts = transport.responseText.extractScripts();
							// update HTML
							var elementId = this.identify();
							var content = transport.responseText
								.stripScripts() // remove script tags
								.replace(new RegExp(inTagScripts), '$(\''+elementId+'\').scriptContext'); // replace in-tag scripts parts
							Element.update(this, content);
	
							// play scripts and valuate result in element scriptContext
							if (evalScripts)
							{
								// Eval scripts
								scripts.each((function(s) {
									try {
										eval(s);
										} catch(ex) {
											this.element.fire("ContentPane:ajaxError", ex);
										}
								}).bind(this.scriptContext));
							}
							
							proceed(transport); 
							
							// warn listeners that we finished updating
							this.contentPane.loaded = true;
							this.fire("ContentPane:load");
							
					  }.bind(this.element)
			); 
			
			// Arm next run if needed
			if (_ajaxParams.period)
				_ajaxOptions.onComplete = (_ajaxOptions.onComplete||Prototype.emptyFunction)
					.wrap(function(proceed, response) {
						if (proceed(response) === false) return;
						this.armTimer();
					}).bind(this);

		      // Start request
			new Ajax.Request(_ajaxParams.url, _ajaxOptions);
		},
	    armTimer : function() {
	    	if (!this.ajaxParams) throw("Use ajaxUpdate first.");
	     	if (!this.upcomingAjaxUpdate && !this.ajaxUpdateSuspended)
	     		this.upcomingAjaxUpdate = (function(){
	     			this.upcomingAjaxUpdate = null;
	     			this.ajaxUpdate();
	     		}).bind(this).delay(this.ajaxParams.period || 10);
		},
		_stop : function()
		{
			if (this.upcomingAjaxUpdate)
			{
				window.clearTimeout(this.upcomingAjaxUpdate);
				this.upcomingAjaxUpdate = null;
			}
		},
	    suspend : function()
	    {
	    	this.ajaxUpdateSuspended = true;
	    	this._stop();
	    	this.fire("ContentPane:ajaxUpdateSuspended");
	    },
	    resume : function(afterPeriod)
	    {
	    	if (!this.ajaxParams) throw("Use ajaxUpdate first.");
	    	this.ajaxUpdateSuspended = false;
	    	this.fire("ContentPane:ajaxUpdateResumed");
	    	if (afterPeriod)
	    		this.armTimer();
	    	else this.ajaxUpdate();
		}

	});

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

