Last active
August 29, 2015 14:20
-
-
Save ruslanas/3503b103d3b278126e84 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* jquery.suggest 1.1b - 2007-08-06 | |
* Patched by Mark Jaquith with Alexander Dick's "multiple items" patch to allow for auto-suggesting of more than one tag before submitting | |
* See: http://www.vulgarisoip.com/2007/06/29/jquerysuggest-an-alternative-jquery-based-autocomplete-library/#comment-7228 | |
* | |
* Uses code and techniques from following libraries: | |
* 1. http://www.dyve.net/jquery/?autocomplete | |
* 2. http://dev.jquery.com/browser/trunk/plugins/interface/iautocompleter.js | |
* | |
* All the new stuff written by Peter Vulgaris (www.vulgarisoip.com) | |
* Feel free to do whatever you want with this file | |
* | |
*/ | |
(function($) { | |
$.suggest = function(input, options) { | |
var $input, $results, timeout, prevLength, cache, cacheSize; | |
$input = $(input).attr("autocomplete", "off"); | |
$results = $("<ul/>"); | |
timeout = false; // hold timeout ID for suggestion results to appear | |
prevLength = 0; // last recorded length of $input.val() | |
cache = []; // cache MRU list | |
cacheSize = 0; // size of cache in chars (bytes?) | |
$results.addClass(options.resultsClass).appendTo('body'); | |
resetPosition(); | |
$(window) | |
.load(resetPosition) // just in case user is changing size of page while loading | |
.resize(resetPosition); | |
$input.blur(function() { | |
setTimeout(function() { $results.hide() }, 200); | |
}); | |
$input.keydown(processKey); | |
function resetPosition() { | |
// requires jquery.dimension plugin | |
var offset = $input.offset(); | |
$results.css({ | |
top: (offset.top + input.offsetHeight) + 'px', | |
left: offset.left + 'px' | |
}); | |
} | |
function processKey(e) { | |
// handling up/down/escape requires results to be visible | |
// handling enter/tab requires that AND a result to be selected | |
if ((/27$|38$|40$/.test(e.keyCode) && $results.is(':visible')) || | |
(/^13$|^9$/.test(e.keyCode) && getCurrentResult())) { | |
if (e.preventDefault) | |
e.preventDefault(); | |
if (e.stopPropagation) | |
e.stopPropagation(); | |
e.cancelBubble = true; | |
e.returnValue = false; | |
switch(e.keyCode) { | |
case 38: // up | |
prevResult(); | |
break; | |
case 40: // down | |
nextResult(); | |
break; | |
case 9: // tab | |
case 13: // return | |
selectCurrentResult(); | |
break; | |
case 27: // escape | |
$results.hide(); | |
break; | |
} | |
} else if ($input.val().length != prevLength) { | |
if (timeout) | |
clearTimeout(timeout); | |
timeout = setTimeout(suggest, options.delay); | |
prevLength = $input.val().length; | |
} | |
} | |
function suggest() { | |
var q = $.trim($input.val()), multipleSepPos, items; | |
if ( options.multiple ) { | |
multipleSepPos = q.lastIndexOf(options.multipleSep); | |
if ( multipleSepPos != -1 ) { | |
q = $.trim(q.substr(multipleSepPos + options.multipleSep.length)); | |
} | |
} | |
if (q.length >= options.minchars) { | |
cached = checkCache(q); | |
if (cached) { | |
displayItems(cached['items']); | |
} else { | |
$.get(options.source, {q: q}, function(txt) { | |
$results.hide(); | |
items = parseTxt(options.filter(txt), q); | |
displayItems(items); | |
addToCache(q, items, txt.length); | |
}); | |
} | |
} else { | |
$results.hide(); | |
} | |
} | |
function checkCache(q) { | |
var i; | |
for (i = 0; i < cache.length; i++) | |
if (cache[i]['q'] == q) { | |
cache.unshift(cache.splice(i, 1)[0]); | |
return cache[0]; | |
} | |
return false; | |
} | |
function addToCache(q, items, size) { | |
var cached; | |
while (cache.length && (cacheSize + size > options.maxCacheSize)) { | |
cached = cache.pop(); | |
cacheSize -= cached['size']; | |
} | |
cache.push({ | |
q: q, | |
size: size, | |
items: items | |
}); | |
cacheSize += size; | |
} | |
function displayItems(items) { | |
var html = '', i; | |
if (!items) | |
return; | |
if (!items.length) { | |
$results.hide(); | |
return; | |
} | |
resetPosition(); // when the form moves after the page has loaded | |
for (i = 0; i < items.length; i++) | |
html += '<li>' + items[i] + '</li>'; | |
$results.html(html).show(); | |
$results | |
.children('li') | |
.mouseover(function() { | |
$results.children('li').removeClass(options.selectClass); | |
$(this).addClass(options.selectClass); | |
}) | |
.click(function(e) { | |
e.preventDefault(); | |
e.stopPropagation(); | |
selectCurrentResult(); | |
}); | |
} | |
function parseTxt(txt, q) { | |
var items = [], tokens = txt.split(options.delimiter), i, token; | |
// parse returned data for non-empty items | |
for (i = 0; i < tokens.length; i++) { | |
token = $.trim(tokens[i]); | |
if (token) { | |
token = token.replace( | |
new RegExp(q, 'ig'), | |
function(q) { return '<span class="' + options.matchClass + '">' + q + '</span>' } | |
); | |
items[items.length] = token; | |
} | |
} | |
return items; | |
} | |
function getCurrentResult() { | |
var $currentResult; | |
if (!$results.is(':visible')) | |
return false; | |
$currentResult = $results.children('li.' + options.selectClass); | |
if (!$currentResult.length) | |
$currentResult = false; | |
return $currentResult; | |
} | |
function selectCurrentResult() { | |
$currentResult = getCurrentResult(); | |
if ($currentResult) { | |
if ( options.multiple ) { | |
if ( $input.val().indexOf(options.multipleSep) != -1 ) { | |
$currentVal = $input.val().substr( 0, ( $input.val().lastIndexOf(options.multipleSep) + options.multipleSep.length ) ) + ' '; | |
} else { | |
$currentVal = ""; | |
} | |
$input.val( $currentVal + $currentResult.text() + options.multipleSep + ' ' ); | |
$input.focus(); | |
} else { | |
$input.val($currentResult.text()); | |
} | |
$results.hide(); | |
$input.trigger('change'); | |
if (options.onSelect) | |
options.onSelect.apply($input[0]); | |
} | |
} | |
function nextResult() { | |
$currentResult = getCurrentResult(); | |
if ($currentResult) | |
$currentResult | |
.removeClass(options.selectClass) | |
.next() | |
.addClass(options.selectClass); | |
else | |
$results.children('li:first-child').addClass(options.selectClass); | |
} | |
function prevResult() { | |
var $currentResult = getCurrentResult(); | |
if ($currentResult) | |
$currentResult | |
.removeClass(options.selectClass) | |
.prev() | |
.addClass(options.selectClass); | |
else | |
$results.children('li:last-child').addClass(options.selectClass); | |
} | |
} | |
$.fn.suggest = function(source, options) { | |
if (!source) | |
return; | |
options = options || {}; | |
options.filter = options.filter || function(txt) { | |
return txt; | |
} | |
options.multiple = options.multiple || false; | |
options.multipleSep = options.multipleSep || ","; | |
options.source = source; | |
options.delay = options.delay || 100; | |
options.resultsClass = options.resultsClass || 'ac_results'; | |
options.selectClass = options.selectClass || 'ac_over'; | |
options.matchClass = options.matchClass || 'ac_match'; | |
options.minchars = options.minchars || 2; | |
options.delimiter = options.delimiter || '\n'; | |
options.onSelect = options.onSelect || false; | |
options.maxCacheSize = options.maxCacheSize || 65536; | |
this.each(function() { | |
new $.suggest(this, options); | |
}); | |
return this; | |
}; | |
})(jQuery); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment