Mercurial > public > sg101
comparison media/js/jquery-autocomplete/jquery.autocomplete.js @ 45:a5b4c5ce0658
Breaking down and controlling all media files, including javascript libraries.
author | Brian Neal <bgneal@gmail.com> |
---|---|
date | Fri, 19 Jun 2009 03:16:03 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
44:08cd19c1ee50 | 45:a5b4c5ce0658 |
---|---|
1 /* | |
2 * Autocomplete - jQuery plugin 1.0.2 | |
3 * | |
4 * Copyright (c) 2007 Dylan Verheul, Dan G. Switzer, Anjesh Tuladhar, Jörn Zaefferer | |
5 * | |
6 * Dual licensed under the MIT and GPL licenses: | |
7 * http://www.opensource.org/licenses/mit-license.php | |
8 * http://www.gnu.org/licenses/gpl.html | |
9 * | |
10 * Revision: $Id: jquery.autocomplete.js 5747 2008-06-25 18:30:55Z joern.zaefferer $ | |
11 * | |
12 */ | |
13 | |
14 ;(function($) { | |
15 | |
16 $.fn.extend({ | |
17 autocomplete: function(urlOrData, options) { | |
18 var isUrl = typeof urlOrData == "string"; | |
19 options = $.extend({}, $.Autocompleter.defaults, { | |
20 url: isUrl ? urlOrData : null, | |
21 data: isUrl ? null : urlOrData, | |
22 delay: isUrl ? $.Autocompleter.defaults.delay : 10, | |
23 max: options && !options.scroll ? 10 : 150 | |
24 }, options); | |
25 | |
26 // if highlight is set to false, replace it with a do-nothing function | |
27 options.highlight = options.highlight || function(value) { return value; }; | |
28 | |
29 // if the formatMatch option is not specified, then use formatItem for backwards compatibility | |
30 options.formatMatch = options.formatMatch || options.formatItem; | |
31 | |
32 return this.each(function() { | |
33 new $.Autocompleter(this, options); | |
34 }); | |
35 }, | |
36 result: function(handler) { | |
37 return this.bind("result", handler); | |
38 }, | |
39 search: function(handler) { | |
40 return this.trigger("search", [handler]); | |
41 }, | |
42 flushCache: function() { | |
43 return this.trigger("flushCache"); | |
44 }, | |
45 setOptions: function(options){ | |
46 return this.trigger("setOptions", [options]); | |
47 }, | |
48 unautocomplete: function() { | |
49 return this.trigger("unautocomplete"); | |
50 } | |
51 }); | |
52 | |
53 $.Autocompleter = function(input, options) { | |
54 | |
55 var KEY = { | |
56 UP: 38, | |
57 DOWN: 40, | |
58 DEL: 46, | |
59 TAB: 9, | |
60 RETURN: 13, | |
61 ESC: 27, | |
62 COMMA: 188, | |
63 PAGEUP: 33, | |
64 PAGEDOWN: 34, | |
65 BACKSPACE: 8 | |
66 }; | |
67 | |
68 // Create $ object for input element | |
69 var $input = $(input).attr("autocomplete", "off").addClass(options.inputClass); | |
70 | |
71 var timeout; | |
72 var previousValue = ""; | |
73 var cache = $.Autocompleter.Cache(options); | |
74 var hasFocus = 0; | |
75 var lastKeyPressCode; | |
76 var config = { | |
77 mouseDownOnSelect: false | |
78 }; | |
79 var select = $.Autocompleter.Select(options, input, selectCurrent, config); | |
80 | |
81 var blockSubmit; | |
82 | |
83 // prevent form submit in opera when selecting with return key | |
84 $.browser.opera && $(input.form).bind("submit.autocomplete", function() { | |
85 if (blockSubmit) { | |
86 blockSubmit = false; | |
87 return false; | |
88 } | |
89 }); | |
90 | |
91 // only opera doesn't trigger keydown multiple times while pressed, others don't work with keypress at all | |
92 $input.bind(($.browser.opera ? "keypress" : "keydown") + ".autocomplete", function(event) { | |
93 // track last key pressed | |
94 lastKeyPressCode = event.keyCode; | |
95 switch(event.keyCode) { | |
96 | |
97 case KEY.UP: | |
98 event.preventDefault(); | |
99 if ( select.visible() ) { | |
100 select.prev(); | |
101 } else { | |
102 onChange(0, true); | |
103 } | |
104 break; | |
105 | |
106 case KEY.DOWN: | |
107 event.preventDefault(); | |
108 if ( select.visible() ) { | |
109 select.next(); | |
110 } else { | |
111 onChange(0, true); | |
112 } | |
113 break; | |
114 | |
115 case KEY.PAGEUP: | |
116 event.preventDefault(); | |
117 if ( select.visible() ) { | |
118 select.pageUp(); | |
119 } else { | |
120 onChange(0, true); | |
121 } | |
122 break; | |
123 | |
124 case KEY.PAGEDOWN: | |
125 event.preventDefault(); | |
126 if ( select.visible() ) { | |
127 select.pageDown(); | |
128 } else { | |
129 onChange(0, true); | |
130 } | |
131 break; | |
132 | |
133 // matches also semicolon | |
134 case options.multiple && $.trim(options.multipleSeparator) == "," && KEY.COMMA: | |
135 case KEY.TAB: | |
136 case KEY.RETURN: | |
137 if( selectCurrent() ) { | |
138 // stop default to prevent a form submit, Opera needs special handling | |
139 event.preventDefault(); | |
140 blockSubmit = true; | |
141 return false; | |
142 } | |
143 break; | |
144 | |
145 case KEY.ESC: | |
146 select.hide(); | |
147 break; | |
148 | |
149 default: | |
150 clearTimeout(timeout); | |
151 timeout = setTimeout(onChange, options.delay); | |
152 break; | |
153 } | |
154 }).focus(function(){ | |
155 // track whether the field has focus, we shouldn't process any | |
156 // results if the field no longer has focus | |
157 hasFocus++; | |
158 }).blur(function() { | |
159 hasFocus = 0; | |
160 if (!config.mouseDownOnSelect) { | |
161 hideResults(); | |
162 } | |
163 }).click(function() { | |
164 // show select when clicking in a focused field | |
165 if ( hasFocus++ > 1 && !select.visible() ) { | |
166 onChange(0, true); | |
167 } | |
168 }).bind("search", function() { | |
169 // TODO why not just specifying both arguments? | |
170 var fn = (arguments.length > 1) ? arguments[1] : null; | |
171 function findValueCallback(q, data) { | |
172 var result; | |
173 if( data && data.length ) { | |
174 for (var i=0; i < data.length; i++) { | |
175 if( data[i].result.toLowerCase() == q.toLowerCase() ) { | |
176 result = data[i]; | |
177 break; | |
178 } | |
179 } | |
180 } | |
181 if( typeof fn == "function" ) fn(result); | |
182 else $input.trigger("result", result && [result.data, result.value]); | |
183 } | |
184 $.each(trimWords($input.val()), function(i, value) { | |
185 request(value, findValueCallback, findValueCallback); | |
186 }); | |
187 }).bind("flushCache", function() { | |
188 cache.flush(); | |
189 }).bind("setOptions", function() { | |
190 $.extend(options, arguments[1]); | |
191 // if we've updated the data, repopulate | |
192 if ( "data" in arguments[1] ) | |
193 cache.populate(); | |
194 }).bind("unautocomplete", function() { | |
195 select.unbind(); | |
196 $input.unbind(); | |
197 $(input.form).unbind(".autocomplete"); | |
198 }); | |
199 | |
200 | |
201 function selectCurrent() { | |
202 var selected = select.selected(); | |
203 if( !selected ) | |
204 return false; | |
205 | |
206 var v = selected.result; | |
207 previousValue = v; | |
208 | |
209 if ( options.multiple ) { | |
210 var words = trimWords($input.val()); | |
211 if ( words.length > 1 ) { | |
212 v = words.slice(0, words.length - 1).join( options.multipleSeparator ) + options.multipleSeparator + v; | |
213 } | |
214 v += options.multipleSeparator; | |
215 } | |
216 | |
217 $input.val(v); | |
218 hideResultsNow(); | |
219 $input.trigger("result", [selected.data, selected.value]); | |
220 return true; | |
221 } | |
222 | |
223 function onChange(crap, skipPrevCheck) { | |
224 if( lastKeyPressCode == KEY.DEL ) { | |
225 select.hide(); | |
226 return; | |
227 } | |
228 | |
229 var currentValue = $input.val(); | |
230 | |
231 if ( !skipPrevCheck && currentValue == previousValue ) | |
232 return; | |
233 | |
234 previousValue = currentValue; | |
235 | |
236 currentValue = lastWord(currentValue); | |
237 if ( currentValue.length >= options.minChars) { | |
238 $input.addClass(options.loadingClass); | |
239 if (!options.matchCase) | |
240 currentValue = currentValue.toLowerCase(); | |
241 request(currentValue, receiveData, hideResultsNow); | |
242 } else { | |
243 stopLoading(); | |
244 select.hide(); | |
245 } | |
246 }; | |
247 | |
248 function trimWords(value) { | |
249 if ( !value ) { | |
250 return [""]; | |
251 } | |
252 var words = value.split( options.multipleSeparator ); | |
253 var result = []; | |
254 $.each(words, function(i, value) { | |
255 if ( $.trim(value) ) | |
256 result[i] = $.trim(value); | |
257 }); | |
258 return result; | |
259 } | |
260 | |
261 function lastWord(value) { | |
262 if ( !options.multiple ) | |
263 return value; | |
264 var words = trimWords(value); | |
265 return words[words.length - 1]; | |
266 } | |
267 | |
268 // fills in the input box w/the first match (assumed to be the best match) | |
269 // q: the term entered | |
270 // sValue: the first matching result | |
271 function autoFill(q, sValue){ | |
272 // autofill in the complete box w/the first match as long as the user hasn't entered in more data | |
273 // if the last user key pressed was backspace, don't autofill | |
274 if( options.autoFill && (lastWord($input.val()).toLowerCase() == q.toLowerCase()) && lastKeyPressCode != KEY.BACKSPACE ) { | |
275 // fill in the value (keep the case the user has typed) | |
276 $input.val($input.val() + sValue.substring(lastWord(previousValue).length)); | |
277 // select the portion of the value not typed by the user (so the next character will erase) | |
278 $.Autocompleter.Selection(input, previousValue.length, previousValue.length + sValue.length); | |
279 } | |
280 }; | |
281 | |
282 function hideResults() { | |
283 clearTimeout(timeout); | |
284 timeout = setTimeout(hideResultsNow, 200); | |
285 }; | |
286 | |
287 function hideResultsNow() { | |
288 var wasVisible = select.visible(); | |
289 select.hide(); | |
290 clearTimeout(timeout); | |
291 stopLoading(); | |
292 if (options.mustMatch) { | |
293 // call search and run callback | |
294 $input.search( | |
295 function (result){ | |
296 // if no value found, clear the input box | |
297 if( !result ) { | |
298 if (options.multiple) { | |
299 var words = trimWords($input.val()).slice(0, -1); | |
300 $input.val( words.join(options.multipleSeparator) + (words.length ? options.multipleSeparator : "") ); | |
301 } | |
302 else | |
303 $input.val( "" ); | |
304 } | |
305 } | |
306 ); | |
307 } | |
308 if (wasVisible) | |
309 // position cursor at end of input field | |
310 $.Autocompleter.Selection(input, input.value.length, input.value.length); | |
311 }; | |
312 | |
313 function receiveData(q, data) { | |
314 if ( data && data.length && hasFocus ) { | |
315 stopLoading(); | |
316 select.display(data, q); | |
317 autoFill(q, data[0].value); | |
318 select.show(); | |
319 } else { | |
320 hideResultsNow(); | |
321 } | |
322 }; | |
323 | |
324 function request(term, success, failure) { | |
325 if (!options.matchCase) | |
326 term = term.toLowerCase(); | |
327 var data = cache.load(term); | |
328 // recieve the cached data | |
329 if (data && data.length) { | |
330 success(term, data); | |
331 // if an AJAX url has been supplied, try loading the data now | |
332 } else if( (typeof options.url == "string") && (options.url.length > 0) ){ | |
333 | |
334 var extraParams = { | |
335 timestamp: +new Date() | |
336 }; | |
337 $.each(options.extraParams, function(key, param) { | |
338 extraParams[key] = typeof param == "function" ? param() : param; | |
339 }); | |
340 | |
341 $.ajax({ | |
342 // try to leverage ajaxQueue plugin to abort previous requests | |
343 mode: "abort", | |
344 // limit abortion to this input | |
345 port: "autocomplete" + input.name, | |
346 dataType: options.dataType, | |
347 url: options.url, | |
348 data: $.extend({ | |
349 q: lastWord(term), | |
350 limit: options.max | |
351 }, extraParams), | |
352 success: function(data) { | |
353 var parsed = options.parse && options.parse(data) || parse(data); | |
354 cache.add(term, parsed); | |
355 success(term, parsed); | |
356 } | |
357 }); | |
358 } else { | |
359 // if we have a failure, we need to empty the list -- this prevents the the [TAB] key from selecting the last successful match | |
360 select.emptyList(); | |
361 failure(term); | |
362 } | |
363 }; | |
364 | |
365 function parse(data) { | |
366 var parsed = []; | |
367 var rows = data.split("\n"); | |
368 for (var i=0; i < rows.length; i++) { | |
369 var row = $.trim(rows[i]); | |
370 if (row) { | |
371 row = row.split("|"); | |
372 parsed[parsed.length] = { | |
373 data: row, | |
374 value: row[0], | |
375 result: options.formatResult && options.formatResult(row, row[0]) || row[0] | |
376 }; | |
377 } | |
378 } | |
379 return parsed; | |
380 }; | |
381 | |
382 function stopLoading() { | |
383 $input.removeClass(options.loadingClass); | |
384 }; | |
385 | |
386 }; | |
387 | |
388 $.Autocompleter.defaults = { | |
389 inputClass: "ac_input", | |
390 resultsClass: "ac_results", | |
391 loadingClass: "ac_loading", | |
392 minChars: 1, | |
393 delay: 400, | |
394 matchCase: false, | |
395 matchSubset: true, | |
396 matchContains: false, | |
397 cacheLength: 10, | |
398 max: 100, | |
399 mustMatch: false, | |
400 extraParams: {}, | |
401 selectFirst: true, | |
402 formatItem: function(row) { return row[0]; }, | |
403 formatMatch: null, | |
404 autoFill: false, | |
405 width: 0, | |
406 multiple: false, | |
407 multipleSeparator: ", ", | |
408 highlight: function(value, term) { | |
409 return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term.replace(/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi, "\\$1") + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "<strong>$1</strong>"); | |
410 }, | |
411 scroll: true, | |
412 scrollHeight: 180 | |
413 }; | |
414 | |
415 $.Autocompleter.Cache = function(options) { | |
416 | |
417 var data = {}; | |
418 var length = 0; | |
419 | |
420 function matchSubset(s, sub) { | |
421 if (!options.matchCase) | |
422 s = s.toLowerCase(); | |
423 var i = s.indexOf(sub); | |
424 if (i == -1) return false; | |
425 return i == 0 || options.matchContains; | |
426 }; | |
427 | |
428 function add(q, value) { | |
429 if (length > options.cacheLength){ | |
430 flush(); | |
431 } | |
432 if (!data[q]){ | |
433 length++; | |
434 } | |
435 data[q] = value; | |
436 } | |
437 | |
438 function populate(){ | |
439 if( !options.data ) return false; | |
440 // track the matches | |
441 var stMatchSets = {}, | |
442 nullData = 0; | |
443 | |
444 // no url was specified, we need to adjust the cache length to make sure it fits the local data store | |
445 if( !options.url ) options.cacheLength = 1; | |
446 | |
447 // track all options for minChars = 0 | |
448 stMatchSets[""] = []; | |
449 | |
450 // loop through the array and create a lookup structure | |
451 for ( var i = 0, ol = options.data.length; i < ol; i++ ) { | |
452 var rawValue = options.data[i]; | |
453 // if rawValue is a string, make an array otherwise just reference the array | |
454 rawValue = (typeof rawValue == "string") ? [rawValue] : rawValue; | |
455 | |
456 var value = options.formatMatch(rawValue, i+1, options.data.length); | |
457 if ( value === false ) | |
458 continue; | |
459 | |
460 var firstChar = value.charAt(0).toLowerCase(); | |
461 // if no lookup array for this character exists, look it up now | |
462 if( !stMatchSets[firstChar] ) | |
463 stMatchSets[firstChar] = []; | |
464 | |
465 // if the match is a string | |
466 var row = { | |
467 value: value, | |
468 data: rawValue, | |
469 result: options.formatResult && options.formatResult(rawValue) || value | |
470 }; | |
471 | |
472 // push the current match into the set list | |
473 stMatchSets[firstChar].push(row); | |
474 | |
475 // keep track of minChars zero items | |
476 if ( nullData++ < options.max ) { | |
477 stMatchSets[""].push(row); | |
478 } | |
479 }; | |
480 | |
481 // add the data items to the cache | |
482 $.each(stMatchSets, function(i, value) { | |
483 // increase the cache size | |
484 options.cacheLength++; | |
485 // add to the cache | |
486 add(i, value); | |
487 }); | |
488 } | |
489 | |
490 // populate any existing data | |
491 setTimeout(populate, 25); | |
492 | |
493 function flush(){ | |
494 data = {}; | |
495 length = 0; | |
496 } | |
497 | |
498 return { | |
499 flush: flush, | |
500 add: add, | |
501 populate: populate, | |
502 load: function(q) { | |
503 if (!options.cacheLength || !length) | |
504 return null; | |
505 /* | |
506 * if dealing w/local data and matchContains than we must make sure | |
507 * to loop through all the data collections looking for matches | |
508 */ | |
509 if( !options.url && options.matchContains ){ | |
510 // track all matches | |
511 var csub = []; | |
512 // loop through all the data grids for matches | |
513 for( var k in data ){ | |
514 // don't search through the stMatchSets[""] (minChars: 0) cache | |
515 // this prevents duplicates | |
516 if( k.length > 0 ){ | |
517 var c = data[k]; | |
518 $.each(c, function(i, x) { | |
519 // if we've got a match, add it to the array | |
520 if (matchSubset(x.value, q)) { | |
521 csub.push(x); | |
522 } | |
523 }); | |
524 } | |
525 } | |
526 return csub; | |
527 } else | |
528 // if the exact item exists, use it | |
529 if (data[q]){ | |
530 return data[q]; | |
531 } else | |
532 if (options.matchSubset) { | |
533 for (var i = q.length - 1; i >= options.minChars; i--) { | |
534 var c = data[q.substr(0, i)]; | |
535 if (c) { | |
536 var csub = []; | |
537 $.each(c, function(i, x) { | |
538 if (matchSubset(x.value, q)) { | |
539 csub[csub.length] = x; | |
540 } | |
541 }); | |
542 return csub; | |
543 } | |
544 } | |
545 } | |
546 return null; | |
547 } | |
548 }; | |
549 }; | |
550 | |
551 $.Autocompleter.Select = function (options, input, select, config) { | |
552 var CLASSES = { | |
553 ACTIVE: "ac_over" | |
554 }; | |
555 | |
556 var listItems, | |
557 active = -1, | |
558 data, | |
559 term = "", | |
560 needsInit = true, | |
561 element, | |
562 list; | |
563 | |
564 // Create results | |
565 function init() { | |
566 if (!needsInit) | |
567 return; | |
568 element = $("<div/>") | |
569 .hide() | |
570 .addClass(options.resultsClass) | |
571 .css("position", "absolute") | |
572 .appendTo(document.body); | |
573 | |
574 list = $("<ul/>").appendTo(element).mouseover( function(event) { | |
575 if(target(event).nodeName && target(event).nodeName.toUpperCase() == 'LI') { | |
576 active = $("li", list).removeClass(CLASSES.ACTIVE).index(target(event)); | |
577 $(target(event)).addClass(CLASSES.ACTIVE); | |
578 } | |
579 }).click(function(event) { | |
580 $(target(event)).addClass(CLASSES.ACTIVE); | |
581 select(); | |
582 // TODO provide option to avoid setting focus again after selection? useful for cleanup-on-focus | |
583 input.focus(); | |
584 return false; | |
585 }).mousedown(function() { | |
586 config.mouseDownOnSelect = true; | |
587 }).mouseup(function() { | |
588 config.mouseDownOnSelect = false; | |
589 }); | |
590 | |
591 if( options.width > 0 ) | |
592 element.css("width", options.width); | |
593 | |
594 needsInit = false; | |
595 } | |
596 | |
597 function target(event) { | |
598 var element = event.target; | |
599 while(element && element.tagName != "LI") | |
600 element = element.parentNode; | |
601 // more fun with IE, sometimes event.target is empty, just ignore it then | |
602 if(!element) | |
603 return []; | |
604 return element; | |
605 } | |
606 | |
607 function moveSelect(step) { | |
608 listItems.slice(active, active + 1).removeClass(CLASSES.ACTIVE); | |
609 movePosition(step); | |
610 var activeItem = listItems.slice(active, active + 1).addClass(CLASSES.ACTIVE); | |
611 if(options.scroll) { | |
612 var offset = 0; | |
613 listItems.slice(0, active).each(function() { | |
614 offset += this.offsetHeight; | |
615 }); | |
616 if((offset + activeItem[0].offsetHeight - list.scrollTop()) > list[0].clientHeight) { | |
617 list.scrollTop(offset + activeItem[0].offsetHeight - list.innerHeight()); | |
618 } else if(offset < list.scrollTop()) { | |
619 list.scrollTop(offset); | |
620 } | |
621 } | |
622 }; | |
623 | |
624 function movePosition(step) { | |
625 active += step; | |
626 if (active < 0) { | |
627 active = listItems.size() - 1; | |
628 } else if (active >= listItems.size()) { | |
629 active = 0; | |
630 } | |
631 } | |
632 | |
633 function limitNumberOfItems(available) { | |
634 return options.max && options.max < available | |
635 ? options.max | |
636 : available; | |
637 } | |
638 | |
639 function fillList() { | |
640 list.empty(); | |
641 var max = limitNumberOfItems(data.length); | |
642 for (var i=0; i < max; i++) { | |
643 if (!data[i]) | |
644 continue; | |
645 var formatted = options.formatItem(data[i].data, i+1, max, data[i].value, term); | |
646 if ( formatted === false ) | |
647 continue; | |
648 var li = $("<li/>").html( options.highlight(formatted, term) ).addClass(i%2 == 0 ? "ac_even" : "ac_odd").appendTo(list)[0]; | |
649 $.data(li, "ac_data", data[i]); | |
650 } | |
651 listItems = list.find("li"); | |
652 if ( options.selectFirst ) { | |
653 listItems.slice(0, 1).addClass(CLASSES.ACTIVE); | |
654 active = 0; | |
655 } | |
656 // apply bgiframe if available | |
657 if ( $.fn.bgiframe ) | |
658 list.bgiframe(); | |
659 } | |
660 | |
661 return { | |
662 display: function(d, q) { | |
663 init(); | |
664 data = d; | |
665 term = q; | |
666 fillList(); | |
667 }, | |
668 next: function() { | |
669 moveSelect(1); | |
670 }, | |
671 prev: function() { | |
672 moveSelect(-1); | |
673 }, | |
674 pageUp: function() { | |
675 if (active != 0 && active - 8 < 0) { | |
676 moveSelect( -active ); | |
677 } else { | |
678 moveSelect(-8); | |
679 } | |
680 }, | |
681 pageDown: function() { | |
682 if (active != listItems.size() - 1 && active + 8 > listItems.size()) { | |
683 moveSelect( listItems.size() - 1 - active ); | |
684 } else { | |
685 moveSelect(8); | |
686 } | |
687 }, | |
688 hide: function() { | |
689 element && element.hide(); | |
690 listItems && listItems.removeClass(CLASSES.ACTIVE); | |
691 active = -1; | |
692 }, | |
693 visible : function() { | |
694 return element && element.is(":visible"); | |
695 }, | |
696 current: function() { | |
697 return this.visible() && (listItems.filter("." + CLASSES.ACTIVE)[0] || options.selectFirst && listItems[0]); | |
698 }, | |
699 show: function() { | |
700 var offset = $(input).offset(); | |
701 element.css({ | |
702 width: typeof options.width == "string" || options.width > 0 ? options.width : $(input).width(), | |
703 top: offset.top + input.offsetHeight, | |
704 left: offset.left | |
705 }).show(); | |
706 if(options.scroll) { | |
707 list.scrollTop(0); | |
708 list.css({ | |
709 maxHeight: options.scrollHeight, | |
710 overflow: 'auto' | |
711 }); | |
712 | |
713 if($.browser.msie && typeof document.body.style.maxHeight === "undefined") { | |
714 var listHeight = 0; | |
715 listItems.each(function() { | |
716 listHeight += this.offsetHeight; | |
717 }); | |
718 var scrollbarsVisible = listHeight > options.scrollHeight; | |
719 list.css('height', scrollbarsVisible ? options.scrollHeight : listHeight ); | |
720 if (!scrollbarsVisible) { | |
721 // IE doesn't recalculate width when scrollbar disappears | |
722 listItems.width( list.width() - parseInt(listItems.css("padding-left")) - parseInt(listItems.css("padding-right")) ); | |
723 } | |
724 } | |
725 | |
726 } | |
727 }, | |
728 selected: function() { | |
729 var selected = listItems && listItems.filter("." + CLASSES.ACTIVE).removeClass(CLASSES.ACTIVE); | |
730 return selected && selected.length && $.data(selected[0], "ac_data"); | |
731 }, | |
732 emptyList: function (){ | |
733 list && list.empty(); | |
734 }, | |
735 unbind: function() { | |
736 element && element.remove(); | |
737 } | |
738 }; | |
739 }; | |
740 | |
741 $.Autocompleter.Selection = function(field, start, end) { | |
742 if( field.createTextRange ){ | |
743 var selRange = field.createTextRange(); | |
744 selRange.collapse(true); | |
745 selRange.moveStart("character", start); | |
746 selRange.moveEnd("character", end); | |
747 selRange.select(); | |
748 } else if( field.setSelectionRange ){ | |
749 field.setSelectionRange(start, end); | |
750 } else { | |
751 if( field.selectionStart ){ | |
752 field.selectionStart = start; | |
753 field.selectionEnd = end; | |
754 } | |
755 } | |
756 field.focus(); | |
757 }; | |
758 | |
759 })(jQuery); |