/**
	SmartTextBox jQuery plugin
	@version 1.2.0-rc1
	@author Pierre Gayvallet
	@see http://wayofspark.com/projects/smarttextbox
	@copyright (c) 2009-2010 Pierre Gayvallet - GPL license.
*/

(function($){

$.SmartTextBox = {};
var SmartTextBox = $.SmartTextBox;

/**
	SmartTextBox.BaseClass
	Allow pseudo inheritance in javascript objects
*/
SmartTextBox.BaseClass = function(o){};
SmartTextBox.BaseClass.prototype.construct = function(){};
SmartTextBox.BaseClass.extend = function(def) {
  var classDef = function() {
      if (arguments[0] !== SmartTextBox.BaseClass) { this.construct.apply(this, arguments); }
  };
  var proto = new this(SmartTextBox.BaseClass);
  var superClass = this.prototype;
  for(var n in def) {
      var item = def[n];
      proto[n] = item;
  }
  classDef.prototype = proto;
  classDef.extend = this.extend;      
  return classDef;
};

/**
	SmartTextBox.SmartTextBox
	Manager Class for a TextBox
*/
SmartTextBox.SmartTextBox = SmartTextBox.BaseClass.extend({
	_options : {
		// Autocomplete configuration
		autocomplete : true,  	 			// on/off
		minSearchLength : 2,       			// min length to type to receive suggestions
		maxResults : 10,     			    // max number of results to display
		caseSensitive : false,  		    // case sensitive search
		highlight : true,				    // highlight autocomplete search in results
		fullSearch : true,                  // search at start of values or in all the string
		autocompleteValues : [],
        autocompleteUrl : null,
		placeholder : "Please start typing to receive suggestions", // search placeholder text
		// SmartTextBox configuration
		onlyAutocomplete: false,            // can only insert from autocomplete values
        uniqueValues : true,                // values can only be added once  
		submitKeys : [13],					// list of keys to save an input to box
		submitChars : [ ";", ","],			// list of chars to save an input to box
		separator : ";",					// separator for serialize method
		updateOriginal : true,				// update the original input value when change
		// Events handling
		onElementAdd : 		null,
		onElementRemove : 	null,
		onElementFocus : 	null,
		onElementBlur : 	null,
		// Misc
		hideEmptyInputs : true,               // hide empty inputs
        editOnFocus : false,                  // edit box on focus ( click or <- -> )
        editOnDoubleClick : false,            // edit box on doubleClick
		// CSS
		containerClass : "smartTextBox",  // base CSS class for containers
		// Debug configuration
		debug : false						// debug mode - show logs in firebug console
	},
	construct : function(el, o){
		this.options = {};
		var o = o || {};
		$.extend(this.options, this._options);
		$.extend(this.options, o);
		
		var self = this;
		this.original = $(el);
		this.focused = false;
		this.elements = [];
		
		$(this.original).data("SmartTextBox", this);
		this.original.hide();
		
		this.container = $("<div></div>")
			.addClass(this.options.containerClass)
			 .click(function(e){
				if( (self.container.index(e.target)>-1 || self.list.index(e.target)>-1) &&
					(!self.currentFocus || self.currentFocus != self.elements[self.elements.length-1]))
						self.focusLast();												
			 }).insertAfter(this.original);
										 
		this.list = $("<ul></ul>")
			.addClass(this.options.containerClass + "-items")
			.appendTo(this.container);
								  
		$(document).keydown(function(e){
						self.onKeyDown(e)
					})
				   .click(function(e){
						if(!self.currentFocus) return;
						if (e.target.className.indexOf(self.options.containerClass)>-1){
							if(self.container.index(e.target)>-1) return;
							var parent = $(e.target).parents('.' + self.options.containerClass);
							if (parent.index(self.container)>-1) return;
						}
						self.blur();
					});
		
		if(this.options.autocomplete){
			this.autocomplete = new SmartTextBox.AutoComplete(this, this.options);
		}
		
		this.add("input");
		this.loadOriginal();
	},
	setValues : function(values){
		this.removeAll();
		var self = this;
		$.each(values, function(i, el){
			self.addBox(el);
		});
	},
	removeAll : function() {
		var toRemove = [];
		$.each(this.elements, function(){
			if(this.is('box')) {
				toRemove.push(this);
			}
		})
		$.each(toRemove, function(){
			this.remove();
		})
	},
	setAutocompleteValues : function(values){
		if(!this.options.autocomplete) return;
		if(typeof values == "string") {
			this.autocomplete.setValues(values.split(this.options.separator));
		} else {
			this.autocomplete.setValues(values);
		}
	},
	onKeyDown : function(event){
		if(!this.currentFocus) return;
		
		var caret = this.currentFocus.is('input') ? this.currentFocus.getCaret() : null;
		var value = this.currentFocus.getValue();
		var custom = (this.currentFocus.is('input') && this.currentFocus.isSelected());
		
		// bug on FF making elem to loose focus... this fix it
		this.currentFocus.valueContainer.focus();
		
		switch(event.keyCode){
			case 37: // <-
				if( this.currentFocus.is('box') || ((caret == 0 || !value.length) && !custom) ){
					event.preventDefault();
					this.focusRelative('previous');
				}
				break;
			case 39: // ->
				if( this.currentFocus.is('box') || (caret == value.length && !custom)){
					event.preventDefault();
					this.focusRelative('next');
				}
				break;
			case 8: // backspace
				if(this.currentFocus.is('box')){
					this.currentFocus.remove();
					event.preventDefault();
				} else if ((caret == 0 || !value.length) && !custom) {
					this.focusRelative('previous');
					event.preventDefault();
				}		
				break;
			case 27: // escape
				this.blur();
				break;
		}
	},
	create : function(type, value, options){
		var n;
		if(type=="box"){
			n = new SmartTextBox.BoxElement(value, this, options);
		}
		else if(type=="input"){
			n = new SmartTextBox.InputElement(value, this, options);
		}
		else{
			// handle case - do nothing for now.
		}
		return n;
	},
	getElementIndex : function(elem){
		return $(this.elements).index(elem);
	},
	getElement : function(index){
		if((index<0) || (index>this.elements.length-1)) return null;
		return this.elements[index];
	},
	getElementsByType : function(type) {
		var els = [];
		$.each(this.elements, function(){
			if(this.is(type)) {
				els.push(this);
			}
		});
		return els;
	},
	getLastBox : function(){
		for(var i=this.elements.length-1;i>-1;i--){
			if(this.elements[i].is('box')) return this.elements[i];
		}
		return null;
	},
	insertElement : function(element, index){
		var i = (arguments.length > 1) ? index : this.elements.length;
		this.elements.splice(i,0, element);
	},
	add : function(type, value, relTo, relPos){
		var type = type || "box";
		var value = value || "";
		var relPos = relPos || "after"
		var relTo = relTo || this.getLastBox();
		
		var n = this.create(type, value);
		var i = 0;
		if(relTo) i = this.getElementIndex(relTo) + ((relPos=='after') ? 1 : 0);

		this.insertElement(n, i);
		relTo ? n.inject(relTo, relPos) : n.inject();
		return n;
	},
	addBox : function(value, relTo, relPos){
		if(!$.trim(value).length) return;
		return this.add("box", $.trim(value), relTo, relPos);
	},
	removeBox : function(valueOrIndex) {
		var found = null;
		$.each(this.elements, function(){
			if(this.getValue()==valueOrIndex) {
				found = this;
				return false;
			}
		});
		if(!found&&!isNaN(valueOrIndex)) {
			var boxes = this.getElementsByType("box");
			if(boxes.length>valueOrIndex) {
				found = boxes[valueOrIndex];
			}
		}
		if(found) {
			found.remove();
		}
	},
	handleElementEvent : function(type, elem, options){
		switch(type){
			case "add":
				this.onElementAdd(elem, options);
				break;
			case "focus":
				this.onElementFocus(elem, options);
				break;
			case "blur" :
				this.onElementBlur(elem, options);
				break;
			case "remove" :
				this.onElementRemove(elem, options);
				break;
		}
	},
	onElementAdd : function(elem){
		if(this.autocomplete) this.autocomplete.setupElement(elem);
		if(elem.is('box')){
			var i = this.getElementIndex(elem);
			var prev = this.getElement(i-1);
			if((prev && prev.is('box')) || (!prev)){
				var b = this.add("input", "", elem, "before");
				if(this.options.hideEmptyInputs) b.hide();
			}
			if(this.options.updateOriginal) this.updateOriginal();
			if(this.options.onElementAdd) this.options.onElementAdd(elem, this);
		}
	},
	onElementFocus : function(elem){
		if(this.currentFocus==elem) return;
		if(this.currentFocus) this.currentFocus.blur();
		this.currentFocus = elem;
		if(this.currentFocus.is('input')&&this.autocomplete) this.autocomplete.search(this.currentFocus);
		if(this.options.onElementFocus) this.options.onElementFocus(elem, this);
	},
	onElementBlur : function(elem){
		if(this.currentFocus==elem){
			this.currentFocus = null;
			if(this.autocomplete) this.autocomplete.hide();
			if(this.options.onElementBlur) this.options.onElementBlur(elem, this);
		}
	},
	onElementRemove : function(elem){
		var removedIndex = this.getElementIndex(elem);
		this.focusRelative((removedIndex==this.elements.length-1) ? 'previous' : 'next', elem);
		this.elements.splice(removedIndex, 1);
		if(this.getElement(removedIndex)&&this.getElement(removedIndex).is("input")) {
			if((this.getElement(removedIndex+1)&&this.getElement(removedIndex+1).is("input"))||
			   (this.getElement(removedIndex-1)&&this.getElement(removedIndex-1).is("input"))) {
				this.getElement(removedIndex).remove();
			}
		}
		if(this.options.updateOriginal) this.updateOriginal();
		if(this.elements.length==1&&this.elements[0].is("input")) {
			this.elements[0].show();
		}
		if(this.options.onElementRemove) this.options.onElementRemove(elem, this);
	},
	blur: function(){
		$.each(this.elements, function(i, el){
			el.blur();
		});
	},
	focusLast : function(){
		this.elements[this.elements.length-1].focus();
	},
	focusRelative : function(dir, from){
		var b = from || this.currentFocus;
		var idx = $(this.elements).index(b);
		idx = (dir == 'previous') ? idx-1 : idx+1;
		if(idx<0) return; //idx = 0;
		if(idx>=this.elements.length) return; //idx = this.elements.length-1;
		this.elements[idx].focus();
		if(this.elements[idx].is("input")){
			(dir == 'previous') ? this.elements[idx].setCaretToEnd() : this.elements[idx].setCaretToStart();
		}
	},
	getBoxValues : function() {
        var values = [];
		$.each(this.elements, function(i, el){
            if(!this.is('box')) return;
            values.push(this.getValue());
        });
		return values;
	},
	containsValue : function(value) {
		return ($(this.getBoxValues()).index(value)>-1);
	},
	serialize : function(){
		return this.getBoxValues().join(this.options.separator);
	},
	updateOriginal : function(){
		this.original.attr('value', this.serialize());
	},
	loadOriginal : function(){
		this.load(this.original.attr('value'));
	},
	load : function(values){
		if(typeof values == "string") {
			var values = values.split(this.options.separator);
		} 
		this.setValues(values);
	}
});

/** 
	SmartTextBox.AutoComplete
*/
SmartTextBox.AutoComplete = SmartTextBox.BaseClass.extend({
	_options : {
	},
	construct : function(stb, o){
		this.stb = stb;
		this.currentValue = "";
		this.options = {};
		$.extend(this.options, this._options);
		$.extend(this.options, o || {});
		if (this.options.autocompleteUrl) {
			this.ajaxLoad(this.options.autocompleteUrl);
		} else {
			this.setValues(this.options.autocompleteValues);
		}
		var self = this;
		this.baseClass = this.stb.options.containerClass + "-autocomplete";
		
		this.container = $("<div></div>")
							.addClass(this.baseClass)
							.css("width", this.stb.container.width())
							.appendTo(this.stb.container);
		this.placeHolder = $("<div></div>")
								.addClass(this.baseClass + "-placeholder")
								.html(this.options.placeholder)
								.appendTo(this.container)
								.hide();
		this.resultList = $("<div></div>")
								.addClass(this.baseClass + "-results")
								.appendTo(this.container)
								.hide();
	},
	ajaxLoad : function(url) {
	   var self = this;
	   $.get(url, {}, function(response){
	       var values = response.values;
		   self.setValues(values);
	   }, 'json');
	},
	setValues : function(values){
		this.values = values || [];
	},
	getValues : function(){
		return this.values || [];
	},
	setupElement : function(element){
		if(!element.is('input')) return;
		var self = this;
		element.valueContainer
					.keydown(function(e){ self.navigate(e);  })
					.keyup(function(){ self.search(); });
	},
	navigate : function(e){
		switch(e.keyCode){
			case 38: // arrowUp
				(this.currentSelection && this.currentSelection.prev().length) ? this.focusRelative('prev') : this.blur(); 
				break;
			case 40: // arrowDown
				this.currentSelection ? this.focusRelative('next') : this.focusFirst();
				break;
			case 13: // enter
				if(this.currentSelection){
					e.stopPropagation();
					this.addCurrent();
					this.currentElem.focus();
					this.search();
				}
				break;
		};
	},
	search : function(elem){		
		if(elem) this.currentElem = elem;
		if (!this.getValues().length) return;
		window.clearTimeout(this.hidetimer);
		var value = this.currentElem.getValue();
		if(value.length<this.options.minSearchLength) this.showPlaceHolder();
		if (value == this.currentValue) return;
		this.currentValue = value;
		this.hideResultList();
		if(value.length<this.options.minSearchLength) return;
		this.showResults(value);
	},
	showPlaceHolder : function(){
		if(this.placeHolder) this.placeHolder.show();
	},
	hidePlaceHolder : function(){
		if(this.placeHolder) this.placeHolder.hide();
	},
	showResultList : function(){
		this.resultList.show();
	},
	hideResultList : function(){
		this.resultList.hide();
	},
	hide : function(){
		var self = this;
		this.hidetimer = window.setTimeout(function(){
			self.hidePlaceHolder();
			self.hideResultList();
			self.currentValue = "";
		}, 150);
	},
	showResults : function(value){
		var results = this.searchResults(value);
		this.hidePlaceHolder();
		if(!results.length) return;
		this.blur();
		this.resultList.empty().show();
		var self = this;
		results.sort();
		$.each(results, function(i, e){
			self.addResult(e, value);
		});
		this.results = results;
	},
	searchResults : function(value){
		var newvals = [], regexp = new RegExp((this.options.fullSearch ? '' : '\\b') + value, this.caseSensitive ? '' : 'i');
		var values = this.getValues(); 
		for (var i = 0; i < values.length; i++){
			if(this.stb.options.uniqueValues&&this.stb.containsValue(values[i])) continue;
			if (regexp.test(values[i])) {
				newvals.push(values[i]);
			}
			if (newvals.length >= this.options.maxResults) break;
		}
		return newvals;
	},
	addResult : function(result, value){
		var self = this;
		var newSel = $("<div></div>")
		   .addClass(this.baseClass+"-result")
		   .appendTo(this.resultList)
		   .mouseenter(function(e){
				self.focus(this);
		   })
		   .mousedown(function(e){
				e.stopPropagation();
				window.clearTimeout(self.hidetimer);
				self.doAdd = true;
		   })
		   .mouseup(function(e){
				if(self.doAdd){
					self.addCurrent();
					self.currentElem.focus();
					self.search();
					self.doAdd = false;
				}
		   });
		   
        var valueContainer = $("<span>")
		    .addClass(self.baseClass+"-value")
            .html(result)
			.css("display", "none")
			.appendTo(newSel);
	    var displayContainer = $("<span>")
	       .addClass(self.baseClass+"-display")
		   .html(self.formatResult(result, value))
		   .appendTo(newSel);			  
	},
	formatResult : function(result, value) {
		if (this.options.highlight) {
			var lcr = result.toLowerCase();
			var lcv = value.toLowerCase();
			var idx = lcr.indexOf(lcv);
			var formatted = "<span>" + result.substring(0, idx) + "</span>" +
			                "<span class='" + this.baseClass + "-highligh'>" + result.substring(idx, idx + value.length) + "</span>" + 
							"<span>" + result.substring(idx + value.length) + "</span>";
			return formatted;
		} else {
			return result;
		}
	},
	addCurrent : function(){
		var value = $("."+this.baseClass+"-value", this.currentSelection).html(); 
		this.currentElem.setValue(value);
		this.currentElem.saveAsBox();
		this.currentSelection = null;
	},
	focus : function(element){
		this.blur();
		this.currentSelection = $(element).addClass(this.baseClass + '-result-focus');
	},
	focusFirst : function(){
		this.focus(this.resultList.children(":first"));
	},
	focusRelative : function(dir){
		if(dir=='next'){
			this.focus( this.currentSelection.next().length ? this.currentSelection.next() : this.currentSelection);
		}
		else{
			this.focus( this.currentSelection.prev().length ? this.currentSelection.prev() : this.currentSelection);
		}
	},
	blur : function(){
		if(this.currentSelection){
			this.currentSelection.removeClass(this.baseClass + "-result-focus");
			this.currentSelection = null;
		}
	}
});


/**
	SmartTextBox.GrowingInput
*/
SmartTextBox.GrowingInput = SmartTextBox.BaseClass.extend({	
	options: {
		min: 0,
		max: null,
		startWidth: 2,
		correction: 10
	},
	construct : function(el, o){
		var o = o || {}; $.extend(this.options, o);
		var self = this;
		this.element = $(el);
		
		this.calc = $("<span></span>")
						.css({
							'float': 'left',
							'display': 'inline-block',
							'position': 'absolute',
							'left': -1000
						})
						.insertAfter(this.element);

		this.requiredStyles = ['font-size', 'font-family', 'padding-left', 'padding-top', 'padding-bottom', 
							   'padding-right', 'border-left', 'border-right', 'border-top', 'border-bottom', 
							   'word-spacing', 'letter-spacing', 'text-indent', 'text-transform'];
		
		this.copyCat();			
		this.resize();
		var resize = function(){ self.resize(); }
		this.element.click(resize)
					.blur(resize)
					.keyup(resize)
					.keydown(resize)
					.keypress(resize);
	},
	copyCat : function(){
		var self = this;
		$.each(this.requiredStyles, function(i, el){
			self.calc.css(el, self.element.css(el));
		});
	},
	calculate: function(chars){
		this.calc.html(chars);
		var width = this.calc.width();
		return (width ? width : this.options.startWidth) + this.options.correction;
	},
	resize: function(){
		this.lastvalue = this.value;
		this.value = this.element.attr('value');
		var value = this.value;
		
		if((this.options.min || (this.options.min==0)) && this.value.length < this.options.min){
			if((this.lastvalue || (this.lastvalue==0)) && (this.lastvalue.length <= this.options.min)) return;
			for(var i=0; i<this.options.min; i++){ value += "-";}
		}
		if((this.options.max || (this.options.max==0)) && this.value.length > this.options.max){
			if((this.lastvalue || (this.lastvalue==0)) && (this.lastvalue.length >= this.options.max)) return;
			value = this.value.substr(0, this.options.max);
		}
		
		var newWidth = this.calculate(value);
		this.element.width(newWidth);
		return this;
	}
});

/**
 * SmartTextBox.BaseElement - Base, abstract class for SmartTextBox elements
 */
SmartTextBox.BaseElement = SmartTextBox.BaseClass.extend({
	type : "base",
	options : {
	},
	construct : function(value, stb, o){
		var o = o || {}; $.extend(this.options, o);
		this.value = value;
		this.stb = stb;
		this.className = this.stb.options.containerClass + "-elem";
		this.elemClassName = this.stb.options.containerClass + "-" + this.type +"-elem";
		this.focused = false;
		this.init();
	},
	init : function(){
		this.constructElement();
	},
	constructElement : function(){
		this.el = null;
	},
	is : function(t){
		return this.type==t;
	},
	inject : function(elem, pos){
		if(elem) (pos == 'before') ? this.getElement().insertBefore(elem.getElement()) : this.getElement().insertAfter(elem.getElement());
		else this.getElement().prependTo(this.stb.list);
		this.notifyEvent("add");
	},
	remove : function(notify){
		this.blur();
		this.el.remove();
		this.onRemove();
		var n = (typeof notify == 'undefined') ? true : notify;
		if(n) this.notifyEvent("remove");
	},
	focus : function(notify){
		if(this.focused) return this;
		this.show();
		this.el.addClass(this.className + "-focus")
			   .addClass(this.className + "-" + this.type + "-focus");
		this.focused = true;
		
		var n = (typeof notify == 'undefined') ? true : notify;
		if(n) this.notifyEvent('focus');
		this.onFocus();
		
		return this;
	},
	blur : function(notify){
		if(!this.focused) return this;
		this.el.removeClass(this.className + "-focus")
			   .removeClass(this.className + "-" + this.type + "-focus");
		this.focused = false;
		
		var n = (typeof notify == 'undefined') ? true : notify;
		if(n) this.notifyEvent('blur');
		this.onBlur();
		
		return this;
	},
	onMouseIn : function(){
		this.el.addClass(this.className + "-hover")
			   .addClass(this.className + "-" + this.type + "-hover");
	},
	onMouseOut : function(){
		this.el.removeClass(this.className + "-hover")
			   .removeClass(this.className + "-" + this.type + "-hover");
	},
	onFocus : function(){
		return;
	},
	onBlur : function(){
		return;
	},
	onRemove : function(){
		return;
	},
	show : function(notify){
		this.el.show();
		var n = (typeof notify == 'undefined') ? true : notify;
		if(n) this.notifyEvent('show');
		return this;
	},
	hide : function(notify){
		this.el.hide();
		var n = (typeof notify == 'undefined') ? true : notify;
		if(n) this.notifyEvent('hide');
		return this;
	},
	setValue : function(v, notify){
		this.value = v;
		this.valueContainer.text(v);
		var n = (typeof notify == 'undefined') ? true : notify;
		if(n) this.notifyEvent("setValue");
		return this;
	},
	getValue : function(){
		return this.value;
	},
	getElement : function(){
		return this.el;
	},
	notifyEvent : function(type){
		this.stb.handleElementEvent(type, this);
		return this;
	},
	toString : function(){
		return "[BoxElement type='" + this.type + "' value='" + this.getValue() + "']";
	}
});

/**
	SmartTextBox.BoxElement
*/
SmartTextBox.BoxElement = SmartTextBox.BaseElement.extend({
	type : "box",
	constructElement : function(){
		var self = this;
		this.el = $("<li></li>")
			.addClass(this.className)
			.addClass(this.elemClassName)
			.hover(
				function(){ self.onMouseIn();  },
				function(){ self.onMouseOut(); }
			)
			.mousedown(function(event){
				self.focus();
			});

		if(this.stb.options.editOnDoubleClick){
			this.el.dblclick(function(event){
				event.preventDefault();
				self.toInput();
			});
		}
		
		this.valueContainer = $("<span></span>")
			.addClass(this.className+"-valueContainer")
			.appendTo(this.el);
		
		this.removeButton = $("<a></a>")
			.addClass(this.className+"-deleteButton")
			.attr("href", "javascript:;")
			.click(function(e){ self.remove(); e.stopPropagation(); })
			.appendTo(this.el);
			
		this.setValue(this.value);
	},
	onFocus : function(){
		var self = this;
		if(this.stb.options.editOnFocus){
			window.setTimeout(function(){
				self.toInput()
			}, 50);
		}
	},
	onBlur : function(){
		return;
	},
	toInput : function(){
		var v = this.getValue();
		var idx = this.stb.getElementIndex(this);
		this.remove();
		var nextElem = this.stb.getElement(idx-1);
		if(nextElem.is("input") && !$.trim(nextElem.getValue()).length){
			nextElem.setValue(v);
			nextElem.valueContainer.focus();
			nextElem.setCaretToEnd();
		}
	}
});

/**
	SmartTextBox.InputElement
*/
SmartTextBox.InputElement = SmartTextBox.BaseElement.extend({
	type : "input",
	constructElement : function(){
		var self = this;
		this.el = $("<li></li>")
			.addClass(this.className)
			.addClass(this.elemClassName)
			.hover(
				function(){ self.onMouseIn(); },
				function(){ self.onMouseOut(); }
			)
			.click(
				function(){ self.focus(); }
			);
					
		this.valueContainer = $("<input />")
			.addClass(this.elemClassName+"-valueInput")
			.focus( function(event){ event.stopPropagation(); self.focus(); })
			.blur( function(event){ self.blur(); })
			.appendTo(this.el);
		
		this.growingInput = new SmartTextBox.GrowingInput(this.valueContainer);

		$(document).keydown( function(event){ self.onKeyDown(event) } );
		$(document).keypress( function(event){ self.onKeyPress(event) } );
		
		this.setValue(this.value);
	},
	getValue : function(){
		return this.valueContainer.attr('value');
	},
	setValue : function(v){
		this.value = v;
		this.valueContainer.attr("value", v);
		this.growingInput.resize();
		this.notifyEvent("setValue");
		return this;
	},
	getCaret: function(){
		var elem = this.valueContainer[0];
		if(elem.createTextRange) {
			var r = document.selection.createRange().duplicate();		
			r.moveEnd('character', elem.value.length);
			if (r.text === '') return elem.value.length;
			return elem.value.lastIndexOf(r.text);
		} else {
			return elem.selectionStart;
		}
	},
	getCaretEnd: function(){
		var elem = this.valueContainer[0];
		if (elem.createTextRange){
			var r = document.selection.createRange().duplicate();
			r.moveStart('character', -elem.value.length);
			return r.text.length;
		} else {
			return elem.selectionEnd;
		}
	},
	setCaret: function(pos){
		var elem = this.valueContainer[0];
		if(elem.createTextRange){
			elem.focus ();
			var sel = document.selection.createRange();
			sel.moveStart('character', -elem.value.length);
			sel.moveStart('character', pos);
			sel.moveEnd('character', 0);
			sel.select();
		}
		else{
			elem.selectionStart = pos; 
			elem.selectionEnd = pos;
		}
	},
	setCaretToStart : function(){
		this.setCaret(0);
	},
	setCaretToEnd : function(){
		this.setCaret(this.valueContainer.attr("value").length || 0);
	},
	isSelected: function(){
		return this.focused && (this.getCaret() !== this.getCaretEnd());
	},
	saveAsBox : function(){
		var v = this.getValue();
		if(!v) return;
		this.stb.add("box", $.trim(v), this, "before");
		this.setValue("");
	},
	onKeyDown : function(event){
		if(!this.focused) return;
		if($.inArray(event.keyCode, this.stb.options.submitKeys)>-1 &&
		   !this.stb.options.onlyAutocomplete &&
		   !(this.stb.options.uniqueValues&&this.stb.containsValue(this.getValue()))) {
			event.preventDefault();
			this.saveAsBox();
		}
	},
	onKeyPress : function(event){
		if(!this.focused) return;
		if($.inArray(String.fromCharCode(event.charCode || event.keyCode || 0), this.stb.options.submitChars)>-1 &&
		   !this.stb.options.onlyAutocomplete && 
		   !(this.stb.options.uniqueValues&&this.stb.containsValue(this.getValue()))) {
			event.preventDefault();
			this.saveAsBox();
		}
	},
	onFocus : function(){
		this.show();
		this.valueContainer.focus();
		this.growingInput.resize();
	},
	onBlur : function(){
		this.valueContainer.blur();
		if(this.stb.options.hideEmptyInputs && this.el.next().length && !this.getValue()) this.hide();
		if(this.stb.options.editOnFocus){
			this.saveAsBox();
		}
	}
});

// Adds SmartTextBox to jQuery fn functions
$.fn.extend({
	smartTextBox: function(key, value){
		return this.each(function(){
			var Smb = $(this).data("SmartTextBox");
			if(Smb) {
				switch(key){
					case "add":
						Smb.addBox(value);
						break;
					case "remove":
						Smb.removeBox(value);
						break;
					case "load":
						Smb.load(value);
						break;
					case "clear":
						Smb.removeAll();
						break;
					case "autocomplete":
						Smb.setAutocompleteValues(value);
						break;
				}
			} else {
				new $.SmartTextBox.SmartTextBox(this, key);
			}
		});
	}
});

})(jQuery);

