/*--- non rimuovere: serve per evidenziare la sintassi in Visualinterdev
<html><SCRIPT LANGUAGE="JAVASCRIPT"> 
   --- fine di non rimuovere*/

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@
	fare:
		testing
		risolvere bugs con gli eventi: problema della onchange o della onblur
			(documentazione? mailing list?)
		design e implementazione del sistema di help
		? rimozione dei commenti?
		? possiblita' di spedificare test di un array di campi in una sola rule ?
		controllo che la pagina non vanga lasciata se sono stati cambiati dei valori nei controlli gui e non e' avvenuto salvataggio

	domande:
		select, radio: validazione realtime? on change? on blur?
	bugs:
		isDate() deve accettare solo / e - come separatori
		internet explorer 5:
			usando "onchange=", in caso di evento "onchange" ed errore
				viene messo il focus e la selez. alla address bar
				(togliendo la address bar doppio messaggio)
		netscape communicator 4.6:
			usando "onblur=", in caso di click di un bottone ed errore
				va in loop il browser sull'evento onblur che non lascia scattare onclick
	note:
		le funzioni accettano un controllo grafico, non una stringa, ma difficile riprogettare
		isMandatory deve essere chiamata per prima altrimenti il campo non verra' riformattato
*/

/*-----------------------------------------------------------------
	isOk =  funzione dispatcher per la validazione di un singolo oggetto di una pagina html,
				registrato secondo la convenzione adottata (vedere l'oggetto "rule")
	attributi:
		obj: oggetto di tipo rule che specifica l'oggetto da validare e le funzioni da applicare
	restituisce:
		err: oggetto di tipo error, con l'esito della validazione
			  se obj contiene un messaggio d'errore contestuale, questo viene anteposto al
			  messaggio d'errore di default restituito dalla funzione di valdazione che ha avuto esito negativo
	uso:
		validazione batch 
			var err = isOk(nome_oggetto_rule_registrato)
			non viene usata nessuna interfaccia utente
			tipicamente viene creato un array di "rule" da dare in pasto ad uan funzione di validazione batch
			che applica le verifiche a tutti i controlli sulla pagina
		validazione real-time
			nel tag html che introduce l'oggetto da validare specificare onblur=isOKGui(nome_oggetto_rule_registrato)
			nel caso di errore viene mostrata una finestra di avviso (vedere gui() )
*/
function isOk(obj) {
	var err
	var i
	// applica all'oggetto le funzioni di validazione associate, una alla volta
	for (i = 2; i < obj.args.length; i++) {
		if (typeof(obj.args[i]) == "function") {
		
			// caso di funzione senza parametri aggiuntivi (passata come funzione)
			err = obj.args[i](obj.args[0])
		} else {
		
			// obj.args[i] e' un array in cui:
			//		- obj.args[i][0] e' la funzione di validazione da eseguire
			//		- obj.args[i][1..6] sono i parametri aggiuntivi passati alla funzione di validazione
			//		  nota: non si prevede di utilizzare piu' di 5 parametri aggiuntivi
			switch (obj.args[i].length) {
				case 1:
					err = obj.args[i][0](obj.args[0])
					break
				case 2:
					err = obj.args[i][0](obj.args[0], obj.args[i][1])
					break
				case 3:
					err = obj.args[i][0](obj.args[0], obj.args[i][1], obj.args[i][2])
					break
				case 4:
					err = obj.args[i][0](obj.args[0], obj.args[i][1], obj.args[i][2], obj.args[i][3])
					break
				case 5:
					err = obj.args[i][0](obj.args[0], obj.args[i][1], obj.args[i][2], obj.args[i][3], obj.args[i][4])
					break
				case 6:
					err = obj.args[i][0](obj.args[0], obj.args[i][1], obj.args[i][2], obj.args[i][3], obj.args[i][4], obj.args[i][5])
					break
				default: 
					// caso di utilizzo di troppi parametri, non gestiti dal presente dispatcher
					err = new error(true, "funzione di validazione: numero di parametri non supportato.")
					break
			}
		}
		
		// al primo errore che si verifica interrompi la validazione
		if (err.thrown) {
			break
		}
	}
	
	// se si e' verificato un errore, il messaggio d'errore registrato in "rule" viene copiato nella 
	// descrizione dell'errore avvenuto, in modo da fornire informazioni sul contesto
	if (err.thrown && obj.args[1] != null && obj.args[1] != "") {
		err.description = obj.args[1] + "\n\n" + err.description 
	}
	return err
}

/*-----------------------------------------------------------------------------
		gui = funzione per gestire la gui della chiamata a funzioni di validazione
		
		uso: gui (nome_funzione, nome_controllo)			
			se c'e' un errore
				-segnala mediante alert
				-setta focus e selezionare
				-restituisce false
			altrimenti
				-riformatta il contentuto del campo in modo standard prendendo il valore da err.description
				-restituisce true
*/
function gui(err, gField) {
	if (err.thrown) {
		// mostra finestra di dialogo con messaggio
		err.alert()
		// posiziona il focus e la selezione per la modifica, solo per i campi che lo supportano
		if (gField.type == "text" || gField.type == "select-one" || gField.type == "select-multiple") {
			gField.focus()
		}
		// i campi di tipo testo possono essere selezionati
		if (gField.type == "text") {
			gField.select()
		}
		return false
	} else {
		// formatta il dato nel campo
		gField.value = err.description
		return true
	}
}

/*-----------------------------------------------------------------
	isOkGui = funzione che combina la funzione di validazione "isOk" con la funzione 
					per la visualizzaqzione su interfaccia utente "gui"
	attributi:
		obj: oggetto di tipo rule che specifica l'oggetto da validare e le funzioni da applicare
	restituisce: 
		true se tutto ok, false se di e' verificato un errore di validazione. In tal caso viene visualizzato
		un avviso e il focus va al controllo grafico in oggetto
*/
function isOkGui(obj) {
	return gui(isOk(obj), obj.args[0])
}

/*-----------------------------------------------------------------
	isBatchOk = funzione per la validazione batch di un array di oggetti registrati
	parametri:		
		ruleArray: array di "rule" (oggetti registrati da verificare)
	restituisce: 
		err: oggetto di tipo error, con l'esito della validazione
			  se obj contiene un messaggio d'errore contestuale, questo viene anteposto al
			  messaggio d'errore di default restituito dalla funzione di valdazione che ha avuto esito negativo
*/
function isBatchOk(ruleArray) {
	var err
	var i
	for (i=0; i < ruleArray.length; i++) {
		// verifica l'elemento i-esimo
		err = isOk(ruleArray[i])
		
		// al primo errore che si verifica interrompi la validazione
		if (err.thrown) {
			break
		}
	}
	return err
}

/*-----------------------------------------------------------------
	isBatchOkGui = funzione che combina la funzione di validazione "isBatchOk" con la funzione 
					per la visualizzaqzione su interfaccia utente "gui"
	parametri:		
		ruleArray: array di "rule" (oggetti registrati da verificare)
	restituisce: 
		true se non ci sono errori, false diversamente, in modo da inibire l'evento associato alla chiamata
	uso: 
		viene associata tipicamente al gestore onsubmit di una form, 
		oppure ad onclick di un link, per evitare che una pagina venga lasciata avendo introdotto
		valori non corretti nei controlli grafici
			es: <a href="session_test.asp"  onclick="return lib.isBatchOkGui(c_array)">link di test</a>
*/
function isBatchOkGui(ruleArray) {
	var isCorrect
	var i
	for (i=0; i < ruleArray.length; i++) {
		// verifica l'elemento i-esimo
		isCorrect = isOkGui(ruleArray[i])
		// al primo errore che si verifica interrompi la validazione
		if (! isCorrect) {
			return false
		}
	}
	return true
}

/*-----------------------------------------------------------------
	ifOkSubmit = funzione che effettua la validazione batch degli oggetti nella form in oggetto
		- in caso di successo effettua la submit della form
		- in caso di insuccesso inibisce l'evento scatenante
	parametri:
		currentForm: form alla quale applicare i controlli	di validita'
		ruleArray: array di "rule" (oggetti registrati da verificare)
	restituisce: 
		false, se errori, in modo da inibire l'evento associato
	uso: 
		viene associata tipicamente al gestore onclick di un bottone o di un link, 
		per eseguire il submit di una form solo se i dati introdotti sono corretti
			es: <INPUT type="button" value="invia i idati" name="invia" onclick="return lib.onSubmit(this.form, c_array)">
*/
function ifOkSubmit(currentForm, ruleArray) {
	if (isBatchOkGui(ruleArray)) {
		currentForm.submit()	
	} else {
		return false;
	}
}

/*-----------------------------------------------------------------
	isSelectValid = funzione che verifica che venga selezionata una voce in un tag <select>
	parametri:
		selectControl: controllo di tipo select da controllare
	restituisce: 
		oggetto "error" relativo all'errore (mancata selezione) oppure contenente l'indice della voce
			selezionata
	uso: 
		viene tipicamente registrata in un oggetto "rule", incluso nell'array di oggetti
			su cui si effettua validazione batch
*/
function isSelectValid(selectControl) {
	var err
	if (selectControl.selectedIndex == -1 || selectControl.selectedIndex == 0 ) {
		err = new error(true, "nessuna voce e' stata selezionata nel campo")
	} else {
		err = new error(false, selectControl.selectedIndex)
	}
	return err
}

/*-----------------------------------------------------------------
	isRadioValid = funzione che verifica che venga cliccato almeno un bottone in un gruppo di radio-button
		mutuamente esclusivi (ovvero con lo stesso parametro "name" nel tag html <INPUT type="radio" name="..." ...> )
	parametri:
		radioButtonGroup: nome del gruppo di radio-button da controllare (parametro "name" nel tag html <INPUT type="radio" name="..." ...> )
	restituisce: 
		oggetto "error" relativo all'errore (mancata selezione) oppure contenente l'indice del bottone selezionato
	uso: 
		viene tipicamente registrata in un oggetto "rule", incluso nell'array di oggetti
			su cui si effettua validazione batch
*/
function isRadioValid(radioButtonGroup) {
	var i
	for (i = 0; i < radioButtonGroup.length; i++) {
		if (radioButtonGroup[i].checked) {
			return new error(false, i)
		}
	}
	return new error(true,"")
}

/*-----------------------------------------------------------------
	rule =  oggetto per memorizzare la registrazione del controllo grafico da validare
			   e delle funzioni di validazione da eseguire su di esso
	attributi:
		gField: controllo grafico di una form in una pagina html
		message: messaggio personalizzato
		argomenti dal 3^ in poi:
			 le funzioni da applicare per validare il campo, od array in caso di parametri opzionali (vedere sotto)
	uso:
		nel gestore di eventi onload() di ogni pagina html locale
		creare un'istanza di un oggetto rule per ogni controllo grafico da verificare
		specificando come parametri aggiuntivi le funzioni di validazione da applicare, che possono essere
			- locali alla pagina html
				es:	r1 = new lib.rule(document.checkdateform.date_std,"messaggio",	localFunction)
			- di libreria: in tal caso si deve specificare il frame in cui sono contenute
				es:	r2 = new lib.rule(document.checkdateform.date_std,"messaggio",	lib.isDate)
		se a tali funzioni devono essere passati dei parametri, si deve creare un array contenente 
			- [0] funzione di validazione
			- [1..5] parametri da passare
				es:	r2 = new lib.rule(document.checkdateform.date_std,"messaggio", new Array (lib.isDate, 1950, 2003))
*/
function rule(gField, message, a1) {
	var args = rule.arguments	
	this.args = args
}

/*-----------------------------------------------------------------
	error = oggetto per la rappresentazione di un errore user-defined
	attributi:
		thrown - boolean; true se errore
		description - stringa per specificazione dettagli ed eventualmente contesto
			@@@ se non si e' verificato nessun errore, la funzione chiamata puo' mettere in 
			questo attributo il dato di output
		alert() - mostra una dialog box con il messaggio d'errore
	uso:
		per sollevare un errore:
			new error(true, "messaggio descrittivo")
		per segnalare un errore in una variabile 'e':
			if (e.thrown) { 
				e.alert() // oppure alert("messaggio rigurdante il contesto\n"+e.description)
				return
			}
*/
function error(thrown, description) {
	this.thrown = thrown
	this.description = description
	this.alert = errorAlert
}

/*-----------------------------------------------------------------
	errorAlert = metodo di segnalazione di errore avvenuto in fase di validazione
*/
function errorAlert() {
	alert (this.description)
}

/*-----------------------------------------------------------------
	isMandatory = verifica che un campo non sia vuoto e che non contenga solo spazi
	argomenti
		gField: textfield da validare
	output
		oggetto di tipo error - nessun errore se il campo contiene caratteri diversi dallo spazio
*/
function isMandatory(gField) {
	var str = gField.value
	if (isEmpty(str)) {
		return new error(true, "")
	}
	return new error(false, str)
}

/*-----------------------------------------------------------------
	isEmpty = verifica che un campo sia vuoto o contenga solo spazi
	argomenti
		gField: textfield da validare
	output
		true se vuoto o con spazi, false diversamente
*/
function isEmpty(str) {
	// controllo sintassi con espressione regolare
	// dall'inizio del campo alla fine:  0 o piu' spazi
	var emptyRegExp = new RegExp("^[ ]*$" )
	return emptyRegExp.test(str)
}

/*-----------------------------------------------------------------
	isInteger = Validazione di un campo intero
	argomenti
		gField: textfield da validare
	output
		oggetto di tipo error - nessun errore se l'intero e' ok
*/
function isInteger(gField) {
	var int_s = gField.value

	// se vuoto salta i controlli
	if (isEmpty(int_s)) {
		return new error(false, "")
	}

	// controllo sintassi con espressione regolare
	// dall'inizio del campo alla fine:
	//  0 o piu' spazi, eventuale segno, cifre decimali, punto decimale, 0 o piu' zeri, 0 o piu' spazi
	var integerRegExp = new RegExp("^[ ]*[-+]?[0-9]+[.]?0*[ ]*$" )
	var ok = integerRegExp.test(int_s)
	if (! ok) {
		return new error(true, "formato di numero intero non corretto")
	}
	
	// la sintassi e' ok. conversione in intero	
	var int_v = parseInt(int_s,10)

	return new error(false, int_v)
}

/*-----------------------------------------------------------------
	isFloat = Validazione di un campo floating point
	argomenti
		gField: textfield da validare
	output
		oggetto di tipo error - nessun errore se l'intero e' ok
*/
function isFloat(gField) {
	var float_s = gField.value

	// se vuoto salta i controlli
	if (isEmpty(float_s)) {
		return new error(false, "")
	}

	// controllo sintassi con espressione regolare
	// dall'inizio del campo alla fine:
	//  0 o piu' spazi, eventuale segno, cifre decimali, punto decimale, 0 o piu' zeri, 0 o piu' spazi
	var floatRegExp = new RegExp("^[ ]*[-+]?[0-9]*([.][0-9]*)?[ ]*$" )
	var ok = floatRegExp.test(float_s)
	if (! ok) {
		return new error(true, "formato di numero reale non corretto")
	}
	
	// la sintassi e' ok. conversione in intero	
	var float_v = parseFloat(float_s)
	return new error(false, float_v)
}

/*-----------------------------------------------------------------
	isInRange = verifica che un campo sia in un intervallo specificato
		eventualmente aperto
	argomenti
		gField: textfield *gia' validato* con isInteger o isFloat
		lowerBound: estremo inferiore
		upperBound: estremo superiore
	output
		oggetto di tipo error - nessun errore se il campo e' nell'intervallo [lowerBound,upperBound]
*/
function isInRange(gField, lowerBound, upperBound) {
	var str = gField.value

	// se vuoto salta i controlli
	if (isEmpty(str)) {
		return new error(false, "")
	}

	// riformattazione del campo
	var x = str * 1
	if (x >= lowerBound && x <= upperBound) {
		return new error(false, x)
	} else {
		return new error(true, "introdurre un valore nell'intervallo ["+lowerBound+", "+upperBound+"]")
	}
}

/*-----------------------------------------------------------------
	isPositive = verifica che un campo sia maggiore o uguale a zero
	argomenti
		gField: textfield *gia' validato* con isInteger o isFloat
	output
		oggetto di tipo error - nessun errore se il campo e' >= 0
*/
function isPositive(gField) {
	var err = isInRange(gField, 0, Number.POSITIVE_INFINITY)
	if (err.thrown) {
		err.description = "introdurre un numero positivo o nullo"
	}
	return err
}

/*-----------------------------------------------------------------
	isDate = Validazione di un campo data
	argomenti
		gField: textfield da validare
		aMinY [opzionale]: anno minimo 
		aMaxY [opzionale]: anno massimo
		(se aMinY e aMaxY non vengono specificati, vengono assunti valori di default definiti nella funzione)
	output
		oggetto di tipo error - nessun errore se la data e' ok
	// @@@attenzione getMonth ritorna valori da o a 11 invece che da 1 a 12 !
*/
function isDate(gField,aMinY,aMaxY) {
	// range di default per l'anno
	var minY = 1900
	var maxY = 2200
	if (aMinY != null) {
		minY = aMinY
	}
	if (aMaxY != null) {
		maxY = aMaxY
	}
	
	// messaggi d'errore
	var format_error = "Formato della data immessa non accettabile. \n\nFormati accettati: ggmmaaaa, gg/mm/aaaa, o gg-mm-aaaa."
	var mm_format_error = "Il mese deve essere compreso tra 01 e 12."
	var dd_format_error = "Il giorno deve essere compreso tra 01 ed al massimo 31 (in dipendenza del mese dell'anno)."
	var year_out_of_range_error = "L'anno deve essere compreso tra "+minY+" e "+maxY

	// salva il testo da analizzare
	var inputStr = gField.value
	
	// se vuoto salta i controlli
	if (isEmpty(inputStr)) {
		return new error(false, "")
	}

	// conversione dei '-' in '/'
	inputStr = inputStr.replace(/-/g, "/")

	// verifica se ci sono 0 o 2 delimitatori. Diversamente errore.
	var delim1 = inputStr.indexOf("/")
	var delim2 = inputStr.lastIndexOf("/")
	if (delim1 != -1 && delim1 == delim2) {		// solo 1 delimitatore
		return new error(true, format_error)
	}

	// divisione della stringa in pezzi
	// parse in base 10
	if (delim1 != -1) {
		// caso in cui sono presenti i delimitatori
		var dd = parseInt(inputStr.substring(0,delim1),10)
		var mm = parseInt(inputStr.substring(delim1 + 1,delim2),10)
		var yyyy = parseInt(inputStr.substring(delim2 + 1,inputStr.length),10)
	} else {
		// nessun delimitatore: si assume ggmmaaaa
		var dd = parseInt(inputStr.substring(0,2),10)
		var mm = parseInt(inputStr.substring(2,4),10)
		var yyyy = parseInt(inputStr.substring(4,inputStr.length),10)
	}  
	
	// verifica che i tre valori letti siano numeri validi 
	if (isNaN(mm) || isNaN(dd) || isNaN(yyyy)) {
		return new error(true, format_error)
	}

	// Sgrossatura della verifica: mese in 1..12, giorno in 1..31
	if (mm < 1 || mm > 12) {
		return new error(true, mm_format_error)
	}
	if (dd < 1 || dd > 31) {
		return new error(true, dd_format_error)
	}

	// validazione del range dell'anno (se specificato, in esso, oppure in quello di default)
	if (yyyy < minY || yyyy > maxY) {
		return new error(true, year_out_of_range_error)
	}

	// verifica della correttezza del giorno del mese, anche per anni bisestili
	var err_giorno = checkMonthLength(mm,dd)
	if (err_giorno.thrown) {
		return err_giorno
	}
	if (mm == 2) {
		err_giorno = checkLeapMonth(mm,dd,yyyy)
		if (err_giorno.thrown) {
			return err_giorno
		}
	}

	// riassembla la stringa della data per eventuale visualizzazione
	var correct_fmt = monthDayFormat(dd) + "/" + monthDayFormat(mm) + "/" + yyyy

	return new error(false, correct_fmt)
}

// verifica della correttezza del giorno del mese
function checkMonthLength(mm,dd) {
	var months = new Array("","Gennaio","Febbraio","Marzo","Aprile","Maggio","Giugno","Luglio","Agosto","Settembre","Ottobre","Novembre","Dicembre")
	if ((mm == 4 || mm == 6 || mm == 9 || mm == 11) && dd > 30) {
		return new error(true, months[mm] + " ha 30 giorni.")
	} else if (dd > 31) {
		return new error(true, months[mm] + " ha 31 giorni.")
	}
	return new error(false, "")
}

// verifica se il giorno specificato di febbraio e' corretto tenendo conto dell'anno e del secolo bisestili
function checkLeapMonth(mm,dd,yyyy) {
	// verifica se anno bisestile
	var isLeap = (yyyy % 4 == 0 && yyyy % 100 !== 0) || yyyy % 400 == 0
	if ((! isLeap) && dd > 28) {
		return new error(true, "Febbraio del " + yyyy + " ha 28 giorni.")
	} else if (dd > 29) {
		return new error(true, "Febbraio del " + yyyy + " ha 29 giorni.")
	}
	return new error(false, "")
}

// restituisce una stringa di 2 caratteri con il giorno o mese passato ed eventuale 0 iniziale
function monthDayFormat(mm) {
	var mmString = ""
	if (mm < 10) {
		mmString = "0"
	}
	mmString += mm
	return mmString
}

