Jquery. AutoComplete. js Chinese version (firefox is supported). Note that it fixes some bugs in Chinese Input. You can test them if you need them.
The Code is as follows:
JQuery. autocomplete = function (input, options ){
// Create a link to self
Var me = this;
// Create jQuery object for input element
Var $ input = $ (input). attr ("autocomplete", "off ");
// Apply inputClass if necessary
If (options. inputClass) $ input. addClass (options. inputClass );
// Create results
Var results = document. createElement ("p ");
// Create jQuery object for results
Var $ results = $ (results );
Using results.hide().addclass(options.resultsclass).css ("position", "absolute ");
If (options. width> 0) Then results.css ("width", options. width );
// Add to body element
$ ("Body"). append (results );
Input. autocompleter = me;
Var timeout = null;
Var prev = "";
Var active =-1;
Var cache = {};
Var keyb = false;
Var hasFocus = false;
Var lastKeyPressCode = null;
// Flush cache
Function flushCache (){
Cache = {};
Cache. data = {};
Cache. length = 0;
};
// Flush cache
FlushCache ();
// If there is a data array supplied
If (options. data! = Null ){
Var sFirstChar = "", stMatchSets ={}, row = [];
// No url was specified, we need to adjust the cache length to make sure it fits the local data store
If (typeof options. url! = "String") options. cacheLength = 1;
// Loop through the array and create a lookup structure
For (var I = 0; I <options. data. length; I ++ ){
// If row is a string, make an array otherwise just reference the array
Row = (typeof options. data [I] = "string ")? [Options. data [I]: options. data [I]);
// If the length is zero, don't add to list
If (row [0]. length> 0 ){
// Get the first character
SFirstChar = row [0]. substring (0, 1). toLowerCase ();
// If no lookup array for this character exists, look it up now
If (! StMatchSets [sFirstChar]) stMatchSets [sFirstChar] = [];
// If the match is a string
StMatchSets [sFirstChar]. push (row );
}
}
// Add the data items to the cache
For (var k in stMatchSets ){
// Increase the cache size
Options. cacheLength ++;
// Add to the cache
AddToCache (k, stMatchSets [k]);
}
}
$ Input
. Keydown (function (e ){
// Track last key pressed
LastKeyPressCode = e. keyCode;
Switch (e. keyCode ){
Case 38: // up
E. preventDefault ();
MoveSelect (-1 );
Break;
Case 40: // down
E. preventDefault ();
MoveSelect (1 );
Break;
Case 9: // tab
Case 13: // return
If (selectCurrent ()){
// Make sure to blur off the current field
$ Input. get (0). blur ();
E. preventDefault ();
}
Break;
Default:
Active =-1;
If (timeout) clearTimeout (timeout );
Timeout = setTimeout (function () {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 = true;
})
. Blur (function (){
// Track whether the field has focus
HasFocus = false;
HideResults ();
})
. Bind ("input", function (){
// @ Hack: support for inputing chinese characters in firefox
OnChange (0, true );
});
HideResultsNow ();
Function onChange (){
// Ignore if the following keys are pressed: [del] [shift] [capslock]
If (lastKeyPressCode = 46 | (lastKeyPressCode> 8 & lastKeyPressCode <32) return $ results. hide ();
Var v = $ input. val ();
If (v = prev) return;
Prev = v;
If (v. length> = options. minChars ){
$ Input. addClass (options. loadingClass );
RequestData (v );
} Else {
$ Input. removeClass (options. loadingClass );
$ Results. hide ();
}
};
Function moveSelect (step ){
Var lis = $ ("li", results );
If (! Lis) return;
Active + = step;
If (active <0 ){
Active = 0;
} Else if (active> = lis. size ()){
Active = lis. size ()-1;
}
Lis. removeClass ("ac_over ");
$ (Lis [active]). addClass ("ac_over ");
// Weird behaviour in IE
// If (lis [active] & lis [active]. scrollIntoView ){
// Lis [active]. scrollIntoView (false );
//}
};
Function selectCurrent (){
Var li = $ ("li. ac_over", results) [0];
If (! Li ){
Var $ li = $ ("li", results );
If (options. selectOnly ){
If ($ li. length = 1) li = $ li [0];
} Else if (options. selectFirst ){
Li = $ li [0];
}
}
If (li ){
SelectItem (li );
Return true;
} Else {
Return false;
}
};
Function selectItem (li ){
If (! Li ){
Li = document. createElement ("li ");
Li. extra = [];
Li. selectValue = "";
}
Var v = $. trim (li. selectValue? Li. selectValue: li. innerHTML );
Input. lastSelected = v;
Prev = v;
Optional results.html ("");
$ Input. val (v );
HideResultsNow ();
If (options. onItemSelect) setTimeout (function () {options. onItemSelect (li)}, 1 );
};
// Selects a portion of the input string
Function createSelection (start, end ){
// Get a reference to the input element
Var field = $ input. get (0 );
If (field. createTextRange ){
Var selRange = field. createTextRange ();
SelRange. collapse (true );
SelRange. moveStart ("character", start );
SelRange. moveEnd ("character", end );
SelRange. select ();
} Else if (field. setSelectionRange ){
Field. setSelectionRange (start, end );
} Else {
If (field. selectionStart ){
Field. selectionStart = start;
Field. selectionEnd = end;
}
}
Field. focus ();
};
// Fills in the input box w/the first match (assumed to be the best match)
Function autoFill (sValue ){
// If the last user key pressed was backspace, don't autofill
If (lastKeyPressCode! = 8 ){
// Fill in the value (keep the case the user has typed)
$ Input. val ($ input. val () + sValue. substring (prev. length ));
// Select the portion of the value not typed by the user (so the next character will erase)
CreateSelection (prev. length, sValue. length );
}
};
Function showResults (){
// Get the position of the input field right now (in case the DOM is shifted)
Var pos = findPos (input );
// Either use the specified width, or autocalculate based on form element
Var iWidth = (options. width> 0 )? Options. width: $ input. width ();
// Reposition
$Results.css ({
Width: parseInt (iWidth) + "px ",
Top: (pos. y + input. offsetHeight) + "px ",
Left: pos. x + "px"
}). Show ();
};
Function hideResults (){
If (timeout) clearTimeout (timeout );
Timeout = setTimeout (hideResultsNow, 200 );
};
Function hideResultsNow (){
If (timeout) clearTimeout (timeout );
$ Input. removeClass (options. loadingClass );
If ($ results. is (": visible ")){
$ Results. hide ();
}
If (options. mustMatch ){
Var v = $ input. val ();
If (v! = Input. lastSelected ){
SelectItem (null );
}
}
};
Function receiveData (q, data ){
If (data ){
$ Input. removeClass (options. loadingClass );
Results. innerHTML = "";
// If the field no longer has focus or if there are no matches, do not display the drop down
If (! HasFocus | data. length = 0) return hideResultsNow ();
If ($. browser. msie ){
// We put a styled iframe behind the calendar so html select elements don't show through
$ Results. append (document. createElement ('iframe '));
}
Results. appendChild (dataToDom (data ));
// Autofill in the complete box w/the first match as long as the user hasn' t entered in more data
If (options. autoFill & ($ input. val (). toLowerCase () = q. toLowerCase () autoFill (data [0] [0]);
ShowResults ();
} Else {
HideResultsNow ();
}
};
Function parseData (data ){
If (! Data) return null;
Var parsed = [];
Var rows = data. split (options. lineSeparator );
For (var I = 0; I <rows. length; I ++ ){
Var row = $. trim (rows [I]);
If (row ){
Parsed [parsed. length] = row. split (options. cellSeparator );
}
}
Return parsed;
};
Function dataToDom (data ){
Var ul = document. createElement ("ul ");
Var num = data. length;
// Limited results to a max number
If (options. maxItemsToShow> 0) & (options. maxItemsToShow <num) num = options. maxItemsToShow;
For (var I = 0; I <num; I ++ ){
Var row = data [I];
If (! Row) continue;
Var li = document. createElement ("li ");
If (options. formatItem ){
Li. innerHTML = options. formatItem (row, I, num );
Li. selectValue = row [0];
} Else {
Li. innerHTML = row [0];
Li. selectValue = row [0];
}
Var extra = null;
If (row. length> 1 ){
Extra = [];
For (var j = 1; j <row. length; j ++ ){
Extra [extra. length] = row [j];
}
}
Li. extra = extra;
Ul. appendChild (li );
$ (Li). hover (
Function () {$ ("li", ul ). removeClass ("ac_over"); $ (this ). addClass ("ac_over"); active = $ ("li", ul ). indexOf ($ (this ). get (0 ));},
Function () {$ (this). removeClass ("ac_over ");}
). Click (function (e) {e. preventDefault (); e. stopPropagation (); selectItem (this )});
}
Return ul;
};
Function requestData (q ){
If (! Options. matchCase) q = q. toLowerCase ();
Var data = options. cacheLength? LoadFromCache (q): null;
// Recieve the cached data
If (data ){
ReceiveData (q, data );
// If an AJAX url has been supplied, try loading the data now
} Else if (typeof options. url = "string") & (options. url. length> 0 )){
$. Get (makeUrl (q), function (data ){
Data = parseData (data );
AddToCache (q, data );
ReceiveData (q, data );
});
// If there's been no data found, remove the loading class
} Else {
$ Input. removeClass (options. loadingClass );
}
};
Function makeUrl (q ){
Var url = options. url + "? Q = "+ escape (q );
For (var I in options. extraParams ){
Url + = "&" + I + "=" + escape (options. extraParams [I]);
}
Return url;
};
Function loadFromCache (q ){
If (! Q) return null;
If (cache. data [q]) return cache. data [q];
If (options. matchSubset ){
For (var I = q. length-1; I> = options. minChars; I --){
Var qs = q. substr (0, I );
Var c = cache. data [qs];
If (c ){
Var csub = [];
For (var j = 0; j <c. length; j ++ ){
Var x = c [j];
Var x0 = x [0];
If (matchSubset (x0, q )){
Csub [csub. length] = x;
}
}
Return csub;
}
}
}
Return null;
};
Function matchSubset (s, sub ){
If (! Options. matchCase) s = s. toLowerCase ();
Var I = s. indexOf (sub );
If (I =-1) return false;
Return I = 0 | options. matchContains;
};
This. flushCache = function (){
FlushCache ();
};
This. setExtraParams = function (p ){
Options. extraParams = p;
};
This. findValue = function (){
Var q = $ input. val ();
If (! Options. matchCase) q = q. toLowerCase ();
Var data = options. cacheLength? LoadFromCache (q): null;
If (data ){
FindValueCallback (q, data );
} Else if (typeof options. url = "string") & (options. url. length> 0 )){
$. Get (makeUrl (q), function (data ){
Data = parseData (data)
AddToCache (q, data );
FindValueCallback (q, data );
});
} Else {
// No matches
FindValueCallback (q, null );
}
}
Function findValueCallback (q, data ){
If (data) $ input. removeClass (options. loadingClass );
Var num = (data )? Data. length: 0;
Var li = null;
For (var I = 0; I <num; I ++ ){
Var row = data [I];
If (row [0]. toLowerCase () = q. toLowerCase ()){
Li = document. createElement ("li ");
If (options. formatItem ){
Li. innerHTML = options. formatItem (row, I, num );
Li. selectValue = row [0];
} Else {
Li. innerHTML = row [0];
Li. selectValue = row [0];
}
Var extra = null;
If (row. length> 1 ){
Extra = [];
For (var j = 1; j <row. length; j ++ ){
Extra [extra. length] = row [j];
}
}
Li. extra = extra;
}
}
If (options. onFindValue) setTimeout (function () {options. onFindValue (li)}, 1 );
}
Function addToCache (q, data ){
If (! Data |! Q |! Options. cacheLength) return;
If (! Cache. length | cache. length> options. cacheLength ){
FlushCache ();
Cache. length ++;
} Else if (! Cache [q]) {
Cache. length ++;
}
Cache. data [q] = data;
};
Function findPos (obj ){
Var curleft = obj. offsetLeft | 0;
Var curtop = obj. offsetTop | 0;
While (obj = obj. offsetParent ){
Curleft + = obj. offsetLeft
Curtop + = obj. offsetTop
}
Return {x: curleft, y: curtop };
}
}
JQuery. fn. autocomplete = function (url, options, data ){
// Make sure options exists
Options = options | {};
// Set url as option
Options. url = url;
// Set some bulk local data
Options. data = (typeof data = "object") & (data. constructor = Array ))? Data: null;
// Set default values for required options
Options. inputClass = options. inputClass | "ac_input ";
Options. resultsClass = options. resultsClass | "ac_results ";
Options. lineSeparator = options. lineSeparator | "\ n ";
Options. cellSeparator = options. cellSeparator | "| ";
Options. minChars = options. minChars | 1;
Options. delay = options. delay | 400;
Options. matchCase = options. matchCase | 0;
Options. matchSubset = options. matchSubset | 1;
Options. matchContains = options. matchContains | 0;
Options. cacheLength = options. cacheLength | 1;
Options. mustMatch = options. mustMatch | 0;
Options. extraParams = options. extraParams || {};
Options. loadingClass = options. loadingClass | "ac_loading ";
Options. selectFirst = options. selectFirst | false;
Options. selectOnly = options. selectOnly | false;
Options. maxItemsToShow = options. maxItemsToShow |-1;
Options. autoFill = options. autoFill | false;
Options. width = parseInt (options. width, 10) | 0;
This. each (function (){
Var input = this;
New jQuery. autocomplete (input, options );
});
// Don't break the chain
Return this;
}
JQuery. fn. autocompleteArray = function (data, options ){
Return this. autocomplete (null, options, data );
}
JQuery. fn. indexOf = function (e ){
For (var I = 0; I <this. length; I ++ ){
If (this [I] = e) return I;
}
Return-1;
};