/**
 * @projectDescription Projekt do zarządania akcjami
 * 
 * @copyright 2007 Robert (nospor) Nodzewski
 * @author Robert Nodzewski (nospor at interia dot pl)
 * @license http://opensource.org/licenses/lgpl-license.php GNU Lesser General Public License
 * @version 1.0
 */

/**
 * @classDescription Klasa do zarządzania okienkami akcji
 */
function ActionsManager(){}

/**
 * Inicjalizuje nowy obiekt akcji
 * 
 * @param {Object} params Obiekt parametrów. Obiekt zawiera następujące właściwości:
 * - guid - identyfikator obiektu
 * - closeLastOpened - zamknij ostatnio otwarte okno (domyślnie false)
 * - closeAfterSuccess - zamyka okno gdy akcja się powiedzie. Dotyczy tylko wyników zwracanych jako xml (domyślnie false)  
 * - link - link pod jaki walić żądanie 
 * - className - klasa głównego diva (domyślnie actionsManager)
 * - title - tytuł w nagłówku (domyślnie pusty)
 * - titleRunning - tytuł w nagłówku podczas trwania akcji (domyślnie null, czyli nie nadpisuje głównego tytułu)
 * - refresh - czy ponownie wykonać akcję, jeśli już istnieje (domyślnie true)
 * - allowRunRunning - czy pozwalać na uruchamianie trwających już akcji (domyślnie false)
 * - messagesAddingType - w jakis sposób dodawać nowe wiadomości: first - na początku wiadomości, last - na końcu wiadomości (domyślnie last) 
 * - params - parametry akcji wysłane ajaxem (domyślnie null), np: {'param1' : 'wartosc1', 'param2':'wartosc2'}
 * - method - metoda wysłania żądania (domyślnie post)
 * - clear - czy czyścić zawartość okienka (domyślnie true)
 * - type - typ pobieranych danych: html, xml,inne (domyślnie xml). Można podać własne typy, należy wówczas dla
 * każdego typu zdefiniować metodę AjaxAction.prototype.writeTYPE = function(obj){...}, gdzie "TYPE" to pisane z dużej litery
 * określony przez was "type", a "obj" to obiekt, jaki zwraca ajax po wykonaniu akcji.
 * @return {AjaxAction} Obiekt akcji
 */
ActionsManager.setup = function(params){
	function pd(name, def) { if (typeof params[name] == "undefined") { params[name] = def; } };
	
	pd("closeLastOpened", false);
	pd("closeAfterSuccess", false);
	pd("className", 'actionsManager');
	pd("title", '');
	pd("titleRunning", null);
	pd("params", null);
	pd("method", 'post');
	pd("refresh", true);
	pd("clear", true);
	pd("allowRunRunning", false);
	pd("type", 'xml');
	pd("messagesAddingType", 'last');
	
	if (!ActionsManager.beforeSetup(params)) return null;
	if (typeof ActionsManager.objects[params.guid] == "undefined" || !ActionsManager.objects[params.guid]){
		ActionsManager.objects[params.guid] = new AjaxAction(params);
		ActionsManager.create(ActionsManager.objects[params.guid]);
		ActionsManager.objects[params.guid].afterSetup();
		ActionsManager.run(ActionsManager.objects[params.guid],params);
	} else {
		if (params.refresh){
			canRun = ActionsManager.canRun(params);
			if (canRun != true){
				ActionsManager.objects[params.guid].afterSetup();
				ActionsManager.runningErrorInfo(params, canRun);
			}	
			else {
				if (params.clear)
					ActionsManager.objects[params.guid].clear();
				ActionsManager.objects[params.guid].params = params;
				ActionsManager.objects[params.guid].afterSetup();	
				ActionsManager.run(ActionsManager.objects[params.guid], params);
			}	
		} else 
			ActionsManager.objects[params.guid].afterSetup();
	}
	return ActionsManager.objects[params.guid];	
}

/**
 * Wgrywa pluginy
 * 
 * @param {String} plugins Lista pluginów oddzielonych przecinkiem, np: dynamic,position
 */
ActionsManager.loadPlugins = function(plugins){
	if (!plugins) return ;
	plugins = plugins.split(',');
	shead = document.getElementsByTagName("head")[0];
	pLength = plugins.length;
	for(ilp=0;ilp<pLength;ilp++) {
		plugin = plugins[ilp].replace(/^\s+/, '').replace(/\s+$/, '');
		if (typeof ActionsManager.loadedPlugins[plugin] == 'undefined'){
			ActionsManager.loadedPlugins[plugin] = true;
			script = document.createElement( 'script' );
			script.type = 'text/javascript';
			script.src = ActionsManager.pluginsPath + 'ActionsManager_'+plugin+'.js';
			shead.appendChild(script);
		}
	}
}

ActionsManager.pluginsPath = 'scripts/plugins/';
ActionsManager.loadedPlugins = {};

ActionsManager.txtClose = 'Zamknij';
ActionsManager.txtRunning = 'Akcja w toku i nie można jej ponownie urochomić. Proszę czekać na zakończenie.';

/**
 * Ostatnio otwarte okienko
 * 
 * @type {AjaxAction}
 */
ActionsManager.lastOpened = null;

/**
 * Obiekt zawierający utworzone obiekty akcji.
 * 
 * @type {object}
 */
ActionsManager.objects = {};

/**
 * Metoda wywoływana przed inicjalizacją okna
 * 
 * @param {Object} params Parametry wywołania
 * @return {Boolean} jeśli zostanie zwrócony false, okienko się nie otworzy i akcja nie zostanie wykonana
 */
ActionsManager.beforeSetup = function(params){
	if (params.closeLastOpened)
		ActionsManager.hide(ActionsManager.lastOpened);
	return true;
}

/**
 * Mówi czy można uruchomić akcję. Defacto sprawdza, czy akcja nie jest przypadkiem w toku
 * 
 * @param {Object} params Parametry wywołania akcji
 * @return {Boolean, String} 
 * - false - nie można uruchomić akcji
 * - String - nie można uruchomić akcji. Zwrócony tekst jest tekstem do wyświetlenia
 * - true - można uruchomić akjcę 
 */
ActionsManager.canRun = function(params){
	aacrObject = ActionsManager.getObject(params.guid);
	if (!aacrObject) return false;
	return aacrObject.running && !params.allowRunRunning ? false : true;
}

/**
 * Wyświetla informację o trwaniu akcji. 
 * Metoda jest wywoływana, gdy próbujemy uruchomić akcję, która jest już uruchomiona
 * @param {Object} params Parametry wywołania akcji
 * @param {String} errorText Tekst do wyświetlenia
 */
ActionsManager.runningErrorInfo = function(params, errorText){
	if (typeof errorText != 'string')
		errorText = ActionsManager.txtRunning;
	ActionsManager.objects[params.guid].addMessage(errorText,'error');
}

/**
 * Tworzy DOM okienka akcji
 * 
 * @param {AjaxAction} aaObject Obiekt akcji
 * @return {Boolean}
 */
ActionsManager.create = function(aaObject){
	parentObj = document.getElementsByTagName("body")[0];
	aaObject.element = Mixed.createElement('div', parentObj, null, 'pm'+aaObject.guid, aaObject.params.className);
	aaObject.objects.header = Mixed.createElement('div', aaObject.element, aaObject.params.title, null, 'amHeader');
	
	aaObject.objects.wait = Mixed.createElement('div', aaObject.objects.header, null, null, 'amWait');
	aaObject.objects.close = Mixed.createElement('div', aaObject.objects.header, null, null, 'amClose');
	aaObject.objects.close.title = ActionsManager.txtClose;
	aaObject.objects.close.onclick = function(){
		ActionsManager.hide(aaObject);
	}
	aaObject.objects.container = Mixed.createElement('div', aaObject.element, null, null, 'amContainer');
	aaObject.objects.errors = Mixed.createElement('div', aaObject.objects.container, null, null, 'amErrors');
	aaObject.objects.messages = Mixed.createElement('div', aaObject.objects.container, null, null, 'amMessages');
	aaObject.objects.content = Mixed.createElement('div', aaObject.objects.container, null, null, 'amContent');
}

/**
 * Niszczy podany obiekt
 * 
 * @param {AjaxAction} aaObject
 */
ActionsManager.destroy = function(aaObject){
	if (!aaObject) return;
	parentObj = document.getElementsByTagName("body")[0];
	parentObj.removeChild(aaObject.element);
	ActionsManager.objects[aaObject.params.guid] = null;
}
/**
 * Wysyła żądanie do serwera w celu uruchomienia akcji
 * 
 * @param {AjaxAction} aaObject Obiekt akcji
 * @param {Object} params Parametry obiektu. Duplikowane jest ten parametr, gdyż może zdarzyć się sytuacja, gdzie aaObject.params!=params 
 * @return {Boolean} false, gdy akcja nie zostanie uruchomiona
 */
ActionsManager.run = function(aaObject,params){
	if (!aaObject.beforeRun()) return false;
	aaObject.countRunning++;
	aaObject.showHideWait(true);
	aaObject.root = null;
	aaObject.running = true;
	aaObject.setTitle(params.titleRunning);
	ActionsManager.ajaxRun(aaObject, params);
	return true;
}

/**
 * Ukrywa podane okienko
 * 
 * @param {AjaxAction} aaObject
 */
ActionsManager.hide = function(aaObject){
	if (!aaObject) return ;
	aaObject.element.style.display = 'none';
}

/**
 * Pokazuje podane okienko
 * 
 * @param {AjaxAction} aaObject
 */
ActionsManager.show = function(aaObject){
	if (!aaObject) return ;
	aaObject.element.style.display = 'block';
}

/**
 * Zwraca obiekt o zadanym guid
 * @param {String} guid Identyfikator obiektu
 * @return {AjaxAction} Zwraca obiekt akcji
 */
ActionsManager.getObject = function(guid){
	if (typeof ActionsManager.objects[guid] == "undefined")
		return null;
	return ActionsManager.objects[guid];
}

/**
 * @classDescription Klasa akcji
 * @param {object} params Obiekt z parametrami
 * @constructor
 */
function AjaxAction(params){
	this.params = params;
	this.position = {};
	this.objects = {};
	this.running = false;
	this.countRunning = 0;
}

/**
 * Metoda wywoływana po inicjalizacji obiektu
 */
AjaxAction.prototype.afterSetup = function(){
	this.setZIndex();
	ActionsManager.show(this);
	ActionsManager.lastOpened = this;
}

/**
 * Metoda wywoływana przed uruchomieniem akcji. 
 * 
 * @return {Boolean} jeśli zostanie zwrócony false, akcja się nie wykona
 */
AjaxAction.prototype.beforeRun = function(){
	return true;
}

/**
 * Metoda wywoływana po odebraniu wyniku akcji
 * 
 * @param {Object} ajaxObj Obiekt wyniku
 * @param {Object} params Parametry obiektu. Duplikowany jest ten parametr, gdyż może zdarzyć się sytuacja, gdzie this.params!=params 
 * @param {Boolean} resultOk czy pobrano poprawnie
 */
AjaxAction.prototype.afterRun = function(ajaxObj,params, resultOk){
	if (resultOk)
		this.write(ajaxObj, params);
	this.countRunning--;
	if (this.countRunning == 0){
		this.showHideWait(false);
		if (params.titleRunning != null)
			this.setTitle(params.title);
		if (params.closeAfterSuccess && resultOk && this.getResult(ajaxObj) == '0')
			ActionsManager.hide(this);	
	}	
	this.running = false;	
}

/**
 * Ustawia tytuł okienka
 * 
 * @param {String} title Tytuł do ustawienia
 */
AjaxAction.prototype.setTitle = function(title){
	if (title == null) return ;
	tn = document.createTextNode(title);
	if (this.objects.header.firstChild != this.objects.wait)
		this.objects.header.replaceChild(tn, this.objects.header.firstChild);
	else	
		this.objects.header.insertBefore(tn, this.objects.wait);
}


/**
 * Ustawia dla danego obiektu zindex o jeden większy od ostatnio zmienianego
 */
AjaxAction.prototype.setZIndex = function(){
	if (typeof ActionsManager.zindex == "undefined")
		ActionsManager.zindex = 1000;
	ActionsManager.zindex++;	
	this.element.style.zIndex = ActionsManager.zindex;
}


/**
 * Pokazuje/ukrywa diva z ikoną czekania
 * 
 * @param {Boolean} show Czy pokazać
 */
AjaxAction.prototype.showHideWait = function(show){
	this.objects.wait.style.display = show ? 'block' : 'none'; 
}
/**
 * Pobiera wynik akcji
 * 
 * @param {Object} obj Obiekt ajaxa
 * @param {Object} params Parametry obiektu. Duplikowany jest ten parametr, gdyż może zdarzyć się sytuacja, gdzie this.params!=params 
 * @return {Boolean}
 */
AjaxAction.prototype.write = function(obj, params){
	eval('this.write'+this.params.type.toUpperCase()+'(obj, params);');		
		
}
/**
 * Pobiera wynik akcji jako XML
 * 
 * @param {Object} obj Obiekt ajaxa
 * @param {Object} params Parametry obiektu. Duplikowany jest ten parametr, gdyż może zdarzyć się sytuacja, gdzie this.params!=params 
 * @return {Boolean}
 */
AjaxAction.prototype.writeXML = function(obj, params){
	root = Mixed.getRoot(obj, 'actionsmanager');
	if (!root) return false;
	this.writeMessages(root, 'error', this.objects.errors);
	this.writeMessages(root, 'message', this.objects.messages);
	
	content = root.getElementsByTagName('content');
	if (content && content.length>0){
		content = Mixed.getText(content.item(0));
		if (content)
			this.objects.content.innerHTML += content;
	}
	
	return true;
}

/**
 * Pobiera wynik akcji jako HTML
 * 
 * @param {Object} obj Obiekt ajaxa
 * @param {Object} params Parametry obiektu. Duplikowany jest ten parametr, gdyż może zdarzyć się sytuacja, gdzie this.params!=params 
 * @return {Boolean}
 */
AjaxAction.prototype.writeHTML = function(obj, params){
	if (!obj || !obj.responseText) return false;
	this.objects.content.innerHTML += obj.responseText;
	return true;
}

/**
 * Pobiera rezultat akcji z roota. 
 * Należy wywoływać tylko wtedy, gdy spodziewamy się wyniku w postaci xml
 * 
 * @param {Object} ajaxObj Obiekt jaki zwraca ajax
 * @return {String, null} 
 *  - 0 - akcja zakończona sukcesem
 *  - innystring - akcja zakończona niepowodzeniem. Może to być numer błędu lub komunikat błędu
 *  - null - zapewne niepoprawny xml
 */
AjaxAction.prototype.getResult = function(ajaxObj){
	grroot = Mixed.getRoot(ajaxObj, 'actionsmanager')
	if (!grroot) return null;
	return grroot.getAttribute('result');
}

/**
 * Pobierze z xmla wiadomości/błędy
 * @param {DOM} root Nasz root
 * @param {String} type Typ wiadomośći (error, message)
 * @param {DOM} divMessage Obiekt diva, do którego wpisać wiadomośći
 */
AjaxAction.prototype.writeMessages = function(root, type, divMessage){
	messages = root.getElementsByTagName(type);
	if (messages && messages.length>0){
		messagesLength = messages.length;
		if (!divMessage.firstChild)
			Mixed.createElement('ul',divMessage);
		for (i=0;i<messagesLength;i++){
			_msg = messages.item(i);
			messageText = Mixed.getText(_msg);
			if (messageText)
				Mixed.createElement('li', divMessage.firstChild,messageText,null,null,null,(this.params.messagesAddingType == 'first'));
		}
	}
}

/**
 * Dodaje wiadomość/błąd
 * 
 * @param {String} message Wiadomość/błąd
 * @param {Object} type Typ wiadomości (message, error)
 */
AjaxAction.prototype.addMessage = function(message, type){
	if (!type || type == 'message')
		divM = this.objects.messages;
	else	
		divM = this.objects.errors;
	if (!divM.firstChild)
		Mixed.createElement('ul',divM);
	Mixed.createElement('li', divM.firstChild,message, null,null,null,(this.params.messagesAddingType == 'first'));
}

/**
 * Czyści okienko akcji 
 */
AjaxAction.prototype.clear = function(){
	if (this.objects.errors.firstChild)
		this.objects.errors.removeChild(this.objects.errors.firstChild);
	if (this.objects.messages.firstChild)
		this.objects.messages.removeChild(this.objects.messages.firstChild);
	this.objects.content.innerHTML = '';	
}
