PageSourceSearch

https://www.horse-ball.org/media/js/jquery.amsify.suggestags-master/js/jquery.amsify.suggestags.js

js horse-ball.org collected 2026-09-26 07:36:54 UTC 29,220 bytes, 926 lines download raw bytes

1/**
2 * Amsify Suggestags
3 * https://github.com/amsify42/jquery.amsify.suggestags
4 */
5
6var AmsifySuggestags;
7
8(function(factory) {
9	if(typeof module === 'object' && typeof module.exports === 'object') {
10		factory(require('jquery'), window, document);
11	} else {
12		factory(jQuery, window, document);
13	}
14}
15(function($, window, document, undefined) {
16	
17	AmsifySuggestags = function(selector) {
18		this.selector = selector;
19		this.settings = {
20			type              : 'bootstrap',
21			tagLimit          : -1,
22			suggestions       : [],
23			suggestMatch 	  : {},
24			suggestionsAction : {timeout: -1, minChars:2, minChange:-1, delay:100, type: 'GET'},
25			defaultTagClass   : '',
26			classes           : [],
27			backgrounds       : [],
28			colors            : [],
29			whiteList         : false,
30			afterAdd          : {},
31			afterRemove       : {},
32			//My changes
33			trimValue         : false,
34			dashspaces        : false,
35			lowercase         : false,
36			selectOnHover     : true,
37			triggerChange     : false,
38			noSuggestionMsg   : '',
39			showAllSuggestions: false,
40			keepLastOnHoverTag: true,
41			printValues 	  : true,
42			checkSimilar 	  : true,
43			delimiters 		  : [],
44			showPlusAfter 	  : 0
45		};
46		this.method        = undefined;
47		this.name          = null;
48		this.defaultLabel  = 'Type here';
49		this.classes       = {
50			sTagsArea     : '.amsify-suggestags-area',
51			inputArea     : '.amsify-suggestags-input-area',
52			inputAreaDef  : '.amsify-suggestags-input-area-default',
53			focus         : '.amsify-focus',
54			sTagsInput    : '.amsify-suggestags-input',
55			listArea      : '.amsify-suggestags-list',
56			list          : '.amsify-list',
57			listItem      : '.amsify-list-item',
58			itemPad       : '.amsify-item-pad',
59			inputType     : '.amsify-select-input',
60			tagItem       : '.amsify-select-tag',
61			plusItem 	  : '.amsify-plus-tag',
62			colBg         : '.col-bg',
63			removeTag     : '.amsify-remove-tag',
64			readyToRemove : '.ready-to-remove',
65			noSuggestion  : '.amsify-no-suggestion',
66			showPlusBg 	  : '.show-plus-bg'
67		};
68		this.selectors     = {
69			sTagsArea     : null,
70			inputArea     : null,
71			inputAreaDef  : null,
72			sTagsInput    : null,
73			listArea      : null,
74			list          : null,
75			listGroup     : null,
76			listItem      : null,
77			itemPad       : null,
78			inputType     : null,
79			plusTag 	  : null
80		};
81		this.isRequired = false;
82		this.ajaxActive = false; 
83		this.tagNames   = [];
84		this.delayTimer = 0;
85		this.blurTgItems= null;
86	};
87	AmsifySuggestags.prototype = {
88	   /**
89		* Merging default settings with custom
90		* @type {object}
91		*/
92		_settings : function(settings) {
93			this.settings = $.extend(true, {}, this.settings, settings);      
94		},
95
96		_setMethod : function(method) {
97			this.method = method;
98		},
99
100		_init : function() {
101			if(this.checkMethod()) {
102				this.name = ($(this.selector).attr('name'))? $(this.selector).attr('name')+'_amsify': 'amsify_suggestags';
103				this.createHTML();
104				this.setEvents();
105				$(this.selector).hide();
106				this.setDefault();
107			}
108		},
109
110		createHTML : function() {
111			var HTML                  = '<div class="'+this.classes.sTagsArea.substring(1)+'"></div>';
112			this.selectors.sTagsArea  = $(HTML).insertAfter(this.selector);
113			var labelHTML             = '<div class="'+this.classes.inputArea.substring(1)+'"></div>';
114			this.selectors.inputArea  = $(labelHTML).appendTo(this.selectors.sTagsArea);
115			this.defaultLabel         = ($(this.selector).attr('placeholder') !== undefined)? $(this.selector).attr('placeholder'): this.defaultLabel;
116			var sTagsInput            = '<input type="text" class="'+this.classes.sTagsInput.substring(1)+'" placeholder="'+this.defaultLabel+'">';
117			this.selectors.sTagsInput = $(sTagsInput).appendTo(this.selectors.inputArea).attr('autocomplete', 'off');
118			if($(this.selector).attr('required')) {
119				$(this.selector).removeAttr('required');
120				this.isRequired = true;
121				this.updateIsRequired();
122			}
123			var listArea              = '<div class="'+this.classes.listArea.substring(1)+'"></div>';
124			this.selectors.listArea   = $(listArea).appendTo(this.selectors.sTagsArea);
125			$(this.selectors.listArea).width($(this.selectors.sTagsArea).width()-3);
126			var list                  = '<ul class="'+this.classes.list.substring(1)+'"></ul>';
127			this.selectors.list       = $(list).appendTo(this.selectors.listArea);
128			this.updateSuggestionList();
129			this.fixCSS();
130		},
131
132		updateIsRequired : function() {
133			if(this.isRequired) {
134				if(this.tagNames.length){
135					$(this.selectors.sTagsInput).removeAttr('required');
136				} else {
137					$(this.selectors.sTagsInput).attr('required', 'required');
138				}
139			}
140		},
141
142		updateSuggestionList : function() {
143			$(this.selectors.list).html('');
144			$(this.createList()).appendTo(this.selectors.list);
145		},
146
147		setEvents : function() {
148			var _self = this;
149			$(this.selectors.inputArea).attr('style', $(this.selector).attr('style')).addClass($(this.selector).attr('class'));
150			this.setTagEvents();
151			if(window !== undefined) {
152				$(window).resize(function(){
153					$(_self.selectors.listArea).width($(_self.selectors.sTagsArea).width()-3);
154				});
155			}
156			this.setSuggestionsEvents();
157			this.setRemoveEvent();
158		},
159
160		setTagEvents : function() {
161			var _self = this;
162			$(this.selectors.sTagsInput).focus(function(){
163			   /**
164				* Show all suggestions if setting set to true
165				*/
166				if(_self.settings.showAllSuggestions) {
167					_self.suggestWhiteList('', 0, true);
168				}
169				$(this).closest(_self.classes.inputArea).addClass(_self.classes.focus.substring(1));
170				if(_self.settings.type == 'materialize') {
171					$(this).css({
172						'border-bottom': 'none',
173						'-webkit-box-shadow': 'none',
174						'box-shadow': 'none',
175					});
176				}
177				_self.checkPlusAfter();
178			});
179			$(this.selectors.sTagsInput).blur(function(){
180				$(this).closest(_self.classes.inputArea).removeClass(_self.classes.focus.substring(1));
181				if(!$(this).val()) {
182					$(_self.selectors.listArea).hide();
183				}
184				_self.checkPlusAfter(true);
185			});
186			$(this.selectors.sTagsInput).keyup(function(e){
187				var keycode = (e.keyCode ? e.keyCode : e.which);
188				var key 	= '';
189				if(e.key) {
190					key = e.key;
191				} else {
192					if(keycode == '13') {
193						key = 'Enter';
194					} else if(keycode == '188') {
195						key = ',';
196					}
197				}
198				/**
199				 * Mobile browser fix
200				 */
201				if(keycode == 229 && key == 'Unidentified' && /android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(navigator.userAgent.toLowerCase()) && $(this).val()[$(this).val().length - 1] == ',')
202				{
203					key = ',';
204				}
205				var isDelimiter = ($.inArray(key, _self.settings.delimiters) !== -1)? true: false;
206				if(key == 'Enter' || key == ',' || isDelimiter) {
207					var value = $.trim($(this).val().replace(/,/g , ''));
208					if(isDelimiter) {
209						$.each(_self.settings.delimiters, function(dkey, delimiter) {
210							value = $.trim(value.replace(delimiter, ''));
211						});
212					}
213					$(this).val('');
214					_self.addTag(_self.getValue(value));
215					if(_self.settings.showAllSuggestions) {
216						_self.suggestWhiteList('', 0, true);
217					}
218				} else if(keycode == '8' && !$(this).val()) {
219					var removeClass = _self.classes.readyToRemove.substring(1);
220					if($(this).hasClass(removeClass)) {
221						$item = $(this).closest(_self.classes.inputArea).find(_self.classes.tagItem+':last');
222						_self.removeTagByItem($item, false);
223					} else {
224						$(this).addClass(removeClass);
225					}
226					$(_self.selectors.listArea).hide();
227					if(_self.settings.showAllSuggestions) {
228						_self.suggestWhiteList('', 0, true);
229					}
230				} else if((_self.settings.suggestions.length || _self.isSuggestAction()) && ($(this).val() || _self.settings.showAllSuggestions)) {
231					$(this).removeClass(_self.classes.readyToRemove.substring(1));
232					_self.processWhiteList(keycode, $(this).val());
233				}
234			});
235			$(this.selectors.sTagsInput).keypress(function(e){
236				var keycode = (e.keyCode ? e.keyCode : e.which);
237				if(keycode == 13) {
238					return false;
239				}
240			});
241			$(this.selectors.sTagsArea).click(function(){
242				$(_self.selectors.sTagsInput).focus();
243			});
244		},
245
246		setSuggestionsEvents : function() {
247			var _self = this;
248			if(this.settings.selectOnHover) {
249				$(this.selectors.listArea).find(this.classes.listItem).hover(function(){
250					$(_self.selectors.listArea).find(_self.classes.listItem).removeClass('active');
251					$(this).addClass('active');
252					$(_self.selectors.sTagsInput).val($(this).text());
253				}, function() {
254					$(this).removeClass('active');
255					if(!_self.settings.keepLastOnHoverTag) {
256						$(_self.selectors.sTagsInput).val('');
257					}
258				});
259			}
260			$(this.selectors.listArea).find(this.classes.listItem).click(function(){
261				_self.addTag($(this).data('val'));
262				$(_self.selectors.sTagsInput).val('').focus();
263			});
264		},
265
266		isSuggestAction : function() {
267			return (this.settings.suggestionsAction && this.settings.suggestionsAction.url);
268		},
269
270		getTag : function(value) {
271			if(this.settings.suggestions.length) {
272				var tag = value;
273				$.each(this.settings.suggestions, function(key, item){
274					if(typeof item === 'object' && item.value == value) {
275						tag = item.tag
276						return false;
277					}
278					else if(item == value) {
279						return false; 
280					}
281				});
282				return tag;
283			}
284			return value;
285		},
286
287		getValue : function(tag) {
288			if(this.settings.suggestions.length) {
289				var value = tag;
290				var lower = tag.toString().toLowerCase();
291				$.each(this.settings.suggestions, function(key, item){
292					if(typeof item === 'object' && item.tag.toString().toLowerCase() == lower) {
293						value = item.value
294						return false;
295					}
296					else if(item.toString().toLowerCase() == lower) {
297						return false; 
298					}
299				});
300				return value;
301			}
302			return tag;
303		},
304
305		processAjaxSuggestion : function(value, keycode) {
306			var _self           = this;
307			var actionURL    	= this.getActionURL(this.settings.suggestionsAction.url);
308			var params          = {existingTags: this.tagNames, existing: this.settings.suggestions, term: value};
309			var ajaxConfig      = (this.settings.suggestionsAction.callbacks)? this.settings.suggestionsAction.callbacks: {};
310			var ajaxFormParams  = {
311				url : actionURL,
312			};
313			if(this.settings.suggestionsAction.type == 'GET') {
314				ajaxFormParams.url = ajaxFormParams.url+'?'+$.param(params);
315			} else {
316				ajaxFormParams.type = this.settings.suggestionsAction.type;
317				ajaxFormParams.data = params;
318			}
319			if(this.settings.suggestionsAction.timeout !== -1) {
320				ajaxFormParams['timeout'] = this.settings.suggestionsAction.timeout*1000;
321			}
322			if(this.settings.suggestionsAction.beforeSend !== undefined && typeof this.settings.suggestionsAction.beforeSend == "function") {
323				ajaxFormParams['beforeSend'] = this.settings.suggestionsAction.beforeSend;
324			}
325			ajaxFormParams['success'] = function(data) {
326				if(data && data.suggestions) {
327					_self.settings.suggestions = $.merge(_self.settings.suggestions, data.suggestions);
328					_self.settings.suggestions = _self.unique(_self.settings.suggestions);
329					_self.updateSuggestionList();
330					_self.setSuggestionsEvents();
331					_self.suggestWhiteList(value, keycode);
332				}
333				if(_self.settings.suggestionsAction.success !== undefined && typeof _self.settings.suggestionsAction.success == "function") {
334					_self.settings.suggestionsAction.success(data);
335				}
336			};
337			if(this.settings.suggestionsAction.error !== undefined && typeof this.settings.suggestionsAction.error == "function") {
338				ajaxFormParams['error'] = this.settings.suggestionsAction.error;
339			}
340			ajaxFormParams['complete'] = function(data) {
341				if(_self.settings.suggestionsAction.complete !== undefined && typeof _self.settings.suggestionsAction.complete == "function") {
342					_self.settings.suggestionsAction.complete(data);
343				}
344				_self.ajaxActive = false;
345			};
346
347			if(this.settings.suggestionsAction.dataType !== undefined && this.settings.suggestionsAction.dataType !== null) {
348                ajaxFormParams['dataType'] = this.settings.suggestionsAction.dataType;
349            }
350
351			$.ajax(ajaxFormParams);
352		},
353
354		processWhiteList : function(keycode, value) {
355			if(keycode == '40' || keycode == '38') {
356				var type = (keycode == '40')? 'down': 'up';
357				this.upDownSuggestion(value, type);
358			} else {
359				clearTimeout(this.delayTimer);
360				var _self       = this;
361				this.delayTimer = setTimeout(function() {
362					if(_self.isSuggestAction() && !_self.ajaxActive) {
363				      	var minChars  = _self.settings.suggestionsAction.minChars;
364						var minChange = _self.settings.suggestionsAction.minChange;
365						var lastSearch= _self.selectors.sTagsInput.attr('last-search');
366						if(value.length >= minChars && (minChange === -1 || !lastSearch || _self.similarity(lastSearch, value)*100 <= minChange)) {
367							_self.selectors.sTagsInput.attr('last-search', value);
368							_self.ajaxActive = true;
369							_self.processAjaxSuggestion(value, keycode);
370						}
371					} else {
372						_self.suggestWhiteList(value, keycode);
373					}
374			    }, this.settings.suggestionsAction.delay);
375			}
376		},
377
378		upDownSuggestion : function(value, type) {
379			var _self     = this;
380			var isActive  = false;
381			$(this.selectors.listArea).find(this.classes.listItem+':visible').each(function(){
382				if($(this).hasClass('active')) {
383					$(this).removeClass('active');
384					if(type == 'up') {
385						$item = $(this).prevAll(_self.classes.listItem+':visible:first');
386					} else {
387						$item = $(this).nextAll(_self.classes.listItem+':visible:first');
388					}
389					if($item.length) {
390						isActive = true;
391						$item.addClass('active');
392						$(_self.selectors.sTagsInput).val($item.text());
393					}
394					return false;
395				}
396			});
397			if(!isActive) {
398				var childItem = (type == 'down')? 'first': 'last';
399				$item = $(this.selectors.listArea).find(this.classes.listItem+':visible:'+childItem);
400				if($item.length) {
401					$item.addClass('active');
402					$(_self.selectors.sTagsInput).val($item.text());
403				}
404			}
405		},
406
407		suggestWhiteList : function(value, keycode, showAll) {
408			var _self = this;
409			var found = false;
410			var all   = (showAll)? true: false;
411			var lower = value.toString().toLowerCase();
412			$(this.selectors.listArea).find(_self.classes.noSuggestion).hide();
413			var $list = $(this.selectors.listArea).find(this.classes.list);
414			$list.find(this.classes.listItem).each(function(){
415				var dataVal = $(this).data('val');
416				if($.isNumeric(dataVal)) {
417					dataVal = (value.toString().indexOf('.') == -1)? parseInt(dataVal): parseFloat(dataVal);
418				}
419
420				/**
421				 * Suggest item matching result
422				 */
423				var suggestItemResult = 0;
424				if(_self.settings.suggestMatch && typeof _self.settings.suggestMatch == "function") {
425					suggestItemResult = _self.settings.suggestMatch($(this).text(), value);
426				} else {
427					suggestItemResult = ~$(this).text().toString().toLowerCase().indexOf(lower);
428				}
429
430				if((all || suggestItemResult) && $.inArray(dataVal, _self.tagNames) === -1) {
431					$(this).attr('data-show', 1);
432					found = true;
433				} else {
434					$(this).removeAttr('data-show');
435				}
436				$(this).hide();
437			});
438			if(found) {
439				/**
440				 * Sorting the suggestions
441				 */
442				$dataShow = $list.find(this.classes.listItem+'[data-show]');
443				if(lower) {
444					$dataShow.sort(function(a, b) {
445						return value.localeCompare($(a).text().toString());
446					}).appendTo($list);
447				} else {
448					$dataShow.sort(function(a, b) {
449						return $(a).text().toString().localeCompare($(b).text().toString());
450					}).appendTo($list);
451				}
452				$dataShow.each(function(){
453					$(this).show();
454				});
455
456			   /**
457				* If only one item left in whitelist suggestions
458				*/
459				$item = $(this.selectors.listArea).find(this.classes.listItem+':visible');
460				if($item.length == 1 && keycode != '8') {
461					if((this.settings.whiteList && this.isSimilarText(value.toLowerCase(), $item.text().toLowerCase(), 40)) || this.isSimilarText(value.toLowerCase(), $item.text().toLowerCase(), 60)) {
462						$item.addClass('active');
463						$(this.selectors.sTagsInput).val($item.text());
464					}
465				} else {
466					$item.removeClass('active');
467				}
468
469				$(this.selectors.listArea).show();
470
471			} else {
472				if(value && _self.settings.noSuggestionMsg) {
473					$(this.selectors.listArea).find(_self.classes.listItem).hide();
474					$(this.selectors.listArea).find(_self.classes.noSuggestion).show();
475				} else {
476					$(this.selectors.listArea).hide();
477				}
478			}
479		},
480
481		setDefault : function() {
482			var _self = this;
483			var items = $(this.selector).val().split(',');
484			if(items.length) {
485				$.each(items, function(index, item){
486					_self.addTag($.trim(item));
487				});
488			}
489		},
490
491		setRemoveEvent: function() {
492			var _self = this;
493			$(this.selectors.inputArea).find(this.classes.removeTag).click(function(e){
494				e.stopImmediatePropagation();
495				$tagItem = $(this).closest(_self.classes.tagItem);
496				_self.removeTagByItem($tagItem, false);
497			});
498		},
499
500		createList : function() {
501			var _self     = this;
502			var listHTML  = '';
503			$.each(this.settings.suggestions, function(index, item){
504				var value = '';
505				var tag   = '';
506				if(typeof item === 'object') {
507					value = item.value
508					tag   = item.tag
509				} else {
510					value = item;
511					tag   = item;
512				}
513				listHTML += '<li class="'+_self.classes.listItem.substring(1)+'" data-val="'+value+'">'+tag+'</li>';
514			});
515			if(_self.settings.noSuggestionMsg) {
516				listHTML += '<li class="'+_self.classes.noSuggestion.substring(1)+'">'+_self.settings.noSuggestionMsg+'</li>';
517			}
518			return listHTML;
519		},
520
521		addTag : function(value, animate=true) {
522			if(!value) {
523                return;
524			}
525			// Trim value
526			if (typeof value === "string" && this.settings.trimValue) {
527				value = $.trim(value);
528			}
529			
530			// lowercase and dash
531			if (typeof value === "string" && this.settings.lowercase && this.settings.dashspaces) {
532				value = value.replace(/\s+/g, '-').toLowerCase();
533			}
534			else if (typeof value === "string" && this.settings.lowercase){
535				value = value.toLowerCase();
536			}
537			else if (typeof value === "string" && this.settings.dashspaces){
538				value = value.replace(/\s+/g, '-');
539			}
540
541			var html = '<span class="'+this.classes.tagItem.substring(1)+'" data-val="'+value+'">'+this.getTag(value)+' '+this.setIcon()+'</span>';
542			$item    = $(html).insertBefore($(this.selectors.sTagsInput));
543			if(this.settings.defaultTagClass) {
544				$item.addClass(this.settings.defaultTagClass);
545			}
546			if(this.settings.tagLimit != -1 && this.settings.tagLimit > 0 && this.tagNames.length >= this.settings.tagLimit) {
547				this.animateRemove($item, animate);
548				if(animate) {
549					this.flashItem(value);
550				}
551				return false;
552			}
553			var itemKey = this.getItemKey(value);
554			if(this.settings.whiteList && itemKey === -1) {
555				this.animateRemove($item, animate);
556				if(animate) {
557					this.flashItem(value, itemKey);
558				}
559				return false;
560			}
561			if(this.isPresent(value)) {
562				this.animateRemove($item, animate);
563				if(animate) {
564					this.flashItem(value, itemKey);
565				}
566			} else {
567				this.customStylings($item, itemKey);
568				var dataVal = value;
569				if($.isNumeric(dataVal)) {
570					dataVal = (value.toString().indexOf('.') == -1)? parseInt(dataVal): parseFloat(dataVal);
571				}
572				this.tagNames.push(dataVal);
573				this.setRemoveEvent();
574				this.setInputValue();
575				if(this.settings.afterAdd && typeof this.settings.afterAdd == "function") {
576					this.settings.afterAdd(value);
577				}
578			}
579			$(this.selector).trigger('suggestags.add', [value]);
580			$(this.selector).trigger('suggestags.change');
581			if(this.settings.triggerChange) {
582				$(this.selector).trigger('change');
583			}
584			$(this.selectors.listArea).find(this.classes.listItem).removeClass('active');
585			$(this.selectors.listArea).hide();
586			$(this.selectors.sTagsInput).removeClass(this.classes.readyToRemove.substring(1));
587			this.checkPlusAfter();
588		},
589
590		getItemKey : function(value) {
591			var itemKey = -1
592			var _self = this;
593			if(this.settings.suggestions.length) {
594				var lower = value.toString().toLowerCase();
595				$.each(this.settings.suggestions, function(key, item){
596					if(typeof item === 'object') {
597						if(item.value.toString().toLowerCase() == lower) {
598							itemKey = key;
599							if(item.class !== undefined && _self.settings.classes[key] === undefined) {
600								_self.settings.classes[key] = item.class;
601							}
602							if(item.background !== undefined && _self.settings.backgrounds[key] === undefined) {
603								_self.settings.backgrounds[key] = item.background;
604							}
605							if(item.color !== undefined && _self.settings.colors[key] === undefined) {
606								_self.settings.colors[key] = item.color;
607							}
608							return false;
609						}  
610					} else if(item.toString().toLowerCase() == lower) {
611						itemKey = key;
612						return false;
613					}
614				});
615			}
616			return itemKey;
617		},
618
619		isPresent : function(value) {
620			var present = false;
621			$.each(this.tagNames, function(index, tag){
622				if(value.toString().toLowerCase() == tag.toString().toLowerCase()) {
623					present = true;
624					return false;
625				}
626			});
627			return present;
628		},
629
630		customStylings : function(item, key) {
631			var isCustom = false;
632			if(this.settings.classes[key]) {
633				isCustom = true;
634				$(item).addClass(this.settings.classes[key]);
635			}
636			if(this.settings.backgrounds[key]) {
637				isCustom = true;
638				$(item).css('background', this.settings.backgrounds[key]);
639			}
640			if(this.settings.colors[key]) {
641				isCustom = true;
642				$(item).css('color', this.settings.colors[key]);
643			}
644			if(!isCustom) {
645                $(item).addClass(this.classes.colBg.substring(1));
646            }
647		},
648
649		removeTag: function(value, animate=true) {
650			var _self = this;
651			$findTags = $(this.selectors.inputArea).find('[data-val="'+value+'"]');
652			if($findTags.length) {
653				$findTags.each(function(){
654					_self.removeTagByItem(this, animate);
655				});
656			}
657		},
658
659		removeTagByItem : function(item, animate) {
660			this.tagNames.splice($(item).index(), 1);
661			this.animateRemove(item, animate);
662			this.setInputValue();
663			$(this.selector).trigger('suggestags.remove', [$(item).attr('data-val')]);
664			$(this.selector).trigger('suggestags.change');
665			if(this.settings.triggerChange) {
666				$(this.selector).trigger('change');
667			}
668			if(this.settings.afterRemove && typeof this.settings.afterRemove == "function") {
669				this.settings.afterRemove($(item).attr('data-val'));
670			}
671			$(this.selectors.sTagsInput).removeClass(this.classes.readyToRemove.substring(1));
672			this.checkPlusAfter();
673		},
674
675		animateRemove : function(item, animate) {
676			$(item).addClass('disabled');
677			if(animate) {
678				setTimeout(function(){
679					$(item).slideUp();
680					setTimeout(function(){
681						$(item).remove();
682					}, 500);
683				}, 500);
684			} else {
685				$(item).remove();
686			}
687		},
688
689		flashItem : function(value, itemKey) {
690			$tagItem = '';
691			value 	 = value.toString().toLowerCase();
692			$(this.selectors.sTagsArea).find(this.classes.tagItem).each(function(){
693				var tagName = $.trim($(this).attr('data-val'));
694				if(value == tagName.toString().toLowerCase()) {
695					$tagItem = $(this);
696					return false;
697				}
698			});
699			if($tagItem) {
700				background = (this.settings.backgrounds[itemKey] !== undefined)? this.settings.backgrounds[itemKey]: null;
701				if(background !== null) {
702					$tagItem.css('background', ''); 
703				}
704				$tagItem.addClass('flash');
705				setTimeout(function(){
706					$tagItem.removeClass('flash');
707					if(background !== null) {
708						$tagItem.css('background', background);
709					}
710				}, 1500, $tagItem);
711			}
712		},
713
714		setIcon : function() {
715			var removeClass = this.classes.removeTag.substring(1);
716			if(this.settings.type == 'bootstrap') {
717				return '<span class="fa fa-times '+removeClass+'"></span>';
718			} else if(this.settings.type == 'materialize') {
719				return '<i class="material-icons right '+removeClass+'">clear</i>';
720			} else {
721				return '<b class="'+removeClass+'">&#10006;</b>';
722			}
723		},
724
725		setInputValue: function() {
726			this.updateIsRequired();
727			$(this.selector).val(this.tagNames.join(','));
728			if(this.settings.printValues) {
729				this.printValues();
730			}
731		},
732
733		fixCSS : function() {
734			if(this.settings.type == 'amsify') {
735				$(this.selectors.inputArea).addClass(this.classes.inputAreaDef.substring(1)).css({'padding': '5px 5px'});
736			} else if(this.settings.type == 'materialize') {
737				$(this.selectors.inputArea).addClass(this.classes.inputAreaDef.substring(1)).css({'height': 'auto', 'padding': '5px 5px'});
738				$(this.selectors.sTagsInput).css({'margin': '0', 'height': 'auto'});
739			}
740		},
741
742		printValues : function() {
743			console.info(this.tagNames, $(this.selector).val());
744		},
745
746		checkMethod : function() {
747			$findTags = $(this.selector).next(this.classes.sTagsArea);
748			if($findTags.length) {
749				$findTags.remove();
750			}
751			$(this.selector).show();
752			if(typeof this.method !== undefined && this.method == 'destroy') {
753				return false;
754			} else {
755				return true;
756			}
757		},
758
759		refresh : function() {
760			this._setMethod('refresh');
761			this._init();
762		},
763
764		destroy : function() {
765			this._setMethod('destroy');
766			this._init();
767		},
768
769		getActionURL : function(urlString) {
770			var URL = '';
771			if(window !== undefined) {
772				URL = window.location.protocol+'//'+window.location.host;
773			}
774			if(this.isAbsoluteURL(urlString)) {
775				URL = urlString;
776			} else {
777				URL += '/'+urlString.replace(/^\/|\/$/g, '');
778			}
779			return URL;
780		},
781
782		isAbsoluteURL : function(urlString) {
783			var regexURL = new RegExp('^(?:[a-z]+:)?//', 'i');
784			return (regexURL.test(urlString))? true: false;
785		},
786
787		unique: function(list) {
788			var result = [];
789			var _self  = this;
790			$.each(list, function(i, e) {
791				if(typeof e === 'object') {
792					if(!_self.objectInArray(e, result)) {
793						result.push(e);
794					}
795				} else {
796					if($.inArray(e, result) == -1) {
797						result.push(e);
798					}
799				}
800			});
801			return result;
802		},
803
804		objectInArray : function(element, result) {
805			if(result.length) {
806				var present = false;
807				$.each(result, function(i, e) {
808					if(typeof e === 'object') {
809						if(e.value == element.value) {
810							present = true;
811							return false;
812						}
813					} else {
814						if(e == element.value) {
815							present = true;
816							return false;
817						}
818					}
819				});
820				return present;
821			} else {
822				return false;
823			}
824		},
825
826		isSimilarText: function(str1, str2, perc) {
827			if(!this.settings.checkSimilar) {
828				return false;
829			}
830			var percent = this.similarity(str1, str2);
831			return (percent*100 >= perc)? true: false;
832		},
833
834		similarity: function(s1, s2) {
835			var longer = s1;
836			var shorter = s2;
837			if(s1.length < s2.length) {
838				longer = s2;
839				shorter = s1;
840			}
841			var longerLength = longer.length;
842			if(longerLength == 0) {
843				return 1.0;
844			}
845			return (longerLength - this.editDistance(longer, shorter))/parseFloat(longerLength);
846		},
847
848		editDistance: function(s1, s2) {
849			s1 = s1.toLowerCase();
850			s2 = s2.toLowerCase();
851			var costs = new Array();
852			for(var i = 0; i <= s1.length; i++) {
853				var lastValue = i;
854				for(var j = 0; j <= s2.length; j++) {
855					if(i == 0) {
856						costs[j] = j;
857					} else {
858						if(j > 0) {
859							var newValue = costs[j-1];
860							if(s1.charAt(i-1) != s2.charAt(j-1)) {
861								newValue = Math.min(Math.min(newValue, lastValue), costs[j]) + 1;
862							}
863							costs[j-1] = lastValue;
864							lastValue  = newValue;
865						}
866					}
867				}
868				if(i > 0)
869					costs[s2.length] = lastValue;
870			}
871			return costs[s2.length];
872		},
873
874		checkPlusAfter : function(fromBlur) {
875			if(this.settings.showPlusAfter > 0) {
876				if(this.tagNames.length > this.settings.showPlusAfter) {
877					var _self 	   = this;
878					var plusNumber = this.tagNames.length - this.settings.showPlusAfter;
879					if(!this.selectors.plusTag) {
880						var html  = '<span class="'+this.classes.plusItem.substring(1)+'"></span>';
881						this.selectors.plusTag 	= $(html).appendTo(this.selectors.inputArea);
882						$(this.selectors.plusTag).addClass(this.classes.showPlusBg.substring(1));
883						/**
884						 * Show input after focus on input area
885						 */
886						$(this.selectors.inputArea).click(function(){
887							$(_self.selectors.sTagsInput).show();
888						});
889					}
890					$(this.selectors.plusTag).text('+ '+plusNumber);
891					$tagItems = $(this.selectors.inputArea).find(this.classes.tagItem);
892					$tagItems.show();
893					if($(this.selectors.inputArea).hasClass(this.classes.focus.substring(1))){
894						$(this.selectors.plusTag).hide();
895					} else {
896						if(fromBlur && !this.blurTgItems) {
897							this.blurTgItems = $tagItems;
898							setTimeout(function(){
899								if(_self.blurTgItems) {
900									_self.blurTgItems.slice(_self.settings.showPlusAfter).hide();
901								}
902							}, 200);
903						} else {
904							this.blurTgItems = null;
905							$tagItems.slice(this.settings.showPlusAfter).hide();
906						}
907						$(this.selectors.sTagsInput).hide();
908						$(this.selectors.plusTag).show();
909					}
910				} else if(this.selectors.plusTag) {
911					$(this.selectors.inputArea).find(this.classes.tagItem).show();
912					$(this.selectors.plusTag).hide();
913				}
914			}
915		}
916	};
917
918	$.fn.amsifySuggestags = function(options, method) {
919		return this.each(function() {
920			var amsifySuggestags = new AmsifySuggestags(this);
921			amsifySuggestags._settings(options);
922			amsifySuggestags._setMethod(method);
923			amsifySuggestags._init();
924		});
925	};
926}));

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.