

/**
 * Suggest JavaScript, version 3.0
 * 2006 Christophe Clermont <cclermont@easyvoyage.fr>
 *
 *  works with Prototype framework
 */
(function() {
	var window = this,
			document = window.document,
			navigator = window.navigator,
			setTimeout = window.setTimeout,
			clearTimeout = window.clearTimeout,
			ev = window.ev,
			dom = ev && ev.dom,
			domElement = dom && dom.element;

	if (!ev) { throw 'suggest#<init>: Needs ev.core module!'; }

	function getRawTexte(texte) {
		texte = texte.replace('&quot;', '\"', 'g');
		texte = texte.replace('&amp;', '&', 'g');
		texte = texte.replace('&lt;', '<', 'g');
		texte = texte.replace('&gt;', '>', 'g');
		texte = texte.replace('&nbsp;', ' ', 'g');
		texte = texte.replace('&iexcl;', '¡', 'g');
		texte = texte.replace('&cent;', '¢', 'g');
		texte = texte.replace('&pound;', '£', 'g');
		texte = texte.replace('&curren;', '¤', 'g');
		texte = texte.replace('&yen;', '¥', 'g');
		texte = texte.replace('&brvbar;', '¦', 'g');
		texte = texte.replace('&sect;', '§', 'g');
		texte = texte.replace('&uml;', '¨', 'g');
		texte = texte.replace('&copy;', '©', 'g');
		texte = texte.replace('&ordf;', 'ª', 'g');
		texte = texte.replace('&laquo;', '«', 'g');
		texte = texte.replace('&not;', '¬', 'g');
		texte = texte.replace('&shy;', '-', 'g');
		texte = texte.replace('&reg;', '®', 'g');
		texte = texte.replace('&macr;', '¯', 'g');
		texte = texte.replace('&deg;', '°', 'g');
		texte = texte.replace('&plusmn;', '±', 'g');
		texte = texte.replace('&sup2;', '²', 'g');
		texte = texte.replace('&sup3;', '³', 'g');
		texte = texte.replace('&acute;', '´', 'g');
		texte = texte.replace('&micro;', 'µ', 'g');
		texte = texte.replace('&para;', '¶', 'g');
		texte = texte.replace('&middot;', '·', 'g');
		texte = texte.replace('&cedil;', '¸', 'g');
		texte = texte.replace('&sup1;', '¹', 'g');
		texte = texte.replace('&ordm;', 'º', 'g');
		texte = texte.replace('&raquo;', '»', 'g');
		texte = texte.replace('&frac14;', '¼', 'g');
		texte = texte.replace('&frac12;', '½', 'g');
		texte = texte.replace('&frac34;', '¾', 'g');
		texte = texte.replace('&iquest;', '¿', 'g');
		texte = texte.replace('&Agrave;', 'À', 'g');
		texte = texte.replace('&Aacute;', 'Á', 'g');
		texte = texte.replace('&Acirc;', 'Â', 'g');
		texte = texte.replace('&Atilde;', 'Ã', 'g');
		texte = texte.replace('&Auml;', 'Ä', 'g');
		texte = texte.replace('&Aring;', 'Å', 'g');
		texte = texte.replace('&AElig;', 'Æ', 'g');
		texte = texte.replace('&Ccedil;', 'Ç', 'g');
		texte = texte.replace('&Egrave;', 'È', 'g');
		texte = texte.replace('&Eacute;', 'É', 'g');
		texte = texte.replace('&Ecirc;', 'Ê', 'g');
		texte = texte.replace('&Euml;', 'Ë', 'g');
		texte = texte.replace('&Igrave;', 'Ì', 'g');
		texte = texte.replace('&Iacute;', 'Í', 'g');
		texte = texte.replace('&Icirc;', 'Î', 'g');
		texte = texte.replace('&Iuml;', 'Ï', 'g');
		texte = texte.replace('&ETH;', 'Ð', 'g');
		texte = texte.replace('&Ntilde;', 'Ñ', 'g');
		texte = texte.replace('&Ograve;', 'Ò', 'g');
		texte = texte.replace('&Oacute;', 'Ó', 'g');
		texte = texte.replace('&Ocirc;', 'Ô', 'g');
		texte = texte.replace('&Otilde;', 'Õ', 'g');
		texte = texte.replace('&Ouml;', 'Ö', 'g');
		texte = texte.replace('&times;', '×', 'g');
		texte = texte.replace('&Oslash;', 'Ø', 'g');
		texte = texte.replace('&Ugrave;', 'Ù', 'g');
		texte = texte.replace('&Uacute;', 'Ú', 'g');
		texte = texte.replace('&Ucirc;', 'Û', 'g');
		texte = texte.replace('&Uuml;', 'Ü', 'g');
		texte = texte.replace('&Yacute;', 'Ý', 'g');
		texte = texte.replace('&THORN;', 'Þ', 'g');
		texte = texte.replace('&szlig;', 'ß', 'g');
		texte = texte.replace('&agrave;', 'à', 'g');
		texte = texte.replace('&aacute;', 'á', 'g');
		texte = texte.replace('&acirc;', 'â', 'g');
		texte = texte.replace('&atilde;', 'ã', 'g');
		texte = texte.replace('&auml;', 'ä', 'g');
		texte = texte.replace('&aring;', 'å', 'g');
		texte = texte.replace('&aelig;', 'æ', 'g');
		texte = texte.replace('&ccedil;', 'ç', 'g');
		texte = texte.replace('&egrave;', 'è', 'g');
		texte = texte.replace('&eacute;', 'é', 'g');
		texte = texte.replace('&ecirc;', 'ê', 'g');
		texte = texte.replace('&euml;', 'ë', 'g');
		texte = texte.replace('&igrave;', 'ì', 'g');
		texte = texte.replace('&iacute;', 'í', 'g');
		texte = texte.replace('&icirc;', 'î', 'g');
		texte = texte.replace('&iuml;', 'ï', 'g');
		texte = texte.replace('&eth;', 'ð', 'g');
		texte = texte.replace('&ntilde;', 'ñ', 'g');
		texte = texte.replace('&ograve;', 'ò', 'g');
		texte = texte.replace('&oacute;', 'ó', 'g');
		texte = texte.replace('&ocirc;', 'ô', 'g');
		texte = texte.replace('&otilde;', 'õ', 'g');
		texte = texte.replace('&ouml;', 'ö', 'g');
		texte = texte.replace('&divide;', '÷', 'g');
		texte = texte.replace('&oslash;', 'ø', 'g');
		texte = texte.replace('&ugrave;', 'ù', 'g');
		texte = texte.replace('&uacute;', 'ú', 'g');
		texte = texte.replace('&ucirc;', 'û', 'g');
		texte = texte.replace('&uuml;', 'ü', 'g');
		texte = texte.replace('&yacute;', 'ý', 'g');
		texte = texte.replace('&thorn;', 'þ', 'g');
		return texte.replace('&yuml;', 'ÿ', 'g');
	}

	window.Element.collectTextNodesIgnoreClass = function(element, ignoreclass) {
		var children = domElement(element).childNodes;
		var text = '';
		var classtest = new RegExp('^([^ ]+ )*' + ignoreclass + '( [^ ]+)*$', 'i');

		for (var i = 0; i < children.length; i++) {
			if (children[i].nodeType == 3) {
				text += children[i].nodeValue;
			} else {
				if ((!children[i].className.match(classtest)) && children[i].hasChildNodes()) {
					text += window.Element.collectTextNodesIgnoreClass(children[i], ignoreclass);
				}
			}
		}

		return text;
	};

	window.Element.stringValue = function(element) {
		if (typeof element == 'string') {
			return element;
		}
		if (element.getName()) {
			return element.getName();
		}
		if (element.getAlias()) {
			return element.getAlias();
		}
		return '';
	};

	/*---------------------------------------------------------------------------*/

	window.Element.notAmbiguousString = function(element) {
		if (typeof element == 'string') {
			var entry = element.toLowerCase();
			entry = entry.replace(/[äâà]/gi, 'a');
			entry = entry.replace(/[ëêéè]/gi, 'e');
			entry = entry.replace(/[ïîì]/gi, 'i');
			entry = entry.replace(/[öôò]/gi, 'o');
			entry = entry.replace(/[üûù]/gi, 'u');
			entry = entry.replace(/[ñ]/gi, 'n');
			entry = entry.replace(/[^a-z0-9 ]/gi, '');
			entry = entry.replace(/saint /gi, 'st ');
			entry = entry.replace(/sainte /gi, 'ste ');
			//entry = entry.replace(/ch /gi,"charles ");
			//entry = entry.replace(/mt /gi,"mount ");
			return entry;
		}
		return '';
	};

	/*---------------------------------------------------------------------------*/

	window.FormSelect = window.Class.create();
	window.FormSelect.prototype = {
		initialize: function(id) {
			this.id = id;
			this.element = domElement(id);
			this.visible(domElement(id).length > 0);
		},

		length: function() {
			return this.element.length;
		},

		focus: function() {
			this.element.focus();
		},

		clear: function() {
			while (this.element.length > 0) {
				this.element.remove(this.element.length - 1);
			}
			this.visible(false);
		},

		disable: function() {
			this.element.disabled = true;
		},

		enable: function() {
			this.element.disabled = false;
		},

		visible: function(visible) {
			this.isVisible = visible;
			var visibility = 'hidden';
			if (visible) {
				visibility = 'inherit';
				try {
					window.VoitureEventShow();
				} catch (e) {
					if (window.console) {window.console.error('FormSelect#visible(): ' + e);}
				}
			} else {
				try {
					window.VoitureEventHide();
				} catch (e2) {
					if (window.console) {window.console.error('FormSelect#visible(): ' + e2);}
				}
			}

			this.element.style.visibility = visibility;
		},

		append: function(text, value) {
			if (!this.isVisible) {
				this.visible(true);
			}

			var option = document.createElement('option');
			option.text = text;
			option.value = value;

			try {
				this.element.add(option, null);
			} catch (e) {
				if (window.console) {window.console.error('FormSelect#append(): ' + e);}
				this.element.add(option); // IE
			}
		},

		remove: function(index) {
			this.element.remove(index);
		},

		getElement: function() {
			return this.element;
		}
	};

	/*---------------------------------------------------------------------------*/

	window.Suggest = {};
	window.Suggest.Common = function() {};
	window.Suggest.Common.prototype = {
		common_initialize: function(element, update, error, maxchoices) {
			this.element = domElement(element);
			this.update = domElement(update);
			this.error = domElement(error);
			this.error.style.visibility = 'hidden';
			this.has_focus = false;
			this.changed = false;
			this.active = false;
			this.index = 0;
			this.startIndex = 0; // indice de debut de la fenetre
			this.entry_count = 0;
			this.total_count = 0; // nombre total de suggestions
			this.exchange = false;

			if (this.setOptions) {
				this.setOptions();
			} else {
				this.options = {};
			}

			this.options.tokens = this.options.tokens || [];
			this.options.min_chars = this.options.min_chars || 3;

			// Villes options
			this.options.array = [];
			this.options.tempArray = [];
			this.options.asynchronous = true;
			this.options.onComplete = this.onComplete.bind(this);
			this.options.method = 'GET';
			this.iatasurl = '/base/xml/iatas/';
			this.citiesurl = '/base/xml/cities/';
			this.adrsurl = '/base/xml/adrs/';
			this.filesuffix = '.xml';
			this.entrylength = 3;
			this.oldentry = '';

			var agt = navigator.userAgent.toLowerCase();
			var is_ie = ((agt.indexOf('msie') != -1) && (agt.indexOf('opera') == -1));
			var defMaxchoices = is_ie ? 7 : 15;
			this.options.choices = maxchoices || defMaxchoices;

			this.options.onShow = this.options.onShow ||
					function(element, update) {
						/*
					if(!update.style.position || update.style.position=='absolute') {
						update.style.position = 'absolute';
						var offsets = window.Position.cumulativeOffset(element);
						update.style.left = offsets[0] + 'px';
						update.style.top  = (offsets[1] + element.offsetHeight) + 'px';
						update.style.width = element.offsetWidth + 'px';
					}
	 */
						window.Element.show(update);
					};

			this.options.onHide = this.options.onHide ||
					function(element, update) {
						window.Element.hide(update);
					};


			this.observer = null;
			// stocke le timer de la disparition de la fenetre de suggestions
			// permet d'être interrompu lors du changement de page
			this.clickobserver = null;

			window.Element.hide(this.update);

			window.Event.observe(this.element, 'blur', this.onBlur.bindAsEventListener(this));
			//window.Event.observe(this.element, "keypress", this.onKeyPress.bindAsEventListener(this));
			window.Event.observe(this.element, 'keydown', this.onKeyPress.bindAsEventListener(this));
			//window.Event.observe(this.element, "keyup", this.onKeyPress.bindAsEventListener(this));
		},

		show: function() {
			if (this.update.style.display == 'none') {
				this.options.onShow(this.element, this.update);
			}
			var agt = navigator.userAgent.toLowerCase(),
					is_ie = ((agt.indexOf('msie') != -1) && (agt.indexOf('opera') == -1)),
					tmpAfter;
			if (is_ie && !this.iefix) {
				tmpAfter = new window.Insertion.After(this.update,
						'<iframe id="' + this.update.id + '_iefix" ' +
						'style="display:none;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' +
						'src="javascript:false;" frameborder="0" scrolling="no"></iframe>');
				this.iefix = domElement(this.update.id + '_iefix');
			}
			if (this.iefix) {
				window.Position.cloneHeight(this.update, this.iefix);
				this.iefix.style.zIndex = 60000;
				this.update.style.zIndex = 61000;
				window.Element.show(this.iefix);
			}
		},

		hide: function() {
			if (!this.update.style.display) {
				this.options.onHide(this.element, this.update);
			}
			if (this.iefix) {
				window.Element.hide(this.iefix);
			}
		},

		startIndicator: function() {
			if (this.indicator) {
				window.Element.show(this.indicator);
			}
		},

		stopIndicator: function() {
			if (this.indicator) {
				window.Element.hide(this.indicator);
			}
		},

		onKeyPress: function(event) {
			if (this.active) {
				switch (event.keyCode) {
					case window.Event.KEY_TAB:
						this.select_entry();
						window.Event.stop(event);
						this.hide();
						this.active = false;
						return;
					case window.Event.KEY_RETURN:
						this.select_entry();
						window.Event.stop(event);
						this.hide();
						this.active = false;
						return;
					case window.Event.KEY_ESC:
						this.hide();
						this.active = false;
						return;
					case window.Event.KEY_LEFT:
						return;
					case window.Event.KEY_RIGHT:
						return;
					case window.Event.KEY_UP:
						this.mark_previous();
						this.render();
						if (navigator.appVersion.indexOf('AppleWebKit') > 0) {
							window.Event.stop(event);
						}
						return;
					case window.Event.KEY_DOWN:
						this.mark_next();
						this.render();
						if (navigator.appVersion.indexOf('AppleWebKit') > 0) {
							window.Event.stop(event);
						}
						return;
					default:
						break;
				}
			} else if (event.keyCode == window.Event.KEY_TAB || event.keyCode == window.Event.KEY_RETURN) {
				return;
			}

			this.changed = true;
			this.has_focus = true;

			if (this.observer) {
				clearTimeout(this.observer);
			}
			this.observer = setTimeout(this.onObserverEvent.bind(this), this.options.frequency * 1000);
		},

		onHover: function(event) {
			var element = window.Event.findElement(event, 'LI');
			if (element.autocompleteIndex >= 0 && this.index != element.autocompleteIndex) {
				this.index = element.autocompleteIndex;
				this.render();
			}

			window.Event.stop(event);
		},

		onClick: function(event) {
			var element = window.Event.findElement(event, 'LI');
			this.index = element.autocompleteIndex;
			this.select_entry();
			window.Event.stop(event);
		},

		onClickPage: function(event) {
			if (this.clickobserver) {
				clearTimeout(this.clickobserver);
			}
			this.element.focus();
			var element = window.Event.findElement(event, 'SPAN');
			if (element.autocompleteIndex == -1) {
				this.index = 0;
				var start = (element.innerHTML - 1) * this.options.choices;
				if (start != this.startIndex) {
					this.startIndex = (element.innerHTML - 1) * this.options.choices;
					this.has_focus = true;

					if (this.observer) {
						clearTimeout(this.observer);
					}
					this.observer = setTimeout(this.onObserverEvent.bind(this), this.options.frequency * 1000);
				}
			}
		},


		onBlur: function(event) {
			// needed to make click events working
			if (this.clickobserver) {
				clearTimeout(this.clickobserver);
			}
			this.clickobserver = setTimeout(this.hide.bind(this), 250);
			this.has_focus = false;
			this.active = false;
		},


		render: function() {
			var i;
			if (this.entry_count > 0) {
				if (this.hideError(this)) {
					this.hideError(this);
				}

				for (i = 0; i < this.entry_count; i++) {
					if (this.index === i) {
						window.Element.addClassName(this.get_entry(i), 'selected');
					}else {
						window.Element.removeClassName(this.get_entry(i), 'selected');
					}
				}

				if (this.has_focus) {
					//if(this.get_current_entry().scrollIntoView)
					//  this.get_current_entry().scrollIntoView(false);
					this.show();
					this.active = true;
				}
			}else {
				this.hide();
				if (this.showError(this)) {
					this.showError(this);
				}
			}
		},

		mark_previous: function() {
			if (this.index > 0) {
				this.index--;
			} else {
				var pageCount = Math.ceil(this.total_count / this.options.choices) - 1;
				if (pageCount > 0) {
					this.startIndex = (this.startIndex >= this.options.choices) ?
							this.startIndex - this.options.choices : (pageCount * this.options.choices);
					this.onObserverEvent();
				} else {
					this.startIndex = 0;
					this.index = this.entry_count - 1;
				}
			}
		},

		mark_next: function() {
			if (this.index < this.entry_count - 1) {
				this.index++;
			} else {
				var pageCount = Math.ceil(this.total_count / this.options.choices);
				this.startIndex = (this.startIndex <= this.total_count - this.options.choices) ? this.startIndex + this.options.choices : 0;
				this.onObserverEvent();
			}
		},

		get_entryObject: function(index) {
			return this.options.array[index];
		},

		get_current_entryObject: function() {
			return this.get_entryObject(this.index);
		},

		get_entry: function(index) {
			return this.update.firstChild.childNodes[index];
		},

		get_current_entry: function() {
			return this.get_entry(this.index);
		},

		select_entry: function() {
			this.active = false;
			this.updateElement(window.Element.stringValue(this.get_current_entryObject()));
			this.element.focus();
			if (this.selectEntry(this)) {
				this.selectEntry(this);
			}
		},

		updateElement: function(value) {
			value = getRawTexte(value);
			var last_token_pos = this.findLastToken();
			if (last_token_pos != -1) {
				var new_value = this.element.value.substr(0, last_token_pos + 1),
						whitespace = this.element.value.substr(last_token_pos + 1).match(/^\s+/);
				if (whitespace) {
					new_value += whitespace[0];
				}
				this.element.value = new_value + value;
			} else {
				this.element.value = value;
			}
		},

		updateChoices: function(choices) {
			if (!this.changed && this.has_focus) {
				this.update.innerHTML = choices;
				window.Element.cleanWhitespace(this.update);
				window.Element.cleanWhitespace(this.update.firstChild);

				if (this.update.firstChild && this.update.firstChild.childNodes) {
					this.entry_count = this.update.firstChild.childNodes.length;
					var entry, i;
					for (i = 0; i < this.entry_count; i++) {
						entry = this.get_entry(i);
						entry.autocompleteIndex = i;
						this.addObservers(entry);
					}
				} else {
					this.entry_count = 1;
					this.update.innerHTML = '<p class="error">Aucune entr&ecute;e ne correspond.</p>';
				}

				if (this.update.childNodes.length > 1) {
					// footer
					var count = this.update.childNodes[1].childNodes.length, j, element;
					for (j = 1; j < count; j++) {
						element = this.update.childNodes[1].childNodes[j];
						if (element.innerHTML) {
							element.autocompleteIndex = -1;
							window.Event.observe(element, 'click', this.onClickPage.bindAsEventListener(this));
						}
					}
				}

				this.stopIndicator();
				this.index = 0;
				this.render();
			}

		},

		// Fonction de mise a jour des suggestions
		getUpdatedChoices: function() {
			var entry = this.getEntry(),
					tmpEntry = window.Element.notAmbiguousString(entry),
					shortEntry = window.Element.notAmbiguousString(entry).substring(0, this.entrylength).replace(' ', ''),
					req;

			if (this.oldentry != tmpEntry) {
				this.startIndex = 0;
				this.total_count = 0;
			}

			if (this.oldentry.substring(0, this.entrylength).replace(' ', '') != shortEntry) {
				this.options.parameters = '';
				req = new window.Ajax.Request(this.citiesurl + shortEntry + this.filesuffix, this.options);

			} else {
				this.updateChoices(this.selector(this));
			}

			this.oldentry = tmpEntry;
			if (this.exchange) {
				var tmpName = this.hidden.name;
				this.hidden.name = this.element.name;
				this.element.name = tmpName;
				this.exchange = false;
			}
		},

		// Fonction de mise a jour du xml a importer
		onComplete: function(request) {
			this.parser(request.responseText, this);
			this.updateChoices(this.selector(this));
		},

		// Fonction de parsing de la page de suggestions
		parser: function(content, instance) {
			instance.options.tempArray = [];
			var suggestsList = content.split('<city>'),
					s, iata, name, country, hasEntry,
					agt, is_ie, nameSuggest, se1,
					cityAirports, keywordsList, k, keyword,
					a, airIata, airName, se2, airportKeywordList;
			for (s in suggestsList) {
				if (suggestsList.hasOwnProperty(s)) {
					try {
						iata = this.valueParser(suggestsList[s], 'iata');
						name = this.valueParser(suggestsList[s], 'name');
						country = this.valueParser(suggestsList[s], 'country');
						hasEntry = (this.valueParser(suggestsList[s], 'hasaddress') == 'true');
						if (iata && name && country) {
							// Nouvelle entree pour la saisie
							agt = navigator.userAgent.toLowerCase();
							is_ie = ((agt.indexOf('msie') != -1) && (agt.indexOf('opera') == -1));
							nameSuggest = name + ', '+ country;
							if (is_ie && nameSuggest.length > 25) {
								nameSuggest = name + ' '+ country.substring(country.length - 5, country.length);
							}
							se1 = new window.SuggestElement(iata, nameSuggest, [true, true, hasEntry]);
							se1.addKeyword(iata);
							se1.addKeyword(name);

							// Sous elements de saisie
							cityAirports = suggestsList[s].split('<airport>');

							// Les mots cles juste avant le premier sous element de saisie (mais pas dans les sous elements)
							keywordsList = cityAirports[0].split('<keyword>');
							for (k in keywordsList) {
								if (keywordsList.hasOwnProperty(k)) {
									keyword = this.valueParser('<keyword>'+ keywordsList[k], 'keyword');
									if (keyword) {
										se1.addKeyword(keyword);
									}
								}
							}

							// temp: retirer ce test -> voir perfs
							if (instance.getEngine() == 'mev') {
								cityAirports.shift();
								for (a in cityAirports) {
									if (cityAirports.hasOwnProperty(a)) {
										try {
											airIata = this.valueParser(cityAirports[a], 'iata');
											airName = this.valueParser(cityAirports[a], 'name');
											if (airIata && airName) {
												se2 = new window.SuggestElement(airIata, airName, [true, false, false]);
												if (se2.isOnline(instance.getEngine())) {
													se1.addKeyword(airIata);
													se1.addKeyword(airName);

													//se2.addKeywork(airIata);
													se2.addKeyword(airName);
													se2.addKeyword(name);

													airportKeywordList = cityAirports[a].split('<keyword>');
													for (k in airportKeywordList) {
														if (airportKeywordList.hasOwnProperty(k)) {
															keyword = this.valueParser('<keyword>'+ airportKeywordList[k], 'keyword');
															se1.addKeyword(keyword);
															se2.addKeyword(keyword);
														}
													}
													se1.addSubElement(se2);
												}
											}
										} catch (e) {
											if (window.console) {window.console.error('Suggest.Common#parser(): ' + e);}
										}
									}
								}
							}
							if (se1.isOnline(instance.getEngine())) {
								instance.options.tempArray.push(se1);
							}
						}
					} catch (e2) {
						if (window.console) {window.console.error('Suggest.Common#parser(): ' + e2);}
					}
				}
			}
		},

		valueParser: function(content, param) {
			var length = param.length + 2;
			var reDeb = new RegExp('<'+ param + '>');
			var reFin = new RegExp('<\/'+ param + '>');
			var debut = content.search(reDeb) + length;
			var fin = content.search(reFin);
			if (debut >= length && fin >= 0) {
				return content.substring(debut, fin);
			}
			return null;
		},

		// La fonction selector va calculer les champs a afficher
		selector: function(instance) {
			instance.options.array = [];
			var ret = [], // Beginning matches
					partial = [], // Inside matches
					entry = window.Element.notAmbiguousString(instance.getEntry()),
					count = 0,
					i, elem, subs, sub, s,
					page, pageNb, statusBar, start, stop;

			//for (i = 0; i < instance.options.tempArray.length && ret.length < instance.options.choices ; i++) {
			for (i = 0; i < instance.options.tempArray.length; i++) {
				elem = instance.options.tempArray[i];
				if (elem.matches(entry, instance)) {
					if (count >= instance.startIndex && ret.length < instance.options.choices) {
						ret.push('<li>' + elem.getName() + '</li>');
						instance.options.array.push(elem);
					}
					count++;
					// ajout des enfants de l'element
					subs = elem.getSubElements();
					for (s in subs) {
						if (subs.hasOwnProperty(s)) {
							sub = subs[s];
							//if(sub.matches(entry, instance)) {
							if (count >= instance.startIndex && ret.length < instance.options.choices) {
								ret.push("<li class='subelement'>" + sub.getName() + '</li>');
								// on cherche sur la ville, donc on stocke la ville pour les sous-elements
								instance.options.array.push(sub);
							}
							count++;
							//}
						}
					}
				}
			}
			instance.total_count = count - 1;
			page = Math.floor(instance.startIndex / instance.options.choices) + 1;
			pageNb = Math.ceil(count / instance.options.choices);
			statusBar = '';
			if (pageNb > 1) {
				statusBar = '<p>Pages : ';
				start = pageNb > 10 ? Math.max(page - 4, 1) : 1;
				stop = pageNb > 10 ? Math.min(page + 4, pageNb) : pageNb;
				for (i = start; i <= stop; i++) {
					statusBar += (i == page) ? '<span class="selected">' : '<span>';
					statusBar += i + '</span>&nbsp;';
				}
				statusBar += '</p>';
			}
			return '<ul class="dropDown">' + ret.join('') + '</ul>' + statusBar;
		},

		addObservers: function(element) {
			window.Event.observe(element, 'mouseover', this.onHover.bindAsEventListener(this));
			window.Event.observe(element, 'click', this.onClick.bindAsEventListener(this));
		},

		onObserverEvent: function() {
			this.changed = false;
			if (this.getEntry().length >= this.options.min_chars) {
				this.startIndicator();
				this.getUpdatedChoices();
			} else {
				this.active = false;
				this.hide();
			}
		},

		getEntry: function() {
			var token_pos = this.findLastToken(), ret;
			if (token_pos != -1) {
				ret = this.element.value.substr(token_pos + 1).replace(/^\s+/, '').replace(/\s+$/, '');
			} else {
				ret = this.element.value;
			}
			return (/\n/).test(ret) ? '' : ret;
		},

		findLastToken: function() {
			var last_token_pos = -1, i, this_token_pos;
			for (i = 0; i < this.options.tokens.length; i++) {
				this_token_pos = this.element.value.lastIndexOf(this.options.tokens[i]);
				if (this_token_pos > last_token_pos) {
					last_token_pos = this_token_pos;
				}
			}
			return last_token_pos;
		}
	};

	/*---------------------------------------------------------------------------*/

	window.Suggest.Voiture = window.Class.create();
	window.Suggest.Voiture.prototype = Object.extend(new window.Suggest.Common(), Object.extend(new window.Ajax.Base(), {
		initialize: function(element, update, select, error) {
			this.common_initialize(element, update, error);
			this.select = new window.FormSelect(select);

			var tmpName = 'pickUpSuggest';
			this.select.getElement().name = this.element.name;
			this.element.name = tmpName;
		},

		// Fonction de mise a jour des suggestions
		getUpdatedChoices: function() {
			this.select.clear();
			var entry = this.getEntry(),
					shortEntry = window.Element.notAmbiguousString(entry).substring(0, this.entrylength).replace(' ', ''),
					req;

			if (this.oldentry != shortEntry) {
				this.options.parameters = '';
				req = new window.Ajax.Request(this.citiesurl + shortEntry + this.filesuffix, this.options);
				this.oldentry = shortEntry;
			} else {
				this.updateChoices(this.selector(this));
			}
		},

		// Initialisation des parametres optionnels
		setOptions: function() {
			this.options = Object.extend({
				//choices: 10,
				partial_search: false,
				partial_chars: 2,
				ignore_case: true,
				full_search: false,

				addressParser: function(content, instance) {
					var suggestsList = content.split('<suggest>'),
							s, id, alias, type, tmpAdress;
					for (s in suggestsList) {
						if (suggestsList.hasOwnProperty(s)) {
							try {
								id = this.valueParser(suggestsList[s], 'id');
								alias = this.valueParser(suggestsList[s], 'alias');
								type = this.valueParser(suggestsList[s], 'type');
								if (id && alias && type) {
									tmpAdress = new window.Address(id, alias, type, instance.select);
								}
							} catch (e) {
								if (window.console) {window.console.error('Suggest.Voiture#setOptions(): ' + e);}
							}
						}
					}
				},

				valueParser: function(content, param) {
					var length = param.length + 2;
					var reDeb = new RegExp('<'+ param + '>');
					var reFin = new RegExp('<\/'+ param + '>');
					var debut = content.search(reDeb) + length;
					var fin = content.search(reFin);

					if (debut >= length && fin >= 0) {
						return content.substring(debut, fin);
					}
					return null;
				}

			}, {});
		},

		selectEntry: function(instance) {
			instance.select.clear();
			instance.select.disable();
			instance.select.append('Recherche des adresses ...', '');

			var obj = instance.get_current_entryObject(),
					iata = obj.getId(),
					adOptions,
					req;

			if (iata) {
				adOptions = {
					onComplete: instance.onCompleteAddress.bind(instance),
					parameters: '',
					asynchronous: true,
					method: 'GET'
				};
				req = new window.Ajax.Request(instance.adrsurl + iata.toLowerCase() + instance.filesuffix, adOptions);
			}
		},

		onCompleteAddress: function(request) {
			var txt = request.responseText;
			this.select.clear();
			if (txt.length > 1) {
				this.select.append('Selectionnez une adresse ...', '');
				this.options.addressParser(getRawTexte(txt), this);

				if (this.select.length() < 3) {
					this.select.remove(0);
				}
				this.select.enable();
				this.select.focus();
			}
		},

		hideError: function(instance) {
			instance.error.style.visibility = 'hidden';
		},

		showError: function(instance) {
			instance.error.style.left = instance.select.getElement().style.left;
			instance.error.style.top = instance.select.getElement().style.top;
			instance.error.style.zIndex = 50;
			instance.error.style.visibility = 'inherit';
		},

		getEngine: function() {
			return 'mec';
		}
	}));

	/*---------------------------------------------------------------------------*/

	window.Address = window.Class.create();
	window.Address.prototype = {
		initialize: function(id, alias, type, select) {
			this.id = id;
			this.alias = alias;
			this.type = type;
			this.updateSelect(select);
		},

		updateSelect: function(select) {
			select.append(this.alias, this.id);
		}
	};

	/*---------------------------------------------------------------------------*/
	/* Element utilise pour stocker une suggestion                               */
	/*                                                                           */
	/* Le tableau online permet de definir pour chaque moteur si la suggestion   */
	/*  doit etre montree (true) ou non (false)                                  */

	window.SuggestElement = window.Class.create();
	window.SuggestElement.prototype = {
		initialize: function(id, name, online) {
			this.id = id;
			this.name = name;
			this.online = online || [];
			this.keywords = [];
			this.subelements = [];
		},

		getId: function() {
			return this.id;
		},

		getName: function() {
			return this.name;
		},

		// fonction interne pour detecter le type
		getType: function() {
			return true;
		},

		// for each meta engine we define a marker
		isOnline: function(engine) {
			if (typeof engine === 'string') {
				if (engine === 'mev' && this.online.length > 0) {
					return this.online[0];
				}
				if (engine === 'meh' && this.online.length > 1) {
					return this.online[1];
				}
				if (engine === 'mec' && this.online.length > 2) {
					return this.online[2];
				}
			}
			return false;
		},

		addKeyword: function(keyword) {
			var key = window.Element.notAmbiguousString(keyword);
			if (key) {
				this.keywords.push(key);
			}
		},

		getKeywords: function() {
			return this.keywords;
		},

		addSubElement: function(subelement) {
			if (subelement.getType()) {
				this.subelements.push(subelement);
			}
		},

		getSubElements: function() {
			return this.subelements;
		},

		matches: function(suggest, instance) {
			/* les elements hors ligne ne sont pas ajoutes au moment
					du parsing -> voir methode Suggest.Common#parser
			if(!this.isOnline(instance.getEngine()))
				return false;
			 */
			var key, found_pos;
			for (key in this.keywords) {
				if (this.keywords.hasOwnProperty(key)) {
					found_pos = this.keywords[key].indexOf(suggest);
					if (!found_pos) {
						return true;
					}
					if (suggest.length >= instance.options.partial_chars && instance.options.partial_search && found_pos >= 0) {
						if (!instance.options.full_search && !(/\s/).test(this.keywords[key].substr(found_pos - 1, 1))) {
							continue;
						}
						return true;
					}
				}
			}
			return (window.Element.notAmbiguousString(this.getName()).indexOf(suggest) >= 0);
		}
	};

	/*---------------------------------------------------------------------------*/

	window.Suggest.Vol = window.Class.create();
	window.Suggest.Vol.prototype = Object.extend(new window.Suggest.Common(), Object.extend(new window.Ajax.Base(), {
		initialize: function(element, update, hidden, error, nextElement, maxchoices) {
			this.common_initialize(element, update, error, maxchoices);
			this.hidden = domElement(hidden);
			this.nextElement = nextElement;

			this.exchange = (this.hidden.value.length > 0);
			if (this.exchange) {
				var tmpName = this.hidden.name;
				this.hidden.name = this.element.name;
				this.element.name = tmpName;
			}
		},

		// Initialisation des parametres optionnels
		setOptions: function() {
			this.options = Object.extend({
				//choices: 16,
				partial_search: false,
				partial_chars: 2,
				ignore_case: true,
				full_search: false

			}, {});
		},

		selectEntry: function(instance) {
			var obj = instance.get_current_entryObject();
			var iata = obj.getId();
			if (iata) {
				instance.hidden.value = iata;
			}
			if (!instance.exchange) {
				var tmpName = instance.hidden.name;
				instance.hidden.name = instance.element.name;
				instance.element.name = tmpName;
			}

			if (instance.nextElement) {
				var elt = domElement(instance.nextElement),
						eltSuggest = domElement(instance.nextElement + 'Suggest');
				if (elt) {
					elt.focus();
				}
				if (eltSuggest) {
					eltSuggest.focus();
				}
			}
		},

		hideError: function(instance) {
			instance.error.style.visibility = 'hidden';
		},

		showError: function(instance) {
			instance.error.style.zIndex = 50;
			instance.error.style.visibility = 'inherit';
		},

		getEngine: function() {
			return 'mev';
		}

	}));

	/*---------------------------------------------------------------------------*/

	window.Suggest.Hotel = window.Class.create();
	window.Suggest.Hotel.prototype = Object.extend(new window.Suggest.Common(), Object.extend(new window.Ajax.Base(), {
		initialize: function(element, update, hidden, error, nextElement, maxchoices) {
			this.common_initialize(element, update, error, maxchoices);
			this.hidden = domElement(hidden);

			this.exchange = (this.hidden.value.length > 0);
			if (this.exchange) {
				var tmpName = this.hidden.name;
				this.hidden.name = this.element.name;
				this.element.name = tmpName;
			}
		},

		// Initialisation des parametres optionnels
		setOptions: function() {
			this.options = Object.extend({
				//choices: 16,
				partial_search: false,
				partial_chars: 2,
				ignore_case: true,
				full_search: false

			}, {});
		},

		selectEntry: function(instance) {
			var obj = instance.get_current_entryObject(),
					iata = obj.getId();
			if (iata) {
				instance.hidden.value = iata;
			}

			if (!instance.exchange) {
				var tmpName = instance.hidden.name;
				instance.hidden.name = instance.element.name;
				instance.element.name = tmpName;
			}
		},

		hideError: function(instance) {
			instance.error.style.visibility = 'hidden';
		},

		showError: function(instance) {
			instance.error.style.zIndex = 50;
			instance.error.style.visibility = 'inherit';
		},

		getEngine: function() {
			return 'meh';
		}
	}));
}());

