/*
Script: TextboxList.Autocomplete.js
	TextboxList Autocomplete plugin

	Authors:
		Guillermo Rauch
	
	Note:
		TextboxList is not priceless for commercial use. See <http://devthought.com/projects/mootools/textboxlist/>
		Purchase to remove this message.
*/

(function(){

TextboxList.Autocomplete = new Class({
	
	Implements: Options,
	
	options: {
		minLength: 1,
		maxResults: 11,
		insensitive: true,
		highlight: true,
		highlightSelector: null,
		mouseInteraction: true,
		onlyFromValues: false,
		queryRemote: false,
		remote: {
			url: '',
			param: 'q',
			extraParams: {},
			loadPlaceholder: '<div style="height:25px; line-height:25px;"><img src="/imgs/cms/loading.gif" width="20" /> Veuillez patienter...</div>'
		},
		method: 'nofilter',
		placeholder: 'Commencez à taper et voyez apparaitre nos suggestions'
	},
	
	initialize: function(textboxlist, options){
		this.setOptions(options);
		this.textboxlist = textboxlist;
		this.textboxlist.addEvent('bitEditableAdd', this.setupBit.bind(this), true)
			.addEvent('bitEditableFocus', this.search.bind(this), true)
			.addEvent('bitEditableBlur', this.hide.bind(this), true)
			.setOptions({bitsOptions: {editable: {addKeys:[], stopEnter: false}}});
		if (Browser.Engine.trident) this.textboxlist.setOptions({bitsOptions: {editable: {addOnBlur: false}}});
		if (this.textboxlist.options.unique){
			this.index = [];
			this.textboxlist.addEvent('bitBoxRemove', function(bit){
				if (bit.autoValue) this.index.erase(bit.autoValue);
			}.bind(this), true);
		}
		this.prefix = this.textboxlist.options.prefix + '-autocomplete';
		this.method = TextboxList.Autocomplete.Methods[this.options.method];
		this.container = new Element('div', {'class': this.prefix}).setStyle('width', this.textboxlist.container.getStyle('width')).inject(this.textboxlist.container);
		if ($chk(this.options.placeholder) || this.options.queryServer) 
			this.placeholder = new Element('div', {'class': this.prefix+'-placeholder'}).inject(this.container);		
		this.list = new Element('ul', {'class': this.prefix + '-results'}).inject(this.container);
		this.list.addEvent('click', function(ev){ ev.stop(); });
		this.values = this.results = this.searchValues = [];
		this.navigate = this.navigate.bind(this);
	},
	
	setValues: function(values){
		this.values = values;
	},
	
	setupBit: function(bit){
		bit.element.addEvent('keydown', this.navigate, true).addEvent('keyup', function(){ this.search(); }.bind(this), true);
	},
		
	search: function(bit){
		if (bit) this.currentInput = bit;
		if (!this.options.queryRemote && !this.values.length) return;
		var search = this.currentInput.getValue()[1];
		if (search.length < this.options.minLength) this.showPlaceholder(this.options.placeholder);
		if (search == this.currentSearch) return;
		this.currentSearch = search;
		this.list.setStyle('display', 'none');
		if (search.length < this.options.minLength) return;
		if (this.options.queryRemote){
			if (this.searchValues[search]){
				this.values = this.searchValues[search];
			} else {
				var data = this.options.remote.extraParams, that = this;
				if ($type(data) == 'function') data = data.run([], this);
				data[this.options.remote.param] = search;
				if (this.currentRequest) this.currentRequest.cancel();
				this.currentRequest = new Request.JSON({url: this.options.remote.url, data: data, onRequest: function(){
					that.showPlaceholder(that.options.remote.loadPlaceholder);
				}, onSuccess: function(data){
					that.searchValues[search] = data;
					that.values = data;
					that.showResults(search);
				}}).send();
			}
		} 
		if (this.values.length) this.showResults(search);
	},
	
	showResults: function(search){		
		var results = this.method.filter(this.values, search, this.options.insensitive, this.options.maxResults);
		if (this.index) results = results.filter(function(v){ return !this.index.contains(v); }, this);
		this.hidePlaceholder();
		if (!results.length) return;
		this.blur();
		this.list.empty().setStyle('display', 'block');
		results.each(function(r){ this.addResult(r, search, results.length); }, this);
		if (this.options.onlyFromValues) this.focusFirst();
		this.results = results;
	},	
	
	addResult: function(r, search, num){
	
		if(r[0] == (this.options.maxResults - 1) || (r[0] == (num - 1) && this.options.maxResults > num)) {
			var c = this.prefix + "-resultEnd";
			var classHighlight = this.prefix + "-highlightEnd";
		}else{
			var c = this.prefix + '-result';
			var classHighlight = this.prefix + '-highlight';
		}
		
		var element = new Element('li', {'class': c, 'html': $pick(r[3], r[1])}).store('textboxlist:auto:value', r);
		this.list.adopt(element);
		if (this.options.highlight) $$(this.options.highlightSelector ? element.getElements(this.options.highlightSelector) : element).each(function(el){
			if (el.get('html')) this.method.highlight(el, search, this.options.insensitive, classHighlight);
		}, this);
		if (this.options.mouseInteraction){
			element.setStyle('cursor', 'pointer').addEvents({
				mouseenter: function(){ this.focus(element); }.bind(this),
				mousedown: function(ev){
					ev.stop(); 
					$clear(this.hidetimer);
					this.doAdd = true;
				}.bind(this),
				mouseup: function(){
					if (this.doAdd){
						this.addCurrent();
						this.currentInput.focus();
						this.search();
						this.doAdd = false;
					}
				}.bind(this)
			});
			if (!this.options.onlyFromValues) element.addEvent('mouseleave', function(){ if (this.current == element) this.blur(); }.bind(this));	
		}
	},
	
	hide: function(ev){
		this.hidetimer = (function(){
			this.hidePlaceholder();
			this.list.setStyle('display', 'none');
			this.currentSearch = null;
		}).delay(Browser.Engine.trident ? 150 : 0, this);
	},
	
	showPlaceholder: function(customHTML){
		if (this.placeholder){
			this.placeholder.setStyle('display', 'block');	
			if (customHTML) this.placeholder.set('html', customHTML);
		}		
	},
	
	hidePlaceholder: function(){
		if (this.placeholder) this.placeholder.setStyle('display', 'none');
	},
	
	focus: function(element){
		if (!element) return this;
		this.blur();
		this.current = element.addClass(this.prefix + '-result-focus');
	},
	
	blur: function(){
		if (this.current){
			this.current.removeClass(this.prefix + '-result-focus');
			this.current = null;
		}
	},
	
	focusFirst: function(){
		return this.focus(this.list.getFirst());
	},
	
	focusRelative: function(dir){
		if (!this.current) return this;
		return this.focus(this.current['get' + dir.capitalize()]());
	},
	
	addCurrent: function(){
		var value = this.current.retrieve('textboxlist:auto:value');
		
		var href = this.current.getElement('span span a').get('href');
		this.blur();
		window.location.href = href;
		return true;
		/*
		var b = this.textboxlist.create('box', value.slice(0, 3));
		if (b){
			b.autoValue = value;
			if (this.index != null) this.index.push(value);
			this.currentInput.setValue([null, '', null]);
			b.inject($(this.currentInput), 'before');
		}
		this.blur();
		return this;
		*/
	},
	
	navigate: function(ev){
		switch (ev.code){
			case Event.Keys.up:			
				ev.stop();
				(!this.options.onlyFromValues && this.current && this.current == this.list.getFirst()) ? this.blur() : this.focusRelative('previous');
				break;
			case Event.Keys.down:			
				ev.stop();
				this.current ? this.focusRelative('next') : this.focusFirst();
				break;
			case Event.Keys.enter:
				ev.stop();
				if (this.current) this.addCurrent();
				else if (!this.options.onlyFromValues){
					var value = this.currentInput.getValue();				
					var b = this.textboxlist.create('box', value);
					if (b){
						b.inject($(this.currentInput), 'before');
						this.currentInput.setValue([null, '', null]);
					}
				}
		}
	}
	
});

TextboxList.Autocomplete.Methods = {
	
	standard: {
		filter: function(values, search, insensitive, max){
			var newvals = [], regexp = new RegExp('\\b' + search.escapeRegExp(), insensitive ? 'i' : '');
			for (var i = 0; i < values.length; i++){
				if (newvals.length === max) break;
				if (values[i][1].test(regexp)) newvals.push(values[i]);
			}
			return newvals;
		},
		
		highlight: function(element, search, insensitive, klass){
			var regex = new RegExp('(<[^>]*>)|(\\b'+ search.escapeRegExp() +')', insensitive ? 'ig' : 'g');
			return element.set('html', element.get('html').replace(regex, function(a, b, c){
				return (a.charAt(0) == '<') ? a : '<strong class="'+ klass +'">' + c + '</strong>'; 
			}));
		}
	},
	
	nofilter: {
		filter: function(values, search, insensitive, max){
			var newvals = [];
			for (var i = 0; i < values.length; i++){
				if (newvals.length === max) break;
				newvals.push(values[i]);
			}
			return newvals;
		},
		
		highlight: function(element, search, insensitive, klass){
			var regex = new RegExp('(<[^>]*>)|(\\b'+ search.escapeRegExp() +')', insensitive ? 'ig' : 'g');
			return element.set('html', element.get('html').replace(regex, function(a, b, c){
				return (a.charAt(0) == '<') ? a : '<strong class="'+ klass +'">' + c + '</strong>'; 
			}));
		}
	}
	
};

})();
