﻿/*
	Ds.AutoComplete.js
*/

function DsAutoComplete(FInput, Options) {
	this._Input        = Ds.$(FInput);
	this._SelectedIdx  = -1;
	this._SelectedItem = null;
	this._Cache        = {};
	this._RequestTimer = null;
	
	// -- Extender Options y Css Atributos
	Ds.Extend(this, DsAutoComplete.DefOptions.Or(Options));
	Ds.Extend(this, DsAutoComplete.Css);

	// -- Resultados - Terminar para ArrayLocal
	//this._Results = this._FnParseResults && this._FnParseResults(this._Results) || this.ParseResults();
		
	// --- Dar de alta los eventos necesarios
	//Ds.AddEvent(this._Input,'focus',    this.OnFocus.Bind(this),   false);
	//Ds.AddEvent(this._Input,'blur',     this.OnBlur.Bind(this),    false);
	//Ds.AddEvent(this._Input,'keyup',    this.OnKeyup.Bind(this),   false);
	//Ds.AddEvent(this._Input,'keydown',  this.OnKeydown.Bind(this), false);
	//Ds.AddEvent(this._Input,'keypress', this.OnKeypress.Bind(this),false);
	
	var pObj = this;
	
	Ds.AddEvent(this._Input,'focus',    function(e) { pObj.OnFocus(e);    }, false);
	Ds.AddEvent(this._Input,'blur',     function(e) { pObj.OnBlur(e);     }, false);
	Ds.AddEvent(this._Input,'keyup',    function(e) { pObj.OnKeyup(e);    }, false);
	Ds.AddEvent(this._Input,'keydown',  function(e) { pObj.OnKeydown(e);  }, false);
	Ds.AddEvent(this._Input,'keypress', function(e) { pObj.OnKeypress(e); }, false);

	// --- Crear la dropdown list para los resultados
	this._List = Ds.$(document.createElement("div"));
	this._List.SetClassName(this.Css_List);
	this._List.SetStyle({width: this._Input.offsetWidth+10+'px', display: 'none'});

	this._Input.parentNode.appendChild(this._List);
	this._Input.SetStyle({margin: "0px"});

	// --- Iniciar
	this.SetFormatedValue();

	// -- Fin Constructor
	return this;
}

// --- OnFocus
DsAutoComplete.prototype.OnFocus = function()
{
	// No se puede en onblur
	this._SelectedIdx = -1;

	if(!!this._SelectedItem)
	{
		this.SetValue();
		this.UpdateResults();
		this._Input.select();
	}
	else
		this.ClearValue();
}

// --- OnBlur
DsAutoComplete.prototype.OnBlur = function(evt)
{
	var e = Ds.Event(evt);
	
	this.Hide();
	//this._SelectedIdx = -1;
	this.SetFormatedValue();
}

// --- OnKeyup
DsAutoComplete.prototype.OnKeyup = function(evt)
{
	var e = Ds.Event(evt);
	
	switch (e.keyCode)
	{
		case 27: // escape
			this.Hide();
			this._Input.blur();
			break;
		case 13: // enter
			break;
		case 0:  
		case 37: // left
		case 9:  // tab
		case 38: // up
		case 39: // right
		case 40: // down
			break;
		default:
			this._SelectedIdx = -1;
			this.UpdateResults();
			break;
	}
}

// --- OnKeydown
DsAutoComplete.prototype.OnKeydown = function(evt)
{
	var e = Ds.Event(evt);
	
	switch (e.keyCode)
	{
		case 9: // tab
			this.OnSelect();
			break;
		case 13: // enter
			this.OnSelect();
			e.PreventDefault();
			this._Input.blur();
			break;
		case 38: // up
			this.Select(this._SelectedIdx - 1);
			e.PreventDefault();
			break;
		case 40: // down
			this.Select(this._SelectedIdx + 1);
			e.PreventDefault();
			break;
	}
}

// --- OnKeypress : Actualmente sin funcionalidad
DsAutoComplete.prototype.OnKeypress = function(evt)
{
	var e = Ds.Event(evt);
	
	switch (e.keyCode)
	{
	    case 13: // return
		case 9:  // tab
		case 38: // up
		case 40: // down
      		e.PreventDefault();
      		break;
	}
}

// --- UpdateResults
DsAutoComplete.prototype.UpdateResults = function()
{
	var val = this.GetNormalTyped();
	
	if( val.length < this._MinInputLength )
		return;
		
	if(this._Cache[val] != null)
	{
		// ---- Utilizar cache
		this._Results = this._Cache[val].Results;
		this.DrawResults(val);
	}
	else if(this._Cache[val] == null || this._Cache[val].CurRequest == null)
	{
		if(this._RequestTimer != null)
			clearTimeout(this._RequestTimer);
		
		this._RequestTimer = setTimeout(
			function()
			{ 
				this.SendAjxRequest(val); 
				this._RequestTimer = null;
			}.Bind(this), this._DelayTime);
	}
}

// --- Select : Selección un elemento de la lista
DsAutoComplete.prototype.Select = function(nIdx)
{
	var Childs = this._List.childNodes;
	
	if(Childs != null && Childs.length > 0)
	{
		if(nIdx < 0)
			nIdx = 0;
		if(nIdx >= Childs.length)
			nIdx = Childs.length - 1;
		
		if( this._SelectedIdx >=0 && this._SelectedIdx < Childs.length )
			Ds.$(Childs[this._SelectedIdx]).RemoveClassName(this.Css_ItemSel);
			
		Ds.$(Childs[(this._SelectedIdx=nIdx)]).SetClassName(this.Css_ItemSel);
	}
}

// --- OnSelect : Selección un elemento de la lista
DsAutoComplete.prototype.OnSelect = function()
{
	if (this._Results[this._SelectedIdx])
	{
		this._SelectedItem = this._Results[this._SelectedIdx];
		this.SetValue();
		this.Hide();
	}
}

// --- OnClickSelect : Selección un elemento de la lista por mouse
DsAutoComplete.prototype.OnClickSelect = function()
{
	this.OnSelect();
	this.OnBlur(null);
}

// --- DrawResults : Actualizar UI
DsAutoComplete.prototype.DrawResults = function(Typed) {
	var Results = this._Results;

	// -- Limpiar lista
	this._List.SetHTML('');

	// -- Controlar resultados
	if (Results == null || Results.length == 0) {
		this.Hide();
		return;
	}

	// -- Mostrar lista
	this.Show();

	// -- Crear Items
	for (var i = 0; i < Results.length; i++) {
		var nBegins = 0,
			Item = Results[i];
		ItemValue = Item.FormatedValue,
			ListItem = null;

		//if ((nBegins = Item.Value.toLowerCase().indexOf(Typed)) == -1)
		//	continue;
		nBegins = Item.Value.toLowerCase().indexOf(Typed);

		ListItem = Ds.$(document.createElement("div")).SetClassName(this.Css_Item);
		Ds.AddEvent(ListItem, 'mousedown', function() { this.OnClickSelect(); } .Bind(this), false);
		Ds.AddEvent(ListItem, 'mouseover', function() { this[0].Select(this[1]); } .Bind([this, i]), false);

		// -- No match
		if (nBegins == -1)
		{
			var oSpan = Ds.$(document.createElement("span")).SetHTML(ItemValue);
			ListItem.appendChild(oSpan);
			this._List.appendChild(ListItem);
			continue;
		}
		
		var InitSpan = Ds.$(document.createElement("span")).SetHTML(ItemValue.substring(0, nBegins));
		ListItem.appendChild(InitSpan);

		var EmItem = Ds.$(document.createElement('em')).SetHTML(ItemValue.substring(nBegins, nBegins + Typed.length));
		ListItem.appendChild(EmItem);

		var EndSpan = Ds.$(document.createElement("span")).SetHTML(ItemValue.substring(nBegins + Typed.length));
		ListItem.appendChild(EndSpan);

		this._List.appendChild(ListItem);
	}

	// -- Seleccionar primer elemento
	this.Select(0);
}

// --- SendAjxRequest : Envia peticion al servidor
DsAutoComplete.prototype.SendAjxRequest = function(val)
{
	// --- Ajax Query
	var pThis = this;
	var Request = Ds.Ajax({ Url: this._AjaxUrl, Async: true });
	Request.OnDone = function() { pThis.OnAjaxDone(Request); };

	this._Cache[val] = { CurRequest: Request };
	this._Cache[val].CurRequest.Submit("Typed=" + val + "&Results=" + this._MaxResults);
}

// --- OnAjaxDone : Respuesta del servidor
DsAutoComplete.prototype.OnAjaxDone = function(oAjx)
{
	var oJSON = eval( "(" + oAjx.ResponseText + ")" );

	// -- ParseResults
	oJSON.Results = this.ParseResults(oJSON.Results);
	
	// --- Guardar a la cache
	this._Cache[oJSON.Typed].Results    = oJSON.Results;
	this._Cache[oJSON.Typed].Keys       = oJSON.Keys;
	this._Cache[oJSON.Typed].CurRequest = null;
	
	// --- Actualizar UI si coincide con lo enviado
	if(this.GetNormalTyped() == oJSON.Typed)
	{
		this._Results = oJSON.Results;
		
		this.DrawResults(oJSON.Typed);
	}
}

// --- ParseResults
DsAutoComplete.prototype.ParseResults = function(Results)
{
	var aParsed = [];
	
	for( var n = 0; n < Results.length; n++ )
	{
		var Row = Results[n];
		if (Row)
		{
			aParsed[aParsed.length] =
			{
				Data: Row,
				Value: this._FnValue && this._FnValue(Row) || Row.Value,
				FormatedValue: this._FnFormatedValue && this._FnFormatedValue(Row) || Row.Value,
				Result: this._FnResult && this._FnResult(Row) || Row.Result
			};
		}
	}
	
	return aParsed;
}

// --- GetNormalTyped : Obtener valor introducido normalizado
DsAutoComplete.prototype.GetNormalTyped = function()
{
	return this._Input.GetValue().toLowerCase();
}

// --- SetValue : Centralizar Teclado y Mouse
DsAutoComplete.prototype.SetValue = function()
{
	if( !!this._SelectedItem )
	{
		this._Input.SetValue(this._SelectedItem.Value);
		this._Input.RemoveClassName(this.Css_PreText);
	}
	else
	{
		this.ClearValue();
		this._Input.SetClassName(this.Css_PreText);
	}
}

// --- SetFormatedValue : 
DsAutoComplete.prototype.SetFormatedValue = function()
{
	if( !!this._SelectedItem )
		this._Input.SetValue(this.CurFormatedValue());
	else
		this._Input.SetValue(this._CurText);

	this._Input.SetClassName(this.Css_PreText);
}

// --- ClearValue : 
DsAutoComplete.prototype.ClearValue = function()
{
	this._Input.SetValue('');
}


// --- CurItem
DsAutoComplete.prototype.CurItem   = function()
	{ return this._SelectedItem; }
DsAutoComplete.prototype.CurResult = function()
	{ return this._SelectedItem ? this._SelectedItem.Result : ""; }
DsAutoComplete.prototype.CurValue  = function()
	{ return this._SelectedItem ? this._SelectedItem.Value : ""; }
DsAutoComplete.prototype.CurFormatedValue = function()
	{ return this._SelectedItem ? this._SelectedItem.FormatedValue : ""; }


// --- Show : Mostrar lista
DsAutoComplete.prototype.Show = function()
{
	if( (this._Input.GetValue().length >= this._MinInputLength) )
		this._List.SetStyle({display:'block'});
	else
		this.Hide();
}

// --- Hide : Ocultar lista
DsAutoComplete.prototype.Hide = function()
{
	this._List.SetStyle( {display:'none'} );
}


// --- Default Options y Css Classes
DsAutoComplete.DefOptions = { _CurText: "",
				              _MaxResults: 12,
				              _MinInputLength: 3,
				              _AjaxUrl: "",
				              _FnOnEnter: null, /*function(Evt) { var s = ""; }*/
				              _FnParseResults: null,
				              _FnValue: null,
				              _FnFormatedValue: null,
				              _FnResult: null,
				              _DelayTime: 700,
				              _ClearOnEnter: true,
				              _Results:[]
				            };
DsAutoComplete.Css = { Css_PreText : "AC_PreText",
                       Css_Found   : "AC_Found",
                       Css_List    : "AC_List",
                       Css_Item    : "AC_Item",
                       Css_ItemSel : "AC_ItemSelected" };