PageSourceSearch

https://www.yfyshop.com/Content/EasyAutocomplete/jquery.easy-autocomplete-yfyshop.js

js yfyshop.com collected 2026-10-06 00:38:47 UTC 38,537 bytes, 1,668 lines download raw bytes

1/*
2 * easy-autocomplete
3 * jQuery plugin for autocompletion
4 * 
5 * @author Łukasz Pawełczak (http://github.com/pawelczak)
6 * @version 1.3.5
7 * Copyright  License: 
8 */
9
10/*
11 * 2020-07-03 congren.chen
12 * iphone在輸入中文時會有問題
13 * keyup事件中input value會抓到注音,不是中文
14 * https://www.tangshuang.net/4643.html
15 * https://my.oschina.net/u/1049380/blog/1938823
16 * 在version 1.4.0版,keyup有加檢查event.keyCode === 0
17 * https://github.com/pawelczak/EasyAutocomplete/issues/189
18 * https://github.com/pawelczak/EasyAutocomplete/commit/43f59d4b551a9a3faf974d6f8636c37b81f1685d
19 * var inputPhrase = $field.val(); 但這個值仍然是注音
20 * 造成進入loadData(inputPhrase) ,仍然找不到match的字
21 *
22 * 修正:
23 * 把function loadData(inputPhrase) 拉到外面
24 * 新增事件 function bindInputPropertychange()
25 * 把keyup 那段複製過來
26 * iphone事件順序
27 * keydown event.keyCode=xxx
28 * input propertychange, inputPhrase=中文
29 * keyup event.keyCode=0, inputPhrase=注音
30 * input propertychange inputPhrase=中文
vendor: 27,337 bytes, lines 30-1244
30
31 */
32
33/*
34 * EasyAutocomplete - Configuration 
35 */
36var EasyAutocomplete = (function(scope){
37
38	scope.Configuration = function Configuration(options) {
39		var defaults = {
40			data: "list-required",
41			url: "list-required",
42			dataType: "json",
43
44			listLocation: function(data) {
45				return data;
46			},
47
48			xmlElementName: "",
49
50			getValue: function(element) {
51				return element;
52			},
53
54			autocompleteOff: true,
55
56			placeholder: false,
57
58			ajaxCallback: function() {},
59
60			matchResponseProperty: false,
61
62			list: {
63				sort: {
64					enabled: false,
65					method: function(a, b) {
66						a = defaults.getValue(a);
67						b = defaults.getValue(b);
68						if (a < b) {
69							return -1;
70						}
71						if (a > b) {
72							return 1;
73						}
74						return 0;
75					}
76				},
77
78				maxNumberOfElements: 6,
79
80				hideOnEmptyPhrase: true,
81
82				match: {
83					enabled: false,
84					caseSensitive: false,
85					method: function(element, phrase) {
86
87						if (element.search(phrase) > -1) {
88							return true;
89						} else {
90							return false;
91						}
92					}
93				},
94
95				showAnimation: {
96					type: "normal", //normal|slide|fade
97					time: 400,
98					callback: function() {}
99				},
100
101				hideAnimation: {
102					type: "normal",
103					time: 400,
104					callback: function() {}
105				},
106
107				/* Events */
108				onClickEvent: function() {},
109				onSelectItemEvent: function() {},
110				onLoadEvent: function() {},
111				onChooseEvent: function() {},
112				onKeyEnterEvent: function() {},
113				onMouseOverEvent: function() {},
114				onMouseOutEvent: function() {},	
115				onShowListEvent: function() {},
116				onHideListEvent: function() {}
117			},
118
119			highlightPhrase: true,
120
121			theme: "",
122
123			cssClasses: "",
124
125			minCharNumber: 0,
126
127			requestDelay: 0,
128
129			adjustWidth: true,
130
131			ajaxSettings: {},
132
133			preparePostData: function(data, inputPhrase) {return data;},
134
135			loggerEnabled: true,
136
137			template: "",
138
139			categoriesAssigned: false,
140
141			categories: [{
142				maxNumberOfElements: 4
143			}]
144
145		};
146		
147		var externalObjects = ["ajaxSettings", "template"];
148
149		this.get = function(propertyName) {
150			return defaults[propertyName];
151		};
152
153		this.equals = function(name, value) {
154			if (isAssigned(name)) {
155				if (defaults[name] === value) {
156					return true;
157				}
158			} 
159			
160			return false;
161		};
162
163		this.checkDataUrlProperties = function() {
164			if (defaults.url === "list-required" && defaults.data === "list-required") {
165				return false;
166			}
167			return true;
168		};
169		this.checkRequiredProperties = function() {
170			for (var propertyName in defaults) {
171				if (defaults[propertyName] === "required") {
172					logger.error("Option " + propertyName + " must be defined");
173					return false;
174				}
175			}
176			return true;
177		};
178
179		this.printPropertiesThatDoesntExist = function(consol, optionsToCheck) {
180			printPropertiesThatDoesntExist(consol, optionsToCheck);
181		};
182
183
184		prepareDefaults();
185
186		mergeOptions();
187
188		if (defaults.loggerEnabled === true) {
189			printPropertiesThatDoesntExist(console, options);	
190		}
191
192		addAjaxSettings();
193
194		processAfterMerge();
195		function prepareDefaults() {
196
197			if (options.dataType === "xml") {
198				
199				if (!options.getValue) {
200
201					options.getValue = function(element) {
202						return $(element).text();
203					};
204				}
205
206				
207				if (!options.list) {
208
209					options.list = {};
210				} 
211
212				if (!options.list.sort) {
213					options.list.sort = {};
214				}
215
216
217				options.list.sort.method = function(a, b) {
218					a = options.getValue(a);
219					b = options.getValue(b);
220					if (a < b) {
221						return -1;
222					}
223					if (a > b) {
224						return 1;
225					}
226					return 0;
227				};
228
229				if (!options.list.match) {
230					options.list.match = {};
231				}
232
233				options.list.match.method = function(element, phrase) {
234
235					if (element.search(phrase) > -1) {
236						return true;
237					} else {
238						return false;
239					}
240				};
241
242			}
243			if (options.categories !== undefined && options.categories instanceof Array) {
244
245				var categories = [];
246
247				for (var i = 0, length = options.categories.length; i < length; i += 1) { 
248
249					var category = options.categories[i];
250
251					for (var property in defaults.categories[0]) {
252
253						if (category[property] === undefined) {
254							category[property] = defaults.categories[0][property];
255						}
256					}
257
258					categories.push(category);
259				}
260
261				options.categories = categories;
262			}
263		}
264
265		function mergeOptions() {
266
267			defaults = mergeObjects(defaults, options);
268
269			function mergeObjects(source, target) {
270				var mergedObject = source || {};
271
272				for (var propertyName in source) {
273					if (target[propertyName] !== undefined && target[propertyName] !== null) {
274
275						if (typeof target[propertyName] !== "object" || 
276								target[propertyName] instanceof Array) {
277							mergedObject[propertyName] = target[propertyName];
278						} else {
279							mergeObjects(source[propertyName], target[propertyName]);
280						}
281					}
282				}
283			
284				/* If data is an object */
285				if (target.data !== undefined && target.data !== null && typeof target.data === "object") {
286					mergedObject.data = target.data;
287				}
288
289				return mergedObject;
290			}
291		}	
292
293
294		function processAfterMerge() {
295			
296			if (defaults.url !== "list-required" && typeof defaults.url !== "function") {
297				var defaultUrl = defaults.url;
298				defaults.url = function() {
299					return defaultUrl;
300				};
301			}
302
303			if (defaults.ajaxSettings.url !== undefined && typeof defaults.ajaxSettings.url !== "function") {
304				var defaultUrl = defaults.ajaxSettings.url;
305				defaults.ajaxSettings.url = function() {
306					return defaultUrl;
307				};
308			}
309
310			if (typeof defaults.listLocation === "string") {
311				var defaultlistLocation = defaults.listLocation;
312
313				if (defaults.dataType.toUpperCase() === "XML") {
314					defaults.listLocation = function(data) {
315						return $(data).find(defaultlistLocation);
316					};
317				} else {
318					defaults.listLocation = function(data) {
319						return data[defaultlistLocation];
320					};	
321				}
322			}
323
324			if (typeof defaults.getValue === "string") {
325				var defaultsGetValue = defaults.getValue;
326				defaults.getValue = function(element) {
327					return element[defaultsGetValue];
328				};
329			}
330
331			if (options.categories !== undefined) {
332				defaults.categoriesAssigned = true;
333			}
334
335		}
336
337		function addAjaxSettings() {
338
339			if (options.ajaxSettings !== undefined && typeof options.ajaxSettings === "object") {
340				defaults.ajaxSettings = options.ajaxSettings;
341			} else {
342				defaults.ajaxSettings = {};	
343			}
344			
345		}
346
347		function isAssigned(name) {
348			if (defaults[name] !== undefined && defaults[name] !== null) {
349				return true;
350			} else {
351				return false;
352			}
353		}
354		function printPropertiesThatDoesntExist(consol, optionsToCheck) {
355			
356			checkPropertiesIfExist(defaults, optionsToCheck);
357
358			function checkPropertiesIfExist(source, target) {
359				for(var property in target) {
360					if (source[property] === undefined) {
361						consol.log("Property '" + property + "' does not exist in EasyAutocomplete options API.");		
362					}
363
364					if (typeof source[property] === "object" && $.inArray(property, externalObjects) === -1) {
365						checkPropertiesIfExist(source[property], target[property]);
366					}
367				}	
368			}
369		}
370	};
371
372	return scope;
373
374})(EasyAutocomplete || {});
375
376
377/*
378 * EasyAutocomplete - Logger 
379 */
380var EasyAutocomplete = (function(scope){
381	
382	scope.Logger = function Logger() {
383
384		this.error = function(message) {
385			console.log("ERROR: " + message);
386		};
387
388		this.warning = function(message) {
389			console.log("WARNING: " + message);
390		};
391	};
392
393	return scope;
394
395})(EasyAutocomplete || {});
396	
397
398/*
399 * EasyAutocomplete - Constans
400 */
401var EasyAutocomplete = (function(scope){	
402	
403	scope.Constans = function Constans() {
404		var constants = {
405			CONTAINER_CLASS: "easy-autocomplete-container",
406			CONTAINER_ID: "eac-container-",
407
408			WRAPPER_CSS_CLASS: "easy-autocomplete"
409		};
410
411		this.getValue = function(propertyName) {
412			return constants[propertyName];
413		};
414
415	};
416
417	return scope;
418
419})(EasyAutocomplete || {});
420
421/*
422 * EasyAutocomplete - ListBuilderService 
423 *
424 * @author Łukasz Pawełczak 
425 *
426 */
427var EasyAutocomplete = (function(scope) {
428
429	scope.ListBuilderService = function ListBuilderService(configuration, proccessResponseData) {
430
431
432		this.init = function(data) {
433			var listBuilder = [],
434				builder = {};
435
436			builder.data = configuration.get("listLocation")(data);
437			builder.getValue = configuration.get("getValue");
438			builder.maxListSize = configuration.get("list").maxNumberOfElements;
439
440				
441			listBuilder.push(builder);
442
443			return listBuilder;
444		};
445
446		this.updateCategories = function(listBuilder, data) {
447			
448			if (configuration.get("categoriesAssigned")) {
449
450				listBuilder = [];
451
452				for(var i = 0; i < configuration.get("categories").length; i += 1) {
453
454					var builder = convertToListBuilder(configuration.get("categories")[i], data);
455
456					listBuilder.push(builder);
457				}
458
459			} 
460
461			return listBuilder;
462		};
463
464		this.convertXml = function(listBuilder) {
465			if(configuration.get("dataType").toUpperCase() === "XML") {
466
467				for(var i = 0; i < listBuilder.length; i += 1) {
468					listBuilder[i].data = convertXmlToList(listBuilder[i]);
469				}
470			}
471
472			return listBuilder;
473		};
474
475		this.processData = function(listBuilder, inputPhrase) {
476
477			for(var i = 0, length = listBuilder.length; i < length; i+=1) {
478				listBuilder[i].data = proccessResponseData(configuration, listBuilder[i], inputPhrase);
479			}
480
481			return listBuilder;
482		};
483
484		this.checkIfDataExists = function(listBuilders) {
485
486			for(var i = 0, length = listBuilders.length; i < length; i += 1) {
487
488				if (listBuilders[i].data !== undefined && listBuilders[i].data instanceof Array) {
489					if (listBuilders[i].data.length > 0) {
490						return true;
491					}
492				} 
493			}
494
495			return false;
496		};
497
498
499		function convertToListBuilder(category, data) {
500
501			var builder = {};
502
503			if(configuration.get("dataType").toUpperCase() === "XML") {
504
505				builder = convertXmlToListBuilder();
506			} else {
507
508				builder = convertDataToListBuilder();
509			}
510			
511
512			if (category.header !== undefined) {
513				builder.header = category.header;
514			}
515
516			if (category.maxNumberOfElements !== undefined) {
517				builder.maxNumberOfElements = category.maxNumberOfElements;
518			}
519
520			if (configuration.get("list").maxNumberOfElements !== undefined) {
521
522				builder.maxListSize = configuration.get("list").maxNumberOfElements;
523			}
524
525			if (category.getValue !== undefined) {
526
527				if (typeof category.getValue === "string") {
528					var defaultsGetValue = category.getValue;
529					builder.getValue = function(element) {
530						return element[defaultsGetValue];
531					};
532				} else if (typeof category.getValue === "function") {
533					builder.getValue = category.getValue;
534				}
535
536			} else {
537				builder.getValue = configuration.get("getValue");	
538			}
539			
540
541			return builder;
542
543
544			function convertXmlToListBuilder() {
545
546				var builder = {},
547					listLocation;
548
549				if (category.xmlElementName !== undefined) {
550					builder.xmlElementName = category.xmlElementName;
551				}
552
553				if (category.listLocation !== undefined) {
554
555					listLocation = category.listLocation;
556				} else if (configuration.get("listLocation") !== undefined) {
557
558					listLocation = configuration.get("listLocation");
559				}
560
561				if (listLocation !== undefined) {
562					if (typeof listLocation === "string") {
563						builder.data = $(data).find(listLocation);
564					} else if (typeof listLocation === "function") {
565
566						builder.data = listLocation(data);
567					}
568				} else {
569
570					builder.data = data;
571				}
572
573				return builder;
574			}
575
576
577			function convertDataToListBuilder() {
578
579				var builder = {};
580
581				if (category.listLocation !== undefined) {
582
583					if (typeof category.listLocation === "string") {
584						builder.data = data[category.listLocation];
585					} else if (typeof category.listLocation === "function") {
586						builder.data = category.listLocation(data);
587					}
588				} else {
589					builder.data = data;
590				}
591
592				return builder;
593			}
594		}
595
596		function convertXmlToList(builder) {
597			var simpleList = [];
598
599			if (builder.xmlElementName === undefined) {
600				builder.xmlElementName = configuration.get("xmlElementName");
601			}
602
603
604			$(builder.data).find(builder.xmlElementName).each(function() {
605				simpleList.push(this);
606			});
607
608			return simpleList;
609		}
610
611	};
612
613	return scope;
614
615})(EasyAutocomplete || {});
616
617
618/*
619 * EasyAutocomplete - Data proccess module
620 *
621 * Process list to display:
622 * - sort 
623 * - decrease number to specific number
624 * - show only matching list
625 *
626 */
627var EasyAutocomplete = (function(scope) {
628
629	scope.proccess = function proccessData(config, listBuilder, phrase) {
630
631		scope.proccess.match = match;
632
633		var list = listBuilder.data,
634			inputPhrase = phrase;//TODO REFACTOR
635
636		list = findMatch(list, inputPhrase);
637		list = reduceElementsInList(list);
638		list = sort(list);
639
640		return list;
641
642
643		function findMatch(list, phrase) {
644			var preparedList = [],
645				value = "";
646
647			if (config.get("list").match.enabled) {
648
649				for(var i = 0, length = list.length; i < length; i += 1) {
650
651					value = config.get("getValue")(list[i]);
652					
653					if (match(value, phrase)) {
654						preparedList.push(list[i]);	
655					}
656					
657				}
658
659			} else {
660				preparedList = list;
661			}
662
663			return preparedList;
664		}
665
666		function match(value, phrase) {
667
668			if (!config.get("list").match.caseSensitive) {
669
670				if (typeof value === "string") {
671					value = value.toLowerCase();	
672				}
673				
674				phrase = phrase.toLowerCase();
675			}
676			if (config.get("list").match.method(value, phrase)) {
677				return true;
678			} else {
679				return false;
680			}
681		}
682
683		function reduceElementsInList(list) {
684			if (listBuilder.maxNumberOfElements !== undefined && list.length > listBuilder.maxNumberOfElements) {
685				list = list.slice(0, listBuilder.maxNumberOfElements);
686			}
687
688			return list;
689		}
690
691		function sort(list) {
692			if (config.get("list").sort.enabled) {
693				list.sort(config.get("list").sort.method);
694			}
695
696			return list;
697		}
698		
699	};
700
701
702	return scope;
703
704
705})(EasyAutocomplete || {});
706
707
708/*
709 * EasyAutocomplete - Template 
710 *
711 * 
712 *
713 */
714var EasyAutocomplete = (function(scope){
715
716	scope.Template = function Template(options) {
717
718
719		var genericTemplates = {
720			basic: {
721				type: "basic",
722				method: function(element) { return element; },
723				cssClass: ""
724			},
725			description: {
726				type: "description",
727				fields: {
728					description: "description"
729				},
730				method: function(element) {	return element + " - description"; },
731				cssClass: "eac-description"
732			},
733			iconLeft: {
734				type: "iconLeft",
735				fields: {
736					icon: ""
737				},
738				method: function(element) {
739					return element;
740				},
741				cssClass: "eac-icon-left"
742			},
743			iconRight: {
744				type: "iconRight",
745				fields: {
746					iconSrc: ""
747				},
748				method: function(element) {
749					return element;
750				},
751				cssClass: "eac-icon-right"
752			},
753			links: {
754				type: "links",
755				fields: {
756					link: ""
757				},
758				method: function(element) {
759					return element;
760				},
761				cssClass: ""
762			},
763			custom: {
764				type: "custom",
765				method: function() {},
766				cssClass: ""
767			}
768		},
769
770
771
772		/*
773		 * Converts method with {{text}} to function
774		 */
775		convertTemplateToMethod = function(template) {
776
777
778			var _fields = template.fields,
779				buildMethod;
780
781			if (template.type === "description") {
782
783				buildMethod = genericTemplates.description.method; 
784
785				if (typeof _fields.description === "string") {
786					buildMethod = function(elementValue, element) {
787						return elementValue + " - <span>" + element[_fields.description] + "</span>";
788					};					
789				} else if (typeof _fields.description === "function") {
790					buildMethod = function(elementValue, element) {
791						return elementValue + " - <span>" + _fields.description(element) + "</span>";
792					};	
793				}
794
795				return buildMethod;
796			}
797
798			if (template.type === "iconRight") {
799
800				if (typeof _fields.iconSrc === "string") {
801					buildMethod = function(elementValue, element) {
802						return elementValue + "<img class='eac-icon' src='" + element[_fields.iconSrc] + "' />" ;
803					};					
804				} else if (typeof _fields.iconSrc === "function") {
805					buildMethod = function(elementValue, element) {
806						return elementValue + "<img class='eac-icon' src='" + _fields.iconSrc(element) + "' />" ;
807					};
808				}
809
810				return buildMethod;
811			}
812
813
814			if (template.type === "iconLeft") {
815
816				if (typeof _fields.iconSrc === "string") {
817					buildMethod = function(elementValue, element) {
818						return "<img class='eac-icon' src='" + element[_fields.iconSrc] + "' />" + elementValue;
819					};					
820				} else if (typeof _fields.iconSrc === "function") {
821					buildMethod = function(elementValue, element) {
822						return "<img class='eac-icon' src='" + _fields.iconSrc(element) + "' />" + elementValue;
823					};
824				}
825
826				return buildMethod;
827			}
828
829			if(template.type === "links") {
830
831				if (typeof _fields.link === "string") {
832					buildMethod = function(elementValue, element) {
833						return "<a href='" + element[_fields.link] + "' >" + elementValue + "</a>";
834					};					
835				} else if (typeof _fields.link === "function") {
836					buildMethod = function(elementValue, element) {
837						return "<a href='" + _fields.link(element) + "' >" + elementValue + "</a>";
838					};
839				}
840
841				return buildMethod;
842			}
843
844
845			if (template.type === "custom") {
846
847				return template.method;
848			}
849
850			return genericTemplates.basic.method;
851
852		},
853
854
855		prepareBuildMethod = function(options) {
856			if (!options || !options.type) {
857
858				return genericTemplates.basic.method;
859			}
860
861			if (options.type && genericTemplates[options.type]) {
862
863				return convertTemplateToMethod(options);
864			} else {
865
866				return genericTemplates.basic.method;
867			}
868
869		},
870
871		templateClass = function(options) {
872			var emptyStringFunction = function() {return "";};
873
874			if (!options || !options.type) {
875
876				return emptyStringFunction;
877			}
878
879			if (options.type && genericTemplates[options.type]) {
880				return (function () { 
881					var _cssClass = genericTemplates[options.type].cssClass;
882					return function() { return _cssClass;};
883				})();
884			} else {
885				return emptyStringFunction;
886			}
887		};
888
889
890		this.getTemplateClass = templateClass(options);
891
892		this.build = prepareBuildMethod(options);
893
894
895	};
896
897	return scope;
898
899})(EasyAutocomplete || {});
900
901
902/*
903 * EasyAutocomplete - jQuery plugin for autocompletion
904 *
905 */
906var EasyAutocomplete = (function(scope) {
907
908	
909	scope.main = function Core($input, options) {
910				
911		var module = {
912				name: "EasyAutocomplete",
913				shortcut: "eac"
914			};
915
916		var consts = new scope.Constans(),
917			config = new scope.Configuration(options),
918			logger = new scope.Logger(),
919			template = new scope.Template(options.template),
920			listBuilderService = new scope.ListBuilderService(config, scope.proccess),
921			checkParam = config.equals,
922
923			$field = $input, 
924			$container = "",
925			elementsList = [],
926			selectedElement = -1,
927			requestDelayTimeoutId;
928
929		scope.consts = consts;
930
931		this.getConstants = function() {
932			return consts;
933		};
934
935		this.getConfiguration = function() {
936			return config;
937		};
938
939		this.getContainer = function() {
940			return $container;
941		};
942
943		this.getSelectedItemIndex = function() {
944			return selectedElement;
945		};
946
947		this.getItems = function () {
948			return elementsList;
949		};
950
951		this.getItemData = function(index) {
952
953			if (elementsList.length < index || elementsList[index] === undefined) {
954				return -1;
955			} else {
956				return elementsList[index];
957			}
958		};
959
960		this.getSelectedItemData = function() {
961			return this.getItemData(selectedElement);
962		};
963
964		this.build = function() {
965			prepareField();
966		};
967
968		this.init = function() {
969			init();
970		};
971		function init() {
972
973			if ($field.length === 0) {
974				logger.error("Input field doesn't exist.");
975				return;
976			}
977
978			if (!config.checkDataUrlProperties()) {
979				logger.error("One of options variables 'data' or 'url' must be defined.");
980				return;
981			}
982
983			if (!config.checkRequiredProperties()) {
984				logger.error("Will not work without mentioned properties.");
985				return;
986			}
987
988
989			prepareField();
990			bindEvents();	
991
992		}
993		function prepareField() {
994
995				
996			if ($field.parent().hasClass(consts.getValue("WRAPPER_CSS_CLASS"))) {
997				removeContainer();
998				removeWrapper();
999			} 
1000			
1001			createWrapper();
1002			createContainer();	
1003
1004			$container = $("#" + getContainerId());
1005			if (config.get("placeholder")) {
1006				$field.attr("placeholder", config.get("placeholder"));
1007			}
1008
1009
1010			function createWrapper() {
1011				var $wrapper = $("<div>"),
1012					classes = consts.getValue("WRAPPER_CSS_CLASS");
1013
1014			
1015				if (config.get("theme") && config.get("theme") !== "") {
1016					classes += " eac-" + config.get("theme");
1017				}
1018
1019				if (config.get("cssClasses") && config.get("cssClasses") !== "") {
1020					classes += " " + config.get("cssClasses");
1021				}
1022
1023				if (template.getTemplateClass() !== "") {
1024					classes += " " + template.getTemplateClass();
1025				}
1026				
1027
1028				$wrapper
1029					.addClass(classes);
1030				$field.wrap($wrapper);
1031
1032
1033				if (config.get("adjustWidth") === true) {
1034					adjustWrapperWidth();	
1035				}
1036				
1037
1038			}
1039
1040			function adjustWrapperWidth() {
1041				var fieldWidth = $field.outerWidth();
1042
1043				$field.parent().css("width", fieldWidth);				
1044			}
1045
1046			function removeWrapper() {
1047				$field.unwrap();
1048			}
1049
1050			function createContainer() {
1051				var $elements_container = $("<div>").addClass(consts.getValue("CONTAINER_CLASS"));
1052
1053				$elements_container
1054						.attr("id", getContainerId())
1055						.prepend($("<ul>"));
1056
1057
1058				(function() {
1059
1060					$elements_container
1061						/* List show animation */
1062						.on("show.eac", function() {
1063
1064							switch(config.get("list").showAnimation.type) {
1065
1066								case "slide":
1067									var animationTime = config.get("list").showAnimation.time,
1068										callback = config.get("list").showAnimation.callback;
1069
1070									$elements_container.find("ul").slideDown(animationTime, callback);
1071								break;
1072
1073								case "fade":
1074									var animationTime = config.get("list").showAnimation.time,
1075										callback = config.get("list").showAnimation.callback;
1076
1077									$elements_container.find("ul").fadeIn(animationTime), callback;
1078								break;
1079
1080								default:
1081									$elements_container.find("ul").show();
1082								break;
1083							}
1084
1085							config.get("list").onShowListEvent();
1086							
1087						})
1088						/* List hide animation */
1089						.on("hide.eac", function() {
1090
1091							switch(config.get("list").hideAnimation.type) {
1092
1093								case "slide":
1094									var animationTime = config.get("list").hideAnimation.time,
1095										callback = config.get("list").hideAnimation.callback;
1096
1097									$elements_container.find("ul").slideUp(animationTime, callback);
1098								break;
1099
1100								case "fade":
1101									var animationTime = config.get("list").hideAnimation.time,
1102										callback = config.get("list").hideAnimation.callback;
1103
1104									$elements_container.find("ul").fadeOut(animationTime, callback);
1105								break;
1106
1107								default:
1108									$elements_container.find("ul").hide();
1109								break;
1110							}
1111
1112							config.get("list").onHideListEvent();
1113
1114						})
1115						.on("selectElement.eac", function() {
1116							$elements_container.find("ul li").removeClass("selected");
1117							$elements_container.find("ul li").eq(selectedElement).addClass("selected");
1118
1119							config.get("list").onSelectItemEvent();
1120						})
1121						.on("loadElements.eac", function(event, listBuilders, phrase) {
1122			
1123
1124							var $item = "",
1125								$listContainer = $elements_container.find("ul");
1126
1127							$listContainer
1128								.empty()
1129								.detach();
1130
1131							elementsList = [];
1132							var counter = 0;
1133							for(var builderIndex = 0, listBuildersLength = listBuilders.length; builderIndex < listBuildersLength; builderIndex += 1) {
1134
1135								var listData = listBuilders[builderIndex].data;
1136
1137								if (listData.length === 0) {
1138									continue;
1139								}
1140
1141								if (listBuilders[builderIndex].header !== undefined && listBuilders[builderIndex].header.length > 0) {
1142									$listContainer.append("<div class='eac-category' >" + listBuilders[builderIndex].header + "</div>");
1143								}
1144
1145								for(var i = 0, listDataLength = listData.length; i < listDataLength && counter < listBuilders[builderIndex].maxListSize; i += 1) {
1146									$item = $("<li><div class='eac-item'></div></li>");
1147									
1148
1149									(function() {
1150										var j = i,
1151											itemCounter = counter,
1152											elementsValue = listBuilders[builderIndex].getValue(listData[j]);
1153
1154										$item.find(" > div")
1155											.on("click", function() {
1156
1157												$field.val(elementsValue).trigger("change");
1158
1159												selectedElement = itemCounter;
1160												selectElement(itemCounter);
1161
1162												config.get("list").onClickEvent();
1163												config.get("list").onChooseEvent();
1164											})
1165											.mouseover(function() {
1166
1167												selectedElement = itemCounter;
1168												selectElement(itemCounter);	
1169
1170												config.get("list").onMouseOverEvent();
1171											})
1172											.mouseout(function() {
1173												config.get("list").onMouseOutEvent();
1174											})
1175											.html(template.build(highlight(elementsValue, phrase), listData[j]));
1176									})();
1177
1178									$listContainer.append($item);
1179									elementsList.push(listData[i]);
1180									counter += 1;
1181								}
1182							}
1183
1184							$elements_container.append($listContainer);
1185
1186							config.get("list").onLoadEvent();
1187						});
1188
1189				})();
1190
1191				$field.after($elements_container);
1192			}
1193
1194			function removeContainer() {
1195				$field.next("." + consts.getValue("CONTAINER_CLASS")).remove();
1196			}
1197
1198			function highlight(string, phrase) {
1199
1200				if(config.get("highlightPhrase") && phrase !== "") {
1201					return highlightPhrase(string, phrase);	
1202				} else {
1203					return string;
1204				}
1205					
1206			}
1207
1208			function escapeRegExp(str) {
1209				return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
1210 			}
1211
1212			function highlightPhrase(string, phrase) {
1213				var escapedPhrase = escapeRegExp(phrase);
1214				return (string + "").replace(new RegExp("(" + escapedPhrase + ")", "gi") , "<b>$1</b>");
1215			}
1216
1217
1218
1219		}
1220		function getContainerId() {
1221			
1222			var elementId = $field.attr("id");
1223
1224			elementId = consts.getValue("CONTAINER_ID") + elementId;
1225
1226			return elementId;
1227		}
1228		function bindEvents() {
1229
1230			bindAllEvents();
1231			
1232
1233			function bindAllEvents() {
1234				if (checkParam("autocompleteOff", true)) {
1235					removeAutocomplete();
1236				}
1237
1238				bindFocusOut();
1239				bindKeyup();
1240				bindKeydown();
1241				bindKeypress();
1242				bindFocus();
1243				bindBlur();
1244			
vendor: 9,630 bytes, lines 1244-1640
1244	bindInputPropertychange();
1245			}
1246
1247			function bindFocusOut() {
1248			    $field.focusout(function () {
1249
1250					var fieldValue = $field.val(),
1251						phrase;
1252
1253					if (!config.get("list").match.caseSensitive) {
1254						fieldValue = fieldValue.toLowerCase();
1255					}
1256
1257					for (var i = 0, length = elementsList.length; i < length; i += 1) {
1258
1259						phrase = config.get("getValue")(elementsList[i]);
1260						if (!config.get("list").match.caseSensitive) {
1261							phrase = phrase.toLowerCase();
1262						}
1263
1264						if (phrase === fieldValue) {
1265							selectedElement = i;
1266							selectElement(selectedElement);
1267							return;
1268						}
1269					}
1270				});
1271			}
1272
1273			function bindInputPropertychange() {
1274			    $($field).on('input propertychange', function (event) {
1275
1276			        var inputPhrase = $field.val();
1277
1278			        if (!(config.get("list").hideOnEmptyPhrase === true && event.keyCode === 8 && inputPhrase === "")) {
1279
1280			            if (config.get("requestDelay") > 0) {
1281			                if (requestDelayTimeoutId !== undefined) {
1282			                    clearTimeout(requestDelayTimeoutId);
1283			                }
1284
1285			                requestDelayTimeoutId = setTimeout(function () { loadData(inputPhrase); }, config.get("requestDelay"));
1286			            } else {
1287			                loadData(inputPhrase);
1288			            }
1289
1290			        } else {
1291			            hideContainer();
1292			        }
1293
1294			    });
1295			}
1296
1297			function bindKeyup() {
1298				$field
1299				.off("keyup")
1300				.keyup(function(event) {
1301
1302					switch(event.keyCode) {
1303
1304						case 27:
1305
1306							hideContainer();
1307							loseFieldFocus();
1308						break;
1309
1310						case 38:
1311
1312							event.preventDefault();
1313
1314							if(elementsList.length > 0 && selectedElement > 0) {
1315
1316								selectedElement -= 1;
1317
1318								$field.val(config.get("getValue")(elementsList[selectedElement]));
1319
1320								selectElement(selectedElement);
1321
1322							}						
1323						break;
1324
1325						case 40:
1326
1327							event.preventDefault();
1328
1329							if(elementsList.length > 0 && selectedElement < elementsList.length - 1) {
1330
1331								selectedElement += 1;
1332
1333								$field.val(config.get("getValue")(elementsList[selectedElement]));
1334
1335								selectElement(selectedElement);
1336								
1337							}
1338
1339						break;
1340
1341						default:
1342
1343						    if (event.keyCode > 40 || event.keyCode === 8 || event.keyCode === 0) {
1344							    var inputPhrase = $field.val();
1345
1346							    if (!(config.get("list").hideOnEmptyPhrase === true && event.keyCode === 8 && inputPhrase === "")) {
1347
1348									if (config.get("requestDelay") > 0) {
1349										if (requestDelayTimeoutId !== undefined) {
1350											clearTimeout(requestDelayTimeoutId);
1351										}
1352
1353										requestDelayTimeoutId = setTimeout(function () { loadData(inputPhrase);}, config.get("requestDelay"));
1354									} else {
1355										loadData(inputPhrase);
1356									}
1357
1358								} else {
1359									hideContainer();
1360								}
1361								
1362							}
1363
1364
1365						break;
1366					}
1367				
1368				});
1369			}
1370
1371			function loadData(inputPhrase) {
1372
1373
1374			    if (inputPhrase.length < config.get("minCharNumber")) {
1375			        return;
1376			    }
1377
1378
1379			    if (config.get("data") !== "list-required") {
1380
1381			        var data = config.get("data");
1382
1383			        var listBuilders = listBuilderService.init(data);
1384
1385			        listBuilders = listBuilderService.updateCategories(listBuilders, data);
1386
1387			        listBuilders = listBuilderService.processData(listBuilders, inputPhrase);
1388
1389			        loadElements(listBuilders, inputPhrase);
1390
1391			        if ($field.parent().find("li").length > 0) {
1392			            showContainer();
1393			        } else {
1394			            hideContainer();
1395			        }
1396
1397			    }
1398
1399			    var settings = createAjaxSettings();
1400
1401			    if (settings.url === undefined || settings.url === "") {
1402			        settings.url = config.get("url");
1403			    }
1404
1405			    if (settings.dataType === undefined || settings.dataType === "") {
1406			        settings.dataType = config.get("dataType");
1407			    }
1408
1409
1410			    if (settings.url !== undefined && settings.url !== "list-required") {
1411
1412			        settings.url = settings.url(inputPhrase);
1413
1414			        settings.data = config.get("preparePostData")(settings.data, inputPhrase);
1415
1416			        $.ajax(settings)
1417                        .done(function (data) {
1418
1419                            var listBuilders = listBuilderService.init(data);
1420
1421                            listBuilders = listBuilderService.updateCategories(listBuilders, data);
1422
1423                            listBuilders = listBuilderService.convertXml(listBuilders);
1424                            if (checkInputPhraseMatchResponse(inputPhrase, data)) {
1425
1426                                listBuilders = listBuilderService.processData(listBuilders, inputPhrase);
1427
1428                                loadElements(listBuilders, inputPhrase);
1429
1430                            }
1431
1432                            if (listBuilderService.checkIfDataExists(listBuilders) && $field.parent().find("li").length > 0) {
1433                                showContainer();
1434                            } else {
1435                                hideContainer();
1436                            }
1437
1438                            config.get("ajaxCallback")();
1439
1440                        })
1441                        .fail(function () {
1442                            logger.warning("Fail to load response data");
1443                        })
1444                        .always(function () {
1445
1446                        });
1447			    }
1448
1449
1450
1451			    function createAjaxSettings() {
1452
1453			        var settings = {},
1454                        ajaxSettings = config.get("ajaxSettings") || {};
1455
1456			        for (var set in ajaxSettings) {
1457			            settings[set] = ajaxSettings[set];
1458			        }
1459
1460			        return settings;
1461			    }
1462
1463			    function checkInputPhraseMatchResponse(inputPhrase, data) {
1464
1465			        if (config.get("matchResponseProperty") !== false) {
1466			            if (typeof config.get("matchResponseProperty") === "string") {
1467			                return (data[config.get("matchResponseProperty")] === inputPhrase);
1468			            }
1469
1470			            if (typeof config.get("matchResponseProperty") === "function") {
1471			                return (config.get("matchResponseProperty")(data) === inputPhrase);
1472			            }
1473
1474			            return true;
1475			        } else {
1476			            return true;
1477			        }
1478
1479			    }
1480
1481			}
1482
1483			function bindKeydown() {
1484				$field
1485					.on("keydown", function(evt) {
1486	        		    evt = evt || window.event;
1487	        		    var keyCode = evt.keyCode;
1488	        		    if (keyCode === 38) {
1489	        		        suppressKeypress = true; 
1490	        		        return false;
1491	        		    }
1492		        	})
1493					.keydown(function (event) {
1494
1495						if (event.keyCode === 13 && selectedElement > -1) {
1496
1497							$field.val(config.get("getValue")(elementsList[selectedElement]));
1498
1499							config.get("list").onKeyEnterEvent();
1500							config.get("list").onChooseEvent();
1501
1502							selectedElement = -1;
1503							hideContainer();
1504
1505							event.preventDefault();
1506						}
1507					});
1508			}
1509
1510			function bindKeypress() {
1511				$field
1512				.off("keypress");
1513			}
1514
1515			function bindFocus() {
1516			    $field.focus(function () {
1517
1518					if ($field.val() !== "" && elementsList.length > 0) {
1519						
1520						selectedElement = -1;
1521						showContainer();	
1522					}
1523									
1524				});
1525			}
1526
1527			function bindBlur() {
1528			    $field.blur(function () {
1529					setTimeout(function() { 
1530						
1531						selectedElement = -1;
1532						hideContainer();
1533					}, 250);
1534				});
1535			}
1536
1537			function removeAutocomplete() {
1538				$field.attr("autocomplete","off");
1539			}
1540
1541		}
1542
1543		function showContainer() {
1544			$container.trigger("show.eac");
1545		}
1546
1547		function hideContainer() {
1548			$container.trigger("hide.eac");
1549		}
1550
1551		function selectElement(index) {
1552			
1553			$container.trigger("selectElement.eac", index);
1554		}
1555
1556		function loadElements(list, phrase) {
1557			$container.trigger("loadElements.eac", [list, phrase]);
1558		}
1559
1560		function loseFieldFocus() {
1561			$field.trigger("blur");
1562		}
1563
1564
1565	};
1566	scope.eacHandles = [];
1567
1568	scope.getHandle = function(id) {
1569		return scope.eacHandles[id];
1570	};
1571
1572	scope.inputHasId = function(input) {
1573
1574		if($(input).attr("id") !== undefined && $(input).attr("id").length > 0) {
1575			return true;
1576		} else {
1577			return false;
1578		}
1579
1580	};
1581
1582	scope.assignRandomId = function(input) {
1583
1584		var fieldId = "";
1585
1586		do {
1587			fieldId = "eac-" + Math.floor(Math.random() * 10000);		
1588		} while ($("#" + fieldId).length !== 0);
1589		
1590		elementId = scope.consts.getValue("CONTAINER_ID") + fieldId;
1591
1592		$(input).attr("id", fieldId);
1593 
1594	};
1595
1596	scope.setHandle = function(handle, id) {
1597		scope.eacHandles[id] = handle;
1598	};
1599
1600
1601	return scope;
1602
1603})(EasyAutocomplete || {});
1604
1605(function($) {
1606
1607	$.fn.easyAutocomplete = function(options) {
1608
1609		return this.each(function() {
1610			var $this = $(this),
1611				eacHandle = new EasyAutocomplete.main($this, options);
1612
1613			if (!EasyAutocomplete.inputHasId($this)) {
1614				EasyAutocomplete.assignRandomId($this);
1615			}
1616
1617			eacHandle.init();
1618
1619			EasyAutocomplete.setHandle(eacHandle, $this.attr("id"));
1620
1621		});
1622	};
1623
1624	$.fn.getSelectedItemIndex = function() {
1625
1626		var inputId = $(this).attr("id");
1627
1628		if (inputId !== undefined) {
1629			return EasyAutocomplete.getHandle(inputId).getSelectedItemIndex();
1630		}
1631
1632		return -1;
1633	};
1634
1635	$.fn.getItems = function () {
1636
1637		var inputId = $(this).attr("id");
1638
1639		if (inputId !== undefined) {
1640			return EasyAutocomplete.getHandle(inputId).getItems();
1641		}
1642
1643		return -1;
1644	};
1645
1646	$.fn.getItemData = function(index) {
1647
1648		var inputId = $(this).attr("id");
1649
1650		if (inputId !== undefined && index > -1) {
1651			return EasyAutocomplete.getHandle(inputId).getItemData(index);
1652		}
1653
1654		return -1;
1655	};
1656
1657	$.fn.getSelectedItemData = function() {
1658
1659		var inputId = $(this).attr("id");
1660
1661		if (inputId !== undefined) {
1662			return EasyAutocomplete.getHandle(inputId).getSelectedItemData();
1663		}
1664
1665		return -1;
1666	};
1667
1668})(jQuery);

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.