/**
 * author: Harris http://www.minube.com
 *
 * class: AutoComplete for Minube
 *
 * version: 1.0.0 

 * REFERENCES AND THANKS 
 * this class is based on the work in AutoSuggest.js of
 * Timothy Groves - http://www.brandspankingnew.net
 * and Antonio Ramirez http://webeaters.blogspot.com
 * and redesigned to work in minube.com systems
 *
 */

var AutoCompleteDB = Class.create();


AutoCompleteDB.prototype = {
  Version: '1.0.0',
  REQUIRED_PROTOTYPE: '1.4.0',
 
  initialize: function (id, param)
  {
  	// check whether we have the appropiate javascript libraries
  	this.PROTOTYPE_CHECK();

  	// get field
	this.fld = $(id);
	
	// Overlay Info
	this.iframe = id + "_iframe";
	this.needOverlay = this.checkOverlaySupport();

	if (!this.fld) {
		throw("AutoComplete requires a field id to initialize");
	}
	
	// init variables
	this.sInp 	= ""; 		// input value 
	this.nInpC 	= 0;		// input value length
	this.aSug 	= []; 		// suggestions array with Tmp Data
	this.actualRoot = "";	// el valor de los caracteres en minchar;
	this.iHigh 	= 0;		// level of list selection 
	this.keyTimer = "";
	this.cache = new Array();	
	this.isMouseBlur = false;
	this.selected = true;
	this.noResultsFound = false;
	
	// parameters object
	this.options = param ? param : {};
	
	// defaults	
	var k, def = {preloadedOptions:new Array(), valueSep:';', minchars:1, meth:"get", varname:"input", className:"autocomplete", timeout:3000, delay:500, offsety:-5, shownoresults: true, noresults: "-", maxheight: 250, maxentries: 25, onAjaxError:null, setWidth: false, minWidth: 100, maxWidth: 200, useNotifier: true, textToDelete: ""};
	
	// vamos a utilizar minchar como valor para establecer la busqueda a bbdd
	// SOLO SE BUSCARA CUANDO EL INPUT DE ENTRADA TENGA minchars CARACTERES y A PARTIR DE AHI SE CACHEARA
	// CUANDO VUELVA A TENER minchars SE VUELVE A BUSCAR 
	
	for (k in def) 
	{
		if (typeof(this.options[k]) != typeof(def[k]))
			this.options[k] = def[k];
	}
	
	//si tenemos preloadedOptions formateamos las llaves para poder realizar búsquedas
	var formattedPreloadedOptions = new Array();
	this.options.preloadedOptions.each
	(
		function(entry)
		{
			var key = this.cleanString(entry);
			formattedPreloadedOptions.push({formatted:key,value:entry});
		}.bind(this)
	);
	this.options.preloadedOptions = formattedPreloadedOptions;
	
	if (this.options.useNotifier) this.fld.addClassName('ac_field');
	
	// set keyup handler for field
	// and prevent AutoComplete from client
	// NOTE: not using addEventListener because UpArrow fired twice in Safari
	this.initializeEvents(this);
	this.fld.setAttribute("AutoComplete","off");
	if (this.fld.value != "")
		this.fld.setStyle({backgroundImage:'none'});

  },
  
  initializeEvents: function(objetoActual) {
	this.fld.onkeypress 	= function(ev){ return objetoActual.onKeyPress(ev); };
	this.fld.onkeyup 		= function(ev){ return objetoActual.onKeyUp(ev); };
	this.fld.onblur			= function(ev){ return objetoActual.onBlur(ev); };
	this.fld.onclick		= function(ev){ objetoActual.fld.focus(); };	
	this.fld.onfocus		= function(ev){
		if (objetoActual.options.textToDelete != "" && objetoActual.fld.value==objetoActual.options.textToDelete) {
			objetoActual.fld.value = "";
		}
		objetoActual.selected=true; objetoActual.fld.select(); }; 
  },
  
  convertVersionString: function (versionString){
      var r = versionString.split('.');
      return parseInt(r[0])*100000 + parseInt(r[1])*1000 + parseInt(r[2]);
  },
  
  PROTOTYPE_CHECK: function() { 
    if((typeof Prototype=='undefined') || 
       (typeof Element == 'undefined') || 
       (typeof Element.Methods=='undefined') ||
       (this.convertVersionString(Prototype.Version) < 
        this.convertVersionString(this.REQUIRED_PROTOTYPE)))
       throw("AutoComplete requires the Prototype JavaScript framework >= " +
        this.REQUIRED_PROTOTYPE);
  },
  
  onBlur: function (e) {
  	if (!this.isMouseBlur && !this.noResultsFound ) {
		this.iHigh = 1;
		this.setHighlight(1);	  		
		this.setHighlightedValue();
		this.selected = false;		
	}
  },
  
  onKeyPress: function (e) {
  	if (!e) e = window.event;
  	var key = e.keyCode || e.which;
  	
	// set responses to keypress events in the field
	// this allows the user to use the arrow keys to scroll through the results
	// ESCAPE clears the list
	// RETURN sets the current highlighted value
	// TAB sets the first item in list

	switch(key)
	{
		case Event.KEY_RETURN:
			this.setHighlightedValue();
			Event.stop(e);
			break;
		case Event.KEY_TAB: // Si tabuleamos se elige el primero;		
			this.fld.blur();		
			Event.stop(e);
			break;
		case Event.KEY_ESC:
			this.clearSuggestions();
			break;
	}
	return true;
  },
  
  onKeyUp: function (e) {
 	if (!e) e = window.event;
  	var key = e.keyCode || e.which;
  	
  	if (key == Event.KEY_UP || key == Event.KEY_DOWN) {
  		this.changeHighlight(key);
  		Event.stop(e);
  	} else {
		this.getSuggestions(this.fld.value,this);
	}
	return true;
  },
  
  cleanString: function(val)
  {
	//Formateando entrada para comparar
	var formattedVal = val.toLowerCase();
	formattedVal = formattedVal.gsub(/[áàäâ]/,'a');
	formattedVal = formattedVal.gsub(/[éèëê]/,'e');
	formattedVal = formattedVal.gsub(/[íìïî]/,'i');
	formattedVal = formattedVal.gsub(/[óòöô]/,'o');
	formattedVal = formattedVal.gsub(/[úùüû]/,'u');
	formattedVal = formattedVal.gsub(/[ñÑ]/,'n');		
	formattedVal = formattedVal.gsub(/[çÇ]/,'c');		
	return formattedVal;
  },
  
  getSuggestions: function(val,objetoActual) {
  	this.noResultsFound = false;
  	
  	// input the same? -> do nothing
  	if(val==this.sInp) return false;
  	
  	// kill the old list
  	if($(this.acID)) $(this.acID).remove();
  	
  	this.sInp = val;
  	
  	if (val.length < this.options.minchars)
  	{
	  	// input length is less than the min required to trigger a request -> do nothing  		
  		this.aSug 	= [];
		this.aFullSug = [];
  		this.nInpC	= val.length; 
  		this.actualRoot = "";  		
  	}
  	else
  	{
		//Buscamos en la lista precargada, si existe
		//LLamarla autocomplete_entries o similar, declarandola como un array en un js externo
		//Si existe, buscar en ella
		//De todos modos, disparar la Ajax Request para que llegue con la lista mas completa despues de un rato
		//Las opciones se almacenan en "this.aSug"
		//Y la lista se crea en "this.createList(this.aSug,this);"
		
		//Devuelve JSON de la forma:
		//{"results": [{"id": "1428", "value": "Barcelonnette, Francia - Barcelonnette (BAE)"}]}
		
		var coincidences = new Array();
		objetoActual.options.preloadedOptions.each
		(
			function(entry,index)
			{
				var formattedVal = this.cleanString(val);
				if(entry.formatted.indexOf(formattedVal)>=0)
				{	coincidences.push({id:index,value:entry.value});	}
			}.bind(objetoActual)
		);
		if(coincidences.length > 0)
		{
			//Usar setSuggestions en lugar de createList directamente
			objetoActual.setSuggestions(coincidences,val);
		}
		
		this.nInpC	= val.length;   		
  		clearTimeout(this.keyTimer); 
  		this.keyTimer = setTimeout( 
			  function () {
			  	if ( objetoActual.fld.value.length == objetoActual.nInpC ) {
			  		cached = objetoActual.findInCache(val);
			  		if (cached)
			  		  	objetoActual.createList(cached,objetoActual);
			  		else
			  		  	objetoActual.doAjaxRequest(val,objetoActual);			  		
				}
		} , this.options.delay);
	}
	
  	return false;
  },
  
  
    
  doAjaxRequest: function(input,objetoActual)  {  	 	
  	// create ajax request
  	// do we need to call a function to recreate the url?
  	var cleanInput = this.cleanString(input);
  	if (typeof this.options.script == 'function')
  		var url = this.options.script(encodeURIComponent(cleanInput));
  	else
	  	var url = this.options.script+this.options.varname+'='+cleanInput;
  		//var url = this.options.script+this.options.varname+'='+encodeURIComponent(cleanInput);
  	
  	if(!url) return false;
  	  	
  	var m = this.options.meth;  // get or post?
  	if (this.options.useNotifier){this.fld.setStyle({backgroundImage:'url(/autocomplete/images/autocomplete_spinner.gif)'});} 
  	
  	var options = {
  		method: m,
  		onSuccess: function (req) 
  		{
  				if (objetoActual.options.useNotifier){objetoActual.fld.setStyle({backgroundImage:'none'});} 
				// response in json format?
				suggestions = objetoActual.processSuggestions(req.responseText);
  				objetoActual.setSuggestions(suggestions,input);
  		},
  		onFailure: (typeof objetoActual.options.onAjaxError == 'function')? function (status) {if (objetoActual.options.useNotifier){objetoActual.fld.setStyle({backgroundImage:'url(/autocomplete/images/autocomplete_leftcap.gif)'});} objetoActual.options.onAjaxError(status)} :  function (status) {if (objetoActual.options.useNotifier){objetoActual.fld.setStyle({backgroundImage:'url(/autocomplete/images/autocomplete_leftcap.gif)'});} alert("AJAX error: "+status); }
  	}
  	// make new ajax request
  	new Ajax.Request(url, options);
  },
  
  //Receives JSON code, parses it to an array and returns it
  processSuggestions : function (json)
  {
	 var aSugTmp = [];
	 var jsondata = eval('(' + json + ')');
	
	 for (var i=0; i<jsondata.results.length;i++)
	 {
	  aSugTmp.push({'id':jsondata.results[i].id, 'value':jsondata.results[i].value, 'info':jsondata.results[i].info});
	 }
	 
	 return aSugTmp;
  },
  
  setSuggestions: function (suggestionsArray,input)
  {
  	// compruebo lo necesario para tirar la request a la basura si se nos ha quedado anticuada...  	
	var aSugTmp = suggestionsArray;
	
	this.cacheResults(input, this.aSugTmp);
	if (input != this.fld.value)
		return false;	
		
	this.aSug = aSugTmp;
	aSugTmp = [];
			
	this.acID = 'ac_'+this.fld.id;	
 	this.actualRoot = input; 
 	if (!this.selected) {
		this.iHigh = 1;
		this.setHighlight(1);			
		this.setHighlightedValue(); 				
	} else
	  	this.createList(this.aSug,this); 
  },
  
  cacheResults: function (input, values) {
	this.cache.push({c:input,v:values});
  },
  
  findInCache: function (needle) {
  	for (i=0,len=this.cache.length; i<len; i++) {
		if (this.cache[i].c.toLowerCase() == needle.toLowerCase())
			return this.cache[i].v;
	}
	return false;
  },
  
  clearCacheResults: function() {
	this.cache.clear();	
  },
  
  createDOMElement: function ( type, attr, cont, html )
  {
	var ne = document.createElement( type );	
	if (!ne)
		return 0;
		
	for (var a in attr)
		ne[a] = attr[a];
	
	var t = typeof(cont);
	
	if (t == "string" && !html)
		ne.appendChild( document.createTextNode(cont) );
	else if (t == "string" && html)
		ne.innerHTML = cont;
	else if (t == "object")
		ne.appendChild( cont );
	return ne;
  },
  
  createList:	function(arr, objetoLocal)
  {
	// get rid of the old list if any  
  	if($(this.acID)) $(this.acID).remove();
  	  	
  	// if no results, and shownoresults is false, do nothing
  	if (arr.length == 0 && !this.options.shownoresults) return false;
  	
  	// create holding div
  	var div	= this.createDOMElement('div', {id:this.acID, className:this.options.className});
  	
  	// create div header
  	var hcorner = this.createDOMElement('div', {className: 'ac_corner'});
  	var hbar	= this.createDOMElement('div', {className: 'ac_bar'});
  	var header	= this.createDOMElement('div', {className: 'ac_header'});
  	header.appendChild(hcorner);
  	header.appendChild(hbar);
  	div.appendChild(header);
  	
	// create and populate ul
	var ul	= this.createDOMElement('ul', {id:'ac_ul'});
	// no results?
	if (arr.length == 0 && this.options.shownoresults)
	{		
		var li = this.createDOMElement('li', {className: 'ac_warning'}, this.options.noresults ,true);
		ul.appendChild(li);
		this.noResultsFound = true;		
	}
	else
	{
		// loop through arr of suggestions creating an LI element for each of them
		// render must be limited by this.options.limit if it is lower than arr.length
		var limit = this.options.maxentries < arr.length? this.options.maxentries: arr.length;
		var val;
		for (var i=0; i<limit; i++)
		{
			// format output with the input enclosed in a EM elementFromPoint
			// (as HTML not DOM)
			val 	= arr[i].value+'';
			var st 		= 0; // Capado a que muestre solo los resultados por delante
			st=val.toLowerCase().indexOf(this.sInp.toLowerCase());
			if(st!=-1)				
				var output 	= val.substring(0,st) + '<em>' + val.substring(st,st+this.sInp.length) + '</em>' + val.substring(st+this.sInp.length);
			else
				var output 	= val;
			var span	= this.createDOMElement('span',{},output,true); // type of, properties, output, isHTML?
			
			if(arr[i].info != '') // do we need to add extra info?
			{
				var br	= this.createDOMElement('br',{});
				span.appendChild(br);
				
				var small = this.createDOMElement('small',{}, arr[i].info);
				span.appendChild(small);
			}
			
			var a 	= this.createDOMElement('a',{href:'javascript:void(0);'});
			
			var tl	= this.createDOMElement('span',{className:'tl'},'&nbsp;',true);
			var tr	= this.createDOMElement('span',{className:'tr'},'&nbsp;',true);
			
			a.appendChild(tl);
			a.appendChild(tr);
			a.appendChild(span); // add the object span into the link
			
			a.name = i+1;
			
			a.onclick 		= function (){ objetoLocal.setHighlightedValue(); };
			a.onmouseover	= function () { objetoLocal.isMouseBlur = true; objetoLocal.setHighlight(this.name); }; 
			a.onmouseout	= function () { objetoLocal.isMouseBlur = false; }; 			
			
			var li = this.createDOMElement('li',{}, a); // add the link element to a li element
			
			// finally add the newly created li element to the ul element 
			ul.appendChild(li);

		}
	}
	
	div.appendChild(ul); // add the newly created list to the div element
	
	// create div footer
	var fcorner = this.createDOMElement('div', {className: 'ac_corner'});
  	var fbar	= this.createDOMElement('div', {className: 'ac_bar'});
  	var footer	= this.createDOMElement('div', {className: 'ac_footer'});
  	footer.appendChild(fcorner);
  	footer.appendChild(fbar);
  	div.appendChild(footer);
  	
  	// get position of target textfield
	// position holding div below it
	// set width of holding div to width of field 
	
	var pos		= Position.cumulativeOffset(this.fld); // deprecated in Prototype 1.6, use instead this.fld.cumulativeOffset()
	div.style.left 		= pos[0] - this.fld.offsetLeft + this.options.hOffset + "px";
	div.style.top 		= pos[1] + this.fld.offsetHeight + "px";
		
	var w 		= (this.options.setWidth && this.fld.offsetWidth < this.options.minWidth)? this.options.minWidth : (this.options.setWidth && this.fld.offsetWidth > this.options.maxWidth)? this.options.maxWidth : this.fld.offsetWidth;
	 
	div.style.width 	= w + "px";
		
	// add DIV to document
	document.getElementsByTagName("body")[0].appendChild(div);
	$(this.acID).show();	
	this.handleOverlay(0);	
	this.handleOverlay(1);
	
	// highlight first item, if there are items to highlight
	if( arr.length > 0 )
	{
		this.iHigh = 1;
		this.setHighlight(1);
		if (!this.selected) {
			this.setHighlightedValue();		
		}
	}
  },
  
  changeHighlight:	function(key)
  {
  	var list = $("ac_ul");
	if (!list)
		return false;
	
	var n;

	n = (key == Event.KEY_DOWN || key == Event.KEY_TAB)? this.iHigh + 1 : this.iHigh - 1; // false assumedd to be Event.KEY_UP
	
	n = (n > list.childNodes.length)? list.childNodes.length : ((n < 1)? 1 : n);	
	
	this.setHighlight(n);
  },
  
  setHighlight:		function(n)
  {
  	var list = $('ac_ul');
  
  	if (!list) 
    {
      return false;
  	}
  	
    if (this.iHigh > 0) this.clearHighlight();
  	
  	this.iHigh = Number(n);
  	
  	list.childNodes[this.iHigh-1].className = 'ac_highlight';	
  },
  
  clearHighlight:	function()
  {
  	var list = $('ac_ul');
  	
  	if(!list) return false;
  	
  	if(this.iHigh > 0)
  	{
  		list.childNodes[this.iHigh-1].className = '';
  		this.iHigh = 0;
  	}	
  },
  
  setHighlightedValue:	function()
  {
  	if (this.iHigh)
  	{
  		// HERE WE NEED TO IMPLEMENT THE GMAIL LIKE SPLITTED VALUE  		
  		if (!this.aSug[this.iHigh - 1]) return;
  		
  		// Old way
  		this.sInp	= this.fld.value = this.aSug[ this.iHigh -1 ].value;
  		
		// pass selected object to callback function, if exists
  		if (typeof this.options.callback == 'function')
  			this.options.callback(this.aSug[this.iHigh-1]); // the object has the properties we want, it will depend of
		
  		this.clearSuggestions();
  	}
  },

  clearSuggestions:	function ()
  {
	// if ($(this.acID)){this.fadeOut(50,function () {$(this.acID).remove();});}
	// probando sin fade
	if ($(this.acID)){
		$(this.acID).remove(); 
		this.handleOverlay(0);
		this.aSug = [];		
	}
  },

	/*************** IE 5.5 & 6.0 HACK ****************/
	// mira si es necesaria la construcción de un iframe de overlay
    checkOverlaySupport: function() {   	
		if (BrowserFind.browser == "Explorer" && (BrowserFind.version > 5 && BrowserFind.version < 7)){
        	return true;
        } else 
		return false;
    }, 
    
    // maneja la construcción del overlay
    handleOverlay: function(bVis) {
        if ( this.needOverlay) {
            switch (bVis) {
                case 1 :
					if (!$(this.iframe)){
					  	// create holding iframe
					
						var iframe = '<iframe id="' + this.iframe + '" frameborder="0" scrolling="no" src="about:blank" style="display:none;position:absolute;border:0px none;z-index:15000; background-color:transparent; filter: alpha(opacity=10); opacity: 0.1"></iframe>';
						document.getElementsByTagName("body")[0].id = "superbody";
						new Insertion.Bottom("superbody", iframe);
						
						var pos		= Position.cumulativeOffset(this.fld); // deprecated in Prototype 1.6, use instead this.fld.cumulativeOffset()		
						var w 		= (this.options.setWidth && this.fld.offsetWidth < this.options.minWidth)? this.options.minWidth : (this.options.setWidth && this.fld.offsetWidth > this.options.maxWidth)? this.options.maxWidth : this.fld.offsetWidth;
						var h = $(this.acID).getHeight() + (2*this.options.offsety);
						
						$(this.iframe).setStyle({			
							left:pos[0] - this.fld.offsetLeft + "px",
							top: pos[1] + (-2 * this.options.offsety) +  this.fld.offsetHeight + "px",
							width: w + "px",
							height: h + "px"							
						});								
						$(this.iframe).show();
					}
                break;
                case 0 :
					if ($(this.iframe)){
						$(this.iframe).hide();						
						$(this.iframe).remove();
					}
                break;
             }
        }
    }      
}


/**
 * DatePicker widget using Prototype and Scriptaculous.
 * (c) 2007 Mathieu Jondet <mathieu@eulerian.com>
 * Eulerian Technologies
 * Prototype 1.6 version compatibility by Harris
 * DatePicker is freely distributable under the same terms as Prototype.
 *
 */

var DatePicker	= Class.create();

DatePicker.prototype	= {
	Version	: '0.9.2',
	_relative	: null,
	_div		: null,
	_zindex	: 1,
	_keepFieldEmpty: false,
	_daysInMonth	: [31,28,31,30,31,30,31,31,30,31,30,31],
	/* language */
	_language	: 'Spanish',
	_language_month	: $H({
		'French'	: [ 'Janvier', 'F&#233;vrier', 'Mars', 'Avril', 'Mai', 'Juin', 
		'Juillet', 'Aout', 'Septembre', 'Octobre', 'Novembre', 'D&#233;cembre' ],
		'English'	: [ 'January', 'February', 'March', 'April', 'May',
		'June', 'July', 'August', 'September', 'October', 'November', 'December' ],
		'Chinese'	: [ '一月', '二月', '三月', '四月', '五月', '六月',
		'七月', '八月', '九月', '十月', '十一月', '十二月' ],
		'Japanese'	: [ '一月', '二月', '三月', '四月', '五月', '六月',
		'七月', '八月', '九月', '十月', '十一月', '十二月' ],
		'Spanish'	: [ 'Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 
		'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre' ],
		'Italian'	: [ 'Gennaio', 'Febbraio', 'Marzo', 'Aprile', 'Maggio', 'Giugno',
		'Luglio', 'Agosto', 'Settembre', 'Ottobre', 'Novembre', 'Dicembre' ],
		'German'	: [ 'Januar', 'Februar', 'M&#228;rz', 'April', 'Mai', 'Juni',
		'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember' ],
		'Portuguese'	: [ 'Janeiro', 'Fevereiro', 'Mar&#231;o', 'Abril', 'Maio', 'Junho',
		'Julho', 'Agosto', 'Setembro', 'Outubro', 'Novembro', 'Dezembro' ]
	}),
	_language_day	: $H({
		'French'	: [ 'Lun', 'Mar', 'Mer', 'Jeu', 'Ven', 'Sam', 'Dim' ],
		'English'	: [ 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun' ],
		'Spanish'	: [ 'Lun', 'Mar', 'Mie', 'Jue', 'Vie', 'S&#224;b', 'Dom' ],		
                'Chinese'	: [ '一', '二', '三', '四', '五', '六', '天' ],
                'Japanese'	: [ '月', '火', '水', '木', '金', '土', '日' ],
		'Italian'	: [ 'Lun', 'Mar', 'Mer', 'Gio', 'Ven', 'Sab', 'Dom' ],
		'German'	: [ 'Mon', 'Die', 'Mit', 'Don', 'Fre', 'Sam', 'Son' ],
		'Portuguese'	: [ 'Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'S&#225;', 'Dom' ]
	}),
	_language_close	: $H({
		'French'	: 'fermer',
		'English'	: 'close',
		'Spanish'	: 'cerrar',
                'Chinese'	: 'close',
                'Japanese'	: 'close',
		'Italian'	: 'fine',
		'German'	: 'schliessen',
		'Portuguese'	: 'fim'
	}),
	/* date manipulation */
	_todayDate	: new Date(),
	_date_regexp	: /^(\d{1,2})(\/|\.|\-)(\d{1,2})(?:\/|\.|\-)(\d{4})$/,
	_current_date	: null,
	_clickCallback	: Prototype.emptyFunction,
	_cellClickCallback	: Prototype.emptyFunction,
	_date_separator: '/',
	_id_datepicker	: null,
	/* positionning */
	_topOffset	: 100,
	_leftOffset	: 10,
	_isPositionned	: false,
	_relativePosition : true,
	
	/* return the name of current month in appropriate language */
	getMonthLocale	: function ( month ) {
		tmp = this._language_month.get(this._language)
		return	tmp[month];  
	},	
	getLocaleClose	: function () {
		return	this._language_close.get(this._language);
	},
	
	_initCurrentDate : function () {
		/* check if value in field is proper, set to today */
		this._current_date	= $F(this._relative);
		if ( !this._date_regexp.test(this._current_date) ) {
			var now	= new Date();
			var day	= this._leftpad_zero(now.getDate(), 2);
			var mon	= this._leftpad_zero(now.getMonth() + 1, 2);
			/* depending on language not presented the same way */
			if ( this._language == 'English' )
				this._current_date	= mon+'/'+day+'/'+now.getFullYear();
			else 
				this._current_date	= day+'/'+mon+'/'+now.getFullYear();
				
			/* set the field value ? */
			if ( !this._keepFieldEmpty )
				$(this._relative).setAttribute('value', this._current_date);
		}
		
		var a_date_regexp	= this._current_date.match(this._date_regexp);
		/* fetch date separator as specified in option or via value */
		this._date_separator	= String(a_date_regexp[2]);
		/* check language */
		if ( this._language == 'English' ) {
			this._current_mon	= Number(a_date_regexp[1]) - 1;
			this._current_day	= Number(a_date_regexp[3]);
		} else {
			this._current_day	= Number(a_date_regexp[1]);
			this._current_mon	= Number(a_date_regexp[3]) - 1;
		}
		this._current_year	= Number(a_date_regexp[4]);
	},
 
 	/* Utility Method */
	createDOMElement: function ( type, attr, cont, html ) {
		var ne = document.createElement( type );	
		if (!ne)
			return 0;
			
		for (var a in attr) {
			if (a == "cursor") {
				ne.style.cursor = attr[a];
			} else if (a == "display") {
				ne.style.display = attr[a];
			} else if (a == "zindex") {
				ne.style.zIndex = attr[a];			
			} else
				ne[a] = attr[a];		
		}
				
		var t = typeof(cont);		
		if (t == "string" && !html)
			ne.appendChild( document.createTextNode(cont) );
		else if (t == "string" && html)
			ne.innerHTML = cont;
		else if (t == "object")
			ne.appendChild( cont );
		return ne;
	},
 
 	/* init */
	initialize	: function ( h_p ) {
		/* arguments */
		this._relative= h_p["relative"];
		if ( h_p["language"] )
			this._language = h_p["language"];
		this._zindex	= ( h_p["zindex"] ) ? parseInt(Number(h_p["zindex"])) : 1;
		if ( typeof(h_p["keepFieldEmpty"]) != 'undefined' )
			this._keepFieldEmpty	= h_p["keepFieldEmpty"];
		if ( typeof(h_p["clickCallback"]) == 'function' )
			this._clickCallback	= h_p["clickCallback"];
		if ( typeof(h_p["cellClickCallback"]) == "function" )
			this._cellClickCallback = h_p["cellClickCallback"];
		if ( typeof(h_p["leftOffset"]) != 'undefined' )
			this._leftOffset	= parseInt(h_p["leftOffset"]);
		if ( typeof(h_p["topOffset"]) != 'undefined' )
			this._topOffset	= parseInt(h_p["topOffset"]);
		if ( typeof(h_p["relativePosition"]) != 'undefined' )
			this._relativePosition = h_p["relativePosition"];
	
		if ( typeof(h_p["timeLoader"]) != 'undefined' )
			this._timeLoader	= h_p["timeLoader"];
		else   
			this._timeLoader	= false;
	  
		this._id_datepicker		= 'datepicker-'+this._relative;
		this._id_datepicker_prev	= this._id_datepicker+'-prev';
		this._id_datepicker_next	= this._id_datepicker+'-next';
		this._id_datepicker_hdr	= this._id_datepicker+'-header';
		this._id_datepicker_ftr	= this._id_datepicker+'-footer';
	
		/* build up calendar skel */
		// create holding div
		this._div = this.createDOMElement('div', {id:this._id_datepicker, className:'datepicker', display:'none', 
			zindex: this._zindex});
	
		// create footer
		var foo = this.createDOMElement('div', {id: this._id_datepicker_ftr, className: 'datepicker-footer'}, 
			this.getLocaleClose(),true);
	
		// create calendar
		var cal = this.createDOMElement('div', {className: 'datepicker-calendar'});
		var calTable = this.createDOMElement('table', {id: this._id_datepicker+'-table'});
		cal.appendChild(calTable);	
	
		// create header
		var head = this.createDOMElement('div', {className: 'datepicker-header'});
		var span1 = this.createDOMElement('span', {id: this._id_datepicker_prev, cursor : 'pointer'}, ' << ', true);
		var span2 = this.createDOMElement('span', {id: this._id_datepicker_hdr});
		var span3 = this.createDOMElement('span', {id: this._id_datepicker_next, cursor : 'pointer'}, ' >> ', true);
		head.appendChild(span1);	
		head.appendChild(span2);	
		head.appendChild(span3);			
		
		// put all together
		this._div.appendChild(head);  
		this._div.appendChild(cal);  	
		this._div.appendChild(foo);  	
	
		/* finally declare the event listener on input field */
		Event.observe('image' + this._relative, 
		'click', this.click.bindAsEventListener(this), false);
		
		/* hacemos lo mismo para el input de fechas */
		Event.observe(this._relative, 
		'click', this.click.bindAsEventListener(this), false);    
		
		// La carga del datepicker se hace onload ya. no es necesario comprobar nada
		this.load();		  	
	},
	
	/**
	* load	: called when document is fully-loaded to append datepicker to main object.
	*/
	load : function () {
		/* append to body */
		// var body	= document.getElementsByTagName("body").item(0);
		//Probamos a apendearlo a otro lado, por ejemplo, el mismo container que tiene las textbox de fecha, llamado 'date_picker'
		//var body	= $("date_picker");
		//  if ( body )
		//  {
		//    body.appendChild( this._div );
		//  }
		//  else	//Si no existe dicha div, para no matar los ya hechos, intenta apendearlo al body
		//  {
		//    var body	= document.getElementsByTagName("body").item(0);
		//    if ( body )
		//    {
		//       body.appendChild( this._div );
		//    }
		//  }  
		document.getElementsByTagName("body")[0].appendChild(this._div);
	
		/* init the date in field if needed */
		this._initCurrentDate();
		/* declare the observers for UI control */
		Event.observe($(this._id_datepicker_prev), 'click', this.prevMonth.bindAsEventListener(this), false);
		Event.observe($(this._id_datepicker_next), 'click', this.nextMonth.bindAsEventListener(this), false);
		Event.observe($(this._id_datepicker_ftr), 'click', this.close.bindAsEventListener(this), false);
	},
	
	/**
	* click	: called when input element is clicked
	*/
	click : function () {
		if ( !this._isPositionned && this._relativePosition ) {
			/* position the datepicker relatively to element */	
			//   var a_lt = $(this._relative).cumulativeOffset(); este es el bueno en 1.6
			var a_lt = Position.cumulativeOffset($(this._relative));
			
			$(this._id_datepicker).setStyle({
				'left'	: Number(a_lt[0]+this._leftOffset)+'px',
				'top'	: Number(a_lt[1]+this._topOffset)+'px'
			});
			this._isPositionned	= true;
		}
		if ( !$(this._id_datepicker).visible() ) {
			this._initCurrentDate();
			this._redrawCalendar();
		}
		/* eval the clickCallback function */
		eval(this._clickCallback());
		
		/* Effect toggle to fade-in / fade-out the datepicker */
		/*  new Effect.toggle(this._id_datepicker, 'appear'); */
                $(this._id_datepicker).show();
//		if ( !$(this._id_datepicker).visible() ) {
//			$(this._id_datepicker).show();
//		} else {
//			//new Effect.Fade(this._id_datepicker, { duration : 0.3 });
//			/* $(this._id_datepicker).hide();  	*/
//			$(this._id_datepicker).hide();
//		}
	},
	
	/**
	* close	: called when the datepicker is closed
	*/
	close : function () {
		/* 	$(this._id_datepicker).hide(); */
		$(this._id_datepicker).hide();  
	},
	
	/**
	* setPosition	: set the position of the datepicker. 
	* param : t=top | l=left
	*/
	setPosition	: function ( t, l ) {
		var h_pos	= { 'top' : '0px', 'left' : '0px' };
		if ( typeof(t) != 'undefined' )
			h_pos['top']	= Number(t)+this._topOffset+'px';
		if ( typeof(l) != 'undefined' )
			h_pos['left']= Number(l)+this._leftOffset+'px';
		$(this._id_datepicker).setStyle(h_pos);
		this._isPositionned	= true;
	},
	
	/**
	* _leftpad_zero : pad the provided string to given number of 0
	*/
	/** CHECK toPaddedString: from http://dev.rubyonrails.org/changeset/6363 */
	_leftpad_zero	: function ( str, padToLength ) {
		var result	= '';
		for ( var i = 0; i < (padToLength - String(str).length); i++ )
			result	+= '0';
		return	result + str;
	},
	
	/**
	* _getMonthDays : given the year and month find the number of days.
	*/
	_getMonthDays	: function ( year, month ) {
		if (((0 == (year%4)) && ( (0 != (year%100)) || (0 == (year%400)))) && (month == 1))
			return 29;
		return this._daysInMonth[month];
	},
	
	/**
	* _buildCalendar	: draw the days array for current date
	*/
	_buildCalendar : function () {
		var _self	= this;
		var tbody	= document.createElement('tbody');
		/* generate day headers */
		var trDay	= document.createElement('tr');
	
		this._language_day.get(this._language).each( function ( item ) {
			var td	= document.createElement('td');
			td.innerHTML	= item;
			td.className	= 'wday';
			trDay.appendChild( td );
		});
		tbody.appendChild( trDay );
		
		/* generate the content of days */	
		/* build-up days matrix */
		var a_d	= [
		[ 0, 0, 0, 0, 0, 0, 0 ]
		,[ 0, 0, 0, 0, 0, 0, 0 ]
		,[ 0, 0, 0, 0, 0, 0, 0 ]
		,[ 0, 0, 0, 0, 0, 0, 0 ]
		,[ 0, 0, 0, 0, 0, 0, 0 ]
		,[ 0, 0, 0, 0, 0, 0, 0 ]
		];
		/* set date at beginning of month to display */
		var d		= new Date(this._current_year, this._current_mon, 1, 12);
		/* start the day list on monday */
		var startIndex	= ( !d.getDay() ) ? 6 : d.getDay() - 1;
		var nbDaysInMonth	= this._getMonthDays(
		this._current_year, this._current_mon);
		var daysIndex		= 1;
		for ( var j = startIndex; j < 7; j++ ) {
			a_d[0][j]	= { 
				 d : daysIndex
				,m : this._current_mon
				,y : this._current_year 
			};
			daysIndex++;
		}
		var a_prevMY	= this._prevMonthYear();
		var nbDaysInMonthPrev	= this._getMonthDays(a_prevMY[1], a_prevMY[0]);
		for ( var j = 0; j < startIndex; j++ ) {
			a_d[0][j]	= { 
				 d : Number(nbDaysInMonthPrev - startIndex + j + 1) 
				,m : Number(a_prevMY[0])
				,y : a_prevMY[1]
				,c : 'outbound'
			};
		}
		var switchNextMonth	= false;
		var currentMonth	= this._current_mon;
		var currentYear	= this._current_year;
		for ( var i = 1; i < 6; i++ ) {
			for ( var j = 0; j < 7; j++ ) {
				a_d[i][j]	= { 
				  d : daysIndex
				 ,m : currentMonth
				 ,y : currentYear
				 ,c : ( switchNextMonth ) ? 'outbound' : ( 
				  ((daysIndex == this._todayDate.getDate()) &&
				    (this._current_mon  == this._todayDate.getMonth()) &&
				    (this._current_year == this._todayDate.getFullYear())) ? 'today' : null)
				};
				daysIndex++;
				
				/* if at the end of the month : reset counter */
				if ( daysIndex > nbDaysInMonth ) {
					 daysIndex	= 1;
					 switchNextMonth = true;
					 if ( this._current_mon + 1 > 11 ) {
						currentMonth = 0;
						currentYear += 1;
					 } else {
						currentMonth += 1;
					 }
				}
			}
		}
	
		/* generate days for current date */
		for ( var i = 0; i < 6; i++ ) {
			var tr	= document.createElement('tr');
			for ( var j = 0; j < 7; j++ ) {
				var h_ij	= a_d[i][j];
				var td	= document.createElement('td');
				/* id is : datepicker-day-mon-year or depending on language other way */
				/* don't forget to add 1 on month for proper formmatting */
				if ( this._language == 'English' ) 
	 				var id	= $A([ this._relative, this._leftpad_zero((h_ij["m"] +1), 2),
	   					this._leftpad_zero(h_ij["d"], 2), h_ij["y"] ]).join('-');
	 			else 
	  				var id	= $A([ this._relative, this._leftpad_zero(h_ij["d"], 2),
						this._leftpad_zero((h_ij["m"] + 1), 2), h_ij["y"] ]).join('-');
				/* set id and classname for cell if exists */
				td.setAttribute('id', id);
				if ( h_ij["c"] )
	 				td.className	= h_ij["c"];
				/* on onclick : rebuild date value from id of current cell */
				td.onclick	= function () { 
	 				$(_self._relative).value = String($(this).readAttribute('id')
						).replace(_self._relative+'-','').replace(/-/g,_self._date_separator); 
	 				_self.close();
				};
				Event.observe( td , 'click' , this._cellClickCallback )
				//td.onclick = this._cellClickCallback;
				td.innerHTML= h_ij["d"];
				tr.appendChild( td );
			}
			tbody.appendChild( tr );
		}
		return	tbody;
	},
	
	
	/**
	* nextMonth	: redraw the calendar content for next month.
	*/
	_nextMonthYear	: function () {
		var c_mon	= this._current_mon;
		var c_year	= this._current_year;
		if ( c_mon + 1 > 11 ) {
			c_mon	= 0;
			c_year	+= 1;
		} else {
			c_mon	+= 1;
		}
		return	[ c_mon, c_year ];
	},	
	nextMonth	: function () {
		var a_next		= this._nextMonthYear();
		this._current_mon	= a_next[0];
		this._current_year 	= a_next[1];
		this._redrawCalendar();
	},
	
 /**
  * prevMonth	: redraw the calendar content for previous month.
  */
	_prevMonthYear	: function () {
		var c_mon	= this._current_mon;
		var c_year	= this._current_year;
		if ( c_mon - 1 < 0 ) {
			c_mon	= 11;
			c_year	-= 1;
		} else {
			c_mon	-= 1;
		}
		return	[ c_mon, c_year ];
	},	
	prevMonth	: function () {
		var a_prev		= this._prevMonthYear();
		this._current_mon	= a_prev[0];
		this._current_year 	= a_prev[1];
		this._redrawCalendar();
	},
	
	
	_redrawCalendar	: function () {
		this._setLocaleHdr();
		var table	= $(this._id_datepicker+'-table');
		try {
			while ( table.hasChildNodes() )
				table.removeChild(table.childNodes[0]);
			} catch ( e ) {}
		table.appendChild( this._buildCalendar() );
	},
	
	_setLocaleHdr	: function () {
		/* next link */
		var a_next	= this._nextMonthYear();
		$(this._id_datepicker_next).setAttribute('title',
		this.getMonthLocale(a_next[0])+' '+a_next[1]);
		/* prev link */
		var a_prev	= this._prevMonthYear();
		$(this._id_datepicker_prev).setAttribute('title',
		this.getMonthLocale(a_prev[0])+' '+a_prev[1]);
		/* header */
		$(this._id_datepicker_hdr).update('&nbsp;&nbsp;&nbsp;'+this.getMonthLocale(this._current_mon)+'&nbsp;'+this._current_year+'&nbsp;&nbsp;&nbsp;');
	}
};


/**
 * Funciones de Utilidad Que pueden ser necesarias para las search boxes
 */
 
// Metodo para actualizar campos de mostrar fecha en formato legible
AlertSelectedDate = function (Source, Target, Lang) {
	var language_month	= $H({
		'fr_FR.UTF-8'	: [ 'Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin', 'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre' ],
		'it_IT.UTF-8'	: [ 'Gennaio', 'Febbraio', 'Marzo', 'Aprile', 'Maggio', 'Giugno', 'Luglio', 'Agosto', 'Settembre', 'Ottobre', 'Novembre', 'Dicembre' ],
		'de_DE.UTF-8'	: [ 'Januar', 'Februar', 'M&#228;rz', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember' ],
		'pt_PT.UTF-8'	: [ 'Janeiro', 'Fevereiro', 'Mar&#231;o', 'Abril', 'Maio', 'Junho', 'Julho', 'Agosto', 'Setembro', 'Outubro', 'Novembro', 'Dezembro' ],
		'es_ES.UTF-8'	: [ 'Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre' ],
		'ja_JP.UTF-8'	: [ '一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月' ],
		'zh_CN.UTF-8'	: [ '一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月' ]
	});
	var language_day = $H({
		'fr_FR.UTF-8'	: [ 'Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi', 'Vendredi', 'Samedi' ],
		'it_IT.UTF-8'	: [ 'Domenica', 'Lunedì', 'Martedì', 'Mercoledì', 'Giovedì', 'Venerdì', 'Sabato' ],
		'de_DE.UTF-8'	: [ 'Sonntag', 'Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag' ],
		'pt_PT.UTF-8'	: [ 'Domingo', 'Segunda', 'Terça', 'Quarta', 'Quinta', 'Sexta', 'Sábado' ],
		'es_ES.UTF-8'	: [ 'Domingo','Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado' ],
		'ja_JP.UTF-8'	: [ '月曜日', '火曜日', '水曜日', '木曜日', '金曜日', '土曜日', '日曜日' ],
		'zh_CN.UTF-8'	: [ '星期一', '星期二', '星期三', '星期四', '星期五', '星期六', '星期天' ] 		
	});
	
	// Formato fecha 22/08/2008
	var dateString = $(Source).value;		
	var parsedDate = dateString.split("/");
	var day = parsedDate[0];
	var month = parsedDate[1];
	var year = parsedDate[2];		
	var date = new Date(year, month-1, day);
	var numday = date.getDay();
	var langDay = language_day.get(Lang);
	var dayName = langDay[numday];
	
	var nummonth = date.getMonth();		
	var langMonth = language_month.get(Lang);
	var monthName = langMonth[nummonth];	
	
	$(Target).update(dayName + " " + day + " " + monthName); 
}

//Source = Nombre de la textbox a observar
//Target = Nombre de la textbox a modificar
//Timelaps = Cantidad en dias a añadir
UpdateDate = function (Source, Target, Timelapse) {
	//1 Week = 7 * 24 * 60 * 60 * 1000 miliseconds = 604800000;
	var timeIncrease = Timelapse * 24 * 60 * 60 * 1000;
	var dateString = $(Source).value;
	var dateMatches = dateString.split("/");
	var dateDay  = dateMatches[0];
	var dateMon  = dateMatches[1];
	var dateYear = dateMatches[2];
	
	var sourceTime = Date.parse( dateMon + "/" + dateDay + "/" + dateYear );
	var targetTime = sourceTime*1 + timeIncrease*1;
	
	var newDate = new Date();
	newDate.setTime(targetTime);
	
	var newDay = newDate.getDate();
	var newMon = (newDate.getMonth()+1);
	var newYear = newDate.getFullYear();
	
	newDay = (newDay + "").length == 1 ? "0"+newDay : newDay;
	newMon = (newMon + "").length == 1 ? "0"+newMon : newMon;
	
	$(Target).value = newDay + "/" + newMon + "/" + newYear;
}


inArray = function (value, container) {
	var exists = false;
	var value1;
	var value2;
	
	for (var i=0; i<container.length; i++)
	{
		value1 = value + "";
		value2 = container[i] + "";

		if ( value1 == value2 ) {
			exists = true;
		}
	}
	
	return exists;
}