reverted: --- b/core/assets/vendor/awesomplete/awesomplete.base.css +++ /dev/null @@ -1,33 +0,0 @@ -.awesomplete [hidden] { - display: none; -} - -.awesomplete .visually-hidden { - position: absolute; - clip: rect(0, 0, 0, 0); -} - -.awesomplete { - display: inline-block; - position: relative; -} - -.awesomplete > input { - display: block; -} - -.awesomplete > ul { - position: absolute; - left: 0; - z-index: 1; - min-width: 100%; - box-sizing: border-box; - list-style: none; - padding: 0; - margin: 0; - background: #fff; -} - -.awesomplete > ul:empty { - display: none; -} reverted: --- b/core/assets/vendor/awesomplete/awesomplete.css +++ /dev/null @@ -1,104 +0,0 @@ -.awesomplete [hidden] { - display: none; -} - -.awesomplete .visually-hidden { - position: absolute; - clip: rect(0, 0, 0, 0); -} - -.awesomplete { - display: inline-block; - position: relative; -} - -.awesomplete > input { - display: block; -} - -.awesomplete > ul { - position: absolute; - left: 0; - z-index: 1; - min-width: 100%; - box-sizing: border-box; - list-style: none; - padding: 0; - margin: 0; - background: #fff; -} - -.awesomplete > ul:empty { - display: none; -} - -.awesomplete > ul { - border-radius: .3em; - margin: .2em 0 0; - background: hsla(0,0%,100%,.9); - background: linear-gradient(to bottom right, white, hsla(0,0%,100%,.8)); - border: 1px solid rgba(0,0,0,.3); - box-shadow: .05em .2em .6em rgba(0,0,0,.2); - text-shadow: none; -} - -@supports (transform: scale(0)) { - .awesomplete > ul { - transition: .3s cubic-bezier(.4,.2,.5,1.4); - transform-origin: 1.43em -.43em; - } - - .awesomplete > ul[hidden], - .awesomplete > ul:empty { - opacity: 0; - transform: scale(0); - display: block; - transition-timing-function: ease; - } -} - - /* Pointer */ - .awesomplete > ul:before { - content: ""; - position: absolute; - top: -.43em; - left: 1em; - width: 0; height: 0; - padding: .4em; - background: white; - border: inherit; - border-right: 0; - border-bottom: 0; - -webkit-transform: rotate(45deg); - transform: rotate(45deg); - } - - .awesomplete > ul > li { - position: relative; - padding: .2em .5em; - cursor: pointer; - } - - .awesomplete > ul > li:hover { - background: hsl(200, 40%, 80%); - color: black; - } - - .awesomplete > ul > li[aria-selected="true"] { - background: hsl(205, 40%, 40%); - color: white; - } - - .awesomplete mark { - background: hsl(65, 100%, 50%); - } - - .awesomplete li:hover mark { - background: hsl(68, 100%, 41%); - } - - .awesomplete li[aria-selected="true"] mark { - background: hsl(86, 100%, 21%); - color: inherit; - } -/*# sourceMappingURL=awesomplete.css.map */ reverted: --- b/core/assets/vendor/awesomplete/awesomplete.css.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["awesomplete.base.css","awesomplete.theme.css"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;ACjCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"awesomplete.css","sourcesContent":[".awesomplete [hidden] {\n display: none;\n}\n\n.awesomplete .visually-hidden {\n position: absolute;\n clip: rect(0, 0, 0, 0);\n}\n\n.awesomplete {\n display: inline-block;\n position: relative;\n}\n\n.awesomplete > input {\n display: block;\n}\n\n.awesomplete > ul {\n position: absolute;\n left: 0;\n z-index: 1;\n min-width: 100%;\n box-sizing: border-box;\n list-style: none;\n padding: 0;\n margin: 0;\n background: #fff;\n}\n\n.awesomplete > ul:empty {\n display: none;\n}\n",".awesomplete > ul {\n\tborder-radius: .3em;\n\tmargin: .2em 0 0;\n\tbackground: hsla(0,0%,100%,.9);\n\tbackground: linear-gradient(to bottom right, white, hsla(0,0%,100%,.8));\n\tborder: 1px solid rgba(0,0,0,.3);\n\tbox-shadow: .05em .2em .6em rgba(0,0,0,.2);\n\ttext-shadow: none;\n}\n\n@supports (transform: scale(0)) {\n\t.awesomplete > ul {\n\t\ttransition: .3s cubic-bezier(.4,.2,.5,1.4);\n\t\ttransform-origin: 1.43em -.43em;\n\t}\n\t\n\t.awesomplete > ul[hidden],\n\t.awesomplete > ul:empty {\n\t\topacity: 0;\n\t\ttransform: scale(0);\n\t\tdisplay: block;\n\t\ttransition-timing-function: ease;\n\t}\n}\n\n\t/* Pointer */\n\t.awesomplete > ul:before {\n\t\tcontent: \"\";\n\t\tposition: absolute;\n\t\ttop: -.43em;\n\t\tleft: 1em;\n\t\twidth: 0; height: 0;\n\t\tpadding: .4em;\n\t\tbackground: white;\n\t\tborder: inherit;\n\t\tborder-right: 0;\n\t\tborder-bottom: 0;\n\t\t-webkit-transform: rotate(45deg);\n\t\ttransform: rotate(45deg);\n\t}\n\n\t.awesomplete > ul > li {\n\t\tposition: relative;\n\t\tpadding: .2em .5em;\n\t\tcursor: pointer;\n\t}\n\t\n\t.awesomplete > ul > li:hover {\n\t\tbackground: hsl(200, 40%, 80%);\n\t\tcolor: black;\n\t}\n\t\n\t.awesomplete > ul > li[aria-selected=\"true\"] {\n\t\tbackground: hsl(205, 40%, 40%);\n\t\tcolor: white;\n\t}\n\t\n\t\t.awesomplete mark {\n\t\t\tbackground: hsl(65, 100%, 50%);\n\t\t}\n\t\t\n\t\t.awesomplete li:hover mark {\n\t\t\tbackground: hsl(68, 100%, 41%);\n\t\t}\n\t\t\n\t\t.awesomplete li[aria-selected=\"true\"] mark {\n\t\t\tbackground: hsl(86, 100%, 21%);\n\t\t\tcolor: inherit;\n\t\t}"]} \ No newline at end of file reverted: --- b/core/assets/vendor/awesomplete/awesomplete.js +++ /dev/null @@ -1,552 +0,0 @@ -/** - * Simple, lightweight, usable local autocomplete library for modern browsers - * Because there weren’t enough autocomplete scripts in the world? Because I’m completely insane and have NIH syndrome? Probably both. :P - * @author Lea Verou http://leaverou.github.io/awesomplete - * MIT license - */ - -(function () { - -var _ = function (input, o) { - var me = this; - - // Keep track of number of instances for unique IDs - _.count = (_.count || 0) + 1; - this.count = _.count; - - // Setup - - this.isOpened = false; - - this.input = $(input); - this.input.setAttribute("autocomplete", "off"); - this.input.setAttribute("aria-expanded", "false"); - this.input.setAttribute("aria-owns", "awesomplete_list_" + this.count); - this.input.setAttribute("role", "combobox"); - - // store constructor options in case we need to distinguish - // between default and customized behavior later on - this.options = o = o || {}; - - configure(this, { - minChars: 2, - maxItems: 10, - autoFirst: false, - data: _.DATA, - filter: _.FILTER_CONTAINS, - sort: o.sort === false ? false : _.SORT_BYLENGTH, - container: _.CONTAINER, - item: _.ITEM, - replace: _.REPLACE, - tabSelect: false - }, o); - - this.index = -1; - - // Create necessary elements - - this.container = this.container(input); - - this.ul = $.create("ul", { - hidden: "hidden", - role: "listbox", - id: "awesomplete_list_" + this.count, - inside: this.container - }); - - this.status = $.create("span", { - className: "visually-hidden", - role: "status", - "aria-live": "assertive", - "aria-atomic": true, - inside: this.container, - textContent: this.minChars != 0 ? ("Type " + this.minChars + " or more characters for results.") : "Begin typing for results." - }); - - // Bind events - - this._events = { - input: { - "input": this.evaluate.bind(this), - "blur": this.close.bind(this, { reason: "blur" }), - "keydown": function(evt) { - var c = evt.keyCode; - - // If the dropdown `ul` is in view, then act on keydown for the following keys: - // Enter / Esc / Up / Down - if(me.opened) { - if (c === 13 && me.selected) { // Enter - evt.preventDefault(); - me.select(undefined, undefined, evt); - } - else if (c === 9 && me.selected && me.tabSelect) { - me.select(undefined, undefined, evt); - } - else if (c === 27) { // Esc - me.close({ reason: "esc" }); - } - else if (c === 38 || c === 40) { // Down/Up arrow - evt.preventDefault(); - me[c === 38? "previous" : "next"](); - } - } - } - }, - form: { - "submit": this.close.bind(this, { reason: "submit" }) - }, - ul: { - // Prevent the default mousedowm, which ensures the input is not blurred. - // The actual selection will happen on click. This also ensures dragging the - // cursor away from the list item will cancel the selection - "mousedown": function(evt) { - evt.preventDefault(); - }, - // The click event is fired even if the corresponding mousedown event has called preventDefault - "click": function(evt) { - var li = evt.target; - - if (li !== this) { - - while (li && !/li/i.test(li.nodeName)) { - li = li.parentNode; - } - - if (li && evt.button === 0) { // Only select on left click - evt.preventDefault(); - me.select(li, evt.target, evt); - } - } - } - } - }; - - $.bind(this.input, this._events.input); - $.bind(this.input.form, this._events.form); - $.bind(this.ul, this._events.ul); - - if (this.input.hasAttribute("list")) { - this.list = "#" + this.input.getAttribute("list"); - this.input.removeAttribute("list"); - } - else { - this.list = this.input.getAttribute("data-list") || o.list || []; - } - - _.all.push(this); -}; - -_.prototype = { - set list(list) { - if (Array.isArray(list)) { - this._list = list; - } - else if (typeof list === "string" && list.indexOf(",") > -1) { - this._list = list.split(/\s*,\s*/); - } - else { // Element or CSS selector - list = $(list); - - if (list && list.children) { - var items = []; - slice.apply(list.children).forEach(function (el) { - if (!el.disabled) { - var text = el.textContent.trim(); - var value = el.value || text; - var label = el.label || text; - if (value !== "") { - items.push({ label: label, value: value }); - } - } - }); - this._list = items; - } - } - - if (document.activeElement === this.input) { - this.evaluate(); - } - }, - - get selected() { - return this.index > -1; - }, - - get opened() { - return this.isOpened; - }, - - close: function (o) { - if (!this.opened) { - return; - } - - this.input.setAttribute("aria-expanded", "false"); - this.ul.setAttribute("hidden", ""); - this.isOpened = false; - this.index = -1; - - this.status.setAttribute("hidden", ""); - - $.fire(this.input, "awesomplete-close", o || {}); - }, - - open: function () { - this.input.setAttribute("aria-expanded", "true"); - this.ul.removeAttribute("hidden"); - this.isOpened = true; - - this.status.removeAttribute("hidden"); - - if (this.autoFirst && this.index === -1) { - this.goto(0); - } - - $.fire(this.input, "awesomplete-open"); - }, - - destroy: function() { - //remove events from the input and its form - $.unbind(this.input, this._events.input); - $.unbind(this.input.form, this._events.form); - - // cleanup container if it was created by Awesomplete but leave it alone otherwise - if (!this.options.container) { - //move the input out of the awesomplete container and remove the container and its children - var parentNode = this.container.parentNode; - - parentNode.insertBefore(this.input, this.container); - parentNode.removeChild(this.container); - } - - //remove autocomplete and aria-autocomplete attributes - this.input.removeAttribute("autocomplete"); - this.input.removeAttribute("aria-autocomplete"); - - //remove this awesomeplete instance from the global array of instances - var indexOfAwesomplete = _.all.indexOf(this); - - if (indexOfAwesomplete !== -1) { - _.all.splice(indexOfAwesomplete, 1); - } - }, - - next: function () { - var count = this.ul.children.length; - this.goto(this.index < count - 1 ? this.index + 1 : (count ? 0 : -1) ); - }, - - previous: function () { - var count = this.ul.children.length; - var pos = this.index - 1; - - this.goto(this.selected && pos !== -1 ? pos : count - 1); - }, - - // Should not be used, highlights specific item without any checks! - goto: function (i) { - var lis = this.ul.children; - - if (this.selected) { - lis[this.index].setAttribute("aria-selected", "false"); - } - - this.index = i; - - if (i > -1 && lis.length > 0) { - lis[i].setAttribute("aria-selected", "true"); - - this.status.textContent = lis[i].textContent + ", list item " + (i + 1) + " of " + lis.length; - - this.input.setAttribute("aria-activedescendant", this.ul.id + "_item_" + this.index); - - // scroll to highlighted element in case parent's height is fixed - this.ul.scrollTop = lis[i].offsetTop - this.ul.clientHeight + lis[i].clientHeight; - - $.fire(this.input, "awesomplete-highlight", { - text: this.suggestions[this.index] - }); - } - }, - - select: function (selected, origin, originalEvent) { - if (selected) { - this.index = $.siblingIndex(selected); - } else { - selected = this.ul.children[this.index]; - } - - if (selected) { - var suggestion = this.suggestions[this.index]; - - var allowed = $.fire(this.input, "awesomplete-select", { - text: suggestion, - origin: origin || selected, - originalEvent: originalEvent - }); - - if (allowed) { - this.replace(suggestion); - this.close({ reason: "select" }); - $.fire(this.input, "awesomplete-selectcomplete", { - text: suggestion, - originalEvent: originalEvent - }); - } - } - }, - - evaluate: function() { - var me = this; - var value = this.input.value; - - if (value.length >= this.minChars && this._list && this._list.length > 0) { - this.index = -1; - // Populate list with options that match - this.ul.innerHTML = ""; - - this.suggestions = this._list - .map(function(item) { - return new Suggestion(me.data(item, value)); - }) - .filter(function(item) { - return me.filter(item, value); - }); - - if (this.sort !== false) { - this.suggestions = this.suggestions.sort(this.sort); - } - - this.suggestions = this.suggestions.slice(0, this.maxItems); - - this.suggestions.forEach(function(text, index) { - me.ul.appendChild(me.item(text, value, index)); - }); - - if (this.ul.children.length === 0) { - - this.status.textContent = "No results found"; - - this.close({ reason: "nomatches" }); - - } else { - this.open(); - - this.status.textContent = this.ul.children.length + " results found"; - } - } - else { - this.close({ reason: "nomatches" }); - - this.status.textContent = "No results found"; - } - } -}; - -// Static methods/properties - -_.all = []; - -_.FILTER_CONTAINS = function (text, input) { - return RegExp($.regExpEscape(input.trim()), "i").test(text); -}; - -_.FILTER_STARTSWITH = function (text, input) { - return RegExp("^" + $.regExpEscape(input.trim()), "i").test(text); -}; - -_.SORT_BYLENGTH = function (a, b) { - if (a.length !== b.length) { - return a.length - b.length; - } - - return a < b? -1 : 1; -}; - -_.CONTAINER = function (input) { - return $.create("div", { - className: "awesomplete", - around: input - }); -} - -_.ITEM = function (text, input, item_id) { - var html = input.trim() === "" ? text : text.replace(RegExp($.regExpEscape(input.trim()), "gi"), "$&"); - return $.create("li", { - innerHTML: html, - "role": "option", - "aria-selected": "false", - "id": "awesomplete_list_" + this.count + "_item_" + item_id - }); -}; - -_.REPLACE = function (text) { - this.input.value = text.value; -}; - -_.DATA = function (item/*, input*/) { return item; }; - -// Private functions - -function Suggestion(data) { - var o = Array.isArray(data) - ? { label: data[0], value: data[1] } - : typeof data === "object" && "label" in data && "value" in data ? data : { label: data, value: data }; - - this.label = o.label || o.value; - this.value = o.value; -} -Object.defineProperty(Suggestion.prototype = Object.create(String.prototype), "length", { - get: function() { return this.label.length; } -}); -Suggestion.prototype.toString = Suggestion.prototype.valueOf = function () { - return "" + this.label; -}; - -function configure(instance, properties, o) { - for (var i in properties) { - var initial = properties[i], - attrValue = instance.input.getAttribute("data-" + i.toLowerCase()); - - if (typeof initial === "number") { - instance[i] = parseInt(attrValue); - } - else if (initial === false) { // Boolean options must be false by default anyway - instance[i] = attrValue !== null; - } - else if (initial instanceof Function) { - instance[i] = null; - } - else { - instance[i] = attrValue; - } - - if (!instance[i] && instance[i] !== 0) { - instance[i] = (i in o)? o[i] : initial; - } - } -} - -// Helpers - -var slice = Array.prototype.slice; - -function $(expr, con) { - return typeof expr === "string"? (con || document).querySelector(expr) : expr || null; -} - -function $$(expr, con) { - return slice.call((con || document).querySelectorAll(expr)); -} - -$.create = function(tag, o) { - var element = document.createElement(tag); - - for (var i in o) { - var val = o[i]; - - if (i === "inside") { - $(val).appendChild(element); - } - else if (i === "around") { - var ref = $(val); - ref.parentNode.insertBefore(element, ref); - element.appendChild(ref); - - if (ref.getAttribute("autofocus") != null) { - ref.focus(); - } - } - else if (i in element) { - element[i] = val; - } - else { - element.setAttribute(i, val); - } - } - - return element; -}; - -$.bind = function(element, o) { - if (element) { - for (var event in o) { - var callback = o[event]; - - event.split(/\s+/).forEach(function (event) { - element.addEventListener(event, callback); - }); - } - } -}; - -$.unbind = function(element, o) { - if (element) { - for (var event in o) { - var callback = o[event]; - - event.split(/\s+/).forEach(function(event) { - element.removeEventListener(event, callback); - }); - } - } -}; - -$.fire = function(target, type, properties) { - var evt = document.createEvent("HTMLEvents"); - - evt.initEvent(type, true, true ); - - for (var j in properties) { - evt[j] = properties[j]; - } - - return target.dispatchEvent(evt); -}; - -$.regExpEscape = function (s) { - return s.replace(/[-\\^$*+?.()|[\]{}]/g, "\\$&"); -}; - -$.siblingIndex = function (el) { - /* eslint-disable no-cond-assign */ - for (var i = 0; el = el.previousElementSibling; i++); - return i; -}; - -// Initialization - -function init() { - $$("input.awesomplete").forEach(function (input) { - new _(input); - }); -} - -// Make sure to export Awesomplete on self when in a browser -if (typeof self !== "undefined") { - self.Awesomplete = _; -} - -// Are we in a browser? Check for Document constructor -if (typeof Document !== "undefined") { - // DOM already loaded? - if (document.readyState !== "loading") { - init(); - } - else { - // Wait for it - document.addEventListener("DOMContentLoaded", init); - } -} - -_.$ = $; -_.$$ = $$; - -// Expose Awesomplete as a CJS module -if (typeof module === "object" && module.exports) { - module.exports = _; -} - -return _; - -}()); reverted: --- b/core/assets/vendor/awesomplete/awesomplete.min.js +++ /dev/null @@ -1,3 +0,0 @@ -// Awesomplete - Lea Verou - MIT license -!function(){function t(t){var e=Array.isArray(t)?{label:t[0],value:t[1]}:"object"==typeof t&&"label"in t&&"value"in t?t:{label:t,value:t};this.label=e.label||e.value,this.value=e.value}function e(t,e,i){for(var n in e){var s=e[n],r=t.input.getAttribute("data-"+n.toLowerCase());"number"==typeof s?t[n]=parseInt(r):!1===s?t[n]=null!==r:s instanceof Function?t[n]=null:t[n]=r,t[n]||0===t[n]||(t[n]=n in i?i[n]:s)}}function i(t,e){return"string"==typeof t?(e||document).querySelector(t):t||null}function n(t,e){return o.call((e||document).querySelectorAll(t))}function s(){n("input.awesomplete").forEach(function(t){new r(t)})}var r=function(t,n){var s=this;r.count=(r.count||0)+1,this.count=r.count,this.isOpened=!1,this.input=i(t),this.input.setAttribute("autocomplete","off"),this.input.setAttribute("aria-expanded","false"),this.input.setAttribute("aria-owns","awesomplete_list_"+this.count),this.input.setAttribute("role","combobox"),this.options=n=n||{},e(this,{minChars:2,maxItems:10,autoFirst:!1,data:r.DATA,filter:r.FILTER_CONTAINS,sort:!1!==n.sort&&r.SORT_BYLENGTH,container:r.CONTAINER,item:r.ITEM,replace:r.REPLACE,tabSelect:!1},n),this.index=-1,this.container=this.container(t),this.ul=i.create("ul",{hidden:"hidden",role:"listbox",id:"awesomplete_list_"+this.count,inside:this.container}),this.status=i.create("span",{className:"visually-hidden",role:"status","aria-live":"assertive","aria-atomic":!0,inside:this.container,textContent:0!=this.minChars?"Type "+this.minChars+" or more characters for results.":"Begin typing for results."}),this._events={input:{input:this.evaluate.bind(this),blur:this.close.bind(this,{reason:"blur"}),keydown:function(t){var e=t.keyCode;s.opened&&(13===e&&s.selected?(t.preventDefault(),s.select()):9===e&&s.selected&&s.tabSelect?s.select():27===e?s.close({reason:"esc"}):38!==e&&40!==e||(t.preventDefault(),s[38===e?"previous":"next"]()))}},form:{submit:this.close.bind(this,{reason:"submit"})},ul:{mousedown:function(t){t.preventDefault()},click:function(t){var e=t.target;if(e!==this){for(;e&&!/li/i.test(e.nodeName);)e=e.parentNode;e&&0===t.button&&(t.preventDefault(),s.select(e,t.target))}}}},i.bind(this.input,this._events.input),i.bind(this.input.form,this._events.form),i.bind(this.ul,this._events.ul),this.input.hasAttribute("list")?(this.list="#"+this.input.getAttribute("list"),this.input.removeAttribute("list")):this.list=this.input.getAttribute("data-list")||n.list||[],r.all.push(this)};r.prototype={set list(t){if(Array.isArray(t))this._list=t;else if("string"==typeof t&&t.indexOf(",")>-1)this._list=t.split(/\s*,\s*/);else if((t=i(t))&&t.children){var e=[];o.apply(t.children).forEach(function(t){if(!t.disabled){var i=t.textContent.trim(),n=t.value||i,s=t.label||i;""!==n&&e.push({label:s,value:n})}}),this._list=e}document.activeElement===this.input&&this.evaluate()},get selected(){return this.index>-1},get opened(){return this.isOpened},close:function(t){this.opened&&(this.input.setAttribute("aria-expanded","false"),this.ul.setAttribute("hidden",""),this.isOpened=!1,this.index=-1,this.status.setAttribute("hidden",""),i.fire(this.input,"awesomplete-close",t||{}))},open:function(){this.input.setAttribute("aria-expanded","true"),this.ul.removeAttribute("hidden"),this.isOpened=!0,this.status.removeAttribute("hidden"),this.autoFirst&&-1===this.index&&this.goto(0),i.fire(this.input,"awesomplete-open")},destroy:function(){if(i.unbind(this.input,this._events.input),i.unbind(this.input.form,this._events.form),!this.options.container){var t=this.container.parentNode;t.insertBefore(this.input,this.container),t.removeChild(this.container)}this.input.removeAttribute("autocomplete"),this.input.removeAttribute("aria-autocomplete");var e=r.all.indexOf(this);-1!==e&&r.all.splice(e,1)},next:function(){var t=this.ul.children.length;this.goto(this.index-1&&e.length>0&&(e[t].setAttribute("aria-selected","true"),this.status.textContent=e[t].textContent+", list item "+(t+1)+" of "+e.length,this.input.setAttribute("aria-activedescendant",this.ul.id+"_item_"+this.index),this.ul.scrollTop=e[t].offsetTop-this.ul.clientHeight+e[t].clientHeight,i.fire(this.input,"awesomplete-highlight",{text:this.suggestions[this.index]}))},select:function(t,e){if(t?this.index=i.siblingIndex(t):t=this.ul.children[this.index],t){var n=this.suggestions[this.index];i.fire(this.input,"awesomplete-select",{text:n,origin:e||t})&&(this.replace(n),this.close({reason:"select"}),i.fire(this.input,"awesomplete-selectcomplete",{text:n}))}},evaluate:function(){var e=this,i=this.input.value;i.length>=this.minChars&&this._list&&this._list.length>0?(this.index=-1,this.ul.innerHTML="",this.suggestions=this._list.map(function(n){return new t(e.data(n,i))}).filter(function(t){return e.filter(t,i)}),!1!==this.sort&&(this.suggestions=this.suggestions.sort(this.sort)),this.suggestions=this.suggestions.slice(0,this.maxItems),this.suggestions.forEach(function(t,n){e.ul.appendChild(e.item(t,i,n))}),0===this.ul.children.length?(this.status.textContent="No results found",this.close({reason:"nomatches"})):(this.open(),this.status.textContent=this.ul.children.length+" results found")):(this.close({reason:"nomatches"}),this.status.textContent="No results found")}},r.all=[],r.FILTER_CONTAINS=function(t,e){return RegExp(i.regExpEscape(e.trim()),"i").test(t)},r.FILTER_STARTSWITH=function(t,e){return RegExp("^"+i.regExpEscape(e.trim()),"i").test(t)},r.SORT_BYLENGTH=function(t,e){return t.length!==e.length?t.length-e.length:t$&"),role:"option","aria-selected":"false",id:"awesomplete_list_"+this.count+"_item_"+n})},r.REPLACE=function(t){this.input.value=t.value},r.DATA=function(t){return t},Object.defineProperty(t.prototype=Object.create(String.prototype),"length",{get:function(){return this.label.length}}),t.prototype.toString=t.prototype.valueOf=function(){return""+this.label};var o=Array.prototype.slice;i.create=function(t,e){var n=document.createElement(t);for(var s in e){var r=e[s];if("inside"===s)i(r).appendChild(n);else if("around"===s){var o=i(r);o.parentNode.insertBefore(n,o),n.appendChild(o),null!=o.getAttribute("autofocus")&&o.focus()}else s in n?n[s]=r:n.setAttribute(s,r)}return n},i.bind=function(t,e){if(t)for(var i in e){var n=e[i];i.split(/\s+/).forEach(function(e){t.addEventListener(e,n)})}},i.unbind=function(t,e){if(t)for(var i in e){var n=e[i];i.split(/\s+/).forEach(function(e){t.removeEventListener(e,n)})}},i.fire=function(t,e,i){var n=document.createEvent("HTMLEvents");n.initEvent(e,!0,!0);for(var s in i)n[s]=i[s];return t.dispatchEvent(n)},i.regExpEscape=function(t){return t.replace(/[-\\^$*+?.()|[\]{}]/g,"\\$&")},i.siblingIndex=function(t){for(var e=0;t=t.previousElementSibling;e++);return e},"undefined"!=typeof self&&(self.Awesomplete=r),"undefined"!=typeof Document&&("loading"!==document.readyState?s():document.addEventListener("DOMContentLoaded",s)),r.$=i,r.$$=n,"object"==typeof module&&module.exports&&(module.exports=r)}(); -//# sourceMappingURL=awesomplete.min.js.map reverted: --- b/core/assets/vendor/awesomplete/awesomplete.min.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["awesomplete.js"],"names":["Suggestion","data","o","Array","isArray","label","value","this","configure","instance","properties","i","initial","attrValue","input","getAttribute","toLowerCase","parseInt","Function","$","expr","con","document","querySelector","$$","slice","call","querySelectorAll","init","forEach","_","me","count","isOpened","setAttribute","options","minChars","maxItems","autoFirst","DATA","filter","FILTER_CONTAINS","sort","SORT_BYLENGTH","container","CONTAINER","item","ITEM","replace","REPLACE","tabSelect","index","ul","create","hidden","role","id","inside","status","className","aria-live","aria-atomic","textContent","_events","evaluate","bind","blur","close","reason","keydown","evt","c","keyCode","opened","selected","preventDefault","select","form","submit","mousedown","click","li","target","test","nodeName","parentNode","button","hasAttribute","list","removeAttribute","all","push","prototype","_list","indexOf","split","children","items","apply","el","disabled","text","trim","activeElement","fire","open","goto","destroy","unbind","insertBefore","removeChild","indexOfAwesomplete","splice","next","length","previous","pos","lis","scrollTop","offsetTop","clientHeight","suggestions","origin","siblingIndex","suggestion","innerHTML","map","appendChild","RegExp","regExpEscape","FILTER_STARTSWITH","a","b","around","item_id","aria-selected","Object","defineProperty","String","get","toString","valueOf","tag","element","createElement","val","ref","focus","event","callback","addEventListener","removeEventListener","type","createEvent","initEvent","j","dispatchEvent","s","previousElementSibling","self","Awesomplete","Document","readyState","module","exports"],"mappings":"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","file":"awesomplete.min.js","sourcesContent":["/**\n * Simple, lightweight, usable local autocomplete library for modern browsers\n * Because there weren’t enough autocomplete scripts in the world? Because I’m completely insane and have NIH syndrome? Probably both. :P\n * @author Lea Verou http://leaverou.github.io/awesomplete\n * MIT license\n */\n\n(function () {\n\nvar _ = function (input, o) {\n\tvar me = this;\n\n // Keep track of number of instances for unique IDs\n _.count = (_.count || 0) + 1;\n this.count = _.count;\n\n\t// Setup\n\n\tthis.isOpened = false;\n\n\tthis.input = $(input);\n\tthis.input.setAttribute(\"autocomplete\", \"off\");\n\tthis.input.setAttribute(\"aria-expanded\", \"false\");\n\tthis.input.setAttribute(\"aria-owns\", \"awesomplete_list_\" + this.count);\n\tthis.input.setAttribute(\"role\", \"combobox\");\n\n\t// store constructor options in case we need to distinguish\n\t// between default and customized behavior later on\n\tthis.options = o = o || {};\n\n\tconfigure(this, {\n\t\tminChars: 2,\n\t\tmaxItems: 10,\n\t\tautoFirst: false,\n\t\tdata: _.DATA,\n\t\tfilter: _.FILTER_CONTAINS,\n\t\tsort: o.sort === false ? false : _.SORT_BYLENGTH,\n\t\tcontainer: _.CONTAINER,\n\t\titem: _.ITEM,\n\t\treplace: _.REPLACE,\n\t\ttabSelect: false\n\t}, o);\n\n\tthis.index = -1;\n\n\t// Create necessary elements\n\n\tthis.container = this.container(input);\n\n\tthis.ul = $.create(\"ul\", {\n\t\thidden: \"hidden\",\n role: \"listbox\",\n id: \"awesomplete_list_\" + this.count,\n\t\tinside: this.container\n\t});\n\n\tthis.status = $.create(\"span\", {\n\t\tclassName: \"visually-hidden\",\n\t\trole: \"status\",\n\t\t\"aria-live\": \"assertive\",\n \"aria-atomic\": true,\n inside: this.container,\n textContent: this.minChars != 0 ? (\"Type \" + this.minChars + \" or more characters for results.\") : \"Begin typing for results.\"\n\t});\n\n\t// Bind events\n\n\tthis._events = {\n\t\tinput: {\n\t\t\t\"input\": this.evaluate.bind(this),\n\t\t\t\"blur\": this.close.bind(this, { reason: \"blur\" }),\n\t\t\t\"keydown\": function(evt) {\n\t\t\t\tvar c = evt.keyCode;\n\n\t\t\t\t// If the dropdown `ul` is in view, then act on keydown for the following keys:\n\t\t\t\t// Enter / Esc / Up / Down\n\t\t\t\tif(me.opened) {\n\t\t\t\t\tif (c === 13 && me.selected) { // Enter\n\t\t\t\t\t\tevt.preventDefault();\n\t\t\t\t\t\tme.select();\n\t\t\t\t\t}\n\t\t\t\t\telse if (c === 9 && me.selected && me.tabSelect) {\n\t\t\t\t\t\tme.select();\n\t\t\t\t\t}\n\t\t\t\t\telse if (c === 27) { // Esc\n\t\t\t\t\t\tme.close({ reason: \"esc\" });\n\t\t\t\t\t}\n\t\t\t\t\telse if (c === 38 || c === 40) { // Down/Up arrow\n\t\t\t\t\t\tevt.preventDefault();\n\t\t\t\t\t\tme[c === 38? \"previous\" : \"next\"]();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\tform: {\n\t\t\t\"submit\": this.close.bind(this, { reason: \"submit\" })\n\t\t},\n\t\tul: {\n\t\t\t// Prevent the default mousedowm, which ensures the input is not blurred.\n\t\t\t// The actual selection will happen on click. This also ensures dragging the\n\t\t\t// cursor away from the list item will cancel the selection\n\t\t\t\"mousedown\": function(evt) {\n\t\t\t\tevt.preventDefault();\n\t\t\t},\n\t\t\t// The click event is fired even if the corresponding mousedown event has called preventDefault\n\t\t\t\"click\": function(evt) {\n\t\t\t\tvar li = evt.target;\n\n\t\t\t\tif (li !== this) {\n\n\t\t\t\t\twhile (li && !/li/i.test(li.nodeName)) {\n\t\t\t\t\t\tli = li.parentNode;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (li && evt.button === 0) { // Only select on left click\n\t\t\t\t\t\tevt.preventDefault();\n\t\t\t\t\t\tme.select(li, evt.target);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t};\n\n\t$.bind(this.input, this._events.input);\n\t$.bind(this.input.form, this._events.form);\n\t$.bind(this.ul, this._events.ul);\n\n\tif (this.input.hasAttribute(\"list\")) {\n\t\tthis.list = \"#\" + this.input.getAttribute(\"list\");\n\t\tthis.input.removeAttribute(\"list\");\n\t}\n\telse {\n\t\tthis.list = this.input.getAttribute(\"data-list\") || o.list || [];\n\t}\n\n\t_.all.push(this);\n};\n\n_.prototype = {\n\tset list(list) {\n\t\tif (Array.isArray(list)) {\n\t\t\tthis._list = list;\n\t\t}\n\t\telse if (typeof list === \"string\" && list.indexOf(\",\") > -1) {\n\t\t\t\tthis._list = list.split(/\\s*,\\s*/);\n\t\t}\n\t\telse { // Element or CSS selector\n\t\t\tlist = $(list);\n\n\t\t\tif (list && list.children) {\n\t\t\t\tvar items = [];\n\t\t\t\tslice.apply(list.children).forEach(function (el) {\n\t\t\t\t\tif (!el.disabled) {\n\t\t\t\t\t\tvar text = el.textContent.trim();\n\t\t\t\t\t\tvar value = el.value || text;\n\t\t\t\t\t\tvar label = el.label || text;\n\t\t\t\t\t\tif (value !== \"\") {\n\t\t\t\t\t\t\titems.push({ label: label, value: value });\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tthis._list = items;\n\t\t\t}\n\t\t}\n\n\t\tif (document.activeElement === this.input) {\n\t\t\tthis.evaluate();\n\t\t}\n\t},\n\n\tget selected() {\n\t\treturn this.index > -1;\n\t},\n\n\tget opened() {\n\t\treturn this.isOpened;\n\t},\n\n\tclose: function (o) {\n\t\tif (!this.opened) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.input.setAttribute(\"aria-expanded\", \"false\");\n\t\tthis.ul.setAttribute(\"hidden\", \"\");\n\t\tthis.isOpened = false;\n\t\tthis.index = -1;\n\n\t\tthis.status.setAttribute(\"hidden\", \"\");\n\n\t\t$.fire(this.input, \"awesomplete-close\", o || {});\n\t},\n\n\topen: function () {\n\t\tthis.input.setAttribute(\"aria-expanded\", \"true\");\n\t\tthis.ul.removeAttribute(\"hidden\");\n\t\tthis.isOpened = true;\n\n\t\tthis.status.removeAttribute(\"hidden\");\n\n\t\tif (this.autoFirst && this.index === -1) {\n\t\t\tthis.goto(0);\n\t\t}\n\n\t\t$.fire(this.input, \"awesomplete-open\");\n\t},\n\n\tdestroy: function() {\n\t\t//remove events from the input and its form\n\t\t$.unbind(this.input, this._events.input);\n\t\t$.unbind(this.input.form, this._events.form);\n\n\t\t// cleanup container if it was created by Awesomplete but leave it alone otherwise\n\t\tif (!this.options.container) {\n\t\t\t//move the input out of the awesomplete container and remove the container and its children\n\t\t\tvar parentNode = this.container.parentNode;\n\n\t\t\tparentNode.insertBefore(this.input, this.container);\n\t\t\tparentNode.removeChild(this.container);\n\t\t}\n\n\t\t//remove autocomplete and aria-autocomplete attributes\n\t\tthis.input.removeAttribute(\"autocomplete\");\n\t\tthis.input.removeAttribute(\"aria-autocomplete\");\n\n\t\t//remove this awesomeplete instance from the global array of instances\n\t\tvar indexOfAwesomplete = _.all.indexOf(this);\n\n\t\tif (indexOfAwesomplete !== -1) {\n\t\t\t_.all.splice(indexOfAwesomplete, 1);\n\t\t}\n\t},\n\n\tnext: function () {\n\t\tvar count = this.ul.children.length;\n\t\tthis.goto(this.index < count - 1 ? this.index + 1 : (count ? 0 : -1) );\n\t},\n\n\tprevious: function () {\n\t\tvar count = this.ul.children.length;\n\t\tvar pos = this.index - 1;\n\n\t\tthis.goto(this.selected && pos !== -1 ? pos : count - 1);\n\t},\n\n\t// Should not be used, highlights specific item without any checks!\n\tgoto: function (i) {\n\t\tvar lis = this.ul.children;\n\n\t\tif (this.selected) {\n\t\t\tlis[this.index].setAttribute(\"aria-selected\", \"false\");\n\t\t}\n\n\t\tthis.index = i;\n\n\t\tif (i > -1 && lis.length > 0) {\n\t\t\tlis[i].setAttribute(\"aria-selected\", \"true\");\n\n\t\t\tthis.status.textContent = lis[i].textContent + \", list item \" + (i + 1) + \" of \" + lis.length;\n\n this.input.setAttribute(\"aria-activedescendant\", this.ul.id + \"_item_\" + this.index);\n\n\t\t\t// scroll to highlighted element in case parent's height is fixed\n\t\t\tthis.ul.scrollTop = lis[i].offsetTop - this.ul.clientHeight + lis[i].clientHeight;\n\n\t\t\t$.fire(this.input, \"awesomplete-highlight\", {\n\t\t\t\ttext: this.suggestions[this.index]\n\t\t\t});\n\t\t}\n\t},\n\n\tselect: function (selected, origin) {\n\t\tif (selected) {\n\t\t\tthis.index = $.siblingIndex(selected);\n\t\t} else {\n\t\t\tselected = this.ul.children[this.index];\n\t\t}\n\n\t\tif (selected) {\n\t\t\tvar suggestion = this.suggestions[this.index];\n\n\t\t\tvar allowed = $.fire(this.input, \"awesomplete-select\", {\n\t\t\t\ttext: suggestion,\n\t\t\t\torigin: origin || selected\n\t\t\t});\n\n\t\t\tif (allowed) {\n\t\t\t\tthis.replace(suggestion);\n\t\t\t\tthis.close({ reason: \"select\" });\n\t\t\t\t$.fire(this.input, \"awesomplete-selectcomplete\", {\n\t\t\t\t\ttext: suggestion\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t},\n\n\tevaluate: function() {\n\t\tvar me = this;\n\t\tvar value = this.input.value;\n\n\t\tif (value.length >= this.minChars && this._list && this._list.length > 0) {\n\t\t\tthis.index = -1;\n\t\t\t// Populate list with options that match\n\t\t\tthis.ul.innerHTML = \"\";\n\n\t\t\tthis.suggestions = this._list\n\t\t\t\t.map(function(item) {\n\t\t\t\t\treturn new Suggestion(me.data(item, value));\n\t\t\t\t})\n\t\t\t\t.filter(function(item) {\n\t\t\t\t\treturn me.filter(item, value);\n\t\t\t\t});\n\n\t\t\tif (this.sort !== false) {\n\t\t\t\tthis.suggestions = this.suggestions.sort(this.sort);\n\t\t\t}\n\n\t\t\tthis.suggestions = this.suggestions.slice(0, this.maxItems);\n\n\t\t\tthis.suggestions.forEach(function(text, index) {\n\t\t\t\t\tme.ul.appendChild(me.item(text, value, index));\n\t\t\t\t});\n\n\t\t\tif (this.ul.children.length === 0) {\n\n this.status.textContent = \"No results found\";\n\n\t\t\t\tthis.close({ reason: \"nomatches\" });\n\n\t\t\t} else {\n\t\t\t\tthis.open();\n\n this.status.textContent = this.ul.children.length + \" results found\";\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tthis.close({ reason: \"nomatches\" });\n\n this.status.textContent = \"No results found\";\n\t\t}\n\t}\n};\n\n// Static methods/properties\n\n_.all = [];\n\n_.FILTER_CONTAINS = function (text, input) {\n\treturn RegExp($.regExpEscape(input.trim()), \"i\").test(text);\n};\n\n_.FILTER_STARTSWITH = function (text, input) {\n\treturn RegExp(\"^\" + $.regExpEscape(input.trim()), \"i\").test(text);\n};\n\n_.SORT_BYLENGTH = function (a, b) {\n\tif (a.length !== b.length) {\n\t\treturn a.length - b.length;\n\t}\n\n\treturn a < b? -1 : 1;\n};\n\n_.CONTAINER = function (input) {\n\treturn $.create(\"div\", {\n\t\tclassName: \"awesomplete\",\n\t\taround: input\n\t});\n}\n\n_.ITEM = function (text, input, item_id) {\n\tvar html = input.trim() === \"\" ? text : text.replace(RegExp($.regExpEscape(input.trim()), \"gi\"), \"$&\");\n\treturn $.create(\"li\", {\n\t\tinnerHTML: html,\n\t\t\"role\": \"option\",\n\t\t\"aria-selected\": \"false\",\n\t\t\"id\": \"awesomplete_list_\" + this.count + \"_item_\" + item_id\n\t});\n};\n\n_.REPLACE = function (text) {\n\tthis.input.value = text.value;\n};\n\n_.DATA = function (item/*, input*/) { return item; };\n\n// Private functions\n\nfunction Suggestion(data) {\n\tvar o = Array.isArray(data)\n\t ? { label: data[0], value: data[1] }\n\t : typeof data === \"object\" && \"label\" in data && \"value\" in data ? data : { label: data, value: data };\n\n\tthis.label = o.label || o.value;\n\tthis.value = o.value;\n}\nObject.defineProperty(Suggestion.prototype = Object.create(String.prototype), \"length\", {\n\tget: function() { return this.label.length; }\n});\nSuggestion.prototype.toString = Suggestion.prototype.valueOf = function () {\n\treturn \"\" + this.label;\n};\n\nfunction configure(instance, properties, o) {\n\tfor (var i in properties) {\n\t\tvar initial = properties[i],\n\t\t attrValue = instance.input.getAttribute(\"data-\" + i.toLowerCase());\n\n\t\tif (typeof initial === \"number\") {\n\t\t\tinstance[i] = parseInt(attrValue);\n\t\t}\n\t\telse if (initial === false) { // Boolean options must be false by default anyway\n\t\t\tinstance[i] = attrValue !== null;\n\t\t}\n\t\telse if (initial instanceof Function) {\n\t\t\tinstance[i] = null;\n\t\t}\n\t\telse {\n\t\t\tinstance[i] = attrValue;\n\t\t}\n\n\t\tif (!instance[i] && instance[i] !== 0) {\n\t\t\tinstance[i] = (i in o)? o[i] : initial;\n\t\t}\n\t}\n}\n\n// Helpers\n\nvar slice = Array.prototype.slice;\n\nfunction $(expr, con) {\n\treturn typeof expr === \"string\"? (con || document).querySelector(expr) : expr || null;\n}\n\nfunction $$(expr, con) {\n\treturn slice.call((con || document).querySelectorAll(expr));\n}\n\n$.create = function(tag, o) {\n\tvar element = document.createElement(tag);\n\n\tfor (var i in o) {\n\t\tvar val = o[i];\n\n\t\tif (i === \"inside\") {\n\t\t\t$(val).appendChild(element);\n\t\t}\n\t\telse if (i === \"around\") {\n\t\t\tvar ref = $(val);\n\t\t\tref.parentNode.insertBefore(element, ref);\n\t\t\telement.appendChild(ref);\n\n\t\t\tif (ref.getAttribute(\"autofocus\") != null) {\n\t\t\t\tref.focus();\n\t\t\t}\n\t\t}\n\t\telse if (i in element) {\n\t\t\telement[i] = val;\n\t\t}\n\t\telse {\n\t\t\telement.setAttribute(i, val);\n\t\t}\n\t}\n\n\treturn element;\n};\n\n$.bind = function(element, o) {\n\tif (element) {\n\t\tfor (var event in o) {\n\t\t\tvar callback = o[event];\n\n\t\t\tevent.split(/\\s+/).forEach(function (event) {\n\t\t\t\telement.addEventListener(event, callback);\n\t\t\t});\n\t\t}\n\t}\n};\n\n$.unbind = function(element, o) {\n\tif (element) {\n\t\tfor (var event in o) {\n\t\t\tvar callback = o[event];\n\n\t\t\tevent.split(/\\s+/).forEach(function(event) {\n\t\t\t\telement.removeEventListener(event, callback);\n\t\t\t});\n\t\t}\n\t}\n};\n\n$.fire = function(target, type, properties) {\n\tvar evt = document.createEvent(\"HTMLEvents\");\n\n\tevt.initEvent(type, true, true );\n\n\tfor (var j in properties) {\n\t\tevt[j] = properties[j];\n\t}\n\n\treturn target.dispatchEvent(evt);\n};\n\n$.regExpEscape = function (s) {\n\treturn s.replace(/[-\\\\^$*+?.()|[\\]{}]/g, \"\\\\$&\");\n};\n\n$.siblingIndex = function (el) {\n\t/* eslint-disable no-cond-assign */\n\tfor (var i = 0; el = el.previousElementSibling; i++);\n\treturn i;\n};\n\n// Initialization\n\nfunction init() {\n\t$$(\"input.awesomplete\").forEach(function (input) {\n\t\tnew _(input);\n\t});\n}\n\n// Make sure to export Awesomplete on self when in a browser\nif (typeof self !== \"undefined\") {\n\tself.Awesomplete = _;\n}\n\n// Are we in a browser? Check for Document constructor\nif (typeof Document !== \"undefined\") {\n\t// DOM already loaded?\n\tif (document.readyState !== \"loading\") {\n\t\tinit();\n\t}\n\telse {\n\t\t// Wait for it\n\t\tdocument.addEventListener(\"DOMContentLoaded\", init);\n\t}\n}\n\n_.$ = $;\n_.$$ = $$;\n\n// Expose Awesomplete as a CJS module\nif (typeof module === \"object\" && module.exports) {\n\tmodule.exports = _;\n}\n\nreturn _;\n\n}());\n"]} \ No newline at end of file reverted: --- b/core/assets/vendor/awesomplete/awesomplete.theme.css +++ /dev/null @@ -1,69 +0,0 @@ -.awesomplete > ul { - border-radius: .3em; - margin: .2em 0 0; - background: hsla(0,0%,100%,.9); - background: linear-gradient(to bottom right, white, hsla(0,0%,100%,.8)); - border: 1px solid rgba(0,0,0,.3); - box-shadow: .05em .2em .6em rgba(0,0,0,.2); - text-shadow: none; -} - -@supports (transform: scale(0)) { - .awesomplete > ul { - transition: .3s cubic-bezier(.4,.2,.5,1.4); - transform-origin: 1.43em -.43em; - } - - .awesomplete > ul[hidden], - .awesomplete > ul:empty { - opacity: 0; - transform: scale(0); - display: block; - transition-timing-function: ease; - } -} - - /* Pointer */ - .awesomplete > ul:before { - content: ""; - position: absolute; - top: -.43em; - left: 1em; - width: 0; height: 0; - padding: .4em; - background: white; - border: inherit; - border-right: 0; - border-bottom: 0; - -webkit-transform: rotate(45deg); - transform: rotate(45deg); - } - - .awesomplete > ul > li { - position: relative; - padding: .2em .5em; - cursor: pointer; - } - - .awesomplete > ul > li:hover { - background: hsl(200, 40%, 80%); - color: black; - } - - .awesomplete > ul > li[aria-selected="true"] { - background: hsl(205, 40%, 40%); - color: white; - } - - .awesomplete mark { - background: hsl(65, 100%, 50%); - } - - .awesomplete li:hover mark { - background: hsl(68, 100%, 41%); - } - - .awesomplete li[aria-selected="true"] mark { - background: hsl(86, 100%, 21%); - color: inherit; - } \ No newline at end of file diff -u b/core/core.libraries.yml b/core/core.libraries.yml --- b/core/core.libraries.yml +++ b/core/core.libraries.yml @@ -104,28 +104,16 @@ - core/drupal - core/drupal.debounce -awesomplete: - version: 1.1.5 - license: - name: MIT - url: https://github.com/LeaVerou/awesomplete/blob/gh-pages/LICENSE - gpl-compatible: true - js: - assets/vendor/awesomplete/awesomplete.js: {} - css: - component: - assets/vendor/awesomplete/awesomplete.css: {} - drupal.autocomplete: version: VERSION js: - misc/autocomplete.js: {} + misc/autocomplete.js: { weight: -1 } dependencies: + - core/jquery - core/drupal - core/drupalSettings - core/drupal.ajax - - core/drupal.announce - - core/awesomplete + - core/jquery.ui.autocomplete drupal.batch: version: VERSION @@ -496,7 +484,6 @@ - core/jquery.ui.widget - core/jquery.ui.position - core/jquery.ui.menu - deprecated: The "%library_id%" asset library is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. See https://www.drupal.org/project/drupal/issues/3076171 jquery.ui.button: version: *jquery_ui_version @@ -505,6 +492,7 @@ - core/jquery.ui.widget - core/jquery.ui.position - core/jquery.ui.menu + deprecated: The "%library_id%" asset library is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. See https://www.drupal.org/project/drupal/issues/3076171 jquery.ui.button: version: *jquery_ui_version reverted: --- b/core/misc/autocomplete.es6.js +++ a/core/misc/autocomplete.es6.js @@ -1,11 +1,10 @@ /** * @file + * Autocomplete based on jQuery UI. - * Autocomplete based on Awesomplete. */ +(function($, Drupal) { -((Drupal, Awesomplete) => { let autocomplete; - const { announce, formatPlural } = Drupal; /** * Helper splitting terms from the autocomplete value. @@ -18,7 +17,7 @@ * @return {Array} * Array of values, split by comma. */ + function autocompleteSplitValues(value) { - const autocompleteSplitValues = value => { // We will match the value against comma-separated terms. const result = []; let quote = false; @@ -39,10 +38,11 @@ } } if (value.length > 0) { + result.push($.trim(current)); - result.push(current.trim()); } + return result; + } - }; /** * Returns the last value of an multi-value textfield. @@ -55,92 +55,150 @@ * @return {string} * The last value of the input field. */ + function extractLastTerm(terms) { + return autocomplete.splitValues(terms).pop(); + } - const extractLastTerm = terms => autocomplete.splitValues(terms).pop(); /** + * The search handler is called before a search is performed. - * Determines if a suggestion should be an available option. - * Does not use arrow syntax so there is access to Awesomplete's this object. - * @param {object} suggestion - * A suggestion based on user input. It is an object with label and value - * properties. - * @param {string} input - * The text entered in the input field. * + * @function Drupal.autocomplete.options.search + * + * @param {object} event + * The event triggered. + * + * @return {bool} + * Whether to perform a search or not. - * @return {boolean} - * If the suggestion should be displayed in the results. */ + function searchHandler(event) { + const options = autocomplete.options; - function filterResults(suggestion, input) { - const { options } = Drupal.autocomplete; - const inputElement = this.input; - const inputWrapper = inputElement.closest( - '[data-autocomplete-cardinality]', - ); - const cardinality = inputWrapper - ? inputWrapper.getAttribute('data-autocomplete-cardinality') - : -1; - const suggestionValue = suggestion.value; - const currentValues = autocomplete.splitValues(input); + if (options.isComposing) { - const inputSpecificFirstCharDenylist = inputElement.hasAttribute( - 'data-autocomplete-first-character-denylist', - ) - ? inputElement.getAttribute('data-autocomplete-first-character-denylist') - : ''; - - // Prevent suggestions if the maximum number of items is reached. - if (cardinality > 0 && currentValues.length > cardinality) { return false; } + const term = autocomplete.extractLastTerm(event.target.value); + // Abort search if the first character is in firstCharacterBlacklist. - // Prevent suggestions if the first input character is in the denylist. if ( + term.length > 0 && + options.firstCharacterBlacklist.indexOf(term[0]) !== -1 - options.firstCharacterDenylist.indexOf(input[0]) !== -1 || - inputSpecificFirstCharDenylist.indexOf(input[0]) !== -1 ) { return false; } + // Only search when the term is at least the minimum length. + return term.length >= options.minLength; + } + /** + * JQuery UI autocomplete source callback. + * + * @param {object} request + * The request object. + * @param {function} response + * The function to call with the response. + */ + function sourceData(request, response) { + const elementId = this.element.attr('id'); + + if (!(elementId in autocomplete.cache)) { + autocomplete.cache[elementId] = {}; + } + + /** + * Filter through the suggestions removing all terms already tagged and + * display the available terms to the user. + * + * @param {object} suggestions + * Suggestions returned by the server. + */ + function showSuggestions(suggestions) { + const tagged = autocomplete.splitValues(request.term); + const il = tagged.length; + for (let i = 0; i < il; i++) { + const index = suggestions.indexOf(tagged[i]); + if (index >= 0) { + suggestions.splice(index, 1); + } + } + response(suggestions); + } + + // Get the desired term and construct the autocomplete URL for it. + const term = autocomplete.extractLastTerm(request.term); + + /** + * Transforms the data object into an array and update autocomplete results. + * + * @param {object} data + * The data sent back from the server. + */ + function sourceCallbackHandler(data) { + autocomplete.cache[elementId][term] = data; + + // Send the new string array of terms to the jQuery UI list. + showSuggestions(data); - // Do not show items already added to field. - if (currentValues.indexOf(suggestionValue) !== -1) { - return false; } + // Check if the term is already cached. + if (autocomplete.cache[elementId].hasOwnProperty(term)) { + showSuggestions(autocomplete.cache[elementId][term]); + } else { + const options = $.extend( + { success: sourceCallbackHandler, data: { q: term } }, + autocomplete.ajax, + ); + $.ajax(this.element.attr('data-autocomplete-path'), options); + } - return Awesomplete.FILTER_CONTAINS(suggestion, extractLastTerm(input)); } /** + * Handles an autocompletefocus event. - * Displays a suggestion with the search string highlighted. * + * @return {bool} + * Always returns false. - * @param {object} suggestion - * A suggestion based on user input. It is an object with label and value - * properties. - * @param {string} input - * The text entered in the input field. - * @return {HTMLElement} - * A list element containing a suggestion. */ + function focusHandler() { + return false; + } - const displayItem = (suggestion, input) => Awesomplete.ITEM(suggestion, extractLastTerm(input)); /** + * Handles an autocompleteselect event. + * + * @param {jQuery.Event} event + * The event triggered. + * @param {object} ui + * The jQuery UI settings object. - * Replaces the value of an input field when a new value is chosen. - * Does not use arrow syntax so there is access to Awesomplete's this object. * + * @return {bool} + * Returns false to indicate the event status. - * @param {object} item - * The item being added to the field. Has label and value properties. */ + function selectHandler(event, ui) { + const terms = autocomplete.splitValues(event.target.value); + // Remove the current input. + terms.pop(); + // Add the selected item. + terms.push(ui.item.value); - function replaceInputValue(item) { - const inputElement = this.input; - const cardinality = inputElement - .closest('[data-autocomplete-cardinality]') - .getAttribute('data-autocomplete-cardinality'); - const numItems = autocomplete.splitValues(inputElement.value).length; + event.target.value = terms.join(', '); + // Return false to tell jQuery UI that we've filled in the value already. + return false; + } - // Add a comma separator if the field allows additional items. - const separator = - numItems < cardinality || parseInt(cardinality, 10) === 0 ? ',' : ''; + /** + * Override jQuery UI _renderItem function to output HTML by default. + * + * @param {jQuery} ul + * jQuery collection of the ul element. + * @param {object} item + * The list item to append. + * + * @return {jQuery} + * jQuery collection of the ul element. + */ + function renderItem(ul, item) { + return $('
  • ') + .append($('').html(item.label)) + .appendTo(ul); - const before = this.input.value.match(/^.+,\s*|/)[0]; - this.input.value = `${before}${item.value}${separator}`; } /** @@ -155,113 +213,40 @@ */ Drupal.behaviors.autocomplete = { attach(context) { + // Act on textfields with the "form-autocomplete" class. + const $autocomplete = $(context) + .find('input.form-autocomplete') + .once('autocomplete'); + if ($autocomplete.length) { + // Allow options to be overridden per instance. + const blacklist = $autocomplete.attr( + 'data-autocomplete-first-character-blacklist', + ); + $.extend(autocomplete.options, { + firstCharacterBlacklist: blacklist || '', + }); + // Use jQuery UI Autocomplete on the textfield. + $autocomplete.autocomplete(autocomplete.options).each(function() { + $(this).data('ui-autocomplete')._renderItem = + autocomplete.options.renderItem; + }); - const autoCompleteInputs = context.querySelectorAll( - 'input.form-autocomplete', - ); - - const { options } = Drupal.autocomplete; - - Array.prototype.forEach.call(autoCompleteInputs, autocompleteElement => { - // The first time autocomplete results appear on a field, the - // screenreader provides detailed instructions on how to navigate the - // options. After the first time, this variable is set to true so the - // screenreader does not provide lengthy instructions each time. - let autocompleteInstructionsRead = false; - - // Determine if a field accepts multiple values and set the Awesomplete - // data attribute accordingly. - const cardinality = autocompleteElement - .closest('[data-autocomplete-cardinality]') - .getAttribute('data-autocomplete-cardinality'); + // Use CompositionEvent to handle IME inputs. It requests remote server on "compositionend" event only. + $autocomplete.on('compositionstart.autocomplete', () => { + autocomplete.options.isComposing = true; - if (cardinality !== '1') { - autocompleteElement.setAttribute('data-multiple', ''); - } - - // Add screenreader-only information to label that specifies it is an - // autocomplete field. - const label = autocompleteElement - .closest('.form-item') - .querySelector('label'); - label.innerHTML += `(${Drupal.t( - 'Autocomplete input', - )})`; - - const awesomplete = new Awesomplete(autocompleteElement, { - minChars: options.minChars, - autoFirst: options.autoFirst, - sort: options.sort, - filter: options.filter, - item: options.item, - replace: options.replace, }); + $autocomplete.on('compositionend.autocomplete', () => { + autocomplete.options.isComposing = false; - - // Add class previously added by jQueryUI for backwards compatibility. - autocompleteElement.classList.add('ui-autocomplete-input'); - - // Change role to textbox, Awesomplete defaults to combobox. The - // screenreader instructions for combobox assume select options are - // already present, which is not the case with autocomplete fields. - autocompleteElement.setAttribute('role', 'textbox'); - - // Screenreader announces the number of results found, and if it is - // the first set of results for a field, additional instructions on - // navigating the results are provided. - const readResults = count => { - const beginning = formatPlural( - count, - 'There is one result available.', - 'There are @count results available.', - ); - const end = autocompleteInstructionsRead - ? '' - : ' Use up and down arrows to review and enter to select. Touch device users, explore by touch or with swipe gestures.'; - announce(Drupal.t(`${beginning}${end}`), 'assertive'); - autocompleteInstructionsRead = true; - }; - - // Responds to user input and generates autocomplete suggestions. - autocompleteElement.addEventListener('input', event => { - const input = event.target; - const inputId = input.getAttribute('id'); - const term = autocomplete.extractLastTerm(input.value); - - if (!(inputId in autocomplete.cache)) { - autocomplete.cache[inputId] = {}; - } - - if (term && term.length > 0) { - if (autocomplete.cache[inputId].hasOwnProperty(term)) { - awesomplete.list = autocomplete.cache[inputId][term]; - awesomplete.evaluate(); - readResults(awesomplete.ul.children.length); - } else { - const apiUrl = input.getAttribute('data-autocomplete-path'); - const xhr = new XMLHttpRequest(); - xhr.open('GET', `${apiUrl}?q=${term}`); - xhr.onload = () => { - if (xhr.status === 200) { - const results = JSON.parse(xhr.response); - awesomplete.list = results; - awesomplete.evaluate(); - autocomplete.cache[inputId][term] = results; - readResults(results.length); - } - }; - xhr.send(); - } - } }); + } - }); }, + detach(context, settings, trigger) { + if (trigger === 'unload') { + $(context) + .find('input.form-autocomplete') + .removeOnce('autocomplete') + .autocomplete('destroy'); + } - - detach(context) { - const autoCompleteInputs = context.querySelectorAll( - 'input.form-autocomplete', - ); - Array.prototype.forEach.call(autoCompleteInputs, autocompleteElement => { - autocompleteElement.removeEventListener('input'); - }); }, }; @@ -272,26 +257,32 @@ */ autocomplete = { cache: {}, + // Exposes options to allow overriding by contrib. splitValues: autocompleteSplitValues, extractLastTerm, + // jQuery UI autocomplete options. - // Awesomplete options. /** + * JQuery UI option object. - * Awesomplete option object. * * @name Drupal.autocomplete.options */ options: { + source: sourceData, + focus: focusHandler, + search: searchHandler, + select: selectHandler, + renderItem, + minLength: 1, + // Custom options, used by Drupal.autocomplete. + firstCharacterBlacklist: '', + // Custom options, indicate IME usage status. + isComposing: false, + }, + ajax: { + dataType: 'json', - filter: filterResults, - item: displayItem, - replace: replaceInputValue, - sort: false, - minChars: 1, - firstCharacterDenylist: ',', - autoFirst: false, - maxItems: 10, }, }; Drupal.autocomplete = autocomplete; +})(jQuery, Drupal); -})(Drupal, Awesomplete); reverted: --- b/core/misc/autocomplete.js +++ a/core/misc/autocomplete.js @@ -5,12 +5,10 @@ * @preserve **/ +(function ($, Drupal) { -(function (Drupal, Awesomplete) { var autocomplete = void 0; - var announce = Drupal.announce, - formatPlural = Drupal.formatPlural; + function autocompleteSplitValues(value) { - var autocompleteSplitValues = function autocompleteSplitValues(value) { var result = []; var quote = false; var current = ''; @@ -30,152 +28,137 @@ } } if (value.length > 0) { + result.push($.trim(current)); - result.push(current.trim()); } + return result; + } - }; + function extractLastTerm(terms) { - var extractLastTerm = function extractLastTerm(terms) { return autocomplete.splitValues(terms).pop(); + } - }; + function searchHandler(event) { + var options = autocomplete.options; - function filterResults(suggestion, input) { - var options = Drupal.autocomplete.options; + if (options.isComposing) { + return false; + } - var inputElement = this.input; - var inputWrapper = inputElement.closest('[data-autocomplete-cardinality]'); - var cardinality = inputWrapper ? inputWrapper.getAttribute('data-autocomplete-cardinality') : -1; - var suggestionValue = suggestion.value; - var currentValues = autocomplete.splitValues(input); + var term = autocomplete.extractLastTerm(event.target.value); - var inputSpecificFirstCharDenylist = inputElement.hasAttribute('data-autocomplete-first-character-denylist') ? inputElement.getAttribute('data-autocomplete-first-character-denylist') : ''; + if (term.length > 0 && options.firstCharacterBlacklist.indexOf(term[0]) !== -1) { - if (cardinality > 0 && currentValues.length > cardinality) { return false; } + return term.length >= options.minLength; + } + + function sourceData(request, response) { + var elementId = this.element.attr('id'); + + if (!(elementId in autocomplete.cache)) { + autocomplete.cache[elementId] = {}; - if (options.firstCharacterDenylist.indexOf(input[0]) !== -1 || inputSpecificFirstCharDenylist.indexOf(input[0]) !== -1) { - return false; } + function showSuggestions(suggestions) { + var tagged = autocomplete.splitValues(request.term); + var il = tagged.length; + for (var i = 0; i < il; i++) { + var index = suggestions.indexOf(tagged[i]); + if (index >= 0) { + suggestions.splice(index, 1); + } + } + response(suggestions); - if (currentValues.indexOf(suggestionValue) !== -1) { - return false; } + var term = autocomplete.extractLastTerm(request.term); - return Awesomplete.FILTER_CONTAINS(suggestion, extractLastTerm(input)); - } + function sourceCallbackHandler(data) { + autocomplete.cache[elementId][term] = data; - var displayItem = function displayItem(suggestion, input) { - return Awesomplete.ITEM(suggestion, extractLastTerm(input)); - }; + showSuggestions(data); + } - function replaceInputValue(item) { - var inputElement = this.input; - var cardinality = inputElement.closest('[data-autocomplete-cardinality]').getAttribute('data-autocomplete-cardinality'); - var numItems = autocomplete.splitValues(inputElement.value).length; + if (autocomplete.cache[elementId].hasOwnProperty(term)) { + showSuggestions(autocomplete.cache[elementId][term]); + } else { + var options = $.extend({ success: sourceCallbackHandler, data: { q: term } }, autocomplete.ajax); + $.ajax(this.element.attr('data-autocomplete-path'), options); + } + } - var separator = numItems < cardinality || parseInt(cardinality, 10) === 0 ? ',' : ''; + function focusHandler() { + return false; - var before = this.input.value.match(/^.+,\s*|/)[0]; - this.input.value = '' + before + item.value + separator; } + function selectHandler(event, ui) { + var terms = autocomplete.splitValues(event.target.value); - Drupal.behaviors.autocomplete = { - attach: function attach(context) { - var autoCompleteInputs = context.querySelectorAll('input.form-autocomplete'); + terms.pop(); - var options = Drupal.autocomplete.options; + terms.push(ui.item.value); + event.target.value = terms.join(', '); - Array.prototype.forEach.call(autoCompleteInputs, function (autocompleteElement) { - var autocompleteInstructionsRead = false; + return false; + } - var cardinality = autocompleteElement.closest('[data-autocomplete-cardinality]').getAttribute('data-autocomplete-cardinality'); + function renderItem(ul, item) { + return $('
  • ').append($('').html(item.label)).appendTo(ul); + } - if (cardinality !== '1') { - autocompleteElement.setAttribute('data-multiple', ''); - } + Drupal.behaviors.autocomplete = { + attach: function attach(context) { + var $autocomplete = $(context).find('input.form-autocomplete').once('autocomplete'); + if ($autocomplete.length) { + var blacklist = $autocomplete.attr('data-autocomplete-first-character-blacklist'); + $.extend(autocomplete.options, { + firstCharacterBlacklist: blacklist || '' + }); - var label = autocompleteElement.closest('.form-item').querySelector('label'); - label.innerHTML += '(' + Drupal.t('Autocomplete input') + ')'; + $autocomplete.autocomplete(autocomplete.options).each(function () { + $(this).data('ui-autocomplete')._renderItem = autocomplete.options.renderItem; - var awesomplete = new Awesomplete(autocompleteElement, { - minChars: options.minChars, - autoFirst: options.autoFirst, - sort: options.sort, - filter: options.filter, - item: options.item, - replace: options.replace }); + $autocomplete.on('compositionstart.autocomplete', function () { + autocomplete.options.isComposing = true; + }); + $autocomplete.on('compositionend.autocomplete', function () { + autocomplete.options.isComposing = false; - autocompleteElement.classList.add('ui-autocomplete-input'); - - autocompleteElement.setAttribute('role', 'textbox'); - - var readResults = function readResults(count) { - var beginning = formatPlural(count, 'There is one result available.', 'There are @count results available.'); - var end = autocompleteInstructionsRead ? '' : ' Use up and down arrows to review and enter to select. Touch device users, explore by touch or with swipe gestures.'; - announce(Drupal.t('' + beginning + end), 'assertive'); - autocompleteInstructionsRead = true; - }; - - autocompleteElement.addEventListener('input', function (event) { - var input = event.target; - var inputId = input.getAttribute('id'); - var term = autocomplete.extractLastTerm(input.value); - - if (!(inputId in autocomplete.cache)) { - autocomplete.cache[inputId] = {}; - } - - if (term && term.length > 0) { - if (autocomplete.cache[inputId].hasOwnProperty(term)) { - awesomplete.list = autocomplete.cache[inputId][term]; - awesomplete.evaluate(); - readResults(awesomplete.ul.children.length); - } else { - var apiUrl = input.getAttribute('data-autocomplete-path'); - var xhr = new XMLHttpRequest(); - xhr.open('GET', apiUrl + '?q=' + term); - xhr.onload = function () { - if (xhr.status === 200) { - var results = JSON.parse(xhr.response); - awesomplete.list = results; - awesomplete.evaluate(); - autocomplete.cache[inputId][term] = results; - readResults(results.length); - } - }; - xhr.send(); - } - } }); + } - }); }, + detach: function detach(context, settings, trigger) { + if (trigger === 'unload') { + $(context).find('input.form-autocomplete').removeOnce('autocomplete').autocomplete('destroy'); + } - detach: function detach(context) { - var autoCompleteInputs = context.querySelectorAll('input.form-autocomplete'); - Array.prototype.forEach.call(autoCompleteInputs, function (autocompleteElement) { - autocompleteElement.removeEventListener('input'); - }); } }; autocomplete = { cache: {}, + splitValues: autocompleteSplitValues, extractLastTerm: extractLastTerm, options: { + source: sourceData, + focus: focusHandler, + search: searchHandler, + select: selectHandler, + renderItem: renderItem, + minLength: 1, + + firstCharacterBlacklist: '', + + isComposing: false + }, + ajax: { + dataType: 'json' - filter: filterResults, - item: displayItem, - replace: replaceInputValue, - sort: false, - minChars: 1, - firstCharacterDenylist: ',', - autoFirst: false, - maxItems: 10 } }; Drupal.autocomplete = autocomplete; +})(jQuery, Drupal); \ No newline at end of file -})(Drupal, Awesomplete); reverted: --- b/core/modules/entity_reference/tests/modules/entity_reference_autocomplete_test/entity_reference_autocomplete_test.info.yml +++ /dev/null @@ -1,6 +0,0 @@ -name: 'Entity reference autocomplete test module' -type: module -description: 'For testing autocomplete functionality' -core: 8.x -package: Testing -version: VERSION reverted: --- b/core/modules/entity_reference/tests/modules/entity_reference_autocomplete_test/entity_reference_autocomplete_test.libraries.yml +++ /dev/null @@ -1,5 +0,0 @@ -autocomplete_events: - js: - js/autocomplete_events.js: {} - dependencies: - - core/drupal.autocomplete reverted: --- b/core/modules/entity_reference/tests/modules/entity_reference_autocomplete_test/entity_reference_autocomplete_test.module +++ /dev/null @@ -1,15 +0,0 @@ - { - /** - * Checks if the argument has the expected properties of the ui object passed - * by jQueryUI's autocomplete events. - * - * @param {object} ui - * An object with the contents of the selected option. - * @return {boolean} - * If the object has properties that match the expected structure. - */ - const uiHasExpectedProperties = ui => { - if (ui.hasOwnProperty('item') && ui.item !== null) { - if (ui.item.hasOwnProperty('value') && ui.item.hasOwnProperty('label')) { - return true; - } - } - return false; - }; - - // Listens to the autocomplete close event. - // Triggered when the suggestions are hidden. - $(document).on('autocompleteclose', (event, ui) => { - // The event target should be the autocomplete text input. - if (!$(event.target).hasClass('form-autocomplete')) { - return; - } - if ($.isEmptyObject(ui)) { - $('body').addClass('autocompleteclose-event-happened'); - } - }); - - // Listens to the autocomplete create event. - // Triggered when the autocomplete is created. - $(document).on('autocompletecreate', (event, ui) => { - // The event target should be the autocomplete text input. - if (!$(event.target).hasClass('form-autocomplete')) { - return; - } - if ($.isEmptyObject(ui)) { - $('body').addClass('autocompletecreate-event-happened'); - } - }); - - // Listens to the autocomplete focus event. - // Triggered when focus is moved to an item (not selecting). - $(document).on('autocompletefocus', (event, ui) => { - // The event target should be the autocomplete text input. - if (!$(event.target).hasClass('form-autocomplete')) { - return; - } - if (uiHasExpectedProperties(ui)) { - $('body').addClass('autocompletefocus-event-happened'); - } - }); - - // Listens to the autocomplete open event. - // Triggered when the suggestion menu is opened or updated. - $(document).on('autocompleteopen', (event, ui) => { - // The event target should be the autocomplete text input. - if (!$(event.target).hasClass('form-autocomplete')) { - return; - } - if ($.isEmptyObject(ui)) { - $('body').addClass('autocompleteopen-event-happened'); - } - }); - - // Listens to the autocomplete response event. - // Triggered after a search completes, before the menu is shown. - $(document).on('autocompleteresponse', (event, ui) => { - // The event target should be the autocomplete text input. - if (!$(event.target).hasClass('form-autocomplete')) { - return; - } - - // The ui variable has a different structure than it does other events. - // Typically it is null or represents a single selection. In this case it - // is an object containing all suggestions. - if ( - ui.hasOwnProperty('content') && - ui.content.length > 0 && - ui.content[0] !== null && - ui.content[0].hasOwnProperty('value') && - ui.content[0].hasOwnProperty('label') - ) { - $('body').addClass('autocompleteresponse-event-happened'); - } - }); - - // Listens to the autocomplete search event. - // Triggered before a search is performed, after minLength and delay are met. - $(document).on('autocompletesearch', (event, ui) => { - // The event target should be the autocomplete text input. - if (!$(event.target).hasClass('form-autocomplete')) { - return; - } - if ($.isEmptyObject(ui)) { - $('body').addClass('autocompletesearch-event-happened'); - } - }); - - // Listens to the autocomplete search event. - // Triggered when an item is selected from the menu. - $(document).on('autocompleteselect', (event, ui) => { - // The event target should be the autocomplete text input. - if (!$(event.target).hasClass('form-autocomplete')) { - return; - } - if (uiHasExpectedProperties(ui)) { - $('body').addClass('autocompleteselect-event-happened'); - } - }); - - // Listens to the autocomplete change event. - // Triggered when the field is blurred, only if the value has changed. - $(document).on('autocompletechange', (event, ui) => { - // Unlike other listeners in this file, the ui variable is not checked as - // its structure can vary depending on what triggers the event. Any - // listners to this event would not be able to make assumptions about the - // ui variable's structure. - // For similar reasons, event.target is not checked either. - $('body').addClass('autocompletechange-event-happened'); - }); -})(jQuery, Drupal); reverted: --- b/core/modules/entity_reference/tests/modules/entity_reference_autocomplete_test/js/autocomplete_events.js +++ /dev/null @@ -1,85 +0,0 @@ -/** -* DO NOT EDIT THIS FILE. -* See the following change record for more information, -* https://www.drupal.org/node/2815083 -* @preserve -**/ - -(function ($, Drupal) { - var uiHasExpectedProperties = function uiHasExpectedProperties(ui) { - if (ui.hasOwnProperty('item') && ui.item !== null) { - if (ui.item.hasOwnProperty('value') && ui.item.hasOwnProperty('label')) { - return true; - } - } - return false; - }; - - $(document).on('autocompleteclose', function (event, ui) { - if (!$(event.target).hasClass('form-autocomplete')) { - return; - } - if ($.isEmptyObject(ui)) { - $('body').addClass('autocompleteclose-event-happened'); - } - }); - - $(document).on('autocompletecreate', function (event, ui) { - if (!$(event.target).hasClass('form-autocomplete')) { - return; - } - if ($.isEmptyObject(ui)) { - $('body').addClass('autocompletecreate-event-happened'); - } - }); - - $(document).on('autocompletefocus', function (event, ui) { - if (!$(event.target).hasClass('form-autocomplete')) { - return; - } - if (uiHasExpectedProperties(ui)) { - $('body').addClass('autocompletefocus-event-happened'); - } - }); - - $(document).on('autocompleteopen', function (event, ui) { - if (!$(event.target).hasClass('form-autocomplete')) { - return; - } - if ($.isEmptyObject(ui)) { - $('body').addClass('autocompleteopen-event-happened'); - } - }); - - $(document).on('autocompleteresponse', function (event, ui) { - if (!$(event.target).hasClass('form-autocomplete')) { - return; - } - - if (ui.hasOwnProperty('content') && ui.content.length > 0 && ui.content[0] !== null && ui.content[0].hasOwnProperty('value') && ui.content[0].hasOwnProperty('label')) { - $('body').addClass('autocompleteresponse-event-happened'); - } - }); - - $(document).on('autocompletesearch', function (event, ui) { - if (!$(event.target).hasClass('form-autocomplete')) { - return; - } - if ($.isEmptyObject(ui)) { - $('body').addClass('autocompletesearch-event-happened'); - } - }); - - $(document).on('autocompleteselect', function (event, ui) { - if (!$(event.target).hasClass('form-autocomplete')) { - return; - } - if (uiHasExpectedProperties(ui)) { - $('body').addClass('autocompleteselect-event-happened'); - } - }); - - $(document).on('autocompletechange', function (event, ui) { - $('body').addClass('autocompletechange-event-happened'); - }); -})(jQuery, Drupal); \ No newline at end of file diff -u b/core/tests/Drupal/FunctionalJavascriptTests/EntityReference/EntityReferenceAutocompleteWidgetTest.php b/core/tests/Drupal/FunctionalJavascriptTests/EntityReference/EntityReferenceAutocompleteWidgetTest.php --- b/core/tests/Drupal/FunctionalJavascriptTests/EntityReference/EntityReferenceAutocompleteWidgetTest.php +++ b/core/tests/Drupal/FunctionalJavascriptTests/EntityReference/EntityReferenceAutocompleteWidgetTest.php @@ -24,11 +24,7 @@ /** * {@inheritdoc} */ - public static $modules = [ - 'node', - 'taxonomy', - 'entity_reference_autocomplete_test', - ]; + public static $modules = ['node', 'taxonomy']; /** * The test vocabulary. @@ -121,186 +117,36 @@ } /** - * Test that selecting terms with spaces and commas work properly. - * - * @dataProvider providerTestSeparators + * Test that spaces and commas work properly with autocomplete fields. */ - public function testExistingSeparators($term_to_select) { + public function testSeparators() { $this->createTagsFieldOnPage(); - $terms = ['I,love,commas', 'Just a fan of spaces', 'Commas, and spaces']; - $terms_not_selected = array_values(array_diff($terms, [$term_to_select])); - - $this->createTerm($this->vocabulary, ['name' => $terms[0]]); - $this->createTerm($this->vocabulary, ['name' => $terms[1]]); - $this->createTerm($this->vocabulary, ['name' => $terms[2]]); + $term_commas = 'I,love,commas'; + $term_spaces = 'Just a fan of spaces'; + $term_commas_spaces = 'I dig both commas and spaces, apparently'; + + $this->createTerm($this->vocabulary, ['name' => $term_commas]); + $this->createTerm($this->vocabulary, ['name' => $term_spaces]); + $this->createTerm($this->vocabulary, ['name' => $term_commas_spaces]); $this->drupalGet('node/add/page'); $page = $this->getSession()->getPage(); $assert_session = $this->assertSession(); $autocomplete_field = $assert_session->waitForElement('css', '[name="taxonomy_reference[target_id]"]'); - $autocomplete_field->setValue('a'); + $this->getSession()->getDriver()->keyDown($autocomplete_field->getXpath(), ' '); $assert_session->waitOnAutocomplete(); $results = $page->findAll('css', '.ui-autocomplete li'); $this->assertCount(3, $results); - $assert_session->elementExists('css', '.ui-autocomplete li:contains("' . $term_to_select . '")')->click(); - $assert_session->pageTextNotContains($terms_not_selected[0]); - $assert_session->pageTextNotContains($terms_not_selected[1]); + $assert_session->elementExists('css', '.ui-autocomplete li:contains("' . $term_commas_spaces . '")')->click(); + $assert_session->pageTextNotContains($term_commas); + $assert_session->pageTextNotContains($term_spaces); $current_value = $autocomplete_field->getValue(); - $this->assertContains($term_to_select, $current_value); - $page->fillField('Title', 'Just a node in a test'); - $page->pressButton('Save'); - - // The term as it appeared in the autocomplete suggestions should be the - // same term on the page. - $assert_session->pageTextContains($term_to_select); - } - - /** - * Test that adding terms with spaces and commas work properly. - * - * @dataProvider providerTestSeparators - */ - public function testAddingSeparators($term_to_add) { - $this->createTagsFieldOnPage(); - $this->drupalGet('node/add/page'); - $page = $this->getSession()->getPage(); - $assert_session = $this->assertSession(); - - // If the term has a comma, it must be enclosed in quotes. - $term_for_input = strpos($term_to_add, ',') !== FALSE ? '"' . $term_to_add . '"' : $term_to_add; - $page->fillField('Tags', $term_for_input); - $page->fillField('Title', 'Just a node in a test'); - $page->pressButton('Save'); - - // Confirm term was added in its expected formatting. - $assert_session->pageTextContains($term_to_add); - - // Confirm term is not wrapped in quotes. - $assert_session->pageTextNotContains('"' . $term_to_add . '"'); - } - - /** - * Data provider for testSeparators(). - */ - public function providerTestSeparators() { - return [ - ['I,love,commas'], - ['Just a fan of spaces'], - ['Commas, and spaces'], - ]; - } - - /** - * Provides data to self::testSetMethod(). - */ - public function providerSetRequestMethod() { - return [ - ['GET'], - ['POST'], - ]; - } - - /** - * Checks that an autocomplete event has triggered. - * - * @param string|array $event - * The autocomplete event/events expected to trigger. - */ - protected function assertEventHappened($event) { - $assert_session = $this->assertSession(); - if (is_array($event)) { - foreach ($event as $an_event) { - $this->assertNotNull($assert_session->waitForElementVisible('css', ".autocomplete$an_event-event-happened")); - } - } - else { - $this->assertNotNull($assert_session->waitForElementVisible('css', ".autocomplete$event-event-happened")); - } - } - - /** - * Checks that an autocomplete event has not triggered. - * - * @param string|array $event - * The autocomplete event/events that should not have triggered. - */ - protected function assertEventNotHappened($event) { - $assert_session = $this->assertSession(); - if (is_array($event)) { - foreach ($event as $an_event) { - $this->assertNull($assert_session->waitForElementVisible('css', ".autocomplete$an_event-event-happened", 5000)); - } - } - else { - $this->assertNull($assert_session->waitForElementVisible('css', ".autocomplete$event-event-happened", 5000)); - } - } - - /** - * Test that markup and events match that of jQueryUI autocomplete. - */ - public function testMarkup() { - $this->createTagsFieldOnPage(); - - $this->createTerm($this->vocabulary, ['name' => 'apple']); - $this->createTerm($this->vocabulary, ['name' => 'banana']); - $this->createTerm($this->vocabulary, ['name' => 'carrot']); - - $this->drupalGet('node/add/page'); - $page = $this->getSession()->getPage(); - $assert_session = $this->assertSession(); - $this->assertNotNull($page->find('css', '.autocompletecreate-event-happened')); - - $autocomplete_field = $assert_session->elementExists('css', '[name="taxonomy_reference[target_id]"]'); - $this->assertTrue($autocomplete_field->hasClass('form-autocomplete')); - $this->assertTrue($autocomplete_field->hasClass('ui-autocomplete-input')); - $this->assertEventNotHappened(['open', 'response', 'search']); - - $autocomplete_field->setValue('a'); - $assert_session->waitOnAutocomplete(); - $this->assertEventHappened(['open', 'response', 'search']); - - $this->assertNull($page->find('css', '.ui-state-active')); - $suggestion_list = $page->find('css', '.ui-autocomplete'); - foreach (['ui-menu', 'ui-widget', 'ui-widget-content', 'ui-front'] as $class) { - $this->assertTrue($suggestion_list->hasClass($class)); - } - $menu_items = $suggestion_list->findAll('css', 'li'); - $this->assertCount(3, $menu_items); - foreach ($menu_items as $item) { - $this->assertTrue($item->hasClass('ui-menu-item')); - $link = $item->find('css', 'a'); - $this->assertTrue($link->hasClass('ui-menu-item-wrapper')); - $this->assertTrue($link->hasAttribute('tabindex')); - $this->assertEqual($link->getAttribute('tabindex'), '-1'); - } - - $this->assertEventNotHappened('focus'); - // Arrow down to the first suggestion. - $autocomplete_field->keyDown($autocomplete_field->getXpath(), 40); - $active_selection = $page->find('css', '.ui-menu-item .ui-state-active'); - $this->assertNotNull($active_selection); - $this->assertEventHappened('focus'); - $this->assertEventNotHappened(['select', 'close']); - - $active_selection->click(); - - // Wait for suggestions to become hidden -- cant use - // JSWebAssert::assertNoElementAfterWait() because the suggestions are - // still part of the DOM, just hidden. - $condition = "jQuery('.ui-autocomplete:visible').length === 0"; - $this->assertJsCondition($condition); - $this->assertEventHappened(['select', 'close']); - - // The change event only happens when the autocomplete input is blurred. - $this->assertEventNotHappened('change'); - $assert_session->elementExists('css', '[name="title[0][value]"]')->setValue('FORCING A BLUR OF THE AUTOCOMPLETE FIELD'); - $this->assertEventHappened('change'); + $this->assertContains($term_commas_spaces, $current_value); } /** reverted: --- b/core/tests/Drupal/FunctionalJavascriptTests/JSWebAssert.php +++ a/core/tests/Drupal/FunctionalJavascriptTests/JSWebAssert.php @@ -215,7 +215,7 @@ */ public function waitOnAutocomplete() { // Wait for the autocomplete to be visible. + return $this->waitForElementVisible('css', '.ui-autocomplete li'); - return $this->waitForElementVisible('css', '.awesomplete li'); } /** only in patch2: unchanged: --- /dev/null +++ b/core/modules/awesomplete_autocomplete/awesomplete_autocomplete.info.yml @@ -0,0 +1,5 @@ +name: 'Awesomplete Autocomplete' +type: module +description: 'Provides a replacement for jQuery UI Autocomplete which will be deprecated in Drupal 9.0.' +package: 'Core (Experimental)' +core: 8.x only in patch2: unchanged: --- /dev/null +++ b/core/modules/awesomplete_autocomplete/awesomplete_autocomplete.libraries.yml @@ -0,0 +1,23 @@ +awesomplete: + version: 1.1.5 + license: + name: MIT + url: https://github.com/LeaVerou/awesomplete/blob/gh-pages/LICENSE + gpl-compatible: true + js: + js/awesomplete.min.js: {} + css: + component: + css/awesomplete.base.css: {} + css/awesomplete.theme.css: {} + +autocomplete: + version: VERSION + js: + js/autocomplete.js: {} + dependencies: + - core/drupal + - core/drupalSettings + - core/drupal.ajax + - core/drupal.announce + - awesomplete_autocomplete/awesomplete only in patch2: unchanged: --- /dev/null +++ b/core/modules/awesomplete_autocomplete/awesomplete_autocomplete.module @@ -0,0 +1,37 @@ +' . t('About') . ''; + $output .= '

    ' . t('The Awesomplete Autocomplete module implements a replacement for jQuery UI Autocomplete. The asset library core/jquery.ui.autocomplete is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Enabling this module overrides the current core asset library to evaluate Awesomplete and to assist with making any necessary style or API changes to themes or modules.') . '

    '; + $output .= '

    ' . t('For more information, see the issue to replace jQuery UI Autocomplete.', [':awesomplete_autocomplete' => 'https://www.drupal.org/project/drupal/issues/3076171']) . '

    '; + return $output; + } +} + +/** + * Implements hook_library_info_alter(). + */ +function awesomplete_autocomplete_library_info_alter(&$libraries, $extension) { + if ($extension == 'core' && isset($libraries['drupal.autocomplete'])) { + // Module full path. + $path = drupal_get_path('module', 'awesomplete_autocomplete') . '/js'; + // Remove core js. + $libraries['drupal.autocomplete']['js'] = []; + // Replace dependency. + if ($index = array_search('core/jquery.ui.autocomplete', $libraries['drupal.autocomplete']['dependencies'])) { + $libraries['drupal.autocomplete']['dependencies'][$index] = 'awesomplete_autocomplete/autocomplete'; + } + } +} only in patch2: unchanged: --- /dev/null +++ b/core/modules/awesomplete_autocomplete/css/awesomplete.base.css @@ -0,0 +1,33 @@ +.awesomplete [hidden] { + display: none; +} + +.awesomplete .visually-hidden { + position: absolute; + clip: rect(0, 0, 0, 0); +} + +.awesomplete { + display: inline-block; + position: relative; +} + +.awesomplete > input { + display: block; +} + +.awesomplete > ul { + position: absolute; + left: 0; + z-index: 1; + min-width: 100%; + box-sizing: border-box; + list-style: none; + padding: 0; + margin: 0; + background: #fff; +} + +.awesomplete > ul:empty { + display: none; +} only in patch2: unchanged: --- /dev/null +++ b/core/modules/awesomplete_autocomplete/css/awesomplete.theme.css @@ -0,0 +1,12 @@ +.awesomplete > ul > li { + padding: 3px 1em 3px 0.4em; + cursor: pointer; +} + +.awesomplete > ul > li:focus, +.awesomplete > ul > li:hover, +.awesomplete > ul > li[aria-selected="true"] { + margin: 0; + color: #fff; + background: #0072b9; +} only in patch2: unchanged: --- /dev/null +++ b/core/modules/awesomplete_autocomplete/js/autocomplete.es6.js @@ -0,0 +1,308 @@ +/** + * @file + * Autocomplete based on Awesomplete. + */ + +((Drupal, Awesomplete) => { + let autocomplete; + const { announce, formatPlural } = Drupal; + + /** + * Helper splitting terms from the autocomplete value. + * + * @function Drupal.autocomplete.splitValues + * + * @param {string} value + * The value being entered by the user. + * + * @return {Array} + * Array of values, split by comma. + */ + const autocompleteSplitValues = value => { + // We will match the value against comma-separated terms. + const result = []; + let quote = false; + let current = ''; + const valueLength = value.length; + let character; + + for (let i = 0; i < valueLength; i++) { + character = value.charAt(i); + if (character === '"') { + current += character; + quote = !quote; + } else if (character === ',' && !quote) { + result.push(current.trim()); + current = ''; + } else { + current += character; + } + } + if (value.length > 0) { + result.push(current.trim()); + } + return result; + }; + + /** + * Returns the last value of an multi-value textfield. + * + * @function Drupal.autocomplete.extractLastTerm + * + * @param {string} terms + * The value of the field. + * + * @return {string} + * The last value of the input field. + */ + const extractLastTerm = terms => autocomplete.splitValues(terms).pop(); + + /** + * Return the id from a term value. + * + * @param {string} term + * @returns {number} + */ + const extractTermID = term => term.value.search(/\((\d+)\)$/); + + /** + * Determines if a suggestion should be an available option. + * Does not use arrow syntax so there is access to Awesomplete's this object. + * @param {object} suggestion + * A suggestion based on user input. It is an object with label and value + * properties. + * @param {string} input + * The text entered in the input field. + * + * @return {boolean} + * If the suggestion should be displayed in the results. + */ + function filterResults(suggestion, input) { + const { options } = Drupal.autocomplete; + const inputElement = this.input; + const inputWrapper = inputElement.closest( + '[data-autocomplete-cardinality]', + ); + const cardinality = inputWrapper + ? inputWrapper.getAttribute('data-autocomplete-cardinality') + : -1; + const suggestionValue = suggestion.value; + const currentValues = autocomplete.splitValues(input); + + const inputSpecificFirstCharDenylist = inputElement.hasAttribute( + 'data-autocomplete-first-character-denylist', + ) + ? inputElement.getAttribute('data-autocomplete-first-character-denylist') + : ''; + + // Prevent suggestions if the maximum number of items is reached. + if (cardinality > 0 && currentValues.length > cardinality) { + return false; + } + + // Prevent suggestions if the first input character is in the denylist. + if ( + options.firstCharacterDenylist.indexOf(input[0]) !== -1 || + inputSpecificFirstCharDenylist.indexOf(input[0]) !== -1 + ) { + return false; + } + + // Do not show items already added to field. + if (currentValues.indexOf(suggestionValue) !== -1) { + return false; + } + + return Awesomplete.FILTER_CONTAINS(suggestion, extractLastTerm(input)); + } + + /** + * Displays a suggestion with the search string highlighted. + * + * @param {object} suggestion + * A suggestion based on user input. It is an object with label and value + * properties. + * @param {string} input + * The text entered in the input field. + * @return {HTMLElement} + * A list element containing a suggestion. + */ + const displayItem = (suggestion, input) => Awesomplete.ITEM(suggestion, extractLastTerm(input), extractTermID(suggestion)); + + /** + * Replaces the value of an input field when a new value is chosen. + * Does not use arrow syntax so there is access to Awesomplete's this object. + * + * @param {object} item + * The item being added to the field. Has label and value properties. + */ + function replaceInputValue(item) { + const inputElement = this.input; + const cardinality = inputElement + .closest('[data-autocomplete-cardinality]') + .getAttribute('data-autocomplete-cardinality'); + const numItems = autocomplete.splitValues(inputElement.value).length; + + // Add a comma separator if the field allows additional items. + const separator = + numItems < cardinality || parseInt(cardinality, 10) === 0 ? ',' : ''; + + const before = this.input.value.match(/^.+,\s*|/)[0]; + this.input.value = `${before}${item.value}${separator}`; + } + + /** + * Attaches the autocomplete behavior to all required fields. + * + * @type {Drupal~behavior} + * + * @prop {Drupal~behaviorAttach} attach + * Attaches the autocomplete behaviors. + * @prop {Drupal~behaviorDetach} detach + * Detaches the autocomplete behaviors. + */ + Drupal.behaviors.autocomplete = { + attach(context) { + const autoCompleteInputs = context.querySelectorAll( + 'input.form-autocomplete', + ); + + const { options } = Drupal.autocomplete; + + Array.prototype.forEach.call(autoCompleteInputs, autocompleteElement => { + // The first time autocomplete results appear on a field, the + // screenreader provides detailed instructions on how to navigate the + // options. After the first time, this variable is set to true so the + // screenreader does not provide lengthy instructions each time. + let autocompleteInstructionsRead = false; + + // Determine if a field accepts multiple values and set the Awesomplete + // data attribute accordingly. + const cardinality = autocompleteElement + .closest('[data-autocomplete-cardinality]') + .getAttribute('data-autocomplete-cardinality'); + + if (cardinality !== '1') { + autocompleteElement.setAttribute('data-multiple', ''); + } + + // Add screenreader-only information to label that specifies it is an + // autocomplete field. + const label = autocompleteElement + .closest('.form-item') + .querySelector('label'); + label.innerHTML += `(${Drupal.t( + 'Autocomplete input', + )})`; + + const awesomplete = new Awesomplete(autocompleteElement, { + minChars: options.minChars, + autoFirst: options.autoFirst, + sort: options.sort, + filter: options.filter, + item: options.item, + replace: options.replace, + }); + + // Add class previously added by jQueryUI for backwards compatibility. + autocompleteElement.classList.add('ui-autocomplete-input'); + awesomplete.ul.classList.add('ui-autocomplete'); + + // Change role to textbox, Awesomplete defaults to combobox. The + // screenreader instructions for combobox assume select options are + // already present, which is not the case with autocomplete fields. + autocompleteElement.setAttribute('role', 'textbox'); + + // Screenreader announces the number of results found, and if it is + // the first set of results for a field, additional instructions on + // navigating the results are provided. + const readResults = count => { + const beginning = formatPlural( + count, + 'There is one result available.', + 'There are @count results available.', + ); + const end = autocompleteInstructionsRead + ? '' + : ' Use up and down arrows to review and enter to select. Touch device users, explore by touch or with swipe gestures.'; + announce(Drupal.t(`${beginning}${end}`), 'assertive'); + autocompleteInstructionsRead = true; + }; + + // Responds to user input and generates autocomplete suggestions. + autocompleteElement.addEventListener('input', event => { + const input = event.target; + const inputId = input.getAttribute('id'); + const term = autocomplete.extractLastTerm(input.value); + + if (!(inputId in autocomplete.cache)) { + autocomplete.cache[inputId] = {}; + } + + if (term && term.length > 0) { + if (autocomplete.cache[inputId].hasOwnProperty(term)) { + awesomplete.list = autocomplete.cache[inputId][term]; + awesomplete.evaluate(); + readResults(awesomplete.ul.children.length); + } else { + const apiUrl = input.getAttribute('data-autocomplete-path'); + input.classList.add('ui-autocomplete-loading'); + const xhr = new XMLHttpRequest(); + xhr.open('GET', `${apiUrl}?q=${term}`); + xhr.onload = () => { + input.classList.remove('ui-autocomplete-loading'); + if (xhr.status === 200) { + const results = JSON.parse(xhr.response); + awesomplete.list = results; + awesomplete.evaluate(); + autocomplete.cache[inputId][term] = results; + readResults(results.length); + } + }; + xhr.send(); + } + } + }); + }); + }, + + detach(context) { + const autoCompleteInputs = context.querySelectorAll( + 'input.form-autocomplete', + ); + Array.prototype.forEach.call(autoCompleteInputs, autocompleteElement => { + autocompleteElement.removeEventListener('input'); + }); + }, + }; + + /** + * Autocomplete object implementation. + * + * @namespace Drupal.autocomplete + */ + autocomplete = { + cache: {}, + splitValues: autocompleteSplitValues, + extractLastTerm, + // Awesomplete options. + + /** + * Awesomplete option object. + * + * @name Drupal.autocomplete.options + */ + options: { + filter: filterResults, + item: displayItem, + replace: replaceInputValue, + sort: false, + minChars: 1, + firstCharacterDenylist: ',', + autoFirst: false, + maxItems: 10, + }, + }; + + Drupal.autocomplete = autocomplete; +})(Drupal, Awesomplete); only in patch2: unchanged: --- /dev/null +++ b/core/modules/awesomplete_autocomplete/js/autocomplete.js @@ -0,0 +1,188 @@ +/** +* DO NOT EDIT THIS FILE. +* See the following change record for more information, +* https://www.drupal.org/node/2815083 +* @preserve +**/ + +(function (Drupal, Awesomplete) { + var autocomplete = void 0; + var announce = Drupal.announce, + formatPlural = Drupal.formatPlural; + + var autocompleteSplitValues = function autocompleteSplitValues(value) { + var result = []; + var quote = false; + var current = ''; + var valueLength = value.length; + var character = void 0; + + for (var i = 0; i < valueLength; i++) { + character = value.charAt(i); + if (character === '"') { + current += character; + quote = !quote; + } else if (character === ',' && !quote) { + result.push(current.trim()); + current = ''; + } else { + current += character; + } + } + if (value.length > 0) { + result.push(current.trim()); + } + return result; + }; + + var extractLastTerm = function extractLastTerm(terms) { + return autocomplete.splitValues(terms).pop(); + }; + + var extractTermID = function extractTermID(term) { + return term.value.search(/\((\d+)\)$/); + }; + + function filterResults(suggestion, input) { + var options = Drupal.autocomplete.options; + + var inputElement = this.input; + var inputWrapper = inputElement.closest('[data-autocomplete-cardinality]'); + var cardinality = inputWrapper ? inputWrapper.getAttribute('data-autocomplete-cardinality') : -1; + var suggestionValue = suggestion.value; + var currentValues = autocomplete.splitValues(input); + + var inputSpecificFirstCharDenylist = inputElement.hasAttribute('data-autocomplete-first-character-denylist') ? inputElement.getAttribute('data-autocomplete-first-character-denylist') : ''; + + if (cardinality > 0 && currentValues.length > cardinality) { + return false; + } + + if (options.firstCharacterDenylist.indexOf(input[0]) !== -1 || inputSpecificFirstCharDenylist.indexOf(input[0]) !== -1) { + return false; + } + + if (currentValues.indexOf(suggestionValue) !== -1) { + return false; + } + + return Awesomplete.FILTER_CONTAINS(suggestion, extractLastTerm(input)); + } + + var displayItem = function displayItem(suggestion, input) { + return Awesomplete.ITEM(suggestion, extractLastTerm(input), extractTermID(suggestion)); + }; + + function replaceInputValue(item) { + var inputElement = this.input; + var cardinality = inputElement.closest('[data-autocomplete-cardinality]').getAttribute('data-autocomplete-cardinality'); + var numItems = autocomplete.splitValues(inputElement.value).length; + + var separator = numItems < cardinality || parseInt(cardinality, 10) === 0 ? ',' : ''; + + var before = this.input.value.match(/^.+,\s*|/)[0]; + this.input.value = '' + before + item.value + separator; + } + + Drupal.behaviors.autocomplete = { + attach: function attach(context) { + var autoCompleteInputs = context.querySelectorAll('input.form-autocomplete'); + + var options = Drupal.autocomplete.options; + + + Array.prototype.forEach.call(autoCompleteInputs, function (autocompleteElement) { + var autocompleteInstructionsRead = false; + + var cardinality = autocompleteElement.closest('[data-autocomplete-cardinality]').getAttribute('data-autocomplete-cardinality'); + + if (cardinality !== '1') { + autocompleteElement.setAttribute('data-multiple', ''); + } + + var label = autocompleteElement.closest('.form-item').querySelector('label'); + label.innerHTML += '(' + Drupal.t('Autocomplete input') + ')'; + + var awesomplete = new Awesomplete(autocompleteElement, { + minChars: options.minChars, + autoFirst: options.autoFirst, + sort: options.sort, + filter: options.filter, + item: options.item, + replace: options.replace + }); + + autocompleteElement.classList.add('ui-autocomplete-input'); + awesomplete.ul.classList.add('ui-autocomplete'); + + autocompleteElement.setAttribute('role', 'textbox'); + + var readResults = function readResults(count) { + var beginning = formatPlural(count, 'There is one result available.', 'There are @count results available.'); + var end = autocompleteInstructionsRead ? '' : ' Use up and down arrows to review and enter to select. Touch device users, explore by touch or with swipe gestures.'; + announce(Drupal.t('' + beginning + end), 'assertive'); + autocompleteInstructionsRead = true; + }; + + autocompleteElement.addEventListener('input', function (event) { + var input = event.target; + var inputId = input.getAttribute('id'); + var term = autocomplete.extractLastTerm(input.value); + + if (!(inputId in autocomplete.cache)) { + autocomplete.cache[inputId] = {}; + } + + if (term && term.length > 0) { + if (autocomplete.cache[inputId].hasOwnProperty(term)) { + awesomplete.list = autocomplete.cache[inputId][term]; + awesomplete.evaluate(); + readResults(awesomplete.ul.children.length); + } else { + var apiUrl = input.getAttribute('data-autocomplete-path'); + input.classList.add('ui-autocomplete-loading'); + var xhr = new XMLHttpRequest(); + xhr.open('GET', apiUrl + '?q=' + term); + xhr.onload = function () { + input.classList.remove('ui-autocomplete-loading'); + if (xhr.status === 200) { + var results = JSON.parse(xhr.response); + awesomplete.list = results; + awesomplete.evaluate(); + autocomplete.cache[inputId][term] = results; + readResults(results.length); + } + }; + xhr.send(); + } + } + }); + }); + }, + detach: function detach(context) { + var autoCompleteInputs = context.querySelectorAll('input.form-autocomplete'); + Array.prototype.forEach.call(autoCompleteInputs, function (autocompleteElement) { + autocompleteElement.removeEventListener('input'); + }); + } + }; + + autocomplete = { + cache: {}, + splitValues: autocompleteSplitValues, + extractLastTerm: extractLastTerm, + + options: { + filter: filterResults, + item: displayItem, + replace: replaceInputValue, + sort: false, + minChars: 1, + firstCharacterDenylist: ',', + autoFirst: false, + maxItems: 10 + } + }; + + Drupal.autocomplete = autocomplete; +})(Drupal, Awesomplete); \ No newline at end of file only in patch2: unchanged: --- /dev/null +++ b/core/modules/awesomplete_autocomplete/js/awesomplete.min.js @@ -0,0 +1,3 @@ +// Awesomplete - Lea Verou - MIT license +!function(){function t(t){var e=Array.isArray(t)?{label:t[0],value:t[1]}:"object"==typeof t&&"label"in t&&"value"in t?t:{label:t,value:t};this.label=e.label||e.value,this.value=e.value}function e(t,e,i){for(var n in e){var s=e[n],r=t.input.getAttribute("data-"+n.toLowerCase());"number"==typeof s?t[n]=parseInt(r):!1===s?t[n]=null!==r:s instanceof Function?t[n]=null:t[n]=r,t[n]||0===t[n]||(t[n]=n in i?i[n]:s)}}function i(t,e){return"string"==typeof t?(e||document).querySelector(t):t||null}function n(t,e){return o.call((e||document).querySelectorAll(t))}function s(){n("input.awesomplete").forEach(function(t){new r(t)})}var r=function(t,n){var s=this;r.count=(r.count||0)+1,this.count=r.count,this.isOpened=!1,this.input=i(t),this.input.setAttribute("autocomplete","off"),this.input.setAttribute("aria-expanded","false"),this.input.setAttribute("aria-owns","awesomplete_list_"+this.count),this.input.setAttribute("role","combobox"),this.options=n=n||{},e(this,{minChars:2,maxItems:10,autoFirst:!1,data:r.DATA,filter:r.FILTER_CONTAINS,sort:!1!==n.sort&&r.SORT_BYLENGTH,container:r.CONTAINER,item:r.ITEM,replace:r.REPLACE,tabSelect:!1},n),this.index=-1,this.container=this.container(t),this.ul=i.create("ul",{hidden:"hidden",role:"listbox",id:"awesomplete_list_"+this.count,inside:this.container}),this.status=i.create("span",{className:"visually-hidden",role:"status","aria-live":"assertive","aria-atomic":!0,inside:this.container,textContent:0!=this.minChars?"Type "+this.minChars+" or more characters for results.":"Begin typing for results."}),this._events={input:{input:this.evaluate.bind(this),blur:this.close.bind(this,{reason:"blur"}),keydown:function(t){var e=t.keyCode;s.opened&&(13===e&&s.selected?(t.preventDefault(),s.select()):9===e&&s.selected&&s.tabSelect?s.select():27===e?s.close({reason:"esc"}):38!==e&&40!==e||(t.preventDefault(),s[38===e?"previous":"next"]()))}},form:{submit:this.close.bind(this,{reason:"submit"})},ul:{mousedown:function(t){t.preventDefault()},click:function(t){var e=t.target;if(e!==this){for(;e&&!/li/i.test(e.nodeName);)e=e.parentNode;e&&0===t.button&&(t.preventDefault(),s.select(e,t.target))}}}},i.bind(this.input,this._events.input),i.bind(this.input.form,this._events.form),i.bind(this.ul,this._events.ul),this.input.hasAttribute("list")?(this.list="#"+this.input.getAttribute("list"),this.input.removeAttribute("list")):this.list=this.input.getAttribute("data-list")||n.list||[],r.all.push(this)};r.prototype={set list(t){if(Array.isArray(t))this._list=t;else if("string"==typeof t&&t.indexOf(",")>-1)this._list=t.split(/\s*,\s*/);else if((t=i(t))&&t.children){var e=[];o.apply(t.children).forEach(function(t){if(!t.disabled){var i=t.textContent.trim(),n=t.value||i,s=t.label||i;""!==n&&e.push({label:s,value:n})}}),this._list=e}document.activeElement===this.input&&this.evaluate()},get selected(){return this.index>-1},get opened(){return this.isOpened},close:function(t){this.opened&&(this.input.setAttribute("aria-expanded","false"),this.ul.setAttribute("hidden",""),this.isOpened=!1,this.index=-1,this.status.setAttribute("hidden",""),i.fire(this.input,"awesomplete-close",t||{}))},open:function(){this.input.setAttribute("aria-expanded","true"),this.ul.removeAttribute("hidden"),this.isOpened=!0,this.status.removeAttribute("hidden"),this.autoFirst&&-1===this.index&&this.goto(0),i.fire(this.input,"awesomplete-open")},destroy:function(){if(i.unbind(this.input,this._events.input),i.unbind(this.input.form,this._events.form),!this.options.container){var t=this.container.parentNode;t.insertBefore(this.input,this.container),t.removeChild(this.container)}this.input.removeAttribute("autocomplete"),this.input.removeAttribute("aria-autocomplete");var e=r.all.indexOf(this);-1!==e&&r.all.splice(e,1)},next:function(){var t=this.ul.children.length;this.goto(this.index-1&&e.length>0&&(e[t].setAttribute("aria-selected","true"),this.status.textContent=e[t].textContent+", list item "+(t+1)+" of "+e.length,this.input.setAttribute("aria-activedescendant",this.ul.id+"_item_"+this.index),this.ul.scrollTop=e[t].offsetTop-this.ul.clientHeight+e[t].clientHeight,i.fire(this.input,"awesomplete-highlight",{text:this.suggestions[this.index]}))},select:function(t,e){if(t?this.index=i.siblingIndex(t):t=this.ul.children[this.index],t){var n=this.suggestions[this.index];i.fire(this.input,"awesomplete-select",{text:n,origin:e||t})&&(this.replace(n),this.close({reason:"select"}),i.fire(this.input,"awesomplete-selectcomplete",{text:n}))}},evaluate:function(){var e=this,i=this.input.value;i.length>=this.minChars&&this._list&&this._list.length>0?(this.index=-1,this.ul.innerHTML="",this.suggestions=this._list.map(function(n){return new t(e.data(n,i))}).filter(function(t){return e.filter(t,i)}),!1!==this.sort&&(this.suggestions=this.suggestions.sort(this.sort)),this.suggestions=this.suggestions.slice(0,this.maxItems),this.suggestions.forEach(function(t,n){e.ul.appendChild(e.item(t,i,n))}),0===this.ul.children.length?(this.status.textContent="No results found",this.close({reason:"nomatches"})):(this.open(),this.status.textContent=this.ul.children.length+" results found")):(this.close({reason:"nomatches"}),this.status.textContent="No results found")}},r.all=[],r.FILTER_CONTAINS=function(t,e){return RegExp(i.regExpEscape(e.trim()),"i").test(t)},r.FILTER_STARTSWITH=function(t,e){return RegExp("^"+i.regExpEscape(e.trim()),"i").test(t)},r.SORT_BYLENGTH=function(t,e){return t.length!==e.length?t.length-e.length:t$&"),role:"option","aria-selected":"false",id:"awesomplete_list_"+this.count+"_item_"+n})},r.REPLACE=function(t){this.input.value=t.value},r.DATA=function(t){return t},Object.defineProperty(t.prototype=Object.create(String.prototype),"length",{get:function(){return this.label.length}}),t.prototype.toString=t.prototype.valueOf=function(){return""+this.label};var o=Array.prototype.slice;i.create=function(t,e){var n=document.createElement(t);for(var s in e){var r=e[s];if("inside"===s)i(r).appendChild(n);else if("around"===s){var o=i(r);o.parentNode.insertBefore(n,o),n.appendChild(o),null!=o.getAttribute("autofocus")&&o.focus()}else s in n?n[s]=r:n.setAttribute(s,r)}return n},i.bind=function(t,e){if(t)for(var i in e){var n=e[i];i.split(/\s+/).forEach(function(e){t.addEventListener(e,n)})}},i.unbind=function(t,e){if(t)for(var i in e){var n=e[i];i.split(/\s+/).forEach(function(e){t.removeEventListener(e,n)})}},i.fire=function(t,e,i){var n=document.createEvent("HTMLEvents");n.initEvent(e,!0,!0);for(var s in i)n[s]=i[s];return t.dispatchEvent(n)},i.regExpEscape=function(t){return t.replace(/[-\\^$*+?.()|[\]{}]/g,"\\$&")},i.siblingIndex=function(t){for(var e=0;t=t.previousElementSibling;e++);return e},"undefined"!=typeof self&&(self.Awesomplete=r),"undefined"!=typeof Document&&("loading"!==document.readyState?s():document.addEventListener("DOMContentLoaded",s)),r.$=i,r.$$=n,"object"==typeof module&&module.exports&&(module.exports=r)}(); +//# sourceMappingURL=awesomplete.min.js.map only in patch2: unchanged: --- /dev/null +++ b/core/modules/awesomplete_autocomplete/js/awesomplete.min.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["awesomplete.js"],"names":["Suggestion","data","o","Array","isArray","label","value","this","configure","instance","properties","i","initial","attrValue","input","getAttribute","toLowerCase","parseInt","Function","$","expr","con","document","querySelector","$$","slice","call","querySelectorAll","init","forEach","_","me","count","isOpened","setAttribute","options","minChars","maxItems","autoFirst","DATA","filter","FILTER_CONTAINS","sort","SORT_BYLENGTH","container","CONTAINER","item","ITEM","replace","REPLACE","tabSelect","index","ul","create","hidden","role","id","inside","status","className","aria-live","aria-atomic","textContent","_events","evaluate","bind","blur","close","reason","keydown","evt","c","keyCode","opened","selected","preventDefault","select","form","submit","mousedown","click","li","target","test","nodeName","parentNode","button","hasAttribute","list","removeAttribute","all","push","prototype","_list","indexOf","split","children","items","apply","el","disabled","text","trim","activeElement","fire","open","goto","destroy","unbind","insertBefore","removeChild","indexOfAwesomplete","splice","next","length","previous","pos","lis","scrollTop","offsetTop","clientHeight","suggestions","origin","siblingIndex","suggestion","innerHTML","map","appendChild","RegExp","regExpEscape","FILTER_STARTSWITH","a","b","around","item_id","aria-selected","Object","defineProperty","String","get","toString","valueOf","tag","element","createElement","val","ref","focus","event","callback","addEventListener","removeEventListener","type","createEvent","initEvent","j","dispatchEvent","s","previousElementSibling","self","Awesomplete","Document","readyState","module","exports"],"mappings":";CAOC,WA6XD,QAASA,GAAWC,GACnB,GAAIC,GAAIC,MAAMC,QAAQH,IAChBI,MAAOJ,EAAK,GAAIK,MAAOL,EAAK,IACd,gBAATA,IAAqB,SAAWA,IAAQ,SAAWA,GAAOA,GAASI,MAAOJ,EAAMK,MAAOL,EAElGM,MAAKF,MAAQH,EAAEG,OAASH,EAAEI,MAC1BC,KAAKD,MAAQJ,EAAEI,MAShB,QAASE,GAAUC,EAAUC,EAAYR,GACxC,IAAK,GAAIS,KAAKD,GAAY,CACzB,GAAIE,GAAUF,EAAWC,GACrBE,EAAYJ,EAASK,MAAMC,aAAa,QAAUJ,EAAEK,cAEjC,iBAAZJ,GACVH,EAASE,GAAKM,SAASJ,IAEH,IAAZD,EACRH,EAASE,GAAmB,OAAdE,EAEND,YAAmBM,UAC3BT,EAASE,GAAK,KAGdF,EAASE,GAAKE,EAGVJ,EAASE,IAAsB,IAAhBF,EAASE,KAC5BF,EAASE,GAAMA,IAAKT,GAAIA,EAAES,GAAKC,IASlC,QAASO,GAAEC,EAAMC,GAChB,MAAuB,gBAATD,IAAoBC,GAAOC,UAAUC,cAAcH,GAAQA,GAAQ,KAGlF,QAASI,GAAGJ,EAAMC,GACjB,MAAOI,GAAMC,MAAML,GAAOC,UAAUK,iBAAiBP,IAgFtD,QAASQ,KACRJ,EAAG,qBAAqBK,QAAQ,SAAUf,GACzC,GAAIgB,GAAEhB,KA7fR,GAAIgB,GAAI,SAAUhB,EAAOZ,GACxB,GAAI6B,GAAKxB,IAGNuB,GAAEE,OAASF,EAAEE,OAAS,GAAK,EAC3BzB,KAAKyB,MAAQF,EAAEE,MAIlBzB,KAAK0B,UAAW,EAEhB1B,KAAKO,MAAQK,EAAEL,GACfP,KAAKO,MAAMoB,aAAa,eAAgB,OACxC3B,KAAKO,MAAMoB,aAAa,gBAAiB,SACzC3B,KAAKO,MAAMoB,aAAa,YAAa,oBAAsB3B,KAAKyB,OAChEzB,KAAKO,MAAMoB,aAAa,OAAQ,YAIhC3B,KAAK4B,QAAUjC,EAAIA,MAEnBM,EAAUD,MACT6B,SAAU,EACVC,SAAU,GACVC,WAAW,EACXrC,KAAM6B,EAAES,KACRC,OAAQV,EAAEW,gBACVC,MAAiB,IAAXxC,EAAEwC,MAAyBZ,EAAEa,cACnCC,UAAWd,EAAEe,UACbC,KAAMhB,EAAEiB,KACRC,QAASlB,EAAEmB,QACXC,WAAW,GACThD,GAEHK,KAAK4C,OAAS,EAId5C,KAAKqC,UAAYrC,KAAKqC,UAAU9B,GAEhCP,KAAK6C,GAAKjC,EAAEkC,OAAO,MAClBC,OAAQ,SACFC,KAAM,UACNC,GAAI,oBAAsBjD,KAAKyB,MACrCyB,OAAQlD,KAAKqC,YAGdrC,KAAKmD,OAASvC,EAAEkC,OAAO,QACtBM,UAAW,kBACXJ,KAAM,SACNK,YAAa,YACPC,eAAe,EACfJ,OAAQlD,KAAKqC,UACbkB,YAA8B,GAAjBvD,KAAK6B,SAAiB,QAAU7B,KAAK6B,SAAW,mCAAsC,8BAK1G7B,KAAKwD,SACJjD,OACCA,MAASP,KAAKyD,SAASC,KAAK1D,MAC5B2D,KAAQ3D,KAAK4D,MAAMF,KAAK1D,MAAQ6D,OAAQ,SACxCC,QAAW,SAASC,GACnB,GAAIC,GAAID,EAAIE,OAITzC,GAAG0C,SACK,KAANF,GAAYxC,EAAG2C,UAClBJ,EAAIK,iBACJ5C,EAAG6C,UAEW,IAANL,GAAWxC,EAAG2C,UAAY3C,EAAGmB,UACrCnB,EAAG6C,SAEW,KAANL,EACRxC,EAAGoC,OAAQC,OAAQ,QAEL,KAANG,GAAkB,KAANA,IACpBD,EAAIK,iBACJ5C,EAAS,KAANwC,EAAU,WAAa,cAK9BM,MACCC,OAAUvE,KAAK4D,MAAMF,KAAK1D,MAAQ6D,OAAQ,YAE3ChB,IAIC2B,UAAa,SAAST,GACrBA,EAAIK,kBAGLK,MAAS,SAASV,GACjB,GAAIW,GAAKX,EAAIY,MAEb,IAAID,IAAO1E,KAAM,CAEhB,KAAO0E,IAAO,MAAME,KAAKF,EAAGG,WAC3BH,EAAKA,EAAGI,UAGLJ,IAAqB,IAAfX,EAAIgB,SACbhB,EAAIK,iBACJ5C,EAAG6C,OAAOK,EAAIX,EAAIY,aAOvB/D,EAAE8C,KAAK1D,KAAKO,MAAOP,KAAKwD,QAAQjD,OAChCK,EAAE8C,KAAK1D,KAAKO,MAAM+D,KAAMtE,KAAKwD,QAAQc,MACrC1D,EAAE8C,KAAK1D,KAAK6C,GAAI7C,KAAKwD,QAAQX,IAEzB7C,KAAKO,MAAMyE,aAAa,SAC3BhF,KAAKiF,KAAO,IAAMjF,KAAKO,MAAMC,aAAa,QAC1CR,KAAKO,MAAM2E,gBAAgB,SAG3BlF,KAAKiF,KAAOjF,KAAKO,MAAMC,aAAa,cAAgBb,EAAEsF,SAGvD1D,EAAE4D,IAAIC,KAAKpF,MAGZuB,GAAE8D,WACDJ,SAASA,GACR,GAAIrF,MAAMC,QAAQoF,GACjBjF,KAAKsF,MAAQL,MAET,IAAoB,gBAATA,IAAqBA,EAAKM,QAAQ,MAAQ,EACxDvF,KAAKsF,MAAQL,EAAKO,MAAM,eAKzB,KAFAP,EAAOrE,EAAEqE,KAEGA,EAAKQ,SAAU,CAC1B,GAAIC,KACJxE,GAAMyE,MAAMV,EAAKQ,UAAUnE,QAAQ,SAAUsE,GAC5C,IAAKA,EAAGC,SAAU,CACjB,GAAIC,GAAOF,EAAGrC,YAAYwC,OACtBhG,EAAQ6F,EAAG7F,OAAS+F,EACpBhG,EAAQ8F,EAAG9F,OAASgG,CACV,MAAV/F,GACH2F,EAAMN,MAAOtF,MAAOA,EAAOC,MAAOA,OAIrCC,KAAKsF,MAAQI,EAIX3E,SAASiF,gBAAkBhG,KAAKO,OACnCP,KAAKyD,YAIPU,eACC,MAAOnE,MAAK4C,OAAS,GAGtBsB,aACC,MAAOlE,MAAK0B,UAGbkC,MAAO,SAAUjE,GACXK,KAAKkE,SAIVlE,KAAKO,MAAMoB,aAAa,gBAAiB,SACzC3B,KAAK6C,GAAGlB,aAAa,SAAU,IAC/B3B,KAAK0B,UAAW,EAChB1B,KAAK4C,OAAS,EAEd5C,KAAKmD,OAAOxB,aAAa,SAAU,IAEnCf,EAAEqF,KAAKjG,KAAKO,MAAO,oBAAqBZ,SAGzCuG,KAAM,WACLlG,KAAKO,MAAMoB,aAAa,gBAAiB,QACzC3B,KAAK6C,GAAGqC,gBAAgB,UACxBlF,KAAK0B,UAAW,EAEhB1B,KAAKmD,OAAO+B,gBAAgB,UAExBlF,KAAK+B,YAA6B,IAAhB/B,KAAK4C,OAC1B5C,KAAKmG,KAAK,GAGXvF,EAAEqF,KAAKjG,KAAKO,MAAO,qBAGpB6F,QAAS,WAMR,GAJAxF,EAAEyF,OAAOrG,KAAKO,MAAOP,KAAKwD,QAAQjD,OAClCK,EAAEyF,OAAOrG,KAAKO,MAAM+D,KAAMtE,KAAKwD,QAAQc,OAGlCtE,KAAK4B,QAAQS,UAAW,CAE5B,GAAIyC,GAAa9E,KAAKqC,UAAUyC,UAEhCA,GAAWwB,aAAatG,KAAKO,MAAOP,KAAKqC,WACzCyC,EAAWyB,YAAYvG,KAAKqC,WAI7BrC,KAAKO,MAAM2E,gBAAgB,gBAC3BlF,KAAKO,MAAM2E,gBAAgB,oBAG3B,IAAIsB,GAAqBjF,EAAE4D,IAAII,QAAQvF,OAEX,IAAxBwG,GACHjF,EAAE4D,IAAIsB,OAAOD,EAAoB,IAInCE,KAAM,WACL,GAAIjF,GAAQzB,KAAK6C,GAAG4C,SAASkB,MAC7B3G,MAAKmG,KAAKnG,KAAK4C,MAAQnB,EAAQ,EAAIzB,KAAK4C,MAAQ,EAAKnB,EAAQ,GAAK,IAGnEmF,SAAU,WACT,GAAInF,GAAQzB,KAAK6C,GAAG4C,SAASkB,OACzBE,EAAM7G,KAAK4C,MAAQ,CAEvB5C,MAAKmG,KAAKnG,KAAKmE,WAAqB,IAAT0C,EAAaA,EAAMpF,EAAQ,IAIvD0E,KAAM,SAAU/F,GACf,GAAI0G,GAAM9G,KAAK6C,GAAG4C,QAEdzF,MAAKmE,UACR2C,EAAI9G,KAAK4C,OAAOjB,aAAa,gBAAiB,SAG/C3B,KAAK4C,MAAQxC,EAETA,GAAK,GAAK0G,EAAIH,OAAS,IAC1BG,EAAI1G,GAAGuB,aAAa,gBAAiB,QAErC3B,KAAKmD,OAAOI,YAAcuD,EAAI1G,GAAGmD,YAAc,gBAAkBnD,EAAI,GAAK,OAAS0G,EAAIH,OAE9E3G,KAAKO,MAAMoB,aAAa,wBAAyB3B,KAAK6C,GAAGI,GAAK,SAAWjD,KAAK4C,OAGvF5C,KAAK6C,GAAGkE,UAAYD,EAAI1G,GAAG4G,UAAYhH,KAAK6C,GAAGoE,aAAeH,EAAI1G,GAAG6G,aAErErG,EAAEqF,KAAKjG,KAAKO,MAAO,yBAClBuF,KAAM9F,KAAKkH,YAAYlH,KAAK4C,WAK/ByB,OAAQ,SAAUF,EAAUgD,GAO3B,GANIhD,EACHnE,KAAK4C,MAAQhC,EAAEwG,aAAajD,GAE5BA,EAAWnE,KAAK6C,GAAG4C,SAASzF,KAAK4C,OAG9BuB,EAAU,CACb,GAAIkD,GAAarH,KAAKkH,YAAYlH,KAAK4C,MAEzBhC,GAAEqF,KAAKjG,KAAKO,MAAO,sBAChCuF,KAAMuB,EACNF,OAAQA,GAAUhD,MAIlBnE,KAAKyC,QAAQ4E,GACbrH,KAAK4D,OAAQC,OAAQ,WACrBjD,EAAEqF,KAAKjG,KAAKO,MAAO,8BAClBuF,KAAMuB,OAMV5D,SAAU,WACT,GAAIjC,GAAKxB,KACLD,EAAQC,KAAKO,MAAMR,KAEnBA,GAAM4G,QAAU3G,KAAK6B,UAAY7B,KAAKsF,OAAStF,KAAKsF,MAAMqB,OAAS,GACtE3G,KAAK4C,OAAS,EAEd5C,KAAK6C,GAAGyE,UAAY,GAEpBtH,KAAKkH,YAAclH,KAAKsF,MACtBiC,IAAI,SAAShF,GACb,MAAO,IAAI9C,GAAW+B,EAAG9B,KAAK6C,EAAMxC,MAEpCkC,OAAO,SAASM,GAChB,MAAOf,GAAGS,OAAOM,EAAMxC,MAGP,IAAdC,KAAKmC,OACRnC,KAAKkH,YAAclH,KAAKkH,YAAY/E,KAAKnC,KAAKmC,OAG/CnC,KAAKkH,YAAclH,KAAKkH,YAAYhG,MAAM,EAAGlB,KAAK8B,UAElD9B,KAAKkH,YAAY5F,QAAQ,SAASwE,EAAMlD,GACtCpB,EAAGqB,GAAG2E,YAAYhG,EAAGe,KAAKuD,EAAM/F,EAAO6C,MAGT,IAA5B5C,KAAK6C,GAAG4C,SAASkB,QAER3G,KAAKmD,OAAOI,YAAc,mBAEtCvD,KAAK4D,OAAQC,OAAQ,gBAGrB7D,KAAKkG,OAEOlG,KAAKmD,OAAOI,YAAcvD,KAAK6C,GAAG4C,SAASkB,OAAS,oBAIjE3G,KAAK4D,OAAQC,OAAQ,cAER7D,KAAKmD,OAAOI,YAAc,sBAO1ChC,EAAE4D,OAEF5D,EAAEW,gBAAkB,SAAU4D,EAAMvF,GACnC,MAAOkH,QAAO7G,EAAE8G,aAAanH,EAAMwF,QAAS,KAAKnB,KAAKkB,IAGvDvE,EAAEoG,kBAAoB,SAAU7B,EAAMvF,GACrC,MAAOkH,QAAO,IAAM7G,EAAE8G,aAAanH,EAAMwF,QAAS,KAAKnB,KAAKkB,IAG7DvE,EAAEa,cAAgB,SAAUwF,EAAGC,GAC9B,MAAID,GAAEjB,SAAWkB,EAAElB,OACXiB,EAAEjB,OAASkB,EAAElB,OAGdiB,EAAIC,GAAI,EAAI,GAGpBtG,EAAEe,UAAY,SAAU/B,GACvB,MAAOK,GAAEkC,OAAO,OACfM,UAAW,cACX0E,OAAQvH,KAIVgB,EAAEiB,KAAO,SAAUsD,EAAMvF,EAAOwH,GAE/B,MAAOnH,GAAEkC,OAAO,MACfwE,UAF2B,KAAjB/G,EAAMwF,OAAgBD,EAAOA,EAAKrD,QAAQgF,OAAO7G,EAAE8G,aAAanH,EAAMwF,QAAS,MAAO,mBAGhG/C,KAAQ,SACRgF,gBAAiB,QACjB/E,GAAM,oBAAsBjD,KAAKyB,MAAQ,SAAWsG,KAItDxG,EAAEmB,QAAU,SAAUoD,GACrB9F,KAAKO,MAAMR,MAAQ+F,EAAK/F,OAGzBwB,EAAES,KAAO,SAAUO,GAAmB,MAAOA,IAY7C0F,OAAOC,eAAezI,EAAW4F,UAAY4C,OAAOnF,OAAOqF,OAAO9C,WAAY,UAC7E+C,IAAK,WAAa,MAAOpI,MAAKF,MAAM6G,UAErClH,EAAW4F,UAAUgD,SAAW5I,EAAW4F,UAAUiD,QAAU,WAC9D,MAAO,GAAKtI,KAAKF,MA6BlB,IAAIoB,GAAQtB,MAAMyF,UAAUnE,KAU5BN,GAAEkC,OAAS,SAASyF,EAAK5I,GACxB,GAAI6I,GAAUzH,SAAS0H,cAAcF,EAErC,KAAK,GAAInI,KAAKT,GAAG,CAChB,GAAI+I,GAAM/I,EAAES,EAEZ,IAAU,WAANA,EACHQ,EAAE8H,GAAKlB,YAAYgB,OAEf,IAAU,WAANpI,EAAgB,CACxB,GAAIuI,GAAM/H,EAAE8H,EACZC,GAAI7D,WAAWwB,aAAakC,EAASG,GACrCH,EAAQhB,YAAYmB,GAEiB,MAAjCA,EAAInI,aAAa,cACpBmI,EAAIC,YAGGxI,KAAKoI,GACbA,EAAQpI,GAAKsI,EAGbF,EAAQ7G,aAAavB,EAAGsI,GAI1B,MAAOF,IAGR5H,EAAE8C,KAAO,SAAS8E,EAAS7I,GAC1B,GAAI6I,EACH,IAAK,GAAIK,KAASlJ,GAAG,CACpB,GAAImJ,GAAWnJ,EAAEkJ,EAEjBA,GAAMrD,MAAM,OAAOlE,QAAQ,SAAUuH,GACpCL,EAAQO,iBAAiBF,EAAOC,OAMpClI,EAAEyF,OAAS,SAASmC,EAAS7I,GAC5B,GAAI6I,EACH,IAAK,GAAIK,KAASlJ,GAAG,CACpB,GAAImJ,GAAWnJ,EAAEkJ,EAEjBA,GAAMrD,MAAM,OAAOlE,QAAQ,SAASuH,GACnCL,EAAQQ,oBAAoBH,EAAOC,OAMvClI,EAAEqF,KAAO,SAAStB,EAAQsE,EAAM9I,GAC/B,GAAI4D,GAAMhD,SAASmI,YAAY,aAE/BnF,GAAIoF,UAAUF,GAAM,GAAM,EAE1B,KAAK,GAAIG,KAAKjJ,GACb4D,EAAIqF,GAAKjJ,EAAWiJ,EAGrB,OAAOzE,GAAO0E,cAActF,IAG7BnD,EAAE8G,aAAe,SAAU4B,GAC1B,MAAOA,GAAE7G,QAAQ,uBAAwB,SAG1C7B,EAAEwG,aAAe,SAAUxB,GAE1B,IAAK,GAAIxF,GAAI,EAAGwF,EAAKA,EAAG2D,uBAAwBnJ,KAChD,MAAOA,IAYY,mBAAToJ,QACVA,KAAKC,YAAclI,GAII,mBAAbmI,YAEkB,YAAxB3I,SAAS4I,WACZtI,IAIAN,SAASgI,iBAAiB,mBAAoB1H,IAIhDE,EAAEX,EAAIA,EACNW,EAAEN,GAAKA,EAGe,gBAAX2I,SAAuBA,OAAOC,UACxCD,OAAOC,QAAUtI","file":"awesomplete.min.js","sourcesContent":["/**\n * Simple, lightweight, usable local autocomplete library for modern browsers\n * Because there weren’t enough autocomplete scripts in the world? Because I’m completely insane and have NIH syndrome? Probably both. :P\n * @author Lea Verou http://leaverou.github.io/awesomplete\n * MIT license\n */\n\n(function () {\n\nvar _ = function (input, o) {\n\tvar me = this;\n\n // Keep track of number of instances for unique IDs\n _.count = (_.count || 0) + 1;\n this.count = _.count;\n\n\t// Setup\n\n\tthis.isOpened = false;\n\n\tthis.input = $(input);\n\tthis.input.setAttribute(\"autocomplete\", \"off\");\n\tthis.input.setAttribute(\"aria-expanded\", \"false\");\n\tthis.input.setAttribute(\"aria-owns\", \"awesomplete_list_\" + this.count);\n\tthis.input.setAttribute(\"role\", \"combobox\");\n\n\t// store constructor options in case we need to distinguish\n\t// between default and customized behavior later on\n\tthis.options = o = o || {};\n\n\tconfigure(this, {\n\t\tminChars: 2,\n\t\tmaxItems: 10,\n\t\tautoFirst: false,\n\t\tdata: _.DATA,\n\t\tfilter: _.FILTER_CONTAINS,\n\t\tsort: o.sort === false ? false : _.SORT_BYLENGTH,\n\t\tcontainer: _.CONTAINER,\n\t\titem: _.ITEM,\n\t\treplace: _.REPLACE,\n\t\ttabSelect: false\n\t}, o);\n\n\tthis.index = -1;\n\n\t// Create necessary elements\n\n\tthis.container = this.container(input);\n\n\tthis.ul = $.create(\"ul\", {\n\t\thidden: \"hidden\",\n role: \"listbox\",\n id: \"awesomplete_list_\" + this.count,\n\t\tinside: this.container\n\t});\n\n\tthis.status = $.create(\"span\", {\n\t\tclassName: \"visually-hidden\",\n\t\trole: \"status\",\n\t\t\"aria-live\": \"assertive\",\n \"aria-atomic\": true,\n inside: this.container,\n textContent: this.minChars != 0 ? (\"Type \" + this.minChars + \" or more characters for results.\") : \"Begin typing for results.\"\n\t});\n\n\t// Bind events\n\n\tthis._events = {\n\t\tinput: {\n\t\t\t\"input\": this.evaluate.bind(this),\n\t\t\t\"blur\": this.close.bind(this, { reason: \"blur\" }),\n\t\t\t\"keydown\": function(evt) {\n\t\t\t\tvar c = evt.keyCode;\n\n\t\t\t\t// If the dropdown `ul` is in view, then act on keydown for the following keys:\n\t\t\t\t// Enter / Esc / Up / Down\n\t\t\t\tif(me.opened) {\n\t\t\t\t\tif (c === 13 && me.selected) { // Enter\n\t\t\t\t\t\tevt.preventDefault();\n\t\t\t\t\t\tme.select();\n\t\t\t\t\t}\n\t\t\t\t\telse if (c === 9 && me.selected && me.tabSelect) {\n\t\t\t\t\t\tme.select();\n\t\t\t\t\t}\n\t\t\t\t\telse if (c === 27) { // Esc\n\t\t\t\t\t\tme.close({ reason: \"esc\" });\n\t\t\t\t\t}\n\t\t\t\t\telse if (c === 38 || c === 40) { // Down/Up arrow\n\t\t\t\t\t\tevt.preventDefault();\n\t\t\t\t\t\tme[c === 38? \"previous\" : \"next\"]();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\tform: {\n\t\t\t\"submit\": this.close.bind(this, { reason: \"submit\" })\n\t\t},\n\t\tul: {\n\t\t\t// Prevent the default mousedowm, which ensures the input is not blurred.\n\t\t\t// The actual selection will happen on click. This also ensures dragging the\n\t\t\t// cursor away from the list item will cancel the selection\n\t\t\t\"mousedown\": function(evt) {\n\t\t\t\tevt.preventDefault();\n\t\t\t},\n\t\t\t// The click event is fired even if the corresponding mousedown event has called preventDefault\n\t\t\t\"click\": function(evt) {\n\t\t\t\tvar li = evt.target;\n\n\t\t\t\tif (li !== this) {\n\n\t\t\t\t\twhile (li && !/li/i.test(li.nodeName)) {\n\t\t\t\t\t\tli = li.parentNode;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (li && evt.button === 0) { // Only select on left click\n\t\t\t\t\t\tevt.preventDefault();\n\t\t\t\t\t\tme.select(li, evt.target);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t};\n\n\t$.bind(this.input, this._events.input);\n\t$.bind(this.input.form, this._events.form);\n\t$.bind(this.ul, this._events.ul);\n\n\tif (this.input.hasAttribute(\"list\")) {\n\t\tthis.list = \"#\" + this.input.getAttribute(\"list\");\n\t\tthis.input.removeAttribute(\"list\");\n\t}\n\telse {\n\t\tthis.list = this.input.getAttribute(\"data-list\") || o.list || [];\n\t}\n\n\t_.all.push(this);\n};\n\n_.prototype = {\n\tset list(list) {\n\t\tif (Array.isArray(list)) {\n\t\t\tthis._list = list;\n\t\t}\n\t\telse if (typeof list === \"string\" && list.indexOf(\",\") > -1) {\n\t\t\t\tthis._list = list.split(/\\s*,\\s*/);\n\t\t}\n\t\telse { // Element or CSS selector\n\t\t\tlist = $(list);\n\n\t\t\tif (list && list.children) {\n\t\t\t\tvar items = [];\n\t\t\t\tslice.apply(list.children).forEach(function (el) {\n\t\t\t\t\tif (!el.disabled) {\n\t\t\t\t\t\tvar text = el.textContent.trim();\n\t\t\t\t\t\tvar value = el.value || text;\n\t\t\t\t\t\tvar label = el.label || text;\n\t\t\t\t\t\tif (value !== \"\") {\n\t\t\t\t\t\t\titems.push({ label: label, value: value });\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tthis._list = items;\n\t\t\t}\n\t\t}\n\n\t\tif (document.activeElement === this.input) {\n\t\t\tthis.evaluate();\n\t\t}\n\t},\n\n\tget selected() {\n\t\treturn this.index > -1;\n\t},\n\n\tget opened() {\n\t\treturn this.isOpened;\n\t},\n\n\tclose: function (o) {\n\t\tif (!this.opened) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.input.setAttribute(\"aria-expanded\", \"false\");\n\t\tthis.ul.setAttribute(\"hidden\", \"\");\n\t\tthis.isOpened = false;\n\t\tthis.index = -1;\n\n\t\tthis.status.setAttribute(\"hidden\", \"\");\n\n\t\t$.fire(this.input, \"awesomplete-close\", o || {});\n\t},\n\n\topen: function () {\n\t\tthis.input.setAttribute(\"aria-expanded\", \"true\");\n\t\tthis.ul.removeAttribute(\"hidden\");\n\t\tthis.isOpened = true;\n\n\t\tthis.status.removeAttribute(\"hidden\");\n\n\t\tif (this.autoFirst && this.index === -1) {\n\t\t\tthis.goto(0);\n\t\t}\n\n\t\t$.fire(this.input, \"awesomplete-open\");\n\t},\n\n\tdestroy: function() {\n\t\t//remove events from the input and its form\n\t\t$.unbind(this.input, this._events.input);\n\t\t$.unbind(this.input.form, this._events.form);\n\n\t\t// cleanup container if it was created by Awesomplete but leave it alone otherwise\n\t\tif (!this.options.container) {\n\t\t\t//move the input out of the awesomplete container and remove the container and its children\n\t\t\tvar parentNode = this.container.parentNode;\n\n\t\t\tparentNode.insertBefore(this.input, this.container);\n\t\t\tparentNode.removeChild(this.container);\n\t\t}\n\n\t\t//remove autocomplete and aria-autocomplete attributes\n\t\tthis.input.removeAttribute(\"autocomplete\");\n\t\tthis.input.removeAttribute(\"aria-autocomplete\");\n\n\t\t//remove this awesomeplete instance from the global array of instances\n\t\tvar indexOfAwesomplete = _.all.indexOf(this);\n\n\t\tif (indexOfAwesomplete !== -1) {\n\t\t\t_.all.splice(indexOfAwesomplete, 1);\n\t\t}\n\t},\n\n\tnext: function () {\n\t\tvar count = this.ul.children.length;\n\t\tthis.goto(this.index < count - 1 ? this.index + 1 : (count ? 0 : -1) );\n\t},\n\n\tprevious: function () {\n\t\tvar count = this.ul.children.length;\n\t\tvar pos = this.index - 1;\n\n\t\tthis.goto(this.selected && pos !== -1 ? pos : count - 1);\n\t},\n\n\t// Should not be used, highlights specific item without any checks!\n\tgoto: function (i) {\n\t\tvar lis = this.ul.children;\n\n\t\tif (this.selected) {\n\t\t\tlis[this.index].setAttribute(\"aria-selected\", \"false\");\n\t\t}\n\n\t\tthis.index = i;\n\n\t\tif (i > -1 && lis.length > 0) {\n\t\t\tlis[i].setAttribute(\"aria-selected\", \"true\");\n\n\t\t\tthis.status.textContent = lis[i].textContent + \", list item \" + (i + 1) + \" of \" + lis.length;\n\n this.input.setAttribute(\"aria-activedescendant\", this.ul.id + \"_item_\" + this.index);\n\n\t\t\t// scroll to highlighted element in case parent's height is fixed\n\t\t\tthis.ul.scrollTop = lis[i].offsetTop - this.ul.clientHeight + lis[i].clientHeight;\n\n\t\t\t$.fire(this.input, \"awesomplete-highlight\", {\n\t\t\t\ttext: this.suggestions[this.index]\n\t\t\t});\n\t\t}\n\t},\n\n\tselect: function (selected, origin) {\n\t\tif (selected) {\n\t\t\tthis.index = $.siblingIndex(selected);\n\t\t} else {\n\t\t\tselected = this.ul.children[this.index];\n\t\t}\n\n\t\tif (selected) {\n\t\t\tvar suggestion = this.suggestions[this.index];\n\n\t\t\tvar allowed = $.fire(this.input, \"awesomplete-select\", {\n\t\t\t\ttext: suggestion,\n\t\t\t\torigin: origin || selected\n\t\t\t});\n\n\t\t\tif (allowed) {\n\t\t\t\tthis.replace(suggestion);\n\t\t\t\tthis.close({ reason: \"select\" });\n\t\t\t\t$.fire(this.input, \"awesomplete-selectcomplete\", {\n\t\t\t\t\ttext: suggestion\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t},\n\n\tevaluate: function() {\n\t\tvar me = this;\n\t\tvar value = this.input.value;\n\n\t\tif (value.length >= this.minChars && this._list && this._list.length > 0) {\n\t\t\tthis.index = -1;\n\t\t\t// Populate list with options that match\n\t\t\tthis.ul.innerHTML = \"\";\n\n\t\t\tthis.suggestions = this._list\n\t\t\t\t.map(function(item) {\n\t\t\t\t\treturn new Suggestion(me.data(item, value));\n\t\t\t\t})\n\t\t\t\t.filter(function(item) {\n\t\t\t\t\treturn me.filter(item, value);\n\t\t\t\t});\n\n\t\t\tif (this.sort !== false) {\n\t\t\t\tthis.suggestions = this.suggestions.sort(this.sort);\n\t\t\t}\n\n\t\t\tthis.suggestions = this.suggestions.slice(0, this.maxItems);\n\n\t\t\tthis.suggestions.forEach(function(text, index) {\n\t\t\t\t\tme.ul.appendChild(me.item(text, value, index));\n\t\t\t\t});\n\n\t\t\tif (this.ul.children.length === 0) {\n\n this.status.textContent = \"No results found\";\n\n\t\t\t\tthis.close({ reason: \"nomatches\" });\n\n\t\t\t} else {\n\t\t\t\tthis.open();\n\n this.status.textContent = this.ul.children.length + \" results found\";\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tthis.close({ reason: \"nomatches\" });\n\n this.status.textContent = \"No results found\";\n\t\t}\n\t}\n};\n\n// Static methods/properties\n\n_.all = [];\n\n_.FILTER_CONTAINS = function (text, input) {\n\treturn RegExp($.regExpEscape(input.trim()), \"i\").test(text);\n};\n\n_.FILTER_STARTSWITH = function (text, input) {\n\treturn RegExp(\"^\" + $.regExpEscape(input.trim()), \"i\").test(text);\n};\n\n_.SORT_BYLENGTH = function (a, b) {\n\tif (a.length !== b.length) {\n\t\treturn a.length - b.length;\n\t}\n\n\treturn a < b? -1 : 1;\n};\n\n_.CONTAINER = function (input) {\n\treturn $.create(\"div\", {\n\t\tclassName: \"awesomplete\",\n\t\taround: input\n\t});\n}\n\n_.ITEM = function (text, input, item_id) {\n\tvar html = input.trim() === \"\" ? text : text.replace(RegExp($.regExpEscape(input.trim()), \"gi\"), \"$&\");\n\treturn $.create(\"li\", {\n\t\tinnerHTML: html,\n\t\t\"role\": \"option\",\n\t\t\"aria-selected\": \"false\",\n\t\t\"id\": \"awesomplete_list_\" + this.count + \"_item_\" + item_id\n\t});\n};\n\n_.REPLACE = function (text) {\n\tthis.input.value = text.value;\n};\n\n_.DATA = function (item/*, input*/) { return item; };\n\n// Private functions\n\nfunction Suggestion(data) {\n\tvar o = Array.isArray(data)\n\t ? { label: data[0], value: data[1] }\n\t : typeof data === \"object\" && \"label\" in data && \"value\" in data ? data : { label: data, value: data };\n\n\tthis.label = o.label || o.value;\n\tthis.value = o.value;\n}\nObject.defineProperty(Suggestion.prototype = Object.create(String.prototype), \"length\", {\n\tget: function() { return this.label.length; }\n});\nSuggestion.prototype.toString = Suggestion.prototype.valueOf = function () {\n\treturn \"\" + this.label;\n};\n\nfunction configure(instance, properties, o) {\n\tfor (var i in properties) {\n\t\tvar initial = properties[i],\n\t\t attrValue = instance.input.getAttribute(\"data-\" + i.toLowerCase());\n\n\t\tif (typeof initial === \"number\") {\n\t\t\tinstance[i] = parseInt(attrValue);\n\t\t}\n\t\telse if (initial === false) { // Boolean options must be false by default anyway\n\t\t\tinstance[i] = attrValue !== null;\n\t\t}\n\t\telse if (initial instanceof Function) {\n\t\t\tinstance[i] = null;\n\t\t}\n\t\telse {\n\t\t\tinstance[i] = attrValue;\n\t\t}\n\n\t\tif (!instance[i] && instance[i] !== 0) {\n\t\t\tinstance[i] = (i in o)? o[i] : initial;\n\t\t}\n\t}\n}\n\n// Helpers\n\nvar slice = Array.prototype.slice;\n\nfunction $(expr, con) {\n\treturn typeof expr === \"string\"? (con || document).querySelector(expr) : expr || null;\n}\n\nfunction $$(expr, con) {\n\treturn slice.call((con || document).querySelectorAll(expr));\n}\n\n$.create = function(tag, o) {\n\tvar element = document.createElement(tag);\n\n\tfor (var i in o) {\n\t\tvar val = o[i];\n\n\t\tif (i === \"inside\") {\n\t\t\t$(val).appendChild(element);\n\t\t}\n\t\telse if (i === \"around\") {\n\t\t\tvar ref = $(val);\n\t\t\tref.parentNode.insertBefore(element, ref);\n\t\t\telement.appendChild(ref);\n\n\t\t\tif (ref.getAttribute(\"autofocus\") != null) {\n\t\t\t\tref.focus();\n\t\t\t}\n\t\t}\n\t\telse if (i in element) {\n\t\t\telement[i] = val;\n\t\t}\n\t\telse {\n\t\t\telement.setAttribute(i, val);\n\t\t}\n\t}\n\n\treturn element;\n};\n\n$.bind = function(element, o) {\n\tif (element) {\n\t\tfor (var event in o) {\n\t\t\tvar callback = o[event];\n\n\t\t\tevent.split(/\\s+/).forEach(function (event) {\n\t\t\t\telement.addEventListener(event, callback);\n\t\t\t});\n\t\t}\n\t}\n};\n\n$.unbind = function(element, o) {\n\tif (element) {\n\t\tfor (var event in o) {\n\t\t\tvar callback = o[event];\n\n\t\t\tevent.split(/\\s+/).forEach(function(event) {\n\t\t\t\telement.removeEventListener(event, callback);\n\t\t\t});\n\t\t}\n\t}\n};\n\n$.fire = function(target, type, properties) {\n\tvar evt = document.createEvent(\"HTMLEvents\");\n\n\tevt.initEvent(type, true, true );\n\n\tfor (var j in properties) {\n\t\tevt[j] = properties[j];\n\t}\n\n\treturn target.dispatchEvent(evt);\n};\n\n$.regExpEscape = function (s) {\n\treturn s.replace(/[-\\\\^$*+?.()|[\\]{}]/g, \"\\\\$&\");\n};\n\n$.siblingIndex = function (el) {\n\t/* eslint-disable no-cond-assign */\n\tfor (var i = 0; el = el.previousElementSibling; i++);\n\treturn i;\n};\n\n// Initialization\n\nfunction init() {\n\t$$(\"input.awesomplete\").forEach(function (input) {\n\t\tnew _(input);\n\t});\n}\n\n// Make sure to export Awesomplete on self when in a browser\nif (typeof self !== \"undefined\") {\n\tself.Awesomplete = _;\n}\n\n// Are we in a browser? Check for Document constructor\nif (typeof Document !== \"undefined\") {\n\t// DOM already loaded?\n\tif (document.readyState !== \"loading\") {\n\t\tinit();\n\t}\n\telse {\n\t\t// Wait for it\n\t\tdocument.addEventListener(\"DOMContentLoaded\", init);\n\t}\n}\n\n_.$ = $;\n_.$$ = $$;\n\n// Expose Awesomplete as a CJS module\nif (typeof module === \"object\" && module.exports) {\n\tmodule.exports = _;\n}\n\nreturn _;\n\n}());\n"]} \ No newline at end of file