/*
   forms.js
   ----------------------------------------
   Funzioni di utilita' per controlli form.
*/

/* Costanti per selezione direzione ordinamento lista record */
var SORT_ASC  = 'A';
var SORT_DESC = 'D';

/*
   Costante per separatore di decimali (default)
*/
var DEFAULT_DECIMAL_SEPARATOR = ".";

/*
   Costante per separatore di migliaia (default)
*/
var DEFAULT_THOUSAND_SEPARATOR = ",";

/*
   Separatore valori per select multiple
*/
var MULTIPLE_SELECT_VALUES_SEPARATOR = "|";

/*
   Vettore ordinato contenente tasti non consentiti.

   Backspace
*/
var keysToIgnore = new Array([8], [116]);


/*
   Ritorna se il carattere (in codifica ASCII) passato come parametro
   e' tra quelli speciali (meta-caratteri).
*/
function isMetaKey(event) {
   return (event.altKey || event.ctrlKey || event.shiftKey || event.metaKey);
}


/*
   Ritorna se il carattere (in codifica ASCII) passato come parametro
   e' una freccia direzionale.
*/
function isArrowKey(event) {
   if (!event)
      var event = window.event;

   if (event.keyCode)
      code = event.keyCode;
   else if (event.which)
      code = event.which;

   if (!isMetaKey(event) && code >= 37 && code <= 40)
     return true;

   return false;
}


/*
   Ritorna se il carattere (in codifica ASCII) passato come parametro
   e' contenuto nel vettore dei tasti da ignorare.
*/
function isKeyToIgnore(key) {
   for (i = 0; i < keysToIgnore.length; i++) {
      if (key == keysToIgnore[i])
         return true;

      /* Il vettore e' ordinato */
      if (keysToIgnore[i] > key)
         return false;
   }

   return false;
}


/*
   Listener per evento onKeyDown.
   Intercetta e previene l'inserimento di uno o piu' tasti
   definiti nel corpo del metodo.
*/
function detectKeysToIgnore(event) {
   if (!event)
      var event = window.event;

   if (event.keyCode)
      code = event.keyCode;
   else if (event.which)
      code = event.which;

   if (isKeyToIgnore(code)) {
      if (event.preventDefault) {  // NS
         /*
            Se l'evento e' stato generato in corrispondenza di un campo
            di input testuale, si prosegue senza invalidarne il bubbling
            (ossia lasciando che il tasto venga effettivamente premuto).
            Differentemente si impedirebbe all'utente di poter utilizzare
            certi tasti per l'immissione di testo (ad es. il tasto di BACK
            per correggere un valore).
         */
         if (event.target.nodeName.toUpperCase() != "INPUT" &&
             event.target.nodeName.toUpperCase() != "TEXTAREA")
            event.preventDefault();
      } else {                     // IE
         /*
            Se l'evento e' stato generato in corrispondenza di un campo
            di input testuale, si prosegue senza invalidarne il bubbling
            (ossia lasciando che il tasto venga effettivamente premuto).
            Differentemente si impedirebbe all'utente di poter utilizzare
            certi tasti per l'immissione di testo (ad es. il tasto di BACK
            per correggere un valore).
         */
         if (event.srcElement.nodeName.toUpperCase() != "INPUT" &&
             event.srcElement.nodeName.toUpperCase() != "TEXTAREA")
            event.keyCode = 0;
      }
   }
}


/*
   Listener per evento onKeyPress.
   Controllo sintattico, non semantico.
   Consente la sola immissione di lettere minuscole [a-z].
   In caso contrario, l'evento viene annullato.
*/
function requestLowercaseLetter(event) {
   if (!event)
      var event = window.event;

   if (event.keyCode)
      code = event.keyCode;
   else if (event.which)
      code = event.which;

   if (!isArrowKey(event) && code >= 32 &&
       (code < 65 || code > 90)) { /* a-z */
      cancelEvent(event);
   }

   return;
}


/*
   Listener per evento onKeyPress.
   Controllo sintattico, non semantico.
   Consente la sola immissione di lettere maiuscole [A-Z].
   In caso contrario, l'evento viene annullato.
*/
function requestUppercaseLetter(event) {
   if (!event)
      var event = window.event;

   if (event.keyCode)
      code = event.keyCode;
   else if (event.which)
      code = event.which;

   if (!isArrowKey(event) && code >= 32 &&
       (code < 97 || code > 122)) {
      cancelEvent(event);
   }

   return;
}


/*
   Listener per evento onKeyPress.
   Controllo sintattico, non semantico.
   Consente la sola immissione di lettere [a-zA-Z].
   In caso contrario, l'evento viene annullato.
*/
function requestLetter(event) {
   if (!event)
      var event = window.event;

   if (event.keyCode)
      code = event.keyCode;
   else if (event.which)
      code = event.which;

   if (!isArrowKey(event) && code >= 32 &&
       !((code >= 65 && code <= 90) || (code >= 97 && code <= 122))) {
      cancelEvent(event);
   }

   return;
}


/*
   Listener per evento onKeyPress.
   Controllo sintattico, non semantico.
   Consente la sola immissione di cifre intere positive [0-9].
   In caso contrario, l'evento viene annullato.
*/
function requestDigit(event) {
   if (!event)
      var event = window.event;

   if (event.keyCode)
      code = event.keyCode;
   else if (event.which)
      code = event.which;

   if (!isArrowKey(event) && code >= 32 &&
       (code < 48 || code > 57)) {
      cancelEvent(event);
   }

   return;
}


/*
   Listener per evento onKeyPress.
   Controllo sintattico, non semantico.
   Consente la sola immissione di cifre decimali [-][0-9][.].
   In caso contrario, l'evento viene annullato.
*/
function requestDecimalDigit(event) {
   if (!event)
      var event = window.event;

   if (event.keyCode)
      code = event.keyCode;
   else if (event.which)
      code = event.which;

   if (!isArrowKey(event) && code >= 32 &&
       !((code >= 48 && code <= 57) || (code > 44 && code <= 46))) {
      cancelEvent(event);
   }

   return;
}


/*
   Listener per evento onKeyPress.
   Controllo sintattico, non semantico.
   Consente la sola immissione di cifre decimali [0-9][.].
   In caso contrario, l'evento viene annullato.
*/
function requestDecimalPositiveDigit(event) {
   if (!event)
      var event = window.event;

   if (event.keyCode)
      code = event.keyCode;
   else if (event.which)
      code = event.which;

   if (!isArrowKey(event) && code >= 32 &&
       !((code >= 48 && code <= 57) || (code > 44 && code <= 46 && code != 45))) {
      cancelEvent(event);
   }

   return;
}


/*
   Listener per evento onKeyPress.
   Controllo sintattico, non semantico.
   Consente la sola immissione di cifre intere positive [0-9]
   o lettere [a-zA-Z] (eventualmente anche il simbolo di underscore).
   In caso contrario, l'evento viene annullato.
*/
function requestDigitOrLetter(event,
                              acceptUnderscore) {
   if (!event)
      var event = window.event;

   if (event.keyCode)
      code = event.keyCode;
   else if (event.which)
      code = event.which;

   if (!isArrowKey(event) && code >= 32 &&
       !((code >= 48 && code <= 57) || (code >= 65 && code <= 90) || (code >= 97 && code <= 122)) &&
      (acceptUnderscore && code != 95)) {
      cancelEvent(event);
   }

   return;
}


/*
   Listener per evento onKeyPress.
   Controllo sintattico, non semantico.
   Consente la sola immissione di date [0-9][-./].
   In caso contrario, l'evento viene annullato.
*/
function requestDateChar(event) {
   if (!event)
      var event = window.event;

   if (event.keyCode)
      code = event.keyCode;
   else if (event.which)
      code = event.which;

   if (!isArrowKey(event) && code >= 32 &&
       (code < 45 || code > 57)) {
      cancelEvent(event);
   }

   return;
}


/*
   Listener per evento onKeyPress.
   Controllo sintattico, non semantico.
   Consente la sola immissione di orari [0-9][.:].
   In caso contrario, l'evento viene annullato.
*/
function requestTimeChar(event) {
   if (!event)
      var event = window.event;

   if (event.keyCode)
      code = event.keyCode;
   else if (event.which)
      code = event.which;

   if (!isArrowKey(event) && code >= 32 &&
       ((code < 48 || code > 57) && code != 46 && code != 58)) {
      cancelEvent(event);
   }

   return;
}


/*
   Listener per evento onKeyPress.
   Controllo sintattico, non semantico.
   Consente la sola immissione dei caratteri [a-zA-Z0-9_-.]
   al fine di avere codici (id) sintatticamente validi.
   In caso contrario, l'evento viene annullato.

   Il parametro acceptWildcards indica se sono ammessi
   anche eventuali caratteri jolly per la ricerca sui
   codici (? e *).
*/
function requestIdCharacter(event,
                            acceptWildcards) {
  if (!event)
    var event = window.event;

    if (event.keyCode)
      code = event.keyCode;
    else if (event.which)
      code = event.which;

    if (!isArrowKey(event) && code >= 32 &&
        !((code >= 48 && code <= 57) || /* 0-9 */
         (code >= 65 && code <= 90) || (code >= 97 && code <= 122) || /* a-zA-Z */
         (code == 95) || /* _ */
         (code == 45) || /* - */
         (code == 46) || /* .*/
         (acceptWildcards && code == 42) || /* * */
         (acceptWildcards && code == 63)    /* ? */)) {
      cancelEvent(event);
    }

    return;
}


/*
   Controllo semantico.
   Verifica che una stringa rappresenti un intero [-]?[0-9]+.
   Ritorna true se il valore e' numerico, false altrimenti.
*/
function isInteger(value,
                   zeroAdmitted) {
   if (value.search(/^[\-]?\d+$/) == -1)
      return false;

   if (!zeroAdmitted && Number(value) == 0)
      return false;

   return true;
}


/*
   Controllo semantico.
   Verifica che una stringa rappresenti un decimale [-]?[0-9]*([.][0-9]+)?.
   Ritorna true se il valore e' numerico, false altrimenti.
*/
function isDecimal(value,
                   zeroAdmitted) {
   if (value.search(/^[\-]?[\.]\d+$/) == -1 &&
       value.search(/^[\-]?\d+([\.]\d+)?$/) == -1)
      return false;

   if (!zeroAdmitted && Number(value) == 0)
      return false;

   return true;
}


/*
   Controllo semantico.
   Verifica che una stringa rappresenti un intero positivo [0-9]+.
   Ritorna true se il valore e' numerico, false altrimenti.
*/
function isPositiveInteger(value,
                           zeroAdmitted) {
   if (value.search(/^\d+$/) == -1)
      return false;

   if (!zeroAdmitted && Number(value) == 0)
      return false;

   return true;
}


/*
   Controllo semantico.
   Verifica che una stringa rappresenti un decimale positivo [0-9]*([.][0-9]+)?.
   Ritorna true se il valore e' numerico, false altrimenti.
*/
function isPositiveDecimal(value,
                           zeroAdmitted) {
   if (value.search(/^\d*([\.]\d+)?$/) == -1)
      return false;

   if (!zeroAdmitted && Number(value) == 0)
      return false;

   return true;
}


/*
   Controllo semantico.
   Verifica che una stringa rappresenti un decimale positivo [0-9]*([.][0-9]{1,3})?.
   avente fino a 3 cifre dopo la virgola (millesimi).
   Ritorna true se il valore e' numerico, false altrimenti.
*/
function isCurrencyChange(value,
                          zeroAdmitted) {
   if (value.search(/^\d*([\.]\d{1,3})?$/) == -1)
      return false;

   if (!zeroAdmitted && Number(value) == 0)
      return false;

   return true;
}


/*
   Controllo semantico.
   Verifica che il decimale specificato sia valido (ossia
   presenti al piu' i cifre intere e al piu' d cifre decimali).
   Ritorna true se il decimale e' valido, false altrimenti.
*/
function isValidDecimal(number, i, d) {
   var pattern = "^[\\-]?\\d{0," + i + "}([\\.]\\d{0," + d + "})?$";
   var re = new RegExp(pattern);
   return re.test(number);
}


/*
   Controllo semantico.
   Verifica che il decimale specificato sia valido (ossia
   presenti al piu' i cifre intere e al piu' d cifre decimali)
   e positivo.
   Ritorna true se il decimale e' valido, false altrimenti.
*/
function isValidPositiveDecimal(number, i, d) {
   var pattern = "^\\d{0," + i + "}([\\.]\\d{0," + d + "})?$";
   var re = new RegExp(pattern);
   return re.test(number);
}


/*
   Controllo semantico.
   Verifica che una stringa rappresenti un importo monetario
   (decimale fino a 15 cifre di cui 2 decimali e con separatore
   di decimali).
   Ritorna true se il valore e' un importo, false altrimenti.
*/
function isCurrency(value) {
   if (value.search(/^\d{0,15}(\.\d{1,2})?$/) == -1)
      return false;

   var nDigits = 0;

   for (var i = 0; i < value.length && value.charAt(i) != "."; i++)
      if (value.charCodeAt(i) >= 48 && value.charCodeAt(i) <= 57)
         nDigits++;

   if (nDigits > 13)
      return false;

   return true;
}


/*
   Controllo semantico.
   Verifica che una stringa rappresenti una data corretta
   (giorno su una o due cifre, separatore, mese su una o due
   cifre, separatore, anno su due, tre o quattro cifre).
   Normalizza inoltre la data nel formato GG/MM/AAAA.
   Ritorna true se il valore e' una data, false altrimenti.

   NOTA: Funzione NON i18n-safe.
*/
function isDateField(dateField) {
   /* Anno di pivoting */
   var pivotYear = 40;

   /* Controllo formato stringa */
   if (dateField.value.search(/^\d{1,2}[\/\-\.]\d{1,2}[\/\-\.]\d{2,4}$/) == -1)
      return false;

   /* Suddivisione campi data */
   var fields = dateField.value.split(/[\/\-\.]/);

   /* Normalizzazione giorno */
   if (fields[0].length == 1)
      fields[0] = "0" + fields[0];

   /* Normalizzazione mese */
   if (fields[1].length == 1)
      fields[1] = "0" + fields[1];

   /* Normalizzazione anno */
   switch (fields[2].length) {
      case 2:
         if (fields[2] <= pivotYear)
            fields[2] = new String(new Date().getFullYear()).substr(0, 2) + fields[2];
         else
            fields[2] = new String(new Date().getFullYear() - 100).substr(0, 2) + fields[2];
         break;

      case 3:
         fields[2] = new String(new Date().getFullYear() - 1000).substr(0, 1) + fields[2];
   }

   /* Merge campi */
   dateField.value = fields.join("/");

   /* Controlla correttezza formale data */
   if (!isDate(fields[0], fields[1], fields[2]))
      return false;

   return true;
}


/*
   Controllo semantico.
   Verifica che una stringa rappresenti un giorno del mese corretto
   (giorno su una o due cifre).
   Normalizza inoltre il giorno nel formato GG.
   Ritorna true se il valore e' un giorno del mese valido, false altrimenti.
*/
function isDayOfMonthField(dayOfMonthField) {
   /* Controllo formato stringa */
   if (dayOfMonthField.value.search(/^\d{1,2}$/) == -1)
      return false;

   /* Normalizzazione giorno del mese */
   if (dayOfMonthField.value.length == 1)
      dayOfMonthField.value = "0" + dayOfMonthField.value;

   /* Controlla correttezza formale orario */
   if (dayOfMonthField.value > 31)
      return false;

   return true;
}


/*
   Controllo semantico.
   Verifica che una stringa rappresenti un mese corretto
   (mese su una o due cifre).
   Normalizza inoltre il mese nel formato MM.
   Ritorna true se il valore e' un mese valido, false altrimenti.
*/
function isMonthField(monthField) {
   /* Controllo formato stringa */
   if (monthField.value.search(/^\d{1,2}$/) == -1)
      return false;

   /* Normalizzazione mese */
   if (monthField.value.length == 1)
      monthField.value = "0" + monthField.value;

   /* Controlla correttezza formale mese */
   if (monthField.value > 12)
      return false;

   return true;
}


function isDate(day,
                month,
                year) {
   /* Verifica range giorno */
   if (day < 1 || day > 31)
      return false;

   /* Verifica range mese */
   if (month < 1 || month > 12)
      return false;

   /*
      Verifica range anno

      Controlli se anno bisestile.
      Eccezioni:
      1753, entrata in vigore del calendario gregoriano
             in tutti i paesi europei;
      4902, anno bisestile anche se non dovrebbe esserlo.
   */
   if (year < 1753 || year > 4902)
      return false;

   switch (Number(month)) {
      case 2:
          if (day > 29)
             return false;

          if (day == 29)
             if (year % 4 != 0 || (year % 100 == 0 && year % 400 !=0))
                return false;

          break;

      case 4:
      case 6:
      case 9:
      case 11:
          if (day > 30)
             return false;
   }

   return true;
}


/*
   Controllo semantico.
   Verifica che una stringa rappresenti un orario corretto
   (ora su una o due cifre, separatore, minuti su una o due
   cifre).
   Normalizza inoltre la data nel formato OO:MM.
   Ritorna true se il valore e' una data, false altrimenti.
*/
function isTimeField(timeField) {
   /* Controllo formato stringa */
   if (timeField.value.search(/^\d{1,2}([\:\.]\d{2})?$/) == -1)
      return false;

   /* Suddivisione campi orario */
   var fields = timeField.value.split(/[\:\.]/);

   /* Normalizzazione ora */
   if (fields[0].length == 1)
      fields[0] = "0" + fields[0];

   /* Normalizzazione minuti */
   if (fields.length == 1)
      fields[1] = "00";

   /* Merge campi */
   timeField.value = fields.join(":");

   /* Controlla correttezza formale orario */
   if (fields[0] > 23)
      return false;

   if (fields[1] > 59)
      return false;

   return true;
}


/*
   Controllo semantico.
   Verifica che una stringa rappresenti un orario corretto
   (ora su una o due cifre).
   Normalizza inoltre l'ora nel formato OO.
   Ritorna true se il valore e' un'ora valida, false altrimenti.
*/
function isHoursField(hoursField) {
   /* Controllo formato stringa */
   if (hoursField.value.search(/^\d{1,2}$/) == -1)
      return false;

   /* Normalizzazione ora */
   if (hoursField.value.length == 1)
      hoursField.value = "0" + hoursField.value;

   /* Controlla correttezza formale orario */
   if (hoursField.value > 23)
      return false;

   return true;
}


/*
   Controllo semantico.
   Verifica che una stringa rappresenti un orario corretto
   (minuti su una o due cifre).
   Normalizza inoltre l'ora nel formato MM.
   Ritorna true se il valore e' un'ora valida, false altrimenti.
*/
function isMinutesField(minutesField) {
   /* Controllo formato stringa */
   if (minutesField.value.search(/^\d{1,2}$/) == -1)
      return false;

   /* Normalizzazione minuti */
   if (minutesField.value.length == 1)
      minutesField.value = "0" + minutesField.value;

   /* Controlla correttezza formale orario */
   if (minutesField.value > 59)
      return false;

   return true;
}


/*
   Controllo semantico.
   Verifica che una stringa rappresenti un orario corretto
   (minuti su una o due cifre).
   Normalizza inoltre l'ora nel formato MM.
   Ritorna true se il valore e' un'ora valida, false altrimenti.
*/
function isSecondsField(secondsField) {
   /* Controllo formato stringa */
   if (secondsField.value.search(/^\d{1,2}$/) == -1)
      return false;

   /* Normalizzazione secondi */
   if (secondsField.value.length == 1)
      secondsField.value = "0" + secondsField.value;

   /* Controlla correttezza formale orario */
   if (secondsField.value > 59)
      return false;

   return true;
}


/*
   Controllo semantico.
   Verifica che l'URL specificato sia valido.
   Ritorna true se l'indirizzo e' valido, false altrimenti.
*/
function isValidUrl(address) {
  var urlRE = /(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/;

  if (trim(address).length == "")
    return false;

  if (!urlRE.test(address))
    return false;

  return true;
}


/*
   Controllo semantico.
   Verifica che l'indirizzo e-mail specificato sia valido.
   Ritorna true se l'indirizzo e' valido, false altrimenti.
*/
function isValidEmailAddress(address) {
  var emailRE = /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/;

  if (trim(address).length == "")
    return false;

  if (!emailRE.test(address))
    return false;

  return true;
}


/*
   Confronto tra due date.
   Ritorna -1 se la prima data e' minore della seconda;
            0 se le due date sono uguali;
            1 se la prima data e' maggiore della seconda.
   Il parametro compareTime indica se considerare anche
   l'orario in fase di confronto.
*/
function compareDate(date1,
                     date2,
                     compareTime) {
   var dapp1, dapp2 = null;

   if (compareTime) {
     dapp1 = new Date(date1.getFullYear() ,date1.getMonth(), date1.getDate(), date1.getHours(), date1.getMinutes(), date1.getSeconds(), date1.getMilliseconds());
     dapp2 = new Date(date2.getFullYear(), date2.getMonth(), date2.getDate(), date2.getHours(), date2.getMinutes(), date2.getSeconds(), date2.getMilliseconds());
   } else {
     dapp1 = new Date(date1.getFullYear() ,date1.getMonth(), date1.getDate(), 0, 0, 0, 0);
     dapp2 = new Date(date2.getFullYear(), date2.getMonth(), date2.getDate(), 0, 0, 0, 0);
   }

   if (dapp1.getTime() < dapp2.getTime())
      return -1;

   if (dapp1.getTime() > dapp2.getTime())
      return 1;

   return 0;
}


/*
   Confronto tra due orari (in formato HH:MM).
   Ritorna -1 se il primo orario e' minore del secondo;
            0 se i due orari sono uguali;
            1 se il primo orario e' maggiore del secondo.
*/
function compareTime(time1,
                     time2) {
   var tapp1 = new Date(0, 0, 0, Number(time1.substr(0, 2)), Number(time1.substr(3, 2)), 0, 0);
   var tapp2 = new Date(0, 0, 0, Number(time2.substr(0, 2)), Number(time2.substr(3, 2)), 0, 0);

   if (tapp1.getTime() < tapp2.getTime())
      return -1;

   if (tapp1.getTime() > tapp2.getTime())
      return 1;

   return 0;
}


/*
   Ricerca in una SELECT l'eventuale OPTION con valore pari a VALUE.
   Restituisce l'indice della OPTION con valore pari a VALUE
   oppure -1 se questa non esiste.
*/
function searchSelectOptionByValue(select,
                                   value) {
   for (var i = 0; i < select.options.length; i++)
      if (select.options[i].value == value)
         return i;

   return -1;
}


/*
   Aggiunge la OPTION passata come parametro alla SELECT specificata.
   Si rende necessario ricorrere a questa funzione in quanto IE si
   comporta diversamente dagli altri browser, non riuscendo a gestire
   correttamente l'istruzione appendChild().
*/
function addOptionToSelect(select,
                           option) {
   try {
      select.add(option, null);
   } catch(ex) {
      select.add(option);
   }
}


/*
   Copia i valori (option) da una select ad un'altra
   includendo quelli compresi nel range specificato.
   L'opzione reverseTextValue e' utile se si desidera
   invertire - durante la copia - i valori della i-esima
   opzione con il relativo testo descrittivo.
*/
function copySelectOption(selectFrom,
                          selectTo,
                          optionFrom,
                          optionTo,
                          reverseTextValue) {
  if (optionFrom > optionTo)
    return;

  var lowerLimit = Math.min(0, optionFrom);
  var upperLimit = Math.min(optionTo, selectFrom.options.length);
  for (var i = lowerLimit; i < upperLimit; i++) {
    var option = document.createElement("option");

    option.value = (reverseTextValue ? selectFrom.options[i].text : selectFrom.options[i].value);
    option.text = (reverseTextValue ? selectFrom.options[i].value : selectFrom.options[i].text);

    option.selected = selectFrom.options[i].selected;

    addOptionToSelect(selectTo, option);
  }
}


/*
   Helper per inserimento codice pratica
   (per passaggio da un sotto-campo all'altro).
*/
function tabToNextSibling(currentField, lengthToTab, nextField, event) {
  if (!event)
    var event = window.event;

  if (event.keyCode)
    code = event.keyCode;
  else if (event.which)
    code = event.which;

  if (isArrowKey(code) || isMetaKey(code) || isKeyToIgnore(code) || code == 46) /* 46 = tasto Canc. */
    return;

  if (currentField.value.length == lengthToTab)
    nextField.focus();
}


/*
   Formatta una dimensione espressa in byte
   restituendo una stringa.
*/
function formatBytes(value) {
   /* Conversione valore a numero */
   bytes = Number(value);

   /*
      Se il parametro non rappresenta una dimensione
      lo si restituisce semplicemente
   */
   if (isNaN(bytes))
      return value;

   /* Dimensione in byte */
   if  (bytes < 1024)
      return value + " byte";

   /* Dimensione in kilobyte */
   if  (bytes < 1048576)
      return Math.round(bytes / 1024) + " KB";

   /* Dimensione in megabyte */
   if  (bytes < 1073741824)
      return Math.round(bytes / 1048576) + " MB";

   /* Dimensione in gigabyte */
   return Math.round(bytes / 1073741824) + " GB";
}


/*
   Formatta un numero alla cifra decimale richiesta.
*/
function formatDecimal(number, decimals) {
   return number.toFixed(decimals);
}


/*
   Formatta un numero inserendo il separatore di migliaia.
*/
function formatNumber(number) {
   return formatNumberUsingSeparator(number, DEFAULT_THOUSAND_SEPARATOR);
}


/*
   Formatta un numero inserendo il separatore di migliaia
   specificato in input.
*/
function formatNumberUsingSeparator(number,
                                    thousandSeparator) {

   var formattedNum = "";

   if (number.match(/^\d+\.\d{0,2}$/)) {
      var strArray = number.split(".");
      for (var i = strArray[0].length - 1; i >= 0; i--) {
         formattedNum = strArray[0].charAt(i) + formattedNum;
         if (i > 0 && ((strArray[0].length - i) % 3) == 0)
            formattedNum = thousandSeparator + formattedNum;
      }
      formattedNum += "." + strArray[1];
   }
   else {
      for (var i = number.length - 1; i >= 0; i--) {
         formattedNum = number.charAt(i) + formattedNum;
         if (i > 0 && ((number.length - i) % 3) == 0)
            formattedNum = thousandSeparator + formattedNum;
      }
   }

   return formattedNum;
}


/*
  Formatta una data secondo il formato DD/MM/YYYY.
*/
function formatDateAsDDMMYYYY(date) {
   /* Se data non specificata si ritorna */
   if (date == null)
      return date;

   /* Suddivisione campi data */
   var fields = new Array();
   fields[0] = new String(date.getDate());
   fields[1] = new String(date.getMonth() + 1);
   fields[2] = new String(date.getFullYear());

   /* Normalizzazione giorno */
   if (fields[0].length == 1)
      fields[0] = "0" + fields[0];

   /* Normalizzazione mese */
   if (fields[1].length == 1)
      fields[1] = "0" + fields[1];

   /* Merge campi */
   return fields.join("/");
}


/*
  Estrae un orario da una data specifica
  (passata come parametro) e lo ritorna
  secondo il formato hh:mm.
*/
function extractTimeAsHHMM(date) {
   /* Se data non specificata si ritorna */
   if (date == null)
      return date;

   /* Suddivisione campi data */
   var fields = new Array();
   fields[0] = new String(date.getHours());
   fields[1] = new String(date.getMinutes());

   /* Normalizzazione ora */
   if (fields[0].length == 1)
      fields[0] = "0" + fields[0];

   /* Normalizzazione minuti */
   if (fields[1].length == 1)
      fields[1] = "0" + fields[1];

   /* Merge campi */
   return fields.join(":");
}


/*
  Aggiunge i millisecondi specificati alla
  data passata come parametro e restituisce la
  nuova data risultante.
*/
function rollDate(startDate,
                  millisecondsToRoll) {
   /* Se data non specificata si ritorna */
   if (startDate == null)
      return startDate;

   /* Conversione data iniziale in millisecondi */
   var startDateInMilliseconds = startDate.getTime();

   return new Date(startDateInMilliseconds + millisecondsToRoll);
}


/*
   Funzione per calcolo differenza in giorni tra due date
*/
function calculateDaysBetweenDates(startDate, endDate) {
  var difference = Date.UTC(endDate.getFullYear(), endDate.getMonth(), endDate.getDate(), 0, 0, 0)
                 - Date.UTC(startDate.getFullYear(), startDate.getMonth(), startDate.getDate(), 0, 0, 0);

  if (difference < 0)
    return 0;

  return difference / 1000 / 60 / 60 / 24;
}


/*
   Ritorna una durata in millisecondi formattata come ore, minuti e secondi
*/
function millisecondsToHours(ms) {
  var t = "";

  var sec = Math.floor(ms / 1000);
  var ms = ms % 1000;
  //t = ((ms > 99) ? "" : "0") + ((ms > 9) ? "" : "0") + ms + "ms";

  var min = Math.floor(sec / 60);
  sec = sec % 60;
  t = ((sec > 9) ? "" : "0") + sec + "s " + t;

  var hr = Math.floor(min / 60);
  min = min % 60;
  t = ((min > 9) ? "" : "0") + min + "m " + t;

  var day = Math.floor(hr / 24);
  hr = hr % 24;
  t = ((hr > 9) ? "" : "0") + hr + "h " + t;
  //t = day + ":" + t;

  return t;
}


/*
   Ritorna una durata in millisecondi a partire da ore, minuti, secondi e millisecondi
*/
function hoursToMilliseconds(hours, minutes, seconds, milliseconds) {
  var timeInMilliseconds = 0;

  timeInMilliseconds += milliseconds;
  timeInMilliseconds += seconds * 1000;
  timeInMilliseconds += minutes * 60 * 1000;
  timeInMilliseconds += hours * 60 * 60 * 1000;

  return timeInMilliseconds;
}


/*
   Epura un numero formattato dal separatore di migliaia
*/
function removeThousandSeparator(string) {
   return removeCustomThousandSeparator(string, DEFAULT_THOUSAND_SEPARATOR);
}


/*
   Epura un numero formattato dal separatore di migliaia
   specificato in input.
*/
function removeCustomThousandSeparator(string,
                                       separator) {
   while (string.indexOf(separator) != -1)
      string = string.replace(separator, separator);

   return string;
}


/*
   Converte il valore di un campo in lettere maiuscole,
   rimuovendo eventuali blanks finali.
*/
function fieldToUpper(field) {
   field.value = trim(field.value.toUpperCase());
}


/*
   Converte il valore di un campo in lettere minuscole,
   rimuovendo eventuali blanks finali.
*/
function fieldToLower(field) {
   field.value = trim(field.value.toLowerCase());
}


/*
   Converte il valore di un campo in lettere minuscole
   ad eccezione dell'iniziale che viene resa maiuscola,
   rimuovendo eventuali blanks finali.
*/
function fieldToCapitalize(field) {
   if (field.value.length <= 1)
      field.value = trim(field.value.toUpperCase());
   else
      field.value = trim(field.value.substr(0, 1).toUpperCase() + field.value.substr(1).toLowerCase());
}


/*
   Logica successiva all'ottenimento del focus per un campo di un form
*/
function focusGainedOnField(field,
                            style) {
   /* Applicazione eventuale stile specificato */
   if (style != null)
      field.className = style;

   /* Si seleziona automaticamente il valore contenuto all'interno del campo (solo text/password/file/textarea) */
   if ((field.nodeName.toUpperCase() == "INPUT" && (field.getAttribute("type").toLowerCase() == "text" || field.getAttribute("type").toLowerCase() == "password" || field.getAttribute("type").toLowerCase() == "file")) ||
       (field.nodeName.toUpperCase() == "TEXTAREA"))
      field.select();
}


/*
   Logica successiva alla perdita del focus per un campo di un form
*/
function focusLostOnField(field,
                          style) {
   /* Applicazione eventuale stile specificato */
   if (style != null)
      field.className = style;
}


/*
   Ritorna se un campo e' vuoto
*/
function isEmptyField(field) {
   if (field.value == null || field.value == undefined || field.value == "")
      return true;

   return false;
}


/*
   Ritorna se un valore e' vuoto
*/
function isEmptyValue(value) {
   if (value == null || value == undefined || value == "")
      return true;

   return false;
}


/*
   Ritorna se il valore specificato come parametro
   rappresenta un id valido (sintatticamente accettabile).
   Sono ammessi solo i caratteri: a-zA-Z0-9_-..
*/
function isValidId(idValue) {
  var pattern = /^[a-zA-Z0-9_\-\.]*$/;

  if (idValue.match(pattern))
    return true;

  return false;
}


/*
   Ritorna se un evento e' valido
*/
function isValidEvent(event) {
   if (event == null || event == undefined)
      return false;

   return true;
}


/*
   Limitazione lunghezza contenuto textarea.
   Tronca il contenuto della textarea se di lunghezza superiore
   al numero massimo di caratteri specificato come parametro.
*/
function limitContent(textarea,
                      maxLength) {
   if (textarea == null || maxLength == null)
      return;

   if (textarea.value.length > maxLength)
      textarea.value = textarea.value.substr(0, maxLength);
}


/*
   Aggiornamento label relativa al numero di caratteri
   ancora ammessi per textarea.
*/
function updateCharsCount(textarea,
                          maxLength,
                          lblCounter) {
   if (textarea == null || maxLength == null || lblCounter == null)
      return;

   var charsTyped = textarea.value.length;
   var charsAvailable = maxLength - charsTyped;

   if (charsAvailable >= 0)
      lblCounter.childNodes[0].nodeValue = charsAvailable;
   else
      lblCounter.childNodes[0].nodeValue = 0;
}


/*
   Aggiornamento label relativa al numero di parole
   contenute nella textarea.
*/
function updateWordsCount(textarea,
                          lblCounter) {
   if (textarea == null || lblCounter == null)
      return;

   var wordsTyped = textarea.value.split(/\s/);

   lblCounter.childNodes[0].nodeValue = wordsTyped.length - 1;
}


/*
   Ritorna la posizione del cursore all'intenro del campo text specificato.
   Il valore ritornato va da 0 a field.length.
*/
function getCaretPosition(field) {
  var caretPos = 0;

  // IE
  if (document.selection) {
    field.focus();
    var c   = "\001";
    var len = 0;
    var selection = document.selection.createRange();
    var dupSelection = selection.duplicate();
    dupSelection.moveToElementText(field);
    selection.text = c;
    len = (dupSelection.text.indexOf(c));
    selection.moveStart('character', -1);
    selection.text = "";
    caretPos = len;
  }
  // NS
  else if (field.selectionStart || field.selectionStart == '0')
    caretPos = field.selectionStart;

  return(caretPos);
}


/*
   Imposta la posizione del cursore all'intenro del campo text specificato.
   La posizione deve essere compresa tra 0 e field.length.
*/
function setCaretPosition(field, caretPos) {

  // IE
  if (document.selection) {
    field.focus ();
    var selection = document.selection.createRange();
    selection.moveStart('character', -field.value.length);
    selection.moveStart('character', caretPos);
    selection.moveEnd('character', 0);
    selection.select();
  }
  // NS
  else if (field.selectionStart || field.selectionStart == '0') {
    field.selectionStart = caretPos;
    field.selectionEnd = caretPos;
    field.focus();
  }
}


/* Invocazione esplicita evento onclick su un controllo */
function invokeOnClickEvent(field) {
  if (field.fireEvent)   // IE
    field.fireEvent("onclick");
  else {                 // NS
     var clickEvent = window.document.createEvent("MouseEvent");
     clickEvent.initEvent("click", false, true);
     field.dispatchEvent(clickEvent);

  }
}


/* Invocazione esplicita evento onchange su un controllo */
function invokeOnChangeEvent(field) {
  if (field.fireEvent)   // IE
    field.fireEvent("onchange");
  else {                 // NS
     var changeEvent = window.document.createEvent("MouseEvent");
     changeEvent.initEvent("change", false, true);
     field.dispatchEvent(changeEvent);

  }
}


/* Cancellazione evento corrente */
function cancelEvent(event) {
  if (event.preventDefault)   // NS
     event.preventDefault();
  else                        // IE
     event.keyCode = 0;
}


/* Cancellazione propagazione evento corrente */
function cancelPropagation(event) {
  event.cancelBubble = true;
  event.returnValue = false;
}


/* Funzione di nextSibling cross-browser */
function getNextSibling(startElement) {
  var endElement = startElement.nextSibling;
  while (endElement != null && endElement.nodeType != 1)
    endElement = endElement.nextSibling;
  return endElement;
}


/* Funzione di previousSibling cross-browser */
function getPrevSibling(startElement) {
  var endElement = startElement.previousSibling;
  while (endElement != null && endElement.nodeType != 1)
    endElement = endElement.previousSibling;
  return endElement;
}


/* Funzione di firstChild cross-browser */
function getFirstChild(startElement) {
  if (!startElement.childNodes.length)
    return;

  var children = startElement.childNodes.length;
  for (var i = 0; i <= children; ++i) {
    if (startElement.childNodes[i].nodeType == 1)
      return startElement.childNodes[i];
  }

  return;
}


/* Ritorna un numero passato come stringa */
function toNumber(numberAsString) {
  return Number(numberAsString).valueOf();
}


/* Formatta un numero con i decimali specificati */
function roundNumber(num, dec) {
  return Math.round(num * Math.pow(10, dec)) / Math.pow(10, dec);
}

