diff --git a/README.md b/README.md index 3b5a5b2..90fbe4a 100644 --- a/README.md +++ b/README.md @@ -51,13 +51,13 @@ autoComplete.js is a simple, pure vanilla Javascript library progressively desig `JS` ```html - + ``` `CSS` ```html - + ``` #### Package Manager diff --git a/dist/autoComplete.js b/dist/autoComplete.js index f28e2ac..355fff3 100644 --- a/dist/autoComplete.js +++ b/dist/autoComplete.js @@ -163,31 +163,8 @@ }; } - var configure = (function (ctx) { - var id = ctx.id, - name = ctx.name, - options = ctx.options, - resultsList = ctx.resultsList, - resultItem = ctx.resultItem; - for (var option in options) { - if (_typeof(options[option]) === "object") { - if (!ctx[option]) ctx[option] = {}; - for (var subOption in options[option]) { - ctx[option][subOption] = options[option][subOption]; - } - } else { - ctx[option] = options[option]; - } - } - ctx.selector = ctx.selector || "#" + name; - resultsList.destination = resultsList.destination || ctx.selector; - resultsList.id = resultsList.id || name + "_list_" + id; - resultItem.id = resultItem.id || name + "_result"; - ctx.input = typeof ctx.selector === "string" ? document.querySelector(ctx.selector) : ctx.selector(); - }); - var select$1 = function select(element) { - return typeof element === "string" ? document.querySelector(element) : element; + return typeof element === "string" ? document.querySelector(element) : element(); }; var create = function create(tag, options) { var el = typeof tag === "string" ? document.createElement(tag) : tag; @@ -198,7 +175,7 @@ } else if (key === "dest") { select$1(val[0]).insertAdjacentElement(val[1], el); } else if (key === "around") { - var ref = select$1(val); + var ref = val; ref.parentNode.insertBefore(el, ref); el.append(ref); if (ref.getAttribute("autofocus") != null) ref.focus(); @@ -237,6 +214,28 @@ })).outerHTML; }; + var configure = (function (ctx) { + var name = ctx.name, + options = ctx.options, + resultsList = ctx.resultsList, + resultItem = ctx.resultItem; + for (var option in options) { + if (_typeof(options[option]) === "object") { + if (!ctx[option]) ctx[option] = {}; + for (var subOption in options[option]) { + ctx[option][subOption] = options[option][subOption]; + } + } else { + ctx[option] = options[option]; + } + } + ctx.selector = ctx.selector || "#" + name; + resultsList.destination = resultsList.destination || ctx.selector; + resultsList.id = resultsList.id || name + "_list_" + ctx.id; + resultItem.id = resultItem.id || name + "_result"; + ctx.input = select$1(ctx.selector); + }); + var eventEmitter = (function (name, ctx) { ctx.input.dispatchEvent(new CustomEvent(name, { bubbles: true, @@ -301,18 +300,15 @@ }; var findMatches = function findMatches(query, ctx) { var data = ctx.data, - searchEngine = ctx.searchEngine, - diacritics = ctx.diacritics, - resultsList = ctx.resultsList, - resultItem = ctx.resultItem; + searchEngine = ctx.searchEngine; var matches = []; data.store.forEach(function (value, index) { var find = function find(key) { var record = key ? value[key] : value; var match = typeof searchEngine === "function" ? searchEngine(query, record) : search(query, record, { mode: searchEngine, - diacritics: diacritics, - highlight: resultItem.highlight + diacritics: ctx.diacritics, + highlight: ctx.resultItem.highlight }); if (!match) return; var result = { @@ -340,7 +336,7 @@ } }); if (data.filter) matches = data.filter(matches); - var results = matches.slice(0, resultsList.maxResults); + var results = matches.slice(0, ctx.resultsList.maxResults); ctx.feedback = { query: query, matches: matches, @@ -362,13 +358,12 @@ list = ctx.list, resultItem = ctx.resultItem, feedback = ctx.feedback; - feedback.query; - var matches = feedback.matches, + var matches = feedback.matches, results = feedback.results; ctx.cursor = -1; list.innerHTML = ""; if (matches.length || resultsList.noResults) { - var fragment = document.createDocumentFragment(); + var fragment = new DocumentFragment(); results.forEach(function (result, index) { var element = create(resultItem.tag, _objectSpread2({ id: "".concat(resultItem.id, "_").concat(index), @@ -403,9 +398,8 @@ eventEmitter("close", ctx); }; var goTo = function goTo(index, ctx) { - var list = ctx.list, - resultItem = ctx.resultItem; - var results = list.getElementsByTagName(resultItem.tag); + var resultItem = ctx.resultItem; + var results = ctx.list.getElementsByTagName(resultItem.tag); var cls = resultItem.selected ? resultItem.selected.split(" ") : false; if (ctx.isOpen && results.length) { var _results$index$classL; @@ -421,7 +415,7 @@ results[index].setAttribute(Selected, true); if (cls) (_results$index$classL = results[index].classList).add.apply(_results$index$classL, _toConsumableArray(cls)); ctx.input.setAttribute(Active, results[ctx.cursor].id); - list.scrollTop = results[index].offsetTop - list.clientHeight + results[index].clientHeight + 5; + ctx.list.scrollTop = results[index].offsetTop - ctx.list.clientHeight + results[index].clientHeight + 5; ctx.feedback.cursor = ctx.cursor; feedback(ctx, index); eventEmitter("navigate", ctx); @@ -476,21 +470,16 @@ function start (ctx, q) { var _this = this; return new Promise(function ($return, $error) { - var input, query, trigger, threshold, resultsList, queryVal, condition; - input = ctx.input; - query = ctx.query; - trigger = ctx.trigger; - threshold = ctx.threshold; - resultsList = ctx.resultsList; - queryVal = q || getQuery(input); - queryVal = query ? query(queryVal) : queryVal; - condition = checkTrigger(queryVal, trigger, threshold); + var queryVal, condition; + queryVal = q || getQuery(ctx.input); + queryVal = ctx.query ? query(queryVal) : queryVal; + condition = checkTrigger(queryVal, ctx.trigger, ctx.threshold); if (condition) { return getData(ctx).then(function ($await_2) { try { if (ctx.feedback instanceof Error) return $return(); findMatches(queryVal, ctx); - if (resultsList) render(ctx); + if (ctx.resultsList) render(ctx); return $If_1.call(_this); } catch ($boundEx) { return $error($boundEx); @@ -515,15 +504,12 @@ }; var addEvents = function addEvents(ctx) { var events = ctx.events; - ctx.trigger; - var timer = ctx.debounce, - resultsList = ctx.resultsList; var run = debounce(function () { return start(ctx); - }, timer); + }, ctx.debounce); var publicEvents = ctx.events = _objectSpread2({ input: _objectSpread2({}, events && events.input) - }, resultsList && { + }, ctx.resultsList && { list: events ? _objectSpread2({}, events.list) : {} }); var privateEvents = { @@ -548,7 +534,7 @@ } }; eventsManager(privateEvents, function (element, event) { - if (!resultsList && event !== "input") return; + if (!ctx.resultsList && event !== "input") return; if (publicEvents[element][event]) return; publicEvents[element][event] = privateEvents[element][event]; }); @@ -565,37 +551,34 @@ function init (ctx) { var _this = this; return new Promise(function ($return, $error) { - var name, input, placeHolder, resultsList, data, parentAttrs; - name = ctx.name; - input = ctx.input; + var placeHolder, resultsList, parentAttrs; placeHolder = ctx.placeHolder; resultsList = ctx.resultsList; - data = ctx.data; parentAttrs = { role: "combobox", "aria-owns": resultsList.id, "aria-haspopup": true, "aria-expanded": false }; - create(input, _objectSpread2(_objectSpread2({ + create(ctx.input, _objectSpread2(_objectSpread2({ "aria-controls": resultsList.id, "aria-autocomplete": "both" }, placeHolder && { placeholder: placeHolder }), !ctx.wrapper && _objectSpread2({}, parentAttrs))); if (ctx.wrapper) ctx.wrapper = create("div", _objectSpread2({ - around: input, - "class": name + "_wrapper" + around: ctx.input, + "class": ctx.name + "_wrapper" }, parentAttrs)); if (resultsList) ctx.list = create(resultsList.tag, _objectSpread2({ - dest: [typeof resultsList.destination === "string" ? document.querySelector(resultsList.destination) : resultsList.destination(), resultsList.position], + dest: [resultsList.destination, resultsList.position], id: resultsList.id, role: "listbox", hidden: "hidden" }, resultsList["class"] && { "class": resultsList["class"] })); - if (data.cache) { + if (ctx.data.cache) { return getData(ctx).then(function ($await_2) { try { return $If_1.call(_this); @@ -648,7 +631,7 @@ select(this, null, index); }; autoComplete.search = prototype.search = function (query, record, options) { - search(query, record, options); + return search(query, record, options); }; } diff --git a/dist/autoComplete.js.gz b/dist/autoComplete.js.gz index 4d33b55..472019d 100644 Binary files a/dist/autoComplete.js.gz and b/dist/autoComplete.js.gz differ diff --git a/dist/autoComplete.min.js b/dist/autoComplete.min.js index df5e706..6cd27ae 100644 --- a/dist/autoComplete.min.js +++ b/dist/autoComplete.min.js @@ -1 +1 @@ -var e,t;e=this,t=function(){"use strict";function e(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function t(t){for(var n=1;ne.length)&&(t=e.length);for(var n=0,r=new Array(t);n=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:i}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var s,a=!0,u=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return a=e.done,e},e:function(e){u=!0,s=e},f:function(){try{a||null==n.return||n.return()}finally{if(u)throw s}}}}(n.keys);try{for(f.s();!(l=f.n()).done;)c(l.value)}catch(e){f.e(e)}finally{f.f()}}else c()})),n.filter&&(u=n.filter(u));var c=u.slice(0,s.maxResults);t.feedback={query:e,matches:u,results:c},f("results",t)},m="aria-expanded",v="aria-activedescendant",y="aria-selected",b=function(e,n){e.feedback.selection=t({index:n},e.feedback.results[n])},g=function(e){e.isOpen||((e.wrapper||e.input).setAttribute(m,!0),e.list.removeAttribute("hidden"),e.isOpen=!0,f("open",e))},w=function(e){e.isOpen&&((e.wrapper||e.input).setAttribute(m,!1),e.input.setAttribute(v,""),e.list.setAttribute("hidden",""),e.isOpen=!1,f("close",e))},O=function(e,t){var n=t.list,r=t.resultItem,o=n.getElementsByTagName(r.tag),s=!!r.selected&&r.selected.split(" ");if(t.isOpen&&o.length){var a,u,c=t.cursor;e>=o.length&&(e=0),e<0&&(e=o.length-1),t.cursor=e,c>-1&&(o[c].removeAttribute(y),s&&(u=o[c].classList).remove.apply(u,i(s))),o[e].setAttribute(y,!0),s&&(a=o[e].classList).add.apply(a,i(s)),t.input.setAttribute(v,o[t.cursor].id),n.scrollTop=o[e].offsetTop-n.clientHeight+o[e].clientHeight+5,t.feedback.cursor=t.cursor,b(t,e),f("navigate",t)}},A=function(e){var t=e.cursor+1;O(t,e)},S=function(e){var t=e.cursor-1;O(t,e)},k=function(e,t,n){(n=n>=0?n:e.cursor)<0||(e.feedback.event=t,b(e,n),f("selection",e),w(e))};function j(e,n){var r=this;return new Promise((function(i,o){var s,a,c,l,f,p,m;return s=e.input,a=e.query,c=e.trigger,l=e.threshold,f=e.resultsList,p=n||((m=s)instanceof HTMLInputElement||m instanceof HTMLTextAreaElement?m.value:m.innerHTML),function(e,t,n){return t?t(e):e.length>=n}(p=a?a(p):p,c,l)?d(e).then((function(n){try{return e.feedback instanceof Error?i():(h(p,e),f&&function(e){var n=e.resultsList,r=e.list,i=e.resultItem,o=e.feedback;o.query;var s=o.matches,a=o.results;if(e.cursor=-1,r.innerHTML="",s.length||n.noResults){var c=document.createDocumentFragment();a.forEach((function(e,n){var r=u(i.tag,t({id:"".concat(i.id,"_").concat(n),role:"option",innerHTML:e.match,inside:c},i.class&&{class:i.class}));i.element&&i.element(r,e)})),r.append(c),n.element&&n.element(r,o),g(e)}else w(e)}(e),v.call(r))}catch(e){return o(e)}}),o):(w(e),v.call(r));function v(){return i()}}))}var L=function(e,t){for(var n in e)for(var r in e[n])t(n,r)},T=function(e){var n=e.events;e.trigger;var r=e.debounce,i=e.resultsList,o=function(e,t){var n;return function(){clearTimeout(n),n=setTimeout((function(){return e()}),t)}}((function(){return j(e)}),r),s=e.events=t({input:t({},n&&n.input)},i&&{list:n?t({},n.list):{}}),a={input:{input:function(){o()},keydown:function(t){!function(e,t){switch(e.keyCode){case 40:case 38:e.preventDefault(),40===e.keyCode?A(t):S(t);break;case 13:t.submit||e.preventDefault(),t.cursor>=0&&k(t,e);break;case 9:t.resultsList.tabSelect&&t.cursor>=0&&k(t,e);break;case 27:t.input.value="",w(t)}}(t,e)},blur:function(){w(e)}},list:{mousedown:function(e){e.preventDefault()},click:function(t){!function(e,t){var n=t.resultItem.tag.toUpperCase(),r=Array.from(t.list.querySelectorAll(n)),i=e.target.closest(n);if(i&&i.nodeName===n){var o=r.indexOf(i);k(t,e,o)}}(t,e)}}};L(a,(function(e,t){(i||"input"===t)&&(s[e][t]||(s[e][t]=a[e][t]))})),L(s,(function(t,n){e[t].addEventListener(n,s[t][n])}))};function E(e){var n=this;return new Promise((function(r,i){var o,s,a,c,l,p;if(o=e.name,s=e.input,a=e.placeHolder,c=e.resultsList,l=e.data,p={role:"combobox","aria-owns":c.id,"aria-haspopup":!0,"aria-expanded":!1},u(s,t(t({"aria-controls":c.id,"aria-autocomplete":"both"},a&&{placeholder:a}),!e.wrapper&&t({},p))),e.wrapper&&(e.wrapper=u("div",t({around:s,class:o+"_wrapper"},p))),c&&(e.list=u(c.tag,t({dest:["string"==typeof c.destination?document.querySelector(c.destination):c.destination(),c.position],id:c.id,role:"listbox",hidden:"hidden"},c.class&&{class:c.class}))),l.cache)return d(e).then((function(e){try{return h.call(n)}catch(e){return i(e)}}),i);function h(){return T(e),f("init",e),r()}return h.call(n)}))}function x(e){var t=e.prototype;t.init=function(){E(this)},t.start=function(e){j(this,e)},t.unInit=function(){if(this.wrapper){var e=this.wrapper.parentNode;e.insertBefore(this.input,this.wrapper),e.removeChild(this.wrapper)}var t;L((t=this).events,(function(e,n){t[e].removeEventListener(n,t.events[e][n])}))},t.open=function(){g(this)},t.close=function(){w(this)},t.goTo=function(e){O(e,this)},t.next=function(){A(this)},t.previous=function(){S(this)},t.select=function(e){k(this,null,e)},e.search=t.search=function(e,t,n){p(e,t,n)}}return function e(t){this.options=t,this.id=e.instances=(e.instances||0)+1,this.name="autoComplete",this.wrapper=1,this.threshold=1,this.debounce=0,this.resultsList={position:"afterend",tag:"ul",maxResults:5},this.resultItem={tag:"li"},function(e){var t=e.id,r=e.name,i=e.options,o=e.resultsList,s=e.resultItem;for(var a in i)if("object"===n(i[a]))for(var u in e[a]||(e[a]={}),i[a])e[a][u]=i[a][u];else e[a]=i[a];e.selector=e.selector||"#"+r,o.destination=o.destination||e.selector,o.id=o.id||r+"_list_"+t,s.id=s.id||r+"_result",e.input="string"==typeof e.selector?document.querySelector(e.selector):e.selector()}(this),x.call(this,e),E(this)}},"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).autoComplete=t(); +var e,t;e=this,t=function(){"use strict";function e(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function t(t){for(var n=1;ne.length)&&(t=e.length);for(var n=0,r=new Array(t);n=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:i}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var s,u=!0,a=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return u=e.done,e},e:function(e){a=!0,s=e},f:function(){try{u||null==n.return||n.return()}finally{if(a)throw s}}}}(n.keys);try{for(l.s();!(c=l.n()).done;)a(c.value)}catch(e){l.e(e)}finally{l.f()}}else a()})),n.filter&&(i=n.filter(i));var s=i.slice(0,t.resultsList.maxResults);t.feedback={query:e,matches:i,results:s},f("results",t)},m="aria-expanded",v="aria-activedescendant",b="aria-selected",y=function(e,n){e.feedback.selection=t({index:n},e.feedback.results[n])},g=function(e){e.isOpen||((e.wrapper||e.input).setAttribute(m,!0),e.list.removeAttribute("hidden"),e.isOpen=!0,f("open",e))},w=function(e){e.isOpen&&((e.wrapper||e.input).setAttribute(m,!1),e.input.setAttribute(v,""),e.list.setAttribute("hidden",""),e.isOpen=!1,f("close",e))},O=function(e,t){var n=t.resultItem,r=t.list.getElementsByTagName(n.tag),o=!!n.selected&&n.selected.split(" ");if(t.isOpen&&r.length){var s,u,a=t.cursor;e>=r.length&&(e=0),e<0&&(e=r.length-1),t.cursor=e,a>-1&&(r[a].removeAttribute(b),o&&(u=r[a].classList).remove.apply(u,i(o))),r[e].setAttribute(b,!0),o&&(s=r[e].classList).add.apply(s,i(o)),t.input.setAttribute(v,r[t.cursor].id),t.list.scrollTop=r[e].offsetTop-t.list.clientHeight+r[e].clientHeight+5,t.feedback.cursor=t.cursor,y(t,e),f("navigate",t)}},A=function(e){var t=e.cursor+1;O(t,e)},k=function(e){var t=e.cursor-1;O(t,e)},L=function(e,t,n){(n=n>=0?n:e.cursor)<0||(e.feedback.event=t,y(e,n),f("selection",e),w(e))};function j(e,n){var r=this;return new Promise((function(i,o){var s,u;return s=n||((u=e.input)instanceof HTMLInputElement||u instanceof HTMLTextAreaElement?u.value:u.innerHTML),function(e,t,n){return t?t(e):e.length>=n}(s=e.query?query(s):s,e.trigger,e.threshold)?d(e).then((function(n){try{return e.feedback instanceof Error?i():(h(s,e),e.resultsList&&function(e){var n=e.resultsList,r=e.list,i=e.resultItem,o=e.feedback,s=o.matches,u=o.results;if(e.cursor=-1,r.innerHTML="",s.length||n.noResults){var c=new DocumentFragment;u.forEach((function(e,n){var r=a(i.tag,t({id:"".concat(i.id,"_").concat(n),role:"option",innerHTML:e.match,inside:c},i.class&&{class:i.class}));i.element&&i.element(r,e)})),r.append(c),n.element&&n.element(r,o),g(e)}else w(e)}(e),c.call(r))}catch(e){return o(e)}}),o):(w(e),c.call(r));function c(){return i()}}))}var S=function(e,t){for(var n in e)for(var r in e[n])t(n,r)},T=function(e){var n,r,i,o=e.events,s=(n=function(){return j(e)},r=e.debounce,function(){clearTimeout(i),i=setTimeout((function(){return n()}),r)}),u=e.events=t({input:t({},o&&o.input)},e.resultsList&&{list:o?t({},o.list):{}}),a={input:{input:function(){s()},keydown:function(t){!function(e,t){switch(e.keyCode){case 40:case 38:e.preventDefault(),40===e.keyCode?A(t):k(t);break;case 13:t.submit||e.preventDefault(),t.cursor>=0&&L(t,e);break;case 9:t.resultsList.tabSelect&&t.cursor>=0&&L(t,e);break;case 27:t.input.value="",w(t)}}(t,e)},blur:function(){w(e)}},list:{mousedown:function(e){e.preventDefault()},click:function(t){!function(e,t){var n=t.resultItem.tag.toUpperCase(),r=Array.from(t.list.querySelectorAll(n)),i=e.target.closest(n);if(i&&i.nodeName===n){var o=r.indexOf(i);L(t,e,o)}}(t,e)}}};S(a,(function(t,n){(e.resultsList||"input"===n)&&(u[t][n]||(u[t][n]=a[t][n]))})),S(u,(function(t,n){e[t].addEventListener(n,u[t][n])}))};function E(e){var n=this;return new Promise((function(r,i){var o,s,u;if(o=e.placeHolder,u={role:"combobox","aria-owns":(s=e.resultsList).id,"aria-haspopup":!0,"aria-expanded":!1},a(e.input,t(t({"aria-controls":s.id,"aria-autocomplete":"both"},o&&{placeholder:o}),!e.wrapper&&t({},u))),e.wrapper&&(e.wrapper=a("div",t({around:e.input,class:e.name+"_wrapper"},u))),s&&(e.list=a(s.tag,t({dest:[s.destination,s.position],id:s.id,role:"listbox",hidden:"hidden"},s.class&&{class:s.class}))),e.data.cache)return d(e).then((function(e){try{return c.call(n)}catch(e){return i(e)}}),i);function c(){return T(e),f("init",e),r()}return c.call(n)}))}function x(e){var t=e.prototype;t.init=function(){E(this)},t.start=function(e){j(this,e)},t.unInit=function(){if(this.wrapper){var e=this.wrapper.parentNode;e.insertBefore(this.input,this.wrapper),e.removeChild(this.wrapper)}var t;S((t=this).events,(function(e,n){t[e].removeEventListener(n,t.events[e][n])}))},t.open=function(){g(this)},t.close=function(){w(this)},t.goTo=function(e){O(e,this)},t.next=function(){A(this)},t.previous=function(){k(this)},t.select=function(e){L(this,null,e)},e.search=t.search=function(e,t,n){return p(e,t,n)}}return function e(t){this.options=t,this.id=e.instances=(e.instances||0)+1,this.name="autoComplete",this.wrapper=1,this.threshold=1,this.debounce=0,this.resultsList={position:"afterend",tag:"ul",maxResults:5},this.resultItem={tag:"li"},function(e){var t=e.name,r=e.options,i=e.resultsList,o=e.resultItem;for(var s in r)if("object"===n(r[s]))for(var a in e[s]||(e[s]={}),r[s])e[s][a]=r[s][a];else e[s]=r[s];e.selector=e.selector||"#"+t,i.destination=i.destination||e.selector,i.id=i.id||t+"_list_"+e.id,o.id=o.id||t+"_result",e.input=u(e.selector)}(this),x.call(this,e),E(this)}},"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).autoComplete=t(); diff --git a/dist/autoComplete.min.js.gz b/dist/autoComplete.min.js.gz index 1dca7f4..d3d077f 100644 Binary files a/dist/autoComplete.min.js.gz and b/dist/autoComplete.min.js.gz differ diff --git a/docs/demo/index.html b/docs/demo/index.html index 4b466b1..c605647 100644 --- a/docs/demo/index.html +++ b/docs/demo/index.html @@ -72,7 +72,7 @@ + href="https://cdn.jsdelivr.net/npm/@tarekraafat/autocomplete.js@10.2.2/dist/css/autoComplete.min.css"> @@ -150,7 +150,7 @@

mode

- + diff --git a/docs/demo/js/autoComplete.js b/docs/demo/js/autoComplete.js index f28e2ac..355fff3 100644 --- a/docs/demo/js/autoComplete.js +++ b/docs/demo/js/autoComplete.js @@ -163,31 +163,8 @@ }; } - var configure = (function (ctx) { - var id = ctx.id, - name = ctx.name, - options = ctx.options, - resultsList = ctx.resultsList, - resultItem = ctx.resultItem; - for (var option in options) { - if (_typeof(options[option]) === "object") { - if (!ctx[option]) ctx[option] = {}; - for (var subOption in options[option]) { - ctx[option][subOption] = options[option][subOption]; - } - } else { - ctx[option] = options[option]; - } - } - ctx.selector = ctx.selector || "#" + name; - resultsList.destination = resultsList.destination || ctx.selector; - resultsList.id = resultsList.id || name + "_list_" + id; - resultItem.id = resultItem.id || name + "_result"; - ctx.input = typeof ctx.selector === "string" ? document.querySelector(ctx.selector) : ctx.selector(); - }); - var select$1 = function select(element) { - return typeof element === "string" ? document.querySelector(element) : element; + return typeof element === "string" ? document.querySelector(element) : element(); }; var create = function create(tag, options) { var el = typeof tag === "string" ? document.createElement(tag) : tag; @@ -198,7 +175,7 @@ } else if (key === "dest") { select$1(val[0]).insertAdjacentElement(val[1], el); } else if (key === "around") { - var ref = select$1(val); + var ref = val; ref.parentNode.insertBefore(el, ref); el.append(ref); if (ref.getAttribute("autofocus") != null) ref.focus(); @@ -237,6 +214,28 @@ })).outerHTML; }; + var configure = (function (ctx) { + var name = ctx.name, + options = ctx.options, + resultsList = ctx.resultsList, + resultItem = ctx.resultItem; + for (var option in options) { + if (_typeof(options[option]) === "object") { + if (!ctx[option]) ctx[option] = {}; + for (var subOption in options[option]) { + ctx[option][subOption] = options[option][subOption]; + } + } else { + ctx[option] = options[option]; + } + } + ctx.selector = ctx.selector || "#" + name; + resultsList.destination = resultsList.destination || ctx.selector; + resultsList.id = resultsList.id || name + "_list_" + ctx.id; + resultItem.id = resultItem.id || name + "_result"; + ctx.input = select$1(ctx.selector); + }); + var eventEmitter = (function (name, ctx) { ctx.input.dispatchEvent(new CustomEvent(name, { bubbles: true, @@ -301,18 +300,15 @@ }; var findMatches = function findMatches(query, ctx) { var data = ctx.data, - searchEngine = ctx.searchEngine, - diacritics = ctx.diacritics, - resultsList = ctx.resultsList, - resultItem = ctx.resultItem; + searchEngine = ctx.searchEngine; var matches = []; data.store.forEach(function (value, index) { var find = function find(key) { var record = key ? value[key] : value; var match = typeof searchEngine === "function" ? searchEngine(query, record) : search(query, record, { mode: searchEngine, - diacritics: diacritics, - highlight: resultItem.highlight + diacritics: ctx.diacritics, + highlight: ctx.resultItem.highlight }); if (!match) return; var result = { @@ -340,7 +336,7 @@ } }); if (data.filter) matches = data.filter(matches); - var results = matches.slice(0, resultsList.maxResults); + var results = matches.slice(0, ctx.resultsList.maxResults); ctx.feedback = { query: query, matches: matches, @@ -362,13 +358,12 @@ list = ctx.list, resultItem = ctx.resultItem, feedback = ctx.feedback; - feedback.query; - var matches = feedback.matches, + var matches = feedback.matches, results = feedback.results; ctx.cursor = -1; list.innerHTML = ""; if (matches.length || resultsList.noResults) { - var fragment = document.createDocumentFragment(); + var fragment = new DocumentFragment(); results.forEach(function (result, index) { var element = create(resultItem.tag, _objectSpread2({ id: "".concat(resultItem.id, "_").concat(index), @@ -403,9 +398,8 @@ eventEmitter("close", ctx); }; var goTo = function goTo(index, ctx) { - var list = ctx.list, - resultItem = ctx.resultItem; - var results = list.getElementsByTagName(resultItem.tag); + var resultItem = ctx.resultItem; + var results = ctx.list.getElementsByTagName(resultItem.tag); var cls = resultItem.selected ? resultItem.selected.split(" ") : false; if (ctx.isOpen && results.length) { var _results$index$classL; @@ -421,7 +415,7 @@ results[index].setAttribute(Selected, true); if (cls) (_results$index$classL = results[index].classList).add.apply(_results$index$classL, _toConsumableArray(cls)); ctx.input.setAttribute(Active, results[ctx.cursor].id); - list.scrollTop = results[index].offsetTop - list.clientHeight + results[index].clientHeight + 5; + ctx.list.scrollTop = results[index].offsetTop - ctx.list.clientHeight + results[index].clientHeight + 5; ctx.feedback.cursor = ctx.cursor; feedback(ctx, index); eventEmitter("navigate", ctx); @@ -476,21 +470,16 @@ function start (ctx, q) { var _this = this; return new Promise(function ($return, $error) { - var input, query, trigger, threshold, resultsList, queryVal, condition; - input = ctx.input; - query = ctx.query; - trigger = ctx.trigger; - threshold = ctx.threshold; - resultsList = ctx.resultsList; - queryVal = q || getQuery(input); - queryVal = query ? query(queryVal) : queryVal; - condition = checkTrigger(queryVal, trigger, threshold); + var queryVal, condition; + queryVal = q || getQuery(ctx.input); + queryVal = ctx.query ? query(queryVal) : queryVal; + condition = checkTrigger(queryVal, ctx.trigger, ctx.threshold); if (condition) { return getData(ctx).then(function ($await_2) { try { if (ctx.feedback instanceof Error) return $return(); findMatches(queryVal, ctx); - if (resultsList) render(ctx); + if (ctx.resultsList) render(ctx); return $If_1.call(_this); } catch ($boundEx) { return $error($boundEx); @@ -515,15 +504,12 @@ }; var addEvents = function addEvents(ctx) { var events = ctx.events; - ctx.trigger; - var timer = ctx.debounce, - resultsList = ctx.resultsList; var run = debounce(function () { return start(ctx); - }, timer); + }, ctx.debounce); var publicEvents = ctx.events = _objectSpread2({ input: _objectSpread2({}, events && events.input) - }, resultsList && { + }, ctx.resultsList && { list: events ? _objectSpread2({}, events.list) : {} }); var privateEvents = { @@ -548,7 +534,7 @@ } }; eventsManager(privateEvents, function (element, event) { - if (!resultsList && event !== "input") return; + if (!ctx.resultsList && event !== "input") return; if (publicEvents[element][event]) return; publicEvents[element][event] = privateEvents[element][event]; }); @@ -565,37 +551,34 @@ function init (ctx) { var _this = this; return new Promise(function ($return, $error) { - var name, input, placeHolder, resultsList, data, parentAttrs; - name = ctx.name; - input = ctx.input; + var placeHolder, resultsList, parentAttrs; placeHolder = ctx.placeHolder; resultsList = ctx.resultsList; - data = ctx.data; parentAttrs = { role: "combobox", "aria-owns": resultsList.id, "aria-haspopup": true, "aria-expanded": false }; - create(input, _objectSpread2(_objectSpread2({ + create(ctx.input, _objectSpread2(_objectSpread2({ "aria-controls": resultsList.id, "aria-autocomplete": "both" }, placeHolder && { placeholder: placeHolder }), !ctx.wrapper && _objectSpread2({}, parentAttrs))); if (ctx.wrapper) ctx.wrapper = create("div", _objectSpread2({ - around: input, - "class": name + "_wrapper" + around: ctx.input, + "class": ctx.name + "_wrapper" }, parentAttrs)); if (resultsList) ctx.list = create(resultsList.tag, _objectSpread2({ - dest: [typeof resultsList.destination === "string" ? document.querySelector(resultsList.destination) : resultsList.destination(), resultsList.position], + dest: [resultsList.destination, resultsList.position], id: resultsList.id, role: "listbox", hidden: "hidden" }, resultsList["class"] && { "class": resultsList["class"] })); - if (data.cache) { + if (ctx.data.cache) { return getData(ctx).then(function ($await_2) { try { return $If_1.call(_this); @@ -648,7 +631,7 @@ select(this, null, index); }; autoComplete.search = prototype.search = function (query, record, options) { - search(query, record, options); + return search(query, record, options); }; } diff --git a/docs/demo/js/autoComplete.js.gz b/docs/demo/js/autoComplete.js.gz index 4d33b55..472019d 100644 Binary files a/docs/demo/js/autoComplete.js.gz and b/docs/demo/js/autoComplete.js.gz differ diff --git a/docs/demo/js/autoComplete.min.js b/docs/demo/js/autoComplete.min.js index df5e706..6cd27ae 100644 --- a/docs/demo/js/autoComplete.min.js +++ b/docs/demo/js/autoComplete.min.js @@ -1 +1 @@ -var e,t;e=this,t=function(){"use strict";function e(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function t(t){for(var n=1;ne.length)&&(t=e.length);for(var n=0,r=new Array(t);n=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:i}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var s,a=!0,u=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return a=e.done,e},e:function(e){u=!0,s=e},f:function(){try{a||null==n.return||n.return()}finally{if(u)throw s}}}}(n.keys);try{for(f.s();!(l=f.n()).done;)c(l.value)}catch(e){f.e(e)}finally{f.f()}}else c()})),n.filter&&(u=n.filter(u));var c=u.slice(0,s.maxResults);t.feedback={query:e,matches:u,results:c},f("results",t)},m="aria-expanded",v="aria-activedescendant",y="aria-selected",b=function(e,n){e.feedback.selection=t({index:n},e.feedback.results[n])},g=function(e){e.isOpen||((e.wrapper||e.input).setAttribute(m,!0),e.list.removeAttribute("hidden"),e.isOpen=!0,f("open",e))},w=function(e){e.isOpen&&((e.wrapper||e.input).setAttribute(m,!1),e.input.setAttribute(v,""),e.list.setAttribute("hidden",""),e.isOpen=!1,f("close",e))},O=function(e,t){var n=t.list,r=t.resultItem,o=n.getElementsByTagName(r.tag),s=!!r.selected&&r.selected.split(" ");if(t.isOpen&&o.length){var a,u,c=t.cursor;e>=o.length&&(e=0),e<0&&(e=o.length-1),t.cursor=e,c>-1&&(o[c].removeAttribute(y),s&&(u=o[c].classList).remove.apply(u,i(s))),o[e].setAttribute(y,!0),s&&(a=o[e].classList).add.apply(a,i(s)),t.input.setAttribute(v,o[t.cursor].id),n.scrollTop=o[e].offsetTop-n.clientHeight+o[e].clientHeight+5,t.feedback.cursor=t.cursor,b(t,e),f("navigate",t)}},A=function(e){var t=e.cursor+1;O(t,e)},S=function(e){var t=e.cursor-1;O(t,e)},k=function(e,t,n){(n=n>=0?n:e.cursor)<0||(e.feedback.event=t,b(e,n),f("selection",e),w(e))};function j(e,n){var r=this;return new Promise((function(i,o){var s,a,c,l,f,p,m;return s=e.input,a=e.query,c=e.trigger,l=e.threshold,f=e.resultsList,p=n||((m=s)instanceof HTMLInputElement||m instanceof HTMLTextAreaElement?m.value:m.innerHTML),function(e,t,n){return t?t(e):e.length>=n}(p=a?a(p):p,c,l)?d(e).then((function(n){try{return e.feedback instanceof Error?i():(h(p,e),f&&function(e){var n=e.resultsList,r=e.list,i=e.resultItem,o=e.feedback;o.query;var s=o.matches,a=o.results;if(e.cursor=-1,r.innerHTML="",s.length||n.noResults){var c=document.createDocumentFragment();a.forEach((function(e,n){var r=u(i.tag,t({id:"".concat(i.id,"_").concat(n),role:"option",innerHTML:e.match,inside:c},i.class&&{class:i.class}));i.element&&i.element(r,e)})),r.append(c),n.element&&n.element(r,o),g(e)}else w(e)}(e),v.call(r))}catch(e){return o(e)}}),o):(w(e),v.call(r));function v(){return i()}}))}var L=function(e,t){for(var n in e)for(var r in e[n])t(n,r)},T=function(e){var n=e.events;e.trigger;var r=e.debounce,i=e.resultsList,o=function(e,t){var n;return function(){clearTimeout(n),n=setTimeout((function(){return e()}),t)}}((function(){return j(e)}),r),s=e.events=t({input:t({},n&&n.input)},i&&{list:n?t({},n.list):{}}),a={input:{input:function(){o()},keydown:function(t){!function(e,t){switch(e.keyCode){case 40:case 38:e.preventDefault(),40===e.keyCode?A(t):S(t);break;case 13:t.submit||e.preventDefault(),t.cursor>=0&&k(t,e);break;case 9:t.resultsList.tabSelect&&t.cursor>=0&&k(t,e);break;case 27:t.input.value="",w(t)}}(t,e)},blur:function(){w(e)}},list:{mousedown:function(e){e.preventDefault()},click:function(t){!function(e,t){var n=t.resultItem.tag.toUpperCase(),r=Array.from(t.list.querySelectorAll(n)),i=e.target.closest(n);if(i&&i.nodeName===n){var o=r.indexOf(i);k(t,e,o)}}(t,e)}}};L(a,(function(e,t){(i||"input"===t)&&(s[e][t]||(s[e][t]=a[e][t]))})),L(s,(function(t,n){e[t].addEventListener(n,s[t][n])}))};function E(e){var n=this;return new Promise((function(r,i){var o,s,a,c,l,p;if(o=e.name,s=e.input,a=e.placeHolder,c=e.resultsList,l=e.data,p={role:"combobox","aria-owns":c.id,"aria-haspopup":!0,"aria-expanded":!1},u(s,t(t({"aria-controls":c.id,"aria-autocomplete":"both"},a&&{placeholder:a}),!e.wrapper&&t({},p))),e.wrapper&&(e.wrapper=u("div",t({around:s,class:o+"_wrapper"},p))),c&&(e.list=u(c.tag,t({dest:["string"==typeof c.destination?document.querySelector(c.destination):c.destination(),c.position],id:c.id,role:"listbox",hidden:"hidden"},c.class&&{class:c.class}))),l.cache)return d(e).then((function(e){try{return h.call(n)}catch(e){return i(e)}}),i);function h(){return T(e),f("init",e),r()}return h.call(n)}))}function x(e){var t=e.prototype;t.init=function(){E(this)},t.start=function(e){j(this,e)},t.unInit=function(){if(this.wrapper){var e=this.wrapper.parentNode;e.insertBefore(this.input,this.wrapper),e.removeChild(this.wrapper)}var t;L((t=this).events,(function(e,n){t[e].removeEventListener(n,t.events[e][n])}))},t.open=function(){g(this)},t.close=function(){w(this)},t.goTo=function(e){O(e,this)},t.next=function(){A(this)},t.previous=function(){S(this)},t.select=function(e){k(this,null,e)},e.search=t.search=function(e,t,n){p(e,t,n)}}return function e(t){this.options=t,this.id=e.instances=(e.instances||0)+1,this.name="autoComplete",this.wrapper=1,this.threshold=1,this.debounce=0,this.resultsList={position:"afterend",tag:"ul",maxResults:5},this.resultItem={tag:"li"},function(e){var t=e.id,r=e.name,i=e.options,o=e.resultsList,s=e.resultItem;for(var a in i)if("object"===n(i[a]))for(var u in e[a]||(e[a]={}),i[a])e[a][u]=i[a][u];else e[a]=i[a];e.selector=e.selector||"#"+r,o.destination=o.destination||e.selector,o.id=o.id||r+"_list_"+t,s.id=s.id||r+"_result",e.input="string"==typeof e.selector?document.querySelector(e.selector):e.selector()}(this),x.call(this,e),E(this)}},"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).autoComplete=t(); +var e,t;e=this,t=function(){"use strict";function e(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function t(t){for(var n=1;ne.length)&&(t=e.length);for(var n=0,r=new Array(t);n=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:i}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var s,u=!0,a=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return u=e.done,e},e:function(e){a=!0,s=e},f:function(){try{u||null==n.return||n.return()}finally{if(a)throw s}}}}(n.keys);try{for(l.s();!(c=l.n()).done;)a(c.value)}catch(e){l.e(e)}finally{l.f()}}else a()})),n.filter&&(i=n.filter(i));var s=i.slice(0,t.resultsList.maxResults);t.feedback={query:e,matches:i,results:s},f("results",t)},m="aria-expanded",v="aria-activedescendant",b="aria-selected",y=function(e,n){e.feedback.selection=t({index:n},e.feedback.results[n])},g=function(e){e.isOpen||((e.wrapper||e.input).setAttribute(m,!0),e.list.removeAttribute("hidden"),e.isOpen=!0,f("open",e))},w=function(e){e.isOpen&&((e.wrapper||e.input).setAttribute(m,!1),e.input.setAttribute(v,""),e.list.setAttribute("hidden",""),e.isOpen=!1,f("close",e))},O=function(e,t){var n=t.resultItem,r=t.list.getElementsByTagName(n.tag),o=!!n.selected&&n.selected.split(" ");if(t.isOpen&&r.length){var s,u,a=t.cursor;e>=r.length&&(e=0),e<0&&(e=r.length-1),t.cursor=e,a>-1&&(r[a].removeAttribute(b),o&&(u=r[a].classList).remove.apply(u,i(o))),r[e].setAttribute(b,!0),o&&(s=r[e].classList).add.apply(s,i(o)),t.input.setAttribute(v,r[t.cursor].id),t.list.scrollTop=r[e].offsetTop-t.list.clientHeight+r[e].clientHeight+5,t.feedback.cursor=t.cursor,y(t,e),f("navigate",t)}},A=function(e){var t=e.cursor+1;O(t,e)},k=function(e){var t=e.cursor-1;O(t,e)},L=function(e,t,n){(n=n>=0?n:e.cursor)<0||(e.feedback.event=t,y(e,n),f("selection",e),w(e))};function j(e,n){var r=this;return new Promise((function(i,o){var s,u;return s=n||((u=e.input)instanceof HTMLInputElement||u instanceof HTMLTextAreaElement?u.value:u.innerHTML),function(e,t,n){return t?t(e):e.length>=n}(s=e.query?query(s):s,e.trigger,e.threshold)?d(e).then((function(n){try{return e.feedback instanceof Error?i():(h(s,e),e.resultsList&&function(e){var n=e.resultsList,r=e.list,i=e.resultItem,o=e.feedback,s=o.matches,u=o.results;if(e.cursor=-1,r.innerHTML="",s.length||n.noResults){var c=new DocumentFragment;u.forEach((function(e,n){var r=a(i.tag,t({id:"".concat(i.id,"_").concat(n),role:"option",innerHTML:e.match,inside:c},i.class&&{class:i.class}));i.element&&i.element(r,e)})),r.append(c),n.element&&n.element(r,o),g(e)}else w(e)}(e),c.call(r))}catch(e){return o(e)}}),o):(w(e),c.call(r));function c(){return i()}}))}var S=function(e,t){for(var n in e)for(var r in e[n])t(n,r)},T=function(e){var n,r,i,o=e.events,s=(n=function(){return j(e)},r=e.debounce,function(){clearTimeout(i),i=setTimeout((function(){return n()}),r)}),u=e.events=t({input:t({},o&&o.input)},e.resultsList&&{list:o?t({},o.list):{}}),a={input:{input:function(){s()},keydown:function(t){!function(e,t){switch(e.keyCode){case 40:case 38:e.preventDefault(),40===e.keyCode?A(t):k(t);break;case 13:t.submit||e.preventDefault(),t.cursor>=0&&L(t,e);break;case 9:t.resultsList.tabSelect&&t.cursor>=0&&L(t,e);break;case 27:t.input.value="",w(t)}}(t,e)},blur:function(){w(e)}},list:{mousedown:function(e){e.preventDefault()},click:function(t){!function(e,t){var n=t.resultItem.tag.toUpperCase(),r=Array.from(t.list.querySelectorAll(n)),i=e.target.closest(n);if(i&&i.nodeName===n){var o=r.indexOf(i);L(t,e,o)}}(t,e)}}};S(a,(function(t,n){(e.resultsList||"input"===n)&&(u[t][n]||(u[t][n]=a[t][n]))})),S(u,(function(t,n){e[t].addEventListener(n,u[t][n])}))};function E(e){var n=this;return new Promise((function(r,i){var o,s,u;if(o=e.placeHolder,u={role:"combobox","aria-owns":(s=e.resultsList).id,"aria-haspopup":!0,"aria-expanded":!1},a(e.input,t(t({"aria-controls":s.id,"aria-autocomplete":"both"},o&&{placeholder:o}),!e.wrapper&&t({},u))),e.wrapper&&(e.wrapper=a("div",t({around:e.input,class:e.name+"_wrapper"},u))),s&&(e.list=a(s.tag,t({dest:[s.destination,s.position],id:s.id,role:"listbox",hidden:"hidden"},s.class&&{class:s.class}))),e.data.cache)return d(e).then((function(e){try{return c.call(n)}catch(e){return i(e)}}),i);function c(){return T(e),f("init",e),r()}return c.call(n)}))}function x(e){var t=e.prototype;t.init=function(){E(this)},t.start=function(e){j(this,e)},t.unInit=function(){if(this.wrapper){var e=this.wrapper.parentNode;e.insertBefore(this.input,this.wrapper),e.removeChild(this.wrapper)}var t;S((t=this).events,(function(e,n){t[e].removeEventListener(n,t.events[e][n])}))},t.open=function(){g(this)},t.close=function(){w(this)},t.goTo=function(e){O(e,this)},t.next=function(){A(this)},t.previous=function(){k(this)},t.select=function(e){L(this,null,e)},e.search=t.search=function(e,t,n){return p(e,t,n)}}return function e(t){this.options=t,this.id=e.instances=(e.instances||0)+1,this.name="autoComplete",this.wrapper=1,this.threshold=1,this.debounce=0,this.resultsList={position:"afterend",tag:"ul",maxResults:5},this.resultItem={tag:"li"},function(e){var t=e.name,r=e.options,i=e.resultsList,o=e.resultItem;for(var s in r)if("object"===n(r[s]))for(var a in e[s]||(e[s]={}),r[s])e[s][a]=r[s][a];else e[s]=r[s];e.selector=e.selector||"#"+t,i.destination=i.destination||e.selector,i.id=i.id||t+"_list_"+e.id,o.id=o.id||t+"_result",e.input=u(e.selector)}(this),x.call(this,e),E(this)}},"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).autoComplete=t(); diff --git a/docs/demo/js/autoComplete.min.js.gz b/docs/demo/js/autoComplete.min.js.gz index 1dca7f4..d3d077f 100644 Binary files a/docs/demo/js/autoComplete.min.js.gz and b/docs/demo/js/autoComplete.min.js.gz differ diff --git a/docs/index.html b/docs/index.html index 4d51e60..61f71a4 100644 --- a/docs/index.html +++ b/docs/index.html @@ -158,7 +158,7 @@ src="//platform-api.sharethis.com/js/sharethis.js#property=5c213660c276020011d38212&product=inline-share-buttons" async="async"> + href="https://cdn.jsdelivr.net/npm/@tarekraafat/autocomplete.js@10.2.2/dist/css/autoComplete.min.css"> @@ -209,7 +209,7 @@ mustache: { data: ["../package.json", { minVersion: "10.2", - version: "10.2.1" + version: "10.2.2" }] } } @@ -224,7 +224,7 @@ - + \ No newline at end of file diff --git a/docs/release-notes.md b/docs/release-notes.md index 23d8d14..9563b2b 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -35,13 +35,18 @@ For more information on semantic versioning, please visit . *** -### v10.2.1 โœจ +### v10.2.2 โœจ +- โž• Added: Security awareness note under the `Usage` section in the [Docs](https://tarekraafat.github.io/autoComplete.js/#/usage?id=_2-script) (Thanks ๐Ÿ‘ @needlag) #254 +- ๐Ÿ”ง Fixed: `search` API method was not returning the result value +- ๐ŸŽ›๏ธ Updated: Library code with deep cleanup & minor optimizations resulted in around `2.4%` size reduction of the minified version and `3.2%` of the original version and additional performance improvements + +### v10.2.1 - ๐Ÿงน Removed: `preventDefault` on `Tab` key press event - ๐ŸŽ›๏ธ Updated: `No Results Found` example under `How-to Guides` in documentation ### v10.2.0 - โž• Added: `submit` API property controls `Enter` button default behavior (Thanks ๐Ÿ‘ @CodeWithOz) #249 #224 #189 -- โž• Added: `query` parameter to the `start("query")` API method for programmatic operations +- โž• Added: `query` `String` argument to the `start("query")` API method for programmatic operations - ๐Ÿ”ง Fixed: Generated errors when `resultsList` is disabled due to the attachment of the `keydown` event - ๐ŸŽ›๏ธ Updated: Library code with minor optimizations - ๐Ÿงน Removed: Engines field in package.json diff --git a/docs/usage.md b/docs/usage.md index 29885ee..17bdcc3 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -92,6 +92,7 @@ const autoCompleteJS = new autoComplete({ config }); 3. Set your `autoComplete.js` instance [configurations](/configuration.md) +> Note: [data.src](/configuration.md?id=data-required) config is **required** #### ** Basic ** @@ -146,7 +147,19 @@ const autoCompleteJS = new autoComplete({ config }); ``` -> Note: [data.src](/configuration.md?id=data-required) config is **required** +> Security Alert: +> +> `autoComplete.js` does not sanitize/manipulate the user's input data query, mainly for flexibility purposes. +> +> Hence, it is advisable to use any trusted sanitization method/strategy/library with the `query()` API method
+> to reduce the risk of `Cross-Frame Scripting (XFS)` or `Cross-Site Scripting (XSS)` attacks. +> +> +> +> Recommended sanitization Libraries:
+> 1- [DOMPurify](https://github.com/cure53/DOMPurify)
+> 2- [js-xss](https://github.com/leizongmin/js-xss)
+> 3- [sanitize-html](https://github.com/apostrophecms/sanitize-html) *** diff --git a/package-lock.json b/package-lock.json index 8dfdccb..dfe25b0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@tarekraafat/autocomplete.js", - "version": "10.2.0", + "version": "10.2.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@tarekraafat/autocomplete.js", - "version": "10.2.0", + "version": "10.2.1", "funding": [ { "type": "opencollective", @@ -1789,9 +1789,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001241", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001241.tgz", - "integrity": "sha512-1uoSZ1Pq1VpH0WerIMqwptXHNNGfdl7d1cJUFs80CwQ/lVzdhTvsFZCeNFslze7AjsQnb4C85tzclPa1VShbeQ==", + "version": "1.0.30001243", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001243.tgz", + "integrity": "sha512-vNxw9mkTBtkmLFnJRv/2rhs1yufpDfCkBZexG3Y0xdOH2Z/eE/85E4Dl5j1YUN34nZVsSp6vVRFQRrez9wJMRA==", "dev": true, "funding": { "type": "opencollective", @@ -1893,9 +1893,9 @@ } }, "node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", "dev": true, "dependencies": { "ms": "2.1.2" @@ -1922,9 +1922,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.3.766", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.766.tgz", - "integrity": "sha512-u2quJ862q9reRKh/je3GXis3w38+RoXH1J9N3XjtsS6NzmUAosNsyZgUVFZPN/ZlJ3v6T0rTyZR3q/J5c6Sy5w==", + "version": "1.3.769", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.769.tgz", + "integrity": "sha512-B+3hW8D76/uoTPSobWI3D/CFn2S4jPn88dVJ+BkD88Lz6LijQpL+hfdzIFJGTQK4KdE0XwmNbjUQFH1OQVwKdQ==", "dev": true }, "node_modules/escalade": { @@ -2516,9 +2516,9 @@ } }, "node_modules/rollup": { - "version": "2.52.7", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.52.7.tgz", - "integrity": "sha512-55cSH4CCU6MaPr9TAOyrIC+7qFCHscL7tkNsm1MBfIJRRqRbCEY0mmeFn4Wg8FKsHtEH8r389Fz38r/o+kgXLg==", + "version": "2.52.8", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.52.8.tgz", + "integrity": "sha512-IjAB0C6KK5/lvqzJWAzsvOik+jV5Bt907QdkQ/gDP4j+R9KYNI1tjqdxiPitGPVrWC21Mf/ucXgowUjN/VemaQ==", "dev": true, "bin": { "rollup": "dist/bin/rollup" @@ -2810,9 +2810,9 @@ } }, "node_modules/ws": { - "version": "7.5.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.1.tgz", - "integrity": "sha512-2c6faOUH/nhoQN6abwMloF7Iyl0ZS2E9HGtsiLrWn0zOOMWlhtDmdf/uihDt6jnuCxgtwGBNy6Onsoy2s2O2Ow==", + "version": "7.5.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.2.tgz", + "integrity": "sha512-lkF7AWRicoB9mAgjeKbGqVUekLnSNO4VjKVnuPHpQeOxZOErX6BPXwJk70nFslRCEEA8EVW7ZjKwXaP9N+1sKQ==", "dev": true, "engines": { "node": ">=8.3.0" @@ -4054,9 +4054,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001241", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001241.tgz", - "integrity": "sha512-1uoSZ1Pq1VpH0WerIMqwptXHNNGfdl7d1cJUFs80CwQ/lVzdhTvsFZCeNFslze7AjsQnb4C85tzclPa1VShbeQ==", + "version": "1.0.30001243", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001243.tgz", + "integrity": "sha512-vNxw9mkTBtkmLFnJRv/2rhs1yufpDfCkBZexG3Y0xdOH2Z/eE/85E4Dl5j1YUN34nZVsSp6vVRFQRrez9wJMRA==", "dev": true }, "chalk": { @@ -4141,9 +4141,9 @@ } }, "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", "dev": true, "requires": { "ms": "2.1.2" @@ -4159,9 +4159,9 @@ } }, "electron-to-chromium": { - "version": "1.3.766", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.766.tgz", - "integrity": "sha512-u2quJ862q9reRKh/je3GXis3w38+RoXH1J9N3XjtsS6NzmUAosNsyZgUVFZPN/ZlJ3v6T0rTyZR3q/J5c6Sy5w==", + "version": "1.3.769", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.769.tgz", + "integrity": "sha512-B+3hW8D76/uoTPSobWI3D/CFn2S4jPn88dVJ+BkD88Lz6LijQpL+hfdzIFJGTQK4KdE0XwmNbjUQFH1OQVwKdQ==", "dev": true }, "escalade": { @@ -4613,9 +4613,9 @@ } }, "rollup": { - "version": "2.52.7", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.52.7.tgz", - "integrity": "sha512-55cSH4CCU6MaPr9TAOyrIC+7qFCHscL7tkNsm1MBfIJRRqRbCEY0mmeFn4Wg8FKsHtEH8r389Fz38r/o+kgXLg==", + "version": "2.52.8", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.52.8.tgz", + "integrity": "sha512-IjAB0C6KK5/lvqzJWAzsvOik+jV5Bt907QdkQ/gDP4j+R9KYNI1tjqdxiPitGPVrWC21Mf/ucXgowUjN/VemaQ==", "dev": true, "requires": { "fsevents": "~2.3.2" @@ -4842,9 +4842,9 @@ "dev": true }, "ws": { - "version": "7.5.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.1.tgz", - "integrity": "sha512-2c6faOUH/nhoQN6abwMloF7Iyl0ZS2E9HGtsiLrWn0zOOMWlhtDmdf/uihDt6jnuCxgtwGBNy6Onsoy2s2O2Ow==", + "version": "7.5.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.2.tgz", + "integrity": "sha512-lkF7AWRicoB9mAgjeKbGqVUekLnSNO4VjKVnuPHpQeOxZOErX6BPXwJk70nFslRCEEA8EVW7ZjKwXaP9N+1sKQ==", "dev": true, "requires": {} } diff --git a/package.json b/package.json index 95509c3..8a200da 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "version": "10.2.1", + "version": "10.2.2", "name": "@tarekraafat/autocomplete.js", "description": "Simple autocomplete pure vanilla Javascript library.", "keywords": [ diff --git a/src/controllers/dataController.js b/src/controllers/dataController.js index 91b554d..0e5c803 100644 --- a/src/controllers/dataController.js +++ b/src/controllers/dataController.js @@ -28,7 +28,7 @@ const getData = async (ctx) => { * @param {Object} ctx - autoComplete.js context */ const findMatches = (query, ctx) => { - const { data, searchEngine, diacritics, resultsList, resultItem } = ctx; + const { data, searchEngine } = ctx; let matches = []; @@ -42,8 +42,8 @@ const findMatches = (query, ctx) => { ? searchEngine(query, record) : search(query, record, { mode: searchEngine, - diacritics, - highlight: resultItem.highlight, + diacritics: ctx.diacritics, + highlight: ctx.resultItem.highlight, }); if (!match) return; @@ -67,7 +67,7 @@ const findMatches = (query, ctx) => { // Find results matching to the query if (data.filter) matches = data.filter(matches); - const results = matches.slice(0, resultsList.maxResults); + const results = matches.slice(0, ctx.resultsList.maxResults); // Prepare data feedback object ctx.feedback = { query, matches, results }; diff --git a/src/controllers/eventController.js b/src/controllers/eventController.js index 0c3c550..040de06 100644 --- a/src/controllers/eventController.js +++ b/src/controllers/eventController.js @@ -1,5 +1,5 @@ import start from "../services/start"; -import { debounce } from "../helpers/io"; +import { debounce as debouncer } from "../helpers/io"; import { click, navigate, close } from "./listController"; /** @@ -22,16 +22,16 @@ const eventsManager = (events, callback) => { * @param {Object} ctx - autoComplete.js context */ const addEvents = (ctx) => { - const { events, trigger, debounce: timer, resultsList } = ctx; + const { events } = ctx; - const run = debounce(() => start(ctx), timer); + const run = debouncer(() => start(ctx), ctx.debounce); // Public events listeners list const publicEvents = (ctx.events = { input: { ...(events && events.input), }, - ...(resultsList && { list: events ? { ...events.list } : {} }), + ...(ctx.resultsList && { list: events ? { ...events.list } : {} }), }); // Private events listeners list @@ -60,7 +60,7 @@ const addEvents = (ctx) => { // Populate all private events into public events list eventsManager(privateEvents, (element, event) => { // Do NOT populate any events except "input" If "resultsList" disabled - if (!resultsList && event !== "input") return; + if (!ctx.resultsList && event !== "input") return; // Do NOT overwrite public events if (publicEvents[element][event]) return; // Populate public events diff --git a/src/controllers/listController.js b/src/controllers/listController.js index bc48442..9949207 100644 --- a/src/controllers/listController.js +++ b/src/controllers/listController.js @@ -26,7 +26,7 @@ const feedback = (ctx, index) => { */ const render = (ctx) => { let { resultsList, list, resultItem, feedback } = ctx; - const { query, matches, results } = feedback; + const { matches, results } = feedback; // Reset cursor ctx.cursor = -1; @@ -35,7 +35,7 @@ const render = (ctx) => { list.innerHTML = ""; if (matches.length || resultsList.noResults) { - const fragment = document.createDocumentFragment(); + const fragment = new DocumentFragment(); // Generate results elements results.forEach((result, index) => { @@ -114,10 +114,10 @@ const close = (ctx) => { * @param {Object} ctx - autoComplete.js context */ const goTo = (index, ctx) => { - const { list, resultItem } = ctx; + const { resultItem } = ctx; // List of result items - const results = list.getElementsByTagName(resultItem.tag); + const results = ctx.list.getElementsByTagName(resultItem.tag); // Selected result item Classes const cls = resultItem.selected ? resultItem.selected.split(" ") : false; @@ -149,7 +149,7 @@ const goTo = (index, ctx) => { ctx.input.setAttribute(Active, results[ctx.cursor].id); // Scroll to selection - list.scrollTop = results[index].offsetTop - list.clientHeight + results[index].clientHeight + 5; + ctx.list.scrollTop = results[index].offsetTop - ctx.list.clientHeight + results[index].clientHeight + 5; // Prepare Selection data feedback object ctx.feedback.cursor = ctx.cursor; diff --git a/src/helpers/io.js b/src/helpers/io.js index d0be3cb..e6dffe6 100644 --- a/src/helpers/io.js +++ b/src/helpers/io.js @@ -5,10 +5,10 @@ * * @returns {HTMLElement} - selected html element */ -const select = (element) => (typeof element === "string" ? document.querySelector(element) : element); +const select = (element) => (typeof element === "string" ? document.querySelector(element) : element()); /** - * Create new element + * Create new element or Edit existing element * * @param {String|HTMLElement} tag - html tag | html element * @param {Object} options - of the html element @@ -26,8 +26,7 @@ const create = (tag, options) => { } else if (key === "dest") { select(val[0]).insertAdjacentElement(val[1], el); } else if (key === "around") { - const ref = select(val); - + const ref = val; ref.parentNode.insertBefore(el, ref); el.append(ref); diff --git a/src/services/configure.js b/src/services/configure.js index 562b911..39824e6 100644 --- a/src/services/configure.js +++ b/src/services/configure.js @@ -1,10 +1,12 @@ +import { select } from "../helpers/io"; + /** * Configuring options stage * * @param {Object} ctx - autoComplete.js configuration options */ export default (ctx) => { - const { id, name, options, resultsList, resultItem } = ctx; + const { name, options, resultsList, resultItem } = ctx; // Populate Configuration options for (const option in options) { @@ -22,9 +24,9 @@ export default (ctx) => { // Dynamic Config Options ctx.selector = ctx.selector || "#" + name; resultsList.destination = resultsList.destination || ctx.selector; - resultsList.id = resultsList.id || name + "_list_" + id; + resultsList.id = resultsList.id || name + "_list_" + ctx.id; resultItem.id = resultItem.id || name + "_result"; // Assign the "input" html element - ctx.input = typeof ctx.selector === "string" ? document.querySelector(ctx.selector) : ctx.selector(); + ctx.input = select(ctx.selector); }; diff --git a/src/services/extend.js b/src/services/extend.js index e489e2f..e8f06f2 100644 --- a/src/services/extend.js +++ b/src/services/extend.js @@ -90,6 +90,6 @@ export default function (autoComplete) { * @returns {String} - Matching data record */ autoComplete.search = prototype.search = function (query, record, options) { - search(query, record, options); + return search(query, record, options); }; } diff --git a/src/services/init.js b/src/services/init.js index 7f042c9..547e3c6 100644 --- a/src/services/init.js +++ b/src/services/init.js @@ -9,7 +9,7 @@ import eventEmitter from "../helpers/eventEmitter"; * @param {Object} ctx - autoComplete.js context */ export default async function (ctx) { - let { name, input, placeHolder, resultsList, data } = ctx; + let { placeHolder, resultsList } = ctx; const parentAttrs = { role: "combobox", @@ -19,7 +19,7 @@ export default async function (ctx) { }; // Set "input" attributes - create(input, { + create(ctx.input, { "aria-controls": resultsList.id, "aria-autocomplete": "both", ...(placeHolder && { placeholder: placeHolder }), @@ -27,17 +27,12 @@ export default async function (ctx) { }); // Create wrapper element - if (ctx.wrapper) ctx.wrapper = create("div", { around: input, class: name + "_wrapper", ...parentAttrs }); + if (ctx.wrapper) ctx.wrapper = create("div", { around: ctx.input, class: ctx.name + "_wrapper", ...parentAttrs }); if (resultsList) // Create new list element ctx.list = create(resultsList.tag, { - dest: [ - typeof resultsList.destination === "string" - ? document.querySelector(resultsList.destination) - : resultsList.destination(), - resultsList.position, - ], + dest: [resultsList.destination, resultsList.position], id: resultsList.id, role: "listbox", hidden: "hidden", @@ -45,7 +40,7 @@ export default async function (ctx) { }); // Get the data from store - if (data.cache) await getData(ctx); + if (ctx.data.cache) await getData(ctx); // Attach Events listeners addEvents(ctx); diff --git a/src/services/start.js b/src/services/start.js index d0688e6..85147ce 100644 --- a/src/services/start.js +++ b/src/services/start.js @@ -9,13 +9,11 @@ import { render, close } from "../controllers/listController"; * @param {String} q - API search query value */ export default async function (ctx, q) { - const { input, query, trigger, threshold, resultsList } = ctx; - // Get "input" query value - let queryVal = q || getQuery(input); - queryVal = query ? query(queryVal) : queryVal; + let queryVal = q || getQuery(ctx.input); + queryVal = ctx.query ? query(queryVal) : queryVal; // Get trigger decision - const condition = checkTrigger(queryVal, trigger, threshold); + const condition = checkTrigger(queryVal, ctx.trigger, ctx.threshold); // Validate trigger condition if (condition) { @@ -26,7 +24,7 @@ export default async function (ctx, q) { // Find matching results to the query findMatches(queryVal, ctx); // Render "resultsList" - if (resultsList) render(ctx); + if (ctx.resultsList) render(ctx); } else { // Close open list close(ctx);