/* Autocomplete Class: creates a popup including search suggestions version 1.0.0
 *	+ Tested with prototype version 1.6.0.2
 *	+ json format:
 *		{"stem":stem, <index:[keyword, count]>}
 *		with
 *			- stem: user input used as a base for the suggestions
 *			- keyword: suggested keyword
 *			- index: relative ranking of the keyword
 *			- count: expected number of results
 *
 *		expl. {"stem":"p","0":["psp",6417],"1":["porter bag",5539],"2":["punk",31441],"3":["pokemon",2578],"4":["porter japan",8257],"5":["pet supplies",773],"6":["head porter",5985],"7":["jigsaw puzzles",3813],"8":["prada wallet",4090],"9":["fred perry",3452]}
 *	+ instantiation:
 *		searchAutocomplete = new Autocomplete('sp_k', 'searchForm', 'sp_st');
 *		with
 *			- 'sp_k' the id of the input box
 *			- 'searchForm' the id of the search form
 *			- 'sp_st' the id of the hidden input used for usage statistics
 *	+ localization:
 *		searchAutocomplete.setText({'results':'{0} results','close':'close'});
 *	+ define ajax call url:
 *		searchAutocomplete.setAjaxUrl('http://www.rakuten.co.jp/ajax/suggest.action');
 *	+ define the search query parameter:
 *		searchAutocomplete.setQueryParam('q');
 *--------------------------------------------------------------------------*/

Autocomplete = Class.create({
 	initialize: function(searchBox, searchForm, statsElt) {
		this.domain = window.location.hostname + (window.location.port ? ':' + window.location.port : '');
 		this.ajaxUrl = 'http://' + this.domain + '/borderless/suggest.action';
 		this.queryParam = 'k';
		this.closeText = 'close';
		this.resultsText = '{0} results';

		this.selectedindex = -1;
		this.elt = $(searchBox);
		this.searchForm = $(searchForm);
		this.statsElt = $(statsElt);

		this.rows = new Array();

		this.elt.setAttribute('autocomplete', 'off');
		this.popup = new Element('div', {id:'suggestBox', 'style':'display:none;'});
        this.elt.up().appendChild(this.popup);

		this.elt.observe('keyup', this.suggest.bind(this));
		this.elt.observe('keydown', this.navigate.bind(this));

		this.elt.autocomplete = 'off';
		this.previousKeyword = $F(searchBox);
		this.keywords = new Array();

		this.isVisible = false;
		this.isNavigating = false;
		this.count = 0;

		this.biggestRoot = '';

		this.eventElt = new Array();
		this.eventFct = new Array();
		this.eventFct2 = new Array();
		this.eventCloseElt = undefined;
	},

	/* Set the text values for display (close & results)*/
	setText: function(hash) {
	   for (var key in hash) {
			if (eval('this.' + key + 'Text')) {
				eval('this.' +key + 'Text=\'' + hash[key].toString()+'\'');
			}
		}
	},

	/* Set the URL for the Ajax call */
	setAjaxUrl: function(url) {
	   this.ajaxUrl = url;
	},

	/* Set the query parameter (default is 'k') */
	setQueryParam: function(param) {
	   this.queryParam = param;
	},

	/* Close the popup */
	close: function() {
		this.popup.hide();
		this.isVisible = false;
		this.elt.value = this.previousKeyword;
		this.statsElt.value = '';
	},

	/* Highlight suggestions on mouseover or keyboard navigation */
	highlight: function(index) {
		if (this.selectedindex > -1) {
			this.rows[this.selectedindex].removeClassName('selected');
		}
		this.selectedindex = index;
		this.rows[this.selectedindex].addClassName('selected');
	},

	/* Select a suggestion, filling the input with the selected keyword*/
	select: function(index) {
		this.elt.value = this.keywords[index];
		this.statsElt.value = '1';
		this.popup.hide();
		this.searchForm.submit();
	},

	/* Get the suggestions */
	suggest: function(event) {
		var key = event.which || event.keyCode;
		if (key == 13) {
			this.popup.hide();
			return;
		}
		if (this.isNavigating) {
			this.isNavigating = false;
			return;
		}
		var newValue = $F(this.elt);
		if (newValue && (key != 27)) {
			if (this.previousKeyword != newValue) {
				this.previousKeyword = newValue;

				if ((this.count == 0) && (this.biggestRoot.length > 0)
						&& (this.previousKeyword.indexOf(this.biggestRoot) == 0)
						&& (this.previousKeyword != this.biggestRoot)) {
					return;
				}

				var parent = this;

				//Ajax call
				new Ajax.Request(parent.ajaxUrl, {
					parameters: parent.queryParam + '=' + parent.previousKeyword,
					method:'get',
					onSuccess: function(transport){
						//Update the position
						parent.popup.clonePosition(parent.elt,{offsetTop:20,setWidth:false,setHeight:false});

						var json = transport.responseText.evalJSON();

						var new_table = new Element('table');
						var new_tbody = new Element('tbody');
						new_table.appendChild(new_tbody);

						parent.rows = new Array();
						parent.keywords = new Array();
						parent.count = 0;

						//Clear events from current popup
						for (var i = 0; i < parent.eventElt.length; i++) {
							parent.eventElt[i].stopObserving('mouseover', parent.eventFct[i]);
							parent.eventElt[i].stopObserving('click', parent.eventFct2[i]);
						}
						parent.eventElt = new Array();
						parent.eventFct = new Array();
						parent.eventFct2 = new Array();


						//Create the table with the suggestions
						var i = 0;
						while (json[i]) {
							var keyword = json[i][0];
							parent.count++;
							parent.keywords[i] = keyword;
							var stem = json['stem'];
							var highlighted;

							//Build the suggestion html with the stem highlighted
							if (keyword.indexOf(stem) == 0) {
								highlighted = '<span class="match">' + stem + '<\/span>' + keyword.substr(keyword.indexOf(stem) + stem.length);
							} else {
								var index = keyword.indexOf(' ' + stem);
								highlighted = keyword.substr(0, index) + '<span class="match"> ' + stem + '<\/span>' + keyword.substr(index + stem.length + 1);
							}
							var new_row = new Element('tr');
							parent.rows[i] = new_row;
							var new_cell = new Element('td', {'class':'word'});
							new_cell.update(highlighted);
							var new_cellCount = new Element('td', {'class':'count'});
							if (json[i][1] > 0) {
								new_cellCount.update(parent.resultsText.replace('{0}', json[i][1]));
							}
							new_row.appendChild(new_cell);
							new_row.appendChild(new_cellCount);
							new_tbody.appendChild(new_row);

							parent.eventElt[i] = new_row;
							parent.eventFct[i] = parent.highlight.bind(parent, i);
							parent.eventFct2[i] = parent.select.bind(parent, i);

							parent.eventElt[i].observe('mouseover', parent.eventFct[i]);
							parent.eventElt[i].observe('click', parent.eventFct2[i]);
							i++
						}

						if (parent.count == 0) {
							parent.popup.hide();
							parent.isVisible = false;
							this.statsElt.value = '';
						} else {
							parent.biggestRoot = parent.previousKeyword;

							//Create the last row with the close button
							var last_row = new Element('tr', {'class':'close'});
							var last_cell = new Element('td', {'colspan':'2'});
							var last_span = new Element('span').update(parent.closeText);

							new_tbody.appendChild(last_row);
							last_row.appendChild(last_cell);
							last_cell.appendChild(last_span);

							//Clean up previous listeners
							if (parent.eventCloseElt) {
								parent.eventCloseElt.stopObserving('click', parent.eventCloseFct);
							}

							parent.eventCloseElt = last_span;
							parent.eventCloseFct = parent.close.bind(parent);
							parent.eventCloseElt.observe('click', parent.eventCloseFct);

							//Show the popup
							parent.popup.update(new_table);
							parent.popup.show();
							parent.isVisible = true;


							//Activate keyboard navigation
							parent.selectedindex = -1;
						}
					}
				});
			}
		} else {
			this.popup.hide();
			this.isVisible = false;
			if (key == 27) {
				this.elt.value = this.previousKeyword;
				this.statsElt.value = '';
			} else {
				this.previousKeyword = '';
			}
		}
	},

	/* Navigate through the suggestion using UP and DOWN keys*/
  	navigate: function(event) {
  		var key = event.which || event.keyCode;
  		if (this.isVisible) {
			 //Down key
			if (key == 40) {
				this.isNavigating = true;
	 			if (this.selectedindex > -1) {
	 				this.rows[this.selectedindex].removeClassName('selected');
		 		}
		 		this.selectedindex++;
		 		if (this.rows[this.selectedindex]) {
		 			this.rows[this.selectedindex].addClassName('selected');
		 			this.elt.value = this.keywords[this.selectedindex];
		 			this.statsElt.value = '1';
		 		} else {
		 			this.selectedindex = -1;
		 			this.elt.value = this.previousKeyword;
		 			this.statsElt.value = '';
		 		}
		 	//UP key
		 	} else if (key == 38) {
		 		this.isNavigating = true;
		 		if (this.selectedindex > -1) {
		 			this.rows[this.selectedindex].removeClassName('selected');
		 			this.selectedindex--;
			 		if (this.selectedindex > -1) {
			 			this.rows[this.selectedindex].addClassName('selected');
			 			this.elt.value = this.keywords[this.selectedindex];
			 			this.statsElt.value = '1';
			 		}
		 		} else if (this.count > 0){
		 			this.selectedindex = this.count - 1;
		 			this.rows[this.selectedindex].addClassName('selected');
			 		this.elt.value = this.keywords[this.selectedindex];
			 		this.statsElt.value = '1';
		 		}  else {
		 			this.selectedindex = -1;
		 			this.elt.value = this.previousKeyword;
		 			this.statsElt.value = '';
		 		}
		 	}
	 	} else if ( ( (key == 40) || (key == 38) ) && this.count > 0) {
			this.popup.show();
			this.isVisible = true;
	 	}
	}
});