123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307 |
- (function () {
- window.initStagSuggest = function () {
- let suggestionsOuter = null;
- const debounce = (func, wait) => {
- let timeout;
- return function executedFunction(...args) {
- const later = () => {
- clearTimeout(timeout);
- func(...args);
- };
- clearTimeout(timeout);
- timeout = setTimeout(later, wait);
- };
- };
- var lastTerm = '';
- var returnedFunction = debounce(function (elem) {
- let term = elem.val();
- let ep = $(elem).attr('stag-suggest-ep'), extra = $(elem).attr('stag-suggest-extra');
- if(!!ep) { // remote lookup
- if (!!term && term.length >= 2) {
- $.get(ep + '?term=' + $.trim(term) + (!!extra ? '&' + extra : ''), function (_data) {
- /*
- expected return format:
- {
- success: true,
- data: [
- {
- x: ...,
- y: ...,
- text: ... // "text" key is "mandatory"
- },
- {
- x: ...,
- y: ...,
- text: ... // "text" key is "mandatory"
- },
- ...
- ]
- }
- */
- suggestionsOuter.empty();
- if(!hasResponseError(_data) && _data.data && _data.data.length) {
- for (let i = 0; i < _data.data.length; i++) {
- let item = $('<a native href="#" class="d-block suggest-item stag-suggest text-nowrap"/>');
- for(let x in _data.data[i]) {
- if(_data.data[i].hasOwnProperty(x) && x !== 'text') {
- item.attr('data-' + x, _data.data[i][x]);
- }
- }
- item.data('suggest-data', _data.data[i]);
- item.html(_data.data[i].text);
- if(_data.data[i].sub_text) {
- item.append($('<span/>')
- .addClass('ml-1 text-sm text-secondary')
- .append(' (')
- .append(_data.data[i].sub_text)
- .append(')')
- );
- }
- if(_data.data[i].text2) {
- item.append($('<div/>')
- .addClass('text-sm text-secondary')
- .html(_data.data[i].text2)
- );
- }
- if(_data.data[i].tooltip) {
- item.attr('title', _data.data[i].tooltip);
- }
- if(i === 0) {
- item.addClass('active');
- }
- suggestionsOuter.append(item);
- }
- }
- else {
- suggestionsOuter.html('<span class="d-block no-suggest-items">No matches!</span>');
- }
- suggestionsOuter.removeClass('d-none');
- }, 'json');
- lastTerm = term;
- } else {
- suggestionsOuter.addClass('d-none');
- }
- }
- else { // local lookup
- let optionList = $(elem).next().next('.data-option-list');
- if(optionList.length) {
- let matches = [];
- optionList.find('>div').each(function() {
- if(!term || $(this).text().toLowerCase().indexOf(term.toLowerCase()) !== -1) {
- matches.push($(this).text());
- }
- });
- suggestionsOuter.empty();
- if(matches.length) {
- for (let i = 0; i < matches.length; i++) {
- let item = $('<a native href="#" class="d-block suggest-item stag-suggest text-nowrap"/>');
- item.data('suggest-data', {});
- item.html(matches[i]);
- suggestionsOuter.append(item);
- }
- }
- else {
- suggestionsOuter.html('<span class="d-block no-suggest-items">No matches!</span>');
- }
- suggestionsOuter.removeClass('d-none');
- }
- }
- }, 250);
- function handleKeydown(elem, e) {
- let term = $.trim(elem.val());
- let activeItem = suggestionsOuter.find('.suggest-item.active');
- switch (e.which) {
- case 27:
- if(suggestionsOuter.is(':visible')) {
- suggestionsOuter.addClass('d-none');
- markEventAsConsumed(e);
- return false;
- }
- break;
- case 38:
- if(suggestionsOuter.is(':visible')) {
- if (activeItem.prev().length) {
- activeItem.prev()
- .addClass('active')
- .siblings().removeClass('active');
- activeItem = suggestionsOuter.find('.suggest-item.active');
- if (activeItem.length) {
- if(activeItem[0].scrollIntoViewIfNeeded) {
- activeItem[0].scrollIntoViewIfNeeded(false);
- }
- else {
- activeItem[0].scrollIntoView({
- behavior: "auto",
- block: "start",
- inline: "start"
- });
- }
- }
- }
- return false;
- }
- break;
- case 40:
- if(suggestionsOuter.is(':visible')) {
- if (activeItem.next().length) {
- activeItem.next()
- .addClass('active')
- .siblings().removeClass('active');
- activeItem = suggestionsOuter.find('.suggest-item.active');
- if (activeItem.length) {
- if(activeItem[0].scrollIntoViewIfNeeded) {
- activeItem[0].scrollIntoViewIfNeeded(false);
- }
- else {
- activeItem[0].scrollIntoView({
- behavior: "auto",
- block: "end",
- inline: "end"
- });
- }
- }
- }
- return false;
- }
- break;
- case 13:
- if(suggestionsOuter.is(':visible')) {
- if (activeItem.length) {
- activeItem.first().trigger('mousedown');
- }
- return false;
- }
- break;
- default:
- if (!!term) {
- suggestionsOuter
- .html('<span class="d-block no-suggest-items">Searching...</span>')
- .removeClass('d-none');
- returnedFunction(elem);
- } else {
- suggestionsOuter.addClass('d-none');
- }
- break;
- }
- }
- function handleKeypress(elem, e) {
- var term = $.trim(elem.val());
- if (!!term || !$(elem).is('[stag-suggest-ep]')) {
- suggestionsOuter
- .html('<span class="d-block no-suggest-items">Searching...</span>')
- .removeClass('d-none');
- returnedFunction(elem);
- } else {
- suggestionsOuter.addClass('d-none');
- }
- }
- $('[stag-suggest]:not([stag-suggest-initialized])').each(function () {
- let elem = $(this);
- elem.next('.stag-suggestions-container').remove();
- $('<div class="stag-suggestions-container position-relative">' +
- '<div class="suggestions-outer stag-suggestions position-absolute d-none"></div>' +
- '</div>').insertAfter(elem);
- elem
- .off('focus.stag-suggest')
- .on('focus.stag-suggest', function (e) {
- if(!$(this).is('[stag-suggest-ep]')) {
- suggestionsOuter = $(this).next('.stag-suggestions-container').find('>.suggestions-outer');
- return handleKeypress($(this), e);
- }
- })
- .off('keydown.stag-suggest')
- .on('keydown.stag-suggest', function (e) {
- suggestionsOuter = $(this).next('.stag-suggestions-container').find('>.suggestions-outer');
- return handleKeydown($(this), e);
- })
- .off('paste.stag-suggest')
- .on('paste.stag-suggest', function (e) {
- window.setTimeout(() => {
- suggestionsOuter = $(this).next('.stag-suggestions-container').find('>.suggestions-outer');
- return handleKeypress($(this), e);
- }, 100);
- })
- .off('keypress.stag-suggest')
- .on('keypress.stag-suggest', function (e) {
- suggestionsOuter = $(this).next('.stag-suggestions-container').find('>.suggestions-outer');
- return handleKeypress($(this), e);
- });
- $(this).attr('stag-suggest-initialized', 1);
- });
- // on auto-suggest selection
- $(document).off('mousedown.stag-suggest', '.suggest-item.stag-suggest');
- $(document).on('mousedown.stag-suggest', '.suggest-item.stag-suggest', function () {
- $('.suggestions-outer.stag-suggestions').addClass('d-none');
- let data = $(this).data('suggest-data'),
- label = $.trim($(this).text());
- // set value
- let input = $(this).closest('.position-relative').prev('[stag-suggest]');
- // if input is [stag-suggest-text-only] - use only the text bit in label
- if(input.is('[stag-suggest-text-only]')) {
- label = data.text;
- }
- input.val(label);
- input.data('suggest-data', data);
- let scope = input.attr('stag-suggest-scope');
- if(!scope) scope = 'form';
- scope = $(scope);
- for(let x in data) {
- if(data.hasOwnProperty(x)) {
- input.attr('data-' + x, data[x]);
- // auto-populate if there's a field matching data-name="x" in the scope
- if(scope.find('[data-name="' + x + '"]').length) {
- scope.find('[data-name="' + x + '"]').val(data[x]).trigger('change');
- }
- }
- }
- input.trigger('input');
- input.trigger('change');
- input.trigger('stag-suggest-selected', [input, data]);
- return false;
- });
- // outside click
- $(document)
- .off('mousedown.stag-suggest-outer-click')
- .on('mousedown.stag-suggest-outer-click', function (_e) {
- let elem = $(_e.target);
- // if mousedown is on the input whose stag-suggest is on display, ignore
- if(elem.is('[stag-suggest]') && elem.next('.stag-suggestions-container').find('>.suggestions-outer').is(':visible')) {
- return false;
- }
- if(!elem.is('.stag-suggestions-container') && !elem.closest('.stag-suggestions-container').length) {
- if($('.stag-suggestions-container .suggestions-outer:not(.d-none)').length) {
- $('.stag-suggestions-container .suggestions-outer').addClass('d-none');
- return false;
- }
- }
- });
- }
- addMCInitializer('stag-suggest', window.initStagSuggest);
- })();
|