PageSourceSearch

http://www.sinagi.com/ajax/ajaxSuggestions.js

js sinagi.com collected 2026-10-09 16:50:58 UTC 12,483 bytes, 413 lines download raw bytes

1/*
2	AJAX Suggestions is developed by Robert Nyman, http://www.robertnyman.com, and it is released according to the
3	Creative Commons Deed license (http://creativecommons.org/licenses/GPL/2.0/)
4	For more information, please see http://www.robertnyman.com/ajax-suggestions
5*/
6// ---
7var ajaxSuggestions = {
8	// Settings
9	elmIdToPresentResultsIn : "search-results",
10	elmIdResultsContainer : "search-result-suggestions",
11	charactersBeforeSearch : 2,
12	timeBeforeSuggest : 200, // In milliseconds
13	sameWidthAsInputElm : false,
14	offsetLeft: 0,
15	offsetTop : 0,
16	urlExt : "search=",
17	addSearchTermToQueryString : true,
18	addKeyNavigationEvents : true,
19	hideResultsOnDocumentClick : true,
20	itemClassName : "item",
21	itemSelectedClassName : "selected",
22	itemInsertValueIntoInputClassName : "choose-value",
23	itemInsertValueSetFocusToInput : true,
24	hideResultsWhenInsertValueIsSelected : true,
25	itemSeparator : ";",
26	turnAutoCompleteOff : true,
27	// Object properties
28	xmlHttp : null,
29	elements : [],
30	timer : null,
31	currentElm : null,
32	currentKeyEvent : null,
33	suggestionsForElm : null,
34	elmToPresentResultsIn : null,
35	elmResultsContainer : null,
36	suggestions : [],
37	resultIndex : 0,
38	selectedItem : -1,
39	resultsAreVisible : false,
40	valueAddedFromResultsListToInput : false,
41	
42	init : function (){
43		this.xmlHttp = this.createXmlHttp();
44		if(this.xmlHttp){
45			if(typeof document.getElementsByClassName != "function"){
46				document.getElementsByClassName = this.elmByClass;
47			}
48			this.elements = document.getElementsByClassName("ajax-suggestion", "input");
49			this.applyEvents();
50			this.elmToPresentResultsIn = document.getElementById(this.elmIdToPresentResultsIn);
51			this.elmResultsContainer = document.getElementById(this.elmIdResultsContainer);
52			if(this.addKeyNavigationEvents){
53				this.addEvent(document, "keydown", this.preventDefaultForArrowKeys);
54				this.addEvent(document, "keypress", this.preventDefaultForArrowKeys);
55				this.addEvent(document, "keyup", this.navigateResults);
56			}
57			if(this.hideResultsOnDocumentClick){
58				this.addEvent(document, "click", this.clearResultsElement);
59			}
60		}
61	},
62	
63	createXmlHttp : function (){
64		this.xmlHttp = null;
65		if(typeof XMLHttpRequest != "undefined"){
66			this.xmlHttp = new XMLHttpRequest();
67		}
68		else if(typeof window.ActiveXObject != "undefined"){
69			try {
70				this.xmlHttp = new ActiveXObject("Msxml2.XMLHTTP.4.0");
71			}
72			catch(e){
73				try {
74					this.xmlHttp = new ActiveXObject("MSXML2.XMLHTTP");
75				}
76				catch(e){
77					try {
78						this.xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
79					}
80					catch(e){
81						this.xmlHttp = null;
82					}
83				}
84			}
85		}
86		return this.xmlHttp;
87	},
88	
89	applyEvents : function (){
90		var element;
91		for(var i=0; i<this.elements.length; i++){
92			element = this.elements[i];
93			if(this.turnAutoCompleteOff){
94				element.setAttribute("autocomplete", "off");
95			}
96			this.addEvent(element, "keyup", this.startSuggestionsTimer);
97			if(this.hideResultsOnDocumentClick){
98				this.addEvent(element, "click", this.preventInputClickBubbling);
99			}
100		}
101	},
102	
103	startSuggestionsTimer : function (evt){
104		clearTimeout(ajaxSuggestions.timer);
105		ajaxSuggestions.currentElm = (/input/i.test(this.nodeName))? this : evt.srcElement;
106		ajaxSuggestions.currentKeyEvent = evt.keyCode;
107		ajaxSuggestions.timer = setTimeout("ajaxSuggestions.getSuggestions()", ajaxSuggestions.timeBeforeSuggest);
108	},
109	
110	getSuggestions : function (){
111		var value = this.currentElm.value;
112		if(!/13|27|37|39/.test(this.currentKeyEvent)){
113			var url = this.currentElm.className.replace(/.*url-([\w\/\?\.-]+).*/, "$1");
114			if(!this.valueAddedFromResultsListToInput){
115				ajaxSuggestions.clearResults(true);
116			}
117			if(value.length > this.charactersBeforeSearch && url.length > 0){
118				this.makeSuggestionCall(value, url);
119			}
120			else if(value.length == 0 || !this.valueAddedFromResultsListToInput){
121				ajaxSuggestions.clearResults();
122			}
123		}
124	},
125	
126	makeSuggestionCall : function (value, url){
127		var regExpValue = new RegExp(("^" + value + "$"), "i");
128		var exists = false;
129		var suggestionItem;
130		var url = url + ((/\?/.test(url))? "&" : "?") + this.urlExt + ((this.addSearchTermToQueryString)? value : "");
131		for(var i=0; i<this.suggestions.length; i++){
132			suggestionItem = this.suggestions[i];
133			if(regExpValue.test(suggestionItem[0]) && url == suggestionItem[2]){
134				exists = true;
135				this.resultIndex = i;
136				this.presentResult(this.suggestions[i][1]);
137				break;
138			}
139		};
140		if(!exists){
141			this.xmlHttp.onreadystatechange = function (){};
142			this.xmlHttp.abort();
143			this.currentValue = value;
144			this.currentURL = url;
145			this.xmlHttp.open("GET", url, true);
146			this.xmlHttp.onreadystatechange = this.getResults;
147			this.xmlHttp.send(null);
148		}
149	},
150	
151	getResults : function (){
152		if(ajaxSuggestions.xmlHttp.readyState == 4 && ajaxSuggestions.xmlHttp.responseText.length > 0){
153			ajaxSuggestions.loadResults();
154		}
155	},
156	
157	loadResults : function (){
158		this.resultIndex = this.suggestions.length;
159		this.suggestions.push([this.currentValue, this.xmlHttp.responseText, this.currentURL]);
160		this.presentResult();
161	},
162	
163	presentResult : function (){
164		this.elmToPresentResultsIn.innerHTML = this.suggestions[this.resultIndex][1];
165		var coordinates = this.getCoordinates();
166		var elm = this.elmResultsContainer.style;
167		elm.left = coordinates[0] + this.offsetLeft + "px";
168		elm.top = coordinates[1] + this.currentElm.offsetHeight + this.offsetTop + "px";
169		if(this.sameWidthAsInputElm){
170			elm.width = this.currentElm.offsetWidth + "px";
171		}
172		this.applyResultEvents();
173		elm.display = "block";
174		this.resultsAreVisible = true;
175		if(this.addKeyNavigationEvents && /38|40/.test(this.currentKeyEvent)){
176			if(!this.valueAddedFromResultsListToInput){
177				this.selectedItem = -1;
178			}
179			this.navigateResults(null, this.currentKeyEvent);
180		}
181	},
182	
183	clearResults : function (justClear){
184		if(this.elmResultsContainer && this.elmToPresentResultsIn){
185			if(!justClear){
186				this.elmResultsContainer.style.display = "none";
187				this.resultsAreVisible = false;
188			}
189			this.elmToPresentResultsIn.innerHTML = "";
190			this.selectedItem = -1;
191		}
192	},
193	
194	clearResultsElement : function (){
195		ajaxSuggestions.clearResults();
196	},
197	
198	navigateResults : function (evt, keyCode){
199		if(ajaxSuggestions.currentElm && ajaxSuggestions.elmToPresentResultsIn){
200			var event = (typeof evt != "undefined")? evt : event;
201			if(typeof ajaxSuggestions.elmToPresentResultsIn.getElementsByClassName != "function"){
202				ajaxSuggestions.elmToPresentResultsIn.getElementsByClassName = ajaxSuggestions.elmByClass;
203			}
204			var results = ajaxSuggestions.elmToPresentResultsIn.getElementsByClassName(ajaxSuggestions.itemClassName);
205			var selectedItem = (!evt && keyCode == 38)? results.length : ajaxSuggestions.selectedItem;
206			var keyCode = keyCode || event.keyCode;
207			var navigateUp = keyCode == 37 || keyCode == 38;
208			var navigateDown = keyCode == 39 || keyCode == 40;
209			if(results.length > 0 && (navigateUp || navigateDown)){
210				if(navigateUp){
211					if((selectedItem - 1) >= 0){
212						selectedItem--;
213					}
214					else{
215						selectedItem = -1;
216					}
217				}		
218				else if(navigateDown){
219					if((selectedItem + 1) < results.length){
220						selectedItem++;
221					}
222					else{
223						selectedItem = -1;				
224					}
225				}
226				var item;
227				var classToRemove;
228				for(var i=0; i<results.length; i++){
229					item = results[i];
230					classToRemove = new RegExp((ajaxSuggestions.itemSelectedClassName + "\s?"), "i");
231					item.className = item.className.replace(classToRemove, "").replace(/^\s?|\s?$/g, "");
232				};
233				ajaxSuggestions.selectedItem = selectedItem;
234				var elmToFocus = ajaxSuggestions.currentElm;
235				if(selectedItem > -1){
236					var currentItem = results[selectedItem];
237					var currentClass = item.className;
238					if(!new RegExp(ajaxSuggestions.itemSelectedClassName, "i").test(currentClass)){
239						currentItem.className = currentClass + ((currentClass.length > 0)? " " : "") + ajaxSuggestions.itemSelectedClassName;
240					}
241					elmToFocus = results[selectedItem];
242				}
243				try{
244					elmToFocus.focus();
245				}
246				catch(e){
247					// Just in case... :-)
248				}
249				if(event){
250					if(event.preventDefault){
251						event.preventDefault();
252					}
253					else{
254						event.returnValue = false;
255					}
256					if(event.stopPropagation){
257						event.stopPropagation();
258					}
259					else{
260						event.cancelBubble = true;
261					}
262				}
263				return false;
264			}
265			else if(keyCode == 27){
266				ajaxSuggestions.clearResults();
267				try{
268					ajaxSuggestions.currentElm.focus();
269				}
270				catch(e){
271					// Just in case... :-)
272				}
273			}
274		}
275	},
276	
277	applyResultEvents : function (){
278		if(typeof this.elmToPresentResultsIn.getElementsByClassName != "function"){
279			this.elmToPresentResultsIn.getElementsByClassName = this.elmByClass;
280		}
281		var insertValueItems = this.elmToPresentResultsIn.getElementsByClassName(this.itemInsertValueIntoInputClassName, "a");
282		var item;
283		for(var i=0; i<insertValueItems.length; i++){
284			item = insertValueItems[i];
285			item.inputRef = this.currentElm;
286			this.addEvent(item, "click", this.insertValueIntoField);
287		};		
288	},
289	
290	insertValueIntoField : function (evt){
291		var elm = (/a/i.test(this.nodeName))? this : evt.srcElement;
292		var input = elm.inputRef;
293		var value = elm.getAttribute("href");
294		if(!new RegExp(value).test(input.value)){
295			input.value = ((input.value.length > 0 && /;/i.test(input.value))? (input.value + value) : value) + ajaxSuggestions.itemSeparator;
296		}
297		if(evt.preventDefault){
298			evt.preventDefault();
299		}
300		else{
301			evt.returnValue = false;
302		}
303		if(evt.stopPropagation){
304			evt.stopPropagation();
305		}
306		else{
307			evt.cancelBubble = true;
308		}
309		if(ajaxSuggestions.itemInsertValueSetFocusToInput){
310			try{
311				input.focus();
312			}
313			catch(e){
314				// Just in case... :-)
315			}
316		}
317		if(ajaxSuggestions.hideResultsWhenInsertValueIsSelected){
318			ajaxSuggestions.clearResults();
319		}
320		ajaxSuggestions.valueAddedFromResultsListToInput = true;
321	},
322	
323	preventInputClickBubbling : function (evt){
324		if(evt.preventDefault){
325			evt.preventDefault();
326		}
327		else{
328			evt.returnValue = false;
329		}
330		if(evt.stopPropagation){
331			evt.stopPropagation();
332		}
333		else{
334			evt.cancelBubble = true;
335		}
336		return false;
337	},
338	
339	preventDefaultForArrowKeys : function (evt){
340		var keyCode = evt.keyCode;
341		var navigateUp = keyCode == 37 || keyCode == 38;
342		var navigateDown = keyCode == 39 || keyCode == 40;
343		if((!evt.ctrlKey && !evt.metaKey) && ajaxSuggestions.resultsAreVisible && (navigateUp || navigateDown)){
344			if(evt.preventDefault){
345				evt.preventDefault();
346			}
347			else{
348				evt.returnValue = false;
349			}
350			if(evt.stopPropagation){
351				evt.stopPropagation();
352			}
353			else{
354				evt.cancelBubble = true;
355			}
356			return false;
357		}	
358	},
359	
360	getCoordinates : function (){
361		var elm = this.currentElm;
362		var offsetLeft = 0;
363		var offsetTop = 0;
364		while(elm.offsetParent){
365			offsetLeft += elm.offsetLeft;
366			offsetTop += elm.offsetTop;
367			if(elm.scrollTop > 0){
368				offsetTop -= elm.scrollTop;
369			}
370			elm = elm.offsetParent;
371		}
372		return [offsetLeft, offsetTop];
373	},
374	
375	closeSession : function (){
376		delete ajaxSuggestions;
377		ajaxSuggestions = null;
378	},
379	
380	elmByClass : function (className, tag){
381		return ajaxSuggestions.getElementsByClassName.call(this, className, tag);
382	},
383	
384	getElementsByClassName : function (className, tag){		
385		var elms = ((!tag || tag == "*") && this.all)? this.all : this.getElementsByTagName(tag || "*");
386		var returnElms = [];
387		var className = className.replace(/\-/g, "\\-");
388		var regExp = new RegExp("(^|\\s)" + className + "(\\s|$)");
389		var elm;
390		for(var i=0; i<elms.length; i++){
391			elm = elms[i];		
392			if(regExp.test(elm.className)){
393				returnElms.push(elm);
394			}
395		}
396		return (returnElms);
397	},
398
399	addEvent : function (elm, evt, func){
400		if(elm){
401			if(elm.addEventListener){
402				elm.addEventListener(evt, func, false);
403			}
404			else if(window.attachEvent){
405				elm.attachEvent(("on" + evt), func)
406			}
407		}
408	}
409};
410// ---
411ajaxSuggestions.addEvent(window, "load", function(){ajaxSuggestions.init();});
412ajaxSuggestions.addEvent(window, "unload", function(){ajaxSuggestions.closeSession();});
413// ---

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.