1 /* jQuery autocomplete Copyright Dylan Verheul <dylan@dyve.net>
2 * Licensed like jQuery, see http://docs.jquery.com/License
5 $.autocomplete
= function(input
, options
) {
6 // Create a link to self
9 // Create jQuery object for input element
10 var $input
= $(input
).attr("autocomplete", "off");;
12 // Apply inputClass if necessary
13 if (options
.inputClass
) $input
.addClass(options
.inputClass
);
16 var results
= document
.createElement("div");
17 // Create jQuery object for results
18 var $results
= $(results
);
19 // Set default values for results
20 var pos
= findPos(input
);
21 $results
.hide().addClass(options
.resultsClass
).css({
23 top
: (pos
.y
+ input
.offsetHeight
) + "px",
25 minWidth
: $(input
).width()
27 // Add to body element
28 $(input
).parent().append(results
);
30 input
.autocompleter
= me
;
31 input
.lastSelected
= $input
.val();
40 .keydown(function(e
) {
52 if (selectCurrent()) {
58 if (timeout
) clearTimeout(timeout
);
59 timeout
= setTimeout(onChange
, options
.delay
);
71 if (v
== prev
) return;
73 if (v
.length
>= options
.minChars
) {
74 $input
.addClass(options
.loadingClass
);
77 $input
.removeClass(options
.loadingClass
);
82 function moveSelect(step
) {
84 var lis
= $("li", results
);
91 } else if (active
>= lis
.size()) {
92 active
= lis
.size() - 1;
95 lis
.removeClass("over");
97 $(lis
[active
]).addClass("over");
99 // Weird behaviour in IE
100 // if (lis[active] && lis[active].scrollIntoView) {
101 // lis[active].scrollIntoView(false);
106 function selectCurrent() {
107 var li
= $("li.over", results
)[0];
109 var $li
= $("li", results
);
110 if (options
.selectOnly
) {
111 if ($li
.length
== 1) li
= $li
[0];
112 } else if (options
.selectFirst
) {
124 function selectItem(li
) {
126 li
= document
.createElement("li");
130 var v
= $.trim(li
.selectValue
? li
.selectValue
: li
.innerHTML
);
131 input
.lastSelected
= v
;
136 if (options
.onItemSelect
) setTimeout(function() { options
.onItemSelect(li
) }, 1);
139 function hideResults() {
140 if (timeout
) clearTimeout(timeout
);
141 timeout
= setTimeout(hideResultsNow
, 200);
144 function hideResultsNow() {
145 if (timeout
) clearTimeout(timeout
);
146 $input
.removeClass(options
.loadingClass
);
147 if ($results
.is(":visible")) {
150 if (options
.mustMatch
) {
151 var v
= $input
.val();
152 if (v
!= input
.lastSelected
) {
158 function receiveData(q
, data
) {
160 $input
.removeClass(options
.loadingClass
);
161 results
.innerHTML
= "";
162 if ($.browser
.msie
) {
163 // we put a styled iframe behind the calendar so HTML SELECT elements don't show through
164 $results
.append(document
.createElement('iframe'));
166 var datasInDom
= dataToDom(data
);
167 results
.appendChild(datasInDom
);
169 // set size of ul smaller (for borders) but almost as big as div
170 $(datasInDom
).width($results
.width()-2);
176 function parseData(data
) {
177 if (!data
) return null;
179 var rows
= data
.split(options
.lineSeparator
);
180 for (var i
=0; i
< rows
.length
; i
++) {
181 var row
= $.trim(rows
[i
]);
183 parsed
[parsed
.length
] = row
.split(options
.cellSeparator
);
189 function dataToDom(data
) {
190 var ul
= document
.createElement("ul");
191 var num
= data
.length
;
192 for (var i
=0; i
< num
; i
++) {
195 var li
= document
.createElement("li");
196 if (options
.formatItem
) {
197 li
.innerHTML
= options
.formatItem(row
, i
, num
);
198 li
.selectValue
= row
[0];
200 li
.innerHTML
= row
[0];
203 if (row
.length
> 1) {
205 for (var j
=1; j
< row
.length
; j
++) {
206 extra
[extra
.length
] = row
[j
];
212 function() { $("li", ul
).removeClass("over"); $(this).addClass("over"); },
213 function() { $(this).removeClass("over"); }
214 ).click(function(e
) { e
.preventDefault(); e
.stopPropagation(); selectItem(this) });
219 function requestData(q
) {
220 if (!options
.matchCase
) q
= q
.toLowerCase();
221 var data
= options
.cacheLength
? loadFromCache(q
) : null;
223 receiveData(q
, data
);
225 $.get(makeUrl(q
), function(data
) {
226 data
= parseData(data
)
228 receiveData(q
, data
);
233 function makeUrl(q
) {
234 var url
= options
.url
+ "?q=" + q
;
235 for (var i
in options
.extraParams
) {
236 url
+= "&" + i
+ "=" + options
.extraParams
[i
];
241 function loadFromCache(q
) {
243 if (cache
[q
]) return cache
[q
];
244 if (options
.matchSubset
) {
245 for (var i
= q
.length
- 1; i
>= options
.minChars
; i
--) {
246 var qs
= q
.substr(0, i
);
250 for (var j
= 0; j
< c
.length
; j
++) {
253 if (matchSubset(x0
, q
)) {
254 csub
[csub
.length
] = x
;
264 function matchSubset(s
, sub
) {
265 if (!options
.matchCase
) s
= s
.toLowerCase();
266 var i
= s
.indexOf(sub
);
267 if (i
== -1) return false;
268 return i
== 0 || options
.matchContains
;
271 this.flushCache
= function() {
275 this.setExtraParams
= function(p
) {
276 options
.extraParams
= p
;
279 function addToCache(q
, data
) {
280 if (!data
|| !q
|| !options
.cacheLength
) return;
281 if (!cache
.length
|| cache
.length
> options
.cacheLength
) {
283 cache
.length
= 1; // we know we're adding something
284 } else if (!cache
[q
]) {
290 function findPos(obj
) {
291 var curleft
= obj
.offsetLeft
|| 0;
292 var curtop
= obj
.offsetTop
|| 0;
293 while (obj
= obj
.offsetParent
) {
294 curleft
+= obj
.offsetLeft
295 curtop
+= obj
.offsetTop
297 return {x
:curleft
,y
:curtop
};
301 $.fn
.autocomplete
= function(url
, options
) {
302 // Make sure options exists
303 options
= options
|| {};
306 // Set default values for required options
307 options
.inputClass
= options
.inputClass
|| "ac_input";
308 options
.resultsClass
= options
.resultsClass
|| "ac_results";
309 options
.lineSeparator
= options
.lineSeparator
|| "\n";
310 options
.cellSeparator
= options
.cellSeparator
|| "|";
311 options
.minChars
= options
.minChars
|| 1;
312 options
.delay
= options
.delay
|| 400;
313 options
.matchCase
= options
.matchCase
|| 0;
314 options
.matchSubset
= typeof(options
.matchSubset
) != 'undefined' ? options
.matchSubset
: 1;
315 options
.matchContains
= options
.matchContains
|| 0;
316 options
.cacheLength
= options
.cacheLength
|| 1;
317 options
.mustMatch
= options
.mustMatch
|| 0;
318 options
.extraParams
= options
.extraParams
|| {};
319 options
.loadingClass
= options
.loadingClass
|| "ac_loading";
320 options
.selectFirst
= options
.selectFirst
|| false;
321 options
.selectOnly
= options
.selectOnly
|| false;
323 this.each(function() {
325 new $.autocomplete(input
, options
);
328 // Don't break the chain