123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773 |
- /* =============================================================
- * bootstrap3-typeahead.js v4.0.2
- * https://github.com/bassjobsen/Bootstrap-3-Typeahead
- * =============================================================
- * Original written by @mdo and @fat
- * =============================================================
- * Copyright 2014 Bass Jobsen @bassjobsen
- *
- * Licensed under the Apache License, Version 2.0 (the 'License');
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an 'AS IS' BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * ============================================================ */
- (function (root, factory) {
- 'use strict';
- // CommonJS module is defined
- if (typeof module !== 'undefined' && module.exports) {
- module.exports = factory(require('jquery'));
- }
- // AMD module is defined
- else if (typeof define === 'function' && define.amd) {
- define(['jquery'], function ($) {
- return factory($);
- });
- } else {
- factory(root.jQuery);
- }
- }(this, function ($) {
- 'use strict';
- // jshint laxcomma: true
- /* TYPEAHEAD PUBLIC CLASS DEFINITION
- * ================================= */
- var Typeahead = function (element, options) {
- this.$element = $(element);
- this.options = $.extend({}, Typeahead.defaults, options);
- this.matcher = this.options.matcher || this.matcher;
- this.sorter = this.options.sorter || this.sorter;
- this.select = this.options.select || this.select;
- this.autoSelect = typeof this.options.autoSelect == 'boolean' ? this.options.autoSelect : true;
- this.highlighter = this.options.highlighter || this.highlighter;
- this.render = this.options.render || this.render;
- this.updater = this.options.updater || this.updater;
- this.displayText = this.options.displayText || this.displayText;
- this.itemLink = this.options.itemLink || this.itemLink;
- this.itemTitle = this.options.itemTitle || this.itemTitle;
- this.followLinkOnSelect = this.options.followLinkOnSelect || this.followLinkOnSelect;
- this.source = this.options.source;
- this.delay = this.options.delay;
- this.theme = this.options.theme && this.options.themes && this.options.themes[this.options.theme] || Typeahead.defaults.themes[Typeahead.defaults.theme];
- this.$menu = $(this.options.menu || this.theme.menu);
- this.$appendTo = this.options.appendTo ? $(this.options.appendTo) : null;
- this.fitToElement = typeof this.options.fitToElement == 'boolean' ? this.options.fitToElement : false;
- this.shown = false;
- this.listen();
- this.showHintOnFocus = typeof this.options.showHintOnFocus == 'boolean' || this.options.showHintOnFocus === 'all' ? this.options.showHintOnFocus : false;
- this.afterSelect = this.options.afterSelect;
- this.afterEmptySelect = this.options.afterEmptySelect;
- this.addItem = false;
- this.value = this.$element.val() || this.$element.text();
- this.keyPressed = false;
- this.focused = this.$element.is(':focus');
- this.changeInputOnSelect = this.options.changeInputOnSelect || this.changeInputOnSelect;
- this.changeInputOnMove = this.options.changeInputOnMove || this.changeInputOnMove;
- this.openLinkInNewTab = this.options.openLinkInNewTab || this.openLinkInNewTab;
- this.selectOnBlur = this.options.selectOnBlur || this.selectOnBlur;
- this.showCategoryHeader = this.options.showCategoryHeader || this.showCategoryHeader;
- };
- Typeahead.prototype = {
- constructor: Typeahead,
- setDefault: function (val) {
- // var val = this.$menu.find('.active').data('value');
- this.$element.data('active', val);
- if (this.autoSelect || val) {
- var newVal = this.updater(val);
- // Updater can be set to any random functions via "options" parameter in constructor above.
- // Add null check for cases when updater returns void or undefined.
- if (!newVal) {
- newVal = '';
- }
- this.$element
- .val(this.displayText(newVal) || newVal)
- .text(this.displayText(newVal) || newVal)
- .change();
- this.afterSelect(newVal);
- }
- return this.hide();
- },
- select: function () {
- var val = this.$menu.find('.active').data('value');
- this.$element.data('active', val);
- if (this.autoSelect || val) {
- var newVal = this.updater(val);
- // Updater can be set to any random functions via "options" parameter in constructor above.
- // Add null check for cases when updater returns void or undefined.
- if (!newVal) {
- newVal = '';
- }
- if (this.changeInputOnSelect) {
- this.$element
- .val(this.displayText(newVal) || newVal)
- .text(this.displayText(newVal) || newVal)
- .change();
- }
- if (this.followLinkOnSelect && this.itemLink(val)) {
- if (this.openLinkInNewTab) {
- window.open(this.itemLink(val), '_blank');
- } else {
- document.location = this.itemLink(val);
- }
- this.afterSelect(newVal);
- } else if (this.followLinkOnSelect && !this.itemLink(val)) {
- this.afterEmptySelect(newVal);
- } else {
- this.afterSelect(newVal);
- }
- } else {
- this.afterEmptySelect();
- }
- return this.hide();
- },
- updater: function (item) {
- return item;
- },
- setSource: function (source) {
- this.source = source;
- },
- show: function () {
- var pos = $.extend({}, this.$element.position(), {
- height: this.$element[0].offsetHeight
- });
- var scrollHeight = typeof this.options.scrollHeight == 'function' ?
- this.options.scrollHeight.call() :
- this.options.scrollHeight;
- var element;
- if (this.shown) {
- element = this.$menu;
- } else if (this.$appendTo) {
- element = this.$menu.appendTo(this.$appendTo);
- this.hasSameParent = this.$appendTo.is(this.$element.parent());
- } else {
- element = this.$menu.insertAfter(this.$element);
- this.hasSameParent = true;
- }
- if (!this.hasSameParent) {
- // We cannot rely on the element position, need to position relative to the window
- element.css('position', 'fixed');
- var offset = this.$element.offset();
- pos.top = offset.top;
- pos.left = offset.left;
- }
- // The rules for bootstrap are: 'dropup' in the parent and 'dropdown-menu-right' in the element.
- // Note that to get right alignment, you'll need to specify `menu` in the options to be:
- // '<ul class="typeahead dropdown-menu" role="listbox"></ul>'
- var dropup = $(element).parent().hasClass('dropup');
- var newTop = dropup ? 'auto' : (pos.top + pos.height + scrollHeight);
- var right = $(element).hasClass('dropdown-menu-right');
- var newLeft = right ? 'auto' : pos.left;
- // it seems like setting the css is a bad idea (just let Bootstrap do it), but I'll keep the old
- // logic in place except for the dropup/right-align cases.
- element.css({ top: newTop, left: newLeft }).show();
- if (this.options.fitToElement === true) {
- element.css('width', this.$element.outerWidth() + 'px');
- }
- this.shown = true;
- return this;
- },
- hide: function () {
- this.$menu.hide();
- this.shown = false;
- return this;
- },
- lookup: function (query) {
- if (typeof(query) != 'undefined' && query !== null) {
- this.query = query;
- } else {
- this.query = this.$element.val();
- }
- if (this.query.length < this.options.minLength && !this.options.showHintOnFocus) {
- return this.shown ? this.hide() : this;
- }
- var worker = $.proxy(function () {
- // Bloodhound (since 0.11) needs three arguments.
- // Two of them are callback functions (sync and async) for local and remote data processing
- // see https://github.com/twitter/typeahead.js/blob/master/src/bloodhound/bloodhound.js#L132
- if ($.isFunction(this.source) && this.source.length === 3) {
- this.source(this.query, $.proxy(this.process, this), $.proxy(this.process, this));
- } else if ($.isFunction(this.source)) {
- this.source(this.query, $.proxy(this.process, this));
- } else if (this.source) {
- this.process(this.source);
- }
- }, this);
- clearTimeout(this.lookupWorker);
- this.lookupWorker = setTimeout(worker, this.delay);
- },
- process: function (items) {
- var that = this;
- items = $.grep(items, function (item) {
- return that.matcher(item);
- });
- items = this.sorter(items);
- if (!items.length && !this.options.addItem) {
- return this.shown ? this.hide() : this;
- }
- if (items.length > 0) {
- this.$element.data('active', items[0]);
- } else {
- this.$element.data('active', null);
- }
- if (this.options.items != 'all') {
- items = items.slice(0, this.options.items);
- }
- // Add item
- if (this.options.addItem) {
- items.push(this.options.addItem);
- }
- return this.render(items).show();
- },
- matcher: function (item) {
- var it = this.displayText(item);
- return ~it.toLowerCase().indexOf(this.query.toLowerCase());
- },
- sorter: function (items) {
- var beginswith = [];
- var caseSensitive = [];
- var caseInsensitive = [];
- var item;
- while ((item = items.shift())) {
- var it = this.displayText(item);
- if (!it.toLowerCase().indexOf(this.query.toLowerCase())) {
- beginswith.push(item);
- } else if (~it.indexOf(this.query)) {
- caseSensitive.push(item);
- } else {
- caseInsensitive.push(item);
- }
- }
- return beginswith.concat(caseSensitive, caseInsensitive);
- },
- highlighter: function (item) {
- var text = this.query;
- if (text === '') {
- return item;
- }
- var matches = item.match(/(>)([^<]*)(<)/g);
- var first = [];
- var second = [];
- var i;
- if (matches && matches.length) {
- // html
- for (i = 0; i < matches.length; ++i) {
- if (matches[i].length > 2) {// escape '><'
- first.push(matches[i]);
- }
- }
- } else {
- // text
- first = [];
- first.push(item);
- }
- text = text.replace((/[\(\)\/\.\*\+\?\[\]]/g), function (mat) {
- return '\\' + mat;
- });
- var reg = new RegExp(text, 'g');
- var m;
- for (i = 0; i < first.length; ++i) {
- m = first[i].match(reg);
- if (m && m.length > 0) {// find all text nodes matches
- second.push(first[i]);
- }
- }
- for (i = 0; i < second.length; ++i) {
- item = item.replace(second[i], second[i].replace(reg, '<strong>$&</strong>'));
- }
- return item;
- },
- render: function (items) {
- var that = this;
- var self = this;
- var activeFound = false;
- var data = [];
- var _category = that.options.separator;
- $.each(items, function (key, value) {
- // inject separator
- if (key > 0 && value[_category] !== items[key - 1][_category]) {
- data.push({
- __type: 'divider'
- });
- }
- if (this.showCategoryHeader) {
- // inject category header
- if (value[_category] && (key === 0 || value[_category] !== items[key - 1][_category])) {
- data.push({
- __type: 'category',
- name: value[_category]
- });
- }
- }
- data.push(value);
- });
- items = $(data).map(function (i, item) {
- if ((item.__type || false) == 'category'){
- return $(that.options.headerHtml || that.theme.headerHtml).text(item.name)[0];
- }
- if ((item.__type || false) == 'divider'){
- return $(that.options.headerDivider || that.theme.headerDivider)[0];
- }
- var text = self.displayText(item);
- i = $(that.options.item || that.theme.item).data('value', item);
- i.find(that.options.itemContentSelector || that.theme.itemContentSelector)
- .addBack(that.options.itemContentSelector || that.theme.itemContentSelector)
- .html(that.highlighter(text, item));
- if(that.options.followLinkOnSelect) {
- i.find('a').attr('href', self.itemLink(item));
- }
- i.find('a').attr('title', self.itemTitle(item));
- if (text == self.$element.val()) {
- i.addClass('active');
- self.$element.data('active', item);
- activeFound = true;
- }
- return i[0];
- });
- if (this.autoSelect && !activeFound) {
- items.filter(':not(.dropdown-header)').first().addClass('active');
- this.$element.data('active', items.first().data('value'));
- }
- this.$menu.html(items);
- return this;
- },
- displayText: function (item) {
- return typeof item !== 'undefined' && typeof item.name != 'undefined' ? item.name : item;
- },
- itemLink: function (item) {
- return null;
- },
- itemTitle: function (item) {
- return null;
- },
- next: function (event) {
- var active = this.$menu.find('.active').removeClass('active');
- var next = active.next();
- if (!next.length) {
- next = $(this.$menu.find($(this.options.item || this.theme.item).prop('tagName'))[0]);
- }
- while (next.hasClass('divider') || next.hasClass('dropdown-header')) {
- next = next.next();
- }
- next.addClass('active');
- // added for screen reader
- var newVal = this.updater(next.data('value'));
- if (this.changeInputOnMove) {
- this.$element.val(this.displayText(newVal) || newVal);
- }
- },
- prev: function (event) {
- var active = this.$menu.find('.active').removeClass('active');
- var prev = active.prev();
- if (!prev.length) {
- prev = this.$menu.find($(this.options.item || this.theme.item).prop('tagName')).last();
- }
- while (prev.hasClass('divider') || prev.hasClass('dropdown-header')) {
- prev = prev.prev();
- }
- prev.addClass('active');
- // added for screen reader
- var newVal = this.updater(prev.data('value'));
- if (this.changeInputOnMove) {
- this.$element.val(this.displayText(newVal) || newVal);
- }
- },
- listen: function () {
- this.$element
- .on('focus.bootstrap3Typeahead', $.proxy(this.focus, this))
- .on('blur.bootstrap3Typeahead', $.proxy(this.blur, this))
- .on('keypress.bootstrap3Typeahead', $.proxy(this.keypress, this))
- .on('propertychange.bootstrap3Typeahead input.bootstrap3Typeahead', $.proxy(this.input, this))
- .on('keyup.bootstrap3Typeahead', $.proxy(this.keyup, this));
- if (this.eventSupported('keydown')) {
- this.$element.on('keydown.bootstrap3Typeahead', $.proxy(this.keydown, this));
- }
- var itemTagName = $(this.options.item || this.theme.item).prop('tagName');
- if ('ontouchstart' in document.documentElement && 'onmousemove' in document.documentElement) {
- this.$menu
- .on('touchstart', itemTagName, $.proxy(this.touchstart, this))
- .on('touchend', itemTagName, $.proxy(this.click, this))
- .on('click', $.proxy(this.click, this))
- .on('mouseenter', itemTagName, $.proxy(this.mouseenter, this))
- .on('mouseleave', itemTagName, $.proxy(this.mouseleave, this))
- .on('mousedown', $.proxy(this.mousedown,this));
- } else if ('ontouchstart' in document.documentElement) {
- this.$menu
- .on('touchstart', itemTagName, $.proxy(this.touchstart, this))
- .on('touchend', itemTagName, $.proxy(this.click, this));
- }
- this.$menu
- .on('click', $.proxy(this.click, this))
- .on('mouseenter', itemTagName, $.proxy(this.mouseenter, this))
- .on('mouseleave', itemTagName, $.proxy(this.mouseleave, this))
- .on('mousedown', $.proxy(this.mousedown, this));
- },
- destroy: function () {
- this.$element.data('typeahead', null);
- this.$element.data('active', null);
- this.$element
- .unbind('focus.bootstrap3Typeahead')
- .unbind('blur.bootstrap3Typeahead')
- .unbind('keypress.bootstrap3Typeahead')
- .unbind('propertychange.bootstrap3Typeahead input.bootstrap3Typeahead')
- .unbind('keyup.bootstrap3Typeahead');
- if (this.eventSupported('keydown')) {
- this.$element.unbind('keydown.bootstrap3-typeahead');
- }
- this.$menu.remove();
- this.destroyed = true;
- },
- eventSupported: function (eventName) {
- var isSupported = eventName in this.$element;
- if (!isSupported) {
- this.$element.setAttribute(eventName, 'return;');
- isSupported = typeof this.$element[eventName] === 'function';
- }
- return isSupported;
- },
- move: function (e) {
- if (!this.shown) {
- return;
- }
- switch (e.keyCode) {
- case 9: // tab
- case 13: // enter
- case 27: // escape
- e.preventDefault();
- break;
- case 38: // up arrow
- // with the shiftKey (this is actually the left parenthesis)
- if (e.shiftKey) {
- return;
- }
- e.preventDefault();
- this.prev();
- break;
- case 40: // down arrow
- // with the shiftKey (this is actually the right parenthesis)
- if (e.shiftKey) {
- return;
- }
- e.preventDefault();
- this.next();
- break;
- }
- },
- keydown: function (e) {
- /**
- * Prevent to make an ajax call while copying and pasting.
- *
- * @author Simone Sacchi
- * @version 2018/01/18
- */
- if (e.keyCode === 17) { // ctrl
- return;
- }
- this.keyPressed = true;
- this.suppressKeyPressRepeat = ~$.inArray(e.keyCode, [40, 38, 9, 13, 27]);
- if (!this.shown && e.keyCode == 40) {
- this.lookup();
- } else {
- this.move(e);
- }
- },
- keypress: function (e) {
- if (this.suppressKeyPressRepeat) {
- return;
- }
- this.move(e);
- },
- input: function (e) {
- // This is a fixed for IE10/11 that fires the input event when a placehoder is changed
- // (https://connect.microsoft.com/IE/feedback/details/810538/ie-11-fires-input-event-on-focus)
- var currentValue = this.$element.val() || this.$element.text();
- if (this.value !== currentValue) {
- this.value = currentValue;
- this.lookup();
- }
- },
- keyup: function (e) {
- if (this.destroyed) {
- return;
- }
- switch (e.keyCode) {
- case 40: // down arrow
- case 38: // up arrow
- case 16: // shift
- case 17: // ctrl
- case 18: // alt
- break;
- case 9: // tab
- if (!this.shown || (this.showHintOnFocus && !this.keyPressed)) {
- return;
- }
- this.select();
- break;
- case 13: // enter
- if (!this.shown) {
- return;
- }
- this.select();
- break;
- case 27: // escape
- if (!this.shown) {
- return;
- }
- this.hide();
- break;
- }
- },
- focus: function (e) {
- if (!this.focused) {
- this.focused = true;
- this.keyPressed = false;
- if (this.options.showHintOnFocus && this.skipShowHintOnFocus !== true) {
- if (this.options.showHintOnFocus === 'all') {
- this.lookup('');
- } else {
- this.lookup();
- }
- }
- }
- if (this.skipShowHintOnFocus) {
- this.skipShowHintOnFocus = false;
- }
- },
- blur: function (e) {
- if (!this.mousedover && !this.mouseddown && this.shown) {
- if (this.selectOnBlur) {
- this.select();
- }
- this.hide();
- this.focused = false;
- this.keyPressed = false;
- } else if (this.mouseddown) {
- // This is for IE that blurs the input when user clicks on scroll.
- // We set the focus back on the input and prevent the lookup to occur again
- this.skipShowHintOnFocus = true;
- this.$element.focus();
- this.mouseddown = false;
- }
- },
- click: function (e) {
- e.preventDefault();
- this.skipShowHintOnFocus = true;
- this.select();
- this.$element.focus();
- this.hide();
- },
- mouseenter: function (e) {
- this.mousedover = true;
- this.$menu.find('.active').removeClass('active');
- $(e.currentTarget).addClass('active');
- },
- mouseleave: function (e) {
- this.mousedover = false;
- if (!this.focused && this.shown) {
- this.hide();
- }
- },
- /**
- * We track the mousedown for IE. When clicking on the menu scrollbar, IE makes the input blur thus hiding the menu.
- */
- mousedown: function (e) {
- this.mouseddown = true;
- this.$menu.one('mouseup', function (e) {
- // IE won't fire this, but FF and Chrome will so we reset our flag for them here
- this.mouseddown = false;
- }.bind(this));
- },
- touchstart: function (e) {
- e.preventDefault();
- this.$menu.find('.active').removeClass('active');
- $(e.currentTarget).addClass('active');
- },
- touchend: function (e) {
- e.preventDefault();
- this.select();
- this.$element.focus();
- }
- };
- /* TYPEAHEAD PLUGIN DEFINITION
- * =========================== */
- var old = $.fn.typeahead;
- $.fn.typeahead = function (option) {
- var arg = arguments;
- if (typeof option == 'string' && option == 'getActive') {
- return this.data('active');
- }
- return this.each(function () {
- var $this = $(this);
- var data = $this.data('typeahead');
- var options = typeof option == 'object' && option;
- if (!data) {
- $this.data('typeahead', (data = new Typeahead(this, options)));
- }
- if (typeof option == 'string' && data[option]) {
- if (arg.length > 1) {
- data[option].apply(data, Array.prototype.slice.call(arg, 1));
- } else {
- data[option]();
- }
- }
- });
- };
- Typeahead.defaults = {
- source: [],
- items: 8,
- minLength: 1,
- scrollHeight: 0,
- autoSelect: true,
- afterSelect: $.noop,
- afterEmptySelect: $.noop,
- addItem: false,
- followLinkOnSelect: false,
- delay: 0,
- separator: 'category',
- changeInputOnSelect: true,
- changeInputOnMove: true,
- openLinkInNewTab: false,
- selectOnBlur: true,
- showCategoryHeader: true,
- theme: "bootstrap3",
- themes: {
- bootstrap3: {
- menu: '<ul class="typeahead dropdown-menu" role="listbox"></ul>',
- item: '<li><a class="dropdown-item" href="#" role="option"></a></li>',
- itemContentSelector: "a",
- headerHtml: '<li class="dropdown-header"></li>',
- headerDivider: '<li class="divider" role="separator"></li>'
- },
- bootstrap4: {
- menu: '<div class="typeahead dropdown-menu" role="listbox"></div>',
- item: '<button class="dropdown-item" role="option"></button>',
- itemContentSelector: '.dropdown-item',
- headerHtml: '<h6 class="dropdown-header"></h6>',
- headerDivider: '<div class="dropdown-divider"></div>'
- }
- }
- };
- $.fn.typeahead.Constructor = Typeahead;
- /* TYPEAHEAD NO CONFLICT
- * =================== */
- $.fn.typeahead.noConflict = function () {
- $.fn.typeahead = old;
- return this;
- };
- /* TYPEAHEAD DATA-API
- * ================== */
- $(document).on('focus.typeahead.data-api', '[data-provide="typeahead"]', function (e) {
- var $this = $(this);
- if ($this.data('typeahead')) {
- return;
- }
- $this.typeahead($this.data());
- });
- }));
|