mirror of
https://gitlab.com/JKANetwork/CheckServer.git
synced 2026-02-21 04:23:47 +01:00
Start again
This commit is contained in:
35
vendors/devbridge-autocomplete/.bower.json
vendored
Normal file
35
vendors/devbridge-autocomplete/.bower.json
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"name": "devbridge-autocomplete",
|
||||
"version": "1.2.24",
|
||||
"homepage": "https://github.com/devbridge/jQuery-Autocomplete",
|
||||
"authors": [
|
||||
"Tomas Kirda"
|
||||
],
|
||||
"description": "Autocomplete provides suggestions while you type into the text field.",
|
||||
"main": "dist/jquery.autocomplete.js",
|
||||
"keywords": [
|
||||
"ajax",
|
||||
"autocomplete"
|
||||
],
|
||||
"license": "MIT",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"spec",
|
||||
"scripts",
|
||||
"content"
|
||||
],
|
||||
"dependencies": {
|
||||
"jquery": ">=1.7"
|
||||
},
|
||||
"_release": "1.2.24",
|
||||
"_resolution": {
|
||||
"type": "version",
|
||||
"tag": "v1.2.24",
|
||||
"commit": "dfbca90052e83682233a2bcc67c668c644630b6b"
|
||||
},
|
||||
"_source": "https://github.com/devbridge/jQuery-Autocomplete.git",
|
||||
"_target": "^1.2.24",
|
||||
"_originalSource": "devbridge-autocomplete"
|
||||
}
|
||||
26
vendors/devbridge-autocomplete/bower.json
vendored
Normal file
26
vendors/devbridge-autocomplete/bower.json
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"name": "devbridge-autocomplete",
|
||||
"version": "1.2.24",
|
||||
"homepage": "https://github.com/devbridge/jQuery-Autocomplete",
|
||||
"authors": [
|
||||
"Tomas Kirda"
|
||||
],
|
||||
"description": "Autocomplete provides suggestions while you type into the text field.",
|
||||
"main": "dist/jquery.autocomplete.js",
|
||||
"keywords": [
|
||||
"ajax",
|
||||
"autocomplete"
|
||||
],
|
||||
"license": "MIT",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"spec",
|
||||
"scripts",
|
||||
"content"
|
||||
],
|
||||
"dependencies": {
|
||||
"jquery": ">=1.7"
|
||||
}
|
||||
}
|
||||
28
vendors/devbridge-autocomplete/devbridge-autocomplete.jquery.json
vendored
Normal file
28
vendors/devbridge-autocomplete/devbridge-autocomplete.jquery.json
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "devbridge-autocomplete",
|
||||
"title": "DevBridge Autocomplete",
|
||||
"description": "Autocomplete provides suggestions while you type into the text field. ",
|
||||
"keywords": [
|
||||
"ajax",
|
||||
"autocomplete"
|
||||
],
|
||||
"version": "1.2.24",
|
||||
"author": {
|
||||
"name": "Tomas Kirda",
|
||||
"url": "https://github.com/tkirda"
|
||||
},
|
||||
"licenses": [
|
||||
{
|
||||
"type": "MIT",
|
||||
"url": "https://github.com/devbridge/jQuery-Autocomplete/blob/master/license.txt"
|
||||
}
|
||||
],
|
||||
"bugs": "https://github.com/devbridge/jQuery-Autocomplete/issues?state=open",
|
||||
"homepage": "https://github.com/devbridge/jQuery-Autocomplete",
|
||||
"docs": "https://github.com/devbridge/jQuery-Autocomplete",
|
||||
"demo": "http://www.devbridge.com/sourcery/components/jquery-autocomplete/",
|
||||
"download": "https://github.com/devbridge/jQuery-Autocomplete/tree/master/dist",
|
||||
"dependencies": {
|
||||
"jquery": ">=1.7"
|
||||
}
|
||||
}
|
||||
979
vendors/devbridge-autocomplete/dist/jquery.autocomplete.js
vendored
Normal file
979
vendors/devbridge-autocomplete/dist/jquery.autocomplete.js
vendored
Normal file
@@ -0,0 +1,979 @@
|
||||
/**
|
||||
* Ajax Autocomplete for jQuery, version 1.2.24
|
||||
* (c) 2015 Tomas Kirda
|
||||
*
|
||||
* Ajax Autocomplete for jQuery is freely distributable under the terms of an MIT-style license.
|
||||
* For details, see the web site: https://github.com/devbridge/jQuery-Autocomplete
|
||||
*/
|
||||
|
||||
/*jslint browser: true, white: true, plusplus: true, vars: true */
|
||||
/*global define, window, document, jQuery, exports, require */
|
||||
|
||||
// Expose plugin as an AMD module if AMD loader is present:
|
||||
(function (factory) {
|
||||
'use strict';
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
// AMD. Register as an anonymous module.
|
||||
define(['jquery'], factory);
|
||||
} else if (typeof exports === 'object' && typeof require === 'function') {
|
||||
// Browserify
|
||||
factory(require('jquery'));
|
||||
} else {
|
||||
// Browser globals
|
||||
factory(jQuery);
|
||||
}
|
||||
}(function ($) {
|
||||
'use strict';
|
||||
|
||||
var
|
||||
utils = (function () {
|
||||
return {
|
||||
escapeRegExChars: function (value) {
|
||||
return value.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
|
||||
},
|
||||
createNode: function (containerClass) {
|
||||
var div = document.createElement('div');
|
||||
div.className = containerClass;
|
||||
div.style.position = 'absolute';
|
||||
div.style.display = 'none';
|
||||
return div;
|
||||
}
|
||||
};
|
||||
}()),
|
||||
|
||||
keys = {
|
||||
ESC: 27,
|
||||
TAB: 9,
|
||||
RETURN: 13,
|
||||
LEFT: 37,
|
||||
UP: 38,
|
||||
RIGHT: 39,
|
||||
DOWN: 40
|
||||
};
|
||||
|
||||
function Autocomplete(el, options) {
|
||||
var noop = function () { },
|
||||
that = this,
|
||||
defaults = {
|
||||
ajaxSettings: {},
|
||||
autoSelectFirst: false,
|
||||
appendTo: document.body,
|
||||
serviceUrl: null,
|
||||
lookup: null,
|
||||
onSelect: null,
|
||||
width: 'auto',
|
||||
minChars: 1,
|
||||
maxHeight: 300,
|
||||
deferRequestBy: 0,
|
||||
params: {},
|
||||
formatResult: Autocomplete.formatResult,
|
||||
delimiter: null,
|
||||
zIndex: 9999,
|
||||
type: 'GET',
|
||||
noCache: false,
|
||||
onSearchStart: noop,
|
||||
onSearchComplete: noop,
|
||||
onSearchError: noop,
|
||||
preserveInput: false,
|
||||
containerClass: 'autocomplete-suggestions',
|
||||
tabDisabled: false,
|
||||
dataType: 'text',
|
||||
currentRequest: null,
|
||||
triggerSelectOnValidInput: true,
|
||||
preventBadQueries: true,
|
||||
lookupFilter: function (suggestion, originalQuery, queryLowerCase) {
|
||||
return suggestion.value.toLowerCase().indexOf(queryLowerCase) !== -1;
|
||||
},
|
||||
paramName: 'query',
|
||||
transformResult: function (response) {
|
||||
return typeof response === 'string' ? $.parseJSON(response) : response;
|
||||
},
|
||||
showNoSuggestionNotice: false,
|
||||
noSuggestionNotice: 'No results',
|
||||
orientation: 'bottom',
|
||||
forceFixPosition: false
|
||||
};
|
||||
|
||||
// Shared variables:
|
||||
that.element = el;
|
||||
that.el = $(el);
|
||||
that.suggestions = [];
|
||||
that.badQueries = [];
|
||||
that.selectedIndex = -1;
|
||||
that.currentValue = that.element.value;
|
||||
that.intervalId = 0;
|
||||
that.cachedResponse = {};
|
||||
that.onChangeInterval = null;
|
||||
that.onChange = null;
|
||||
that.isLocal = false;
|
||||
that.suggestionsContainer = null;
|
||||
that.noSuggestionsContainer = null;
|
||||
that.options = $.extend({}, defaults, options);
|
||||
that.classes = {
|
||||
selected: 'autocomplete-selected',
|
||||
suggestion: 'autocomplete-suggestion'
|
||||
};
|
||||
that.hint = null;
|
||||
that.hintValue = '';
|
||||
that.selection = null;
|
||||
|
||||
// Initialize and set options:
|
||||
that.initialize();
|
||||
that.setOptions(options);
|
||||
}
|
||||
|
||||
Autocomplete.utils = utils;
|
||||
|
||||
$.Autocomplete = Autocomplete;
|
||||
|
||||
Autocomplete.formatResult = function (suggestion, currentValue) {
|
||||
var pattern = '(' + utils.escapeRegExChars(currentValue) + ')';
|
||||
|
||||
return suggestion.value
|
||||
.replace(new RegExp(pattern, 'gi'), '<strong>$1<\/strong>')
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/<(\/?strong)>/g, '<$1>');
|
||||
};
|
||||
|
||||
Autocomplete.prototype = {
|
||||
|
||||
killerFn: null,
|
||||
|
||||
initialize: function () {
|
||||
var that = this,
|
||||
suggestionSelector = '.' + that.classes.suggestion,
|
||||
selected = that.classes.selected,
|
||||
options = that.options,
|
||||
container;
|
||||
|
||||
// Remove autocomplete attribute to prevent native suggestions:
|
||||
that.element.setAttribute('autocomplete', 'off');
|
||||
|
||||
that.killerFn = function (e) {
|
||||
if ($(e.target).closest('.' + that.options.containerClass).length === 0) {
|
||||
that.killSuggestions();
|
||||
that.disableKillerFn();
|
||||
}
|
||||
};
|
||||
|
||||
// html() deals with many types: htmlString or Element or Array or jQuery
|
||||
that.noSuggestionsContainer = $('<div class="autocomplete-no-suggestion"></div>')
|
||||
.html(this.options.noSuggestionNotice).get(0);
|
||||
|
||||
that.suggestionsContainer = Autocomplete.utils.createNode(options.containerClass);
|
||||
|
||||
container = $(that.suggestionsContainer);
|
||||
|
||||
container.appendTo(options.appendTo);
|
||||
|
||||
// Only set width if it was provided:
|
||||
if (options.width !== 'auto') {
|
||||
container.width(options.width);
|
||||
}
|
||||
|
||||
// Listen for mouse over event on suggestions list:
|
||||
container.on('mouseover.autocomplete', suggestionSelector, function () {
|
||||
that.activate($(this).data('index'));
|
||||
});
|
||||
|
||||
// Deselect active element when mouse leaves suggestions container:
|
||||
container.on('mouseout.autocomplete', function () {
|
||||
that.selectedIndex = -1;
|
||||
container.children('.' + selected).removeClass(selected);
|
||||
});
|
||||
|
||||
// Listen for click event on suggestions list:
|
||||
container.on('click.autocomplete', suggestionSelector, function () {
|
||||
that.select($(this).data('index'));
|
||||
});
|
||||
|
||||
that.fixPositionCapture = function () {
|
||||
if (that.visible) {
|
||||
that.fixPosition();
|
||||
}
|
||||
};
|
||||
|
||||
$(window).on('resize.autocomplete', that.fixPositionCapture);
|
||||
|
||||
that.el.on('keydown.autocomplete', function (e) { that.onKeyPress(e); });
|
||||
that.el.on('keyup.autocomplete', function (e) { that.onKeyUp(e); });
|
||||
that.el.on('blur.autocomplete', function () { that.onBlur(); });
|
||||
that.el.on('focus.autocomplete', function () { that.onFocus(); });
|
||||
that.el.on('change.autocomplete', function (e) { that.onKeyUp(e); });
|
||||
that.el.on('input.autocomplete', function (e) { that.onKeyUp(e); });
|
||||
},
|
||||
|
||||
onFocus: function () {
|
||||
var that = this;
|
||||
that.fixPosition();
|
||||
if (that.options.minChars === 0 && that.el.val().length === 0) {
|
||||
that.onValueChange();
|
||||
}
|
||||
},
|
||||
|
||||
onBlur: function () {
|
||||
this.enableKillerFn();
|
||||
},
|
||||
|
||||
abortAjax: function () {
|
||||
var that = this;
|
||||
if (that.currentRequest) {
|
||||
that.currentRequest.abort();
|
||||
that.currentRequest = null;
|
||||
}
|
||||
},
|
||||
|
||||
setOptions: function (suppliedOptions) {
|
||||
var that = this,
|
||||
options = that.options;
|
||||
|
||||
$.extend(options, suppliedOptions);
|
||||
|
||||
that.isLocal = $.isArray(options.lookup);
|
||||
|
||||
if (that.isLocal) {
|
||||
options.lookup = that.verifySuggestionsFormat(options.lookup);
|
||||
}
|
||||
|
||||
options.orientation = that.validateOrientation(options.orientation, 'bottom');
|
||||
|
||||
// Adjust height, width and z-index:
|
||||
$(that.suggestionsContainer).css({
|
||||
'max-height': options.maxHeight + 'px',
|
||||
'width': options.width + 'px',
|
||||
'z-index': options.zIndex
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
clearCache: function () {
|
||||
this.cachedResponse = {};
|
||||
this.badQueries = [];
|
||||
},
|
||||
|
||||
clear: function () {
|
||||
this.clearCache();
|
||||
this.currentValue = '';
|
||||
this.suggestions = [];
|
||||
},
|
||||
|
||||
disable: function () {
|
||||
var that = this;
|
||||
that.disabled = true;
|
||||
clearInterval(that.onChangeInterval);
|
||||
that.abortAjax();
|
||||
},
|
||||
|
||||
enable: function () {
|
||||
this.disabled = false;
|
||||
},
|
||||
|
||||
fixPosition: function () {
|
||||
// Use only when container has already its content
|
||||
|
||||
var that = this,
|
||||
$container = $(that.suggestionsContainer),
|
||||
containerParent = $container.parent().get(0);
|
||||
// Fix position automatically when appended to body.
|
||||
// In other cases force parameter must be given.
|
||||
if (containerParent !== document.body && !that.options.forceFixPosition) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Choose orientation
|
||||
var orientation = that.options.orientation,
|
||||
containerHeight = $container.outerHeight(),
|
||||
height = that.el.outerHeight(),
|
||||
offset = that.el.offset(),
|
||||
styles = { 'top': offset.top, 'left': offset.left };
|
||||
|
||||
if (orientation === 'auto') {
|
||||
var viewPortHeight = $(window).height(),
|
||||
scrollTop = $(window).scrollTop(),
|
||||
topOverflow = -scrollTop + offset.top - containerHeight,
|
||||
bottomOverflow = scrollTop + viewPortHeight - (offset.top + height + containerHeight);
|
||||
|
||||
orientation = (Math.max(topOverflow, bottomOverflow) === topOverflow) ? 'top' : 'bottom';
|
||||
}
|
||||
|
||||
if (orientation === 'top') {
|
||||
styles.top += -containerHeight;
|
||||
} else {
|
||||
styles.top += height;
|
||||
}
|
||||
|
||||
// If container is not positioned to body,
|
||||
// correct its position using offset parent offset
|
||||
if(containerParent !== document.body) {
|
||||
var opacity = $container.css('opacity'),
|
||||
parentOffsetDiff;
|
||||
|
||||
if (!that.visible){
|
||||
$container.css('opacity', 0).show();
|
||||
}
|
||||
|
||||
parentOffsetDiff = $container.offsetParent().offset();
|
||||
styles.top -= parentOffsetDiff.top;
|
||||
styles.left -= parentOffsetDiff.left;
|
||||
|
||||
if (!that.visible){
|
||||
$container.css('opacity', opacity).hide();
|
||||
}
|
||||
}
|
||||
|
||||
// -2px to account for suggestions border.
|
||||
if (that.options.width === 'auto') {
|
||||
styles.width = (that.el.outerWidth() - 2) + 'px';
|
||||
}
|
||||
|
||||
$container.css(styles);
|
||||
},
|
||||
|
||||
enableKillerFn: function () {
|
||||
var that = this;
|
||||
$(document).on('click.autocomplete', that.killerFn);
|
||||
},
|
||||
|
||||
disableKillerFn: function () {
|
||||
var that = this;
|
||||
$(document).off('click.autocomplete', that.killerFn);
|
||||
},
|
||||
|
||||
killSuggestions: function () {
|
||||
var that = this;
|
||||
that.stopKillSuggestions();
|
||||
that.intervalId = window.setInterval(function () {
|
||||
if (that.visible) {
|
||||
that.el.val(that.currentValue);
|
||||
that.hide();
|
||||
}
|
||||
|
||||
that.stopKillSuggestions();
|
||||
}, 50);
|
||||
},
|
||||
|
||||
stopKillSuggestions: function () {
|
||||
window.clearInterval(this.intervalId);
|
||||
},
|
||||
|
||||
isCursorAtEnd: function () {
|
||||
var that = this,
|
||||
valLength = that.el.val().length,
|
||||
selectionStart = that.element.selectionStart,
|
||||
range;
|
||||
|
||||
if (typeof selectionStart === 'number') {
|
||||
return selectionStart === valLength;
|
||||
}
|
||||
if (document.selection) {
|
||||
range = document.selection.createRange();
|
||||
range.moveStart('character', -valLength);
|
||||
return valLength === range.text.length;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
onKeyPress: function (e) {
|
||||
var that = this;
|
||||
|
||||
// If suggestions are hidden and user presses arrow down, display suggestions:
|
||||
if (!that.disabled && !that.visible && e.which === keys.DOWN && that.currentValue) {
|
||||
that.suggest();
|
||||
return;
|
||||
}
|
||||
|
||||
if (that.disabled || !that.visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (e.which) {
|
||||
case keys.ESC:
|
||||
that.el.val(that.currentValue);
|
||||
that.hide();
|
||||
break;
|
||||
case keys.RIGHT:
|
||||
if (that.hint && that.options.onHint && that.isCursorAtEnd()) {
|
||||
that.selectHint();
|
||||
break;
|
||||
}
|
||||
return;
|
||||
case keys.TAB:
|
||||
if (that.hint && that.options.onHint) {
|
||||
that.selectHint();
|
||||
return;
|
||||
}
|
||||
if (that.selectedIndex === -1) {
|
||||
that.hide();
|
||||
return;
|
||||
}
|
||||
that.select(that.selectedIndex);
|
||||
if (that.options.tabDisabled === false) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case keys.RETURN:
|
||||
if (that.selectedIndex === -1) {
|
||||
that.hide();
|
||||
return;
|
||||
}
|
||||
that.select(that.selectedIndex);
|
||||
break;
|
||||
case keys.UP:
|
||||
that.moveUp();
|
||||
break;
|
||||
case keys.DOWN:
|
||||
that.moveDown();
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
// Cancel event if function did not return:
|
||||
e.stopImmediatePropagation();
|
||||
e.preventDefault();
|
||||
},
|
||||
|
||||
onKeyUp: function (e) {
|
||||
var that = this;
|
||||
|
||||
if (that.disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (e.which) {
|
||||
case keys.UP:
|
||||
case keys.DOWN:
|
||||
return;
|
||||
}
|
||||
|
||||
clearInterval(that.onChangeInterval);
|
||||
|
||||
if (that.currentValue !== that.el.val()) {
|
||||
that.findBestHint();
|
||||
if (that.options.deferRequestBy > 0) {
|
||||
// Defer lookup in case when value changes very quickly:
|
||||
that.onChangeInterval = setInterval(function () {
|
||||
that.onValueChange();
|
||||
}, that.options.deferRequestBy);
|
||||
} else {
|
||||
that.onValueChange();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
onValueChange: function () {
|
||||
var that = this,
|
||||
options = that.options,
|
||||
value = that.el.val(),
|
||||
query = that.getQuery(value);
|
||||
|
||||
if (that.selection && that.currentValue !== query) {
|
||||
that.selection = null;
|
||||
(options.onInvalidateSelection || $.noop).call(that.element);
|
||||
}
|
||||
|
||||
clearInterval(that.onChangeInterval);
|
||||
that.currentValue = value;
|
||||
that.selectedIndex = -1;
|
||||
|
||||
// Check existing suggestion for the match before proceeding:
|
||||
if (options.triggerSelectOnValidInput && that.isExactMatch(query)) {
|
||||
that.select(0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (query.length < options.minChars) {
|
||||
that.hide();
|
||||
} else {
|
||||
that.getSuggestions(query);
|
||||
}
|
||||
},
|
||||
|
||||
isExactMatch: function (query) {
|
||||
var suggestions = this.suggestions;
|
||||
|
||||
return (suggestions.length === 1 && suggestions[0].value.toLowerCase() === query.toLowerCase());
|
||||
},
|
||||
|
||||
getQuery: function (value) {
|
||||
var delimiter = this.options.delimiter,
|
||||
parts;
|
||||
|
||||
if (!delimiter) {
|
||||
return value;
|
||||
}
|
||||
parts = value.split(delimiter);
|
||||
return $.trim(parts[parts.length - 1]);
|
||||
},
|
||||
|
||||
getSuggestionsLocal: function (query) {
|
||||
var that = this,
|
||||
options = that.options,
|
||||
queryLowerCase = query.toLowerCase(),
|
||||
filter = options.lookupFilter,
|
||||
limit = parseInt(options.lookupLimit, 10),
|
||||
data;
|
||||
|
||||
data = {
|
||||
suggestions: $.grep(options.lookup, function (suggestion) {
|
||||
return filter(suggestion, query, queryLowerCase);
|
||||
})
|
||||
};
|
||||
|
||||
if (limit && data.suggestions.length > limit) {
|
||||
data.suggestions = data.suggestions.slice(0, limit);
|
||||
}
|
||||
|
||||
return data;
|
||||
},
|
||||
|
||||
getSuggestions: function (q) {
|
||||
var response,
|
||||
that = this,
|
||||
options = that.options,
|
||||
serviceUrl = options.serviceUrl,
|
||||
params,
|
||||
cacheKey,
|
||||
ajaxSettings;
|
||||
|
||||
options.params[options.paramName] = q;
|
||||
params = options.ignoreParams ? null : options.params;
|
||||
|
||||
if (options.onSearchStart.call(that.element, options.params) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($.isFunction(options.lookup)){
|
||||
options.lookup(q, function (data) {
|
||||
that.suggestions = data.suggestions;
|
||||
that.suggest();
|
||||
options.onSearchComplete.call(that.element, q, data.suggestions);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (that.isLocal) {
|
||||
response = that.getSuggestionsLocal(q);
|
||||
} else {
|
||||
if ($.isFunction(serviceUrl)) {
|
||||
serviceUrl = serviceUrl.call(that.element, q);
|
||||
}
|
||||
cacheKey = serviceUrl + '?' + $.param(params || {});
|
||||
response = that.cachedResponse[cacheKey];
|
||||
}
|
||||
|
||||
if (response && $.isArray(response.suggestions)) {
|
||||
that.suggestions = response.suggestions;
|
||||
that.suggest();
|
||||
options.onSearchComplete.call(that.element, q, response.suggestions);
|
||||
} else if (!that.isBadQuery(q)) {
|
||||
that.abortAjax();
|
||||
|
||||
ajaxSettings = {
|
||||
url: serviceUrl,
|
||||
data: params,
|
||||
type: options.type,
|
||||
dataType: options.dataType
|
||||
};
|
||||
|
||||
$.extend(ajaxSettings, options.ajaxSettings);
|
||||
|
||||
that.currentRequest = $.ajax(ajaxSettings).done(function (data) {
|
||||
var result;
|
||||
that.currentRequest = null;
|
||||
result = options.transformResult(data, q);
|
||||
that.processResponse(result, q, cacheKey);
|
||||
options.onSearchComplete.call(that.element, q, result.suggestions);
|
||||
}).fail(function (jqXHR, textStatus, errorThrown) {
|
||||
options.onSearchError.call(that.element, q, jqXHR, textStatus, errorThrown);
|
||||
});
|
||||
} else {
|
||||
options.onSearchComplete.call(that.element, q, []);
|
||||
}
|
||||
},
|
||||
|
||||
isBadQuery: function (q) {
|
||||
if (!this.options.preventBadQueries){
|
||||
return false;
|
||||
}
|
||||
|
||||
var badQueries = this.badQueries,
|
||||
i = badQueries.length;
|
||||
|
||||
while (i--) {
|
||||
if (q.indexOf(badQueries[i]) === 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
hide: function () {
|
||||
var that = this,
|
||||
container = $(that.suggestionsContainer);
|
||||
|
||||
if ($.isFunction(that.options.onHide) && that.visible) {
|
||||
that.options.onHide.call(that.element, container);
|
||||
}
|
||||
|
||||
that.visible = false;
|
||||
that.selectedIndex = -1;
|
||||
clearInterval(that.onChangeInterval);
|
||||
$(that.suggestionsContainer).hide();
|
||||
that.signalHint(null);
|
||||
},
|
||||
|
||||
suggest: function () {
|
||||
if (this.suggestions.length === 0) {
|
||||
if (this.options.showNoSuggestionNotice) {
|
||||
this.noSuggestions();
|
||||
} else {
|
||||
this.hide();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var that = this,
|
||||
options = that.options,
|
||||
groupBy = options.groupBy,
|
||||
formatResult = options.formatResult,
|
||||
value = that.getQuery(that.currentValue),
|
||||
className = that.classes.suggestion,
|
||||
classSelected = that.classes.selected,
|
||||
container = $(that.suggestionsContainer),
|
||||
noSuggestionsContainer = $(that.noSuggestionsContainer),
|
||||
beforeRender = options.beforeRender,
|
||||
html = '',
|
||||
category,
|
||||
formatGroup = function (suggestion, index) {
|
||||
var currentCategory = suggestion.data[groupBy];
|
||||
|
||||
if (category === currentCategory){
|
||||
return '';
|
||||
}
|
||||
|
||||
category = currentCategory;
|
||||
|
||||
return '<div class="autocomplete-group"><strong>' + category + '</strong></div>';
|
||||
};
|
||||
|
||||
if (options.triggerSelectOnValidInput && that.isExactMatch(value)) {
|
||||
that.select(0);
|
||||
return;
|
||||
}
|
||||
|
||||
// Build suggestions inner HTML:
|
||||
$.each(that.suggestions, function (i, suggestion) {
|
||||
if (groupBy){
|
||||
html += formatGroup(suggestion, value, i);
|
||||
}
|
||||
|
||||
html += '<div class="' + className + '" data-index="' + i + '">' + formatResult(suggestion, value) + '</div>';
|
||||
});
|
||||
|
||||
this.adjustContainerWidth();
|
||||
|
||||
noSuggestionsContainer.detach();
|
||||
container.html(html);
|
||||
|
||||
if ($.isFunction(beforeRender)) {
|
||||
beforeRender.call(that.element, container);
|
||||
}
|
||||
|
||||
that.fixPosition();
|
||||
container.show();
|
||||
|
||||
// Select first value by default:
|
||||
if (options.autoSelectFirst) {
|
||||
that.selectedIndex = 0;
|
||||
container.scrollTop(0);
|
||||
container.children('.' + className).first().addClass(classSelected);
|
||||
}
|
||||
|
||||
that.visible = true;
|
||||
that.findBestHint();
|
||||
},
|
||||
|
||||
noSuggestions: function() {
|
||||
var that = this,
|
||||
container = $(that.suggestionsContainer),
|
||||
noSuggestionsContainer = $(that.noSuggestionsContainer);
|
||||
|
||||
this.adjustContainerWidth();
|
||||
|
||||
// Some explicit steps. Be careful here as it easy to get
|
||||
// noSuggestionsContainer removed from DOM if not detached properly.
|
||||
noSuggestionsContainer.detach();
|
||||
container.empty(); // clean suggestions if any
|
||||
container.append(noSuggestionsContainer);
|
||||
|
||||
that.fixPosition();
|
||||
|
||||
container.show();
|
||||
that.visible = true;
|
||||
},
|
||||
|
||||
adjustContainerWidth: function() {
|
||||
var that = this,
|
||||
options = that.options,
|
||||
width,
|
||||
container = $(that.suggestionsContainer);
|
||||
|
||||
// If width is auto, adjust width before displaying suggestions,
|
||||
// because if instance was created before input had width, it will be zero.
|
||||
// Also it adjusts if input width has changed.
|
||||
// -2px to account for suggestions border.
|
||||
if (options.width === 'auto') {
|
||||
width = that.el.outerWidth() - 2;
|
||||
container.width(width > 0 ? width : 300);
|
||||
}
|
||||
},
|
||||
|
||||
findBestHint: function () {
|
||||
var that = this,
|
||||
value = that.el.val().toLowerCase(),
|
||||
bestMatch = null;
|
||||
|
||||
if (!value) {
|
||||
return;
|
||||
}
|
||||
|
||||
$.each(that.suggestions, function (i, suggestion) {
|
||||
var foundMatch = suggestion.value.toLowerCase().indexOf(value) === 0;
|
||||
if (foundMatch) {
|
||||
bestMatch = suggestion;
|
||||
}
|
||||
return !foundMatch;
|
||||
});
|
||||
|
||||
that.signalHint(bestMatch);
|
||||
},
|
||||
|
||||
signalHint: function (suggestion) {
|
||||
var hintValue = '',
|
||||
that = this;
|
||||
if (suggestion) {
|
||||
hintValue = that.currentValue + suggestion.value.substr(that.currentValue.length);
|
||||
}
|
||||
if (that.hintValue !== hintValue) {
|
||||
that.hintValue = hintValue;
|
||||
that.hint = suggestion;
|
||||
(this.options.onHint || $.noop)(hintValue);
|
||||
}
|
||||
},
|
||||
|
||||
verifySuggestionsFormat: function (suggestions) {
|
||||
// If suggestions is string array, convert them to supported format:
|
||||
if (suggestions.length && typeof suggestions[0] === 'string') {
|
||||
return $.map(suggestions, function (value) {
|
||||
return { value: value, data: null };
|
||||
});
|
||||
}
|
||||
|
||||
return suggestions;
|
||||
},
|
||||
|
||||
validateOrientation: function(orientation, fallback) {
|
||||
orientation = $.trim(orientation || '').toLowerCase();
|
||||
|
||||
if($.inArray(orientation, ['auto', 'bottom', 'top']) === -1){
|
||||
orientation = fallback;
|
||||
}
|
||||
|
||||
return orientation;
|
||||
},
|
||||
|
||||
processResponse: function (result, originalQuery, cacheKey) {
|
||||
var that = this,
|
||||
options = that.options;
|
||||
|
||||
result.suggestions = that.verifySuggestionsFormat(result.suggestions);
|
||||
|
||||
// Cache results if cache is not disabled:
|
||||
if (!options.noCache) {
|
||||
that.cachedResponse[cacheKey] = result;
|
||||
if (options.preventBadQueries && result.suggestions.length === 0) {
|
||||
that.badQueries.push(originalQuery);
|
||||
}
|
||||
}
|
||||
|
||||
// Return if originalQuery is not matching current query:
|
||||
if (originalQuery !== that.getQuery(that.currentValue)) {
|
||||
return;
|
||||
}
|
||||
|
||||
that.suggestions = result.suggestions;
|
||||
that.suggest();
|
||||
},
|
||||
|
||||
activate: function (index) {
|
||||
var that = this,
|
||||
activeItem,
|
||||
selected = that.classes.selected,
|
||||
container = $(that.suggestionsContainer),
|
||||
children = container.find('.' + that.classes.suggestion);
|
||||
|
||||
container.find('.' + selected).removeClass(selected);
|
||||
|
||||
that.selectedIndex = index;
|
||||
|
||||
if (that.selectedIndex !== -1 && children.length > that.selectedIndex) {
|
||||
activeItem = children.get(that.selectedIndex);
|
||||
$(activeItem).addClass(selected);
|
||||
return activeItem;
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
selectHint: function () {
|
||||
var that = this,
|
||||
i = $.inArray(that.hint, that.suggestions);
|
||||
|
||||
that.select(i);
|
||||
},
|
||||
|
||||
select: function (i) {
|
||||
var that = this;
|
||||
that.hide();
|
||||
that.onSelect(i);
|
||||
},
|
||||
|
||||
moveUp: function () {
|
||||
var that = this;
|
||||
|
||||
if (that.selectedIndex === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (that.selectedIndex === 0) {
|
||||
$(that.suggestionsContainer).children().first().removeClass(that.classes.selected);
|
||||
that.selectedIndex = -1;
|
||||
that.el.val(that.currentValue);
|
||||
that.findBestHint();
|
||||
return;
|
||||
}
|
||||
|
||||
that.adjustScroll(that.selectedIndex - 1);
|
||||
},
|
||||
|
||||
moveDown: function () {
|
||||
var that = this;
|
||||
|
||||
if (that.selectedIndex === (that.suggestions.length - 1)) {
|
||||
return;
|
||||
}
|
||||
|
||||
that.adjustScroll(that.selectedIndex + 1);
|
||||
},
|
||||
|
||||
adjustScroll: function (index) {
|
||||
var that = this,
|
||||
activeItem = that.activate(index);
|
||||
|
||||
if (!activeItem) {
|
||||
return;
|
||||
}
|
||||
|
||||
var offsetTop,
|
||||
upperBound,
|
||||
lowerBound,
|
||||
heightDelta = $(activeItem).outerHeight();
|
||||
|
||||
offsetTop = activeItem.offsetTop;
|
||||
upperBound = $(that.suggestionsContainer).scrollTop();
|
||||
lowerBound = upperBound + that.options.maxHeight - heightDelta;
|
||||
|
||||
if (offsetTop < upperBound) {
|
||||
$(that.suggestionsContainer).scrollTop(offsetTop);
|
||||
} else if (offsetTop > lowerBound) {
|
||||
$(that.suggestionsContainer).scrollTop(offsetTop - that.options.maxHeight + heightDelta);
|
||||
}
|
||||
|
||||
if (!that.options.preserveInput) {
|
||||
that.el.val(that.getValue(that.suggestions[index].value));
|
||||
}
|
||||
that.signalHint(null);
|
||||
},
|
||||
|
||||
onSelect: function (index) {
|
||||
var that = this,
|
||||
onSelectCallback = that.options.onSelect,
|
||||
suggestion = that.suggestions[index];
|
||||
|
||||
that.currentValue = that.getValue(suggestion.value);
|
||||
|
||||
if (that.currentValue !== that.el.val() && !that.options.preserveInput) {
|
||||
that.el.val(that.currentValue);
|
||||
}
|
||||
|
||||
that.signalHint(null);
|
||||
that.suggestions = [];
|
||||
that.selection = suggestion;
|
||||
|
||||
if ($.isFunction(onSelectCallback)) {
|
||||
onSelectCallback.call(that.element, suggestion);
|
||||
}
|
||||
},
|
||||
|
||||
getValue: function (value) {
|
||||
var that = this,
|
||||
delimiter = that.options.delimiter,
|
||||
currentValue,
|
||||
parts;
|
||||
|
||||
if (!delimiter) {
|
||||
return value;
|
||||
}
|
||||
|
||||
currentValue = that.currentValue;
|
||||
parts = currentValue.split(delimiter);
|
||||
|
||||
if (parts.length === 1) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return currentValue.substr(0, currentValue.length - parts[parts.length - 1].length) + value;
|
||||
},
|
||||
|
||||
dispose: function () {
|
||||
var that = this;
|
||||
that.el.off('.autocomplete').removeData('autocomplete');
|
||||
that.disableKillerFn();
|
||||
$(window).off('resize.autocomplete', that.fixPositionCapture);
|
||||
$(that.suggestionsContainer).remove();
|
||||
}
|
||||
};
|
||||
|
||||
// Create chainable jQuery plugin:
|
||||
$.fn.autocomplete = $.fn.devbridgeAutocomplete = function (options, args) {
|
||||
var dataKey = 'autocomplete';
|
||||
// If function invoked without argument return
|
||||
// instance of the first matched element:
|
||||
if (arguments.length === 0) {
|
||||
return this.first().data(dataKey);
|
||||
}
|
||||
|
||||
return this.each(function () {
|
||||
var inputElement = $(this),
|
||||
instance = inputElement.data(dataKey);
|
||||
|
||||
if (typeof options === 'string') {
|
||||
if (instance && typeof instance[options] === 'function') {
|
||||
instance[options](args);
|
||||
}
|
||||
} else {
|
||||
// If instance already exists, destroy it:
|
||||
if (instance && instance.dispose) {
|
||||
instance.dispose();
|
||||
}
|
||||
instance = new Autocomplete(this, options);
|
||||
inputElement.data(dataKey, instance);
|
||||
}
|
||||
});
|
||||
};
|
||||
}));
|
||||
8
vendors/devbridge-autocomplete/dist/jquery.autocomplete.min.js
vendored
Normal file
8
vendors/devbridge-autocomplete/dist/jquery.autocomplete.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
21
vendors/devbridge-autocomplete/dist/license.txt
vendored
Normal file
21
vendors/devbridge-autocomplete/dist/license.txt
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
Copyright 2012 DevBridge and other contributors
|
||||
http://www.devbridge.com/projects/autocomplete/jquery/
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
70
vendors/devbridge-autocomplete/gruntfile.js
vendored
Normal file
70
vendors/devbridge-autocomplete/gruntfile.js
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
module.exports = function(grunt) {
|
||||
|
||||
var pkg = grunt.file.readJSON('package.json');
|
||||
|
||||
var banner = [
|
||||
'/**',
|
||||
'* Ajax Autocomplete for jQuery, version ' + pkg.version,
|
||||
'* (c) 2014 Tomas Kirda',
|
||||
'*',
|
||||
'* Ajax Autocomplete for jQuery is freely distributable under the terms of an MIT-style license.',
|
||||
'* For details, see the web site: https://github.com/devbridge/jQuery-Autocomplete',
|
||||
'*/'].join('\n') + '\n';
|
||||
|
||||
// Project configuration.
|
||||
grunt.initConfig({
|
||||
pkg: pkg,
|
||||
uglify: {
|
||||
options: {
|
||||
banner: banner
|
||||
},
|
||||
build: {
|
||||
src: 'src/jquery.autocomplete.js',
|
||||
dest: 'dist/jquery.autocomplete.min.js'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Load the plugin that provides the "uglify" task.
|
||||
grunt.loadNpmTasks('grunt-contrib-uglify');
|
||||
|
||||
// Default task(s).
|
||||
grunt.registerTask('default', ['uglify']);
|
||||
|
||||
grunt.task.registerTask('build', 'Create release', function() {
|
||||
var version = pkg.version,
|
||||
src = grunt.file.read('src/jquery.autocomplete.js').replace('%version%', version),
|
||||
filePath = 'dist/jquery.autocomplete.js';
|
||||
|
||||
// Update not minimized release version:
|
||||
console.log('Updating: ' + filePath);
|
||||
grunt.file.write(filePath, src);
|
||||
|
||||
// Minify latest version:
|
||||
grunt.task.run('uglify');
|
||||
|
||||
// Update plugin version:
|
||||
filePath = 'devbridge-autocomplete.jquery.json';
|
||||
src = grunt.file.readJSON(filePath);
|
||||
|
||||
if (src.version !== version){
|
||||
src.version = version;
|
||||
console.log('Updating: ' + filePath);
|
||||
grunt.file.write(filePath, JSON.stringify(src, null, 4));
|
||||
} else {
|
||||
console.log('No updates for: ' + filePath);
|
||||
}
|
||||
|
||||
// Update bower version:
|
||||
filePath = 'bower.json';
|
||||
src = grunt.file.readJSON(filePath);
|
||||
|
||||
if (src.version !== version){
|
||||
src.version = version;
|
||||
console.log('Updating: ' + filePath);
|
||||
grunt.file.write(filePath, JSON.stringify(src, null, 4));
|
||||
} else {
|
||||
console.log('No updates for: ' + filePath);
|
||||
}
|
||||
});
|
||||
};
|
||||
48
vendors/devbridge-autocomplete/index.htm
vendored
Normal file
48
vendors/devbridge-autocomplete/index.htm
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>DevBridge Autocomplete Demo</title>
|
||||
<link href="content/styles.css" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>Ajax Autocomplete Demo</h1>
|
||||
|
||||
<h2>Ajax Lookup</h2>
|
||||
<p>Type country name in english:</p>
|
||||
<div style="position: relative; height: 80px;">
|
||||
<input type="text" name="country" id="autocomplete-ajax" style="position: absolute; z-index: 2; background: transparent;"/>
|
||||
<input type="text" name="country" id="autocomplete-ajax-x" disabled="disabled" style="color: #CCC; position: absolute; background: transparent; z-index: 1;"/>
|
||||
</div>
|
||||
<div id="selction-ajax"></div>
|
||||
|
||||
<h2>Local Lookup and Grouping</h2>
|
||||
<p>Type NHL or NBA team name:</p>
|
||||
<div>
|
||||
<input type="text" name="country" id="autocomplete"/>
|
||||
</div>
|
||||
<div id="selection"></div>
|
||||
|
||||
<h2>Custom Lookup Container</h2>
|
||||
<p>Type country name in english:</p>
|
||||
<div>
|
||||
<input type="text" name="country" id="autocomplete-custom-append" style="float: left;"/>
|
||||
<div id="suggestions-container" style="position: relative; float: left; width: 400px; margin: 10px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="width: 50%; margin: 0 auto; clear: both;">
|
||||
<h2>Dynamic Width</h2>
|
||||
<p>Type country name in english:</p>
|
||||
<div>
|
||||
<input type="text" name="country" id="autocomplete-dynamic" style="width: 100%; border-width: 5px;"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" src="scripts/jquery-1.8.2.min.js"></script>
|
||||
<script type="text/javascript" src="scripts/jquery.mockjax.js"></script>
|
||||
<script type="text/javascript" src="src/jquery.autocomplete.js"></script>
|
||||
<script type="text/javascript" src="scripts/countries.js"></script>
|
||||
<script type="text/javascript" src="scripts/demo.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
21
vendors/devbridge-autocomplete/license.txt
vendored
Normal file
21
vendors/devbridge-autocomplete/license.txt
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
Copyright 2012 DevBridge and other contributors
|
||||
http://www.devbridge.com/projects/autocomplete/jquery/
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
24
vendors/devbridge-autocomplete/package.json
vendored
Normal file
24
vendors/devbridge-autocomplete/package.json
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"name": "devbridge-autocomplete",
|
||||
"version": "1.2.24",
|
||||
"description": "Autocomplete provides suggestions while you type into the text field.",
|
||||
"homepage": "https://github.com/devbridge/jQuery-Autocomplete",
|
||||
"author": "Tomas Kirda (https://twitter.com/tkirda)",
|
||||
"main": "dist/jquery.autocomplete.js",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/devbridge/jQuery-Autocomplete.git"
|
||||
},
|
||||
"dependencies": {
|
||||
"jquery": ">=1.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"grunt": "^0.4.5",
|
||||
"grunt-contrib-uglify": "^0.5.1"
|
||||
},
|
||||
"files": [
|
||||
"dist/",
|
||||
"readme.md"
|
||||
]
|
||||
}
|
||||
251
vendors/devbridge-autocomplete/readme.md
vendored
Normal file
251
vendors/devbridge-autocomplete/readme.md
vendored
Normal file
@@ -0,0 +1,251 @@
|
||||
#Ajax Autocomplete for jQuery
|
||||
|
||||
Ajax Autocomplete for jQuery allows you to easily create
|
||||
autocomplete/autosuggest boxes for text input fields.
|
||||
|
||||
Has no dependencies other than jQuery.
|
||||
|
||||
The standard jquery.autocomplete.js file is around 13KB when minified.
|
||||
|
||||
##API
|
||||
|
||||
* `$(selector).autocomplete(options);`
|
||||
* Sets up autocomplete for input field(s).
|
||||
* `options`: An object literal which defines the settings to use for the autocomplete plugin. Available option settings listed below.
|
||||
|
||||
###Ajax Settings
|
||||
* `serviceUrl`: Server side URL or callback function that returns serviceUrl string. Optional if local lookup data is provided.
|
||||
* `type`: Ajax request type to get suggestions. Default: `GET`.
|
||||
* `dataType`: type of data returned from server. Either `text` (default), `json` or `jsonp`, which will cause the autocomplete to use jsonp. You may return a json object in your callback when using jsonp.
|
||||
* `paramName`: Default `query`. The name of the request parameter that contains the query.
|
||||
* `params`: Additional parameters to pass with the request, optional.
|
||||
* `deferRequestBy`: Number of miliseconds to defer ajax request. Default: `0`.
|
||||
* `ajaxSettings`: Any additional [Ajax Settings](http://api.jquery.com/jquery.ajax/#jQuery-ajax-settings) that configure the jQuery Ajax request.
|
||||
|
||||
###Configuration Settings
|
||||
* `noCache`: Boolean value indicating whether to cache suggestion results. Default `false`.
|
||||
* `delimiter`: String or RegExp, that splits input value and takes last part to as query for suggestions.
|
||||
Useful when for example you need to fill list of coma separated values.
|
||||
* `onSearchStart`: `function (query) {}` called before ajax request. `this` is bound to input element.
|
||||
* `onSearchComplete`: `function (query, suggestions) {}` called after ajax response is processed. `this` is bound to input element. `suggestions` is an array containing the results.
|
||||
* `onSearchError`: `function (query, jqXHR, textStatus, errorThrown) {}` called if ajax request fails. `this` is bound to input element.
|
||||
* `transformResult`: `function(response, originalQuery) {}` called after the result of the query is ready. Converts the result into response.suggestions format.
|
||||
* `onSelect`: `function (suggestion) {}` Callback function invoked when user selects suggestion
|
||||
from the list. `this` inside callback refers to input HtmlElement.
|
||||
* `minChars`: Minimum number of characters required to trigger autosuggest. Default: `1`.
|
||||
* `lookupLimit`: Number of maximum results to display for local lookup. Default: no limit.
|
||||
* `lookup`: Callback function or lookup array for the suggestions. It may be array of strings or `suggestion` object literals.
|
||||
* `suggestion`: An object literal with the following format: `{ value: 'string', data: any }`.
|
||||
* `lookupFilter`: `function (suggestion, query, queryLowerCase) {}` filter function for local lookups. By default it does partial string match (case insensitive).
|
||||
* `triggerSelectOnValidInput`: Boolean value indicating if `select` should be triggered if it matches suggestion. Default `true`.
|
||||
* `preventBadQueries`: Boolean value indicating if it shoud prevent future ajax requests for queries with the same root if no results were returned. E.g. if `Jam` returns no suggestions, it will not fire for any future query that starts with `Jam`. Default `true`.
|
||||
* `autoSelectFirst`: if set to `true`, first item will be selected when showing suggestions. Default value `false`.
|
||||
* `onHide`: `function (container) {}` called before container will be hidden
|
||||
|
||||
###Presentation Settings
|
||||
* `beforeRender`: `function (container) {}` called before displaying the suggestions. You may manipulate suggestions DOM before it is displayed.
|
||||
* `formatResult`: `function (suggestion, currentValue) {}` custom function to
|
||||
format suggestion entry inside suggestions container, optional.
|
||||
* `groupBy`: property name of the suggestion `data` object, by which results should be grouped.
|
||||
* `maxHeight`: Maximum height of the suggestions container in pixels. Default: `300`.
|
||||
* `width`: Suggestions container width in pixels, e.g.: 300. Default: `auto`, takes input field width.
|
||||
* `zIndex`: 'z-index' for suggestions container. Default: `9999`.
|
||||
* `appendTo`: container where suggestions will be appended. Default value `document.body`. Can be jQuery object, selector or html element. Make sure to set `position: absolute` or `position: relative` for that element.
|
||||
* `forceFixPosition`: Default: `false`. Suggestions are automatically positioned when their container is appended to body (look at `appendTo` option), in other cases suggestions are rendered but no positioning is applied.
|
||||
Set this option to force auto positioning in other cases.
|
||||
* `orientation`: Default `bottom`. Vertical orientation of the displayed suggestions, available values are `auto`, `top`, `bottom`.
|
||||
If set to `auto`, the suggestions will be orientated it the way that place them closer to middle of the view port.
|
||||
* `preserveInput`: if `true`, input value stays the same when navigating over suggestions. Default: `false`.
|
||||
* `showNoSuggestionNotice`: Default `false`. When no matching results, display a notification label.
|
||||
* `noSuggestionNotice`: Default `No results`. Text or htmlString or Element or jQuery object for no matching results label.
|
||||
* `onInvalidateSelection`: `function () {}` called when input is altered after selection has been made. `this` is bound to input element.
|
||||
* `tabDisabled`: Default `false`. Set to true to leave the cursor in the input field after the user tabs to select a suggestion.
|
||||
|
||||
## Instance Methods
|
||||
|
||||
Autocomplete instance has following methods:
|
||||
|
||||
* `setOptions(options)`: you may update any option at any time. Options are listed above.
|
||||
* `clear`: clears suggestion cache and current suggestions suggestions.
|
||||
* `clearCache`: clears suggestion cache.
|
||||
* `disable`: deactivate autocomplete.
|
||||
* `enable`: activates autocomplete if it was deactivated before.
|
||||
* `hide`: hides suggestions.
|
||||
* `dispose`: destroys autocomplete instance. All events are detached and suggestion containers removed.
|
||||
|
||||
There are two ways that you can invoke Autocomplete method. One is calling autocomplete on jQuery object and passing method name as string literal.
|
||||
If method has arguments, arguments are passed as consecutive parameters:
|
||||
|
||||
```javascript
|
||||
$('#autocomplete').autocomplete('disable');
|
||||
$('#autocomplete').autocomplete('setOptions', options);
|
||||
```
|
||||
|
||||
Or you can get Autocomplete instance by calling autcomplete on jQuery object without any parameters and then invoke desired method.
|
||||
|
||||
```javascript
|
||||
$('#autocomplete').autocomplete().disable();
|
||||
$('#autocomplete').autocomplete().setOptions(options);
|
||||
```
|
||||
|
||||
##Usage
|
||||
|
||||
Html:
|
||||
|
||||
```html
|
||||
<input type="text" name="country" id="autocomplete"/>
|
||||
```
|
||||
|
||||
Ajax lookup:
|
||||
|
||||
```javascript
|
||||
$('#autocomplete').autocomplete({
|
||||
serviceUrl: '/autocomplete/countries',
|
||||
onSelect: function (suggestion) {
|
||||
alert('You selected: ' + suggestion.value + ', ' + suggestion.data);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
Local lookup (no ajax):
|
||||
|
||||
```javascript
|
||||
var countries = [
|
||||
{ value: 'Andorra', data: 'AD' },
|
||||
// ...
|
||||
{ value: 'Zimbabwe', data: 'ZZ' }
|
||||
];
|
||||
|
||||
$('#autocomplete').autocomplete({
|
||||
lookup: countries,
|
||||
onSelect: function (suggestion) {
|
||||
alert('You selected: ' + suggestion.value + ', ' + suggestion.data);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
Custom lookup function:
|
||||
```javascript
|
||||
|
||||
$('#autocomplete').autocomplete({
|
||||
lookup: function (query, done) {
|
||||
// Do ajax call or lookup locally, when done,
|
||||
// call the callback and pass your results:
|
||||
var result = {
|
||||
suggestions: [
|
||||
{ "value": "United Arab Emirates", "data": "AE" },
|
||||
{ "value": "United Kingdom", "data": "UK" },
|
||||
{ "value": "United States", "data": "US" }
|
||||
]
|
||||
};
|
||||
|
||||
done(result);
|
||||
},
|
||||
onSelect: function (suggestion) {
|
||||
alert('You selected: ' + suggestion.value + ', ' + suggestion.data);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
##Styling
|
||||
|
||||
Generated HTML markup for suggestions is displayed below. You may style it any way you'd like.
|
||||
|
||||
```html
|
||||
<div class="autocomplete-suggestions">
|
||||
<div class="autocomplete-group"><strong>NHL</strong></div>
|
||||
<div class="autocomplete-suggestion autocomplete-selected">...</div>
|
||||
<div class="autocomplete-suggestion">...</div>
|
||||
<div class="autocomplete-suggestion">...</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
Style sample:
|
||||
|
||||
```css
|
||||
.autocomplete-suggestions { border: 1px solid #999; background: #FFF; overflow: auto; }
|
||||
.autocomplete-suggestion { padding: 2px 5px; white-space: nowrap; overflow: hidden; }
|
||||
.autocomplete-selected { background: #F0F0F0; }
|
||||
.autocomplete-suggestions strong { font-weight: normal; color: #3399FF; }
|
||||
.autocomplete-group { padding: 2px 5px; }
|
||||
.autocomplete-group strong { display: block; border-bottom: 1px solid #000; }
|
||||
```
|
||||
|
||||
|
||||
##Response Format
|
||||
|
||||
Response from the server must be JSON formatted following JavaScript object:
|
||||
|
||||
```javascript
|
||||
{
|
||||
// Query is not required as of version 1.2.5
|
||||
"query": "Unit",
|
||||
"suggestions": [
|
||||
{ "value": "United Arab Emirates", "data": "AE" },
|
||||
{ "value": "United Kingdom", "data": "UK" },
|
||||
{ "value": "United States", "data": "US" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Data can be any value or object. Data object is passed to formatResults function
|
||||
and onSelect callback. Alternatively, if there is no data you can
|
||||
supply just a string array for suggestions:
|
||||
|
||||
```json
|
||||
{
|
||||
"query": "Unit",
|
||||
"suggestions": ["United Arab Emirates", "United Kingdom", "United States"]
|
||||
}
|
||||
```
|
||||
|
||||
## Non standard query/results
|
||||
|
||||
If your ajax service expects the query in a different format, and returns data in a different format than the standard response,
|
||||
you can supply the "paramName" and "transformResult" options:
|
||||
|
||||
```javascript
|
||||
$('#autocomplete').autocomplete({
|
||||
paramName: 'searchString',
|
||||
transformResult: function(response) {
|
||||
return {
|
||||
suggestions: $.map(response.myData, function(dataItem) {
|
||||
return { value: dataItem.valueField, data: dataItem.dataField };
|
||||
})
|
||||
};
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## Grouping Results
|
||||
|
||||
Specify `groupBy` option of you data property if you wish results to be displayed in groups. For example, set `groupBy: 'category'` if your suggestion data format is:
|
||||
|
||||
```javascript
|
||||
[
|
||||
{ value: 'Chicago Blackhawks', data: { category: 'NHL' } },
|
||||
{ value: 'Chicago Bulls', data: { category: 'NBA' } }
|
||||
]
|
||||
```
|
||||
|
||||
Results will be formatted into two groups **NHL** and **NBA**.
|
||||
|
||||
##Known Issues
|
||||
|
||||
If you use it with jQuery UI library it also has plugin named `autocomplete`. In this case you can use plugin alias `devbridgeAutocomplete`:
|
||||
|
||||
```javascript
|
||||
$('.autocomplete').devbridgeAutocomplete({ ... });
|
||||
```
|
||||
|
||||
##License
|
||||
|
||||
Ajax Autocomplete for jQuery is freely distributable under the
|
||||
terms of an MIT-style [license](https://github.com/devbridge/jQuery-Autocomplete/blob/master/dist/license.txt).
|
||||
|
||||
Copyright notice and permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
##Authors
|
||||
|
||||
Tomas Kirda / [@tkirda](https://twitter.com/tkirda)
|
||||
40
vendors/devbridge-autocomplete/src/jquery.autocomplete.d.ts
vendored
Normal file
40
vendors/devbridge-autocomplete/src/jquery.autocomplete.d.ts
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
|
||||
interface JQueryAutocompleteOptions {
|
||||
serviceUrl?: string;
|
||||
lookup?: AutocompleteSuggestion[];
|
||||
lookupFilter? (suggestion: AutocompleteSuggestion, query: string, queryLowercase: string): any;
|
||||
onSelect? (suggestion: AutocompleteSuggestion): void;
|
||||
minChars: number;
|
||||
maxHeight: number;
|
||||
deferRequestBy?: number;
|
||||
width?: number;
|
||||
params?: Object;
|
||||
formatResult? (suggestion: AutocompleteSuggestion, currentValue: string): string;
|
||||
delimiter?: any;
|
||||
zIndex?: number;
|
||||
type?: string;
|
||||
noCache?: bool;
|
||||
onSearchStart? (query: string): void;
|
||||
onSearchComplete? (query: string): void;
|
||||
tabDisabled?: bool;
|
||||
paramName?: string;
|
||||
transformResult? (response: any, originalQuery: string): AutocompleteSuggestion[];
|
||||
autoSelectFirst?: bool;
|
||||
appendTo: any;
|
||||
dataType: string;
|
||||
}
|
||||
|
||||
interface AutocompleteSuggestion {
|
||||
value: string;
|
||||
data: any;
|
||||
}
|
||||
|
||||
interface AutocompleteInstance {
|
||||
setOptions(options: JQueryAutocompleteOptions): void;
|
||||
clear(): void;
|
||||
clearCache(): void;
|
||||
disable(): void;
|
||||
enable(): void;
|
||||
hide(): void;
|
||||
dispose(): void;
|
||||
}
|
||||
979
vendors/devbridge-autocomplete/src/jquery.autocomplete.js
vendored
Normal file
979
vendors/devbridge-autocomplete/src/jquery.autocomplete.js
vendored
Normal file
@@ -0,0 +1,979 @@
|
||||
/**
|
||||
* Ajax Autocomplete for jQuery, version %version%
|
||||
* (c) 2015 Tomas Kirda
|
||||
*
|
||||
* Ajax Autocomplete for jQuery is freely distributable under the terms of an MIT-style license.
|
||||
* For details, see the web site: https://github.com/devbridge/jQuery-Autocomplete
|
||||
*/
|
||||
|
||||
/*jslint browser: true, white: true, plusplus: true, vars: true */
|
||||
/*global define, window, document, jQuery, exports, require */
|
||||
|
||||
// Expose plugin as an AMD module if AMD loader is present:
|
||||
(function (factory) {
|
||||
'use strict';
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
// AMD. Register as an anonymous module.
|
||||
define(['jquery'], factory);
|
||||
} else if (typeof exports === 'object' && typeof require === 'function') {
|
||||
// Browserify
|
||||
factory(require('jquery'));
|
||||
} else {
|
||||
// Browser globals
|
||||
factory(jQuery);
|
||||
}
|
||||
}(function ($) {
|
||||
'use strict';
|
||||
|
||||
var
|
||||
utils = (function () {
|
||||
return {
|
||||
escapeRegExChars: function (value) {
|
||||
return value.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
|
||||
},
|
||||
createNode: function (containerClass) {
|
||||
var div = document.createElement('div');
|
||||
div.className = containerClass;
|
||||
div.style.position = 'absolute';
|
||||
div.style.display = 'none';
|
||||
return div;
|
||||
}
|
||||
};
|
||||
}()),
|
||||
|
||||
keys = {
|
||||
ESC: 27,
|
||||
TAB: 9,
|
||||
RETURN: 13,
|
||||
LEFT: 37,
|
||||
UP: 38,
|
||||
RIGHT: 39,
|
||||
DOWN: 40
|
||||
};
|
||||
|
||||
function Autocomplete(el, options) {
|
||||
var noop = function () { },
|
||||
that = this,
|
||||
defaults = {
|
||||
ajaxSettings: {},
|
||||
autoSelectFirst: false,
|
||||
appendTo: document.body,
|
||||
serviceUrl: null,
|
||||
lookup: null,
|
||||
onSelect: null,
|
||||
width: 'auto',
|
||||
minChars: 1,
|
||||
maxHeight: 300,
|
||||
deferRequestBy: 0,
|
||||
params: {},
|
||||
formatResult: Autocomplete.formatResult,
|
||||
delimiter: null,
|
||||
zIndex: 9999,
|
||||
type: 'GET',
|
||||
noCache: false,
|
||||
onSearchStart: noop,
|
||||
onSearchComplete: noop,
|
||||
onSearchError: noop,
|
||||
preserveInput: false,
|
||||
containerClass: 'autocomplete-suggestions',
|
||||
tabDisabled: false,
|
||||
dataType: 'text',
|
||||
currentRequest: null,
|
||||
triggerSelectOnValidInput: true,
|
||||
preventBadQueries: true,
|
||||
lookupFilter: function (suggestion, originalQuery, queryLowerCase) {
|
||||
return suggestion.value.toLowerCase().indexOf(queryLowerCase) !== -1;
|
||||
},
|
||||
paramName: 'query',
|
||||
transformResult: function (response) {
|
||||
return typeof response === 'string' ? $.parseJSON(response) : response;
|
||||
},
|
||||
showNoSuggestionNotice: false,
|
||||
noSuggestionNotice: 'No results',
|
||||
orientation: 'bottom',
|
||||
forceFixPosition: false
|
||||
};
|
||||
|
||||
// Shared variables:
|
||||
that.element = el;
|
||||
that.el = $(el);
|
||||
that.suggestions = [];
|
||||
that.badQueries = [];
|
||||
that.selectedIndex = -1;
|
||||
that.currentValue = that.element.value;
|
||||
that.intervalId = 0;
|
||||
that.cachedResponse = {};
|
||||
that.onChangeInterval = null;
|
||||
that.onChange = null;
|
||||
that.isLocal = false;
|
||||
that.suggestionsContainer = null;
|
||||
that.noSuggestionsContainer = null;
|
||||
that.options = $.extend({}, defaults, options);
|
||||
that.classes = {
|
||||
selected: 'autocomplete-selected',
|
||||
suggestion: 'autocomplete-suggestion'
|
||||
};
|
||||
that.hint = null;
|
||||
that.hintValue = '';
|
||||
that.selection = null;
|
||||
|
||||
// Initialize and set options:
|
||||
that.initialize();
|
||||
that.setOptions(options);
|
||||
}
|
||||
|
||||
Autocomplete.utils = utils;
|
||||
|
||||
$.Autocomplete = Autocomplete;
|
||||
|
||||
Autocomplete.formatResult = function (suggestion, currentValue) {
|
||||
var pattern = '(' + utils.escapeRegExChars(currentValue) + ')';
|
||||
|
||||
return suggestion.value
|
||||
.replace(new RegExp(pattern, 'gi'), '<strong>$1<\/strong>')
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/<(\/?strong)>/g, '<$1>');
|
||||
};
|
||||
|
||||
Autocomplete.prototype = {
|
||||
|
||||
killerFn: null,
|
||||
|
||||
initialize: function () {
|
||||
var that = this,
|
||||
suggestionSelector = '.' + that.classes.suggestion,
|
||||
selected = that.classes.selected,
|
||||
options = that.options,
|
||||
container;
|
||||
|
||||
// Remove autocomplete attribute to prevent native suggestions:
|
||||
that.element.setAttribute('autocomplete', 'off');
|
||||
|
||||
that.killerFn = function (e) {
|
||||
if ($(e.target).closest('.' + that.options.containerClass).length === 0) {
|
||||
that.killSuggestions();
|
||||
that.disableKillerFn();
|
||||
}
|
||||
};
|
||||
|
||||
// html() deals with many types: htmlString or Element or Array or jQuery
|
||||
that.noSuggestionsContainer = $('<div class="autocomplete-no-suggestion"></div>')
|
||||
.html(this.options.noSuggestionNotice).get(0);
|
||||
|
||||
that.suggestionsContainer = Autocomplete.utils.createNode(options.containerClass);
|
||||
|
||||
container = $(that.suggestionsContainer);
|
||||
|
||||
container.appendTo(options.appendTo);
|
||||
|
||||
// Only set width if it was provided:
|
||||
if (options.width !== 'auto') {
|
||||
container.width(options.width);
|
||||
}
|
||||
|
||||
// Listen for mouse over event on suggestions list:
|
||||
container.on('mouseover.autocomplete', suggestionSelector, function () {
|
||||
that.activate($(this).data('index'));
|
||||
});
|
||||
|
||||
// Deselect active element when mouse leaves suggestions container:
|
||||
container.on('mouseout.autocomplete', function () {
|
||||
that.selectedIndex = -1;
|
||||
container.children('.' + selected).removeClass(selected);
|
||||
});
|
||||
|
||||
// Listen for click event on suggestions list:
|
||||
container.on('click.autocomplete', suggestionSelector, function () {
|
||||
that.select($(this).data('index'));
|
||||
});
|
||||
|
||||
that.fixPositionCapture = function () {
|
||||
if (that.visible) {
|
||||
that.fixPosition();
|
||||
}
|
||||
};
|
||||
|
||||
$(window).on('resize.autocomplete', that.fixPositionCapture);
|
||||
|
||||
that.el.on('keydown.autocomplete', function (e) { that.onKeyPress(e); });
|
||||
that.el.on('keyup.autocomplete', function (e) { that.onKeyUp(e); });
|
||||
that.el.on('blur.autocomplete', function () { that.onBlur(); });
|
||||
that.el.on('focus.autocomplete', function () { that.onFocus(); });
|
||||
that.el.on('change.autocomplete', function (e) { that.onKeyUp(e); });
|
||||
that.el.on('input.autocomplete', function (e) { that.onKeyUp(e); });
|
||||
},
|
||||
|
||||
onFocus: function () {
|
||||
var that = this;
|
||||
that.fixPosition();
|
||||
if (that.options.minChars === 0 && that.el.val().length === 0) {
|
||||
that.onValueChange();
|
||||
}
|
||||
},
|
||||
|
||||
onBlur: function () {
|
||||
this.enableKillerFn();
|
||||
},
|
||||
|
||||
abortAjax: function () {
|
||||
var that = this;
|
||||
if (that.currentRequest) {
|
||||
that.currentRequest.abort();
|
||||
that.currentRequest = null;
|
||||
}
|
||||
},
|
||||
|
||||
setOptions: function (suppliedOptions) {
|
||||
var that = this,
|
||||
options = that.options;
|
||||
|
||||
$.extend(options, suppliedOptions);
|
||||
|
||||
that.isLocal = $.isArray(options.lookup);
|
||||
|
||||
if (that.isLocal) {
|
||||
options.lookup = that.verifySuggestionsFormat(options.lookup);
|
||||
}
|
||||
|
||||
options.orientation = that.validateOrientation(options.orientation, 'bottom');
|
||||
|
||||
// Adjust height, width and z-index:
|
||||
$(that.suggestionsContainer).css({
|
||||
'max-height': options.maxHeight + 'px',
|
||||
'width': options.width + 'px',
|
||||
'z-index': options.zIndex
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
clearCache: function () {
|
||||
this.cachedResponse = {};
|
||||
this.badQueries = [];
|
||||
},
|
||||
|
||||
clear: function () {
|
||||
this.clearCache();
|
||||
this.currentValue = '';
|
||||
this.suggestions = [];
|
||||
},
|
||||
|
||||
disable: function () {
|
||||
var that = this;
|
||||
that.disabled = true;
|
||||
clearInterval(that.onChangeInterval);
|
||||
that.abortAjax();
|
||||
},
|
||||
|
||||
enable: function () {
|
||||
this.disabled = false;
|
||||
},
|
||||
|
||||
fixPosition: function () {
|
||||
// Use only when container has already its content
|
||||
|
||||
var that = this,
|
||||
$container = $(that.suggestionsContainer),
|
||||
containerParent = $container.parent().get(0);
|
||||
// Fix position automatically when appended to body.
|
||||
// In other cases force parameter must be given.
|
||||
if (containerParent !== document.body && !that.options.forceFixPosition) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Choose orientation
|
||||
var orientation = that.options.orientation,
|
||||
containerHeight = $container.outerHeight(),
|
||||
height = that.el.outerHeight(),
|
||||
offset = that.el.offset(),
|
||||
styles = { 'top': offset.top, 'left': offset.left };
|
||||
|
||||
if (orientation === 'auto') {
|
||||
var viewPortHeight = $(window).height(),
|
||||
scrollTop = $(window).scrollTop(),
|
||||
topOverflow = -scrollTop + offset.top - containerHeight,
|
||||
bottomOverflow = scrollTop + viewPortHeight - (offset.top + height + containerHeight);
|
||||
|
||||
orientation = (Math.max(topOverflow, bottomOverflow) === topOverflow) ? 'top' : 'bottom';
|
||||
}
|
||||
|
||||
if (orientation === 'top') {
|
||||
styles.top += -containerHeight;
|
||||
} else {
|
||||
styles.top += height;
|
||||
}
|
||||
|
||||
// If container is not positioned to body,
|
||||
// correct its position using offset parent offset
|
||||
if(containerParent !== document.body) {
|
||||
var opacity = $container.css('opacity'),
|
||||
parentOffsetDiff;
|
||||
|
||||
if (!that.visible){
|
||||
$container.css('opacity', 0).show();
|
||||
}
|
||||
|
||||
parentOffsetDiff = $container.offsetParent().offset();
|
||||
styles.top -= parentOffsetDiff.top;
|
||||
styles.left -= parentOffsetDiff.left;
|
||||
|
||||
if (!that.visible){
|
||||
$container.css('opacity', opacity).hide();
|
||||
}
|
||||
}
|
||||
|
||||
// -2px to account for suggestions border.
|
||||
if (that.options.width === 'auto') {
|
||||
styles.width = (that.el.outerWidth() - 2) + 'px';
|
||||
}
|
||||
|
||||
$container.css(styles);
|
||||
},
|
||||
|
||||
enableKillerFn: function () {
|
||||
var that = this;
|
||||
$(document).on('click.autocomplete', that.killerFn);
|
||||
},
|
||||
|
||||
disableKillerFn: function () {
|
||||
var that = this;
|
||||
$(document).off('click.autocomplete', that.killerFn);
|
||||
},
|
||||
|
||||
killSuggestions: function () {
|
||||
var that = this;
|
||||
that.stopKillSuggestions();
|
||||
that.intervalId = window.setInterval(function () {
|
||||
if (that.visible) {
|
||||
that.el.val(that.currentValue);
|
||||
that.hide();
|
||||
}
|
||||
|
||||
that.stopKillSuggestions();
|
||||
}, 50);
|
||||
},
|
||||
|
||||
stopKillSuggestions: function () {
|
||||
window.clearInterval(this.intervalId);
|
||||
},
|
||||
|
||||
isCursorAtEnd: function () {
|
||||
var that = this,
|
||||
valLength = that.el.val().length,
|
||||
selectionStart = that.element.selectionStart,
|
||||
range;
|
||||
|
||||
if (typeof selectionStart === 'number') {
|
||||
return selectionStart === valLength;
|
||||
}
|
||||
if (document.selection) {
|
||||
range = document.selection.createRange();
|
||||
range.moveStart('character', -valLength);
|
||||
return valLength === range.text.length;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
onKeyPress: function (e) {
|
||||
var that = this;
|
||||
|
||||
// If suggestions are hidden and user presses arrow down, display suggestions:
|
||||
if (!that.disabled && !that.visible && e.which === keys.DOWN && that.currentValue) {
|
||||
that.suggest();
|
||||
return;
|
||||
}
|
||||
|
||||
if (that.disabled || !that.visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (e.which) {
|
||||
case keys.ESC:
|
||||
that.el.val(that.currentValue);
|
||||
that.hide();
|
||||
break;
|
||||
case keys.RIGHT:
|
||||
if (that.hint && that.options.onHint && that.isCursorAtEnd()) {
|
||||
that.selectHint();
|
||||
break;
|
||||
}
|
||||
return;
|
||||
case keys.TAB:
|
||||
if (that.hint && that.options.onHint) {
|
||||
that.selectHint();
|
||||
return;
|
||||
}
|
||||
if (that.selectedIndex === -1) {
|
||||
that.hide();
|
||||
return;
|
||||
}
|
||||
that.select(that.selectedIndex);
|
||||
if (that.options.tabDisabled === false) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case keys.RETURN:
|
||||
if (that.selectedIndex === -1) {
|
||||
that.hide();
|
||||
return;
|
||||
}
|
||||
that.select(that.selectedIndex);
|
||||
break;
|
||||
case keys.UP:
|
||||
that.moveUp();
|
||||
break;
|
||||
case keys.DOWN:
|
||||
that.moveDown();
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
// Cancel event if function did not return:
|
||||
e.stopImmediatePropagation();
|
||||
e.preventDefault();
|
||||
},
|
||||
|
||||
onKeyUp: function (e) {
|
||||
var that = this;
|
||||
|
||||
if (that.disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (e.which) {
|
||||
case keys.UP:
|
||||
case keys.DOWN:
|
||||
return;
|
||||
}
|
||||
|
||||
clearInterval(that.onChangeInterval);
|
||||
|
||||
if (that.currentValue !== that.el.val()) {
|
||||
that.findBestHint();
|
||||
if (that.options.deferRequestBy > 0) {
|
||||
// Defer lookup in case when value changes very quickly:
|
||||
that.onChangeInterval = setInterval(function () {
|
||||
that.onValueChange();
|
||||
}, that.options.deferRequestBy);
|
||||
} else {
|
||||
that.onValueChange();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
onValueChange: function () {
|
||||
var that = this,
|
||||
options = that.options,
|
||||
value = that.el.val(),
|
||||
query = that.getQuery(value);
|
||||
|
||||
if (that.selection && that.currentValue !== query) {
|
||||
that.selection = null;
|
||||
(options.onInvalidateSelection || $.noop).call(that.element);
|
||||
}
|
||||
|
||||
clearInterval(that.onChangeInterval);
|
||||
that.currentValue = value;
|
||||
that.selectedIndex = -1;
|
||||
|
||||
// Check existing suggestion for the match before proceeding:
|
||||
if (options.triggerSelectOnValidInput && that.isExactMatch(query)) {
|
||||
that.select(0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (query.length < options.minChars) {
|
||||
that.hide();
|
||||
} else {
|
||||
that.getSuggestions(query);
|
||||
}
|
||||
},
|
||||
|
||||
isExactMatch: function (query) {
|
||||
var suggestions = this.suggestions;
|
||||
|
||||
return (suggestions.length === 1 && suggestions[0].value.toLowerCase() === query.toLowerCase());
|
||||
},
|
||||
|
||||
getQuery: function (value) {
|
||||
var delimiter = this.options.delimiter,
|
||||
parts;
|
||||
|
||||
if (!delimiter) {
|
||||
return value;
|
||||
}
|
||||
parts = value.split(delimiter);
|
||||
return $.trim(parts[parts.length - 1]);
|
||||
},
|
||||
|
||||
getSuggestionsLocal: function (query) {
|
||||
var that = this,
|
||||
options = that.options,
|
||||
queryLowerCase = query.toLowerCase(),
|
||||
filter = options.lookupFilter,
|
||||
limit = parseInt(options.lookupLimit, 10),
|
||||
data;
|
||||
|
||||
data = {
|
||||
suggestions: $.grep(options.lookup, function (suggestion) {
|
||||
return filter(suggestion, query, queryLowerCase);
|
||||
})
|
||||
};
|
||||
|
||||
if (limit && data.suggestions.length > limit) {
|
||||
data.suggestions = data.suggestions.slice(0, limit);
|
||||
}
|
||||
|
||||
return data;
|
||||
},
|
||||
|
||||
getSuggestions: function (q) {
|
||||
var response,
|
||||
that = this,
|
||||
options = that.options,
|
||||
serviceUrl = options.serviceUrl,
|
||||
params,
|
||||
cacheKey,
|
||||
ajaxSettings;
|
||||
|
||||
options.params[options.paramName] = q;
|
||||
params = options.ignoreParams ? null : options.params;
|
||||
|
||||
if (options.onSearchStart.call(that.element, options.params) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($.isFunction(options.lookup)){
|
||||
options.lookup(q, function (data) {
|
||||
that.suggestions = data.suggestions;
|
||||
that.suggest();
|
||||
options.onSearchComplete.call(that.element, q, data.suggestions);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (that.isLocal) {
|
||||
response = that.getSuggestionsLocal(q);
|
||||
} else {
|
||||
if ($.isFunction(serviceUrl)) {
|
||||
serviceUrl = serviceUrl.call(that.element, q);
|
||||
}
|
||||
cacheKey = serviceUrl + '?' + $.param(params || {});
|
||||
response = that.cachedResponse[cacheKey];
|
||||
}
|
||||
|
||||
if (response && $.isArray(response.suggestions)) {
|
||||
that.suggestions = response.suggestions;
|
||||
that.suggest();
|
||||
options.onSearchComplete.call(that.element, q, response.suggestions);
|
||||
} else if (!that.isBadQuery(q)) {
|
||||
that.abortAjax();
|
||||
|
||||
ajaxSettings = {
|
||||
url: serviceUrl,
|
||||
data: params,
|
||||
type: options.type,
|
||||
dataType: options.dataType
|
||||
};
|
||||
|
||||
$.extend(ajaxSettings, options.ajaxSettings);
|
||||
|
||||
that.currentRequest = $.ajax(ajaxSettings).done(function (data) {
|
||||
var result;
|
||||
that.currentRequest = null;
|
||||
result = options.transformResult(data, q);
|
||||
that.processResponse(result, q, cacheKey);
|
||||
options.onSearchComplete.call(that.element, q, result.suggestions);
|
||||
}).fail(function (jqXHR, textStatus, errorThrown) {
|
||||
options.onSearchError.call(that.element, q, jqXHR, textStatus, errorThrown);
|
||||
});
|
||||
} else {
|
||||
options.onSearchComplete.call(that.element, q, []);
|
||||
}
|
||||
},
|
||||
|
||||
isBadQuery: function (q) {
|
||||
if (!this.options.preventBadQueries){
|
||||
return false;
|
||||
}
|
||||
|
||||
var badQueries = this.badQueries,
|
||||
i = badQueries.length;
|
||||
|
||||
while (i--) {
|
||||
if (q.indexOf(badQueries[i]) === 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
hide: function () {
|
||||
var that = this,
|
||||
container = $(that.suggestionsContainer);
|
||||
|
||||
if ($.isFunction(that.options.onHide) && that.visible) {
|
||||
that.options.onHide.call(that.element, container);
|
||||
}
|
||||
|
||||
that.visible = false;
|
||||
that.selectedIndex = -1;
|
||||
clearInterval(that.onChangeInterval);
|
||||
$(that.suggestionsContainer).hide();
|
||||
that.signalHint(null);
|
||||
},
|
||||
|
||||
suggest: function () {
|
||||
if (this.suggestions.length === 0) {
|
||||
if (this.options.showNoSuggestionNotice) {
|
||||
this.noSuggestions();
|
||||
} else {
|
||||
this.hide();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var that = this,
|
||||
options = that.options,
|
||||
groupBy = options.groupBy,
|
||||
formatResult = options.formatResult,
|
||||
value = that.getQuery(that.currentValue),
|
||||
className = that.classes.suggestion,
|
||||
classSelected = that.classes.selected,
|
||||
container = $(that.suggestionsContainer),
|
||||
noSuggestionsContainer = $(that.noSuggestionsContainer),
|
||||
beforeRender = options.beforeRender,
|
||||
html = '',
|
||||
category,
|
||||
formatGroup = function (suggestion, index) {
|
||||
var currentCategory = suggestion.data[groupBy];
|
||||
|
||||
if (category === currentCategory){
|
||||
return '';
|
||||
}
|
||||
|
||||
category = currentCategory;
|
||||
|
||||
return '<div class="autocomplete-group"><strong>' + category + '</strong></div>';
|
||||
};
|
||||
|
||||
if (options.triggerSelectOnValidInput && that.isExactMatch(value)) {
|
||||
that.select(0);
|
||||
return;
|
||||
}
|
||||
|
||||
// Build suggestions inner HTML:
|
||||
$.each(that.suggestions, function (i, suggestion) {
|
||||
if (groupBy){
|
||||
html += formatGroup(suggestion, value, i);
|
||||
}
|
||||
|
||||
html += '<div class="' + className + '" data-index="' + i + '">' + formatResult(suggestion, value) + '</div>';
|
||||
});
|
||||
|
||||
this.adjustContainerWidth();
|
||||
|
||||
noSuggestionsContainer.detach();
|
||||
container.html(html);
|
||||
|
||||
if ($.isFunction(beforeRender)) {
|
||||
beforeRender.call(that.element, container);
|
||||
}
|
||||
|
||||
that.fixPosition();
|
||||
container.show();
|
||||
|
||||
// Select first value by default:
|
||||
if (options.autoSelectFirst) {
|
||||
that.selectedIndex = 0;
|
||||
container.scrollTop(0);
|
||||
container.children('.' + className).first().addClass(classSelected);
|
||||
}
|
||||
|
||||
that.visible = true;
|
||||
that.findBestHint();
|
||||
},
|
||||
|
||||
noSuggestions: function() {
|
||||
var that = this,
|
||||
container = $(that.suggestionsContainer),
|
||||
noSuggestionsContainer = $(that.noSuggestionsContainer);
|
||||
|
||||
this.adjustContainerWidth();
|
||||
|
||||
// Some explicit steps. Be careful here as it easy to get
|
||||
// noSuggestionsContainer removed from DOM if not detached properly.
|
||||
noSuggestionsContainer.detach();
|
||||
container.empty(); // clean suggestions if any
|
||||
container.append(noSuggestionsContainer);
|
||||
|
||||
that.fixPosition();
|
||||
|
||||
container.show();
|
||||
that.visible = true;
|
||||
},
|
||||
|
||||
adjustContainerWidth: function() {
|
||||
var that = this,
|
||||
options = that.options,
|
||||
width,
|
||||
container = $(that.suggestionsContainer);
|
||||
|
||||
// If width is auto, adjust width before displaying suggestions,
|
||||
// because if instance was created before input had width, it will be zero.
|
||||
// Also it adjusts if input width has changed.
|
||||
// -2px to account for suggestions border.
|
||||
if (options.width === 'auto') {
|
||||
width = that.el.outerWidth() - 2;
|
||||
container.width(width > 0 ? width : 300);
|
||||
}
|
||||
},
|
||||
|
||||
findBestHint: function () {
|
||||
var that = this,
|
||||
value = that.el.val().toLowerCase(),
|
||||
bestMatch = null;
|
||||
|
||||
if (!value) {
|
||||
return;
|
||||
}
|
||||
|
||||
$.each(that.suggestions, function (i, suggestion) {
|
||||
var foundMatch = suggestion.value.toLowerCase().indexOf(value) === 0;
|
||||
if (foundMatch) {
|
||||
bestMatch = suggestion;
|
||||
}
|
||||
return !foundMatch;
|
||||
});
|
||||
|
||||
that.signalHint(bestMatch);
|
||||
},
|
||||
|
||||
signalHint: function (suggestion) {
|
||||
var hintValue = '',
|
||||
that = this;
|
||||
if (suggestion) {
|
||||
hintValue = that.currentValue + suggestion.value.substr(that.currentValue.length);
|
||||
}
|
||||
if (that.hintValue !== hintValue) {
|
||||
that.hintValue = hintValue;
|
||||
that.hint = suggestion;
|
||||
(this.options.onHint || $.noop)(hintValue);
|
||||
}
|
||||
},
|
||||
|
||||
verifySuggestionsFormat: function (suggestions) {
|
||||
// If suggestions is string array, convert them to supported format:
|
||||
if (suggestions.length && typeof suggestions[0] === 'string') {
|
||||
return $.map(suggestions, function (value) {
|
||||
return { value: value, data: null };
|
||||
});
|
||||
}
|
||||
|
||||
return suggestions;
|
||||
},
|
||||
|
||||
validateOrientation: function(orientation, fallback) {
|
||||
orientation = $.trim(orientation || '').toLowerCase();
|
||||
|
||||
if($.inArray(orientation, ['auto', 'bottom', 'top']) === -1){
|
||||
orientation = fallback;
|
||||
}
|
||||
|
||||
return orientation;
|
||||
},
|
||||
|
||||
processResponse: function (result, originalQuery, cacheKey) {
|
||||
var that = this,
|
||||
options = that.options;
|
||||
|
||||
result.suggestions = that.verifySuggestionsFormat(result.suggestions);
|
||||
|
||||
// Cache results if cache is not disabled:
|
||||
if (!options.noCache) {
|
||||
that.cachedResponse[cacheKey] = result;
|
||||
if (options.preventBadQueries && result.suggestions.length === 0) {
|
||||
that.badQueries.push(originalQuery);
|
||||
}
|
||||
}
|
||||
|
||||
// Return if originalQuery is not matching current query:
|
||||
if (originalQuery !== that.getQuery(that.currentValue)) {
|
||||
return;
|
||||
}
|
||||
|
||||
that.suggestions = result.suggestions;
|
||||
that.suggest();
|
||||
},
|
||||
|
||||
activate: function (index) {
|
||||
var that = this,
|
||||
activeItem,
|
||||
selected = that.classes.selected,
|
||||
container = $(that.suggestionsContainer),
|
||||
children = container.find('.' + that.classes.suggestion);
|
||||
|
||||
container.find('.' + selected).removeClass(selected);
|
||||
|
||||
that.selectedIndex = index;
|
||||
|
||||
if (that.selectedIndex !== -1 && children.length > that.selectedIndex) {
|
||||
activeItem = children.get(that.selectedIndex);
|
||||
$(activeItem).addClass(selected);
|
||||
return activeItem;
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
selectHint: function () {
|
||||
var that = this,
|
||||
i = $.inArray(that.hint, that.suggestions);
|
||||
|
||||
that.select(i);
|
||||
},
|
||||
|
||||
select: function (i) {
|
||||
var that = this;
|
||||
that.hide();
|
||||
that.onSelect(i);
|
||||
},
|
||||
|
||||
moveUp: function () {
|
||||
var that = this;
|
||||
|
||||
if (that.selectedIndex === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (that.selectedIndex === 0) {
|
||||
$(that.suggestionsContainer).children().first().removeClass(that.classes.selected);
|
||||
that.selectedIndex = -1;
|
||||
that.el.val(that.currentValue);
|
||||
that.findBestHint();
|
||||
return;
|
||||
}
|
||||
|
||||
that.adjustScroll(that.selectedIndex - 1);
|
||||
},
|
||||
|
||||
moveDown: function () {
|
||||
var that = this;
|
||||
|
||||
if (that.selectedIndex === (that.suggestions.length - 1)) {
|
||||
return;
|
||||
}
|
||||
|
||||
that.adjustScroll(that.selectedIndex + 1);
|
||||
},
|
||||
|
||||
adjustScroll: function (index) {
|
||||
var that = this,
|
||||
activeItem = that.activate(index);
|
||||
|
||||
if (!activeItem) {
|
||||
return;
|
||||
}
|
||||
|
||||
var offsetTop,
|
||||
upperBound,
|
||||
lowerBound,
|
||||
heightDelta = $(activeItem).outerHeight();
|
||||
|
||||
offsetTop = activeItem.offsetTop;
|
||||
upperBound = $(that.suggestionsContainer).scrollTop();
|
||||
lowerBound = upperBound + that.options.maxHeight - heightDelta;
|
||||
|
||||
if (offsetTop < upperBound) {
|
||||
$(that.suggestionsContainer).scrollTop(offsetTop);
|
||||
} else if (offsetTop > lowerBound) {
|
||||
$(that.suggestionsContainer).scrollTop(offsetTop - that.options.maxHeight + heightDelta);
|
||||
}
|
||||
|
||||
if (!that.options.preserveInput) {
|
||||
that.el.val(that.getValue(that.suggestions[index].value));
|
||||
}
|
||||
that.signalHint(null);
|
||||
},
|
||||
|
||||
onSelect: function (index) {
|
||||
var that = this,
|
||||
onSelectCallback = that.options.onSelect,
|
||||
suggestion = that.suggestions[index];
|
||||
|
||||
that.currentValue = that.getValue(suggestion.value);
|
||||
|
||||
if (that.currentValue !== that.el.val() && !that.options.preserveInput) {
|
||||
that.el.val(that.currentValue);
|
||||
}
|
||||
|
||||
that.signalHint(null);
|
||||
that.suggestions = [];
|
||||
that.selection = suggestion;
|
||||
|
||||
if ($.isFunction(onSelectCallback)) {
|
||||
onSelectCallback.call(that.element, suggestion);
|
||||
}
|
||||
},
|
||||
|
||||
getValue: function (value) {
|
||||
var that = this,
|
||||
delimiter = that.options.delimiter,
|
||||
currentValue,
|
||||
parts;
|
||||
|
||||
if (!delimiter) {
|
||||
return value;
|
||||
}
|
||||
|
||||
currentValue = that.currentValue;
|
||||
parts = currentValue.split(delimiter);
|
||||
|
||||
if (parts.length === 1) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return currentValue.substr(0, currentValue.length - parts[parts.length - 1].length) + value;
|
||||
},
|
||||
|
||||
dispose: function () {
|
||||
var that = this;
|
||||
that.el.off('.autocomplete').removeData('autocomplete');
|
||||
that.disableKillerFn();
|
||||
$(window).off('resize.autocomplete', that.fixPositionCapture);
|
||||
$(that.suggestionsContainer).remove();
|
||||
}
|
||||
};
|
||||
|
||||
// Create chainable jQuery plugin:
|
||||
$.fn.autocomplete = $.fn.devbridgeAutocomplete = function (options, args) {
|
||||
var dataKey = 'autocomplete';
|
||||
// If function invoked without argument return
|
||||
// instance of the first matched element:
|
||||
if (arguments.length === 0) {
|
||||
return this.first().data(dataKey);
|
||||
}
|
||||
|
||||
return this.each(function () {
|
||||
var inputElement = $(this),
|
||||
instance = inputElement.data(dataKey);
|
||||
|
||||
if (typeof options === 'string') {
|
||||
if (instance && typeof instance[options] === 'function') {
|
||||
instance[options](args);
|
||||
}
|
||||
} else {
|
||||
// If instance already exists, destroy it:
|
||||
if (instance && instance.dispose) {
|
||||
instance.dispose();
|
||||
}
|
||||
instance = new Autocomplete(this, options);
|
||||
inputElement.data(dataKey, instance);
|
||||
}
|
||||
});
|
||||
};
|
||||
}));
|
||||
Reference in New Issue
Block a user