|
@@ -0,0 +1,180 @@
|
|
|
|
+(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();
|
|
|
|
+ if (!!term && lastTerm !== term) {
|
|
|
|
+ let ep = $(elem).attr('stag-suggest-ep');
|
|
|
|
+ $.get(ep + '?term=' + $.trim(term), 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);
|
|
|
|
+ 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');
|
|
|
|
+ }
|
|
|
|
+ }, 250);
|
|
|
|
+
|
|
|
|
+ function handleKeydown(elem, e) {
|
|
|
|
+ let term = $.trim(elem.val());
|
|
|
|
+ let activeItem = suggestionsOuter.find('.suggest-item.active');
|
|
|
|
+ switch (e.which) {
|
|
|
|
+ case 27:
|
|
|
|
+ suggestionsOuter.addClass('d-none');
|
|
|
|
+ return false;
|
|
|
|
+ case 38:
|
|
|
|
+ if (activeItem.prev().length) {
|
|
|
|
+ activeItem.prev()
|
|
|
|
+ .addClass('active')
|
|
|
|
+ .siblings().removeClass('active');
|
|
|
|
+ activeItem = suggestionsOuter.find('.suggest-item.active');
|
|
|
|
+ if (activeItem.length) {
|
|
|
|
+ activeItem[0].scrollIntoView();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return false;
|
|
|
|
+ case 40:
|
|
|
|
+ if (activeItem.next().length) {
|
|
|
|
+ activeItem.next()
|
|
|
|
+ .addClass('active')
|
|
|
|
+ .siblings().removeClass('active');
|
|
|
|
+ activeItem = suggestionsOuter.find('.suggest-item.active');
|
|
|
|
+ if (activeItem.length) {
|
|
|
|
+ activeItem[0].scrollIntoView();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return false;
|
|
|
|
+ case 13:
|
|
|
|
+ if (activeItem.length) {
|
|
|
|
+ activeItem.first().click();
|
|
|
|
+ }
|
|
|
|
+ return false;
|
|
|
|
+ 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) {
|
|
|
|
+ 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('keydown.stag-suggest')
|
|
|
|
+ .on('keydown.stag-suggest', function (e) {
|
|
|
|
+ suggestionsOuter = $(this).next('.stag-suggestions-container').find('>.suggestions-outer');
|
|
|
|
+ return handleKeydown($(this), e);
|
|
|
|
+ })
|
|
|
|
+ .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('click', '.suggest-item.stag-suggest');
|
|
|
|
+ $(document).on('click', '.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]');
|
|
|
|
+ input.val(label);
|
|
|
|
+ input.data('suggest-data', data);
|
|
|
|
+ for(let x in data) {
|
|
|
|
+ if(data.hasOwnProperty(x)) {
|
|
|
|
+ input.attr('data-' + x, data[x]);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ input.trigger('input');
|
|
|
|
+ input.trigger('change');
|
|
|
|
+
|
|
|
|
+ input.trigger('stag-suggest-selected', [input, data]);
|
|
|
|
+
|
|
|
|
+ return false;
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ addMCInitializer('stag-suggest', window.initStagSuggest);
|
|
|
|
+})();
|