|
1 | 1 | /** |
2 | | -* Ajax Autocomplete for jQuery, version 1.2.8 |
| 2 | +* Ajax Autocomplete for jQuery, version 1.2.9 |
3 | 3 | * (c) 2013 Tomas Kirda |
4 | 4 | * |
5 | 5 | * Ajax Autocomplete for jQuery is freely distributable under the terms of an MIT-style license. |
|
75 | 75 | tabDisabled: false, |
76 | 76 | dataType: 'text', |
77 | 77 | currentRequest: null, |
| 78 | + triggerSelectOnValidInput: true, |
78 | 79 | lookupFilter: function (suggestion, originalQuery, queryLowerCase) { |
79 | 80 | return suggestion.value.toLowerCase().indexOf(queryLowerCase) !== -1; |
80 | 81 | }, |
|
92 | 93 | that.selectedIndex = -1; |
93 | 94 | that.currentValue = that.element.value; |
94 | 95 | that.intervalId = 0; |
95 | | - that.cachedResponse = []; |
| 96 | + that.cachedResponse = {}; |
96 | 97 | that.onChangeInterval = null; |
97 | 98 | that.onChange = null; |
98 | 99 | that.isLocal = false; |
|
219 | 220 | }, |
220 | 221 |
|
221 | 222 | clearCache: function () { |
222 | | - this.cachedResponse = []; |
| 223 | + this.cachedResponse = {}; |
223 | 224 | this.badQueries = []; |
224 | 225 | }, |
225 | 226 |
|
|
390 | 391 |
|
391 | 392 | onValueChange: function () { |
392 | 393 | var that = this, |
393 | | - q; |
| 394 | + options = that.options, |
| 395 | + value = that.el.val(), |
| 396 | + query = that.getQuery(value), |
| 397 | + index; |
394 | 398 |
|
395 | 399 | if (that.selection) { |
396 | 400 | that.selection = null; |
397 | | - (that.options.onInvalidateSelection || $.noop)(); |
| 401 | + (options.onInvalidateSelection || $.noop).call(that.element); |
398 | 402 | } |
399 | 403 |
|
400 | 404 | clearInterval(that.onChangeInterval); |
401 | | - that.currentValue = that.el.val(); |
402 | | - |
403 | | - q = that.getQuery(that.currentValue); |
| 405 | + that.currentValue = value; |
404 | 406 | that.selectedIndex = -1; |
405 | 407 |
|
406 | | - if (q.length < that.options.minChars) { |
| 408 | + // Check existing suggestion for the match before proceeding: |
| 409 | + if (options.triggerSelectOnValidInput) { |
| 410 | + index = that.findSuggestionIndex(query); |
| 411 | + if (index !== -1) { |
| 412 | + that.select(index); |
| 413 | + return; |
| 414 | + } |
| 415 | + } |
| 416 | + |
| 417 | + if (query.length < options.minChars) { |
407 | 418 | that.hide(); |
408 | 419 | } else { |
409 | | - that.getSuggestions(q); |
| 420 | + that.getSuggestions(query); |
410 | 421 | } |
411 | 422 | }, |
412 | 423 |
|
| 424 | + findSuggestionIndex: function (query) { |
| 425 | + var that = this, |
| 426 | + index = -1, |
| 427 | + queryLowerCase = query.toLowerCase(); |
| 428 | + |
| 429 | + $.each(that.suggestions, function (i, suggestion) { |
| 430 | + if (suggestion.value.toLowerCase() === queryLowerCase) { |
| 431 | + index = i; |
| 432 | + return false; |
| 433 | + } |
| 434 | + }); |
| 435 | + |
| 436 | + return index; |
| 437 | + }, |
| 438 | + |
413 | 439 | getQuery: function (value) { |
414 | 440 | var delimiter = this.options.delimiter, |
415 | 441 | parts; |
416 | 442 |
|
417 | 443 | if (!delimiter) { |
418 | | - return $.trim(value); |
| 444 | + return value; |
419 | 445 | } |
420 | 446 | parts = value.split(delimiter); |
421 | 447 | return $.trim(parts[parts.length - 1]); |
422 | 448 | }, |
423 | 449 |
|
424 | 450 | getSuggestionsLocal: function (query) { |
425 | 451 | var that = this, |
| 452 | + options = that.options, |
426 | 453 | queryLowerCase = query.toLowerCase(), |
427 | | - filter = that.options.lookupFilter; |
| 454 | + filter = options.lookupFilter, |
| 455 | + limit = parseInt(options.lookupLimit, 10), |
| 456 | + data; |
428 | 457 |
|
429 | | - return { |
430 | | - suggestions: $.grep(that.options.lookup, function (suggestion) { |
| 458 | + data = { |
| 459 | + suggestions: $.grep(options.lookup, function (suggestion) { |
431 | 460 | return filter(suggestion, query, queryLowerCase); |
432 | 461 | }) |
433 | 462 | }; |
| 463 | + |
| 464 | + if (limit && data.suggestions.length > limit) { |
| 465 | + data.suggestions = data.suggestions.slice(0, limit); |
| 466 | + } |
| 467 | + |
| 468 | + return data; |
434 | 469 | }, |
435 | 470 |
|
436 | 471 | getSuggestions: function (q) { |
437 | 472 | var response, |
438 | 473 | that = this, |
439 | 474 | options = that.options, |
440 | | - serviceUrl = options.serviceUrl; |
| 475 | + serviceUrl = options.serviceUrl, |
| 476 | + data, |
| 477 | + cacheKey; |
441 | 478 |
|
442 | | - response = that.isLocal ? that.getSuggestionsLocal(q) : that.cachedResponse[q]; |
| 479 | + options.params[options.paramName] = q; |
| 480 | + data = options.ignoreParams ? null : options.params; |
| 481 | + |
| 482 | + if (that.isLocal) { |
| 483 | + response = that.getSuggestionsLocal(q); |
| 484 | + } else { |
| 485 | + if ($.isFunction(serviceUrl)) { |
| 486 | + serviceUrl = serviceUrl.call(that.element, q); |
| 487 | + } |
| 488 | + cacheKey = serviceUrl + '?' + $.param(data || {}); |
| 489 | + response = that.cachedResponse[cacheKey]; |
| 490 | + } |
443 | 491 |
|
444 | 492 | if (response && $.isArray(response.suggestions)) { |
445 | 493 | that.suggestions = response.suggestions; |
446 | 494 | that.suggest(); |
447 | 495 | } else if (!that.isBadQuery(q)) { |
448 | | - options.params[options.paramName] = q; |
449 | 496 | if (options.onSearchStart.call(that.element, options.params) === false) { |
450 | 497 | return; |
451 | 498 | } |
452 | | - if ($.isFunction(options.serviceUrl)) { |
453 | | - serviceUrl = options.serviceUrl.call(that.element, q); |
454 | | - } |
455 | 499 | if (that.currentRequest) { |
456 | 500 | that.currentRequest.abort(); |
457 | 501 | } |
458 | 502 | that.currentRequest = $.ajax({ |
459 | 503 | url: serviceUrl, |
460 | | - data: options.ignoreParams ? null : options.params, |
| 504 | + data: data, |
461 | 505 | type: options.type, |
462 | 506 | dataType: options.dataType |
463 | 507 | }).done(function (data) { |
464 | 508 | that.currentRequest = null; |
465 | | - that.processResponse(data, q); |
466 | | - options.onSearchComplete.call(that.element, q, data); |
| 509 | + that.processResponse(data, q, cacheKey); |
| 510 | + options.onSearchComplete.call(that.element, q); |
467 | 511 | }).fail(function (jqXHR, textStatus, errorThrown) { |
468 | 512 | options.onSearchError.call(that.element, q, jqXHR, textStatus, errorThrown); |
469 | 513 | }); |
|
498 | 542 | } |
499 | 543 |
|
500 | 544 | var that = this, |
501 | | - formatResult = that.options.formatResult, |
| 545 | + options = that.options, |
| 546 | + formatResult = options.formatResult, |
502 | 547 | value = that.getQuery(that.currentValue), |
503 | 548 | className = that.classes.suggestion, |
504 | 549 | classSelected = that.classes.selected, |
505 | 550 | container = $(that.suggestionsContainer), |
506 | | - beforeRender = that.options.beforeRender, |
| 551 | + beforeRender = options.beforeRender, |
507 | 552 | html = '', |
| 553 | + index, |
508 | 554 | width; |
509 | 555 |
|
| 556 | + if (options.triggerSelectOnValidInput) { |
| 557 | + index = that.findSuggestionIndex(value); |
| 558 | + if (index !== -1) { |
| 559 | + that.select(index); |
| 560 | + return; |
| 561 | + } |
| 562 | + } |
| 563 | + |
510 | 564 | // Build suggestions inner HTML: |
511 | 565 | $.each(that.suggestions, function (i, suggestion) { |
512 | 566 | html += '<div class="' + className + '" data-index="' + i + '">' + formatResult(suggestion, value) + '</div>'; |
|
516 | 570 | // because if instance was created before input had width, it will be zero. |
517 | 571 | // Also it adjusts if input width has changed. |
518 | 572 | // -2px to account for suggestions border. |
519 | | - if (that.options.width === 'auto') { |
| 573 | + if (options.width === 'auto') { |
520 | 574 | width = that.el.outerWidth() - 2; |
521 | 575 | container.width(width > 0 ? width : 300); |
522 | 576 | } |
523 | 577 |
|
524 | 578 | container.html(html); |
525 | 579 |
|
526 | 580 | // Select first value by default: |
527 | | - if (that.options.autoSelectFirst) { |
| 581 | + if (options.autoSelectFirst) { |
528 | 582 | that.selectedIndex = 0; |
529 | 583 | container.children().first().addClass(classSelected); |
530 | 584 | } |
|
583 | 637 | return suggestions; |
584 | 638 | }, |
585 | 639 |
|
586 | | - processResponse: function (response, originalQuery) { |
| 640 | + processResponse: function (response, originalQuery, cacheKey) { |
587 | 641 | var that = this, |
588 | 642 | options = that.options, |
589 | 643 | result = options.transformResult(response, originalQuery); |
|
592 | 646 |
|
593 | 647 | // Cache results if cache is not disabled: |
594 | 648 | if (!options.noCache) { |
595 | | - that.cachedResponse[result[options.paramName]] = result; |
| 649 | + that.cachedResponse[cacheKey] = result; |
596 | 650 | if (result.suggestions.length === 0) { |
597 | | - that.badQueries.push(result[options.paramName]); |
| 651 | + that.badQueries.push(cacheKey); |
598 | 652 | } |
599 | 653 | } |
600 | 654 |
|
601 | | - // Display suggestions only if returned query matches current value: |
602 | | - if (originalQuery === that.getQuery(that.currentValue)) { |
603 | | - that.suggestions = result.suggestions; |
604 | | - that.suggest(); |
| 655 | + // Return if originalQuery is not matching current query: |
| 656 | + if (originalQuery !== that.getQuery(that.currentValue)) { |
| 657 | + return; |
605 | 658 | } |
| 659 | + |
| 660 | + that.suggestions = result.suggestions; |
| 661 | + that.suggest(); |
606 | 662 | }, |
607 | 663 |
|
608 | 664 | activate: function (index) { |
|
0 commit comments