/*
 * form.js
 *
 * Copyright (C) 2006 - OS3 srl - http://www.os3.it
 *
 * Written by: Fabio Rotondo - fabio.rotondo@os3.it
 *
 * This is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation;
 * version 2 of the License ONLY.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this software; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * NOTE: this is the GPL version of the library. If you want to include this 
 *       library inside a CLOSED SOURCE / PROPRIETARY software, you need 
 *       to purchase a COMMERCIAL LICENSE. See http://www.os3.it/license/
 *       for more info.
 */

/*
 * 	2009-05-18:	ADD:	form.select_set_values ()
 *
 * 	2009-05-04:	ADD:	form.events: 'start', 'before-complete', 'complete'
 *			ADD:	form.easy
 *
 *			DEL:	form.onsubmit
 *			DEL:	form.form_callback
 *
 *	2009-03-06:	form.descr () now also accepts both a string and an object as param
 */

/*
	liwe.events [ 'submit' ] = callback con parametri ( campi_form, azione, func_di_cback );
*/

liwe.form = {};

liwe.form._instances = [];
liwe.form.cbacks = {};		// Internal callbacks
liwe.form.utils  = {};		// Global functions (forms only)

// liwe.form Callbacks
liwe.form.cbacks.ip_change = function ( v )
{
	console.debug ( v );
};

liwe.form.cbacks.calendar = function ( cal, year, month, day )
{
	document.getElementById ( cal.id + "@cal_year" ).value = year;
	document.getElementById ( cal.id + "@cal_month" ).value = month +1;
	document.getElementById ( cal.id + "@cal_day" ).value = day;

	liwe.form.utils.cal_set_hidden ( cal.id );
};

liwe.form.cbacks.calendar_change = function ( i )
{
	var base = i.id.split ( "@" ) [ 0 ];
	var two = "";
	var cal;

	if ( i.id.split ( "@" ) [ 1 ] )
		two  = '@' + i.id.split ( "@" ) [ 1 ];
	if ( two )
		cal = _os3_cals [ base + two ];
	else
		cal = _os3_cals [ base ];
	var year = document.getElementById ( base + two + '@cal_year' ).value;
	var month = document.getElementById ( base + two + '@cal_month' ).value;
	var day = document.getElementById ( base + two + '@cal_day' ).value;

	cal.set_date ( year, month - 1, day );

	liwe.form.utils.cal_set_hidden ( base + two );
	
	return true;
};

liwe.form.utils.cal_set_hidden = function ( id )
{
	var old = document.getElementById ( id );

	var year = document.getElementById ( id + '@cal_year' ).value;
	var month = document.getElementById ( id + "@cal_month" ).value;
	var day = document.getElementById ( id + "@cal_day" ).value;
	var s = '';

	if ( ! month ) month = "0";
	if ( ! day ) day = "0";
	if ( ! year ) year = "0000";

	day = new String ( day );
	month = new String ( month );
	year = new String ( year );

	s += year + "-" + ( month.length < 2 ? "0" + month : month ) + "-" + ( day.length < 2 ? "0" + day : day );

	if ( old.value != "" && old.value != s ) console.debug ( "Calendar: cambiato %s", s );

	document.getElementById ( id ).value = s;
};


liwe.form.get = function ( name )
{
	return liwe.form._instances [ name ];
};

liwe.form.check = function ( id )
{
	var s, t;
	var el, hint;
	var icon;
	var vals;
	var arr;

	var f = liwe.form.get ( id );

	// f.fetch_htmled_contents ();

	vals = f._mandatory;
	for ( t = 0; t < vals.length; t ++ )
	{
		el = document.getElementById ( vals [ t ] );
		hint = document.getElementById ( vals [ t ] + "@hint" );
		icon = document.getElementById ( vals [ t ] + "_mandatory" );

		if ( ! el ) continue;

		hint.innerHTML = "&nbsp;";

		if ( el.value.length == 0 ) 
		{
			hint.innerHTML = _ ( "os3_form", "Mandatory&nbsp;field" );

			icon.src = liwe._libbase + "/gfx/form/error.gif";
			el.focus ();
			return false;
		}
		icon.src = liwe._libbase + "/gfx/form/ok.gif";
	}

	vals = f._validates;
	for ( t = 0; t < vals.length; t ++ )
	{
		el = document.getElementById ( vals [ t ] [ 0 ] );
		icon = document.getElementById ( vals [ t ] [ 0 ] + "_valid" );
		hint = document.getElementById ( vals [ t ] [ 0 ] + "@hint" );
		if ( ! el ) continue;

		hint.innerHTML = "&nbsp;";
		if ( el.value.length == 0 ) continue;

		if ( vals [ t ] [ 1 ] ( el.value ) == false )
		{
			hint.innerHTML = _ ( "os3_form", "Validation&nbsp;failed" );	
			icon.src = liwe._libbase + "/gfx/form/error.gif";
			icon.alt = _ ( "os3_form", "Validation failed" );
			icon.title = _ ( "os3_form", "Validation failed" );
			el.focus ();
			return false;
		}
		icon.src = liwe._libbase + "/gfx/form/ok.gif";
		icon.alt = _ ( "os3_form", "Validation OK" );
		icon.title = _ ( "os3_form", "Validation OK" );
	}

	return true;
};

liwe.form.cbacks.ajax_submit = function ( id )
{
	var frm = liwe.form._instances [ id ];

	if ( ! frm.check () ) return;

	if ( frm.events [ 'start' ] )  
	{
		var r = frm.events [ 'start' ] ( frm );
		if ( ! r ) return;
	}

	var a = frm.get_values ();
	var action = frm._resolve_action ();
	
	// Resolve function pointer from function name in frm.events [ 'complete' ]
	eval ( "var _func = " + frm.events [ 'complete' ] );

	if ( ! frm.events [ 'submit' ] )
	{
		// AJAX Request
		liwe.AJAX.request ( action, a, _func, true );
	} else {
		frm.events [ 'submit' ] ( a, action, _func );
	}
};

// SPECIAL:
//	multi, bicolor, columns, width, height
function form_sel ( vars )
{
	if ( typeof OS3Sel == 'undefined' ) 
	{
		alert ( _ ( "os3_form", "You *MUST* include OS3Sel!" ) );
		return null;
	}

	this._start_field ( vars );

	vars [ 'id' ] = this.name + "@" + this._get_name ( vars );

	this.html += '<div id="' + vars [ 'id' ] + '"></div>';

	this._newline ( vars );

	var g = new OS3Sel ( 
		{ 	div_id: vars [ 'id' ], 
			multi: vars.get ( 'multi', 0 ), 
			bicolor: vars.get ( 'bicolor', true ),
			num_columns: vars.get ( 'columns', 1 ),
			width: vars.get ( 'width', '100%' ),
			height: vars.get ( 'height', '100%' )
		} );

	if ( vars [ 'headers' ] ) g.set_headers ( vars [ 'headers' ] );

	return g;
}

/*
function Form ( name, action, no_table )
{
	console.warn ( "Do not use the Form() function, use liwe.form.instance() instead" );
	return new liwe.form.instance ( name, action, no_table );
}
*/

// PUBLIC:
//		Form
liwe.form.instance = function ( name, action, no_table )
{
	this.name	= name;
	this.action	= action;
	this.easy	= true;

	this.events	= { 'start' : null, 'before-complete' : null, 'complete': null, 'submit' : null };

	this.no_table	= no_table;
	// this.auto_id	= true;

	this.multipart 	= false;
	// this.form_callback = '';

	this.html	= '';

	this.ajax_mode	= null;

	this.debug	= false;

	this._first_field = null;

	// PUBLIC METHODS
	// {{{ upload ( vars )
	this.upload = function ( vars )
	{
		this.multipart = true;

		this._start_field ( vars );
		this._add_field ( 'file', vars );
	};
	// }}}
	// {{{ text ( vars )
	this.text = function ( vars )
	{
		this._start_field ( vars );
		this._add_field ( 'text', vars );
	};
	// }}}
	// {{{ email ( vars )
	this.email = function ( vars )
	{
		vars [ 'filter' ] = "0123456789abcdefghijklmnopqrstuvwxyz._@";
		vars [ 'os3_def_validator' ] = liwe.validators.is_email;

		this._start_field ( vars );
		this._add_field ( 'text', vars );
	};
	// }}}
	// {{{ number ( vars )
	this.number = function ( vars )
	{
		vars [ 'filter' ] = "-0123456789";
		vars [ 'class'  ] = "number";
		vars [ 'os3_def_validator' ] = liwe.validators.is_integer;

		this._start_field ( vars );
		this._add_field ( 'text', vars );
	};
	// }}}
	// {{{ money ( vars )
	this.money = function ( vars )
	{
		if ( typeof Money == 'undefined' ) liwe.utils.append_js ( liwe._libbase + '/money.js' );
		
		vars [ 'filter' ] = "0123456789,.";
		vars [ 'class'  ] = "number";
		if ( ! vars [ 'onblur' ] )
			vars [ 'onblur' ]  = 'this.value = money_format ( this.value )';
		else
			 vars [ 'onblur' ]  = 'this.value = money_format ( this.value  );' + vars [ 'onblur' ] ;

		// vars [ 'os3_def_validator' ] = check_money;

		if ( vars [ 'defval' ] ) vars [ 'defval' ] = Money.fromLongInt ( vars [ 'defval' ] );
		if ( vars [ 'value' ] ) vars [ 'value' ] = Money.fromLongInt ( vars [ 'value' ] );

		this._start_field ( vars );
		this._add_field ( 'text', vars );
	};
	// }}}
	// {{{ submit ( vars )
	this.submit = function ( vars )
	{
		if ( typeof ( vars ) == 'string' ) vars = { value: vars };

		this._start_field ( vars );

		if ( this.multipart )
			this._add_field ( 'submit', vars );
		else
		{
			vars [ 'onclick' ] = "liwe.form.cbacks.ajax_submit('" + this.name + "');";
			this._add_button ( vars );
		}
	};
	// }}}
	// {{{ label ( vars )
	this.label = function ( vars )
	{
		this._start_field ( vars );
		this.html += vars [ 'value' ];
		this._newline ( vars );
	};
	// }}}

	// {{{ password ( vars )
	this.password = function ( vars )
	{
		this._start_field ( vars );
		this._add_field ( 'password', vars );
	};
	// }}}
	// {{{ checkbox ( vars ) 
	this.checkbox	= function ( vars )
	{
		this._start_field ( vars );
		this._add_field ( 'checkbox', vars );
	};
	// }}}
	// {{{ hidden ( vars, val )
	this.hidden = function ( vars, val )
	{
		/*
		NOTA: il metodo f.hidden() puo' essere usato in due modi:

			1 - La sintassi standard a "dizionario", cosi': f.hidden ( { name: 'nome_campo', value: 'valore' } );
			2 - Passando due parametri al posto del dizionario: f.hidden ( "module", "shop" );
			    che corrispondono a "name" e "value". 

			Nel secondo caso, il campo "id" viene valorizzato di default.
		*/
		if ( typeof ( val ) != 'undefined' )
		{
			var id = this.name + "@" + vars;

			this._fields.push ( vars );

			this.html += '<input type="hidden" name="' + vars + '" id="' + id + '" value="' + val + '" ';
			this.html += ' />';
		} else {
			this._fields.push ( vars [ 'name' ] );

			this.html += '<input type="hidden" name="' + vars [ 'name' ] + '" value="' + vars [ 'value' ] + '" ';
			if ( vars [ 'id' ] ) this.html += ' id="' + vars [ 'id' ] + '" ';
			if ( vars [ 'onchange' ] ) this.html += ' onchange="' + vars [ 'onchange' ] + '" ';
			this.html += ' />';
		}
	};
	// }}}
	// {{{ select ( vars ) 
	this.select = function ( vars )
	{
		this._start_field ( vars );

		var s = '<select ';
		var k;
		var skips = "{label}{mandatory}{hint}{options}{force_select}{nonl}{os3_full}{defval}";  
		var sel_val = -1;

		vars [ 'id' ] = this.name + "@" + this._get_name ( vars );
		if ( ( vars [ 'defval' ] != undefined ) && ( vars [ 'value' ] == undefined ) ) vars [ 'value' ] = vars [ 'defval' ];
		
		for ( k in vars )
		{
			if ( typeof vars [ k ] == 'function' ) continue;	// Skip functions
			if ( skips.indexOf ( '{' + k.toLowerCase () + '}' ) != -1 ) continue;
			s += k + '="' + vars [ k ] + '" ';
		}

		s += '>';

		this.html += s;

		if ( vars [ 'value' ] != undefined ) sel_val = vars [ 'value' ];

		s = '';
		if ( vars [ 'force_select' ] ) s += '<option value="">' + _ ( "os3_form", "(Selezionare)" ) + '</option>';

		if ( vars [ 'options' ] )
		{
			var opts = Array.fromObject ( vars [ 'options' ] );	
			var l = opts.length;
			var t, opt;

			for ( t = 0; t < l; t ++ )
			{
				opt = opts [ t ];
				s += '<option value="' + opt [ 'value' ] + '" ';
				if ( opt [ 'class' ] ) s += ' class="' + opt [ 'class' ] + '" ';
				if ( ( opt [ 'selected' ] || ( sel_val == opt [ 'value' ] ) ) ) s += ' selected="selected" ';
				s += '>' + opt [ 'label' ] + '</option>';
			}
		}
		
		s += '</select>';
		this.html += s;

		if ( vars [ 'mandatory' ] )	this._mandatory.push ( vars [ 'id' ] );

		this._create_hint ( vars );
		this._newline ( vars );
	};
	// }}}
	// {{{ select_set_values ( field_name, values )
	this.select_set_values = function ( field_name, values )
	{
		var t, l = values.length, e;
		var select = this.get_element ( field_name );

		for ( t = 0; t < l; t ++ )
			select.options [ 0 ] = null;

		for ( t = 0; t < l; t ++ )
		{
			e = new Option ( values [ t ] [ 'label' ], values [ t ] [ 'value' ], false, false );
			select.options [ select.options.length ] = e;
		}
	};
	// }}}
	// {{{ sep ( vars )
	this.sep = function ( vars )
	{
		this._newline ( {} );

		if ( ! this.no_table ) this.html += '<tr><td colspan="200">';
		this.html += '<hr />';
		this._newline ( {} );
	};
	// }}}
	// {{{ calendar ( vars ) 
	this.calendar_old = function ( vars )
	{
		var split = {};

		if ( typeof OS3Calendar == 'undefined' ) 
		{
			alert ( _ ( "os3_form", "You *MUST* include OS3Calendar" ) );
			return null;
		}

		this._start_field ( vars );
		vars [ 'id' ] = this.name + "@" + this._get_name ( vars );

		if ( ( vars [ 'defval' ] != undefined ) && ( vars [ 'value' ] == undefined ) ) vars [ 'value' ] = vars [ 'defval' ];

		if ( ! vars [ 'value' ] ) 
		{
			var Today = new Date ();
			vars [ 'value' ] = Today.getFullYear () + "-" + ( Today.getMonth() +1 ) + "-" + Today.getDate ();
		}

		if ( vars [ 'value' ] ) split = vars [ 'value' ].match ( new RegExp ( "([0-9]{4}).([0-9]{1,2}).([0-9]{1,2})" ) );

		if ( ! vars [ 'onchange' ] )
			this.hidden ( vars [ 'name' ], vars [ 'value' ] );
		else
			this.hidden ( vars );
		

		if ( ! vars [ 'onchange' ] )
			vars [ 'onchange' ] = "liwe.form.cbacks.calendar_change ( this )";
		else
			vars [ 'onchange' ] = "liwe.form.cbacks.calendar_change ( this );" + vars [ 'onchange' ];

		this.html += '<table border="0" cellpadding="0" cellspacing="0"><tr><td>';

		var v = Array ();

		v [ 'filter' ] = "0123456789";
		v [ 'class'  ] = "number";
		v [ 'os3_def_validator' ] = liwe.validators.is_integer;
		v [ 'nonl' ] = 1;
		if ( ! vars [ 'onchange' ] )
			v [ 'onchange' ] = "liwe.form.cbacks.calendar_change ( this )";
		else
			v [ 'onchange' ] = "liwe.form.cbacks.calendar_change ( this );" + vars [ 'onchange' ];

		v [ 'name' ] = vars [ 'name' ] + "@cal_year";
		v [ 'id'   ] = vars [ 'name' ] + "@cal_year";
		v [ 'size' ] = 4;
		v [ 'maxlength' ] = 4;
		v [ 'title' ] = "Year";
		v [ 'value' ] = split [ 1 ];
		
		this._add_field ( 'text', v  );
		this.html += '</td><td>';

		v [ 'name' ] = vars [ 'name' ] + "@cal_month";
		v [ 'id'   ] = vars [ 'name' ] + "@cal_month";
		v [ 'size' ] = 2;
		v [ 'maxlength' ] = 2;
		v [ 'title' ] = "Month";
		v [ 'value' ] = split [ 2 ];


		this._add_field ( 'text', v  );
		this.html += '</td><td>';

		v [ 'name' ] = vars [ 'name' ] + "@cal_day";
		v [ 'id'   ] = vars [ 'name' ] + "@cal_day";
		v [ 'title' ] = "Day";
		v [ 'value' ] = split [ 3 ];
		this._add_field ( 'text', v  );

		this.html += '</td><td><a href="javascript:os3cal_show(\'' + vars [ 'id' ] + '\',this)"><img src="' + liwe._libbase + '/gfx/icons/calendar.png" value="Calendar" border="0" /></a>';

		this._create_hint ( vars );

		this.html += '</td><td><div id="' + vars [ 'id' ] + '"></div>';
		this.html += '</td></tr></table>';

		this._newline ( vars );

		var cal = new OS3Calendar (); 
		cal.id = vars [ 'id' ];
		cal.show_days_labels = true; 
		cal.is_popup = true; 
		cal.cb_date_changed = liwe.form.cbacks.calendar;
		os3cal_register ( vars [ 'id' ], cal);

		return cal;
	};
	// }}}
	// {{{ textarea ( vars ) 
	this.textarea = function ( vars )
	{
		this._start_field ( vars );

		var s = '<textarea ';
		var k;
		var skips = "{label}{mandatory}{akey}{hint}{text}{nonl}{os3_full}{defval}{value}{show_entity}{code}";  

		vars [ 'id' ] = this.name + "@" + this._get_name ( vars );
		if ( ( vars [ 'value' ] ) && ( ! vars [ 'text' ] ) ) vars [ 'text' ] = vars [ 'value' ];
		if ( ( vars [ 'defval' ] != undefined ) && ( vars [ 'text' ] == undefined ) ) vars [ 'text' ] = vars [ 'defval' ];
		
		for ( k in vars )
		{
			if ( skips.indexOf ( '{' + k.toLowerCase () + '}' ) != -1 ) continue;
			if ( typeof vars [ k ] == 'function' ) continue;	// Skip functions
			s += k + '="' + vars [ k ] + '" ';
		}

		if ( vars [ 'akey' ] ) s += ' accesskey="' + vars [ 'akey' ] + '" ';
		if ( vars [ 'code' ] ) s += ' style="font-family: Courier, monospace; font-size: 80%;" ';

		s += '>';

		if ( vars [ 'text' ] ) 
		{
			if ( vars [ 'show_entity' ] )
				s += vars [ 'text' ].replace ( new RegExp ( "&([a-zA-Z][a-z]+);", "g" ), "&amp;$1;" ).replace ( new RegExp ( "<br[^>]*>", "g" ), "\n" );
			else
				s += vars [ 'text' ];
		}

		s += '</textarea>';

		this.html += s;

		if ( vars [ 'mandatory' ] ) this._mandatory.push ( vars [ 'id' ] );

		this._create_hint ( vars );
		this._newline ( vars );
	};
	// }}}
	// {{{ grid ( vars )
	this.grid = function ( vars )
	{
		if ( typeof OS3Grid == 'undefined' ) 
		{
			alert ( _ ( "os3_form", "You *MUST* include OS3Grid" ) );		
			return null;
		}

		var name = vars [ 'name' ];

		this._start_field ( vars );

		vars [ 'id' ] = this.name + "@" + this._get_name ( vars );

		this.html += '<div id="' + vars [ 'id' ] + '"></div>';

		this._newline ( vars );

		var g = new OS3Grid ( vars [ 'name' ] );

		g.width = "100%";
		g.height = "100%";
		g.highlight = true;
		g.div_id    = vars [ 'id' ];

		if ( vars [ 'headers' ] ) g.set_headers ( vars [ 'headers' ] );

		return g;
	};
	// }}}
	// {{{ button ( vars )
	this.button = function ( vars )
	{
		this._start_field ( vars );

		vars [ 'id' ] = this.name + "@" + this._get_name ( vars );
		this._add_button ( vars );

		this._newline ( vars );
	};
	// }}}
	// {{{ descr ( vars ) 
	this.descr = function ( vars )
	{
		if ( typeof ( vars ) == "string" ) vars = { "text" : vars };
		this._newline ( {} );
		if ( ! this.no_table ) this.html += '<tr><td colspan="200" class="text_descr">';
		this.html += vars [ 'text' ];
		this._newline ( {} );
	};
	// }}}
	// {{{ error_msg ( txt )
	this.error_msg	= function ( txt )
	{
		var e = $( this.name + "_err_box" );

		if ( ! e ) console.error ( "No error box for form: " + this.name );

		if ( txt )
		{
			e.innerHTML = txt;
			e.style.visibility = 'visible';
		} else
			e.style.visibility = 'hidden';
	};
	// }}}
	// {{{ get ()
	this.get = function ()
	{
		var start = '';
		var end   = '';
		var cback = '';
		var action = this._resolve_action ();

		if ( ! this.name ) this.name = "no_name";

		start += '<form method="post" name="' + this.name + '" id="' + this.name + '" ' + 
			 ' action="' + action + '" ' + 
			 ' onsubmit="return liwe.form._aim.submit(this,{onStart: liwe.form._event_on_start, onComplete: liwe.form._event_on_complete})">';

		if ( this.no_table  )
		{
			start += '<div class="frm" id="' + this.name + '_frm" >';
			end   += '</div>';
		} else {
			start += '<table border="0" class="frm" id="' + this.name + '_frm" cellpadding="0" cellspacing="0" >';
			end   += '</table>';
		}

		return start + this.html + end + '</form>';
	};
	// }}}
	// {{{ set ( id_dest )
	this.set = function ( id_dest )
	{
		document.getElementById ( id_dest ).innerHTML = this.get ();
		this._render_all ();

		if ( this._first_field )
			this.set_focus ( this._first_field );
	};
	// }}}
	// {{{ _resolve_action ()
	this._resolve_action = function ()
	{
		if ( this.action ) return this.action;
		if ( this.ajax_mode ) return this.ajax_mode;
		if ( liwe.AJAX && liwe.AJAX.url ) return liwe.AJAX.url;

		console.error ( "Form: %s does not have an 'action' value set", this.name );
		return '';
	};
	// }}}

	// NOT DOCUMENTED
	// {{{ add_wiget ( vars ) 
	this.add_widget		= function ( vars )
	{
		this._start_field ( vars );
		this.html += vars [ 'widget' ];

		this._newline ( vars );
	};
	// }}}
	// {{{ flat_calendar ( vars )
	this.flat_calendar = function ( vars )
	{
		var split = {};

		if ( typeof OS3Calendar == 'undefined' ) 
		{
			alert ( _ ( "os3_form", "You *MUST* include OS3Calendar" ) );
			return null;
		}

		this._start_field ( vars );

		vars [ 'id' ] = this.name + "@" + this._get_name ( vars );

		if ( ( vars [ 'defval' ] != undefined ) && ( vars [ 'value' ] == undefined ) ) vars [ 'value' ] = vars [ 'defval' ];

		if ( ! vars [ 'value' ] ) 
		{
			var Today = new Date ();
			vars [ 'value' ] = Today.getFullYear () + "-" + ( Today.getMonth() +1 ) + "-" + Today.getDate ();
		}


		split = vars [ 'value' ].match ( new RegExp ( "([0-9]{4}).([0-9]{1,2}).([0-9]{1,2})" ) );

		this.html += '<div id="' + vars [ 'name' ] + '">CIAO</div>';
		this._create_hint ( vars );
		this._newline ( vars );

		var cal = new OS3Calendar (); 
		cal.id = vars [ 'name' ];
		cal.show_days_labels = true; 
		cal.is_popup = false; 
		cal.cb_date_changed = liwe.form.cbacks.calendar;
		os3cal_register ( vars [ 'name' ], cal);

		return cal;
	};
	// }}}
	// {{{ error_box ( vars ) 
	this.error_box = function ()
	{
		var s;
		var vars = Array ();

		vars [ 'os3_full' ] = 1;

		this._start_field ( vars );

		// vars [ 'id' ] = this.name + "@" + this._get_name ( vars );
		s = 'border: 1px solid red; padding: 4px; background-color: #FFE87F; color: black; visibility: ' + ( vars [ 'show' ] ? 'visible' : 'hidden' );

		// this.html += '<div id="' + vars [ 'id' ] + '" style="' + s + '"></div>';
		this.html += '<div id="' + this.name + '_err_box" style="' + s + '"></div>';
		this._newline ( vars );
	};
	// }}}
	// {{{ htmlarea ( vars ) 
	this.htmlarea = function ( vars )
	{
		console.error ( "form.htmlarea is deprecated. Use form.rte" );
		/*
		if ( typeof HTMLEdManager == 'undefined' ) 
		{
			alert ( _ ( "os3_form", "You *MUST* include HTMLEd" ) );
			return;
		}

		if ( ! vars [ 'value' ] ) vars [ 'value' ] = '';

		this.textarea ( vars );
		this.html += '<input type="hidden" name="' + vars [ 'name' ] + '" id="' + vars [ 'id' ] + ':htmled" />';

		// Register the HTMLEd 
		this._htmled.push ( vars [ 'name' ] );
		this._htmled_names [ vars [ 'name' ] ] = vars;
		*/
	};
	// }}}
	// {{{ workspace ( vars )
	this.workspace		= function ( vars )
	{
		this._newline ( {} );
		if ( ! this.no_table ) this.html += '<tr><td colspan="200">';
		this.html += '<div id="' + vars [ 'name' ] + '"></div>';
		this._newline ( {} );
	};
	// }}}
	// {{{ check ()
	this.check = function () { return liwe.form.check ( this.name ); };
	// }}}
	// {{{ get_value ( widget_name )
	this.get_value	= function ( widget_name )
	{
		if ( this._widgets [ widget_name ] )
		{
			var w = this._widgets [ widget_name ];
			return w.get_value ();
		}

		var e = document.getElementById ( this.name + "@" + widget_name );
		if ( ! e ) return null;

		if ( ( e.type == 'checkbox' ) && ( ! e.checked ) ) return false;
		return e.value;
	};
	// }}}
	// {{{ set_value ( widget_name, value )
	this.set_value	= function ( widget_name, value )
	{
		/*
		if ( this._htmled_names [ widget_name ] )
		{
			var h = HTMLEdManager.get ( this.name + "@" + widget_name );
			if ( h ) 
				h.set_html ( value );
			else
				console.warn ( "Form: could not find HTML Area '%s'", widget_name );

			return;
		} 
		*/

		if ( this._widgets [ widget_name ] )
		{
			this._widgets [ widget_name ].set_value ( value );
			return;
		}

		var e = document.getElementById ( this.name + "@" + widget_name );
		if ( ! e ) 
		{
			console.warn ( "Form: could not find input '%s'", this.name + "@" + widget_name );
			return;
		}

		switch ( e.type )
		{
			case "checkbox":
				e.checked = value;
				break;

			default:
				e.value = value;
		}
	};
	// }}}
	// {{{ set_focus ( widget_name )
	this.set_focus = function ( widget_name )
	{
		if ( this._widgets [ widget_name ] )
		{
			var w = this._widgets [ widget_name ];
			w.set_focus ();
			return;
		}

		var e = document.getElementById ( this.name + "@" + widget_name );
		if ( ! e ) return;

		e.focus ();
		e.select ();
	};
	// }}}
	// {{{ get_element ( widget_name )
	this.get_element = function ( widget_name ) { return document.getElementById ( this.name + "@" + widget_name ); };
	// }}}
	// {{{ get_values ()
	this.get_values	= function ()
	{
		var res = {};
		var _obj = this;

		this._fields.iterate ( function ( v )
			{
				res [ v ] = _obj.get_value ( v );
			} );
		// var a = Array.fromForm ( this.name + "_frm" );
		return res;
	};
	// }}}
	// {{{ clear ()
	this.clear = function ()
	{
		function _clear_input ( lst )
		{
			var l = lst.length;
			var t;

			for ( t = 0; t < l; t ++ ) lst [ t ].value = null;
		}

		var frm = $( this.name + "_frm" );

		_clear_input ( frm.getElementsByTagName ( 'input' ) );
		_clear_input ( frm.getElementsByTagName ( 'select' ) );
		_clear_input ( frm.getElementsByTagName ( 'textarea' ) );
	};
	// }}}

	// UNSTABLE
	// {{{ fetch_htmled_contents ()
	// FIXME: THIS FUNCTION DOES NOT WORK ANYMORE!
	this.fetch_htmled_contents = function ()
	{
		console.error ( "form.fetch_htmled_contents() has been REMOVED" );
		/*
		var i = this._htmled.length;
		var t, n;

		if ( ! i ) return;

		for ( t = 0; t < i; t ++ )
		{
			// Field name
			n = this._htmled [ t ];

			document.getElementById ( this.name + "@" + n + ":htmled" ).value = htmled_getText ( this.name + "@" + n );
		}
		*/
	};
	// }}}
	// {{{ _render_all ()
	this._render_all = function ()
	{
		this._widgets.iterate ( function ( v, k )
		{
			if ( v [ "render" ] )
				v.render ();

			if ( v [ "_refresh" ] )
				v._refresh ();
		} );

		/*
		// I have to use lists and not dicts because
		// the iterate function skips functions
		var t, l = this._suggests.length;
		for ( t = 0; t < l; t ++ )
			this._suggests [ t ] [ 1 ] ( this._suggests [ t ] [ 0 ] );
		*/
	};
	// }}}
	// {{{ ipaddr ( vars )
	this.ipaddr = function ( vars )
	{
		var split = {};

		this._start_field ( vars );
		// vars [ 'id' ] = this.name + "@" + this._get_name ( vars );

		if ( ( vars [ 'defval' ] != undefined ) && ( vars [ 'value' ] == undefined ) ) vars [ 'value' ] = vars [ 'defval' ];

		if ( vars [ 'value' ] ) split = vars [ 'value' ].match ( new RegExp ( "([1-9][0-9]{0,2})\.([1-9][0-9]{0,2})\.([1-9][0-9]{0,2})\.([1-9][0-9]{0,2})" ) );

		if ( ! vars [ 'onchange' ] )
			vars [ 'onchange' ] = "liwe.form.cbacks.ip_change ( this )";
		else
			vars [ 'onchange' ] = "liwe.form.cbacks.ip_change ( this );" + vars [ 'onchange' ];

		this.html += '<table border="0" cellpadding="0" cellspacing="0"><tr><td>';

		var v = Array ();

		v [ 'filter' ] = "0123456789";
		v [ 'class'  ] = "number";
		v [ 'os3_def_validator' ] = liwe.validators.is_integer;
		v [ 'nonl' ] = 1;
		v [ 'onchange' ] = vars [ 'onchange' ];

		v [ 'name' ] = vars [ 'name' ] + "@ip1";
		v [ 'id'   ] = vars [ 'name' ] + "@ip1";
		v [ 'size' ] = 3;
		v [ 'maxlength' ] = 3;
		v [ 'title' ] = "IP 1";
		v [ 'value' ] = split [ 1 ];
		
		this._add_field ( 'text', v  );
		this.html += '.</td><td>';

		v [ 'name' ] = vars [ 'name' ] + "@ip2";
		v [ 'id'   ] = vars [ 'name' ] + "@ip2";
		v [ 'size' ] = 3;
		v [ 'maxlength' ] = 3;
		v [ 'title' ] = "IP 2";
		v [ 'value' ] = split [ 2 ];


		this._add_field ( 'text', v  );
		this.html += '.</td><td>';

		v [ 'name' ] = vars [ 'name' ] + "@ip3";
		v [ 'id'   ] = vars [ 'name' ] + "@ip3";
		v [ 'title' ] = "IP 3";
		v [ 'value' ] = split [ 3 ];

		this._add_field ( 'text', v  );
		this.html += '.</td><td>';

		v [ 'name' ] = vars [ 'name' ] + "@ip4";
		v [ 'id'   ] = vars [ 'name' ] + "@ip4";
		v [ 'title' ] = "IP 4";
		v [ 'value' ] = split [ 4 ];
		this._add_field ( 'text', v  );
		this.html += '</td>';

		this._create_hint ( vars );

		if ( vars [ 'id' ] ) this.html += '</td><td><div id="' + vars [ 'id' ] + '"></div>';
		this.html += '</td></tr></table>';

		this._newline ( vars );
	};
	// }}}
	// {{{ cod_fisc ( vars )
	this.cod_fisc = function ( vars )
	{
		vars [ 'size' ] = 16;
		vars [ 'maxlength' ] = 16;
		vars [ 'filter' ] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
		vars [ 'os3_def_validator' ] = liwe.validators.is_codice_fiscale;

		this._start_field ( vars );
		this._add_field ( 'text', vars );
	};
	// }}}
	// {{{ p_iva ( vars )
	this.p_iva = function ( vars )
	{
		vars [ 'size' ] = 11;
		vars [ 'maxlength' ] = 11;
		vars [ 'filter' ] = "0123456789";
		vars [ 'os3_def_validator' ] = liwe.validators.is_partita_iva;

		this._start_field ( vars );
		this._add_field ( 'text', vars );
	};
	// }}}
	// {{{ radio ( vars )
	this.radio = function ( vars )
	{
		this._start_field ( vars );
		this._add_field ( 'radio', vars );
	};
	// }}}
	
	// this.sel		= form_sel;

	// ===========================================================
	// INTERNAL METHODS
	// ===========================================================
	// {{{ _start_field ( vars )
	this._start_field 	= function ( vars, kind )
	{
		var sty = '';
		var descr = vars.get ( "os3_class_descr", "descr" );
		var value = vars.get ( "os3_class_value", "value" );

		this._fields.push ( vars [ 'name' ] );

		if ( vars [ 'style' ] ) sty = 'style="' + vars [ 'style' ] + '"';	

		if ( ( ! this.no_table ) && ( ! this._row_open ) ) 
		{
			if ( vars [ 'os3_full' ] )
			{
				this._row_open = true;
				this.html += '<tr><td colspan="100" align="center" width="100%" class="' + descr + '" ' + sty + '>';
				return;
			} else
				this.html += '<tr><td class="' + descr +'" ' + sty + '>';
		}

		this._row_open = true;

		if ( vars [ 'label' ] ) 
		{
			if ( vars [ 'akey' ] )
			{
				var re = new RegExp ( "(" + vars [ 'akey' ] + ")", "i" );
				if ( ! re.test ( vars [ 'label' ] ) ) vars [ 'label' ] += " (" + vars [ 'akey' ] + ")";

				this.html  += '<label>' + vars [ 'label' ].replace ( re, '<span class="akey">$1</span>' ) + ": </label>";
			} else 
				this.html  += '<label>' + vars [ 'label' ] + ": </label>";
		}

		if ( ! this.no_table ) this.html += '</td><td class="' + value + '">';
	};
	// }}}
	// {{{ _add_field ( kind, vars )
	this._add_field	= function ( kind, vars )
	{
		var s = '<input type="' + kind + '" ';
		var k;
		var skips = "{label}{hint}{filter}{akey}{mandatory}{validator}{os3_def_validator}{nonl}{os3_full}{defval}{checked}{id}{suggest}";

		vars [ 'id' ] = this.name + "@" + this._get_name ( vars );

		if ( ( vars [ 'defval' ] != undefined ) && ( vars [ 'value' ] == undefined ) ) vars [ 'value' ] = vars [ 'defval' ];

		/*
		if ( vars [ 'suggest' ] ) 
		{
			if ( typeof AutoSuggest != "undefined" )
				this._suggests.push ( [ vars [ 'id' ], vars [ 'suggest' ] ] );
			else
				console.warn ( "AutoSuggest requested for %s but not included", vars [ 'id' ] );
		}
		*/

		if ( vars [ 'value' ] ) 
		{
			vars [ 'value' ] = ( vars [ 'value' ] + '' ).replace ( /"/g, '&quot;' );
			// console.debug ( "VALUE: %s", vars [ 'value' ] );
		}
		
		for ( k in vars )
		{
			if ( typeof vars [ k ] == 'function' ) continue;	// Skip functions
			if ( skips.indexOf ( '{' + k.toLowerCase () + '}' ) != -1 ) continue;
			if ( vars [ k ] == undefined ) continue;
			s += k + '="' + vars [ k ] + '" ';
		}

		if ( vars [ 'filter' ] ) s += ' onkeypress="return filter_valid_chars ( event, \'' + vars [ 'filter' ] + '\', false )" ';

		if ( ( kind == 'checkbox' ) && vars [ 'checked' ] ) s += ' checked="checked" ';
		if ( vars [ 'akey' ] )   	s += ' accesskey="' + vars [ 'akey' ] + '" ';
		if ( vars [ 'mandatory' ] )	this._mandatory.push ( vars [ 'id' ] );
		if ( vars [ 'validator' ] )
		{

			if ( vars [ 'validator' ] == true )
			{
				if ( vars [ 'os3_def_validator' ] ) this._validates.push ( [ vars [ 'id' ], vars [ 'os3_def_validator' ] ] );
			} else {
				this._validates.push ( [ vars [ 'id' ], vars [ 'validator' ] ] );
			}
		}

		s += ' id="' + vars [ 'id' ] + '"';

		s += ' />';

		this.html += s;
		this._create_hint ( vars );

		if ( ( ! this._first_field ) && ( vars [ 'name' ] ) ) 
			this._first_field = this._get_name ( vars );

		this._newline ( vars );
	};
	// }}}
	// {{{ _add_button ( vars )
	this._add_button = function ( vars )
	{
		var s = '<button ';
		var k;
		var skips = "{hint}{akey}{nonl}{os3_full}";  

		vars [ 'id' ] = this.name + "@" + this._get_name ( vars );
		
		for ( k in vars )
		{
			if ( typeof vars [ k ] == 'function' ) continue;	// Skip functions
			if ( skips.indexOf ( '{' + k.toLowerCase () + '}' ) != -1 ) continue;
			s += k + '="' + vars [ k ] + '" ';
		}

		if ( vars [ 'akey' ] ) s += ' accesskey="' + vars [ 'akey' ] + '" ';

		s += ' type="button">';
		s += vars [ 'value' ];
		s += '</button>';

		this.html += s;
		this._create_hint ( vars );

		this._newline ( vars );
	};
	// }}}
	// {{{ _newline ( vars ) 
	this._newline = function ( vars )
	{
		if ( ( vars [ 'nonl' ] ) || ( ! this._row_open ) ) 
		{
			this.html += '</td><td valign="top" nowrap="nowrap" class="descr">';
			return;
		}

		this._row_open = false;

		if ( this.no_table ) 
			this.html += '<br />';
		else
			this.html += '</td></tr>';
	};
	// }}}
	// {{{ _create_hint ( vars ) 
	this._create_hint = function ( vars )
	{
		var s;
		var l;

		if ( vars [ 'hint' ] )
		{
			l = _ ( "os3_form", "hint" );
			s = '<img alt="' + l + '" src="' + liwe._libbase + '/gfx/bubble/hint.png" onclick="bubble_show ( this.previousSibling, \'' + vars [ 'hint' ] + '\', true )" />';
			this.html += s;

			if ( typeof bubble_show == "undefined" ) liwe.utils.append_js ( liwe._libbase + "/bubble.js" );
		}

		if ( vars [ 'mandatory' ] )
		{
			l = _ ( "os3_form", "Mandatory field" );
			s = '&nbsp;<img alt="' + l + '" title="' + l + '" src="' + liwe._libbase + '/gfx/form/mandatory.gif" id="' + vars [ 'id' ] + '_mandatory" />';
			this.html += s;
		}

		if ( vars [ 'validator' ] )
		{
			l = _ ( "os3_form", "Field needs Validation" );

			s = '&nbsp;<img alt="' + l + '" title="' + l + '" src="' + liwe._libbase + '/gfx/form/okb.gif" id="' + vars [ 'id' ] + '_valid" />';
			this.html += s;
		}

		if ( vars [ 'id' ] ) this.html += '&nbsp;<span id="' + vars [ 'id' ] + "@hint" + '">&nbsp;</span>';
	};
	// }}}
	// {{{ _get_name ( vars )
	this._get_name = function ( vars )
	{
		this._widget_count ++;
		if ( ! vars [ 'name' ] ) vars [ 'name' ] = "W" + this._widget_count;
		return vars [ 'name' ];
	};
	// }}}

	// ===========================================================
	// INTERNAL ATTRS
	// ===========================================================
	this._row_open		= false;  // Flag T/F to tell if the row has been started

	this._widget_count	= 0;	  // Internal Widget Counter (used to create unique IDs)

	// this._htmled = Array ();
	// this._htmled_names = {};

	// this._htmled_manager = null;

	this._mandatory = Array ();
	this._validates = Array ();
	this._fields = [];
	this._widgets = {};
	// this._suggests = [];

	liwe.form._instances  [ name ] = this;
};
// {{{ _aim 
liwe.form._aim = {
	frame : function ( f, c, frm ) 
	{
		var i = document.getElementById ( '__ajax_form' );

		if ( ! i )
		{
			var d = document.createElement ( 'DIV' );
			d.innerHTML = '<iframe style="display:none" src="about:blank" id="__ajax_form" name="__ajax_form" onload="liwe.form._aim.loaded()"></iframe>';
			document.body.appendChild(d);
 
			i = document.getElementById ( '__ajax_form' );
		}

		if ( c && typeof ( c.onComplete ) == 'function') i.onComplete = c.onComplete;
		i._form = frm;
	},
 
	form : function ( f ) {
		f.setAttribute ( 'target', '__ajax_form' );
		f.setAttribute ( 'enctype', 'multipart/form-data' );
	},
 
	submit : function ( f, c ) 
	{
		var frm = liwe.form.get ( f.getAttribute ( "name" ) );

		if ( ! frm.check () ) return false;

		liwe.form._aim.form ( f, liwe.form._aim.frame ( f, c, frm ) );

		if ( c && typeof ( c.onStart ) == 'function' ) 
			return c.onStart ( frm );

		return true;
	},
 
	loaded : function () 
	{
		var i = document.getElementById( '__ajax_form' );
		var d;

		if ( i.contentDocument ) 
		{
			d = i.contentDocument;
		} else if (i.contentWindow) {
			d = i.contentWindow.document;
		} else {
			d = window.frames [ '__ajax_form' ].document;
		}

		if ( d.location.href == "about:blank" ) return;
 
		if ( typeof ( i.onComplete ) == 'function' ) i.onComplete ( i._form, d.body.innerHTML );
	}
 
};
// }}}
// {{{ _event_on_start ( frm )
liwe.form._event_on_start = function ( frm )
{
	if ( frm.events [ 'start' ] ) 
		if ( ! frm.events [ 'start' ] ( frm ) ) return false;

	return true;
};
// }}}
// {{{ _event_on_complete ( frm, vals )
liwe.form._event_on_complete = function ( frm, vals )
{
	if ( frm.easy )
	{
		AJAXManager.handle_easy ( vals, frm.events [ 'complete' ] );
	} else {
		if ( frm.events [ 'complete' ] ) frm.events [ 'complete' ] ( vals );
	}
}; 
// }}}
