﻿//
// jquery.atomsuggest.js, licensed under the MIT license.
//
// (c) 2010 INIST-CNRS, Nicolas Thouvenin <nicolas.thouvenin@inist.fr>
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
//
(function($)
{	
	$.fn.autosuggest = function(options) 
	{
	    var _lastcall = null;
		var _lastresponse = "";


		//fusionnement des options et des options par défaut
		var fusionOptions = $.extend( {
										"url": "", 
										"widthFactor": 1,
										"showInfos": {"identifier": false, "updated": false, "content": false, "result": false},
										"paramValue": "",
										"extramParam": {"ws": "", "alt": "", "action": "", "lang": "fr"}
										}, options);		
		
		//récupération du sélecteur courant
		var _inputField=$(this);
		
		//récupération de l'élément input pour les createRange
		var _input=window.document.getElementById($(this).attr('id'));
	
		$(_inputField).attr("autocomplete", "off");
	  
		//on crée notre div des suggestions	
		var context = $("<div>").appendTo($(this).parent()).addClass("feed").addClass("autosuggest")
									.css( 
											{
												zIndex: "1",
												left: $(_inputField).offset().left +"px",
												top: $(_inputField).offset().top +$(_inputField).height()+7+"px",
												width: function() 	{
																		if(fusionOptions.widthFactor)
																			return $(_inputField).width()*fusionOptions.widthFactor+"px"
																		else
																			return $(_inputField).width()+"px"
																	}
											}
										);
		
		if($.browser.opera)
		{
			//longeur
			$(context).css("width", parseInt($(_inputField).css("width"))*parseInt(fusionOptions.widthFactor)+"px");
			//hauteur
			$(context).css("top", parseInt($(_inputField).offset().top)+parseInt($(_inputField).css("height"))+7+"px");
		}
		
		//on stocke la valeur courante du champ texte
		var valeurCourante = $(_inputField).attr("value");
		
		//on initialise la valeur précédente
		var valeurPreced = valeurCourante;
		
		$(document).keydown( function (event)
									{ 
										// accès evenement compatible IE/Firefox
										if(!event&&window.event) 
										{
											event=window.event;
										}
										// on enregistre la touche ayant déclenché l'evenement
										if(event) 
										{
											_lastKeyCode=event.keyCode;
										} 
									});
		
		// Handler pour le keyup de lu champ texte
		$(_inputField).keyup( function(event) 
									{ 
										setTimeout( function() 
										{
											if(valeurPreced!=valeurCourante)
											{
												var valeur=valeurCourante;
											
												//on va chercher les suggestions
												callSuggestions(valeur, fusionOptions, context, _inputField, _input, valeurCourante)
												
												$(_inputField).focus()	
											}
											valeurPreced=valeurCourante;
											return true
										}, 100);
										
										// accès evenement compatible IE/Firefox
										if(!event&&window.event) 
											event=window.event;
										
										_eventKeycode=event.keyCode;
										  
										// Dans les cas touches touche haute(38) ou touche basse (40)
										if(_eventKeycode==40||_eventKeycode==38) 
											// on autorise le blur du champ (traitement dans onblur)
											blurThenGetFocus(_inputField);
										

										// contenu du champ texte
										var V = $(_inputField).attr("value");
										  
										// si la touche n'est ni haut, ni bas, on stocke la valeur utilisateur du champ
										if(_eventKeycode!=40&&_eventKeycode!=38) 
											valeurCourante=V;
										
										if(handleCursorUpDownEnter(_eventKeycode, fusionOptions, context, _inputField)&&_eventKeycode!=0) 
											// si on a pressé une touche autre que haut/bas/enter
											PressAction(context, _inputField, _input, valeurCourante);
										
									});
		
		// recalcule la taille des suggestions
		$(window).resize( function() { $(context)
											.width($(_inputField).width()*fusionOptions.widthFactor+"px")
											.offset( {top : $(_inputField).offset().top +$(_inputField).height()+7, 
													 left: $(_inputField).offset().left}) 
									} );
	}

	function valof(entry)
	{
		if (typeof entry.$t != 'undefined') 
			return entry.$t;
		else if (typeof entry.value != 'undefined') 
			return entry.value;
		else
			return '';
	}

	function callSuggestions(valeur, fusionOptions, context, _inputField, _input, valeurCourante)
	{			
        var params  = new Object();
		for (cle in fusionOptions.extraParams)
		{			
			if(typeof(fusionOptions.extraParams[cle])=="function") {				
                params[cle] = fusionOptions.extraParams[cle]();
			}
			else {
                params[cle] = fusionOptions.extraParams[cle];
            }
		}
        params[fusionOptions.paramValue] = valeur+"*";

		if(typeof _lastcall != 'undefined') {
			_lastcall.abort();
		}
		_lastcall = $.getJSON( 
				fusionOptions.url,
				params,
				function (jsondata)
						{	
                            _lastresponse = jsondata;        
							while($(" > *", context).length>0) 
							{
								$(context).empty();
							}
							
							$.each(_lastresponse.feed.entry, function(i)
									{
										var nouveauDiv = $("<div>").addClass("hentry").appendTo($(context));
											
										$("<p>").addClass("headline").append(valof(this.title)).appendTo(nouveauDiv);
										
										//supplément d'informations
										if(fusionOptions.showInfos.identifier)
											$("<p>").addClass("identifier").append(valof(this.id)).appendTo(nouveauDiv);
										
										if(fusionOptions.showInfos.updated)
											$("<p>").addClass("updated").append(valof(this.updated)).appendTo(nouveauDiv);
										
										if(fusionOptions.showInfos.content)
										{
											if(this.content[0])
											{
												$.each(this.content, function()
																	{
																		if(this.type=="text")
																			$("<p>").addClass("content").text(valof(this)).appendTo(nouveauDiv);
																	});
											}
											else
												$("<p>").addClass("content").text(valof(this.content)).appendTo(nouveauDiv);
										}
										
										nouveauDiv.mousedown( function () { 
																			$(_inputField).attr("value", $(this).find("p.headline").text());
																			
																			if (typeof(fusionOptions.onSelect) == "function")
																			{
																				fusionOptions.onSelect(_lastresponse.feed.entry[i]);																				
																			}
																		  });
																		  
										nouveauDiv.mouseover( function() { 
																			if(_highlightedSuggestionDiv) 
																				$(_highlightedSuggestionDiv).attr("class", "hentry");
																				
																			$(this).attr("class", "divselector");
																		});
																		
										nouveauDiv.mouseout( function() { 
																			$(this).attr("class", "hentry");
																		});
									});
							
							PressAction(context, _inputField, _input, valeurCourante);			
							
							if(fusionOptions.showInfos.result)
							{
								var nbResultAffich="";
								
								if(_lastresponse.feed.openSearch$totalResults)
								{
									if(parseInt(valof(_lastresponse.feed.openSearch$totalResults))>=parseInt(valof(_lastresponse.feed.openSearch$itemsPerPage)))
									{
										nbResultAffich = valof(_lastresponse.feed.openSearch$itemsPerPage);
									}
									else
									{
										nbResultAffich = valof(_lastresponse.feed.openSearch$totalResults)-1;
									}
									
									$("<div>").addClass("result")
									.append("Résultats : <span>"+(parseInt(nbResultAffich))+ "</span> sur <span>"+(parseInt(valof(_lastresponse.feed.openSearch$totalResults))-1)+"</span>")
									.appendTo($(context));
								}
								else
								{
									$("<div>").addClass("result")
									.append("<span>"+suggestDivRows+"</span> résultat(s) affiché(s)")
									.appendTo($(context));
								}
							}
								
								
							//highlight pour les mots ne commencant pas par une minuscule
							highlight(valeurCourante.substring(0,1).toLowerCase()+valeurCourante.substring(1), context)
							
							//highlight pour les mots commencant par une majuscule
							highlight(valeurCourante.substring(0,1).toUpperCase()+valeurCourante.substring(1), context)
						}
			);
	}	
	
	function highlight(saisie, context)
	{	
		$("div.hentry p.headline, div.hentry p.content, div.divselector p.headline, div.divselector p.content", context).each( function()
							{
								var chaine="";								
								
								if($(this).text().search(saisie) != -1)
								{
									chaine+=$(this).text().substr(0,$(this).text().search(saisie));
									chaine+="<span class='highlight'>"+$(this).text().substr($(this).text().search(saisie),saisie.length);
									var nbDebutFinValeur = $(this).text().search(saisie)+(saisie.length);
									var nbFinValeurFin = $(this).text().length-($(this).text().search(saisie)+(saisie.length));
									chaine+="</span>"+$(this).text().substr(nbDebutFinValeur, nbFinValeurFin);
									
									$(this).empty();
									$(this).append(chaine);
								}
							});
	}
	
	var _lastKeyCode=null;
	
	// Change la suggestion selectionné.
	// cette méthode traite les touches haut, bas et enter
	function handleCursorUpDownEnter(eventCode, fusionOptions, context, _inputField)
	{
		if(eventCode==40) {
			highlightNewValue(_highlightedSuggestionIndex+1, context, _inputField);
			return false;
		}
		else if(eventCode==38) {
			highlightNewValue(_highlightedSuggestionIndex-1, context, _inputField);
			return false;
		}
		else if( (eventCode==13 || eventCode==3) && typeof(fusionOptions.onSelect) == "function" &&  typeof(_lastresponse.feed.entry[_highlightedSuggestionIndex]) != 'undefined') {
			fusionOptions.onSelect(_lastresponse.feed.entry[_highlightedSuggestionIndex]);
			return false
		}
		return true;
	}

	var suggestDivRows = 0;
	var suggestDivDivList = null;
	var _highlightedSuggestionIndex = -1;
	var _highlightedSuggestionDiv = null;

	// gère une touche pressée autre que haut/bas/enter
	function PressAction(context, _inputField, _input, valeurCourante) 
	{
		_highlightedSuggestionIndex=-1;
		
		var suggestionList=$("div.hentry", context); 
		
		var suggestionLongueur=suggestionList.length;
	
		// nombre de possibilités de complétion
		suggestDivRows=suggestionLongueur;
				
		// possiblités de complétion
		suggestDivDivList=suggestionList;
	
		// si le champ est vide, on cache les propositions de complétion
		if(valeurCourante==""||suggestDivRows==0)
		{
			hideSuggestDiv(context)
		}else
		{
			showSuggestDiv(context)
		}
		var trouve=false;
		// si on a du texte sur lequel travailler
		if(valeurCourante.length>0)
		{
			var indice=0;
			
			$("p.headline", context).each(function(i) 
			{
					if ($(this).text().toLowerCase().indexOf(valeurCourante.toLowerCase()) == 0) 
					{
						trouve=true;
						indice=i;
						return false;
					}
				
			});
		}

		// si l'entrée utilisateur (n) est le début d'une suggestion (n-1) on sélectionne cette suggestion avant de continuer
		if(trouve)
		{
			_highlightedSuggestionIndex=indice;
			_highlightedSuggestionDiv=$("div:eq("+_highlightedSuggestionIndex+")", context);
		}else
		{
			_highlightedSuggestionIndex=-1;
			_highlightedSuggestionDiv=null
		}
		var supprSelection=false;
	  
		switch(_eventKeycode)
		{
			// cursor left, cursor right, page up, page down
			case 8:
			case 33:
			case 34:
			case 35:
			case 35:
			case 36:
			case 37:
			case 39:
			case 45:
			case 46:
				// on supprime la suggestion du texte utilisateur
				supprSelection=true;
				break;
				default:
				break
		}
		// si on a une suggestion (n-1) sélectionnée
		if(!supprSelection&&_highlightedSuggestionDiv)
		{
			$(_highlightedSuggestionDiv).attr("class", "divselector");
			var z;
		
			if(trouve) 
			{
				z=$("p.headline:eq("+_highlightedSuggestionIndex+")", context).text();
			} else 
			{
				z=valeurCourante;
			}
		
			if(z!=$(_inputField).attr("value"))
			{
				if($(_inputField).attr("value")!=valeurCourante) 
				{
					return;
				}
				// alert(_input);
				// si on peut créer des range dans le document
				if(_input.createTextRange||_input.setSelectionRange) 
				{
					$(_inputField).attr("value", z);
				}

				createTextRange(_input, valeurCourante, $(_inputField).attr("value").length);
			}
		}else
		{
			// sinon, plus aucune suggestion de sélectionnée
			_highlightedSuggestionIndex=-1;
		}
	}

	var _cursorUpDownPressed = null;

	// permet le blur du champ texte après que la touche haut/bas ai été pressé.
	// le focus est récupéré après traitement (via le timeout).
	function blurThenGetFocus(_inputField)
	{
		_cursorUpDownPressed=true;
		$(_inputField).blur();
		$(_inputField).focus()
		return
	}

	// Cache completement les choix de completion
	function hideSuggestDiv(context)
	{
		$(context).css("visibility", "hidden");
	}

	// Rends les choix de completion visibles
	function showSuggestDiv(context)
	{
		$(context).css("visibility", "visible");
	}

	// Change la suggestion en surbrillance
	function highlightNewValue(C, context, _inputField)
	{
		if(!suggestDivDivList||suggestDivRows<=0) 
		{
			return;
		}
		showSuggestDiv(context);
	  
		if(C>=suggestDivRows)
			C=suggestDivRows-1
	  
		if(_highlightedSuggestionIndex!=-1&&C!=_highlightedSuggestionIndex)
		{
			$(_highlightedSuggestionDiv).attr("class", "hentry");
			_highlightedSuggestionIndex=-1
		}
	  
		if(C<0)
		{
			_highlightedSuggestionIndex=-1;
			$(_inputField).focus();
			return
		}
	  
		_highlightedSuggestionIndex=C;
		_highlightedSuggestionDiv=$("div:eq("+C+")", context);
	  
		$(_highlightedSuggestionDiv).attr("class", "divselector");
		$(_inputField).attr("value", _highlightedSuggestionDiv.find("p.headline").text());
	}
	
	function createTextRange(_input, valeurCourante, longueur)
	{
		// on sélectionne la fin de la suggestion
		if(_input.createTextRange)
		{
			var t=_input.createTextRange();
			t.moveStart("character", valeurCourante.length); 
			t.select()
		}else if(_input.setSelectionRange)
		{
			_input.setSelectionRange(valeurCourante.length, longueur) 
		}
	}
				
})(jQuery);

