PageSourceSearch

https://ventilatorbolt.hu/!common_packages/jquery/plugins/autocomplete/autocomplete.js?mod_time=1759314983

js ventilatorbolt.hu collected 2026-10-02 15:06:00 UTC 20,913 bytes, 785 lines download raw bytes

1/*
2 * Autocomplete - jQuery plugin 1.1pre
3 *
4 * Copyright (c) 2007 Dylan Verheul, Dan G. Switzer, Anjesh Tuladhar, Jörn Zaefferer
5 *
6 * Dual licensed under the MIT and GPL licenses:
7 *   http://www.opensource.org/licenses/mit-license.php
8 *   http://www.gnu.org/licenses/gpl.html
9 *
10 * Revision: $Id: jquery.autocomplete.js 5785 2008-07-12 10:37:33Z joern.zaefferer $
11 *
12 */
13
14;(function($) {
15	
16$.fn.extend({
17	autocomplete: function(urlOrData, options) {
18		var isUrl = typeof urlOrData == "string";
19		options = $.extend({}, $.Autocompleter.defaults, {
20			url: isUrl ? urlOrData : null,
21			data: isUrl ? null : urlOrData,
22			delay: isUrl ? $.Autocompleter.defaults.delay : 10,
23			max: options && !options.scroll ? 10 : 150
24		}, options);
25		
26		// if highlight is set to false, replace it with a do-nothing function
27		options.highlight = options.highlight || function(value) { return value; };
28		
29		// if the formatMatch option is not specified, then use formatItem for backwards compatibility
30		options.formatMatch = options.formatMatch || options.formatItem;
31		
32		return this.each(function() {
33			new $.Autocompleter(this, options);
34		});
35	},
36	result: function(handler) {
37		return this.bind("result", handler);
38	},
39	onSelect: null,
40	search: function(handler) {
41		return this.trigger("search", [handler]);
42	},
43	flushCache: function() {
44		return this.trigger("flushCache");
45	},
46	setOptions: function(options){
47		return this.trigger("setOptions", [options]);
48	},
49	unautocomplete: function() {
50		return this.trigger("unautocomplete");
51	}
52});
53
54$.Autocompleter = function(input, options) {
55
56	var KEY = {
57		UP: 38,
58		DOWN: 40,
59		DEL: 46,
60		TAB: 9,
61		RETURN: 13,
62		ESC: 27,
63		COMMA: 188,
64		PAGEUP: 33,
65		PAGEDOWN: 34,
66		BACKSPACE: 8
67	};
68
69	// Create $ object for input element
70	var $input = $(input).attr("autocomplete", "off").addClass(options.inputClass);
71
72	var timeout;
73	var previousValue = "";
74	var cache = $.Autocompleter.Cache(options);
75	var hasFocus = 0;
76	var lastKeyPressCode;
77	var config = {
78		mouseDownOnSelect: false
79	};
80	var select = $.Autocompleter.Select(options, input, selectCurrent, config);
81	
82	var blockSubmit;
83	
84	// prevent form submit in opera when selecting with return key
85    // jquery 3.7.1 does not support $.browser object
86    /*	$.browser.opera && $(input.form).bind("submit.autocomplete", function() {
87		if (blockSubmit) {
88			blockSubmit = false;
89			return false;
90		}
91	});*/
92	
93	// only opera doesn't trigger keydown multiple times while pressed, others don't work with keypress at all
94	$input.bind("keydown.autocomplete", function(event) {
95		// track last key pressed
96		lastKeyPressCode = event.keyCode;
97		switch(event.keyCode) {
98		
99			case KEY.UP:
100				event.preventDefault();
101				if ( select.visible() ) {
102					select.prev();
103				} else {
104					onChange(0, true);
105				}
106				break;
107				
108			case KEY.DOWN:
109				event.preventDefault();
110				if ( select.visible() ) {
111					select.next();
112				} else {
113					onChange(0, true);
114				}
115				break;
116				
117			case KEY.PAGEUP:
118				event.preventDefault();
119				if ( select.visible() ) {
120					select.pageUp();
121				} else {
122					onChange(0, true);
123				}
124				break;
125				
126			case KEY.PAGEDOWN:
127				event.preventDefault();
128				if ( select.visible() ) {
129					select.pageDown();
130				} else {
131					onChange(0, true);
132				}
133				break;
134			
135			// matches also semicolon
136			case options.multiple && $.trim(options.multipleSeparator) == "," && KEY.COMMA:
137			case KEY.TAB:
138			case KEY.RETURN:
139				if( selectCurrent() ) {
140					// stop default to prevent a form submit, Opera needs special handling
141					event.preventDefault();
142					blockSubmit = true;
143					return false;
144				}
145				break;
146				
147			case KEY.ESC:
148				select.hide();
149				break;
150				
151			default:
152				clearTimeout(timeout);
153				timeout = setTimeout(onChange, options.delay);
154				break;
155		}
156	}).focus(function(){
157		// track whether the field has focus, we shouldn't process any
158		// results if the field no longer has focus
159		hasFocus++;
160	}).blur(function() {
161		hasFocus = 0;
162		if (!config.mouseDownOnSelect) {
163			hideResults();
164		}
165	}).click(function() {
166		// show select when clicking in a focused field
167		if ( hasFocus++ > 1 && !select.visible() ) {
168			onChange(0, true);
169		}
170	}).bind("search", function() {
171		// TODO why not just specifying both arguments?
172		var fn = (arguments.length > 1) ? arguments[1] : null;
173		function findValueCallback(q, data) {
174			var result;
175			if( data && data.length ) {
176				for (var i=0; i < data.length; i++) {
177					if( data[i].result.toLowerCase() == q.toLowerCase() ) {
178						result = data[i];
179						break;
180					}
181				}
182			}
183			if( typeof fn == "function" ) fn(result);
184			else $input.trigger("result", result && [result.data, result.value]);
185		}
186		$.each(trimWords($input.val()), function(i, value) {
187			request(value, findValueCallback, findValueCallback);
188		});
189	}).bind("flushCache", function() {
190		cache.flush();
191	}).bind("setOptions", function() {
192		$.extend(options, arguments[1]);
193		// if we've updated the data, repopulate
194		if ( "data" in arguments[1] )
195			cache.populate();
196	}).bind("unautocomplete", function() {
197		select.unbind();
198		$input.unbind();
199		$(input.form).unbind(".autocomplete");
200	});
201	
202	
203	function selectCurrent() {
204		var selected = select.selected();
205		if( !selected )
206			return false;
207		
208		var v = selected.result;
209		previousValue = v;
210		
211		if ( options.multiple ) {
212			var words = trimWords($input.val());
213			if ( words.length > 1 ) {
214				v = words.slice(0, words.length - 1).join( options.multipleSeparator ) + options.multipleSeparator + v;
215			}
216			v += options.multipleSeparator;
217		}
218		
219		$input.val(v);
220		hideResultsNow();
221		$input.trigger("result", [selected.data, selected.value]);
222		if($.isFunction(options.onSelect)) options.onSelect.apply();
223		return true;
224	}
225	
226	function onChange(crap, skipPrevCheck) {
227		if( lastKeyPressCode == KEY.DEL ) {
228			select.hide();
229			return;
230		}
231		
232		var currentValue = $input.val();
233		
234		if ( !skipPrevCheck && currentValue == previousValue )
235			return;
236		
237		previousValue = currentValue;
238		
239		currentValue = lastWord(currentValue);
240		if ( currentValue.length >= options.minChars) {
241			$input.addClass(options.loadingClass);
242			if (!options.matchCase)
243				currentValue = currentValue.toLowerCase();
244			request(currentValue, receiveData, hideResultsNow);
245		} else {
246			stopLoading();
247			select.hide();
248		}
249	};
250	
251	function trimWords(value) {
252		if ( !value ) {
253			return [""];
254		}
255		var words = value.split( options.multipleSeparator );
256		var result = [];
257		$.each(words, function(i, value) {
258			if ( $.trim(value) )
259				result[i] = $.trim(value);
260		});
261		return result;
262	}
263	
264	function lastWord(value) {
265		if ( !options.multiple )
266			return value;
267		var words = trimWords(value);
268		return words[words.length - 1];
269	}
270	
271	// fills in the input box w/the first match (assumed to be the best match)
272	// q: the term entered
273	// sValue: the first matching result
274	function autoFill(q, sValue){
275		// autofill in the complete box w/the first match as long as the user hasn't entered in more data
276		// if the last user key pressed was backspace, don't autofill
277		if( options.autoFill && (lastWord($input.val()).toLowerCase() == q.toLowerCase()) && lastKeyPressCode != KEY.BACKSPACE ) {
278			// fill in the value (keep the case the user has typed)
279			$input.val($input.val() + sValue.substring(lastWord(previousValue).length));
280			// select the portion of the value not typed by the user (so the next character will erase)
281			$.Autocompleter.Selection(input, previousValue.length, previousValue.length + sValue.length);
282		}
283	};
284
285	function hideResults() {
286		clearTimeout(timeout);
287		timeout = setTimeout(hideResultsNow, 200);
288	};
289
290	function hideResultsNow() {
291		var wasVisible = select.visible();
292		select.hide();
293		clearTimeout(timeout);
294		stopLoading();
295		if (options.mustMatch) {
296			// call search and run callback
297			$input.search(
298				function (result){
299					// if no value found, clear the input box
300					if( !result ) {
301						if (options.multiple) {
302							var words = trimWords($input.val()).slice(0, -1);
303							$input.val( words.join(options.multipleSeparator) + (words.length ? options.multipleSeparator : "") );
304						}
305						else
306							$input.val( "" );
307					}
308				}
309			);
310		}
311		if (wasVisible)
312			// position cursor at end of input field
313			$.Autocompleter.Selection(input, input.value.length, input.value.length);
314	};
315
316	function receiveData(q, data) {
317		if ( data && data.length && hasFocus ) {
318			stopLoading();
319			select.display(data, q);
320			autoFill(q, data[0].value);
321			select.show();
322		} else {
323			hideResultsNow();
324		}
325	};
326
327	function request(term, success, failure) {
328		if (!options.matchCase)
329			term = term.toLowerCase();
330		//var data = cache.load(term);
331		var data = "";
332		// recieve the cached data
333		if (data && data.length) {
334			success(term, data);
335		// if an AJAX url has been supplied, try loading the data now
336		} else if( (typeof options.url == "string") && (options.url.length > 0) ){
337			
338			var extraParams = {
339				timestamp: +new Date()
340			};
341			$.each(options.extraParams, function(key, param) {
342				extraParams[key] = typeof param == "function" ? param() : param;
343			});
344			
345			$.ajax({
346				// try to leverage ajaxQueue plugin to abort previous requests
347				mode: "abort",
348				// limit abortion to this input
349				port: "autocomplete" + input.name,
350				dataType: options.dataType,
351				url: options.url,
352				data: $.extend({
353					q: lastWord(term),
354					limit: options.max
355				}, extraParams),
356				success: function(data) {
357					var parsed = options.parse && options.parse(data) || parse(data);
358					cache.add(term, parsed);
359					success(term, parsed);
360				}
361			});
362		} else {
363			// if we have a failure, we need to empty the list -- this prevents the the [TAB] key from selecting the last successful match
364			select.emptyList();
365			failure(term);
366		}
367	};
368	
369	function parse(data) {
370		var parsed = [];
371		var rows = data.split("\n");
372		for (var i=0; i < rows.length; i++) {
373			var row = $.trim(rows[i]);
374			if (row) {
375				row = row.split("|");
376				parsed[parsed.length] = {
377					data: row,
378					value: row[0],
379					result: options.formatResult && options.formatResult(row, row[0]) || row[1]
380				};
381			}
382		}
383		return parsed;
384	};
385
386	function stopLoading() {
387		$input.removeClass(options.loadingClass);
388	};
389
390};
391
392$.Autocompleter.defaults = {
393	inputClass: "ac_input",
394	resultsClass: "ac_results",
395    resultsClassPlus: "",
396	loadingClass: "ac_loading",
397	minChars: 1,
398	delay: 400,
399	matchCase: false,
400	matchSubset: true,
401	matchContains: false,
402	cacheLength: 10,
403	max: 100,
404	mustMatch: false,
405	extraParams: {},
406	selectFirst: true,
407	formatItem: function(row) { return row[0]; },
408	formatMatch: null,
409	autoFill: false,
410	width: 0,
411	multiple: false,
412	multipleSeparator: ", ",
413	highlight: function(value, term) {
414		return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term.replace(/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi, "\\$1") + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "<strong>$1</strong>");
415	},
416    scroll: true,
417    scrollHeight: 260
418};
419
420$.Autocompleter.Cache = function(options) {
421
422	var data = {};
423	var length = 0;
424	
425	function matchSubset(s, sub) {
426		if (!options.matchCase) 
427			s = s.toLowerCase();
428		var i = s.indexOf(sub);
429		if (options.matchContains == "word"){
430			i = s.toLowerCase().search("\\b" + sub.toLowerCase());
431		}
432		if (i == -1) return false;
433		return i == 0 || options.matchContains;
434	};
435	
436	function add(q, value) {
437		if (length > options.cacheLength){
438			flush();
439		}
440		if (!data[q]){ 
441			length++;
442		}
443		data[q] = value;
444	}
445	
446	function populate(){
447		if( !options.data ) return false;
448		// track the matches
449		var stMatchSets = {},
450			nullData = 0;
451
452		// no url was specified, we need to adjust the cache length to make sure it fits the local data store
453		if( !options.url ) options.cacheLength = 1;
454		
455		// track all options for minChars = 0
456		stMatchSets[""] = [];
457		
458		// loop through the array and create a lookup structure
459		for ( var i = 0, ol = options.data.length; i < ol; i++ ) {
460			var rawValue = options.data[i];
461			// if rawValue is a string, make an array otherwise just reference the array
462			rawValue = (typeof rawValue == "string") ? [rawValue] : rawValue;
463			
464			var value = options.formatMatch(rawValue, i+1, options.data.length);
465			if ( value === false )
466				continue;
467				
468			var firstChar = value.charAt(0).toLowerCase();
469			// if no lookup array for this character exists, look it up now
470			if( !stMatchSets[firstChar] ) 
471				stMatchSets[firstChar] = [];
472
473			// if the match is a string
474			var row = {
475				value: value,
476				data: rawValue,
477				result: options.formatResult && options.formatResult(rawValue) || value
478			};
479			
480			// push the current match into the set list
481			stMatchSets[firstChar].push(row);
482
483			// keep track of minChars zero items
484			if ( nullData++ < options.max ) {
485				stMatchSets[""].push(row);
486			}
487		};
488
489		// add the data items to the cache
490		$.each(stMatchSets, function(i, value) {
491			// increase the cache size
492			options.cacheLength++;
493			// add to the cache
494			add(i, value);
495		});
496	}
497	
498	// populate any existing data
499	setTimeout(populate, 25);
500	
501	function flush(){
502		data = {};
503		length = 0;
504	}
505	
506	return {
507		flush: flush,
508		add: add,
509		populate: populate,
510		load: function(q) {
511			if (!options.cacheLength || !length)
512				return null;
513			/* 
514			 * if dealing w/local data and matchContains than we must make sure
515			 * to loop through all the data collections looking for matches
516			 */
517			if( !options.url && options.matchContains ){
518				// track all matches
519				var csub = [];
520				// loop through all the data grids for matches
521				for( var k in data ){
522					// don't search through the stMatchSets[""] (minChars: 0) cache
523					// this prevents duplicates
524					if( k.length > 0 ){
525						var c = data[k];
526						$.each(c, function(i, x) {
527							// if we've got a match, add it to the array
528							if (matchSubset(x.value, q)) {
529								csub.push(x);
530							}
531						});
532					}
533				}				
534				return csub;
535			} else 
536			// if the exact item exists, use it
537			if (data[q]){
538				return data[q];
539			} else
540			if (options.matchSubset) {
541				for (var i = q.length - 1; i >= options.minChars; i--) {
542					var c = data[q.substr(0, i)];
543					if (c) {
544						var csub = [];
545						$.each(c, function(i, x) {
546							if (matchSubset(x.value, q)) {
547								csub[csub.length] = x;
548							}
549						});
550						return csub;
551					}
552				}
553			}
554			return null;
555		}
556	};
557};
558
559$.Autocompleter.Select = function (options, input, select, config) {
560	var CLASSES = {
561		ACTIVE: "ac_over"
562	};
563	
564	var listItems,
565		active = -1,
566		data,
567		term = "",
568		needsInit = true,
569		element,
570		list;
571	
572	// Create results
573	function init() {
574		if (!needsInit)
575			return;
576
577		if ($("."+options.resultsClass).length==0) {
578            element = $("<div/>")
579                .hide()
580                .addClass(options.resultsClass)
581                .css("position", "absolute")
582                .appendTo(document.body);
583        } else {
584			element=$("."+options.resultsClass);
585		}
586		if (options.resultsClassPlus!="") element.addClass(options.resultsClassPlus);
587
588		list = $("<ul/>").appendTo(element).mouseover( function(event) {
589			if(target(event).nodeName && target(event).nodeName.toUpperCase() == 'LI') {
590	            active = $("li", list).removeClass(CLASSES.ACTIVE).index(target(event));
591			    $(target(event)).addClass(CLASSES.ACTIVE);            
592	        }
593		}).click(function(event) {
594		    if (event.target.closest('.info-icon') || event.target.closest('.icon--info')) {
595                return false;
596            }
597			$(target(event)).addClass(CLASSES.ACTIVE);
598			select();
599			// TODO provide option to avoid setting focus again after selection? useful for cleanup-on-focus
600			input.focus();
601			return false;
602		}).mousedown(function() {
603			config.mouseDownOnSelect = true;
604		}).mouseup(function() {
605			config.mouseDownOnSelect = false;
606		});
607		
608		if( options.width > 0 )
609			element.css("width", options.width);
610			
611		needsInit = false;
612	} 
613	
614	function target(event) {
615		var element = event.target;
616		while(element && element.tagName != "LI")
617			element = element.parentNode;
618		// more fun with IE, sometimes event.target is empty, just ignore it then
619		if(!element)
620			return [];
621		return element;
622	}
623
624	function moveSelect(step) {
625		listItems.slice(active, active + 1).removeClass(CLASSES.ACTIVE);
626		movePosition(step);
627        var activeItem = listItems.slice(active, active + 1).addClass(CLASSES.ACTIVE);
628        if(options.scroll) {
629            var offset = 0;
630            listItems.slice(0, active).each(function() {
631				offset += this.offsetHeight;
632			});
633            if((offset + activeItem[0].offsetHeight - list.scrollTop()) > list[0].clientHeight) {
634                list.scrollTop(offset + activeItem[0].offsetHeight - list.innerHeight());
635            } else if(offset < list.scrollTop()) {
636                list.scrollTop(offset);
637            }
638        }
639	}
640	
641	function movePosition(step) {
642		active += step;
643		if (active < 0) {
644			active = listItems.length - 1;
645		} else if (active >= listItems.length) {
646			active = 0;
647		}
648	}
649	
650	function limitNumberOfItems(available) {
651		return options.max && options.max < available
652			? options.max
653			: available;
654	}
655	
656	function fillList() {
657		list.empty();
658		var max = limitNumberOfItems(data.length);
659		for (var i=0; i < max; i++) {
660			if (!data[i])
661				continue;
662			var formatted = options.formatItem(data[i].data, i+1, max, data[i].value, term);
663			if ( formatted === false )
664				continue;
665
666            (data[i]["result"].indexOf("unas_category_link") >= 0) ? is_cat=" category_item" : is_cat="";
667
668			var li = $("<li/>").html( options.highlight(formatted, term) ).attr('role','link').addClass(i%2 == 0 ? "ac_even"+is_cat : "ac_odd"+is_cat).appendTo(list)[0];
669			$.data(li, "ac_data", data[i]);
670		}
671		listItems = list.find("li");
672		if ( options.selectFirst ) {
673			listItems.slice(0, 1).addClass(CLASSES.ACTIVE);
674			active = 0;
675		}
676		// apply bgiframe if available
677		if ( $.fn.bgiframe )
678			list.bgiframe();
679	}
680	
681	return {
682		display: function(d, q) {
683			init();
684			data = d;
685			term = q;
686			fillList();
687            $(document).trigger("autoCompleteListDisplayed");
688		},
689		next: function() {
690			moveSelect(1);
691		},
692		prev: function() {
693			moveSelect(-1);
694		},
695		pageUp: function() {
696			if (active != 0 && active - 8 < 0) {
697				moveSelect( -active );
698			} else {
699				moveSelect(-8);
700			}
701		},
702		pageDown: function() {
703			if (active != listItems.length - 1 && active + 8 > listItems.length) {
704				moveSelect( listItems.length - 1 - active );
705			} else {
706				moveSelect(8);
707			}
708		},
709		hide: function() {
710			element && element.hide().removeClass('show');
711			listItems && listItems.removeClass(CLASSES.ACTIVE);
712			active = -1;
713            $(document).trigger("autoCompleteListHide");
714		},
715		visible : function() {
716			return element && element.is(":visible");
717		},
718		current: function() {
719			return this.visible() && (listItems.filter("." + CLASSES.ACTIVE)[0] || options.selectFirst && listItems[0]);
720		},
721		show: function() {
722			var offset = $(input).offset();
723            //var input_height = $(input).height();
724			element.css({
725				width: typeof options.width == "string" || options.width > 0 ? options.width : $(input).width(),
726				top: offset.top + input.offsetHeight,
727			    left: offset.left
728                //top: input_height,
729                //left:0
730			}).show().addClass('show');
731            if(options.scroll) {
732                list.scrollTop(0);
733                list.css({
734					maxHeight: options.scrollHeight,
735					overflow: 'auto'
736				});
737
738                // remove ie support
739/*                if($.browser.msie && typeof document.body.style.maxHeight === "undefined") {
740					var listHeight = 0;
741					listItems.each(function() {
742						listHeight += this.offsetHeight;
743					});
744					var scrollbarsVisible = listHeight > options.scrollHeight;
745                    list.css('height', scrollbarsVisible ? options.scrollHeight : listHeight );
746					if (!scrollbarsVisible) {
747						// IE doesn't recalculate width when scrollbar disappears
748						listItems.width( list.width() - parseInt(listItems.css("padding-left")) - parseInt(listItems.css("padding-right")) );
749					}
750                }*/
751                
752            }
753		},
754		selected: function() {
755			var selected = listItems && listItems.filter("." + CLASSES.ACTIVE).removeClass(CLASSES.ACTIVE);
756			return selected && selected.length && $.data(selected[0], "ac_data");
757		},
758		emptyList: function (){
759			list && list.empty();
760		},
761		unbind: function() {
762			element && element.remove();
763		}
764	};
765};
766
767$.Autocompleter.Selection = function(field, start, end) {
768	if( field.createTextRange ){
769		var selRange = field.createTextRange();
770		selRange.collapse(true);
771		selRange.moveStart("character", start);
772		selRange.moveEnd("character", end);
773		selRange.select();
774	} else if( field.setSelectionRange ){
775		field.setSelectionRange(start, end);
776	} else {
777		if( field.selectionStart ){
778			field.selectionStart = start;
779			field.selectionEnd = end;
780		}
781	}
782	field.focus();
783};
784
785})(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.