var fw =
{
	ajax:
	{
		GetResponse: function(strJSon) {
			var jSon = {};
			try {
				jSon = eval('('+strJSon+')');
			} catch(err) {
				jSon.blnSuccess = false;
				jSon.strMessage = strJSon;
			}
			// do what it's supposed to:
			if (jSon.blnSuccess !== true) {
				if (!jSon.strMsgType) jSon.strMsgType = 'X';
				var strMsg = jSon.action+" has returned an error:\n"+jSon.strMessage+"\npassed param(s) (if available):\n"+fw.debug.dump(jSon.arrParams);
				if(jSon.strMessage=='redirect_to_login'){
					window.location = "http://"+location.host+"/index.phtml";
				}else{
					this.ShowMessage('divTestDialog', jSon.action, strMsg, jSon.strMsgType);
				}
				jSon.blnSuccess = false;
			} else {
				if (jSon.strMessage != '') {
					this.ShowMessage('divTestDialog', jSon.action, jSon.strMessage, jSon.strMsgType);
				}
			}
			return jSon;
		},
		ShowMessage: function(strDialogID, strTitle, strMessage, chrType) {
			// function to easily build-up ui modal dialog.
			// chrType allowed values: 'I'=info, 'Q'=question, 'W'=warning, 'E'=error, 'X'=fatal error.
			if (!$('#'+strDialogID).length) {
				alert(strTitle+":\n\n"+strMessage);
			} else {
				var jqDialog = $('#'+strDialogID);
				jqDialog.find('#divDialogText').html(fw.conv.Nl2br(strMessage));
				jqDialog.find('#divDialogIcon').css('background', 'url("/common/images/'+chrType+'-ico.png") no-repeat scroll left top transparent');
				jqDialog.dialog('option', 'title', strTitle);
				jqDialog.dialog('open');
				//$('#divTestDialog').dialog('open');
			}
		}
	},
	conv:
	{
		Nl2br: function(str) {
		    return (str + '').replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1<br>$2');
		},
		Iso2Dtm: function(dtDateTime, dtOpt)
		{
			// dtOpt expected: 1=time, 2=date, 3=date and time or 0(missing)=autodetect
			// BEWARE! a dtOpt value of 0 (or missing) is behaving A DIFFERENT WAY compared to explicit values:
			// 1, 2 or 3 means I want to take the complete date/time string and extract a part of it (or all, in case of 3).
			// 0 means the system will try to detect the type of info in input, and will produced a formatted output.
			// Then, for example, trying to extract a time only (1) from a field which have only the date, will result in errors!

			if (!dtOpt) dtOpt = 0;
			var arrDate = dtDateTime.split(/[-, :]/);
			switch (dtOpt) {
			case 0:		// auto detect!
				switch (arrDate.length) {
				case 3:		// it's a date OR a time
					if (dtDateTime.indexOf(':') != -1) {		// it's a time
						return ''+arrDate[0]+':'+arrDate[1]+':'+arrDate[2]+'';
					} else {								// it's a date
						return ''+arrDate[2]+'/'+arrDate[1]+'/'+arrDate[0]+'';
					}
					break;
				case 6:		// it's a date/time
					return ''+arrDate[2]+'/'+arrDate[1]+'/'+arrDate[0]+' '+''+arrDate[3]+':'+arrDate[4]+':'+arrDate[5]+'';
					break;
				default:	// invalid date
					return false;
				}
				break;
			case 1:		// time only
				return ''+arrDate[3]+':'+arrDate[4]+':'+arrDate[5]+'';
			case 2:		// date only
				return ''+arrDate[2]+'/'+arrDate[1]+'/'+arrDate[0]+'';
			case 3:		// date and time
				return ''+arrDate[2]+'/'+arrDate[1]+'/'+arrDate[0]+' '+''+arrDate[3]+':'+arrDate[4]+':'+arrDate[5]+'';
			default:	// invalid option
				return false;
			}
		},
		Iso2Obj: function(dtDateTime, dtOpt)
		{
			// dtOpt expected: 1=time, 2=date, 3=date and time or 0(missing)=autodetect
			if (!dtOpt) dtOpt = 0;
			var arrDate = dtDateTime.split(/[-, :]/);
			switch (dtOpt) {
			case 0:		// auto detect!
				switch (arrDate.length) {
				case 3:		// it's a date OR a time
					if (dtDateTime.indexOf(':') != -1) {		// it's a time
						return new Date(null, null, null, arrDate[0], arrDate[1], arrDate[2]);
					} else {								// it's a date
						return new Date(arrDate[0], (arrDate[1]-1), arrDate[2]);
					}
				case 6:		// it's a date/time
					return new Date(arrDate[0], (arrDate[1]-1), arrDate[2], arrDate[3], arrDate[4], arrDate[5]);
				default:	// invalid date
					return false;
				}
			case 1:		// time only
				return new Date(null, null, null, arrDate[0], arrDate[1], arrDate[2]);
			case 2:		// date only
				return new Date(arrDate[0], (arrDate[1]-1), arrDate[2]);
			case 3:		// date and time
				return new Date(arrDate[0], (arrDate[1]-1), arrDate[2], arrDate[3], arrDate[4], arrDate[5]);
			default:	// invalid option
				return false;
			}
		},
		Dtm2Obj: function(dtDateTime)
		{
			var arrDate = dtDateTime.split(/[-, :\/]/);
			if (arrDate.length > 3) {
				return new Date(arrDate[2], (arrDate[1]-1), arrDate[0], arrDate[3], arrDate[4], arrDate[5]);
			} else {
				return new Date(arrDate[2], (arrDate[1]-1), arrDate[0]);
			}
		},
		Dtm2Iso: function(dtDateTime, blnIsReversed, dtOpt)
		{
			// dmReversed:	refers to input value: false = italian (d/m/y), true = english (m/d/y) - default to ita.
			// dtOpt: 		refers to output value: expected 1=time, 2=date, 3=date and time - default to 2 (date only)
			if (!dtOpt) dtOpt = 2;
			var strDtm = '';
			var arrDate = dtDateTime.split(/[-, :\/]/);
			var strDay = (blnIsReversed ? arrDate[1] : arrDate[0]);
			var strMonth = (blnIsReversed ? arrDate[0] : arrDate[1]);
			var strYear = arrDate[2];
			var strHours = arrDate[3];
			var strMinutes = arrDate[4];
			var strSeconds = arrDate[5];
			switch (dtOpt) {
			case 1:	//time only
				if (arrDate.length > 3)	{ strDtm = strHours+':'+strMinutes+':'+strSeconds; }
				else					{ strDtm = '00:00:00'; }
				break;
			case 2:	//date only
				strDtm = strYear+'-'+strMonth+'-'+strDay;
				break;
			case 3:	// date and time
				if (arrDate.length > 3)	{ strDtm = strYear+'-'+strMonth+'-'+strDay+' '+strHours+':'+strMinutes+':'+strSeconds; }
				else					{ strDtm = strYear+'-'+strMonth+'-'+strDay+' 00:00:00'; }
				break;
			}
			return strDtm;
		},
		Obj2Dtm: function(objDateTime, dtOpt)
		{
			// dtOpt expected: 1=time, 2=date, 3=date and time
			var strDtm = '';
			var strDay = objDateTime.getDate(); if (strDay < 10) strDay = '0'+strDay;
			var strMonth = objDateTime.getMonth()+1; if (strMonth < 10) strMonth = '0'+strMonth;
			var strYear = objDateTime.getFullYear();
			if (dtOpt != 2) {
				var strHours = objDateTime.getHours(); if (strHours < 10) strHours = '0'+strHours;
				var strMinutes = objDateTime.getMinutes(); if (strMinutes < 10) strMinutes = '0'+strMinutes;
				var strSeconds = objDateTime.getSeconds(); if (strSeconds < 10) strSeconds = '0'+strSeconds;
			}
			switch (dtOpt) {
			case 1:	//time only
				strDtm = strHours+':'+strMinutes+':'+strSeconds;
				break;
			case 2:	//date only
				strDtm = strDay+'/'+strMonth+'/'+strYear;
				break;
			case 3:	// date and time
				strDtm = strDay+'/'+strMonth+'/'+strYear+' '+strHours+':'+strMinutes+':'+strSeconds;
				break;
			}
			return strDtm;
		}
	},
	cookie:
	{
		Create: function(name,value,days) {
			if (days) {
				var date = new Date();
				date.setTime(date.getTime()+(days*24*60*60*1000));
				var expires = "; expires="+date.toGMTString();
			}
			else var expires = "";
			document.cookie = name+"="+value+expires+"; path=/";
		},
		Read: function(name) {
			var nameEQ = name + "=";
			var ca = document.cookie.split(';');
			for(var i=0;i < ca.length;i++) {
				var c = ca[i];
				while (c.charAt(0)==' ') c = c.substring(1,c.length);
				if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
			}
			return null;
		},
		Erase: function(name) {
			this.Create(name, '', -1);
		}
	},
	debug:
	{
		dump: function (arr,level) {
			var dumped_text = "";
			if(!level) level = 0;

			//The padding given at the beginning of the line.
			var level_padding = "";
			for(var j=0;j<level+1;j++) level_padding += "    ";
			if(typeof(arr) == 'object') { //Array/Hashes/Objects
				for(var item in arr) {
					var value = arr[item];
					if(typeof(value) == 'object') { //If it is an array,
						dumped_text += level_padding + "'" + item + "' ...\n";
						dumped_text += this.dump(value,level+1);
					} else {
						dumped_text += level_padding + "'" + item + "' => \"" + value + "\"\n";
					}
				}
			} else { //Stings/Chars/Numbers etc.
				dumped_text = "===>"+arr+"<===("+typeof(arr)+")";
			}
			return dumped_text;
		},
		log: function(obj) {
			if (!window.console) {
				//this.console = ($('#fw_debug_console') ? $('#fw_debug_console') : $('<div id="fw_debug_console"></div>').appendTo(document).draggable());
				this.fwc = (
					($('#fw_debug_console').length != 0)
					?
					$('#fw_debug_console')
					:
					$(
						'<div '+
							'id="fw_debug_console" '+
							'class="ui-widget-content ui-state-default" '+
							'style="width: 800px; height: 600px; overflow: auto;">'+
							'<h3 class="ui-widget-header">(Ugly) debug output</h3></div>'
					).appendTo('body').draggable({handle: 'h3'}));
				this.fwc.append('<pre>'+fw.debug.dump(obj)+'</pre>').show().find('h3').one('dblclick', function() {$(this).parent().hide();});
				//this.console.append(fw.debug.dump(obj)).show();
				//alert("DEBUG (no console found):\n"+fw.debug.dump(obj));
			} else {
				console.log(obj);
			}
		}
	},
	form:
	{
		DeleteBound: function(formID, pkValue, async, msgID, fncCallBack) {
			if (async === undefined) async = true;
			if (msgID) $('#'+msgID).html('Eliminazione...');
			var aF = formID.split(',');
			var aS = {};
			var i = 0;
			for (var f in aF) {
				var jqF = $('#'+aF[f]);
				var key = {tbName: jqF.attr('fwtbl'), pkName: jqF.attr('fwpkn'), pkType: jqF.attr('fwpkt'), pkValue: pkValue};
				aS[i] = {key: key};
				i++;
			}
			if (async) {
				$.ajax({url:'/common/fw.php?action=DeleteBound', type:'POST', data:aS, cache:false, async:true,
					success: function(jSonRaw) {
						var jSon = fw.ajax.GetResponse(jSonRaw);
						if (msgID) $('#'+msgID).html('');
						if (typeof(fncCallBack) == 'function') fncCallBack(jSon);
					}
				});
			} else {
				var jSon = fw.ajax.GetResponse($.ajax({
					url:'/common/fw.php?action=DeleteBound', type:'POST', data:aS, cache:false, async:false
				}).responseText);
				return jSon;
			}
		},
		GetValues: function(formID) {
			var data = {};
			var thisForm = $('#'+formID);
			var thisVal = '';
			var defTb = thisForm.attr('fwtbl') || '';
			thisForm.find('.fw-bound').each(function() {

				switch ($(this).attr('type')) {
				case 'checkbox':
					thisVal = ($(this).is(':checked') ? 1 : 0);
					break;
				default:
					thisVal = $(this).val();
					break;
				}

				data[this.id] = {
					tbl	: ($(this).attr('fwtbl') || defTb),
					fld	: $(this).attr('fwfld'),
					typ	: $(this).attr('fwtyp'),
					val : thisVal
				};
			});
			return data;
		},
		LoadBound: function(formID, pkValue, async, msgID, fncCallBack) {
			if (async === undefined) async = true;
			if (msgID) $('#'+msgID).html('Caricamento...');
			var aL = {};
			var aF = formID.split(',');
			for (var f in aF) {
				var jqF = $('#'+aF[f]);
				var keys = {
					fwtbl: (jqF.attr('fwtbl') || ''),
					fwpkn: (jqF.attr('fwpkn') || ''),
					fwpkt: (jqF.attr('fwpkt') || ''),
					fwpkv: pkValue
				};
				var data = [];
				jqF.find('.fw-bound').each(function() {
					data.push({
						tbl: ($(this).attr('fwtbl') || ''),
						fld: ($(this).attr('fwfld') || ''),
						typ: ($(this).attr('fwtyp') || '')
					});
				});
				aL[aF[f]] = {keys: keys, data: data};
			}
			if (async) {
				$.ajax({url:'/common/fw.php?action=LoadBound', type:'POST', data:aL, cache:false, async:true,
					success: function(jSonRaw) {
						var jSon = fw.ajax.GetResponse(jSonRaw);
						if (msgID) $('#'+msgID).html('');
						if (typeof(fncCallBack) == 'function') fncCallBack(jSon);
					}
				});
			} else {
				var jSon = fw.ajax.GetResponse($.ajax({
					url:'/common/fw.php?action=SaveBound', type:'POST', data:aL, cache:false, async:false
				}).responseText);
				return jSon;
			}
		},
		SaveBound: function(formID, pkValue, additionalData, async, msgID, fncCallBack) {
			if (async === undefined) async = true;
			if (msgID) $('#'+msgID).html('Salvataggio...');
			var aF = formID.split(',');
			var aS = {};
			var i = 0;
			for (var f in aF) {
				var jqF = $('#'+aF[f]);
				var data = fw.form.GetValues(aF[f]);
				if (additionalData && additionalData[aF[f]]) $.extend(data, additionalData[aF[f]]);
				if (data) {
					var key = {tbName: jqF.attr('fwtbl'), pkName: jqF.attr('fwpkn'), pkType: jqF.attr('fwpkt'), pkValue: pkValue};
					aS[i] = {key: key, data: data};
					i++;
				}
			}
			if (async) {
				$.ajax({url:'/common/fw.php?action=SaveBound', type:'POST', data:aS, cache:false, async:true,
					success: function(jSonRaw) {
						var jSon = fw.ajax.GetResponse(jSonRaw);
						if (msgID) $('#'+msgID).html('');
						if (typeof(fncCallBack) == 'function') fncCallBack(jSon);
					}
				});
			} else {
				var jSon = fw.ajax.GetResponse($.ajax({
					url:'/common/fw.php?action=SaveBound', type:'POST', data:aS, cache:false, async:false
				}).responseText);
				return jSon;
			}
		},
		SetValues: function(data) {
			for (var frmd in data) {
				$('#'+frmd+' .fw-bound').each(function() {
					if (data[frmd]) {
						var cVal = (data[frmd][$(this).attr('fwfld')] || '');
						if ($(this).is('input, select, textarea')) {
							switch ($(this).attr('type')) {
							case 'checkbox':
								if (cVal == 'false' || cVal == '0' || cVal == '') {
									$(this).removeAttr('checked');
								} else {
									$(this).attr('checked', 'checked');
								}
								break;
							default:
								$(this).val(cVal);
								break;
							};
						} else {
							$(this).text(cVal);
						}
					}
				});
			};
		},
		select:
		{
			appendOption: function(jqSelect, sOptionVal, sOptionLabel) {
				jqSelect.append($('<option></option>').val(sOptionVal).html(sOptionLabel));
			}
		},
		checklist:
		{
			GetMask: function(jqChkCont) {
				var mask = 0;
				var i = 0;
				jqChkCont.find('input[type=checkbox], input[type=radio]').each(function() {
					//var p = Math.pow(2, i);
					// uses checkboxes values if present, otherwise uses powers of 2
					var p =  parseInt(($(this).val() || Math.pow(2, i)), 10);
					if ($(this).is(':checked') || $(this).is(':selected')) mask += p;
					i++;
				});
				return mask;
			},
			GetLabels: function(jqChkCont) {
				var labels = [];
				jqChkCont.find('input[type=checkbox], input[type=radio]').each(function() {
					if ($(this).is(':checked') || $(this).is(':selected')) labels.push($(this).parent().text().replace(/^\s+|\s+$/g,""));
				});
				return labels;
			},
			SetMask: function(jqChkCont, mask) {
				var i=0;
				$(jqChkCont).find('input[type=checkbox], input[type=radio]').each(function() {
					//((mask & (Math.pow(2,i))) ? $(this).attr('checked', 'checked').attr('selected', 'selected') : $(this).removeAttr('checked').removeAttr('selected'));
					// uses checkboxes values if present, otherwise uses powers of 2
					var p = parseInt($(this).val() || (Math.pow(2,i)), 10);
					(((mask & p) == p) ? $(this).attr('checked', 'checked').attr('selected', 'selected') : $(this).removeAttr('checked').removeAttr('selected'));
					i++;
				});
				return jqChkCont;
			}
		}
	},
	report:
	{
		Open: function(rptName, rp, blnDebug)
		{
			// notes on rp param structure. It is expected to be that way:
			/* rp
			 * 	[0]
			 * 		source: /complete/path/to/php/report/file0.php
			 * 		params: key1: value1, key2: value2, ..., keyN: valueN
			 * 	[1]
			 * 		source: /complete/path/to/php/report/file1.php
			 * 		params: key1: value1, key2: value2, ..., keyN: valueN
			 * 	...
			 * 	[N]
			 * 		source: /complete/path/to/php/report/fileN.php
			 * 		params: key1: value1, key2: value2, ..., keyN: valueN
			 */
			// for the moment, only 1 item is allowed in the array.
			// this structure has been made for future uses!

			var newWindow = {};
			var oNewDoc = {};

			// define the appereance of the report container window.
			var strHtmlWaitMsg = '<div align="center"><table><tr><td align="center"><h2>Apertura del report in corso...<br>Attendere, prego...</h2></td></tr><tr><td align="center"><img src="/common/images/please-wait.gif" /></td></tr></table></div>';
			var strWParams = 'channelmode=0,directories=0,fullscreen=0,height=600,left=100,location=0,menubar=0,resizable=1,scollbars=1,status=1,titlebar=0,toolbar=0,top=100,width=800';

			// check if reports array exists for the selected array AND if it's not closed
			if (newWindow[rptName] && !newWindow[rptName].closed) {
				newWindow[rptName] = window.open('',rptName,'');
				newWindow[rptName].close();
				newWindow[rptName] = null;
			}
			// instance a new window. If it was already existing, it would be destroyed previously.
			newWindow[rptName] = window.open('about:blank', rptName, strWParams);
			try
			{
				// tries to open the document (window content). This will fail on IE if the window was previously existing.
				oNewDoc[rptName] = newWindow[rptName].document.open();
			}
			catch (err)
			{
				// following code will recover both the situations:
				// - previous code have failed due to IE + prev. existing doc.	- or -
				// - link to previous report windows lost due to page refresh!
				newWindow[rptName] = window.open('',rptName,'');
				oNewDoc[rptName] = newWindow[rptName].document.open();
			}

			// just put the report ahead other windows...
			newWindow[rptName].focus();

			// build the page, creating the "wait please", adding params and then forcing a submit.
			oNewDoc[rptName].write(strHtmlWaitMsg);

			// builds the hidden controls used for passing params in post.
			var strHtml = '<form id="reportGateway" name="reportGateway" method="POST" action="/common/fw.php?action=OpenReport">';
			strHtml += '<input id="hidDebugOn" name="hidDebugOn" type="hidden" value="'+(blnDebug ? blnDebug : false)+'">';

			var idx = -1;
			for (var i in rp) {
				idx++;
				// extract the filename from the complete report reference, to be used as key in hidden controls
				var m = rp[i]['source'].match(/(.*)[\/\\]([^\/\\]+\.\w+)$/);
				//var strRptName = m[2].replace(/\./g, '_');
				var strRptName = m[2];

				strHtml += '<input name="r['+idx+'][s]" type="hidden" value="'+strRptName+'">';
				// no need to encode values, because they're stuffed into hidden controls. Submit built-in procedure will encode them!
				// just take care of quotes, in order to set the correct value up to the hidden control
				//var sVal = serialize(rp[i]).replace(/"/g,'&quot;');
				//var sVal = $.param(rp[i]);	//.replace(/"/g,'&quot;');

				var p = rp[i].params;
				for (var k in p) {
					var v = p[k];
					strHtml += '<input name="r['+idx+'][p]['+k+']" type="hidden" value="'+v+'">';
				}
				//var sVal = $.param(rp[i]);
				//strHtml += '<input id="' + strRptName + '" name="' + strRptName + '[]" type="hidden" value="' + sVal + '">';
			}

			strHtml += '</form>';
			oNewDoc[rptName].write(strHtml);

			// close the document (content) and actually send it to the browser.
			oNewDoc[rptName].close();
			newWindow[rptName].document.reportGateway.submit();
		}
	},
	ui:
	{
		PleaseWait: function(visible, divSelector)
		{
			if ((visible === undefined) || (visible === true)) {
				var jqOverlay = $('#fw-div-busy');
				if (jqOverlay.length == 0) jqOverlay = $('<div id="fw-div-busy"></div>').appendTo('body');
				var jqDiv = $(divSelector);
				var h = jqDiv.height();
				var w = jqDiv.width();
				var offset = jqDiv.offset();
				var l = offset.left;
				var t = offset.top;
				jqOverlay.css({width: w, height: h, left: l, top: t}).fadeTo('fast', 0.3);
			} else {
				$('#fw-div-busy').fadeTo('fast', 0, function() { $(this).hide(); });
			}
		}
	},
	utils:
	{
		trim: function(text)
		{
			return (text || "").replace( /^\s+|\s+$/g, "" );
		},
		DateDiff: function(objDtH, objDtL, blnStrict)
		{
			// accept 2 js date objects as inputs (no matter the order, unless blnStrict is set to true),
			// returns the difference (expressed in number of days)

			// The number of milliseconds in one day
		    var ONE_DAY = 1000 * 60 * 60 * 24;

		    // Convert both dates to milliseconds
		    var date1_ms = objDtH.getTime();
		    var date2_ms = objDtL.getTime();

		    // Calculate the difference in milliseconds
		    var difference_ms = (blnStrict ? (date1_ms - date2_ms) : Math.abs(date1_ms - date2_ms));

		    // Convert back to days and return
		    return Math.round(difference_ms/ONE_DAY);
		}
	},
	validation:
	{
		_errors: 0
		,
		_errMessages: ''
		,
		Attach: function(strContID, blnPreventDefault, hCF)
		{
			// hCF = hash of Classes / Functions
			if (!blnPreventDefault) {
				// default validation handlers
				// IsAccordingToRegExp and IsIntegerBetween are not attached by default, because they need more params...
				var jqCont = $('#'+strContID);

				jqCont.find('.fw-validate-compiled').each(function() {
					$(this).blur(function(event) { event.stopImmediatePropagation(); return fw.validation.IsCompiled(this, true); } );
				});
				jqCont.find('.fw-validate-cap').each(function() {
					$(this).blur(function() { return fw.validation.IsCap(this, $(this).hasClass('fw-validate-compiled')); } );
				});
				jqCont.find('.fw-validate-date').each(function() {
					$(this).blur(function() { return fw.validation.IsDate(this, $(this).hasClass('fw-validate-compiled')); } );
				});
				jqCont.find('.fw-validate-email').each(function() {
					$(this).blur(function() { return fw.validation.IsEMail(this, $(this).hasClass('fw-validate-compiled')); } );
				});
				jqCont.find('.fw-validate-iban').each(function() {
					$(this).blur(function() { return fw.validation.IsIban(this, $(this).hasClass('fw-validate-compiled')); } );
				});
				jqCont.find('.fw-validate-number').each(function() {
					$(this).blur(function() { return fw.validation.IsNumber(this, $(this).hasClass('fw-validate-compiled')); } );
				});
				jqCont.find('.fw-validate-integer').each(function() {
					$(this).blur(function() { return fw.validation.IsInteger(this, $(this).hasClass('fw-validate-compiled')); } );
				});
				jqCont.find('.fw-validate-telnum').each(function() {
					$(this).blur(function() { return fw.validation.IsTelNum(this, $(this).hasClass('fw-validate-compiled')); } );
				});
				jqCont.find('.fw-validate-cf').each(function() {
					$(this).blur(function() { return fw.validation.IsCF(this, $(this).hasClass('fw-validate-compiled')); } );
				});
				jqCont.find('.fw-validate-piva').each(function() {
					$(this).blur(function() { return fw.validation.IsPIVA(this, $(this).hasClass('fw-validate-compiled')); } );
				});

				/*
				// the use of "live" event binder result in an insidious bug: "blur" event is not triggered
				// the 1st time, but only starting from the 2nd time (don't know why).
				// so, I didn't use it!
				*/
			}
			// add user-defined valudation functions...
			for (var cl in hCF) {
				$('.'+cl).addClass('fw-validate-custom').live('blur', function(){
					var fnc = hCF[cl];
					var res = fnc(this, false);
					if (res !== true) this._errors++;
					return res;
				});
			}
		},
		Handling: function(obj, blnIsValid, strMessage)
		{
			if (!blnIsValid) {
				$(obj).addClass('ui-state-error').attr('title', strMessage);
				this._errors++;
				//alert(strMessage);
			} else {
				$(obj).removeClass('ui-state-error').attr('title', '');
			}
			return blnIsValid;
		},
		CheckAll: function(strContID, blnSilent)
		{
			var sMessage = '';
			this._errMessages = '';
			this._errors = 0;
			$('#'+strContID+' [class*=fw-validate-]').each(function() {
				$(this).trigger('blur');
				if (m = $(this).attr('title')) {
					//this._errMessages += $(this).parent().prev().html()+' ('+this.id+'): '+m;
					//check if exist a label associated with control and get that value instead element id:
					if ($('label[for="' + this.id + '"]').length > 0) {
						fw.validation._errMessages += "\n" + $('label[for="' + this.id + '"]').text() + ': ' + m.replace("\n", '');
					} else {
						fw.validation._errMessages += "\n" + this.id + ': ' + m.replace("\n", '');
					}
				}
			});
			if (this._errors > 0) {
				if (!blnSilent) alert('Sono presenti '+this._errors+' errori di validazione: '+fw.validation._errMessages);
				return false;
			} else {
				return true;
			}
		},
		IsCompiled: function(obj, isMandatory)
		{
			var blnIsValid; var strMessage = '';
			if (isMandatory && obj.value=='') {
				strMessage += "\nIl campo \u00E8 obbligatorio!";
				blnIsValid = false;
			} else {
				blnIsValid = true;
			}
			return this.Handling(obj, blnIsValid, strMessage);
		},
		IsInteger: function(obj, isMandatory)
		{
			var blnIsValid;	var strMessage = '';
			if (isMandatory && obj.value=='') {
				strMessage += "\nIl campo \u00E8 obbligatorio!";
				blnIsValid = false;
			} else if (!isMandatory && obj.value=='') {
				blnIsValid = true;
			} else {
				if (Math.ceil(obj.value) == Math.floor(obj.value)) {
					blnIsValid = true;
				} else {
					strMessage += "\nIl numero deve essere intero";
					blnIsValid = false;
				}
			}
			return this.Handling(obj, blnIsValid, strMessage);
		},
		IsIntegerBetween: function(obj, intMin, intMax, isMandatory)
		{
			var blnIsValid;	var strMessage = '';
			if (isMandatory && obj.value=='') {
				strMessage += "\nIl campo \u00E8 obbligatorio!";
				blnIsValid = false;
			} else if (!isMandatory && obj.value=='') {
				blnIsValid = true;
			} else {
				var intValue = parseInt(obj.value, 10);
				if (intValue != obj.value) {		//not an integer
					strMessage += "\nIl numero deve essere intero";
					blnIsValid = false;
				} else {
					if (!intMin) intMin = -2147483648;
					if (!intMax) intMax = 2147483647;
					if ((intValue >= intMin) && (intValue <= intMax)) {
						blnIsValid = true;
					} else {
						strMessage += "\n\ne compreso fra "+intMin+' e '+intMax;
						blnIsValid = false;
					}
				}
			}
			return this.Handling(obj, blnIsValid, strMessage);
		},
		IsNumber: function(obj, isMandatory)
		{
			var blnIsValid;	var strMessage = '';
			if (isMandatory && obj.value=='') {
				strMessage += "\nIl campo \u00E8 obbligatorio!";
				blnIsValid = false;
			} else if (!isMandatory && obj.value=='') {
				blnIsValid = true;
			} else {
				if (isNaN(parseFloat(obj.value)) || (obj.value != parseFloat(obj.value))) {
					strMessage += "\nIl valore deve essere numerico";
					blnIsValid = false;
				} else {
					blnIsValid = true;
				}
			}
			return this.Handling(obj, blnIsValid, strMessage);
		},
		IsAccordingToRegExp: function(obj, re, isMandatory)
		{
			var blnIsValid;	var strMessage = '';
			if (isMandatory && obj.value=='') {
				strMessage += "\nIl campo \u00E8 obbligatorio!";
				blnIsValid = false;
			} else if (!isMandatory && obj.value=='') {
				blnIsValid = true;
			} else if (!re.test(obj.value)) {
				strMessage += "\nValore non conforme all'espressione richiesta!";
				blnIsValid = false;
			} else {
				blnIsValid = true;
			}
			return this.Handling(obj, blnIsValid, strMessage);
		},
		IsCap: function(obj, isMandatory)
		{
			var blnIsValid;	var strMessage = '';
			if (isMandatory && obj.value=='') {
				strMessage += "\nIl campo \u00E8 obbligatorio!";
				blnIsValid = false;
			} else if (!isMandatory && obj.value=='') {
				blnIsValid = true;
			} else {
				var re = /^\d{5}$/;
				if (!re.test(obj.value)) {
					strMessage += "\nSono ammessi esclusivamente numeri, 5 cifre!";
					blnIsValid = false;
				} else {
					blnIsValid = true;
				}
			}
			return this.Handling(obj, blnIsValid, strMessage);
		},
		IsCF: function(obj, isMandatory)
		{
			var blnIsValid;	var strMessage = '';
			if (isMandatory && obj.value=='') {
				strMessage += "\nIl campo \u00E8 obbligatorio!";
				blnIsValid = false;
			} else if (!isMandatory && obj.value=='') {
				blnIsValid = true;
			} else {
				var cf = obj.value.toUpperCase();
				if( cf.length != 16 ) {
					strMessage += "\nLunghezza codice errata!";
					blnIsValid = false;
				} else {
					var i;
					var validi = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
					for(i=0; i<16; i++) {
						if (validi.indexOf(cf.charAt(i)) == -1) {
							strMessage += "\nCarattere'"+cf.charAt(i)+"' non valido! Sono ammesse solo lettere e cifre!";
							blnIsValid = false;
						} else {
							var set1 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
							var set2 = "ABCDEFGHIJABCDEFGHIJKLMNOPQRSTUVWXYZ";
							var setpari = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
							var setdisp = "BAKPLCQDREVOSFTGUHMINJWZYX";
							var s = 0;
							for(i=1; i<=13; i+=2)
								s += setpari.indexOf(set2.charAt(set1.indexOf(cf.charAt(i))));
							for(i=0; i<=14; i+=2)
								s += setdisp.indexOf(set2.charAt(set1.indexOf(cf.charAt(i))));
							if (s%26 != cf.charCodeAt(15)-'A'.charCodeAt(0)) {
								strMessage += "\nCodice non corretto! (codice di controllo non corrispondente)";
								blnIsValid = false;
							} else {
								blnIsValid = true;
							}
						}
					}
				}
			}
			return this.Handling(obj, blnIsValid, strMessage);
		},
		IsDate: function(obj, isMandatory)
		{
			var blnIsValid;	var strMessage = '';
			if (isMandatory && obj.value=='') {
				strMessage += "\nIl campo \u00E8 obbligatorio!";
				blnIsValid = false;
			} else if (!isMandatory && obj.value=='') {
				blnIsValid = true;
			} else {
				var dtObj = fw.conv.Dtm2Obj(obj.value, 2);
				if (dtObj.toString() == 'Invalid Date') {
					strMessage += "\nIl valore inserito non \u00E8 una data valida!";
					blnIsValid = false;
				} else {
					blnIsValid = true;
				}
			}
			return this.Handling(obj, blnIsValid, strMessage);
		},
		IsEMail: function(obj, isMandatory)
		{
			var blnIsValid;	var strMessage = '';
			if (isMandatory && obj.value=='') {
				strMessage += "\nIl campo \u00E8 obbligatorio!";
				blnIsValid = false;
			} else if (!isMandatory && obj.value=='') {
				blnIsValid = true;
			} else {
				var re = /^([a-zA-Z0-9_\.\-\+])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/;
				if (!re.test(obj.value)) {
					strMessage += "\nIl valore inserito non sembra essere un indirizzo e-mail!\nLa forma attesa \u00E8: nome-utente@nome-dominio.xxx";
					blnIsValid = false;
				} else {
					blnIsValid = true;
				}
			}
			return this.Handling(obj, blnIsValid, strMessage);
		},
		IsIban: function(obj, isMandatory)
		{
			var blnIsValid;	var strMessage = '';
			if (isMandatory && obj.value=='') {
				strMessage += "\nIl campo \u00E8 obbligatorio!";
				blnIsValid = false;
			} else if (!isMandatory && obj.value=='') {
				blnIsValid = true;
			} else {
				// IT\d{2}[ ][a-zA-Z]\d{3}[ ]\d{4}[ ]\d{4}[ ]\d{4}[ ]\d{4}[ ]\d{3}|IT\d{2}[a-zA-Z]\d{22}
				var re = /^IT\d{2}[ ][a-zA-Z]\d{3}[ ]\d{4}[ ]\d{4}[ ]\d{4}[ ]\d{4}[ ]\d{3}|IT\d{2}[a-zA-Z]\d{22}$/;
				if (!re.test(obj.value)) {
					strMessage += "\nIl valore inserito non sembra essere un codice iban valido!";
					blnIsValid = false;
				} else {
					blnIsValid = true;
				}
			}
			return this.Handling(obj, blnIsValid, strMessage);
		},
		IsPIVA: function(obj, isMandatory)
		{
			var blnIsValid;	var strMessage = '';
			if (isMandatory && obj.value=='') {
				strMessage += "\nIl campo \u00E8 obbligatorio!";
				blnIsValid = false;
			} else if (!isMandatory && obj.value=='') {
				blnIsValid = true;
			} else {
				var pi = obj.value;
				if (pi.length != 11) {
					strMessage += "\nLunghezza non corretta: attesi 11 caratteri!";
					blnIsValid = false;
				} else {
					var validi = "0123456789";
					for (i=0; i<11; i++) {
						if (validi.indexOf(pi.charAt(i)) == -1) {
							strMessage += "\ncarattere '"+pi.charAt(i)+"' non valido. Sono ammesse solo cifre!";
							blnIsValid = false;
						} else {
							var s = 0;
							for (i=0; i<=9; i+=2)
								s += pi.charCodeAt(i) - '0'.charCodeAt(0);
							for (i=1; i<=9; i+=2) {
								c = 2*(pi.charCodeAt(i) - '0'.charCodeAt(0));
								if (c > 9) c = c - 9;
								s += c;
							}
							if ((10 - s%10)%10 != pi.charCodeAt(10) - '0'.charCodeAt(0)) {
								strMessage += "\nPartita IVA non valida: il codice di controllo non corrisponde!";
								blnIsValid = false;
							} else {
								blnIsValid = true;
							}
						}
					}
				}
			}
			return this.Handling(obj, blnIsValid, strMessage);
		},
		IsTelNum: function(obj, isMandatory)
		{
			var blnIsValid;	var strMessage = '';
			if (isMandatory && obj.value=='') {
				strMessage += "\nIl campo \u00E8 obbligatorio!";
				blnIsValid = false;
			} else if (!isMandatory && obj.value=='') {
				blnIsValid = true;
			} else {
				var re = /^\d{6,}$/;
				if (!re.test(obj.value.replace(/(\+|-|\s|\.|\/)/gi,""))) {
					strMessage += "\n- Il numero deve essere composto di almeno 6 cifre\n- Sono ammessi esclusivamente numeri, spazi, barre, punti ed il simbolo '+'.";
					blnIsValid = false;
				} else {
					blnIsValid = true;
				}
			}
			return this.Handling(obj, blnIsValid, strMessage);
		}
	},
	widget:
	{
		AtlCal: {
			ac: {},
			mouse2Down: 0,
			activate: function(container) {
				var contID = container+'_ol';
				$('#'+contID+', #'+container+'_magnifier').bind("contextmenu", function () { return false; }).disableSelection();
				var ac = fw.widget.AtlCal.ac;
				// prepare the magnifier...
				var mag = $('#'+container+'_magnifier');
	        	var magDay = mag.find('.cal-mag-day');
	        	var magVal = mag.find('.cal-mag-val');
				mag.mouseup(function(evt) { $(this).hide();});
				// attach magnifier events
				$('#'+contID).find('li.cal-sel').each(function() {
				    $(this).hover(
				        function() {
				        	var offSet = $(this).offset();
				        	
				        	var cl = $(this).attr('class');
				        	var i = cl.indexOf('y');
				        	var r = cl.substring(i+1, i+3);
				        	var m = $('#'+container+' .divMonth').filter('[ref="'+r+'"]').html();
				        	
				        	magDay.html($(this).find('.day').html()+' '+m);
				        	magVal.html($(this).find('.val').html());
				        	offSet.left -= 12;	offSet.top -= 12;
				            mag.offset(offSet);
				        },
				        function() {
				            //$('#'+container+'_magnifier').hide();
				        }
				    ).mousedown(function(evt) {
				    	if (evt.button == 2) {
				    		var offSet = $(this).offset();
				    		
				    		var cl = $(this).attr('class');
				        	var i = cl.indexOf('y');
				        	var r = cl.substring(i+1, i+3);
				        	var m = $('#'+container+' .divMonth').filter('[ref="'+r+'"]').html();
				        	
				        	magDay.html($(this).find('.day').html());
				        	magVal.html($(this).find('.val').html());
				        	offSet.left -= 12;	offSet.top -= 12;
				            mag.show().offset(offSet);
				    	}
				    });
				});
				// make its elements selectable
				$('#'+contID).selectable({
					filter: '.cal-sel',
					stop: function(){
						if ($('#'+contID+' .divYear').hasClass('ui-selected')) {
							$('#'+contID+' li.cal-sel').addClass('ui-selected');
						} else {
							$('#'+contID+' .divMonth').filter('.ui-selected').each(function(){
								var idY = $(this).attr('ref');
								$('#'+contID+' .y'+idY).addClass('ui-selected');
							});
							$('#'+contID+' .weekDay').filter('.ui-selected').each(function(){
								var idX = $(this).attr('ref');
								$('#'+contID+' .x'+idX).addClass('ui-selected');
							});
						}
					}
				});
				// set button
				$('#'+container+' .'+container+'_set').click(function() { fw.widget.AtlCal.set(container, $('#'+container+'_sel').val(), $('#'+container+'_val').val()); });
				// unset button
				$('#'+container+' .'+container+'_unset').click(function() { fw.widget.AtlCal.unset(container, $('#'+container+'_sel').val()); });
				// show button
				//$('#'+container+' .'+container+'_show').click(function() { console.log(fw.widget.AtlCal.ac[container]); alert(fw.debug.dump(fw.widget.AtlCal.ac[container])); });
				// load button
				$('#'+container+' .'+container+'_load').click(function() { fw.widget.AtlCal.load(container); });
				// load button
				$('#'+container+' .'+container+'_save').click(function() { fw.widget.AtlCal.save(container); });
				// select change
				$('#'+container+'_sel').change(function() { fw.widget.AtlCal.change(container, $(this).val()); });
				// onblur validation
				$('#'+container+'_val').blur(function() {
					if (fw.widget.AtlCal.validate(container, $('#'+container+'_sel').val(), $(this).val())) {
						$(this).removeClass('ui-state-error');
					} else {
						$(this).addClass('ui-state-error');
					}
				});
				return this;
			},
			change: function(container, param) {
				var contID = container+'_ol';
				var pt = fw.widget.AtlCal.ac[container]['cfg']['fldTypes'][param];
				switch (pt) {
				case 'bln':
				case 'btm':
					$('#'+container+'_val').attr('disabled', 'disabled');	break;
				default:
					$('#'+container+'_val').removeAttr('disabled');			break;
				}
				$('#'+contID+' li.cal-sel').attr('style', '');
				$('#'+contID+' .cal-sel .val').html('&nbsp;');
				var acp = fw.widget.AtlCal.ac[container]['data'][param];
				var style = (fw.widget.AtlCal.ac[container]['cfg']['cssStyle'] || '');
				if (style) style = (style[param] || '');
				for (var i in acp) {
					$('#'+contID+' li#d'+i).find('.val').html(acp[i]);
					if (acp[i]) $('#'+contID+' li#d'+i).attr('style', style);
				}
				return this;
			},
			set: function(container, param, value) {
				var contID = container+'_ol';
				if (vv = fw.widget.AtlCal.validate(container, param, value)) {
					$('#'+container+'_val').removeClass('ui-state-error');
					$('#'+contID).find('.divYear, .divMonth, .weekDay').removeClass('ui-selected');
					var style = (fw.widget.AtlCal.ac[container]['cfg']['cssStyle'] || ''); if (style) style = (style[param] || '');
					$('#'+contID+' .ui-selected').each(function(){
						fw.widget.AtlCal.ac[container]['data'][param][$(this).attr('id').replace('d', '')] = vv;
						$(this).attr('style', style).find('.val').text(vv);	//$(this).removeClass('ui-selected').find('.val').text(value);
					});
				} else {
					$('#'+container+'_val').addClass('ui-state-error');
					alert(_.msgCannotSetWrongDataType);
				}
				return this;
			},
			unset: function(container, param) {
				var contID = container+'_ol';
				$('#'+contID).find('.divYear, .divMonth, .weekDay').removeClass('ui-selected');
				$('#'+contID+' .ui-selected').each(function(){
					//delete fw.widget.AtlCal.ac[container]['data'][param][$(this).attr('id').replace('d', '')];
					fw.widget.AtlCal.ac[container]['data'][param][$(this).attr('id').replace('d', '')] = 'null';
					$(this).attr('style', '').find('.val').html('&nbsp;');	//$(this).removeClass('ui-selected').find('.val').html('&nbsp;');
				});
				return this;
			},
			load: function(container, action) {
				if (!action) action = 'AtlCal_load';
				fw.ui.PleaseWait(true, '#'+container);
				$.ajax({url: (fw.widget.AtlCal.ac[container].cfg.call || '/common/fw.widget.atlcal.php')+'?action='+action, type:'POST', data: {cfg: fw.widget.AtlCal.ac[container]['cfg']},
					cache:false, async:true,
					success: function(jSonRaw) {
						var jSon = fw.ajax.GetResponse(jSonRaw);
						if (jSon.blnSuccess === true) {
							var data = jSon.arrParams;
							for (var p in fw.widget.AtlCal.ac[container].data) {
								fw.widget.AtlCal.ac[container].data[p] = (data[p] || {});
							}
							fw.widget.AtlCal.change(container, $('#'+container+'_sel').val());
						}
						fw.ui.PleaseWait(false);
					}
				});
				return this;
			},
			save: function(container, action) {
				if (!action) action = 'AtlCal_save';
				fw.ui.PleaseWait(true, '#'+container);
				//console.info(fw.widget.AtlCal.ac[container].data);
				$.ajax({url: (fw.widget.AtlCal.ac[container].cfg.call || '/common/fw.widget.atlcal.php')+'?action='+action, type:'POST', data: fw.widget.AtlCal.ac[container],
					cache:false, async:true,
					success: function(jSonRaw) {
						var jSon = fw.ajax.GetResponse(jSonRaw);
						fw.ui.PleaseWait(false);
						$('#'+container).trigger('atlcal_savecomplete', jSon.blnSuccess);
					}
				});
				return this;
			},
			setFlt: function(container, filter) {
				fw.widget.AtlCal.ac[container].cfg.filter = filter;
				return this;
			},
			validate: function(container, param, value) {
				//var value = $('#'+container+'_val').val();
				var pt = fw.widget.AtlCal.ac[container]['cfg']['fldTypes'][param];
				switch (pt) {
				case 'str':
					return value;	break;
				case 'bln':
					return 1;		break;
				default:
					//if (fw.validation.IsNumber(document.getElementById(container+'_val'), true)) {
					if (fw.validation.IsNumber({value: value}, true)) {
						return value;
					} else {
						return false;
					}
				}
			}
		},
		AtlLst:
		{
			activate: function(container) {
				$('#'+container+' .atl-lst-chkall').click(function() {
					var checked = $(this).is(':checked');
					if (checked) {
						$(this).parent().parent().find('.atl-lst-item').attr('checked', 'checked');
					} else  {
						$(this).parent().parent().find('.atl-lst-item').removeAttr('checked');
					}
				});
				return this;
			},
			uncheck: function(subCont) {
				$('#'+subCont+' .atl-lst-item:checked').removeAttr('checked');
				return this;
			},
			getValues: function(subCont) {
				var arrChk = [];
				$('#'+subCont+' .atl-lst-item:checked').each(function() {arrChk.push($(this).val());});
				return arrChk;
			}
		}
	}
	
};
