mirror of
				https://github.com/xcat2/xcat-core.git
				synced 2025-10-31 03:12:30 +00:00 
			
		
		
		
	Removed autocomplete plugin and use one provided by jquery.ui.
git-svn-id: https://svn.code.sf.net/p/xcat/code/xcat-core/trunk@8651 8638fb3e-16cb-4fca-ae20-7b5d299a9bcd
This commit is contained in:
		| @@ -1,852 +0,0 @@ | ||||
| /* | ||||
|  * jQuery Autocomplete plugin 1.1 | ||||
|  * | ||||
|  * Copyright (c) 2009 Jörn Zaefferer | ||||
|  * | ||||
|  * Dual licensed under the MIT and GPL licenses: | ||||
|  *   http://www.opensource.org/licenses/mit-license.php | ||||
|  *   http://www.gnu.org/licenses/gpl.html | ||||
|  * | ||||
|  * Revision: $Id: jquery.autocomplete.js 15 2009-08-22 10:30:27Z joern.zaefferer $ | ||||
|  */ | ||||
|  | ||||
| ; | ||||
| (function($) { | ||||
|  | ||||
| 	$.fn.extend( { | ||||
| 		autocomplete : function(urlOrData, options) { | ||||
| 			var isUrl = typeof urlOrData == "string"; | ||||
| 			options = $.extend( {}, $.Autocompleter.defaults, { | ||||
| 				url : isUrl ? urlOrData : null, | ||||
| 				data : isUrl ? null : urlOrData, | ||||
| 				delay : isUrl ? $.Autocompleter.defaults.delay : 10, | ||||
| 				max : options && !options.scroll ? 10 : 150 | ||||
| 			}, options); | ||||
|  | ||||
| 			// if highlight is set to false, replace it with a do-nothing | ||||
| 			// function | ||||
| 			options.highlight = options.highlight || function(value) { | ||||
| 				return value; | ||||
| 			}; | ||||
|  | ||||
| 			// if the formatMatch option is not specified, then use formatItem for | ||||
| 			// backwards compatibility | ||||
| 			options.formatMatch = options.formatMatch || options.formatItem; | ||||
|  | ||||
| 			return this.each(function() { | ||||
| 				new $.Autocompleter(this, options); | ||||
| 			}); | ||||
| 		}, | ||||
| 		result : function(handler) { | ||||
| 			return this.bind("result", handler); | ||||
| 		}, | ||||
| 		search : function(handler) { | ||||
| 			return this.trigger("search", [ handler ]); | ||||
| 		}, | ||||
| 		flushCache : function() { | ||||
| 			return this.trigger("flushCache"); | ||||
| 		}, | ||||
| 		setOptions : function(options) { | ||||
| 			return this.trigger("setOptions", [ options ]); | ||||
| 		}, | ||||
| 		unautocomplete : function() { | ||||
| 			return this.trigger("unautocomplete"); | ||||
| 		} | ||||
| 	}); | ||||
|  | ||||
| 	$.Autocompleter = function(input, options) { | ||||
|  | ||||
| 		var KEY = { | ||||
| 			UP : 38, | ||||
| 			DOWN : 40, | ||||
| 			DEL : 46, | ||||
| 			TAB : 9, | ||||
| 			RETURN : 13, | ||||
| 			ESC : 27, | ||||
| 			COMMA : 188, | ||||
| 			PAGEUP : 33, | ||||
| 			PAGEDOWN : 34, | ||||
| 			BACKSPACE : 8 | ||||
| 		}; | ||||
|  | ||||
| 		// Create $ object for input element | ||||
| 		var $input = $(input).attr("autocomplete", "off") | ||||
| 			.addClass(options.inputClass); | ||||
|  | ||||
| 		var timeout; | ||||
| 		var previousValue = ""; | ||||
| 		var cache = $.Autocompleter.Cache(options); | ||||
| 		var hasFocus = 0; | ||||
| 		var lastKeyPressCode; | ||||
| 		var config = { | ||||
| 			mouseDownOnSelect : false | ||||
| 		}; | ||||
| 		var select = $.Autocompleter | ||||
| 			.Select(options, input, selectCurrent, config); | ||||
|  | ||||
| 		var blockSubmit; | ||||
|  | ||||
| 		// prevent form submit in opera when selecting with return key | ||||
| 		$.browser.opera && | ||||
| 			$(input.form).bind("submit.autocomplete", function() { | ||||
| 				if (blockSubmit) { | ||||
| 					blockSubmit = false; | ||||
| 					return false; | ||||
| 				} | ||||
| 			}); | ||||
|  | ||||
| 		// only opera doesn't trigger keydown multiple times while pressed, | ||||
| 		// others don't work with keypress at all | ||||
| 		$input.bind(($.browser.opera ? "keypress" : "keydown") + | ||||
| 			".autocomplete", function(event) { | ||||
| 			// a keypress means the input has focus | ||||
| 			// avoids issue where input had focus before the autocomplete | ||||
| 			// was applied | ||||
| 			hasFocus = 1; | ||||
| 			// track last key pressed | ||||
| 			lastKeyPressCode = event.keyCode; | ||||
| 			switch (event.keyCode) { | ||||
|  | ||||
| 			case KEY.UP: | ||||
| 				event.preventDefault(); | ||||
| 				if (select.visible()) { | ||||
| 					select.prev(); | ||||
| 				} else { | ||||
| 					onChange(0, true); | ||||
| 				} | ||||
| 				break; | ||||
|  | ||||
| 			case KEY.DOWN: | ||||
| 				event.preventDefault(); | ||||
| 				if (select.visible()) { | ||||
| 					select.next(); | ||||
| 				} else { | ||||
| 					onChange(0, true); | ||||
| 				} | ||||
| 				break; | ||||
|  | ||||
| 			case KEY.PAGEUP: | ||||
| 				event.preventDefault(); | ||||
| 				if (select.visible()) { | ||||
| 					select.pageUp(); | ||||
| 				} else { | ||||
| 					onChange(0, true); | ||||
| 				} | ||||
| 				break; | ||||
|  | ||||
| 			case KEY.PAGEDOWN: | ||||
| 				event.preventDefault(); | ||||
| 				if (select.visible()) { | ||||
| 					select.pageDown(); | ||||
| 				} else { | ||||
| 					onChange(0, true); | ||||
| 				} | ||||
| 				break; | ||||
|  | ||||
| 			// matches also semicolon | ||||
| 			case options.multiple && $.trim(options.multipleSeparator) == "," && | ||||
| 				KEY.COMMA: | ||||
| 			case KEY.TAB: | ||||
| 			case KEY.RETURN: | ||||
| 				if (selectCurrent()) { | ||||
| 					// stop default to prevent a form submit, Opera needs special handling | ||||
| 					event.preventDefault(); | ||||
| 					blockSubmit = true; | ||||
| 					return false; | ||||
| 				} | ||||
| 				break; | ||||
|  | ||||
| 			case KEY.ESC: | ||||
| 				select.hide(); | ||||
| 				break; | ||||
|  | ||||
| 			default: | ||||
| 				clearTimeout(timeout); | ||||
| 				timeout = setTimeout(onChange, options.delay); | ||||
| 				break; | ||||
| 			} | ||||
| 		}).focus(function() { | ||||
| 			// track whether the field has focus, we shouldn't process any | ||||
| 			// results if the field no longer has focus | ||||
| 			hasFocus++; | ||||
| 		}).blur(function() { | ||||
| 			hasFocus = 0; | ||||
| 			if (!config.mouseDownOnSelect) { | ||||
| 				hideResults(); | ||||
| 			} | ||||
| 		}).click(function() { | ||||
| 			// show select when clicking in a focused field | ||||
| 			if (hasFocus++ > 1 && !select.visible()) { | ||||
| 				onChange(0, true); | ||||
| 			} | ||||
| 		}).bind("search", function() { | ||||
| 			// TODO why not just specifying both arguments? | ||||
| 			var fn = (arguments.length > 1) ? arguments[1] : null; | ||||
| 			function findValueCallback(q, data) { | ||||
| 				var result; | ||||
| 				if (data && data.length) { | ||||
| 					for ( var i = 0; i < data.length; i++) { | ||||
| 						if (data[i].result.toLowerCase() == q.toLowerCase()) { | ||||
| 							result = data[i]; | ||||
| 							break; | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 				if (typeof fn == "function") | ||||
| 					fn(result); | ||||
| 				else | ||||
| 					$input.trigger("result", result && | ||||
| 						[ result.data, result.value ]); | ||||
| 			} | ||||
| 			$.each(trimWords($input.val()), function(i, value) { | ||||
| 				request(value, findValueCallback, findValueCallback); | ||||
| 			}); | ||||
| 		}).bind("flushCache", function() { | ||||
| 			cache.flush(); | ||||
| 		}).bind("setOptions", function() { | ||||
| 			$.extend(options, arguments[1]); | ||||
| 			// if we've updated the data, repopulate | ||||
| 			if ("data" in arguments[1]) | ||||
| 				cache.populate(); | ||||
| 		}).bind("unautocomplete", function() { | ||||
| 			select.unbind(); | ||||
| 			$input.unbind(); | ||||
| 			$(input.form).unbind(".autocomplete"); | ||||
| 		}); | ||||
|  | ||||
| 		function selectCurrent() { | ||||
| 			var selected = select.selected(); | ||||
| 			if (!selected) | ||||
| 				return false; | ||||
|  | ||||
| 			var v = selected.result; | ||||
| 			previousValue = v; | ||||
|  | ||||
| 			if (options.multiple) { | ||||
| 				var words = trimWords($input.val()); | ||||
| 				if (words.length > 1) { | ||||
| 					var seperator = options.multipleSeparator.length; | ||||
| 					var cursorAt = $(input).selection().start; | ||||
| 					var wordAt, progress = 0; | ||||
| 					$.each(words, function(i, word) { | ||||
| 						progress += word.length; | ||||
| 						if (cursorAt <= progress) { | ||||
| 							wordAt = i; | ||||
| 							return false; | ||||
| 						} | ||||
| 						progress += seperator; | ||||
| 					}); | ||||
| 					words[wordAt] = v; | ||||
| 					// TODO this should set the cursor to the right position, | ||||
| 					// but it gets overriden somewhere | ||||
| 					// $.Autocompleter.Selection(input, progress + seperator, | ||||
| 					// progress + seperator); | ||||
| 					v = words.join(options.multipleSeparator); | ||||
| 				} | ||||
| 				v += options.multipleSeparator; | ||||
| 			} | ||||
|  | ||||
| 			$input.val(v); | ||||
| 			hideResultsNow(); | ||||
| 			$input.trigger("result", [ selected.data, selected.value ]); | ||||
| 			return true; | ||||
| 		} | ||||
|  | ||||
| 		function onChange(crap, skipPrevCheck) { | ||||
| 			if (lastKeyPressCode == KEY.DEL) { | ||||
| 				select.hide(); | ||||
| 				return; | ||||
| 			} | ||||
|  | ||||
| 			var currentValue = $input.val(); | ||||
|  | ||||
| 			if (!skipPrevCheck && currentValue == previousValue) | ||||
| 				return; | ||||
|  | ||||
| 			previousValue = currentValue; | ||||
|  | ||||
| 			currentValue = lastWord(currentValue); | ||||
| 			if (currentValue.length >= options.minChars) { | ||||
| 				$input.addClass(options.loadingClass); | ||||
| 				if (!options.matchCase) | ||||
| 					currentValue = currentValue.toLowerCase(); | ||||
| 				request(currentValue, receiveData, hideResultsNow); | ||||
| 			} else { | ||||
| 				stopLoading(); | ||||
| 				select.hide(); | ||||
| 			} | ||||
| 		} | ||||
| 		; | ||||
|  | ||||
| 		function trimWords(value) { | ||||
| 			if (!value) | ||||
| 				return [ "" ]; | ||||
| 			if (!options.multiple) | ||||
| 				return [ $.trim(value) ]; | ||||
| 			return $ | ||||
| 				.map(value.split(options.multipleSeparator), function(word) { | ||||
| 					return $.trim(value).length ? $.trim(word) : null; | ||||
| 				}); | ||||
| 		} | ||||
|  | ||||
| 		function lastWord(value) { | ||||
| 			if (!options.multiple) | ||||
| 				return value; | ||||
| 			var words = trimWords(value); | ||||
| 			if (words.length == 1) | ||||
| 				return words[0]; | ||||
| 			var cursorAt = $(input).selection().start; | ||||
| 			if (cursorAt == value.length) { | ||||
| 				words = trimWords(value) | ||||
| 			} else { | ||||
| 				words = trimWords(value.replace(value.substring(cursorAt), "")); | ||||
| 			} | ||||
| 			return words[words.length - 1]; | ||||
| 		} | ||||
|  | ||||
| 		// fills in the input box w/the first match (assumed to be the best match) | ||||
| 		// q: the term entered | ||||
| 		// sValue: the first matching result | ||||
| 		function autoFill(q, sValue) { | ||||
| 			// autofill in the complete box w/the first match as long as the user hasn't entered in more data | ||||
| 			// if the last user key pressed was backspace, don't autofill | ||||
| 			if (options.autoFill && | ||||
| 				(lastWord($input.val()).toLowerCase() == q.toLowerCase()) && | ||||
| 				lastKeyPressCode != KEY.BACKSPACE) { | ||||
| 				// fill in the value (keep the case the user has typed) | ||||
| 				$input.val($input.val() + | ||||
| 					sValue.substring(lastWord(previousValue).length)); | ||||
| 				// select the portion of the value not typed by the user (so the | ||||
| 				// next character will erase) | ||||
| 				$(input).selection(previousValue.length, previousValue.length + | ||||
| 					sValue.length); | ||||
| 			} | ||||
| 		} | ||||
| 		; | ||||
|  | ||||
| 		function hideResults() { | ||||
| 			clearTimeout(timeout); | ||||
| 			timeout = setTimeout(hideResultsNow, 200); | ||||
| 		} | ||||
| 		; | ||||
|  | ||||
| 		function hideResultsNow() { | ||||
| 			var wasVisible = select.visible(); | ||||
| 			select.hide(); | ||||
| 			clearTimeout(timeout); | ||||
| 			stopLoading(); | ||||
| 			if (options.mustMatch) { | ||||
| 				// call search and run callback | ||||
| 				$input.search(function(result) { | ||||
| 					// if no value found, clear the input box | ||||
| 					if (!result) { | ||||
| 						if (options.multiple) { | ||||
| 							var words = trimWords($input.val()).slice(0, -1); | ||||
| 							$input | ||||
| 								.val(words.join(options.multipleSeparator) + | ||||
| 									(words.length ? options.multipleSeparator : "")); | ||||
| 						} else { | ||||
| 							$input.val(""); | ||||
| 							$input.trigger("result", null); | ||||
| 						} | ||||
| 					} | ||||
| 				}); | ||||
| 			} | ||||
| 		} | ||||
| 		; | ||||
|  | ||||
| 		function receiveData(q, data) { | ||||
| 			if (data && data.length && hasFocus) { | ||||
| 				stopLoading(); | ||||
| 				select.display(data, q); | ||||
| 				autoFill(q, data[0].value); | ||||
| 				select.show(); | ||||
| 			} else { | ||||
| 				hideResultsNow(); | ||||
| 			} | ||||
| 		} | ||||
| 		; | ||||
|  | ||||
| 		function request(term, success, failure) { | ||||
| 			if (!options.matchCase) | ||||
| 				term = term.toLowerCase(); | ||||
| 			var data = cache.load(term); | ||||
| 			// recieve the cached data | ||||
| 			if (data && data.length) { | ||||
| 				success(term, data); | ||||
| 				// if an AJAX url has been supplied, try loading the data now | ||||
| 			} else if ((typeof options.url == "string") && | ||||
| 				(options.url.length > 0)) { | ||||
|  | ||||
| 				var extraParams = { | ||||
| 					timestamp : +new Date() | ||||
| 				}; | ||||
| 				$ | ||||
| 					.each(options.extraParams, function(key, param) { | ||||
| 						extraParams[key] = typeof param == "function" ? param() : param; | ||||
| 					}); | ||||
|  | ||||
| 				$.ajax( { | ||||
| 					// try to leverage ajaxQueue plugin to abort previous requests | ||||
| 					mode : "abort", | ||||
| 					// limit abortion to this input | ||||
| 					port : "autocomplete" + input.name, | ||||
| 					dataType : options.dataType, | ||||
| 					url : options.url, | ||||
| 					data : $.extend( { | ||||
| 						q : lastWord(term), limit : options.max | ||||
| 					}, extraParams), | ||||
| 					success : function(data) { | ||||
| 						var parsed = options.parse && options.parse(data) || | ||||
| 							parse(data); | ||||
| 						cache.add(term, parsed); | ||||
| 						success(term, parsed); | ||||
| 					} | ||||
| 				}); | ||||
| 			} else { | ||||
| 				// if we have a failure, we need to empty the list -- this prevents the the [TAB] key from selecting the | ||||
| 				// last successful match | ||||
| 				select.emptyList(); | ||||
| 				failure(term); | ||||
| 			} | ||||
| 		} | ||||
| 		; | ||||
|  | ||||
| 		function parse(data) { | ||||
| 			var parsed = []; | ||||
| 			var rows = data.split("\n"); | ||||
| 			for ( var i = 0; i < rows.length; i++) { | ||||
| 				var row = $.trim(rows[i]); | ||||
| 				if (row) { | ||||
| 					row = row.split("|"); | ||||
| 					parsed[parsed.length] = { | ||||
| 						data : row, | ||||
| 						value : row[0], | ||||
| 						result : options.formatResult && | ||||
| 							options.formatResult(row, row[0]) || row[0] | ||||
| 					}; | ||||
| 				} | ||||
| 			} | ||||
| 			return parsed; | ||||
| 		} | ||||
| 		; | ||||
|  | ||||
| 		function stopLoading() { | ||||
| 			$input.removeClass(options.loadingClass); | ||||
| 		} | ||||
| 		; | ||||
|  | ||||
| 	}; | ||||
|  | ||||
| 	$.Autocompleter.defaults = { | ||||
| 		inputClass : "ac_input", | ||||
| 		resultsClass : "ac_results", | ||||
| 		loadingClass : "ac_loading", | ||||
| 		minChars : 1, | ||||
| 		delay : 400, | ||||
| 		matchCase : false, | ||||
| 		matchSubset : true, | ||||
| 		matchContains : false, | ||||
| 		cacheLength : 10, | ||||
| 		max : 100, | ||||
| 		mustMatch : false, | ||||
| 		extraParams : {}, | ||||
| 		selectFirst : true, | ||||
| 		formatItem : function(row) { | ||||
| 			return row[0]; | ||||
| 		}, | ||||
| 		formatMatch : null, | ||||
| 		autoFill : false, | ||||
| 		width : 0, | ||||
| 		multiple : false, | ||||
| 		multipleSeparator : ", ", | ||||
| 		highlight : function(value, term) { | ||||
| 			return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + | ||||
| 				term.replace(/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi, "\\$1") + | ||||
| 				")(?![^<>]*>)(?![^&;]+;)", "gi"), "<strong>$1</strong>"); | ||||
| 		}, | ||||
| 		scroll : true, | ||||
| 		scrollHeight : 180 | ||||
| 	}; | ||||
|  | ||||
| 	$.Autocompleter.Cache = function(options) { | ||||
|  | ||||
| 		var data = {}; | ||||
| 		var length = 0; | ||||
|  | ||||
| 		function matchSubset(s, sub) { | ||||
| 			if (!options.matchCase) | ||||
| 				s = s.toLowerCase(); | ||||
| 			var i = s.indexOf(sub); | ||||
| 			if (options.matchContains == "word") { | ||||
| 				i = s.toLowerCase().search("\\b" + sub.toLowerCase()); | ||||
| 			} | ||||
| 			if (i == -1) | ||||
| 				return false; | ||||
| 			return i == 0 || options.matchContains; | ||||
| 		} | ||||
| 		; | ||||
|  | ||||
| 		function add(q, value) { | ||||
| 			if (length > options.cacheLength) { | ||||
| 				flush(); | ||||
| 			} | ||||
| 			if (!data[q]) { | ||||
| 				length++; | ||||
| 			} | ||||
| 			data[q] = value; | ||||
| 		} | ||||
|  | ||||
| 		function populate() { | ||||
| 			if (!options.data) | ||||
| 				return false; | ||||
| 			// track the matches | ||||
| 			var stMatchSets = {}, nullData = 0; | ||||
|  | ||||
| 			// no url was specified, we need to adjust the cache length to make | ||||
| 			// sure it fits the local data store | ||||
| 			if (!options.url) | ||||
| 				options.cacheLength = 1; | ||||
|  | ||||
| 			// track all options for minChars = 0 | ||||
| 			stMatchSets[""] = []; | ||||
|  | ||||
| 			// loop through the array and create a lookup structure | ||||
| 			for ( var i = 0, ol = options.data.length; i < ol; i++) { | ||||
| 				var rawValue = options.data[i]; | ||||
| 				// if rawValue is a string, make an array otherwise just | ||||
| 				// reference the array | ||||
| 				rawValue = (typeof rawValue == "string") ? [ rawValue ] : rawValue; | ||||
|  | ||||
| 				var value = options | ||||
| 					.formatMatch(rawValue, i + 1, options.data.length); | ||||
| 				if (value === false) | ||||
| 					continue; | ||||
|  | ||||
| 				var firstChar = value.charAt(0).toLowerCase(); | ||||
| 				// if no lookup array for this character exists, look it up now | ||||
| 				if (!stMatchSets[firstChar]) | ||||
| 					stMatchSets[firstChar] = []; | ||||
|  | ||||
| 				// if the match is a string | ||||
| 				var row = { | ||||
| 					value : value, | ||||
| 					data : rawValue, | ||||
| 					result : options.formatResult && | ||||
| 						options.formatResult(rawValue) || value | ||||
| 				}; | ||||
|  | ||||
| 				// push the current match into the set list | ||||
| 				stMatchSets[firstChar].push(row); | ||||
|  | ||||
| 				// keep track of minChars zero items | ||||
| 				if (nullData++ < options.max) { | ||||
| 					stMatchSets[""].push(row); | ||||
| 				} | ||||
| 			} | ||||
| 			; | ||||
|  | ||||
| 			// add the data items to the cache | ||||
| 			$.each(stMatchSets, function(i, value) { | ||||
| 				// increase the cache size | ||||
| 				options.cacheLength++; | ||||
| 				// add to the cache | ||||
| 				add(i, value); | ||||
| 			}); | ||||
| 		} | ||||
|  | ||||
| 		// populate any existing data | ||||
| 		setTimeout(populate, 25); | ||||
|  | ||||
| 		function flush() { | ||||
| 			data = {}; | ||||
| 			length = 0; | ||||
| 		} | ||||
|  | ||||
| 		return { | ||||
| 			flush : flush, add : add, populate : populate, load : function(q) { | ||||
| 				if (!options.cacheLength || !length) | ||||
| 					return null; | ||||
| 				/* | ||||
| 				 * if dealing w/local data and matchContains than we must make sure to loop through all the data | ||||
| 				 * collections looking for matches | ||||
| 				 */ | ||||
| 				if (!options.url && options.matchContains) { | ||||
| 					// track all matches | ||||
| 			var csub = []; | ||||
| 			// loop through all the data grids for matches | ||||
| 			for ( var k in data) { | ||||
| 				// don't search through the stMatchSets[""] (minChars: 0) cache | ||||
| 				// this prevents duplicates | ||||
| 				if (k.length > 0) { | ||||
| 					var c = data[k]; | ||||
| 					$.each(c, function(i, x) { | ||||
| 						// if we've got a match, add it to the array | ||||
| 						if (matchSubset(x.value, q)) { | ||||
| 							csub.push(x); | ||||
| 						} | ||||
| 					}); | ||||
| 				} | ||||
| 			} | ||||
| 			return csub; | ||||
| 		} else | ||||
| 		// if the exact item exists, use it | ||||
| 		if (data[q]) { | ||||
| 			return data[q]; | ||||
| 		} else if (options.matchSubset) { | ||||
| 			for ( var i = q.length - 1; i >= options.minChars; i--) { | ||||
| 				var c = data[q.substr(0, i)]; | ||||
| 				if (c) { | ||||
| 					var csub = []; | ||||
| 					$.each(c, function(i, x) { | ||||
| 						if (matchSubset(x.value, q)) { | ||||
| 							csub[csub.length] = x; | ||||
| 						} | ||||
| 					}); | ||||
| 					return csub; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		return null; | ||||
| 	} | ||||
| 		}; | ||||
| 	}; | ||||
|  | ||||
| 	$.Autocompleter.Select = function(options, input, select, config) { | ||||
| 		var CLASSES = { | ||||
| 			ACTIVE : "ac_over" | ||||
| 		}; | ||||
|  | ||||
| 		var listItems, active = -1, data, term = "", needsInit = true, element, list; | ||||
|  | ||||
| 		// Create results | ||||
| 		function init() { | ||||
| 			if (!needsInit) | ||||
| 				return; | ||||
| 			element = $("<div/>").hide().addClass(options.resultsClass) | ||||
| 				.css("position", "absolute").appendTo(document.body); | ||||
|  | ||||
| 			list = $("<ul/>").appendTo(element).mouseover(function(event) { | ||||
| 				if (target(event).nodeName && | ||||
| 					target(event).nodeName.toUpperCase() == 'LI') { | ||||
| 					active = $("li", list).removeClass(CLASSES.ACTIVE) | ||||
| 						.index(target(event)); | ||||
| 					$(target(event)).addClass(CLASSES.ACTIVE); | ||||
| 				} | ||||
| 			}).click(function(event) { | ||||
| 				$(target(event)).addClass(CLASSES.ACTIVE); | ||||
| 				select(); | ||||
| 				// TODO provide option to avoid setting focus again after | ||||
| 				// selection? useful for cleanup-on-focus | ||||
| 				input.focus(); | ||||
| 				return false; | ||||
| 			}).mousedown(function() { | ||||
| 				config.mouseDownOnSelect = true; | ||||
| 			}).mouseup(function() { | ||||
| 				config.mouseDownOnSelect = false; | ||||
| 			}); | ||||
|  | ||||
| 			if (options.width > 0) | ||||
| 				element.css("width", options.width); | ||||
|  | ||||
| 			needsInit = false; | ||||
| 		} | ||||
|  | ||||
| 		function target(event) { | ||||
| 			var element = event.target; | ||||
| 			while (element && element.tagName != "LI") | ||||
| 				element = element.parentNode; | ||||
| 			// more fun with IE, sometimes event.target is empty, just ignore it | ||||
| 			// then | ||||
| 			if (!element) | ||||
| 				return []; | ||||
| 			return element; | ||||
| 		} | ||||
|  | ||||
| 		function moveSelect(step) { | ||||
| 			listItems.slice(active, active + 1).removeClass(CLASSES.ACTIVE); | ||||
| 			movePosition(step); | ||||
| 			var activeItem = listItems.slice(active, active + 1) | ||||
| 				.addClass(CLASSES.ACTIVE); | ||||
| 			if (options.scroll) { | ||||
| 				var offset = 0; | ||||
| 				listItems.slice(0, active).each(function() { | ||||
| 					offset += this.offsetHeight; | ||||
| 				}); | ||||
| 				if ((offset + activeItem[0].offsetHeight - list.scrollTop()) > list[0].clientHeight) { | ||||
| 					list.scrollTop(offset + activeItem[0].offsetHeight - | ||||
| 						list.innerHeight()); | ||||
| 				} else if (offset < list.scrollTop()) { | ||||
| 					list.scrollTop(offset); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		; | ||||
|  | ||||
| 		function movePosition(step) { | ||||
| 			active += step; | ||||
| 			if (active < 0) { | ||||
| 				active = listItems.size() - 1; | ||||
| 			} else if (active >= listItems.size()) { | ||||
| 				active = 0; | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		function limitNumberOfItems(available) { | ||||
| 			return options.max && options.max < available ? options.max : available; | ||||
| 		} | ||||
|  | ||||
| 		function fillList() { | ||||
| 			list.empty(); | ||||
| 			var max = limitNumberOfItems(data.length); | ||||
| 			for ( var i = 0; i < max; i++) { | ||||
| 				if (!data[i]) | ||||
| 					continue; | ||||
| 				var formatted = options | ||||
| 					.formatItem(data[i].data, i + 1, max, data[i].value, term); | ||||
| 				if (formatted === false) | ||||
| 					continue; | ||||
| 				var li = $("<li/>").html(options.highlight(formatted, term)) | ||||
| 					.addClass(i % 2 == 0 ? "ac_even" : "ac_odd").appendTo(list)[0]; | ||||
| 				$.data(li, "ac_data", data[i]); | ||||
| 			} | ||||
| 			listItems = list.find("li"); | ||||
| 			if (options.selectFirst) { | ||||
| 				listItems.slice(0, 1).addClass(CLASSES.ACTIVE); | ||||
| 				active = 0; | ||||
| 			} | ||||
| 			// apply bgiframe if available | ||||
| 			if ($.fn.bgiframe) | ||||
| 				list.bgiframe(); | ||||
| 		} | ||||
|  | ||||
| 		return { | ||||
| 			display : function(d, q) { | ||||
| 				init(); | ||||
| 				data = d; | ||||
| 				term = q; | ||||
| 				fillList(); | ||||
| 			}, | ||||
| 			next : function() { | ||||
| 				moveSelect(1); | ||||
| 			}, | ||||
| 			prev : function() { | ||||
| 				moveSelect(-1); | ||||
| 			}, | ||||
| 			pageUp : function() { | ||||
| 				if (active != 0 && active - 8 < 0) { | ||||
| 					moveSelect(-active); | ||||
| 				} else { | ||||
| 					moveSelect(-8); | ||||
| 				} | ||||
| 			}, | ||||
| 			pageDown : function() { | ||||
| 				if (active != listItems.size() - 1 && | ||||
| 					active + 8 > listItems.size()) { | ||||
| 					moveSelect(listItems.size() - 1 - active); | ||||
| 				} else { | ||||
| 					moveSelect(8); | ||||
| 				} | ||||
| 			}, | ||||
| 			hide : function() { | ||||
| 				element && element.hide(); | ||||
| 				listItems && listItems.removeClass(CLASSES.ACTIVE); | ||||
| 				active = -1; | ||||
| 			}, | ||||
| 			visible : function() { | ||||
| 				return element && element.is(":visible"); | ||||
| 			}, | ||||
| 			current : function() { | ||||
| 				return this.visible() && | ||||
| 					(listItems.filter("." + CLASSES.ACTIVE)[0] || options.selectFirst && | ||||
| 						listItems[0]); | ||||
| 			}, | ||||
| 			show : function() { | ||||
| 				var offset = $(input).offset(); | ||||
| 				element.css( { | ||||
| 					width : typeof options.width == "string" || | ||||
| 						options.width > 0 ? options.width : $(input).width(), | ||||
| 					top : offset.top + input.offsetHeight, | ||||
| 					left : offset.left | ||||
| 				}).show(); | ||||
| 				if (options.scroll) { | ||||
| 					list.scrollTop(0); | ||||
| 					list.css( { | ||||
| 						maxHeight : options.scrollHeight, overflow : 'auto' | ||||
| 					}); | ||||
|  | ||||
| 					if ($.browser.msie && | ||||
| 						typeof document.body.style.maxHeight === "undefined") { | ||||
| 						var listHeight = 0; | ||||
| 						listItems.each(function() { | ||||
| 							listHeight += this.offsetHeight; | ||||
| 						}); | ||||
| 						var scrollbarsVisible = listHeight > options.scrollHeight; | ||||
| 						list | ||||
| 							.css('height', scrollbarsVisible ? options.scrollHeight : listHeight); | ||||
| 						if (!scrollbarsVisible) { | ||||
| 							// IE doesn't recalculate width when scrollbar disappears | ||||
| 							listItems.width(list.width() - | ||||
| 								parseInt(listItems.css("padding-left")) - | ||||
| 								parseInt(listItems.css("padding-right"))); | ||||
| 						} | ||||
| 					} | ||||
|  | ||||
| 				} | ||||
| 			}, | ||||
| 			selected : function() { | ||||
| 				var selected = listItems && | ||||
| 					listItems.filter("." + CLASSES.ACTIVE) | ||||
| 						.removeClass(CLASSES.ACTIVE); | ||||
| 				return selected && selected.length && | ||||
| 					$.data(selected[0], "ac_data"); | ||||
| 			}, | ||||
| 			emptyList : function() { | ||||
| 				list && list.empty(); | ||||
| 			}, | ||||
| 			unbind : function() { | ||||
| 				element && element.remove(); | ||||
| 			} | ||||
| 		}; | ||||
| 	}; | ||||
|  | ||||
| 	$.fn.selection = function(start, end) { | ||||
| 		if (start !== undefined) { | ||||
| 			return this.each(function() { | ||||
| 				if (this.createTextRange) { | ||||
| 					var selRange = this.createTextRange(); | ||||
| 					if (end === undefined || start == end) { | ||||
| 						selRange.move("character", start); | ||||
| 						selRange.select(); | ||||
| 					} else { | ||||
| 						selRange.collapse(true); | ||||
| 						selRange.moveStart("character", start); | ||||
| 						selRange.moveEnd("character", end); | ||||
| 						selRange.select(); | ||||
| 					} | ||||
| 				} else if (this.setSelectionRange) { | ||||
| 					this.setSelectionRange(start, end); | ||||
| 				} else if (this.selectionStart) { | ||||
| 					this.selectionStart = start; | ||||
| 					this.selectionEnd = end; | ||||
| 				} | ||||
| 			}); | ||||
| 		} | ||||
| 		var field = this[0]; | ||||
| 		if (field.createTextRange) { | ||||
| 			var range = document.selection.createRange(), orig = field.value, teststring = "<->", textLength = range.text.length; | ||||
| 			range.text = teststring; | ||||
| 			var caretAt = field.value.indexOf(teststring); | ||||
| 			field.value = orig; | ||||
| 			this.selection(caretAt, caretAt + textLength); | ||||
| 			return { | ||||
| 				start : caretAt, end : caretAt + textLength | ||||
| 			} | ||||
| 		} else if (field.selectionStart !== undefined) { | ||||
| 			return { | ||||
| 				start : field.selectionStart, end : field.selectionEnd | ||||
| 			} | ||||
| 		} | ||||
| 	}; | ||||
|  | ||||
| })(jQuery); | ||||
		Reference in New Issue
	
	Block a user