aboutsummaryrefslogtreecommitdiffstats
path: root/foundation-default-colours/static/js/vendor/jquery.autocomplete.js
diff options
context:
space:
mode:
authorPeter Fraenkel <pnf@podsnap.com>2014-08-21 12:53:41 -0400
committerPeter Fraenkel <pnf@podsnap.com>2014-08-21 12:53:41 -0400
commitcfa5f40283668540cda45794200809640f251acd (patch)
treecf2bd8ffbea3ed4c01b5690ffd8ae2702976b583 /foundation-default-colours/static/js/vendor/jquery.autocomplete.js
parent1cd742a84a81f135234be021cf4fe7b8ff2c13ff (diff)
parenta928687830876af8c919606bae47195af65bc82d (diff)
downloadpelican-themes-cfa5f40283668540cda45794200809640f251acd.tar.gz
Merge remote-tracking branch 'upstream/master'
Keep the latex and tag-specific feeds in bootstrap3 Conflicts: pelican-bootstrap3/templates/base.html
Diffstat (limited to 'foundation-default-colours/static/js/vendor/jquery.autocomplete.js')
-rw-r--r--foundation-default-colours/static/js/vendor/jquery.autocomplete.js645
1 files changed, 645 insertions, 0 deletions
diff --git a/foundation-default-colours/static/js/vendor/jquery.autocomplete.js b/foundation-default-colours/static/js/vendor/jquery.autocomplete.js
new file mode 100644
index 0000000..85556a7
--- /dev/null
+++ b/foundation-default-colours/static/js/vendor/jquery.autocomplete.js
@@ -0,0 +1,645 @@
+/**
+* Ajax Autocomplete for jQuery, version 1.2.7
+* (c) 2013 Tomas Kirda
+*
+* Ajax Autocomplete for jQuery is freely distributable under the terms of an MIT-style license.
+* For details, see the web site: http://www.devbridge.com/projects/autocomplete/jquery/
+*
+*/
+
+/*jslint browser: true, white: true, plusplus: true */
+/*global define, window, document, jQuery */
+
+// 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 {
+ // Browser globals
+ factory(jQuery);
+ }
+}(function ($) {
+ 'use strict';
+
+ var
+ utils = (function () {
+ return {
+
+ extend: function (target, source) {
+ return $.extend(target, source);
+ },
+
+ createNode: function (html) {
+ var div = document.createElement('div');
+ div.innerHTML = html;
+ return div.firstChild;
+ }
+
+ };
+ }()),
+
+ keys = {
+ ESC: 27,
+ TAB: 9,
+ RETURN: 13,
+ UP: 38,
+ DOWN: 40
+ };
+
+ function Autocomplete(el, options) {
+ var noop = function () { },
+ that = this,
+ defaults = {
+ autoSelectFirst: false,
+ appendTo: '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,
+ containerClass: 'autocomplete-suggestions',
+ tabDisabled: false,
+ dataType: 'text',
+ lookupFilter: function (suggestion, originalQuery, queryLowerCase) {
+ return suggestion.value.toLowerCase().indexOf(queryLowerCase) !== -1;
+ },
+ paramName: 'query',
+ transformResult: function (response) {
+ return typeof response === 'string' ? $.parseJSON(response) : response;
+ }
+ };
+
+ // 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.ignoreValueChange = false;
+ that.isLocal = false;
+ that.suggestionsContainer = null;
+ that.options = $.extend({}, defaults, options);
+ that.classes = {
+ selected: 'autocomplete-selected',
+ suggestion: 'autocomplete-suggestion'
+ };
+
+ // Initialize and set options:
+ that.initialize();
+ that.setOptions(options);
+ }
+
+ Autocomplete.utils = utils;
+
+ $.Autocomplete = Autocomplete;
+
+ Autocomplete.formatResult = function (suggestion, currentValue) {
+ var reEscape = new RegExp('(\\' + ['/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\'].join('|\\') + ')', 'g'),
+ pattern = '(' + currentValue.replace(reEscape, '\\$1') + ')';
+
+ return suggestion.value.replace(new RegExp(pattern, 'gi'), '<strong>$1<\/strong>');
+ };
+
+ 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();
+ }
+ };
+
+ // Determine suggestions width:
+ if (!options.width || options.width === 'auto') {
+ options.width = that.el.outerWidth();
+ }
+
+ that.suggestionsContainer = Autocomplete.utils.createNode('<div class="' + options.containerClass + '" style="position: absolute; display: none;"></div>');
+
+ container = $(that.suggestionsContainer);
+
+ container.appendTo(options.appendTo).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'), false);
+ });
+
+ that.fixPosition();
+
+ // Opera does not like keydown:
+ if (window.opera) {
+ that.el.on('keypress.autocomplete', function (e) { that.onKeyPress(e); });
+ } else {
+ 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.fixPosition(); });
+ },
+
+ onBlur: function () {
+ this.enableKillerFn();
+ },
+
+ setOptions: function (suppliedOptions) {
+ var that = this,
+ options = that.options;
+
+ utils.extend(options, suppliedOptions);
+
+ that.isLocal = $.isArray(options.lookup);
+
+ if (that.isLocal) {
+ options.lookup = that.verifySuggestionsFormat(options.lookup);
+ }
+
+ // 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 = null;
+ this.suggestions = [];
+ },
+
+ disable: function () {
+ this.disabled = true;
+ },
+
+ enable: function () {
+ this.disabled = false;
+ },
+
+ fixPosition: function () {
+ var that = this,
+ offset;
+
+ // Don't adjsut position if custom container has been specified:
+ if (that.options.appendTo !== 'body') {
+ return;
+ }
+
+ offset = that.el.offset();
+
+ $(that.suggestionsContainer).css({
+ top: (offset.top + that.el.outerHeight()) + 'px',
+ left: offset.left + 'px'
+ });
+ },
+
+ 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 () {
+ that.hide();
+ that.stopKillSuggestions();
+ }, 300);
+ },
+
+ stopKillSuggestions: function () {
+ window.clearInterval(this.intervalId);
+ },
+
+ onKeyPress: function (e) {
+ var that = this;
+
+ // If suggestions are hidden and user presses arrow down, display suggestions:
+ if (!that.disabled && !that.visible && e.keyCode === keys.DOWN && that.currentValue) {
+ that.suggest();
+ return;
+ }
+
+ if (that.disabled || !that.visible) {
+ return;
+ }
+
+ switch (e.keyCode) {
+ case keys.ESC:
+ that.el.val(that.currentValue);
+ that.hide();
+ break;
+ case keys.TAB:
+ case keys.RETURN:
+ if (that.selectedIndex === -1) {
+ that.hide();
+ return;
+ }
+ that.select(that.selectedIndex, e.keyCode === keys.RETURN);
+ if (e.keyCode === keys.TAB && this.options.tabDisabled === false) {
+ return;
+ }
+ 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.keyCode) {
+ case keys.UP:
+ case keys.DOWN:
+ return;
+ }
+
+ clearInterval(that.onChangeInterval);
+
+ if (that.currentValue !== that.el.val()) {
+ 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,
+ q;
+
+ clearInterval(that.onChangeInterval);
+ that.currentValue = that.element.value;
+
+ q = that.getQuery(that.currentValue);
+ that.selectedIndex = -1;
+
+ if (that.ignoreValueChange) {
+ that.ignoreValueChange = false;
+ return;
+ }
+
+ if (q.length < that.options.minChars) {
+ that.hide();
+ } else {
+ that.getSuggestions(q);
+ }
+ },
+
+ getQuery: function (value) {
+ var delimiter = this.options.delimiter,
+ parts;
+
+ if (!delimiter) {
+ return $.trim(value);
+ }
+ parts = value.split(delimiter);
+ return $.trim(parts[parts.length - 1]);
+ },
+
+ getSuggestionsLocal: function (query) {
+ var that = this,
+ queryLowerCase = query.toLowerCase(),
+ filter = that.options.lookupFilter;
+
+ return {
+ suggestions: $.grep(that.options.lookup, function (suggestion) {
+ return filter(suggestion, query, queryLowerCase);
+ })
+ };
+ },
+
+ getSuggestions: function (q) {
+ var response,
+ that = this,
+ options = that.options,
+ serviceUrl = options.serviceUrl;
+
+ response = that.isLocal ? that.getSuggestionsLocal(q) : that.cachedResponse[q];
+
+ if (response && $.isArray(response.suggestions)) {
+ that.suggestions = response.suggestions;
+ that.suggest();
+ } else if (!that.isBadQuery(q)) {
+ options.params[options.paramName] = q;
+ if (options.onSearchStart.call(that.element, options.params) === false) {
+ return;
+ }
+ if ($.isFunction(options.serviceUrl)) {
+ serviceUrl = options.serviceUrl.call(that.element, q);
+ }
+ $.ajax({
+ url: serviceUrl,
+ data: options.ignoreParams ? null : options.params,
+ type: options.type,
+ dataType: options.dataType
+ }).done(function (data) {
+ that.processResponse(data, q);
+ options.onSearchComplete.call(that.element, q);
+ });
+ }
+ },
+
+ isBadQuery: function (q) {
+ var badQueries = this.badQueries,
+ i = badQueries.length;
+
+ while (i--) {
+ if (q.indexOf(badQueries[i]) === 0) {
+ return true;
+ }
+ }
+
+ return false;
+ },
+
+ hide: function () {
+ var that = this;
+ that.visible = false;
+ that.selectedIndex = -1;
+ $(that.suggestionsContainer).hide();
+ },
+
+ suggest: function () {
+ if (this.suggestions.length === 0) {
+ this.hide();
+ return;
+ }
+
+ var that = this,
+ formatResult = that.options.formatResult,
+ value = that.getQuery(that.currentValue),
+ className = that.classes.suggestion,
+ classSelected = that.classes.selected,
+ container = $(that.suggestionsContainer),
+ html = '';
+
+ // Build suggestions inner HTML:
+ $.each(that.suggestions, function (i, suggestion) {
+ html += '<div class="' + className + '" data-index="' + i + '">' + formatResult(suggestion, value) + '</div>';
+ });
+
+ container.html(html).show();
+ that.visible = true;
+
+ // Select first value by default:
+ if (that.options.autoSelectFirst) {
+ that.selectedIndex = 0;
+ container.children().first().addClass(classSelected);
+ }
+ },
+
+ 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;
+ },
+
+ processResponse: function (response, originalQuery) {
+ var that = this,
+ options = that.options,
+ result = options.transformResult(response, originalQuery);
+
+ result.suggestions = that.verifySuggestionsFormat(result.suggestions);
+
+ // Cache results if cache is not disabled:
+ if (!options.noCache) {
+ that.cachedResponse[result[options.paramName]] = result;
+ if (result.suggestions.length === 0) {
+ that.badQueries.push(result[options.paramName]);
+ }
+ }
+
+ // Display suggestions only if returned query matches current value:
+ if (originalQuery === that.getQuery(that.currentValue)) {
+ that.suggestions = result.suggestions;
+ that.suggest();
+ }
+ },
+
+ activate: function (index) {
+ var that = this,
+ activeItem,
+ selected = that.classes.selected,
+ container = $(that.suggestionsContainer),
+ children = container.children();
+
+ container.children('.' + 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;
+ },
+
+ select: function (i, shouldIgnoreNextValueChange) {
+ var that = this,
+ selectedValue = that.suggestions[i];
+
+ if (selectedValue) {
+ that.el.val(selectedValue);
+ that.ignoreValueChange = shouldIgnoreNextValueChange;
+ 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);
+ 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),
+ offsetTop,
+ upperBound,
+ lowerBound,
+ heightDelta = 25;
+
+ if (!activeItem) {
+ return;
+ }
+
+ 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);
+ }
+
+ that.el.val(that.getValue(that.suggestions[index].value));
+ },
+
+ onSelect: function (index) {
+ var that = this,
+ onSelectCallback = that.options.onSelect,
+ suggestion = that.suggestions[index];
+
+ that.el.val(that.getValue(suggestion.value));
+
+ 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();
+ $(that.suggestionsContainer).remove();
+ }
+ };
+
+ // Create chainable jQuery plugin:
+ $.fn.autocomplete = 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);
+ }
+ });
+ };
+}));