/*global log4javascript, ActiveXObject, window, $ */

/**
 * La variabile rappresenta il namespace utilizzato per tutto il codice javascript.
 */
var SISAL = (function SISAL() {
	
	var popUpAppender,
		logger,
		defaultDomain
		;
	
    /**
     * La funzione verifica se un valore è nullo.
     * @param value il valore (obbligatorio)
     * @return true se il valore è null o undefined, false altrimenti
     * @type boolean
     */
    function isNull(value)
    {
        return (value === undefined || value === null);
    }

    /**
     * La funzione verifica se un valore è nullo o vuoto.
     * @param value il valore (obbligatorio)
     * @return true se il valore è nullo o "", false altrimenti
     * @type boolean
     */
    function isNullOrEmpty(value)
    {
        return (isNull(value) || value === "");
    }

    /**
     * La funzione clona un oggetto.
     * @param object l'oggetto da clonare (obbligatorio)
     * @return il clone
     */
    function copy(object)
    {
        var i, myObject;
        // per valore
        if (isNull(object) || (typeof object === "boolean") || (typeof object === "number") || (typeof object === "string") || (typeof object === "function"))
        {
            return object;
        }
        // copio array
        else if (object instanceof Array) { 
            myObject = [];
        }
        // per riferimento
        else if (typeof object === "object")
        {
            myObject = {};
        }
        if (object instanceof Array || typeof object === "object") {
            for (i in object)
            {
                if (object.hasOwnProperty(i))
                {
                    myObject[i] = copy(object[i]);
                }
            }
            return myObject;
        }
        return null;
    }

    /**
     * La funzione genera ricorsivamente una stringa di un oggetto, formattato come object literal.
     * @param object l'oggetto da convertire in stringa (obbligatorio)
     * @param options
     * <ul>
     * <li> indentation: l'indentazione (numero di spazi) iniziale (opzionale, default: 0) NUMBER </li>
     * </ul>
     * @return la stringa
     * @type string
     */
    function toStringAsLiteral(object, options)
    {
        // gestione options
        options = isNullOrEmpty(options) ? {} : options;
        options.indentation = isNaN(options.indentation) ? 0 : options.indentation;
        var tab = 4,
            tabIndentation = "",
            i,
            indentationString = "",
            string = "{\n",
            property;
        for (i = 0; i < tab; i = i + 1)
        {
            tabIndentation = tabIndentation + " ";
        }
        for (i = 0; i < options.indentation; i = i + 1)
        {
            indentationString = indentationString + " ";
        }
        if (!isNull(object) && (typeof object === "object"))
        {
            for (i in object)
            {
                if (object.hasOwnProperty(i))
                {
                    property = object[i];
                    if (!isNull(property) && (typeof property === "object"))
                    {
                        string = string + tabIndentation + indentationString + i + ": " + toStringAsLiteral(property, {indentation: tab + options.indentation + (i + ": ").length}) + "\n";
                    }
                    else if (typeof property === "string")
                    {
                        string = string + tabIndentation + indentationString + i + ": \"" + property + "\"\n";
                    }
                    else
                    {
                        string = string + tabIndentation + indentationString + i + ": " + property + "\n";
                    }
                }
            }
            string = string + indentationString + "}";
            return string;
        }
        else if (typeof object === "string")
        {
            return indentationString + "\"" + object + "\"";
        }
        else
        {
            return indentationString + object;
        }
    }

    /**
     * La funzione recupera il logger (log4javascript).
     * @param options
     * <ul>
     * <li> category: la categoria del logger (opzionale, default: location.pathname) STRING </li>
     * </ul>
     * @return il logger
     * @type Logger
     * @throws Error se si verifica un errore nel recupero del logger
     */
	function getLogger(options)
	{
	    var popUpLayout,
	        logger = null,
	        myOptions,
	        categorySplitted,
	        i;
	    
	    try
	    {
	        // gestione options
	        myOptions = (isNullOrEmpty(options) || (typeof options !== "object")) ? {} : copy(options);
	        myOptions.category = (typeof myOptions.category !== "string") ? location.pathname : myOptions.category;
	        categorySplitted = myOptions.category.split("/");
	        if (!isNull(categorySplitted))
	        {
	            categorySplitted.reverse();
	            i = null;
	            for (i in categorySplitted)
	            {
	                if (categorySplitted.hasOwnProperty(i))
	                {
	                    myOptions.category = categorySplitted[i];
	                    if (!isNullOrEmpty(myOptions.category))
	                    {
	                        break;
	                    }
	                }
	            }
	        }
	        
	        // recupero logger
	        popUpLayout = new log4javascript.PatternLayout("%d [%5p] [%c] %m%n");
	        
	        //popUpAppender = new log4javascript.InPageAppender();
	        popUpAppender = new log4javascript.PopUpAppender();
	        //popUpAppender.setInitiallyMinimized(true);
	        popUpAppender.setLayout(popUpLayout);
	        
	        logger = log4javascript.getLogger(myOptions.category);
	        logger.addAppender(popUpAppender);
	        logger.setLevel(log4javascript.Level.ALL);
	        
	        if (logger.isInfoEnabled())
	        {
	            logger.info("getLogger - logger recuperato");
	        }
	        if (logger.isDebugEnabled())
	        {
	            logger.debug("getLogger - options:\n" + toStringAsLiteral(options));
	        }
	        if (logger.isTraceEnabled())
	        {
	            logger.trace("getLogger - options:\n" + toStringAsLiteral(myOptions));
	        }
	    }
	    catch (e)
	    {
	        alert(e);
	
	        throw e;
	    }
	    finally
	    {
	        return logger;
	    }
	}
	
	/**
	 * La funzione rilascia il logger (log4javascript).
	 * @return null
	 */
	function hideLogger()
	{
	    try
	    {
	        popUpAppender.hide();
	    }
	    catch (e)
	    {
	    }
	}
    
	function showLogger()
	{
		try
	    {
			popUpAppender.show();
			popUpAppender.focus();
		}
	    catch (e)
	    {
	    }
	}

    /**
     * La variabile rappresenta il logger utilizzabile dal codice javascript.
     */
    logger = isNull(logger) ? getLogger() : logger;
    
    /**
     * La funzione recupera un oggetto XMLHttpRequest o equivalente.
     * @return l'oggetto
     * @type XMLHttpRequest o ActiveXObject
     * @throws Error se si verifica un errore nel recupero dell'oggetto
     */
    function getXMLHttpRequest()
    {
        var request = null;
        try
        {
            // Firefox, Opera 8.0+, Safari (anche Internet Explorer 7)
            request = new XMLHttpRequest();
        }
        catch (e)
        {
            // Internet Explorer
            try
            {
                request = new ActiveXObject("Msxml2.XMLHTTP");
            }
            catch (ee)
            {
                try
                {
                    request = new ActiveXObject("Microsoft.XMLHTTP");
                }
                catch (eee)
                {
                    if (logger.isErrorEnabled())
                    {
                        logger.error("getXMLHttpRequest - ", eee);
                    }

                    alert("Il tuo browser non supporta AJAX!");

                    throw eee;
                }
            }
        }
        finally
        {
            return request;
        }
    }

    /**
     * La funzione sostituisce la funzione eval().
     * @param script il codice javascript da eseguire (opzionale, default: null) STRING
     * @return null
     */
    function goodEval(script)
    {
        if (typeof script === "string")
        {
            var scriptElement = document.createElement("script");
            scriptElement.type = "text/javascript";
            try
            {
                scriptElement.innerHTML = script;
            }
            catch (e)
            {
                // Internet Explorer
                scriptElement.text = script;
            }
            $(document).ready(function () {
                document.getElementsByTagName("body")[0].appendChild(scriptElement);
                document.getElementsByTagName("body")[0].removeChild(scriptElement);
            });
        }
    }

    /**
     * La funzione cerca e valuta il javascript inline presente nel codice html fornito.
     * @param html il codice html in cui cercare (opzionale, default: null) STRING
     * @return null
     */
    function htmlEval(html)
    {  
        try
        {
            if (logger.isDebugEnabled())
            {
                logger.debug("htmlEval - inizio");
            }
            if (logger.isTraceEnabled())
            {
                logger.trace("htmlEval - html: " + html);
            }

            // cerco gli elementi <script> di tipo "text/javascript"
            if (typeof html === "string")
            {
                var startScriptTagRegExp = new RegExp("<(script|SCRIPT)\\s+(type|TYPE)\\s*=\\s*\"text\/javascript\"\\s*>"),
                    endScriptTagRegExp = new RegExp("<\/(script|SCRIPT)\\s*>"),
                    script,
                    myHtml = html,
                    startScriptTagIndex = myHtml.search(startScriptTagRegExp),
                    endScriptTagIndex = startScriptTagIndex + myHtml.substring(startScriptTagIndex).search(endScriptTagRegExp);
                while (startScriptTagIndex !== -1 && endScriptTagIndex !== -1)
                {
                    if (isNull(script))
                    {
                        script = [];
                    }
                    script[script.length] = myHtml.substring(startScriptTagIndex, endScriptTagIndex).replace(startScriptTagRegExp, "");

                    if (logger.isTraceEnabled())
                    {
                        logger.trace("htmlEval - javascript fragment: " + script[script.length - 1]);
                    }

                    myHtml = myHtml.substring(endScriptTagIndex).replace(endScriptTagRegExp, "");
                    startScriptTagIndex = myHtml.search(startScriptTagRegExp);
                    endScriptTagIndex = startScriptTagIndex + myHtml.substring(startScriptTagIndex).search(endScriptTagRegExp);
                }
                if (!isNull(script))
                {
                    if (logger.isDebugEnabled())
                    {
                        logger.debug("htmlEval - javascript: " + script.join("\n"));
                    }
                    
                    goodEval(script.join("\n"));
                }
            }

            if (logger.isDebugEnabled())
            {
                logger.debug("htmlEval - fine");
            }
        }
        catch (e)
        {
            if (logger.isErrorEnabled())
            {
                logger.error("htmlEval - ", e);
            }
        }
    }

    /**
     * La funzione cerca e carica il css inline e/o esterno presente nel codice html fornito.
     * @param html il codice html in cui cercare (opzionale, default: null) STRING
     * @return null
     */
    function htmlCSSEval(html)
    {  
        try
        {
            if (logger.isDebugEnabled())
            {
                logger.debug("htmlCSSEval - inizio");
            }
            if (logger.isTraceEnabled())
            {
                logger.trace("htmlCSSEval - html: " + html);
            }

            if (typeof html === "string")
            {
                // cerco gli elemnti <link> di tipo "text/ccs"
                // TODO (foreach <link> chiamo loadCSS)
                // cerco gli elementi <style> di tipo "text/css"
                // TODO (forEach <style> creo l'elemento e lo appendo al body, un po' come faccio nella funzione goodEval per il javascript)
            }

            if (logger.isDebugEnabled())
            {
                logger.debug("htmlCSSEval - fine");
            }
        }
        catch (e)
        {
            if (logger.isErrorEnabled())
            {
                logger.error("htmlCSSEval - ", e);
            }
        }
    }
    
    /**
     * La funzione costruisce la parte di query string relativa ai parametri da passare ad una componente applicativa
     * @param component la componente applicativa
     * <ul>
     * <li> url la url della componente (obbligatoria) STRING </li>
     * <li> options le opzioni della richiesta XMLHttp (opzionale, si veda la funzione ajax(url, options)) </li>
     * </ul>
     * @return la parte di query string
     * @type string
     */
    function getComponentParams(component)
    {
        var params = "", element, i, param, value;
        
        try
        {
            element = $("#" + component.options.target);
            if (element.length > 0 && component.options.params instanceof Array)
	        {
				for (i = 0; i < component.options.params.length; i = i + 1)
	            {
					param = $(".ajaxParam#" + component.options.params[i] + ":first", element);
					value = $.trim(param.text());
					if (param.length > 0 && value !== "")
	                {
						params = params + ((params !== "") ? "&" : "") + param.attr("id") + "=" + value;
					}
				}
			}
        }
        catch (e)
        {
            if (logger.isErrorEnabled())
            {
                logger.error("getComponentParams - ", e);
            }
        }
	    finally
	    {
	        return params;
	    }
	}

    /**
     * La funzione esegue una richiesta XMLHttp.
     * @param url la url richiesta (obbligatoria) STRING
     * @param options
     * <ul>
     * <li> method: "POST"|"GET" (opzionale, default: POST) STRING </li>
     * <li> async: true|false (opzionale, default: true) BOOLEAN </li>
     * <li> softSync: true|false (per evitare richieste ripetute) (opzionale, default: true) BOOLEAN </li>
     * <li> target: l'id dell'elemento in cui includere la risposta (opzionale, default: null) STRING </li>
     * <li> javascript: true|false (per eseguire javascript presente nella risposta) (opzionale, default: true) BOOLEAN </li>
     * <li> css: true|false (per caricare css presente nella risposta, inline ed esterno) (opzionale, default: true) BOOLEAN </li>
     * <li> success: la funzione da eseguire nel caso di esito positivo (opzionale, default: null) FUNCTION </li>
     * <li> response: true|false (per manipolare la risposta) (opzionale, default: false) BOOLEAN </li>
     * <li> data: i dati di cui fare il POST (opzionale, default: "") STRING </li>
     * <li> params: i parametri da concatenare all'url (opzionale, default: null) ARRAY di STRING </li>
     * </ul>
     * @return null
     */
    function ajax(url, options)
    {
        var targetElement,
            semaforo,
            request,
            completeUrl = "";
        function removeLock()
        {
            if (!isNullOrEmpty(options))
            {
                // semaforo applicabile
                if (options.softSync)
                {
                    semaforo = document.getElementById("semaforo_" + options.target);
                    if (!isNull(semaforo))
                    {
                        // rimuovo il semaforo
                        targetElement = document.getElementById(options.target);
                        if (!isNull(targetElement))
                        {
                            targetElement.removeChild(semaforo);
                        }
                        else
                        {
                            document.getElementsByTagName("body")[0].removeChild(semaforo);
                        }
        
                        if (logger.isDebugEnabled())
                        {
                            logger.debug("ajax - " + url + " - semaforo rimosso: semaforo_" + options.target);
                        }
                    }
                }
            }
        }
        function onComplete()
        {
            if (request.readyState === 4)
            {
                if (request.status === 200)
                {
                    if (logger.isTraceEnabled())
                    {
                        logger.trace("ajax - " + url + " - responseText: " + request.responseText);
                    }

                    if (!isNull(targetElement))
                    {
                        if (logger.isTraceEnabled())
                        {
                            logger.trace("ajax - " + url + " - innerHTML: " + targetElement.innerHTML);
                        }

                        // TODO verificare che l'esecuzione sia sincrona
                        targetElement.innerHTML = request.responseText;
                    }
                    if (options.javascript && !options.response)
                    {
                        htmlEval(request.responseText);
                    }
                    if (options.css && !options.response)
                    {
                        htmlCSSEval(request.responseText);
                    }

                    if (logger.isDebugEnabled())
                    {
                        logger.debug("ajax - " + url + " - fine esito positivo");
                    }

                    // funzione in cascata
                    if (typeof options.success === "function")
                    {
                        if (options.response)
                        {
                            options.success(request.responseText);
                        }
                        else
                        {
                            options.success();
                        }
                    }
                }
                else
                {
                    if (logger.isErrorEnabled())
                    {
                        logger.error("ajax - " + url + " - status: " + request.status);
                    }
                }
                removeLock();
            }
        }
        try
        {
            if (logger.isDebugEnabled())
            {
                logger.debug("ajax - " + url + " - inizio");
                logger.debug("ajax - " + url + " - url: " + url);
                logger.debug("ajax - " + url + " - options:\n" + toStringAsLiteral(options));
            }

            // gestione parametri
            if (isNull(url))
            {
                throw new Error("Parametro obbligatorio url nullo.");
            }
            else if (typeof url !== "string")
            {
                throw new Error("Tipo parametro obbligatorio url non corretto: " + typeof url + ".");
            }   
            // gestione options
            options = isNullOrEmpty(options) ? {} : copy(options);
            options.method = options.method !== "GET" ? "POST" : options.method;
            options.async = options.async !== false ? true : options.async;
            options.softSync = options.softSync !== false ? true : options.softSync;
            options.target = typeof options.target !== "string" ? null : options.target;
            options.javascript = options.javascript !== false ? true : options.javascript;
            options.css = options.css !== false ? true : options.css;
            options.success = typeof options.success !== "function" ? null : options.success;
            options.response = options.response !== true ? false : options.response;
            options.data = typeof options.data !== "string" ? "" : options.data;
            options.params = !(options.params instanceof Array) ? null : options.params;

            if (logger.isTraceEnabled())
            {
                logger.trace("ajax - " + url + " - options:\n" + toStringAsLiteral(options));
            }

            // semaforo assente o non applicabile: posso eseguire la chiamata
            if (!options.softSync || (options.softSync && (isNull(document.getElementById("semaforo_" + options.target)))))
            {
                targetElement = document.getElementById(options.target);
                
                // semaforo applicabile
                if (options.softSync && options.target !== null)
                {
                    // creo il semaforo
                    semaforo = document.createElement("div");
                    semaforo.setAttribute("id", "semaforo_" + options.target);
                    if (!isNull(targetElement))
                    {
                        targetElement.appendChild(semaforo);
                    }
                    else
                    {
                        document.getElementsByTagName("body")[0].appendChild(semaforo);
                    }

                    if (logger.isDebugEnabled())
                    {
                        logger.debug("ajax - " + url + " - semaforo creato: semaforo_" + options.target);
                    }
                }

                // recupero l'oggetto XMLHttpRequest o equivalente
                request = getXMLHttpRequest();
                
                // gestisco l'esito della chiamata ajax asincrona
                if (options.async)
                {
                    request.onreadystatechange = onComplete;
                }
                
                // costruisco la url, completa di parametri
                if (!isNull(options.params))
                {
                    completeUrl = getComponentParams({ url : url, options : options });
                    if (!isNullOrEmpty(options.params))
                    {
                        completeUrl = (url.search("[?]") !== -1 ? "&" : "?") + completeUrl;
                    }
                }

                // eseguo la chiamata ajax
                request.open(options.method, url + completeUrl, options.async);
                
                /*
                 * TODO gestione form submit
                 * gestire questi due metodi con le options passate al metodo?
                 * setRequestHeader(header, value):
                 * "Content-Type": "application/x-www-form-urlencoded"|"multipart/form-data" (form semplice|file upload) (utile solo per POST)
                 * send(data):
                 * per fare il submit di una form... e per file upload?
                 * indagare su ajax form submit e file upload
                 */
                request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
                request.setRequestHeader("Content-length", options.data.length);
                request.send(options.data);

                // gestisco l'esito della chiamata ajax sincrona
                if (!options.async)
                {
                    onComplete();
                }
            }
            // semaforo presente: non eseguo la chiamata
            else
            {
                if (logger.isDebugEnabled())
                {
                    logger.debug("ajax - " + url + " - semaforo presente: semaforo_" + options.target);
                }
            }

            if (logger.isDebugEnabled())
            {
                logger.debug("ajax - " + url + " - fine");
            }
        }
        catch (e)
        {
            if (logger.isErrorEnabled())
            {
                logger.error("ajax - " + url + " - ", e);
            }
            
            try
            {
                removeLock();
            }
            catch (ee)
            {
                if (logger.isErrorEnabled())
                {
                    logger.error("ajax - " + url + " - ", ee);
                }
            }
        }
    }
    
    /**
	 * La funzione carica le componenti di un'applicazione mediante ajax successive alla prima in modo asincrono.
	 * @return null
	 */
    function loadApplicationAsync(components, randomNum)
    {
        var i;
        
        try
        {
            if (logger.isDebugEnabled())
            {
                logger.debug("loadApplicationAsync - " + randomNum + " - inizio");
                logger.debug("loadApplicationAsync - " + randomNum + " - components:\n" + toStringAsLiteral(components));
            }
            for (i in components)
            {
                if (components.hasOwnProperty(i) && !isNullOrEmpty(components[i].options) && !isNull(document.getElementById(components[i].options.target)))
                {
                    ajax(components[i].url, components[i].options);
                }
            }
            if (logger.isDebugEnabled())
            {
                logger.debug("loadApplicationAsync - " + randomNum + " - fine");
            }
        }
        catch (e)
        {
            if (logger.isErrorEnabled())
            {
                logger.error("loadApplicationAsync - " + randomNum + " - ", e);
            }
        }
    }
    
    // TODO se le componenti singolarmente sono nulle? testa!
    /**
     * La funzione carica le componenti di un'applicazione mediante ajax.
     * <br> La funzione garantisce che venga creata al piÃ¹ una sessione http per applicazione eseguendo la prima richiesta XMLHttp e tutte le altre solo al completamento di questa.
     * @param components le componenti da caricare ARRAY di
     * <ul>
     * <li> url la url della componente (obbligatoria) STRING </li>
     * <li> options le opzioni della richiesta XMLHttp (opzionale, si veda la funzione ajax(url, options)) </li>
     * </ul>
     * @return null
     */
    function loadApplication(components)
    {
        var firstComponent,
            success,
            randomNum = Math.random(),
            i;
        try
        {
            if (logger.isDebugEnabled())
            {
                logger.debug("loadApplication - " + randomNum + " - inizio");
                logger.debug("loadApplication - " + randomNum + " - components:\n" + toStringAsLiteral(components));
            }

            // gestione parametri
            if (!(components instanceof Array))
            {
                throw new Error(randomNum + " Classe parametro obbligatorio portlets non corretto: atteso Array.");
            }
            
            for (i in components)
            {
                if (components.hasOwnProperty(i))
                {
                    if (!isNull(document.getElementById(components[i].options.target)))
                    {
                        break;
                    }
                }
            }
            firstComponent = copy(components[i]);
            // copio components in locale
            components = copy(components.slice(Number(i) + 1));
            if (logger.isDebugEnabled())
            {
                logger.debug("loadApplication - " + randomNum + " - localComponents:\n" + toStringAsLiteral(components));
            }
            firstComponent.options = isNullOrEmpty(firstComponent.options) ? {} : copy(firstComponent.options);
            if (typeof firstComponent.options.success === "function")
            {
                success = function () {
                    firstComponent.options.success();
                    loadApplicationAsync(components, randomNum);
                };
            }
            else
            {
                success = function () {
                    loadApplicationAsync(components, randomNum);
                };
            }
            firstComponent.options.success = success;
            if (!isNull(document.getElementById(firstComponent.options.target)))
            {
                ajax(firstComponent.url, firstComponent.options);
            }

            if (logger.isDebugEnabled())
            {
                logger.debug("loadApplication - " + randomNum + " - fine");
            }
        }
        catch (e)
        {
            if (logger.isErrorEnabled())
            {
                logger.error("loadApplication - " + randomNum + " - ", e);
            }
        }
    }

    
    /**
     * La funzione effettua la pulizia di tutte le componenti ricevute come parametro
     * @return null
     */
    function unloadComponents(components)
    {
        var i, targetElement;
        
        try
        {
            if (logger.isDebugEnabled())
            {
                logger.debug("unloadComponents - components:\n" + toStringAsLiteral(components));
            }
            for (i in components)
            {
                if (components.hasOwnProperty(i) && !isNullOrEmpty(components[i].options) && !isNull(document.getElementById(components[i].options.target)))
                {
					targetElement = document.getElementById(components[i].options.target);
                    if (!isNull(targetElement)) {
						targetElement.innerHTML = "";
                    }
                }
            }
            if (logger.isDebugEnabled())
            {
                logger.debug("unloadComponents - fine");
            }
        }
        catch (e)
        {
            if (logger.isErrorEnabled())
            {
                logger.error("unloadComponents - ", e);
            }
        }
    }
    
    /**
     * La funzione aggiunge alle varie url i parametri che sono necessari
     * @return null
     */
    function addParameters(components, parameters)
    {
        var i, k, separator;

        try
        {
            if (logger.isDebugEnabled())
            {
                logger.debug("addParameters - components:\n" + toStringAsLiteral(components));
                logger.debug("addParameters - parameters:\n" + toStringAsLiteral(parameters));
            }
            components = copy(components);
            for (i in components)
            {
                if (components.hasOwnProperty(i))
                {
                    for (k in parameters)
                    {
                        if (parameters.hasOwnProperty(k)) {
                            separator = components[i].url.search("[?]") !== -1 ? "&" : "?";
                            components[i].url = components[i].url + separator + k + "=" + parameters[k];
                        }
                    }
                }
            }
            if (logger.isDebugEnabled())
            {
                logger.debug("addParameters - components:\n" + toStringAsLiteral(components));
            }
            return components;
        }
        catch (e)
        {
            if (logger.isErrorEnabled())
            {
                logger.error("addParameters - ", e);
            }
        }
        return null;
    }
    
    /**
     * La funzione inverte la visibilità di un elemento.
     * @param element l'id dell'elemento di cui invertire la visibilità (obbligatorio) STRING
     * @param options
     * <ul>
     * <li> display: "block"|"inline" (opzionale, default: "block") STRING </li>
     * </ul>
     * @return la visibilità corrente
     * @type string
     */
    function switchDisplay(element, options)
    {
        var display, el;
        try
        {
            if (logger.isDebugEnabled())
            {
                logger.debug("switchDisplay - inizio");
                logger.debug("switchDisplay - element: " + element);
                logger.debug("switchDisplay - options:\n" + toStringAsLiteral(options));
            }

            // gestione parametri
            if (isNull(element))
            {
                throw new Error("Parametro obbligatorio element nullo.");
            }
            else if (typeof element !== "string")
            {
                throw new Error("Tipo parametro obbligatorio element non corretto: " + typeof element + ".");
            }   
            // gestione options
            options = isNullOrEmpty(options) ? {} : copy(options);
            options.display = options.display !== "inline" ? "block" : options.display;

            if (logger.isTraceEnabled())
            {
                logger.trace("ajax - options:\n" + toStringAsLiteral(options));
            }
            
            el = document.getElementById(element);
            if (!isNull(el))
            {
                if (el.style.display === "none")
                {
                    display = options.display;
                }
                else
                {
                    display = "none";
                }
                el.style.display = display;
            }

            if (logger.isDebugEnabled())
            {
                logger.debug("switchDisplay - fine");
            }
        }
        catch (e)
        {
            if (logger.isErrorEnabled())
            {
                logger.error("switchDisplay - ", e);
            }
        }
        finally
        {
            return display;
        }
    }
    
	function setDefaultDomain(domain)
	{
	    if (!isNullOrEmpty(domain))
	    {
	        defaultDomain = domain;
	    }
	    else
	    {
	        /*var hostname = location.hostname.split(".").reverse();
	        defaultDomain = hostname[1] + "." + hostname[0];*/
	        defaultDomain = location.hostname;
            if (logger.isDebugEnabled())
            {
                logger.debug("setDefaultDomain - defaultDomain: " + defaultDomain);
            }
	    }
	}
    
    function getDefaultDomain()
    {
        if (isNullOrEmpty(defaultDomain))
        {
            defaultDomain = location.hostname;
            if (logger.isDebugEnabled())
            {
                logger.debug("getDefaultDomain - defaultDomain: " + defaultDomain);
            }
        }
        return defaultDomain;
    }

    // TODO jsdoc
    function setCookie(name, options)
    {   
        var cookie,
            cookieExpireDate,
            minutesInADay = 60 * 24;
        
        try
        {
            if (logger.isDebugEnabled())
            {
                logger.debug("setCookie - inizio");
                logger.debug("setCookie - name: " + name);
                logger.debug("setCookie - options:\n" + toStringAsLiteral(options));
            }

            // gestione parametri
            if (isNull(name))
            {
                throw new Error("Parametro obbligatorio name nullo.");
            }
            else if (typeof name !== "string")
            {
                throw new Error("Tipo parametro obbligatorio name non corretto: " + typeof name + ".");
            }   
            // gestione options
            options = isNullOrEmpty(options) ? {} : copy(options);
            options.value = typeof options.value !== "string" ? null : options.value;
            options.days = isNaN(options.days) ? null : options.days;
            options.secure = options.secure !== true ? false : options.secure;
            options.domain = typeof options.domain !== "string" ? null : options.domain;
            options.path = typeof options.path !== "string" ? "/" : options.path;

            if (logger.isTraceEnabled())
            {
                logger.trace("setCookie - options:\n" + toStringAsLiteral(options));
            }
            
            cookie = encodeURIComponent(name) + "=" + encodeURIComponent(options.value);
            if (!isNullOrEmpty(options.days))
            {
                cookieExpireDate = new Date();
                cookieExpireDate.setMinutes(cookieExpireDate.getMinutes() + (options.days * minutesInADay));
                cookie = cookie + "; expires=" + cookieExpireDate.toGMTString();
            }
            if (options.secure)
            {
                cookie = cookie + "; secure";
            }
            
            if (!isNullOrEmpty(options.domain))
            {
                cookie = cookie + "; domain=" + encodeURIComponent(options.domain);
            } else
			if (!isNullOrEmpty(defaultDomain))
			{
				cookie = cookie + "; domain=" + encodeURIComponent(defaultDomain);
			}
            
            if (!isNullOrEmpty(options.path))
            {
                cookie = cookie + "; path=" + encodeURI(options.path);
            }
            
            document.cookie = cookie;

            if (logger.isDebugEnabled())
            {
                logger.debug("setCookie - fine");
            }
        }
        catch (e)
        {
            if (logger.isErrorEnabled())
            {
                logger.error("setCookie - ", e);
            }
        }
    }
    
	// TODO jsdoc (mantenere?...)
	function unsetCookie(name, options)
	{
		options = isNullOrEmpty(options) ? {} : copy(options);
		options.domain = typeof options.domain !== "string" ? null : options.domain;
        options.secure = options.secure !== true ? false : options.secure;
		
		setCookie(name, { value: null, days: -1, secure: options.secure, domain: options.domain});
	}
    
    // TODO jsdoc
    function getCookie(name)
    {
        var cookies,
            cookie,
            cookieIndex,
            myName,
            i;
        
        try
        {
            if (logger.isDebugEnabled())
            {
                logger.debug("getCookie - inizio");
                logger.debug("getCookie - name: " + name);
            }

            // gestione parametri
            if (isNull(name))
            {
                throw new Error("Parametro obbligatorio name nullo.");
            }
            else if (typeof name !== "string")
            {
                throw new Error("Tipo parametro obbligatorio name non corretto: " + typeof name + ".");
            }
            
            cookies = document.cookie.split(";");
            for (i in cookies)
            {
                if (cookies.hasOwnProperty(i))
                {
                    cookie = cookies[i];
                    myName = encodeURIComponent(name) + "=";
                    cookieIndex = cookie.indexOf(myName);
                    if (cookieIndex !== -1)
                    {
                        return decodeURIComponent(cookie.substring(cookieIndex + myName.length));
                    }
                }
            }

            if (logger.isDebugEnabled())
            {
                logger.debug("getCookie - fine");
            }
            
            return null;
        }
        catch (e)
        {
            if (logger.isErrorEnabled())
            {
                logger.error("getCookie - ", e);
            }
        }
    }
    
    // TODO jsdoc
    function getCookieField(cookie, field)
    {
        var myCookie,
            fields,
            myField,
            name,
            value,
            i;
        
        try
        {
            if (logger.isDebugEnabled())
            {
                logger.debug("getCookieField - inizio");
                logger.debug("getCookieField - cookie: " + cookie);
                logger.debug("getCookieField - field: " + field);
            }

            // gestione parametri
            if (isNull(cookie))
            {
                throw new Error("Parametro obbligatorio cookie nullo.");
            }
            else if (isNull(field))
            {
                throw new Error("Parametro obbligatorio field nullo.");
            }
            else if (typeof cookie !== "string")
            {
                throw new Error("Tipo parametro obbligatorio cookie non corretto: " + typeof cookie + ".");
            }
            else if (typeof field !== "string")
            {
                throw new Error("Tipo parametro obbligatorio field non corretto: " + typeof field + ".");
            }
            
            // recupero il cookie
            myCookie = getCookie(cookie);
            if (!isNull(myCookie))
            {
                // cerco il campo
                fields = myCookie.split(";");
                for (i in fields)
                {
                    if (fields.hasOwnProperty(i))
                    {
                        myField = fields[i].split("=");
                        if (myField[0] === field)
                        {
                            return myField[1].replace(/\+/g, " ");
                        }
                    }
                }
            }

            if (logger.isDebugEnabled())
            {
                logger.debug("getCookieField - fine");
            }
            
            return null;
        }
        catch (e)
        {
            if (logger.isErrorEnabled())
            {
                logger.error("getCookieField - ", e);
            }
        }
    }
    
	/**
	 * ricava il parametro dalla pagina
	 * @param parameter il nome del parametro di cui si vuole il valore
	 * @return il valore del parametro richiesto
	 * @type string
	 */
	function getParameter(parameter)
	{
		var queryString, paramRegExp, result;
		queryString = location.search;
		//paramRegExp = new RegExp("[\\?&]" + parameter + "=(\\w*)");
		paramRegExp = new RegExp("[\\?&]" + parameter + "=([^&#]*)");
		result = queryString.match(paramRegExp);
		return result === null ? null : decodeURIComponent(result[1]);
    }

    /**
     * La funzione carica un file css.
     * @param url l'url del file css da caricare (obbligatorio) STRING
     * @return null
     */
    function loadCSS(url)
    {
        if (typeof url === "string")
        {
            var linkElement = document.createElement("link");
            linkElement.type = "text/css";
            linkElement.rel = "stylesheet";
            linkElement.href = url;
            document.getElementsByTagName("head")[0].appendChild(linkElement);
        }
        else
        {
            throw new Error("Parametro obbligatorio url nullo.");
        }
    }
	
	
	function matchRegExp(e, reg)
    {
        var key = null,
            keychar = null
            ;
        key = window.event ? e.keyCode : e.which;
        keychar = String.fromCharCode(key);
		//alert(key);
        if (key === 8 || key === 0)
        {    
            return true;
        }    
        else
        {    
            return reg.test(keychar);
        }    
    }
    
	return {
		isNull: isNull,
		isNullOrEmpty: isNullOrEmpty,
		copy: copy,
		toStringAsLiteral: toStringAsLiteral,
		hideLogger: hideLogger,
		showLogger: showLogger,
		logger: logger,
		htmlEval: htmlEval,
		ajax: ajax,
		loadApplication: loadApplication,
		unloadComponents: unloadComponents,
		addParameters: addParameters,
		switchDisplay: switchDisplay,
        setDefaultDomain: setDefaultDomain,
        getDefaultDomain: getDefaultDomain,
		setCookie: setCookie,
        unsetCookie: unsetCookie,
        getCookie: getCookie,
        getCookieField: getCookieField,
        getParameter: getParameter,
        loadCSS: loadCSS,
		matchRegExp : matchRegExp
	};
}());

if (SISAL.logger.isInfoEnabled())
{
    SISAL.logger.info("SISAL - namespace creato");
}
