diff --git a/dist/jquery.typeahead.min.js b/dist/jquery.typeahead.min.js index b4cf9f8..f42d44b 100644 --- a/dist/jquery.typeahead.min.js +++ b/dist/jquery.typeahead.min.js @@ -4,7 +4,7 @@ * Licensed under the MIT license * * @author Tom Bertrand - * @version 2.10.7 (2019-10-19) + * @version 2.10.7 (2019-10-22) * @link http://www.runningcoder.org/jquerytypeahead/ */ -!function(e){var t;"function"==typeof define&&define.amd?define("jquery-typeahead",["jquery"],function(t){return e(t)}):"object"==typeof module&&module.exports?module.exports=(void 0===t&&(t="undefined"!=typeof window?require("jquery"):require("jquery")(void 0)),e(t)):e(jQuery)}(function(j){"use strict";function r(t,e){this.rawQuery=t.val()||"",this.query=t.val()||"",this.selector=t[0].selector,this.deferred=null,this.tmpSource={},this.source={},this.dynamicGroups=[],this.hasDynamicGroups=!1,this.generatedGroupCount=0,this.groupBy="group",this.groups=[],this.searchGroups=[],this.generateGroups=[],this.requestGroups=[],this.result=[],this.tmpResult={},this.groupTemplate="",this.resultHtml=null,this.resultCount=0,this.resultCountPerGroup={},this.options=e,this.node=t,this.namespace="."+this.helper.slugify.call(this,this.selector)+".typeahead",this.isContentEditable=void 0!==this.node.attr("contenteditable")&&"false"!==this.node.attr("contenteditable"),this.container=null,this.resultContainer=null,this.item=null,this.items=null,this.comparedItems=null,this.xhr={},this.hintIndex=null,this.filters={dropdown:{},dynamic:{}},this.dropdownFilter={static:[],dynamic:[]},this.dropdownFilterAll=null,this.isDropdownEvent=!1,this.requests={},this.backdrop={},this.hint={},this.label={},this.hasDragged=!1,this.focusOnly=!1,this.displayEmptyTemplate,this.__construct()}var i,s={input:null,minLength:2,maxLength:!(window.Typeahead={version:"2.10.7"}),maxItem:8,dynamic:!1,delay:300,order:null,offset:!1,hint:!1,accent:!1,highlight:!0,multiselect:null,group:!1,groupOrder:null,maxItemPerGroup:null,dropdownFilter:!1,dynamicFilter:null,backdrop:!1,backdropOnFocus:!1,cache:!1,ttl:36e5,compression:!1,searchOnFocus:!1,blurOnTab:!0,resultContainer:null,generateOnLoad:null,mustSelectItem:!1,href:null,display:["display"],template:null,templateValue:null,groupTemplate:null,correlativeTemplate:!1,emptyTemplate:!1,cancelButton:!0,loadingAnimation:!0,filter:!0,matcher:null,source:null,callback:{onInit:null,onReady:null,onShowLayout:null,onHideLayout:null,onSearch:null,onResult:null,onLayoutBuiltBefore:null,onLayoutBuiltAfter:null,onNavigateBefore:null,onNavigateAfter:null,onEnter:null,onLeave:null,onClickBefore:null,onClickAfter:null,onDropdownFilter:null,onSendRequest:null,onReceiveRequest:null,onPopulateSource:null,onCacheSave:null,onSubmit:null,onCancel:null},selector:{container:"typeahead__container",result:"typeahead__result",list:"typeahead__list",group:"typeahead__group",item:"typeahead__item",empty:"typeahead__empty",display:"typeahead__display",query:"typeahead__query",filter:"typeahead__filter",filterButton:"typeahead__filter-button",dropdown:"typeahead__dropdown",dropdownItem:"typeahead__dropdown-item",labelContainer:"typeahead__label-container",label:"typeahead__label",button:"typeahead__button",backdrop:"typeahead__backdrop",hint:"typeahead__hint",cancelButton:"typeahead__cancel-button"},debug:!1},o={from:"ãàáäâẽèéëêìíïîõòóöôùúüûñç",to:"aaaaaeeeeeiiiiooooouuuunc"},n=~window.navigator.appVersion.indexOf("MSIE 9."),a=~window.navigator.appVersion.indexOf("MSIE 10"),l=!!~window.navigator.userAgent.indexOf("Trident")&&~window.navigator.userAgent.indexOf("rv:11");r.prototype={_validateCacheMethod:function(t){var e;if(!0===t)t="localStorage";else if("string"==typeof t&&!~["localStorage","sessionStorage"].indexOf(t))return!1;e=void 0!==window[t];try{window[t].setItem("typeahead","typeahead"),window[t].removeItem("typeahead")}catch(t){e=!1}return e&&t||!1},extendOptions:function(){if(this.options.cache=this._validateCacheMethod(this.options.cache),this.options.compression&&("object"==typeof LZString&&this.options.cache||(this.options.compression=!1)),this.options.maxLength&&!isNaN(this.options.maxLength)||(this.options.maxLength=1/0),void 0!==this.options.maxItem&&~[0,!1].indexOf(this.options.maxItem)&&(this.options.maxItem=1/0),this.options.maxItemPerGroup&&!/^\d+$/.test(this.options.maxItemPerGroup)&&(this.options.maxItemPerGroup=null),this.options.display&&!Array.isArray(this.options.display)&&(this.options.display=[this.options.display]),this.options.multiselect&&(this.items=[],this.comparedItems=[],"string"==typeof this.options.multiselect.matchOn&&(this.options.multiselect.matchOn=[this.options.multiselect.matchOn])),this.options.group&&(Array.isArray(this.options.group)||("string"==typeof this.options.group?this.options.group={key:this.options.group}:"boolean"==typeof this.options.group&&(this.options.group={key:"group"}),this.options.group.key=this.options.group.key||"group")),this.options.highlight&&!~["any",!0].indexOf(this.options.highlight)&&(this.options.highlight=!1),this.options.dropdownFilter&&this.options.dropdownFilter instanceof Object){Array.isArray(this.options.dropdownFilter)||(this.options.dropdownFilter=[this.options.dropdownFilter]);for(var t=0,e=this.options.dropdownFilter.length;te.maxLength&&(e.minLength=e.maxLength),this.options.source[t]=e,this.options.source[t].dynamic&&this.dynamicGroups.push(t),e.cache=void 0!==e.cache?this._validateCacheMethod(e.cache):this.options.cache,e.compression&&("object"==typeof LZString&&e.cache||(e.compression=!1))}return this.hasDynamicGroups=this.options.dynamic||!!this.dynamicGroups.length,!0},init:function(){this.helper.executeCallback.call(this,this.options.callback.onInit,[this.node]),this.container=this.node.closest("."+this.options.selector.container)},delegateEvents:function(){var i=this,t=["focus"+this.namespace,"input"+this.namespace,"propertychange"+this.namespace,"keydown"+this.namespace,"keyup"+this.namespace,"search"+this.namespace,"generate"+this.namespace];j("html").on("touchmove",function(){i.hasDragged=!0}).on("touchstart",function(){i.hasDragged=!1}),this.node.closest("form").on("submit",function(t){if(!i.options.mustSelectItem||!i.helper.isEmpty(i.item))return i.options.backdropOnFocus||i.hideLayout(),i.options.callback.onSubmit?i.helper.executeCallback.call(i,i.options.callback.onSubmit,[i.node,this,i.item||i.items,t]):void 0;t.preventDefault()}).on("reset",function(){setTimeout(function(){i.node.trigger("input"+i.namespace),i.hideLayout()})});var s=!1;if(this.node.attr("placeholder")&&(a||l)){var e=!0;this.node.on("focusin focusout",function(){e=!(this.value||!this.placeholder)}),this.node.on("input",function(t){e&&(t.stopImmediatePropagation(),e=!1)})}this.node.off(this.namespace).on(t.join(" "),function(t,e){switch(t.type){case"generate":i.generateSource(Object.keys(i.options.source));break;case"focus":if(i.focusOnly){i.focusOnly=!1;break}i.options.backdropOnFocus&&(i.buildBackdropLayout(),i.showLayout()),i.options.searchOnFocus&&!i.item&&(i.deferred=j.Deferred(),i.assignQuery(),i.generateSource());break;case"keydown":8===t.keyCode&&i.options.multiselect&&i.options.multiselect.cancelOnBackspace&&""===i.query&&i.items.length?i.cancelMultiselectItem(i.items.length-1,null,t):t.keyCode&&~[9,13,27,38,39,40].indexOf(t.keyCode)&&(s=!0,i.navigate(t));break;case"keyup":n&&i.node[0].value.replace(/^\s+/,"").toString().length=this.options.source[t].minLength&&this.query.length<=this.options.source[t].maxLength){if(this.filters.dropdown&&"group"===this.filters.dropdown.key&&this.filters.dropdown.value!==t)continue;if(this.searchGroups.push(t),!this.options.source[t].dynamic&&this.source[t])continue;this.generateGroups.push(t)}},generateSource:function(t){if(this.filterGenerateSource(),Array.isArray(t)&&t.length)this.generateGroups=t;else if(!this.generateGroups.length)return void this.node.trigger("search"+this.namespace);if(this.requestGroups=[],this.generatedGroupCount=0,this.options.loadingAnimation&&this.container.addClass("loading"),!this.helper.isEmpty(this.xhr)){for(var e in this.xhr)this.xhr.hasOwnProperty(e)&&this.xhr[e].abort();this.xhr={}}for(var i,s,o,n,r,a,l,h=this,c=(e=0,this.generateGroups.length);e(new Date).getTime()?(this.populateSource(a.data,i),l=!0):window[n].removeItem("TYPEAHEAD_"+this.selector+":"+i)}catch(t){}if(l)continue}!o.data||o.ajax?o.ajax&&(this.requests[i]||(this.requests[i]=this.generateRequestObject(i)),this.requestGroups.push(i)):"function"==typeof o.data?(s=o.data.call(this),Array.isArray(s)?h.populateSource(s,i):"function"==typeof s.promise&&function(e){j.when(s).then(function(t){t&&Array.isArray(t)&&h.populateSource(t,e)})}(i)):this.populateSource(j.extend(!0,[],o.data),i)}return this.requestGroups.length&&this.handleRequests(),!!this.generateGroups.length},generateRequestObject:function(s){var o=this,n=this.options.source[s],t={request:{url:n.ajax.url||null,dataType:"json",beforeSend:function(t,e){o.xhr[s]=t;var i=o.requests[s].callback.beforeSend||n.ajax.beforeSend;"function"==typeof i&&i.apply(null,arguments)}},callback:{beforeSend:null,done:null,fail:null,then:null,always:null},extra:{path:n.ajax.path||null,group:s},validForGroup:[s]};if("function"!=typeof n.ajax&&(n.ajax instanceof Object&&(t=this.extendXhrObject(t,n.ajax)),1/g," ").replace(/\s{2,}/," ").trim();for(l=0,h=i.length;l").html(g.replace(/\{\{([\w\-\.]+)(?:\|(\w+))?}}/g,function(t,e){return s.helper.namespace.call(s,e,i[l],"get","")}).trim()).text();o.display?~o.display.indexOf("compiled")||o.display.unshift("compiled"):~this.options.display.indexOf("compiled")||this.options.display.unshift("compiled")}else;}this.options.callback.onPopulateSource&&(i=this.helper.executeCallback.call(this,this.options.callback.onPopulateSource,[this.node,i,t,e])),this.tmpSource[t]=Array.isArray(i)&&i||[];var y=this.options.source[t].cache,v=this.options.source[t].compression,b=this.options.source[t].ttl||this.options.ttl;if(y&&!window[y].getItem("TYPEAHEAD_"+this.selector+":"+t)){this.options.callback.onCacheSave&&(i=this.helper.executeCallback.call(this,this.options.callback.onCacheSave,[this.node,i,t,e]));var k=JSON.stringify({data:i,ttl:(new Date).getTime()+b});v&&(k=LZString.compressToUTF16(k)),window[y].setItem("TYPEAHEAD_"+this.selector+":"+t,k)}this.incrementGeneratedGroup()},incrementGeneratedGroup:function(){if(this.generatedGroupCount++,this.generatedGroupCount===this.generateGroups.length){this.xhr={};for(var t=0,e=this.generateGroups.length;t=this.query.length&&i.filter('[data-index="'+this.hintIndex+'"]').find("a:first")[0].click()}},getTemplateValue:function(i){if(i){var t=i.group&&this.options.source[i.group].templateValue||this.options.templateValue;if("function"==typeof t&&(t=t.call(this)),!t)return this.helper.namespace.call(this,i.matchedKey,i).toString();var s=this;return t.replace(/\{\{([\w\-.]+)}}/gi,function(t,e){return s.helper.namespace.call(s,e,i,"get","")})}},clearActiveItem:function(){this.resultContainer.find("."+this.options.selector.item).removeClass("active")},addActiveItem:function(t){t.addClass("active")},searchResult:function(){this.resetLayout(),!1!==this.helper.executeCallback.call(this,this.options.callback.onSearch,[this.node,this.query])&&(!this.searchGroups.length||this.options.multiselect&&this.options.multiselect.limit&&this.items.length>=this.options.multiselect.limit||this.searchResultData(),this.helper.executeCallback.call(this,this.options.callback.onResult,[this.node,this.query,this.result,this.resultCount,this.resultCountPerGroup]),this.isDropdownEvent&&(this.helper.executeCallback.call(this,this.options.callback.onDropdownFilter,[this.node,this.query,this.filters.dropdown,this.result]),this.isDropdownEvent=!1))},searchResultData:function(){var t,e,i,s,o,n,r,a,l,h,c,p=this.groupBy,u=null,d=this.query.toLowerCase(),f=this.options.maxItem,m=this.options.maxItemPerGroup,g=this.filters.dynamic&&!this.helper.isEmpty(this.filters.dynamic),y="function"==typeof this.options.matcher&&this.options.matcher;this.options.accent&&(d=this.helper.removeAccent.call(this,d));for(var v=0,b=this.searchGroups.length;v=f)||this.options.callback.onResult);k++)if((!g||this.dynamicFilter.validate.apply(this,[this.source[F][k]]))&&null!==(t=this.source[F][k])&&"boolean"!=typeof t&&(!this.options.multiselect||this.isMultiselectUniqueData(t))&&(!this.filters.dropdown||(t[this.filters.dropdown.key]||"").toLowerCase()===(this.filters.dropdown.value||"").toLowerCase())){if((u="group"===p?F:t[p]?t[p]:t.group)&&!this.tmpResult[u]&&(this.tmpResult[u]=[],this.resultCountPerGroup[u]=0),m&&"group"===p&&this.tmpResult[u].length>=m&&!this.options.callback.onResult)break;for(var x=0,C=(S=this.options.source[F].display||this.options.display).length;x=m)break;this.tmpResult[u].push(j.extend(!0,{matchedKey:S[x]},t)),this.resultItemCount++}break}if(!this.options.callback.onResult){if(this.resultItemCount>=f)break;if(m&&this.tmpResult[u].length>=m&&"group"===p)break}}}if(this.options.order){var O,S=[];for(var F in this.tmpResult)if(this.tmpResult.hasOwnProperty(F)){for(v=0,b=this.tmpResult[F].length;v",{class:this.options.selector.result}),this.container.append(this.resultContainer)),!this.result.length)if(this.options.multiselect&&this.options.multiselect.limit&&this.items.length>=this.options.multiselect.limit)h=this.options.multiselect.limitTemplate?"function"==typeof this.options.multiselect.limitTemplate?this.options.multiselect.limitTemplate.call(this,this.query):this.options.multiselect.limitTemplate.replace(/\{\{query}}/gi,j("
").text(this.helper.cleanStringFromScript(this.query)).html()):"Can't select more than "+this.items.length+" items.";else{if(!this.options.emptyTemplate||""===this.query)return;h="function"==typeof this.options.emptyTemplate?this.options.emptyTemplate.call(this,this.query):this.options.emptyTemplate.replace(/\{\{query}}/gi,j("
").text(this.helper.cleanStringFromScript(this.query)).html())}this.displayEmptyTemplate=!!h;var o=this.query.toLowerCase();this.options.accent&&(o=this.helper.removeAccent.call(this,o));var c=this,t=this.groupTemplate||"
    ",p=!1;this.groupTemplate?t=j(t.replace(/<([^>]+)>\{\{(.+?)}}<\/[^>]+>/g,function(t,e,i,s,o){var n="",r="group"===i?c.groups:[i];if(!c.result.length)return!0===p?"":(p=!0,"<"+e+' class="'+c.options.selector.empty+'">'+h+"");for(var a=0,l=r.length;a
      ";return n})):(t=j(t),this.result.length||t.append(h instanceof j?h:'
    • '+h+"
    • ")),t.addClass(this.options.selector.list+(this.helper.isEmpty(this.result)?" empty":""));for(var e,i,n,s,r,a,l,u,d,f,m,g,y,v=this.groupTemplate&&this.result.length&&c.groups||[],b=0,k=this.result.length;b",{class:c.options.selector.group,html:j("",{href:"javascript:;",html:i||e,tabindex:-1}),"data-search-group":e}))),this.groupTemplate&&v.length&&~(m=v.indexOf(e||n.group))&&v.splice(m,1),r=j("
    • ",{class:c.options.selector.item+" "+c.options.selector.group+"-"+this.helper.slugify.call(this,e),disabled:!!n.disabled,"data-group":e,"data-index":b,html:j("",{href:s&&!n.disabled?(g=s,y=n,y.href=c.generateHref.call(c,g,y)):"javascript:;",html:function(){if(a=n.group&&c.options.source[n.group].template||c.options.template)"function"==typeof a&&(a=a.call(c,c.query,n)),l=a.replace(/\{\{([^\|}]+)(?:\|([^}]+))*}}/gi,function(t,e,i){var s=c.helper.cleanStringFromScript(String(c.helper.namespace.call(c,e,n,"get","")));return~(i=i&&i.split("|")||[]).indexOf("slugify")&&(s=c.helper.slugify.call(c,s)),~i.indexOf("raw")||!0===c.options.highlight&&o&&~d.indexOf(e)&&(s=c.helper.highlight.call(c,s,o.split(" "),c.options.accent)),s});else{for(var t=0,e=d.length;t'+c.helper.cleanStringFromScript(String(u.join(" ")))+""}(!0===c.options.highlight&&o&&!a||"any"===c.options.highlight)&&(l=c.helper.highlight.call(c,l,o.split(" "),c.options.accent)),j(this).append(l)}})}),function(t,i,e){e.on("click",function(t,e){i.disabled?t.preventDefault():(e&&"object"==typeof e&&(t.originalEvent=e),c.options.mustSelectItem&&c.helper.isEmpty(i)?t.preventDefault():(c.options.multiselect||(c.item=i),!1!==c.helper.executeCallback.call(c,c.options.callback.onClickBefore,[c.node,j(this),i,t])&&(t.originalEvent&&t.originalEvent.defaultPrevented||t.isDefaultPrevented()||(c.options.multiselect?(c.query=c.rawQuery="",c.addMultiselectItemLayout(i)):(c.focusOnly=!0,c.query=c.rawQuery=c.getTemplateValue.call(c,i),c.isContentEditable&&(c.node.text(c.query),c.helper.setCaretAtEnd(c.node[0]))),c.hideLayout(),c.node.val(c.query).focus(),c.options.cancelButton&&c.toggleCancelButtonVisibility(),c.helper.executeCallback.call(c,c.options.callback.onClickAfter,[c.node,j(this),i,t])))))}),e.on("mouseenter",function(t){i.disabled||(c.clearActiveItem(),c.addActiveItem(j(this))),c.helper.executeCallback.call(c,c.options.callback.onEnter,[c.node,j(this),i,t])}),e.on("mouseleave",function(t){i.disabled||c.clearActiveItem(),c.helper.executeCallback.call(c,c.options.callback.onLeave,[c.node,j(this),i,t])})}(0,n,r),(this.groupTemplate?t.find('[data-group-template="'+e+'"] ul'):t).append(r);if(this.result.length&&v.length)for(b=0,k=v.length;b",{class:this.options.selector.backdrop,css:this.backdrop.css}).insertAfter(this.container)),this.container.addClass("backdrop").css({"z-index":this.backdrop.css["z-index"]+1,position:"relative"}))},buildHintLayout:function(t){if(this.options.hint)if(this.node[0].scrollWidth>Math.ceil(this.node.innerWidth()))this.hint.container&&this.hint.container.val("");else{var e=this,i="",s=(t=t||this.result,this.query.toLowerCase());if(this.options.accent&&(s=this.helper.removeAccent.call(this,s)),this.hintIndex=null,this.searchGroups.length){if(this.hint.container||(this.hint.css=j.extend({"border-color":"transparent",position:"absolute",top:0,display:"inline","z-index":-1,float:"none",color:"silver","box-shadow":"none",cursor:"default","-webkit-user-select":"none","-moz-user-select":"none","-ms-user-select":"none","user-select":"none"},this.options.hint),this.hint.container=j("<"+this.node[0].nodeName+"/>",{type:this.node.attr("type"),class:this.node.attr("class"),readonly:!0,unselectable:"on","aria-hidden":"true",tabindex:-1,click:function(){e.node.focus()}}).addClass(this.options.selector.hint).css(this.hint.css).insertAfter(this.node),this.node.parent().css({position:"relative"})),this.hint.container.css("color",this.hint.css.color),s)for(var o,n,r,a=0,l=t.length;a",{class:this.options.selector.filter,html:function(){j(this).append(j(" +
    • +
      + + + + + + + + + diff --git a/src/jquery.typeahead.js b/src/jquery.typeahead.js index 4b2e342..1b27c5f 100644 --- a/src/jquery.typeahead.js +++ b/src/jquery.typeahead.js @@ -4,7 +4,7 @@ * Licensed under the MIT license * * @author Tom Bertrand - * @version 2.10.7 (2019-10-19) + * @version 2.10.7 (2019-10-22) * @link http://www.runningcoder.org/jquerytypeahead/ */ (function (factory) { @@ -75,6 +75,7 @@ emptyTemplate: false, // Display an empty template if no result cancelButton: true, // If text is detected in the input, a cancel button will be available to reset the input (pressing ESC also cancels) loadingAnimation: true, // Display a loading animation when typeahead is doing request / searching for results + asyncResult: false, // If set to true, the search results will be displayed as they are beging received from the requests / async data function filter: true, // Set to false or function to bypass Typeahead filtering. WARNING: accent, correlativeTemplate, offset & matcher will not be interpreted matcher: null, // Add an extra filtering function after the typeahead functions source: null, // Source of data for Typeahead to filter @@ -218,7 +219,7 @@ this.label = {}; // The label object this.hasDragged = false; // Will cancel mouseend events if true this.focusOnly = false; // Focus the input preventing any operations - this.displayEmptyTemplate // Display the empty template in the result list + this.displayEmptyTemplate; // Display the empty template in the result list this.__construct(); }; @@ -832,6 +833,9 @@ generateSource: function (generateGroups) { this.filterGenerateSource(); + + this.generatedGroupCount = 0; + if (Array.isArray(generateGroups) && generateGroups.length) { this.generateGroups = generateGroups; } else if (!this.generateGroups.length) { @@ -840,7 +844,6 @@ } this.requestGroups = []; - this.generatedGroupCount = 0; this.options.loadingAnimation && this.container.addClass("loading"); if (!this.helper.isEmpty(this.xhr)) { @@ -866,6 +869,10 @@ cache = groupSource.cache; compression = groupSource.compression; + if (this.options.asyncResult) { + delete this.source[group]; + } + if (cache) { dataInStorage = window[cache].getItem( "TYPEAHEAD_" + this.selector + ":" + group @@ -941,6 +948,10 @@ this.handleRequests(); } + if (this.options.asyncResult && this.searchGroups.length !== this.generateGroups) { + this.node.trigger("search" + this.namespace); + } + return !!this.generateGroups.length; }, @@ -1507,28 +1518,33 @@ ); } - this.incrementGeneratedGroup(); + this.incrementGeneratedGroup(group); }, - incrementGeneratedGroup: function () { + incrementGeneratedGroup: function (group) { this.generatedGroupCount++; - if (this.generatedGroupCount !== this.generateGroups.length) { + if (this.generatedGroupCount !== this.generateGroups.length && !this.options.asyncResult) { return; } - this.xhr = {}; + if (this.xhr && this.xhr[group]) { + delete this.xhr[group]; + } for (var i = 0, ii = this.generateGroups.length; i < ii; i++) { this.source[this.generateGroups[i]] = this.tmpSource[ this.generateGroups[i] - ]; + ]; } if (!this.hasDynamicGroups) { this.buildDropdownItemLayout("dynamic"); } - this.options.loadingAnimation && this.container.removeClass("loading"); + if (this.generatedGroupCount === this.generateGroups.length) { + this.xhr = {}; + this.options.loadingAnimation && this.container.removeClass("loading"); + } this.node.trigger("search" + this.namespace); }, @@ -1849,6 +1865,8 @@ this.options.source[group].matcher) || matcher; + if (!this.source[group]) continue; + for (var k = 0, kk = this.source[group].length; k < kk; k++) { if (this.resultItemCount >= maxItem && !this.options.callback.onResult) break; if (hasDynamicFilters && !this.dynamicFilter.validate.apply(this, [this.source[group][k]])) continue; @@ -2144,7 +2162,7 @@ } var emptyTemplate; - if (!this.result.length) { + if (!this.result.length && this.generatedGroupCount === this.generateGroups.length) { if ( this.options.multiselect && this.options.multiselect.limit && diff --git a/test/jestSetupFile.js b/test/jestSetupFile.js index 9570593..863cd5c 100644 --- a/test/jestSetupFile.js +++ b/test/jestSetupFile.js @@ -1,4 +1,4 @@ -import sinon from 'sinon'; +import sinon from "sinon"; /** * Mocking legacy code global objects/functions required for our @@ -7,62 +7,229 @@ import sinon from 'sinon'; const fakeServer = sinon.fakeServer.create(); -fakeServer.respondWith('GET', /\/game\.json/, [ - 200, - { 'Content-Type': 'application/json' }, - JSON.stringify({ - "data":[{"id":"1","display":"the last stand"},{"id":"5","display":"portal"},{"id":"8","display":"shift 4"},{"id":"9","display":"storm the house 3"},{"id":"10","display":"palisade guardian"},{"id":"11","display":"super mafia land"},{"id":"12","display":"rage 3"},{"id":"14","display":"snowboard slope"},{"id":"16","display":"last touchdown"},{"id":"17","display":"tuberacer"},{"id":"18","display":"canyon shooter"},{"id":"19","display":"pyro"},{"id":"22","display":"bubble tanks 2"},{"id":"24","display":"protector III"},{"id":"25","display":"crush the castle"},{"id":"27","display":"power tank"},{"id":"28","display":"amateur surgeon"},{"id":"29","display":"medieval golf"},{"id":"31","display":"break the wall"},{"id":"33","display":"rihanna's revenge"},{"id":"34","display":"aero acrobat"},{"id":"35","display":"bar b que"},{"id":"36","display":"bombardment"},{"id":"38","display":"gaps solitaire"},{"id":"39","display":"gold panic"},{"id":"40","display":"golf solitaire"},{"id":"41","display":"hex mines"},{"id":"42","display":"jet fighter"},{"id":"45","display":"pizza pronto"},{"id":"46","display":"the primitive"},{"id":"47","display":"sea of fire"},{"id":"48","display":"sea of fire 2"},{"id":"49","display":"the curve ball"},{"id":"50","display":"multitask"},{"id":"51","display":"retro rally"},{"id":"52","display":"helicopter"},{"id":"53","display":"dogfight"},{"id":"54","display":"presidential paintball"},{"id":"56","display":"deluxe pool"},{"id":"57","display":"bubble trouble"},{"id":"58","display":"black knight"},{"id":"59","display":"bloxorz"},{"id":"60","display":"age of war"},{"id":"62","display":"warzone tower defense"},{"id":"63","display":"doom hexen heretic"},{"id":"64","display":"epic war 2"},{"id":"65","display":"warfare 1917"},{"id":"66","display":"paladin"},{"id":"67","display":"sinmark"},{"id":"68","display":"battleships"},{"id":"69","display":"governor of poker"},{"id":"70","display":"juggerdome"},{"id":"71","display":"gemcraft chapter 0"},{"id":"72","display":"free rider 2"},{"id":"73","display":"sandstorm racing"},{"id":"74","display":"goal in one"},{"id":"75","display":"heat rush"},{"id":"76","display":"deal or no deal"},{"id":"77","display":"sift heads 5"},{"id":"78","display":"zombie baseball 2"},{"id":"79","display":"crow in hell 3"},{"id":"80","display":"extreme pamplona"},{"id":"81","display":"laserworx"},{"id":"83","display":"canyon defense"},{"id":"84","display":"boxhead 2"},{"id":"85","display":"war machine"},{"id":"86","display":"icycle"},{"id":"87","display":"diesel and death"},{"id":"88","display":"jungle truck"},{"id":"89","display":"ragdoll avalanche 2"},{"id":"90","display":"ragdoll master"},{"id":"91","display":"field command 2"},{"id":"92","display":"invasion 3"},{"id":"93","display":"kanye vs taylor"},{"id":"94","display":"the last stand 2"},{"id":"95","display":"epsilon"},{"id":"96","display":"race to kill"},{"id":"97","display":"arsenal 2"}] - }), -]) +fakeServer.respondWith("GET", /\/game\.json/, [ + 200, + { "Content-Type": "application/json" }, + JSON.stringify({ + data: [ + { id: "1", display: "the last stand" }, + { id: "5", display: "portal" }, + { id: "8", display: "shift 4" }, + { id: "9", display: "storm the house 3" }, + { id: "10", display: "palisade guardian" }, + { id: "11", display: "super mafia land" }, + { id: "12", display: "rage 3" }, + { id: "14", display: "snowboard slope" }, + { id: "16", display: "last touchdown" }, + { id: "17", display: "tuberacer" }, + { id: "18", display: "canyon shooter" }, + { id: "19", display: "pyro" }, + { id: "22", display: "bubble tanks 2" }, + { id: "24", display: "protector III" }, + { id: "25", display: "crush the castle" }, + { id: "27", display: "power tank" }, + { id: "28", display: "amateur surgeon" }, + { id: "29", display: "medieval golf" }, + { id: "31", display: "break the wall" }, + { id: "33", display: "rihanna's revenge" }, + { id: "34", display: "aero acrobat" }, + { id: "35", display: "bar b que" }, + { id: "36", display: "bombardment" }, + { id: "38", display: "gaps solitaire" }, + { id: "39", display: "gold panic" }, + { id: "40", display: "golf solitaire" }, + { id: "41", display: "hex mines" }, + { id: "42", display: "jet fighter" }, + { id: "45", display: "pizza pronto" }, + { id: "46", display: "the primitive" }, + { id: "47", display: "sea of fire" }, + { id: "48", display: "sea of fire 2" }, + { id: "49", display: "the curve ball" }, + { id: "50", display: "multitask" }, + { id: "51", display: "retro rally" }, + { id: "52", display: "helicopter" }, + { id: "53", display: "dogfight" }, + { id: "54", display: "presidential paintball" }, + { id: "56", display: "deluxe pool" }, + { id: "57", display: "bubble trouble" }, + { id: "58", display: "black knight" }, + { id: "59", display: "bloxorz" }, + { id: "60", display: "age of war" }, + { id: "62", display: "warzone tower defense" }, + { id: "63", display: "doom hexen heretic" }, + { id: "64", display: "epic war 2" }, + { id: "65", display: "warfare 1917" }, + { id: "66", display: "paladin" }, + { id: "67", display: "sinmark" }, + { id: "68", display: "battleships" }, + { id: "69", display: "governor of poker" }, + { id: "70", display: "juggerdome" }, + { id: "71", display: "gemcraft chapter 0" }, + { id: "72", display: "free rider 2" }, + { id: "73", display: "sandstorm racing" }, + { id: "74", display: "goal in one" }, + { id: "75", display: "heat rush" }, + { id: "76", display: "deal or no deal" }, + { id: "77", display: "sift heads 5" }, + { id: "78", display: "zombie baseball 2" }, + { id: "79", display: "crow in hell 3" }, + { id: "80", display: "extreme pamplona" }, + { id: "81", display: "laserworx" }, + { id: "83", display: "canyon defense" }, + { id: "84", display: "boxhead 2" }, + { id: "85", display: "war machine" }, + { id: "86", display: "icycle" }, + { id: "87", display: "diesel and death" }, + { id: "88", display: "jungle truck" }, + { id: "89", display: "ragdoll avalanche 2" }, + { id: "90", display: "ragdoll master" }, + { id: "91", display: "field command 2" }, + { id: "92", display: "invasion 3" }, + { id: "93", display: "kanye vs taylor" }, + { id: "94", display: "the last stand 2" }, + { id: "95", display: "epsilon" }, + { id: "96", display: "race to kill" }, + { id: "97", display: "arsenal 2" } + ] + }) +]); -fakeServer.respondWith('GET', /\/category\.json/, [ - 200, - { 'Content-Type': 'application/json' }, - JSON.stringify({ - "data":[{"id":"1","display":"sport"},{"id":"2","display":"puzzle"},{"id":"3","display":"shooting"},{"id":"4","display":"racing"},{"id":"5","display":"dress up"},{"id":"6","display":"classic"},{"id":"7","display":"action"},{"id":"8","display":"kid"},{"id":"9","display":"funny"},{"id":"10","display":"NSFW"},{"id":"11","display":"card"},{"id":"12","display":"strategy"},{"id":"13","display":"skill"},{"id":"14","display":"tower defense"},{"id":"15","display":"nintendo"},{"id":"16","display":"girl"},{"id":"17","display":"video"},{"id":"18","display":"multiplayer"},{"id":"19","display":"terrorism"},{"id":"21","display":"halloween"}] - }), +fakeServer.respondWith("GET", /\/category\.json/, [ + 200, + { "Content-Type": "application/json" }, + JSON.stringify({ + data: [ + { id: "1", display: "sport" }, + { id: "2", display: "puzzle" }, + { id: "3", display: "shooting" }, + { id: "4", display: "racing" }, + { id: "5", display: "dress up" }, + { id: "6", display: "classic" }, + { id: "7", display: "action" }, + { id: "8", display: "kid" }, + { id: "9", display: "funny" }, + { id: "10", display: "NSFW" }, + { id: "11", display: "card" }, + { id: "12", display: "strategy" }, + { id: "13", display: "skill" }, + { id: "14", display: "tower defense" }, + { id: "15", display: "nintendo" }, + { id: "16", display: "girl" }, + { id: "17", display: "video" }, + { id: "18", display: "multiplayer" }, + { id: "19", display: "terrorism" }, + { id: "21", display: "halloween" } + ] + }) ]); -fakeServer.respondWith('GET', /\/tag\.json/, [ - 200, - { 'Content-Type': 'application/json' }, - JSON.stringify({ - "data":[{"id":"1","display":"zombie"},{"id":"2","display":"classic"},{"id":"3","display":"motorcycle"},{"id":"4","display":"war"},{"id":"5","display":"castle"},{"id":"6","display":"hitman"},{"id":"7","display":"stickman"},{"id":"8","display":"celebrity"},{"id":"9","display":"tv show"},{"id":"10","display":"bicycle"},{"id":"11","display":"ice"},{"id":"12","display":"restaurant"},{"id":"13","display":"bomb"},{"id":"14","display":"platform"},{"id":"15","display":"bow"},{"id":"16","display":"parachute"},{"id":"17","display":"soccer"},{"id":"18","display":"winter"},{"id":"19","display":"space"},{"id":"20","display":"pool"},{"id":"21","display":"american football"},{"id":"22","display":"snowboard"},{"id":"23","display":"arena"},{"id":"24","display":"laser"},{"id":"25","display":"ship"},{"id":"26","display":"tank"},{"id":"27","display":"bridge"},{"id":"28","display":"desert"},{"id":"29","display":"battlefield"},{"id":"30","display":"gem"},{"id":"31","display":"turret"},{"id":"32","display":"fire"},{"id":"33","display":"mage"},{"id":"34","display":"futuristic"},{"id":"35","display":"obstacle course"},{"id":"36","display":"plane"},{"id":"37","display":"helicopter"},{"id":"38","display":"ball"},{"id":"39","display":"hockey"},{"id":"40","display":"surgery"},{"id":"42","display":"balloon"},{"id":"43","display":"catapult"},{"id":"44","display":"monster"},{"id":"45","display":"car"},{"id":"46","display":"army"},{"id":"47","display":"jungle"},{"id":"48","display":"water"},{"id":"49","display":"box"},{"id":"50","display":"politics"},{"id":"51","display":"poker"},{"id":"52","display":"penguin"},{"id":"53","display":"sniper"},{"id":"54","display":"death"},{"id":"55","display":"role playing game (RPG)"},{"id":"56","display":"paintball"},{"id":"57","display":"snow"},{"id":"58","display":"jet"},{"id":"59","display":"golf"},{"id":"60","display":"swordman"},{"id":"61","display":"medieval"},{"id":"62","display":"monkey"},{"id":"63","display":"dart"},{"id":"64","display":"drawing"},{"id":"65","display":"dragon"},{"id":"66","display":"robot"},{"id":"67","display":"clothes"},{"id":"68","display":"cartoon"},{"id":"69","display":"japanese"},{"id":"70","display":"hotel"},{"id":"71","display":"escape"},{"id":"72","display":"cannon"},{"id":"73","display":"pirate"},{"id":"74","display":"word"},{"id":"75","display":"beach"},{"id":"76","display":"terrorism"},{"id":"77","display":"babes"},{"id":"78","display":"hunting"},{"id":"79","display":"super hero"}] - }), +fakeServer.respondWith("GET", /\/tag\.json/, [ + 200, + { "Content-Type": "application/json" }, + JSON.stringify({ + data: [ + { id: "1", display: "zombie" }, + { id: "2", display: "classic" }, + { id: "3", display: "motorcycle" }, + { id: "4", display: "war" }, + { id: "5", display: "castle" }, + { id: "6", display: "hitman" }, + { id: "7", display: "stickman" }, + { id: "8", display: "celebrity" }, + { id: "9", display: "tv show" }, + { id: "10", display: "bicycle" }, + { id: "11", display: "ice" }, + { id: "12", display: "restaurant" }, + { id: "13", display: "bomb" }, + { id: "14", display: "platform" }, + { id: "15", display: "bow" }, + { id: "16", display: "parachute" }, + { id: "17", display: "soccer" }, + { id: "18", display: "winter" }, + { id: "19", display: "space" }, + { id: "20", display: "pool" }, + { id: "21", display: "american football" }, + { id: "22", display: "snowboard" }, + { id: "23", display: "arena" }, + { id: "24", display: "laser" }, + { id: "25", display: "ship" }, + { id: "26", display: "tank" }, + { id: "27", display: "bridge" }, + { id: "28", display: "desert" }, + { id: "29", display: "battlefield" }, + { id: "30", display: "gem" }, + { id: "31", display: "turret" }, + { id: "32", display: "fire" }, + { id: "33", display: "mage" }, + { id: "34", display: "futuristic" }, + { id: "35", display: "obstacle course" }, + { id: "36", display: "plane" }, + { id: "37", display: "helicopter" }, + { id: "38", display: "ball" }, + { id: "39", display: "hockey" }, + { id: "40", display: "surgery" }, + { id: "42", display: "balloon" }, + { id: "43", display: "catapult" }, + { id: "44", display: "monster" }, + { id: "45", display: "car" }, + { id: "46", display: "army" }, + { id: "47", display: "jungle" }, + { id: "48", display: "water" }, + { id: "49", display: "box" }, + { id: "50", display: "politics" }, + { id: "51", display: "poker" }, + { id: "52", display: "penguin" }, + { id: "53", display: "sniper" }, + { id: "54", display: "death" }, + { id: "55", display: "role playing game (RPG)" }, + { id: "56", display: "paintball" }, + { id: "57", display: "snow" }, + { id: "58", display: "jet" }, + { id: "59", display: "golf" }, + { id: "60", display: "swordman" }, + { id: "61", display: "medieval" }, + { id: "62", display: "monkey" }, + { id: "63", display: "dart" }, + { id: "64", display: "drawing" }, + { id: "65", display: "dragon" }, + { id: "66", display: "robot" }, + { id: "67", display: "clothes" }, + { id: "68", display: "cartoon" }, + { id: "69", display: "japanese" }, + { id: "70", display: "hotel" }, + { id: "71", display: "escape" }, + { id: "72", display: "cannon" }, + { id: "73", display: "pirate" }, + { id: "74", display: "word" }, + { id: "75", display: "beach" }, + { id: "76", display: "terrorism" }, + { id: "77", display: "babes" }, + { id: "78", display: "hunting" }, + { id: "79", display: "super hero" } + ] + }) ]); -fakeServer.respondWith('GET', /\/null\.json/, [ - 200, - { 'Content-Type': 'application/json' }, - JSON.stringify(null), +fakeServer.respondWith("GET", /\/null\.json/, [ + 200, + { "Content-Type": "application/json" }, + JSON.stringify(null) ]); -fakeServer.respondWith('GET', /\/empty\.json/, [ - 200, - { 'Content-Type': 'application/json' }, - JSON.stringify([]), +fakeServer.respondWith("GET", /\/empty\.json/, [ + 200, + { "Content-Type": "application/json" }, + JSON.stringify([]) ]); -fakeServer.respondWith('GET', /\/groups\.json/, [ - 200, - { 'Content-Type': 'application/json' }, - JSON.stringify({ - "group1": [ - "group1-test1", - "group1-test2", - "group1-test3" - ], - "group2": [ - "group2-test1", - "group2-test2", - "group2-test3" - ], - "group3": [ - "group3-test1", - "group3-test2", - "group3-test3" - ] - }), +fakeServer.respondWith("GET", /\/groups\.json/, [ + 200, + { "Content-Type": "application/json" }, + JSON.stringify({ + group1: ["group1-item1", "group1-item2", "group1-item3"], + group2: ["group2-item1", "group2-item2", "group2-item3"], + group3: ["group3-item1", "group3-item2", "group3-item3"] + }) ]); fakeServer.autoRespond = true; diff --git a/test/option/asyncResult.test.js b/test/option/asyncResult.test.js new file mode 100644 index 0000000..8aa4b58 --- /dev/null +++ b/test/option/asyncResult.test.js @@ -0,0 +1,177 @@ +const $ = require("jquery"); +const Typeahead = require("../../src/jquery.typeahead"); +const sinon = require("sinon"); + +function sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); +} + +describe("Typeahead asyncResult option Tests", () => { + "use strict"; + + let myTypeahead; + + describe("when asyncResult has multiple static groups", () => { + beforeAll(() => { + document.body.innerHTML = ''; + + myTypeahead = $.typeahead({ + input: ".js-typeahead", + minLength: 0, + group: "category", + groupOrder: "desc", + display: "name", + asyncResult: true, + source: { + group1: { + data: [ + { id: "1", name: "group1-item1" }, + { id: "2", name: "group1-item2" }, + { id: "3", name: "group1-item3" } + ] + }, + group2: { + data: function() { + return [ + { id: "1", name: "group2-item1" }, + { id: "2", name: "group2-item2" }, + { id: "3", name: "group2-item3" } + ]; + } + } + } + }); + }); + + it("Should have 6 results", () => { + myTypeahead.node.val("").trigger("input"); + + expect(myTypeahead.result.length).toEqual(6); + }); + }); + + describe.only("when asyncResult has multiple static, dynamic and promised groups", () => { + beforeAll(() => { + const delayFakeServer = sinon.fakeServer.create(); + + delayFakeServer.respondWith("GET", /\/group3\.json/, [ + 200, + { "Content-Type": "application/json" }, + JSON.stringify([ + { id: "1", name: "group3-item1" }, + { id: "2", name: "group3-item2" }, + { id: "3", name: "group3-item3" } + ]) + ]); + + delayFakeServer.autoRespond = true; + delayFakeServer.autoRespondAfter = 1000; + + // document.body.innerHTML = ''; + document.body.innerHTML = `
      +
      +
      +
      + +
      +
      +
      +
      `; + + myTypeahead = $.typeahead({ + input: ".js-typeahead", + minLength: 0, + display: "name", + asyncResult: true, + maxItem: 9, + emptyTemplate: "no result for {{query}}", + source: { + group1: { + data: [ + { id: "1", name: "group1-item1" }, + { id: "2", name: "group1-item2" }, + { id: "3", name: "group1-item3" } + ] + }, + group2: { + data: () => { + const deferred = $.Deferred(); + const data = [ + { id: "1", name: "group2-item1" }, + { id: "2", name: "group2-item2" }, + { id: "3", name: "group2-item3" } + ]; + + setTimeout(() => { + deferred.resolve(data); + }, 2000); + + return deferred; + } + }, + group3: { + dynamic: true, + ajax: { + type: "GET", + url: "http://test.com/group3.json" + } + } + } + }); + }); + + it("Should display async results", done => { + myTypeahead.node.triggerHandler("input").done(async () => { + await sleep(10); + expect(myTypeahead.container.hasClass("loading")).toBe(true); + expect(myTypeahead.result.length).toEqual(3); + await sleep(1010); + expect(myTypeahead.container.hasClass("loading")).toBe(true); + expect(myTypeahead.result.length).toEqual(6); + await sleep(2010); + expect(myTypeahead.container.hasClass("loading")).toBe(false); + expect(myTypeahead.result.length).toEqual(9); + done(); + }); + }); + + it("Should display async results for dynamic request", done => { + myTypeahead.node + .val("item") + .triggerHandler("input") + .done(async () => { + await sleep(10); + expect(myTypeahead.container.hasClass("loading")).toBe(true); + expect(myTypeahead.result.length).toEqual(6); + await sleep(2010); + expect(myTypeahead.container.hasClass("loading")).toBe(false); + expect(myTypeahead.result.length).toEqual(9); + done(); + }); + }); + + it("Should display no async results for dynamic request", done => { + myTypeahead.node + .val("invalid") + .triggerHandler("input") + .done(async () => { + expect(myTypeahead.container.hasClass("loading")).toBe(true); + expect(myTypeahead.result.length).toEqual(0); + await sleep(2010); + expect(myTypeahead.container.hasClass("loading")).toBe(false); + expect( + myTypeahead.container.find("." + myTypeahead.options.selector.empty) + .length + ).toBe(1); + expect(myTypeahead.result.length).toEqual(0); + expect(myTypeahead.resultHtml.text()).toEqual( + "no result for invalid" + ); + done(); + }); + }); + }); +});