/**
 * Amsify Suggestags
 * https://github.com/amsify42/jquery.amsify.suggestags
 * http://www.amsify42.com
 */

var AmsifySuggestags;

(function(factory) {
	if(typeof module === 'object' && typeof module.exports === 'object') {
		factory(require('jquery'), window, document);
	} else {
		factory(jQuery, window, document);
	}
}
(function($, window, document, undefined) {
	
	AmsifySuggestags = function(selector) {
		this.selector = selector;
		this.settings = {
			type              : 'bootstrap',
			tagLimit          : -1,
			suggestions       : [],
			suggestionsAction : {timeout: -1, minChars:2, minChange:-1, type: 'GET'},
			defaultTagClass   : '',
			classes           : [],
			backgrounds       : [],
			colors            : [],
			whiteList         : false,
			afterAdd          : {},
			afterRemove       : {},
			selectOnHover     : true,
			triggerChange     : false,
			noSuggestionMsg   : '',
			showAllSuggestions: false,
			keepLastOnHoverTag: true,
			printValues 	  : true,
			checkSimilar 	  : true,
			delimiters 		  : []
		};
		this.method        = undefined;
		this.name          = null;
		this.defaultLabel  = 'Type here';
		this.classes       = {
			sTagsArea     : '.amsify-suggestags-area',
			inputArea     : '.amsify-suggestags-input-area',
			inputAreaDef  : '.amsify-suggestags-input-area-default',
			focus         : '.amsify-focus',
			sTagsInput    : '.amsify-suggestags-input',
			listArea      : '.amsify-suggestags-list',
			list          : '.amsify-list',
			listItem      : '.amsify-list-item',
			itemPad       : '.amsify-item-pad',
			inputType     : '.amsify-select-input',
			tagItem       : '.amsify-select-tag',
			colBg         : '.col-bg',
			removeTag     : '.amsify-remove-tag',
			readyToRemove : '.ready-to-remove',
			noSuggestion  : '.amsify-no-suggestion',
		};
		this.selectors     = {
			sTagsArea     : null,
			inputArea     : null,
			inputAreaDef  : null,
			sTagsInput    : null,
			listArea      : null,
			list          : null,
			listGroup     : null,
			listItem      : null,
			itemPad       : null,
			inputType     : null,
		};
		this.isRequired = false;
		this.ajaxActive = false; 
		this.tagNames   = [];
	};
	AmsifySuggestags.prototype = {
	   /**
		* Merging default settings with custom
		* @type {object}
		*/
		_settings : function(settings) {
			this.settings = $.extend(true, {}, this.settings, settings);      
		},

		_setMethod : function(method) {
			this.method = method;
		},

		_init : function() {
			if(this.checkMethod()) {
				this.name = ($(this.selector).attr('name'))? $(this.selector).attr('name')+'_amsify': 'amsify_suggestags';
				this.createHTML();
				this.setEvents();
				$(this.selector).hide();
				this.setDefault();
			}
		},

		createHTML : function() {
			var HTML                  = '<div class="'+this.classes.sTagsArea.substring(1)+'"></div>';
			this.selectors.sTagsArea  = $(HTML).insertAfter(this.selector);
			var labelHTML             = '<div class="'+this.classes.inputArea.substring(1)+'"></div>';
			this.selectors.inputArea  = $(labelHTML).appendTo(this.selectors.sTagsArea);
			this.defaultLabel         = ($(this.selector).attr('placeholder') !== undefined)? $(this.selector).attr('placeholder'): this.defaultLabel;
			var sTagsInput            = '<input type="text" class="'+this.classes.sTagsInput.substring(1)+'" placeholder="'+this.defaultLabel+'">';
			this.selectors.sTagsInput = $(sTagsInput).appendTo(this.selectors.inputArea).attr('autocomplete', 'off');
			if($(this.selector).attr('required')) {
				$(this.selector).removeAttr('required');
				this.isRequired = true;
				this.updateIsRequired();
			}
			var listArea              = '<div class="'+this.classes.listArea.substring(1)+'"></div>';
			this.selectors.listArea   = $(listArea).appendTo(this.selectors.sTagsArea);
			$(this.selectors.listArea).width($(this.selectors.sTagsArea).width()-3);
			var list                  = '<ul class="'+this.classes.list.substring(1)+'"></ul>';
			this.selectors.list       = $(list).appendTo(this.selectors.listArea);
			this.updateSuggestionList();
			this.fixCSS();
		},

		updateIsRequired : function() {
			if(this.isRequired) {
				if(this.tagNames.length){
					$(this.selectors.sTagsInput).removeAttr('required');
				} else {
					$(this.selectors.sTagsInput).attr('required', 'required');
				}
			}
		},

		updateSuggestionList : function() {
			$(this.selectors.list).html('');
			$(this.createList()).appendTo(this.selectors.list);
		},

		setEvents : function() {
			var _self = this;
			$(this.selectors.inputArea).attr('style', $(this.selector).attr('style')).addClass($(this.selector).attr('class'));
			this.setTagEvents();
			if(window !== undefined) {
				$(window).resize(function(){
					$(_self.selectors.listArea).width($(_self.selectors.sTagsArea).width()-3);
				});
			}
			this.setSuggestionsEvents();
			this.setRemoveEvent();
		},

		setTagEvents : function() {
			var _self = this;
			$(this.selectors.sTagsInput).focus(function(){
			   /**
				* Show all suggestions if setting set to true
				*/
				if(_self.settings.showAllSuggestions) {
					_self.suggestWhiteList('', 0, true);
				}
				$(this).closest(_self.classes.inputArea).addClass(_self.classes.focus.substring(1));
				if(_self.settings.type == 'materialize') {
					$(this).css({
						'border-bottom': 'none',
						'-webkit-box-shadow': 'none',
						'box-shadow': 'none',
					});
				}
			});
			$(this.selectors.sTagsInput).blur(function(){
				$(this).closest(_self.classes.inputArea).removeClass(_self.classes.focus.substring(1));
				if(!$(this).val()) {
					$(_self.selectors.listArea).hide();
				}
			});
			$(this.selectors.sTagsInput).keyup(function(e){
				var keycode = (e.keyCode ? e.keyCode : e.which);
				var key 	= '';
				if(e.key) {
					key = e.key;
				} else {
					if(keycode == '13') {
						key = 'Enter';
					} else if(keycode == '188') {
						key = ',';
					}
				}
				var isDelimiter = ($.inArray(key, _self.settings.delimiters) !== -1)? true: false;
				if(key == 'Enter' || key == ',' || isDelimiter) {
					var value = $.trim($(this).val().replace(/,/g , ''));
					if(isDelimiter) {
						$.each(_self.settings.delimiters, function(dkey, delimiter) {
							value = $.trim(value.replace(delimiter, ''));
						});
					}
					$(this).val('');
					_self.addTag(_self.getValue(value));
					if(_self.settings.showAllSuggestions) {
						_self.suggestWhiteList('', 0, true);
					}
				} else if(keycode == '8' && !$(this).val()) {
					var removeClass = _self.classes.readyToRemove.substring(1);
					if($(this).hasClass(removeClass)) {
						$item = $(this).closest(_self.classes.inputArea).find(_self.classes.tagItem+':last');
						_self.removeTagByItem($item, false);
					} else {
						$(this).addClass(removeClass);
					}
					$(_self.selectors.listArea).hide();
					if(_self.settings.showAllSuggestions) {
						_self.suggestWhiteList('', 0, true);
					}
				} else if((_self.settings.suggestions.length || _self.isSuggestAction()) && ($(this).val() || _self.settings.showAllSuggestions)) {
					$(this).removeClass(_self.classes.readyToRemove.substring(1));
					_self.processWhiteList(keycode, $(this).val());
				}
			});
			$(this.selectors.sTagsInput).keypress(function(e){
				var keycode = (e.keyCode ? e.keyCode : e.which);
				if(keycode == 13) {
					return false;
				}
			});
			$(this.selectors.sTagsArea).click(function(){
				$(_self.selectors.sTagsInput).focus();
			});
		},

		setSuggestionsEvents : function() {
			var _self = this;
			if(this.settings.selectOnHover) {
				$(this.selectors.listArea).find(this.classes.listItem).hover(function(){
					$(_self.selectors.listArea).find(_self.classes.listItem).removeClass('active');
					$(this).addClass('active');
					$(_self.selectors.sTagsInput).val($(this).text());
				}, function() {
					$(this).removeClass('active');
					if(!_self.settings.keepLastOnHoverTag) {
						$(_self.selectors.sTagsInput).val('');
					}
				});
			}
			$(this.selectors.listArea).find(this.classes.listItem).click(function(){
				_self.addTag($(this).data('val'));
				$(_self.selectors.sTagsInput).val('').focus();
			});
		},

		isSuggestAction : function() {
			return (this.settings.suggestionsAction && this.settings.suggestionsAction.url);
		},

		getTag : function(value) {
			if(this.settings.suggestions.length) {
				var tag = value;
				$.each(this.settings.suggestions, function(key, item){
					if(typeof item === 'object' && item.value == value) {
						tag = item.tag
						return false;
					}
					else if(item == value) {
						return false; 
					}
				});
				return tag;
			}
			return value;
		},

		getValue : function(tag) {
			if(this.settings.suggestions.length) {
				var value = tag;
				var lower = tag.toString().toLowerCase();
				$.each(this.settings.suggestions, function(key, item){
					if(typeof item === 'object' && item.tag.toString().toLowerCase() == lower) {
						value = item.value
						return false;
					}
					else if(item.toString().toLowerCase() == lower) {
						return false; 
					}
				});
				return value;
			}
			return tag;
		},

		processAjaxSuggestion : function(value, keycode) {
			var _self           = this;
			var actionURL    	= this.getActionURL(this.settings.suggestionsAction.url);
			var params          = {existingTags: this.tagNames, existing: this.settings.suggestions, term: value};
			var ajaxConfig      = (this.settings.suggestionsAction.callbacks)? this.settings.suggestionsAction.callbacks: {};
			var ajaxFormParams  = {
				url : actionURL,
			};
			if(this.settings.suggestionsAction.type == 'GET') {
				ajaxFormParams.url = ajaxFormParams.url+'?'+$.param(params);
			} else {
				ajaxFormParams.type = this.settings.suggestionsAction.type;
				ajaxFormParams.data = params;
			}
			if(this.settings.suggestionsAction.timeout !== -1) {
				ajaxFormParams['timeout'] = this.settings.suggestionsAction.timeout*1000;
			}
			if(this.settings.suggestionsAction.beforeSend !== undefined && typeof this.settings.suggestionsAction.beforeSend == "function") {
				ajaxFormParams['beforeSend'] = this.settings.suggestionsAction.beforeSend;
			}
			ajaxFormParams['success'] = function(data) {
				if(data && data.suggestions) {
					_self.settings.suggestions = $.merge(_self.settings.suggestions, data.suggestions);
					_self.settings.suggestions = _self.unique(_self.settings.suggestions);
					_self.updateSuggestionList();
					_self.setSuggestionsEvents();
					_self.suggestWhiteList(value, keycode);
				}
				if(_self.settings.suggestionsAction.success !== undefined && typeof _self.settings.suggestionsAction.success == "function") {
					_self.settings.suggestionsAction.success(data);
				}
			};
			if(this.settings.suggestionsAction.error !== undefined && typeof this.settings.suggestionsAction.error == "function") {
				ajaxFormParams['error'] = this.settings.suggestionsAction.error;
			}
			ajaxFormParams['complete'] = function(data) {
				if(_self.settings.suggestionsAction.complete !== undefined && typeof _self.settings.suggestionsAction.complete == "function") {
					_self.settings.suggestionsAction.complete(data);
				}
				_self.ajaxActive = false;
			};
			$.ajax(ajaxFormParams);
		},

		processWhiteList : function(keycode, value) {
			if(keycode == '40' || keycode == '38') {
				var type = (keycode == '40')? 'down': 'up';
				this.upDownSuggestion(value, type);
			} else {
				if(this.isSuggestAction() && !this.ajaxActive) {
					var minChars  = this.settings.suggestionsAction.minChars;
					var minChange = this.settings.suggestionsAction.minChange;
					var lastSearch= this.selectors.sTagsInput.attr('last-search');
					if(value.length >= minChars && (minChange === -1 || !lastSearch || this.similarity(lastSearch, value)*100 <= minChange)) {
						this.selectors.sTagsInput.attr('last-search', value);
						this.ajaxActive = true;
						this.processAjaxSuggestion(value, keycode);
					}
				} else {
					this.suggestWhiteList(value, keycode);
				}
			}
		},

		upDownSuggestion : function(value, type) {
			var _self     = this;
			var isActive  = false;
			$(this.selectors.listArea).find(this.classes.listItem+':visible').each(function(){
				if($(this).hasClass('active')) {
					$(this).removeClass('active');
					if(type == 'up') {
						$item = $(this).prevAll(_self.classes.listItem+':visible:first');
					} else {
						$item = $(this).nextAll(_self.classes.listItem+':visible:first');
					}
					if($item.length) {
						isActive = true;
						$item.addClass('active');
						$(_self.selectors.sTagsInput).val($item.text());
					}
					return false;
				}
			});
			if(!isActive) {
				var childItem = (type == 'down')? 'first': 'last';
				$item = $(this.selectors.listArea).find(this.classes.listItem+':visible:'+childItem);
				if($item.length) {
					$item.addClass('active');
					$(_self.selectors.sTagsInput).val($item.text());
				}
			}
		},

		suggestWhiteList : function(value, keycode, showAll) {
			var _self = this;
			var found = false;
			var all   = (showAll)? true: false;
			var lower = value.toString().toLowerCase();
			$(this.selectors.listArea).find(_self.classes.noSuggestion).hide();
			var $list = $(this.selectors.listArea).find(this.classes.list);
			$list.find(this.classes.listItem).each(function(){
				var dataVal = $(this).data('val');
				if($.isNumeric(dataVal)) {
					dataVal = (value.toString().indexOf('.') == -1)? parseInt(dataVal): parseFloat(dataVal);
				}
				if((all || ~$(this).text().toString().toLowerCase().indexOf(lower)) && $.inArray(dataVal, _self.tagNames) === -1) {
					$(this).attr('data-show', 1);
					found = true;
				} else {
					$(this).removeAttr('data-show');
				}
				$(this).hide();
			});
			if(found) {
				/**
				 * Sorting the suggestions
				 */
				$dataShow = $list.find(this.classes.listItem+'[data-show]');
				if(lower) {
					$dataShow.sort(function(a, b) {
						return value.localeCompare($(a).text().toString());
					}).appendTo($list);
				} else {
					$dataShow.sort(function(a, b) {
						return $(a).text().toString().localeCompare($(b).text().toString());
					}).appendTo($list);
				}
				$dataShow.each(function(){
					$(this).show();
				});

			   /**
				* If only one item left in whitelist suggestions
				*/
				$item = $(this.selectors.listArea).find(this.classes.listItem+':visible');
				if($item.length == 1 && keycode != '8') {
					if((this.settings.whiteList && this.isSimilarText(value.toLowerCase(), $item.text().toLowerCase(), 40)) || this.isSimilarText(value.toLowerCase(), $item.text().toLowerCase(), 60)) {
						$item.addClass('active');
						$(this.selectors.sTagsInput).val($item.text());
					}
				} else {
					$item.removeClass('active');
				}

				$(this.selectors.listArea).show();

			} else {
				if(value && _self.settings.noSuggestionMsg) {
					$(this.selectors.listArea).find(_self.classes.listItem).hide();
					$(this.selectors.listArea).find(_self.classes.noSuggestion).show();
				} else {
					$(this.selectors.listArea).hide();
				}
			}
		},

		setDefault : function() {
			var _self = this;
			var items = $(this.selector).val().split(',');
			if(items.length) {
				$.each(items, function(index, item){
					_self.addTag($.trim(item));
				});
			}
		},

		setRemoveEvent: function() {
			var _self = this;
			$(this.selectors.inputArea).find(this.classes.removeTag).click(function(e){
				e.stopImmediatePropagation();
				$tagItem = $(this).closest(_self.classes.tagItem);
				_self.removeTagByItem($tagItem, false);
			});
		},

		createList : function() {
			var _self     = this;
			var listHTML  = '';
			$.each(this.settings.suggestions, function(index, item){
				var value = '';
				var tag   = '';
				if(typeof item === 'object') {
					value = item.value
					tag   = item.tag
				} else {
					value = item;
					tag   = item;
				}
				listHTML += '<li class="'+_self.classes.listItem.substring(1)+'" data-val="'+value+'">'+tag+'</li>';
			});
			if(_self.settings.noSuggestionMsg) {
				listHTML += '<li class="'+_self.classes.noSuggestion.substring(1)+'">'+_self.settings.noSuggestionMsg+'</li>';
			}
			return listHTML;
		},

		addTag : function(value) {
			if(!value) {
                return;
            }
			var html = '<span class="'+this.classes.tagItem.substring(1)+'" data-val="'+value+'">'+this.getTag(value)+' '+this.setIcon()+'</span>';
			$item    = $(html).insertBefore($(this.selectors.sTagsInput));
			if(this.settings.defaultTagClass) {
				$item.addClass(this.settings.defaultTagClass);
			}
			if(this.settings.tagLimit != -1 && this.settings.tagLimit > 0 && this.tagNames.length >= this.settings.tagLimit) {
				this.animateRemove($item, true);
				this.flashItem(value);
				return false;
			}
			var itemKey = this.getItemKey(value);
			if(this.settings.whiteList && itemKey === -1) {
				this.animateRemove($item, true);
				this.flashItem(value);
				return false;
			}
			if(this.isPresent(value)) {
				this.animateRemove($item, true);
				this.flashItem(value);
			} else {
				this.customStylings($item, itemKey);
				var dataVal = value;
				if($.isNumeric(dataVal)) {
					dataVal = (value.toString().indexOf('.') == -1)? parseInt(dataVal): parseFloat(dataVal);
				}
				this.tagNames.push(dataVal);
				this.setRemoveEvent();
				this.setInputValue();
				if(this.settings.afterAdd && typeof this.settings.afterAdd == "function") {
					this.settings.afterAdd(value);
				}
			}
			$(this.selector).trigger('suggestags.add', [value]);
			$(this.selector).trigger('suggestags.change');
			if(this.settings.triggerChange) {
				$(this.selector).trigger('change');
			}
			$(this.selectors.listArea).find(this.classes.listItem).removeClass('active');
			$(this.selectors.listArea).hide();
			$(this.selectors.sTagsInput).removeClass(this.classes.readyToRemove.substring(1));
		},

		getItemKey : function(value) {
			var itemKey = -1
			if(this.settings.suggestions.length) {
				var lower = value.toString().toLowerCase();
				$.each(this.settings.suggestions, function(key, item){
					if(typeof item === 'object') {
						if(item.value.toString().toLowerCase() == lower) {
							itemKey = key;
							return false;
						}  
					} else if(item.toString().toLowerCase() == lower) {
						itemKey = key;
						return false;
					}
				});
			}
			return itemKey;
		},

		isPresent : function(value) {
			var present = false;
			$.each(this.tagNames, function(index, tag){
				if(value.toString().toLowerCase() == tag.toString().toLowerCase()) {
					present = true;
					return false;
				}
			});
			return present;
		},

		customStylings : function(item, key) {
			var isCutom = false;
			if(this.settings.classes[key]) {
				isCutom = true;
				$(item).addClass(this.settings.classes[key]);
			}
			if(this.settings.backgrounds[key]) {
				isCutom = true;
				$(item).css('background', this.settings.backgrounds[key]);
			}
			if(this.settings.colors[key]) {
				isCutom = true;
				$(item).css('color', this.settings.colors[key]);
			}
			if(!isCutom) {
                $(item).addClass(this.classes.colBg.substring(1));
            }
		},

		removeTag: function(value) {
			var _self = this;
			$findTags = $(this.selectors.sTagsArea).find('[data-val="'+value+'"]');
			if($findTags.length) {
				$findTags.each(function(){
					_self.removeTagByItem(this, true);
				});
			}
		},

		removeTagByItem : function(item, animate) {
			this.tagNames.splice($(item).index(), 1);
			this.animateRemove(item, animate);
			this.setInputValue();
			$(this.selector).trigger('suggestags.remove', [$(item).attr('data-val')]);
			$(this.selector).trigger('suggestags.change');
			if(this.settings.triggerChange) {
				$(this.selector).trigger('change');
			}
			if(this.settings.afterRemove && typeof this.settings.afterRemove == "function") {
				this.settings.afterRemove($(item).attr('data-val'));
			}
			$(this.selectors.sTagsInput).removeClass(this.classes.readyToRemove.substring(1));
		},

		animateRemove : function(item, animate) {
			$(item).addClass('disabled');
			if(animate) {
				setTimeout(function(){
					$(item).slideUp();
					setTimeout(function(){
						$(item).remove();
					}, 500);
				}, 500);
			} else {
				$(item).remove();
			}
		},

		flashItem : function(value) {
			$item = '';
			value = value.toString().toLowerCase();
			$(this.selectors.sTagsArea).find(this.classes.tagItem).each(function(){
				var tagName = $.trim($(this).attr('data-val'));
				if(value == tagName.toString().toLowerCase()) {
					$item = $(this);
					return false;
				}
			});
			if($item) {
				$item.addClass('flash');
				setTimeout(function(){
					$item.removeClass('flash');
				}, 1500);
			}
		},

		setIcon : function() {
			var removeClass = this.classes.removeTag.substring(1);
			if(this.settings.type == 'bootstrap') {
				return '<span class="fa fa-times '+removeClass+'"></span>';
			} else if(this.settings.type == 'materialize') {
				return '<i class="material-icons right '+removeClass+'">clear</i>';
			} else {
				return '<b class="'+removeClass+'">&#10006;</b>';
			}
		},

		setInputValue: function() {
			this.updateIsRequired();
			$(this.selector).val(this.tagNames.join(','));
			if(this.settings.printValues) {
				this.printValues();
			}
		},

		fixCSS : function() {
			if(this.settings.type == 'amsify') {
				$(this.selectors.inputArea).addClass(this.classes.inputAreaDef.substring(1)).css({'padding': '5px 5px'});
			} else if(this.settings.type == 'materialize') {
				$(this.selectors.inputArea).addClass(this.classes.inputAreaDef.substring(1)).css({'height': 'auto', 'padding': '5px 5px'});
				$(this.selectors.sTagsInput).css({'margin': '0', 'height': 'auto'});
			}
		},

		printValues : function() {
			console.info(this.tagNames, $(this.selector).val());
		},

		checkMethod : function() {
			$findTags = $(this.selector).next(this.classes.sTagsArea);
			if($findTags.length) {
				$findTags.remove();
			}
			$(this.selector).show();
			if(typeof this.method !== undefined && this.method == 'destroy') {
				return false;
			} else {
				return true;
			}
		},

		refresh : function() {
			this._setMethod('refresh');
			this._init();
		},

		destroy : function() {
			this._setMethod('destroy');
			this._init();
		},

		getActionURL : function(urlString) {
			var URL = '';
			if(window !== undefined) {
				URL = window.location.protocol+'//'+window.location.host;
			}
			if(this.isAbsoluteURL(urlString)) {
				URL = urlString;
			} else {
				URL += '/'+urlString.replace(/^\/|\/$/g, '');
			}
			return URL;
		},

		isAbsoluteURL : function(urlString) {
			var regexURL = new RegExp('^(?:[a-z]+:)?//', 'i');
			return (regexURL.test(urlString))? true: false;
		},

		unique: function(list) {
			var result = [];
			var _self  = this;
			$.each(list, function(i, e) {
				if(typeof e === 'object') {
					if(!_self.objectInArray(e, result)) {
						result.push(e);
					}
				} else {
					if($.inArray(e, result) == -1) {
						result.push(e);
					}
				}
			});
			return result;
		},

		objectInArray : function(element, result) {
			if(result.length) {
				var present = false;
				$.each(result, function(i, e) {
					if(typeof e === 'object') {
						if(e.value == element.value) {
							present = true;
							return false;
						}
					} else {
						if(e == element.value) {
							present = true;
							return false;
						}
					}
				});
				return present;
			} else {
				return false;
			}
		},

		isSimilarText: function(str1, str2, perc) {
			if(!this.settings.checkSimilar) {
				return false;
			}
			var percent = this.similarity(str1, str2);
			return (percent*100 >= perc)? true: false;
		},

		similarity: function(s1, s2) {
			var longer = s1;
			var shorter = s2;
			if(s1.length < s2.length) {
				longer = s2;
				shorter = s1;
			}
			var longerLength = longer.length;
			if(longerLength == 0) {
				return 1.0;
			}
			return (longerLength - this.editDistance(longer, shorter))/parseFloat(longerLength);
		},

		editDistance: function(s1, s2) {
			s1 = s1.toLowerCase();
			s2 = s2.toLowerCase();
			var costs = new Array();
			for(var i = 0; i <= s1.length; i++) {
				var lastValue = i;
				for(var j = 0; j <= s2.length; j++) {
					if(i == 0) {
						costs[j] = j;
					} else {
						if(j > 0) {
							var newValue = costs[j-1];
							if(s1.charAt(i-1) != s2.charAt(j-1)) {
								newValue = Math.min(Math.min(newValue, lastValue), costs[j]) + 1;
							}
							costs[j-1] = lastValue;
							lastValue  = newValue;
						}
					}
				}
				if(i > 0)
					costs[s2.length] = lastValue;
			}
			return costs[s2.length];
		}
	};

	$.fn.amsifySuggestags = function(options, method) {
		return this.each(function() {
			var amsifySuggestags = new AmsifySuggestags(this);
			amsifySuggestags._settings(options);
			amsifySuggestags._setMethod(method);
			amsifySuggestags._init();
		});
	};
}));