/*	Manager JavaScript framework, version 1.0 beta
 *	(c) 2007 Clement Hallet, from AF83 company
 *
/*--------------------------------------------------------------------------*/


var Manager = {
    /**
    * destroy given manager from outside, and delete dom reference to it.
    * @param manager (Manager) the one to destroy
    */
	unregister: function(manager) {
		manager.container.manager = undefined;
		delete manager.container.manager;
		for(var propertie in manager) {
			manager[propertie] = undefined;
			delete manager[propertie];
		}
		manager = unedefined;
		delete manager;
	}
};

/**
 * Base class to manage Dom element from a uri, can't be used as it, but contains utility methods
 * @author Clément Hallet
 **/
Manager.Base = new Class({
    
    /**
    * constructor
    * @param element (DomElement|id) element which will be managed
	* @param updaterURI (string) URI used to update related element
	* @param options (json object)
    */
	initialize: function(container,updaterURI,options) {
		
		this.setOptions(options);	
		
		this.container = $(container);
		this.checkDependencies();
		this.container.manager = this;
		
		this.updaterURI = updaterURI;
		this.addEvent('onDestroy', function() {Manager.unregister(this)});
	
		this.parameters = {};
	},
	
	/**
	* check if a parent element is also managed, in that case, add events to destroy itself if parent element is updated or destroyed
	* @return void
    * @author Clément Hallet
	*/
	checkDependencies: function() {
		var parent = this.container;
		do {
			if($chk(parent.manager)) {
				parent.manager.addEvent('onStart',function(){this.fireEvent('onDestroy')}.bind(this));
				parent.manager.addEvent('onDestroy',function(){this.fireEvent('onDestroy')}.bind(this));
				break;
			}
			parent = parent.parentNode;
		} while(parent != null);
	},

    /**
     * add or change a state parameter
     * @param key (string) state parameter identifier
     * @param value (string) state parameter value 
     * @return void
     * @author Clément Hallet
     **/
	setParameter: function(key,value) {
		if(this.parameters[key] === value) return;
		this.parameters[key] = value;
		this.upToDate = false;
		this.sortParameters();
	},
	
	/**
     * remove a state parameter
     * @param key (string) state parameter identifier
     * @return void
     * @author Clément Hallet
     **/
	removeParameter: function(key) {
		delete this.parameters[key];
		this.upToDate = false;
		this.sortParameters();
	},

	/**
     * sort state parameters, in order to make a unique combination
     * @return void
     * @author Clément Hallet
     **/
	sortParameters: function(){
		var keys = [];
		var orderedParameters = {};
		
		for (var property in this.parameters) keys.push(property);
		keys.sort()
		
		for(i = 0; i < keys.length; i++) orderedParameters[keys[i]] = this.parameters[keys[i]];
		
		this.parameters = orderedParameters;
	}
	
});
Manager.Base.implement(new Options,new Events);

/**
 * Manage content of a dom element
 * @author Clément Hallet
 **/
Manager.Area = Manager.Base.extend({
	
	options: {
		onStart: function() {
		},
		onUpdate: function() {
		},
		connectResources: false
	},
	
	/**
	* constructor
	* @param element (DomElement|id) element which will be managed
	* @param updaterURI (string) URI used to update related element
	* @param options (json object) onStart,onUpdate,connectResources(bool)
	*/
	initialize: function(container,updaterURI,options) {
	
		this.parent(container,updaterURI,options);
	
		this.cache = new Hash();
		this.upToDate = false;
		this.stylesheets = new Array();
		this.scripts = new Array();
	},	
	
	/**
	* called to update container from the related URI
	* @param fromCache (bool) if true, update will try to perform from local cache (from the object, not browser)
	* @return void
	*/
	update: function(useCache) { 
		
		if(this.upToDate) return;
		if(this.ajax && this.ajax.running) 
			this.ajax.cancel();
		
		var cacheKey = Object.toQueryString(this.parameters);

		if(useCache && this.isCacheAvailable(cacheKey)) {
			this.cache.get(cacheKey).fireEvent('onRequest');
			this.cache.get(cacheKey).fireEvent('onSuccess',this.cache.get(cacheKey).response.text);
		} else {
			// todo : put parameters in querystring OR in body, function of the used method.
			date = new Date();
			// this.setParameter('cacheavoider',date.getTime());
			this.cache.set(cacheKey,new Ajax(this.updaterURI, {
					encoding: 'UTF-8',
					method: 'get',
					autoCancel: true,
					async: true,
					onRequest: function(){
						this.fireEvent('onStart');
						this.ajax = this.cache.get(cacheKey);
					}.bind(this),
					onSuccess: function(html) {
						// includes header parameters as css,js
						this.removeParameter('cacheavoider');
						if(this.options.connectResources){
							this.cleanScripts();
							this.addScripts(Json.evaluate(this.cache.get(cacheKey).getHeader("X-Javascript")));
							this.cleanStyleSheets();
							this.addStyleSheets(Json.evaluate(this.cache.get(cacheKey).getHeader("X-StyleSheet")));
						}
						this.container.setHTML(html);
						this.upToDate = true;
						this.fireEvent('onUpdate',this.cache.get(cacheKey));
					}.bind(this)
				})
			);
			this.ajax = this.cache.get(cacheKey).request(this.parameters);
		}
	},
	
	/**
     * Add passed scripts to the document, via ScriptManager, and keep a reference to in the object
     * @param scripts (array) array of script resource identifier
     * @return void
     * @author Clément Hallet
     **/
	addScripts: function(scripts) {
		this.scripts = scripts;
		if(scripts==undefined) return;
		for(var i=0;i<scripts.length;i++) ScriptManager.increment(scripts[i]);
	},
	
	/**
     * Add passed stylesheets to the document, via StyleSheetManager, and keep a reference to in the object
     * @param stylesheets (array) array of stylesheets resource identifier
     * @return void
     * @author Clément Hallet
     **/
	addStyleSheets: function(stylesheets){
		this.stylesheets = stylesheets;
		if(stylesheets==undefined) return;
		for(var i=0;i<stylesheets.length;i++) StyleSheetManager.increment(stylesheets[i]);
	},
	
	/**
     * Remove scripts used by the object from the document, via ScriptManager
     * @return void
     * @author Clément Hallet
     **/
	cleanScripts: function() {
		for(var i=0;i<this.scripts.length;i++) ScriptManager.decrement(this.scripts[i]);
	},
	
	/**
     * Remove scripts used by the object from the document, via StyleSheetManager
     * @return void
     * @author Clément Hallet
     **/
	cleanStyleSheets: function(){
		for(var i=0;i<this.stylesheets.length;i++) StyleSheetManager.decrement(this.stylesheets[i]);
	},
	
	/**
     * check if a cache is available
     * @param cacheKey (string) unique state identifier
     * @return bool 
     * @author Clément Hallet
	 **/
	isCacheAvailable: function(cacheKey){
		if(cacheKey==undefined) cacheKey = Object.toQueryString(this.parameters);
		return this.cache.hasKey(cacheKey) && $chk(this.cache.get(cacheKey).response);
	},
	
	setParameter: function(key,value) {
		this.upToDate = false;
		this.parent(key,value);
	},
	
	removeParameter: function(key) {
		this.upToDate = false;
		this.parent(key);
	}
	
});

