
(function(global, doc, $){
    
    var AutoSuggest = function(field, settings) {
    
    	if(!(this instanceof AutoSuggest)) {
    		return new AutoSuggest(field, settings);
    	}
    
    	this.field = field;
    	this.data = [];
    	this.cache = {};
    	this.selected;
    	this.active = false;
    	this.defaultSettings = {
    		className : "autosuggest",
    		selectFirst : false,
    		size : 6,
    		url : null,
    		selectMandatory: true
    	};
    	this.settings = $.extend({}, this.defaultSettings, settings);
    	this.disableDefault(field);
    	
    	if(this.settings.url) {
    		this.loadData(this.settings.url);
    	}
    
    	var app = this;
    	
    	field = $(field);
    
    	$(document).keyup(function(e){
    		if(e.keyCode === 9) {
    			app.toggleBox(e);
    		}
    	});
    	
    	if(this.settings.selectMandatory) {
	    	field.closest("form").submit(function(e){
	    		return !app.active;
	    	});
    	}
    	
    	
    	field.closest("form").mousedown(function(e){
    		var n = e.target;
    		if(/input/i.test(n.nodeName) && /submit|image/i.test(n.type)) {
    			app.active = false;
    			//this.submit();
    		}
    	});
    	
    
    	field.keydown(function(e){
    		if(e.keyCode === 13) {
    			if(!!app.selected) {
    				field.val($(app.selected).text());
    				app.getBox().hide();
    				setTimeout(function(){
    					app.active = false;
    				},10);
    			}
    		}
    	});
    
    	field.keyup(function(e){
    		if(e.keyCode !== 13) {
    			app.toggleSelect(e);
    			app.toggleBox(e);
    		}
    	});
    
    	this.getBox().click(function(e){
    		if(/li/i.test(e.target.nodeName)) {
    			field.val(e.target.innerHTML);
    			app.toggleBox(e);
    			field.focus();
    		}
    	}).mouseover(function(e){
    		if(/li/i.test(e.target.nodeName)) {
    			$(app.selected).removeClass("selected");
    			$(e.target).addClass("selected");
    			app.selected = e.target;
    		}
    	}).mouseup(function(){
    		field.focus();
    	});
    }
    
    AutoSuggest.prototype = {
    
    	disableDefault : function() {
    		this.field.setAttribute('autocomplete', 'off');
    		this.field.setAttribute('multiline', 'true');
    	},
    
    	getBox : function() {
    		if(!AutoSuggest.box) {
    			AutoSuggest.box = document.body.appendChild(document.createElement("ul"));
    		}
    		AutoSuggest.box.className = this.settings.className;
    		return $(AutoSuggest.box);
    	},
    
    	toggleSelect : function(e) {
    		if(e.keyCode === 38 || e.keyCode === 40) {
    			if(!this.selected) {
    				this.selected = $("li:first",this.getBox()).addClass("selected");
    			} else {
    				var sel = $(this.selected).removeClass("selected");
    				if(e.keyCode === 40) {
    					sel = $(sel).next("li")[0] || $("li:first", this.getBox())[0];
    				} else if (e.keyCode === 38) {
    					sel = $(sel).prev("li")[0] || $("li:last", this.getBox())[0];
    				}
    				this.selected = $(sel).addClass("selected");
    			}
    			var box = this.getBox();
    			var h = box.height();
    			var y = this.selected.position().top + this.selected.height();
    			var delta = (y - h) + box.scrollTop();
    			if(delta > 0) {
    				box.scrollTop(delta);
    			} else {
    				box.scrollTop(0);
    			}
    
    		}
    	},
    
    	getHTML : function() {
    		var value = this.field.value.toLowerCase();
    		if(!this.cache[value] && value) {
    			var m = $.grep(this.data, function(n, i) {
    				return new RegExp("^"+value, "i").test(n);
    			});
    			this.cache[value] = m.length ? "<li>" + m.sort().join("</li><li>") + "</li>" : "";
    		}
    		return this.cache[value];
    	},
    
    	fillBox : function(html) {
    		this.getBox().html(html);
    		if(this.settings.selectFirst) {
    			this.selected = $("li:first",this.getBox()).addClass("selected");
    		}
    	},
    
    	loadData : function(url) {
    		var self = this;
    		$.getJSON(url,function(JSON){
    			if(self.settings.dataMapping && JSON[self.settings.dataMapping]){
    				JSON = JSON[self.settings.dataMapping];
    			}
    			self.data = JSON;
    		});
    	},
    
    	toggleBox : function(e) {
    		if(e.keyCode === 38 || e.keyCode === 40) {
    			return;
    		}
    		var html = this.getHTML();
    		if((e.target == this.field || e.target == AutoSuggest.box) && html) {
    			this.active = true;
    			this.fillBox(html);
    			this.getBox().show();
    			this.positionBox();
    		} else {
    			this.active = false;
    			$(this.selected).removeClass("selected");
    			this.selected = null;
    			this.getBox().hide();
    		}
    	},
    
    	positionBox : function() {
    		var pos = $(this.field).offset();
    		var lis = $("li",this.getBox());
    		var lh = lis.outerHeight();
    		var h =  lh * lis.length;
    		$(AutoSuggest.box).css({
    			width : $(this.field).outerWidth()-1,
    			top : pos.top + $(this.field).outerHeight()-1,
    			left : pos.left,
    			height : Math.min(h, (lh * this.settings.size))
    		});
    	}
    }
    
    $.fn.autoSuggest = function(options) {
    	this.each(function(){
    		if(!$(this).data("AUTOCOMPLETE")) {
    			$(this).data("AUTOCOMPLETE", new AutoSuggest(this, options));
    		}
    	});
    }
    
}(this, document.documentElement, jQuery));
