Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed
/** 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);options.highlight = options.highlight || function(value) {return value;};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};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;$.browser.opera&& $(input.form).bind("submit.autocomplete", function() {if (blockSubmit) {blockSubmit = false;return false;}});$input.bind(($.browser.opera ? "keypress" : "keydown") + ".autocomplete",function(event) {hasFocus = 1;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;case options.multiple&& $.trim(options.multipleSeparator) == ","&& KEY.COMMA:case KEY.TAB:case KEY.RETURN:if (selectCurrent()) {event.preventDefault();blockSubmit = true;return false;}break;case KEY.ESC:select.hide();break;default:clearTimeout(timeout);timeout = setTimeout(onChange, options.delay);break;}}).focus(function() {hasFocus++;}).blur(function() {hasFocus = 0;if (!config.mouseDownOnSelect) {hideResults();}}).click(function() {if (hasFocus++ > 1 && !select.visible()) {onChange(0, true);}}).bind("search",function() {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 ("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;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];}function autoFill(q, sValue) {if (options.autoFill&& (lastWord($input.val()).toLowerCase() == q.toLowerCase())&& lastKeyPressCode != KEY.BACKSPACE) {$input.val($input.val()+ sValue.substring(lastWord(previousValue).length));$(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) {$input.search(function(result) {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);if (data && data.length) {success(term, data);} 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({mode : "abort",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 {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;var stMatchSets = {}, nullData = 0;if (!options.url)options.cacheLength = 1;stMatchSets[""] = [];for ( var i = 0, ol = options.data.length; i < ol; i++) {var rawValue = options.data[i];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 (!stMatchSets[firstChar])stMatchSets[firstChar] = [];var row = {value : value,data : rawValue,result : options.formatResult&& options.formatResult(rawValue) || value};stMatchSets[firstChar].push(row);if (nullData++ < options.max) {stMatchSets[""].push(row);}};$.each(stMatchSets, function(i, value) {options.cacheLength++;add(i, value);});}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 (!options.url && options.matchContains) {var csub = [];for ( var k in data) {if (k.length > 0) {var c = data[k];$.each(c, function(i, x) {if (matchSubset(x.value, q)) {csub.push(x);}});}}return csub;} else 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;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();input.focus();return false;}).mousedown(function() {config.mouseDownOnSelect = true;}).mouseup(function() {config.mouseDownOnSelect = false;});if (options.width > 0){element.css("width", options.width);}else{options.width = 232;element.css("width", options.width);}needsInit = false;}function target(event) {var element = event.target;while (element && element.tagName != "LI")element = element.parentNode;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;}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) {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);