From 981c493f6187c223009fa48259d15b34efe4fb80 Mon Sep 17 00:00:00 2001 From: Denys Krasnoshchok Date: Tue, 7 Feb 2023 04:12:18 +0100 Subject: [PATCH] Missing documentation added to the source code --- autocomplete.d.ts | 9 +++++++++ autocomplete.js | 2 +- autocomplete.js.map | 2 +- autocomplete.min.js | 2 +- autocomplete.min.js.map | 2 +- autocomplete.ts | 11 ++++++++++- 6 files changed, 23 insertions(+), 5 deletions(-) diff --git a/autocomplete.d.ts b/autocomplete.d.ts index 9c35b2a..810395e 100644 --- a/autocomplete.d.ts +++ b/autocomplete.d.ts @@ -8,7 +8,13 @@ export interface AutocompleteItem { group?: string; } export interface AutocompleteEvent { + /** + * Native event object passed by browser to the event handler + */ event: T; + /** + * Fetch data and display autocomplete + */ fetch: () => void; } export interface AutocompleteSettings { @@ -91,6 +97,9 @@ export interface AutocompleteSettings { click?: (e: AutocompleteEvent) => void; } export interface AutocompleteResult { + /** + * Remove event handlers, DOM elements and ARIA/accessibility attributes created by the widget. + */ destroy: () => void; } export default function autocomplete(settings: AutocompleteSettings): AutocompleteResult; diff --git a/autocomplete.js b/autocomplete.js index 17eeb2d..4890ff3 100644 --- a/autocomplete.js +++ b/autocomplete.js @@ -160,7 +160,7 @@ renderGroup = settings.renderGroup; } var fragment = doc.createDocumentFragment(); - var prevGroup = "#9?$"; + var prevGroup = uid(); items.forEach(function (item, index) { if (item.group && item.group !== prevGroup) { prevGroup = item.group; diff --git a/autocomplete.js.map b/autocomplete.js.map index 6857c41..f68b128 100644 --- a/autocomplete.js.map +++ b/autocomplete.js.map @@ -1 +1 @@ -{"version":3,"file":"autocomplete.js","sources":["autocomplete.ts"],"sourcesContent":["/*\n * https://github.com/kraaden/autocomplete\n * Copyright (c) 2016 Denys Krasnoshchok\n * MIT License\n */\n\nexport const enum EventTrigger {\n Keyboard = 0,\n Focus = 1,\n Mouse = 2\n}\n\nexport interface AutocompleteItem {\n label?: string;\n group?: string;\n}\n\nexport interface AutocompleteEvent {\n event: T;\n fetch: () => void;\n}\n\nexport interface AutocompleteSettings {\n /**\n * Autocomplete will be attached to this element.\n */\n input: HTMLInputElement | HTMLTextAreaElement;\n\n /**\n * Provide your own container for the widget.\n * If not specified, a new DIV element will be created.\n */\n container?: HTMLDivElement;\n\n /**\n * This method allows you to override the default rendering function for items.\n * It must return a DIV element or undefined to skip rendering.\n */\n render?: (item: T, currentValue: string, index: number) => HTMLDivElement | undefined;\n\n /**\n * This method allows you to override the default rendering function for item groups.\n * It must return a DIV element or undefined to skip rendering.\n */\n renderGroup?: (name: string, currentValue: string) => HTMLDivElement | undefined;\n\n /**\n * If specified, the autocomplete DOM element will have this class assigned to it.\n */\n className?: string;\n\n /**\n * Specify the minimum text length required to show autocomplete.\n */\n minLength?: number;\n\n /**\n * The message that will be showed when there are no suggestions that match the entered value.\n */\n emptyMsg?: string;\n\n /**\n * This method will be called when user choose an item in autocomplete. The selected item will be passed as the first parameter.\n */\n onSelect: (item: T, input: HTMLInputElement | HTMLTextAreaElement) => void;\n\n /**\n * Show autocomplete on focus event. Focus event will ignore the `minLength` property and will always call `fetch`.\n */\n showOnFocus?: boolean;\n\n /**\n * This method will be called to prepare suggestions and then pass them to autocomplete.\n * @param {string} text - text in the input field\n * @param {(items: T[] | false) => void} update - a callback function that must be called after suggestions are prepared\n * @param {EventTrigger} trigger - type of the event that triggered the fetch\n * @param {number} cursorPos - position of the cursor in the input field\n */\n fetch: (text: string, update: (items: T[] | false) => void, trigger: EventTrigger, cursorPos: number) => void;\n\n /**\n * Enforces that the fetch function will only be called once within the specified time frame (in milliseconds) and\n * delays execution. This prevents flooding your server with AJAX requests.\n */\n debounceWaitMs?: number;\n\n /**\n * Callback for additional autocomplete customization\n * @param {HTMLInputElement | HTMLTextAreaElement} input - input box associated with autocomplete\n * @param {ClientRect | DOMRect} inputRect - size of the input box and its position relative to the viewport\n * @param {HTMLDivElement} container - container with suggestions\n * @param {number} maxHeight - max height that can be used by autocomplete\n */\n customize?: (input: HTMLInputElement | HTMLTextAreaElement, inputRect: ClientRect | DOMRect, container: HTMLDivElement, maxHeight: number) => void;\n\n /**\n * Prevents automatic form submit when ENTER is pressed\n */\n preventSubmit?: boolean;\n\n /**\n * Prevents the first item in the list from being selected automatically. This option allows you\n * to submit a custom text by pressing ENTER even when autocomplete is displayed.\n */\n disableAutoSelect?: boolean;\n\n /**\n * Provide your keyup event handler to display autocomplete when a key is pressed that doesn't modify the content. You can also perform some additional actions.\n */\n keyup?: (e: AutocompleteEvent) => void;\n\n /**\n * Allows to display autocomplete on mouse clicks or perform some additional actions.\n */\n click?: (e: AutocompleteEvent) => void;\n}\n\nexport interface AutocompleteResult {\n destroy: () => void;\n}\n\nexport default function autocomplete(settings: AutocompleteSettings): AutocompleteResult {\n\n // just an alias to minimize JS file size\n const doc = document;\n\n const container: HTMLDivElement = settings.container || doc.createElement(\"div\");\n container.id = container.id || \"autocomplete-\" + uid();\n const containerStyle = container.style;\n const debounceWaitMs = settings.debounceWaitMs || 0;\n const preventSubmit = settings.preventSubmit || false;\n const disableAutoSelect = settings.disableAutoSelect || false;\n\n let items: T[] = [];\n let inputValue = \"\";\n let minLen = 2;\n const showOnFocus = settings.showOnFocus;\n let selected: T | undefined;\n let keypressCounter = 0;\n let debounceTimer: number | undefined;\n\n if (settings.minLength !== undefined) {\n minLen = settings.minLength;\n }\n\n if (!settings.input) {\n throw new Error(\"input undefined\");\n }\n\n const input: HTMLInputElement | HTMLTextAreaElement = settings.input;\n\n container.className = \"autocomplete \" + (settings.className || \"\");\n container.setAttribute(\"role\", \"listbox\");\n\n input.setAttribute(\"role\", \"combobox\");\n input.setAttribute(\"aria-expanded\", \"false\");\n input.setAttribute(\"aria-autocomplete\", \"list\");\n input.setAttribute(\"aria-controls\", container.id);\n input.setAttribute(\"aria-owns\", container.id);\n input.setAttribute(\"aria-activedescendant\", \"\");\n input.setAttribute(\"aria-haspopup\", \"listbox\");\n\n // IOS implementation for fixed positioning has many bugs, so we will use absolute positioning\n containerStyle.position = \"absolute\";\n\n /**\n * Generate a unique ID\n */\n function uid(): string {\n return Date.now().toString(36) + Math.random().toString(36).substring(2);\n }\n\n /**\n * Detach the container from DOM\n */\n function detach(): void {\n const parent = container.parentNode;\n if (parent) {\n parent.removeChild(container);\n }\n }\n\n /**\n * Clear debouncing timer if assigned\n */\n function clearDebounceTimer(): void {\n if (debounceTimer) {\n window.clearTimeout(debounceTimer);\n }\n }\n\n /**\n * Attach the container to DOM\n */\n function attach(): void {\n if (!container.parentNode) {\n doc.body.appendChild(container);\n }\n }\n\n /**\n * Check if container for autocomplete is displayed\n */\n function containerDisplayed(): boolean {\n return !!container.parentNode;\n }\n\n /**\n * Clear autocomplete state and hide container\n */\n function clear(): void {\n // prevent the update call if there are pending AJAX requests\n keypressCounter++;\n\n items = [];\n inputValue = \"\";\n selected = undefined;\n input.setAttribute(\"aria-activedescendant\", \"\");\n input.setAttribute(\"aria-expanded\", \"false\");\n detach();\n }\n\n /**\n * Update autocomplete position\n */\n function updatePosition(): void {\n if (!containerDisplayed()) {\n return;\n }\n\n input.setAttribute(\"aria-expanded\", \"true\");\n\n containerStyle.height = \"auto\";\n containerStyle.width = input.offsetWidth + \"px\";\n\n let maxHeight = 0;\n let inputRect: ClientRect | DOMRect | undefined;\n\n function calc() {\n const docEl = doc.documentElement as HTMLElement;\n const clientTop = docEl.clientTop || doc.body.clientTop || 0;\n const clientLeft = docEl.clientLeft || doc.body.clientLeft || 0;\n const scrollTop = window.pageYOffset || docEl.scrollTop;\n const scrollLeft = window.pageXOffset || docEl.scrollLeft;\n\n inputRect = input.getBoundingClientRect();\n\n const top = inputRect.top + input.offsetHeight + scrollTop - clientTop;\n const left = inputRect.left + scrollLeft - clientLeft;\n\n containerStyle.top = top + \"px\";\n containerStyle.left = left + \"px\";\n\n maxHeight = window.innerHeight - (inputRect.top + input.offsetHeight);\n\n if (maxHeight < 0) {\n maxHeight = 0;\n }\n\n containerStyle.top = top + \"px\";\n containerStyle.bottom = \"\";\n containerStyle.left = left + \"px\";\n containerStyle.maxHeight = maxHeight + \"px\";\n }\n\n // the calc method must be called twice, otherwise the calculation may be wrong on resize event (chrome browser)\n calc();\n calc();\n\n if (settings.customize && inputRect) {\n settings.customize(input, inputRect, container, maxHeight);\n }\n }\n\n /**\n * Redraw the autocomplete div element with suggestions\n */\n function update(): void {\n\n // delete all children from autocomplete DOM container\n while (container.firstChild) {\n container.removeChild(container.firstChild);\n }\n\n input.setAttribute(\"aria-activedescendant\", \"\");\n\n // function for rendering autocomplete suggestions\n let render = function (item: T, _: string, __: number): HTMLDivElement | undefined {\n const itemElement = doc.createElement(\"div\");\n itemElement.textContent = item.label || \"\";\n return itemElement;\n };\n if (settings.render) {\n render = settings.render;\n }\n\n // function to render autocomplete groups\n let renderGroup = function (groupName: string, _: string): HTMLDivElement | undefined {\n const groupDiv = doc.createElement(\"div\");\n groupDiv.textContent = groupName;\n return groupDiv;\n };\n if (settings.renderGroup) {\n renderGroup = settings.renderGroup;\n }\n\n const fragment = doc.createDocumentFragment();\n let prevGroup = \"#9?$\";\n\n items.forEach(function (item: T, index: number): void {\n if (item.group && item.group !== prevGroup) {\n prevGroup = item.group;\n const groupDiv = renderGroup(item.group, inputValue);\n if (groupDiv) {\n groupDiv.className += \" group\";\n fragment.appendChild(groupDiv);\n }\n }\n const div = render(item, inputValue, index);\n if (div) {\n div.id = `${container.id}_${index}`;\n div.setAttribute(\"role\", \"option\");\n div.addEventListener(\"click\", function (ev: MouseEvent): void {\n settings.onSelect(item, input);\n clear();\n ev.preventDefault();\n ev.stopPropagation();\n });\n if (item === selected) {\n div.className += \" selected\";\n div.setAttribute(\"aria-selected\", \"true\");\n input.setAttribute(\"aria-activedescendant\", div.id);\n }\n fragment.appendChild(div);\n }\n });\n container.appendChild(fragment);\n if (items.length < 1) {\n if (settings.emptyMsg) {\n const empty = doc.createElement(\"div\");\n empty.id = `${container.id}_${uid()}`;\n empty.className = \"empty\";\n empty.textContent = settings.emptyMsg;\n container.appendChild(empty);\n input.setAttribute(\"aria-activedescendant\", empty.id);\n } else {\n clear();\n return;\n }\n }\n\n attach();\n updatePosition();\n\n updateScroll();\n }\n\n function updateIfDisplayed(): void {\n if (containerDisplayed()) {\n update();\n }\n }\n\n function resizeEventHandler(): void {\n updateIfDisplayed();\n }\n\n function scrollEventHandler(e: Event): void {\n if (e.target !== container) {\n updateIfDisplayed();\n } else {\n e.preventDefault();\n }\n }\n\n function inputEventHandler(): void {\n startFetch(EventTrigger.Keyboard);\n }\n\n /**\n * Automatically move scroll bar if selected item is not visible\n */\n function updateScroll(): void {\n const elements = container.getElementsByClassName(\"selected\");\n if (elements.length > 0) {\n let element = elements[0] as HTMLDivElement;\n\n // make group visible\n const previous = element.previousElementSibling as HTMLDivElement;\n if (previous && previous.className.indexOf(\"group\") !== -1 && !previous.previousElementSibling) {\n element = previous;\n }\n\n if (element.offsetTop < container.scrollTop) {\n container.scrollTop = element.offsetTop;\n } else {\n const selectBottom = element.offsetTop + element.offsetHeight;\n const containerBottom = container.scrollTop + container.offsetHeight;\n if (selectBottom > containerBottom) {\n container.scrollTop += selectBottom - containerBottom;\n }\n }\n }\n }\n\n /**\n * Select the previous item in suggestions\n */\n function selectPrev(): void {\n if (items.length < 1) {\n selected = undefined;\n } else {\n if (selected === items[0]) {\n selected = items[items.length - 1];\n } else {\n for (let i = items.length - 1; i > 0; i--) {\n if (selected === items[i] || i === 1) {\n selected = items[i - 1];\n break;\n }\n }\n }\n }\n }\n\n /**\n * Select the next item in suggestions\n */\n function selectNext(): void {\n if (items.length < 1) {\n selected = undefined;\n }\n if (!selected || selected === items[items.length - 1]) {\n selected = items[0];\n return;\n }\n for (let i = 0; i < (items.length - 1); i++) {\n if (selected === items[i]) {\n selected = items[i + 1];\n break;\n }\n }\n }\n\n function keydownEventHandler(ev: KeyboardEvent): void {\n const key = ev.key;\n\n if (key === \"ArrowUp\" || key === \"ArrowDown\" || key === \"Escape\") {\n const containerIsDisplayed = containerDisplayed();\n\n if (key === \"Escape\") {\n clear();\n } else {\n if (!containerIsDisplayed || items.length < 1) {\n return;\n }\n key === \"ArrowUp\"\n ? selectPrev()\n : selectNext();\n update();\n }\n\n ev.preventDefault();\n if (containerIsDisplayed) {\n ev.stopPropagation();\n }\n\n return;\n }\n\n if (key === 'Enter') {\n if (selected) {\n settings.onSelect(selected, input);\n clear();\n }\n\n if (preventSubmit) {\n ev.preventDefault();\n }\n }\n }\n\n function focusEventHandler(): void {\n if (showOnFocus) {\n startFetch(EventTrigger.Focus);\n }\n }\n\n function startFetch(trigger: EventTrigger) {\n // If multiple keys were pressed, before we get an update from server,\n // this may cause redrawing autocomplete multiple times after the last key was pressed.\n // To avoid this, the number of times keyboard was pressed will be saved and checked before redraw.\n const savedKeypressCounter = ++keypressCounter;\n\n const inputText = input.value;\n const cursorPos = input.selectionStart || 0;\n\n if (inputText.length >= minLen || trigger === EventTrigger.Focus) {\n clearDebounceTimer();\n debounceTimer = window.setTimeout(function (): void {\n settings.fetch(inputText, function (elements: T[] | false): void {\n if (keypressCounter === savedKeypressCounter && elements) {\n items = elements;\n inputValue = inputText;\n selected = (items.length < 1 || disableAutoSelect) ? undefined : items[0];\n update();\n }\n }, trigger, cursorPos);\n }, trigger === EventTrigger.Keyboard || trigger === EventTrigger.Mouse ? debounceWaitMs : 0);\n } else {\n clear();\n }\n }\n\n function keyupEventHandler(e: KeyboardEvent) {\n if (settings.keyup) {\n settings.keyup({\n event: e,\n fetch: () => startFetch(EventTrigger.Keyboard)\n });\n return;\n }\n\n if (!containerDisplayed() && e.key === \"ArrowDown\") {\n startFetch(EventTrigger.Keyboard);\n }\n }\n\n function clickEventHandler(e: MouseEvent) {\n settings.click && settings.click({\n event: e,\n fetch: () => startFetch(EventTrigger.Mouse)\n });\n }\n\n function blurEventHandler() {\n // we need to delay clear, because when we click on an item, blur will be called before click and remove items from DOM\n setTimeout(() => {\n if (doc.activeElement !== input) {\n clear();\n }\n }, 200);\n }\n\n /**\n * Fixes #26: on long clicks focus will be lost and onSelect method will not be called\n */\n container.addEventListener(\"mousedown\", function (evt: Event) {\n evt.stopPropagation();\n evt.preventDefault();\n });\n\n /**\n * Fixes #30: autocomplete closes when scrollbar is clicked in IE\n * See: https://stackoverflow.com/a/9210267/13172349\n */\n container.addEventListener(\"focus\", () => input.focus());\n\n /**\n * This function will remove DOM elements and clear event handlers\n */\n function destroy(): void {\n input.removeEventListener(\"focus\", focusEventHandler);\n input.removeEventListener(\"keyup\", keyupEventHandler as EventListenerOrEventListenerObject)\n input.removeEventListener(\"click\", clickEventHandler as EventListenerOrEventListenerObject)\n input.removeEventListener(\"keydown\", keydownEventHandler as EventListenerOrEventListenerObject);\n input.removeEventListener(\"input\", inputEventHandler as EventListenerOrEventListenerObject);\n input.removeEventListener(\"blur\", blurEventHandler);\n window.removeEventListener(\"resize\", resizeEventHandler);\n doc.removeEventListener(\"scroll\", scrollEventHandler, true);\n input.removeAttribute(\"role\");\n input.removeAttribute(\"aria-expanded\");\n input.removeAttribute(\"aria-autocomplete\");\n input.removeAttribute(\"aria-controls\");\n input.removeAttribute(\"aria-activedescendant\");\n input.removeAttribute(\"aria-owns\");\n input.removeAttribute(\"aria-haspopup\");\n clearDebounceTimer();\n clear();\n }\n\n // setup event handlers\n input.addEventListener(\"keyup\", keyupEventHandler as EventListenerOrEventListenerObject);\n input.addEventListener(\"click\", clickEventHandler as EventListenerOrEventListenerObject);\n input.addEventListener(\"keydown\", keydownEventHandler as EventListenerOrEventListenerObject);\n input.addEventListener(\"input\", inputEventHandler as EventListenerOrEventListenerObject);\n input.addEventListener(\"blur\", blurEventHandler);\n input.addEventListener(\"focus\", focusEventHandler);\n window.addEventListener(\"resize\", resizeEventHandler);\n doc.addEventListener(\"scroll\", scrollEventHandler, true);\n\n return {\n destroy\n };\n}\n"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"autocomplete.js","sources":["autocomplete.ts"],"sourcesContent":["/*\n * https://github.com/kraaden/autocomplete\n * Copyright (c) 2016 Denys Krasnoshchok\n * MIT License\n */\n\nexport const enum EventTrigger {\n Keyboard = 0,\n Focus = 1,\n Mouse = 2\n}\n\nexport interface AutocompleteItem {\n label?: string;\n group?: string;\n}\n\nexport interface AutocompleteEvent {\n /**\n * Native event object passed by browser to the event handler\n */\n event: T;\n /**\n * Fetch data and display autocomplete\n */\n fetch: () => void;\n}\n\nexport interface AutocompleteSettings {\n /**\n * Autocomplete will be attached to this element.\n */\n input: HTMLInputElement | HTMLTextAreaElement;\n\n /**\n * Provide your own container for the widget.\n * If not specified, a new DIV element will be created.\n */\n container?: HTMLDivElement;\n\n /**\n * This method allows you to override the default rendering function for items.\n * It must return a DIV element or undefined to skip rendering.\n */\n render?: (item: T, currentValue: string, index: number) => HTMLDivElement | undefined;\n\n /**\n * This method allows you to override the default rendering function for item groups.\n * It must return a DIV element or undefined to skip rendering.\n */\n renderGroup?: (name: string, currentValue: string) => HTMLDivElement | undefined;\n\n /**\n * If specified, the autocomplete DOM element will have this class assigned to it.\n */\n className?: string;\n\n /**\n * Specify the minimum text length required to show autocomplete.\n */\n minLength?: number;\n\n /**\n * The message that will be showed when there are no suggestions that match the entered value.\n */\n emptyMsg?: string;\n\n /**\n * This method will be called when user choose an item in autocomplete. The selected item will be passed as the first parameter.\n */\n onSelect: (item: T, input: HTMLInputElement | HTMLTextAreaElement) => void;\n\n /**\n * Show autocomplete on focus event. Focus event will ignore the `minLength` property and will always call `fetch`.\n */\n showOnFocus?: boolean;\n\n /**\n * This method will be called to prepare suggestions and then pass them to autocomplete.\n * @param {string} text - text in the input field\n * @param {(items: T[] | false) => void} update - a callback function that must be called after suggestions are prepared\n * @param {EventTrigger} trigger - type of the event that triggered the fetch\n * @param {number} cursorPos - position of the cursor in the input field\n */\n fetch: (text: string, update: (items: T[] | false) => void, trigger: EventTrigger, cursorPos: number) => void;\n\n /**\n * Enforces that the fetch function will only be called once within the specified time frame (in milliseconds) and\n * delays execution. This prevents flooding your server with AJAX requests.\n */\n debounceWaitMs?: number;\n\n /**\n * Callback for additional autocomplete customization\n * @param {HTMLInputElement | HTMLTextAreaElement} input - input box associated with autocomplete\n * @param {ClientRect | DOMRect} inputRect - size of the input box and its position relative to the viewport\n * @param {HTMLDivElement} container - container with suggestions\n * @param {number} maxHeight - max height that can be used by autocomplete\n */\n customize?: (input: HTMLInputElement | HTMLTextAreaElement, inputRect: ClientRect | DOMRect, container: HTMLDivElement, maxHeight: number) => void;\n\n /**\n * Prevents automatic form submit when ENTER is pressed\n */\n preventSubmit?: boolean;\n\n /**\n * Prevents the first item in the list from being selected automatically. This option allows you\n * to submit a custom text by pressing ENTER even when autocomplete is displayed.\n */\n disableAutoSelect?: boolean;\n\n /**\n * Provide your keyup event handler to display autocomplete when a key is pressed that doesn't modify the content. You can also perform some additional actions.\n */\n keyup?: (e: AutocompleteEvent) => void;\n\n /**\n * Allows to display autocomplete on mouse clicks or perform some additional actions.\n */\n click?: (e: AutocompleteEvent) => void;\n}\n\nexport interface AutocompleteResult {\n /**\n * Remove event handlers, DOM elements and ARIA/accessibility attributes created by the widget.\n */\n destroy: () => void;\n}\n\nexport default function autocomplete(settings: AutocompleteSettings): AutocompleteResult {\n\n // just an alias to minimize JS file size\n const doc = document;\n\n const container: HTMLDivElement = settings.container || doc.createElement(\"div\");\n container.id = container.id || \"autocomplete-\" + uid();\n const containerStyle = container.style;\n const debounceWaitMs = settings.debounceWaitMs || 0;\n const preventSubmit = settings.preventSubmit || false;\n const disableAutoSelect = settings.disableAutoSelect || false;\n\n let items: T[] = [];\n let inputValue = \"\";\n let minLen = 2;\n const showOnFocus = settings.showOnFocus;\n let selected: T | undefined;\n let keypressCounter = 0;\n let debounceTimer: number | undefined;\n\n if (settings.minLength !== undefined) {\n minLen = settings.minLength;\n }\n\n if (!settings.input) {\n throw new Error(\"input undefined\");\n }\n\n const input: HTMLInputElement | HTMLTextAreaElement = settings.input;\n\n container.className = \"autocomplete \" + (settings.className || \"\");\n container.setAttribute(\"role\", \"listbox\");\n\n input.setAttribute(\"role\", \"combobox\");\n input.setAttribute(\"aria-expanded\", \"false\");\n input.setAttribute(\"aria-autocomplete\", \"list\");\n input.setAttribute(\"aria-controls\", container.id);\n input.setAttribute(\"aria-owns\", container.id);\n input.setAttribute(\"aria-activedescendant\", \"\");\n input.setAttribute(\"aria-haspopup\", \"listbox\");\n\n // IOS implementation for fixed positioning has many bugs, so we will use absolute positioning\n containerStyle.position = \"absolute\";\n\n /**\n * Generate a unique ID\n */\n function uid(): string {\n return Date.now().toString(36) + Math.random().toString(36).substring(2);\n }\n\n /**\n * Detach the container from DOM\n */\n function detach(): void {\n const parent = container.parentNode;\n if (parent) {\n parent.removeChild(container);\n }\n }\n\n /**\n * Clear debouncing timer if assigned\n */\n function clearDebounceTimer(): void {\n if (debounceTimer) {\n window.clearTimeout(debounceTimer);\n }\n }\n\n /**\n * Attach the container to DOM\n */\n function attach(): void {\n if (!container.parentNode) {\n doc.body.appendChild(container);\n }\n }\n\n /**\n * Check if container for autocomplete is displayed\n */\n function containerDisplayed(): boolean {\n return !!container.parentNode;\n }\n\n /**\n * Clear autocomplete state and hide container\n */\n function clear(): void {\n // prevent the update call if there are pending AJAX requests\n keypressCounter++;\n\n items = [];\n inputValue = \"\";\n selected = undefined;\n input.setAttribute(\"aria-activedescendant\", \"\");\n input.setAttribute(\"aria-expanded\", \"false\");\n detach();\n }\n\n /**\n * Update autocomplete position\n */\n function updatePosition(): void {\n if (!containerDisplayed()) {\n return;\n }\n\n input.setAttribute(\"aria-expanded\", \"true\");\n\n containerStyle.height = \"auto\";\n containerStyle.width = input.offsetWidth + \"px\";\n\n let maxHeight = 0;\n let inputRect: ClientRect | DOMRect | undefined;\n\n function calc() {\n const docEl = doc.documentElement as HTMLElement;\n const clientTop = docEl.clientTop || doc.body.clientTop || 0;\n const clientLeft = docEl.clientLeft || doc.body.clientLeft || 0;\n const scrollTop = window.pageYOffset || docEl.scrollTop;\n const scrollLeft = window.pageXOffset || docEl.scrollLeft;\n\n inputRect = input.getBoundingClientRect();\n\n const top = inputRect.top + input.offsetHeight + scrollTop - clientTop;\n const left = inputRect.left + scrollLeft - clientLeft;\n\n containerStyle.top = top + \"px\";\n containerStyle.left = left + \"px\";\n\n maxHeight = window.innerHeight - (inputRect.top + input.offsetHeight);\n\n if (maxHeight < 0) {\n maxHeight = 0;\n }\n\n containerStyle.top = top + \"px\";\n containerStyle.bottom = \"\";\n containerStyle.left = left + \"px\";\n containerStyle.maxHeight = maxHeight + \"px\";\n }\n\n // the calc method must be called twice, otherwise the calculation may be wrong on resize event (chrome browser)\n calc();\n calc();\n\n if (settings.customize && inputRect) {\n settings.customize(input, inputRect, container, maxHeight);\n }\n }\n\n /**\n * Redraw the autocomplete div element with suggestions\n */\n function update(): void {\n\n // delete all children from autocomplete DOM container\n while (container.firstChild) {\n container.removeChild(container.firstChild);\n }\n\n input.setAttribute(\"aria-activedescendant\", \"\");\n\n // function for rendering autocomplete suggestions\n let render = function (item: T, _: string, __: number): HTMLDivElement | undefined {\n const itemElement = doc.createElement(\"div\");\n itemElement.textContent = item.label || \"\";\n return itemElement;\n };\n if (settings.render) {\n render = settings.render;\n }\n\n // function to render autocomplete groups\n let renderGroup = function (groupName: string, _: string): HTMLDivElement | undefined {\n const groupDiv = doc.createElement(\"div\");\n groupDiv.textContent = groupName;\n return groupDiv;\n };\n if (settings.renderGroup) {\n renderGroup = settings.renderGroup;\n }\n\n const fragment = doc.createDocumentFragment();\n let prevGroup = uid();\n\n items.forEach(function (item: T, index: number): void {\n if (item.group && item.group !== prevGroup) {\n prevGroup = item.group;\n const groupDiv = renderGroup(item.group, inputValue);\n if (groupDiv) {\n groupDiv.className += \" group\";\n fragment.appendChild(groupDiv);\n }\n }\n const div = render(item, inputValue, index);\n if (div) {\n div.id = `${container.id}_${index}`;\n div.setAttribute(\"role\", \"option\");\n div.addEventListener(\"click\", function (ev: MouseEvent): void {\n settings.onSelect(item, input);\n clear();\n ev.preventDefault();\n ev.stopPropagation();\n });\n if (item === selected) {\n div.className += \" selected\";\n div.setAttribute(\"aria-selected\", \"true\");\n input.setAttribute(\"aria-activedescendant\", div.id);\n }\n fragment.appendChild(div);\n }\n });\n container.appendChild(fragment);\n if (items.length < 1) {\n if (settings.emptyMsg) {\n const empty = doc.createElement(\"div\");\n empty.id = `${container.id}_${uid()}`;\n empty.className = \"empty\";\n empty.textContent = settings.emptyMsg;\n container.appendChild(empty);\n input.setAttribute(\"aria-activedescendant\", empty.id);\n } else {\n clear();\n return;\n }\n }\n\n attach();\n updatePosition();\n\n updateScroll();\n }\n\n function updateIfDisplayed(): void {\n if (containerDisplayed()) {\n update();\n }\n }\n\n function resizeEventHandler(): void {\n updateIfDisplayed();\n }\n\n function scrollEventHandler(e: Event): void {\n if (e.target !== container) {\n updateIfDisplayed();\n } else {\n e.preventDefault();\n }\n }\n\n function inputEventHandler(): void {\n startFetch(EventTrigger.Keyboard);\n }\n\n /**\n * Automatically move scroll bar if selected item is not visible\n */\n function updateScroll(): void {\n const elements = container.getElementsByClassName(\"selected\");\n if (elements.length > 0) {\n let element = elements[0] as HTMLDivElement;\n\n // make group visible\n const previous = element.previousElementSibling as HTMLDivElement;\n if (previous && previous.className.indexOf(\"group\") !== -1 && !previous.previousElementSibling) {\n element = previous;\n }\n\n if (element.offsetTop < container.scrollTop) {\n container.scrollTop = element.offsetTop;\n } else {\n const selectBottom = element.offsetTop + element.offsetHeight;\n const containerBottom = container.scrollTop + container.offsetHeight;\n if (selectBottom > containerBottom) {\n container.scrollTop += selectBottom - containerBottom;\n }\n }\n }\n }\n\n /**\n * Select the previous item in suggestions\n */\n function selectPrev(): void {\n if (items.length < 1) {\n selected = undefined;\n } else {\n if (selected === items[0]) {\n selected = items[items.length - 1];\n } else {\n for (let i = items.length - 1; i > 0; i--) {\n if (selected === items[i] || i === 1) {\n selected = items[i - 1];\n break;\n }\n }\n }\n }\n }\n\n /**\n * Select the next item in suggestions\n */\n function selectNext(): void {\n if (items.length < 1) {\n selected = undefined;\n }\n if (!selected || selected === items[items.length - 1]) {\n selected = items[0];\n return;\n }\n for (let i = 0; i < (items.length - 1); i++) {\n if (selected === items[i]) {\n selected = items[i + 1];\n break;\n }\n }\n }\n\n function keydownEventHandler(ev: KeyboardEvent): void {\n const key = ev.key;\n\n if (key === \"ArrowUp\" || key === \"ArrowDown\" || key === \"Escape\") {\n const containerIsDisplayed = containerDisplayed();\n\n if (key === \"Escape\") {\n clear();\n } else {\n if (!containerIsDisplayed || items.length < 1) {\n return;\n }\n key === \"ArrowUp\"\n ? selectPrev()\n : selectNext();\n update();\n }\n\n ev.preventDefault();\n if (containerIsDisplayed) {\n ev.stopPropagation();\n }\n\n return;\n }\n\n if (key === 'Enter') {\n if (selected) {\n settings.onSelect(selected, input);\n clear();\n }\n\n if (preventSubmit) {\n ev.preventDefault();\n }\n }\n }\n\n function focusEventHandler(): void {\n if (showOnFocus) {\n startFetch(EventTrigger.Focus);\n }\n }\n\n function startFetch(trigger: EventTrigger) {\n // If multiple keys were pressed, before we get an update from server,\n // this may cause redrawing autocomplete multiple times after the last key was pressed.\n // To avoid this, the number of times keyboard was pressed will be saved and checked before redraw.\n const savedKeypressCounter = ++keypressCounter;\n\n const inputText = input.value;\n const cursorPos = input.selectionStart || 0;\n\n if (inputText.length >= minLen || trigger === EventTrigger.Focus) {\n clearDebounceTimer();\n debounceTimer = window.setTimeout(function (): void {\n settings.fetch(inputText, function (elements: T[] | false): void {\n if (keypressCounter === savedKeypressCounter && elements) {\n items = elements;\n inputValue = inputText;\n selected = (items.length < 1 || disableAutoSelect) ? undefined : items[0];\n update();\n }\n }, trigger, cursorPos);\n }, trigger === EventTrigger.Keyboard || trigger === EventTrigger.Mouse ? debounceWaitMs : 0);\n } else {\n clear();\n }\n }\n\n function keyupEventHandler(e: KeyboardEvent) {\n if (settings.keyup) {\n settings.keyup({\n event: e,\n fetch: () => startFetch(EventTrigger.Keyboard)\n });\n return;\n }\n\n if (!containerDisplayed() && e.key === \"ArrowDown\") {\n startFetch(EventTrigger.Keyboard);\n }\n }\n\n function clickEventHandler(e: MouseEvent) {\n settings.click && settings.click({\n event: e,\n fetch: () => startFetch(EventTrigger.Mouse)\n });\n }\n\n function blurEventHandler() {\n // we need to delay clear, because when we click on an item, blur will be called before click and remove items from DOM\n setTimeout(() => {\n if (doc.activeElement !== input) {\n clear();\n }\n }, 200);\n }\n\n /**\n * Fixes #26: on long clicks focus will be lost and onSelect method will not be called\n */\n container.addEventListener(\"mousedown\", function (evt: Event) {\n evt.stopPropagation();\n evt.preventDefault();\n });\n\n /**\n * Fixes #30: autocomplete closes when scrollbar is clicked in IE\n * See: https://stackoverflow.com/a/9210267/13172349\n */\n container.addEventListener(\"focus\", () => input.focus());\n\n /**\n * This function will remove DOM elements and clear event handlers\n */\n function destroy(): void {\n input.removeEventListener(\"focus\", focusEventHandler);\n input.removeEventListener(\"keyup\", keyupEventHandler as EventListenerOrEventListenerObject)\n input.removeEventListener(\"click\", clickEventHandler as EventListenerOrEventListenerObject)\n input.removeEventListener(\"keydown\", keydownEventHandler as EventListenerOrEventListenerObject);\n input.removeEventListener(\"input\", inputEventHandler as EventListenerOrEventListenerObject);\n input.removeEventListener(\"blur\", blurEventHandler);\n window.removeEventListener(\"resize\", resizeEventHandler);\n doc.removeEventListener(\"scroll\", scrollEventHandler, true);\n input.removeAttribute(\"role\");\n input.removeAttribute(\"aria-expanded\");\n input.removeAttribute(\"aria-autocomplete\");\n input.removeAttribute(\"aria-controls\");\n input.removeAttribute(\"aria-activedescendant\");\n input.removeAttribute(\"aria-owns\");\n input.removeAttribute(\"aria-haspopup\");\n clearDebounceTimer();\n clear();\n }\n\n // setup event handlers\n input.addEventListener(\"keyup\", keyupEventHandler as EventListenerOrEventListenerObject);\n input.addEventListener(\"click\", clickEventHandler as EventListenerOrEventListenerObject);\n input.addEventListener(\"keydown\", keydownEventHandler as EventListenerOrEventListenerObject);\n input.addEventListener(\"input\", inputEventHandler as EventListenerOrEventListenerObject);\n input.addEventListener(\"blur\", blurEventHandler);\n input.addEventListener(\"focus\", focusEventHandler);\n window.addEventListener(\"resize\", resizeEventHandler);\n doc.addEventListener(\"scroll\", scrollEventHandler, true);\n\n return {\n destroy\n };\n}\n"],"names":[],"mappings":";;;;;;IAAA;;;;;aAkIwB,YAAY,CAA6B,QAAiC;;QAG9F,IAAM,GAAG,GAAG,QAAQ,CAAC;QAErB,IAAM,SAAS,GAAmB,QAAQ,CAAC,SAAS,IAAI,GAAG,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QACjF,SAAS,CAAC,EAAE,GAAG,SAAS,CAAC,EAAE,IAAI,eAAe,GAAG,GAAG,EAAE,CAAC;QACvD,IAAM,cAAc,GAAG,SAAS,CAAC,KAAK,CAAC;QACvC,IAAM,cAAc,GAAG,QAAQ,CAAC,cAAc,IAAI,CAAC,CAAC;QACpD,IAAM,aAAa,GAAG,QAAQ,CAAC,aAAa,IAAI,KAAK,CAAC;QACtD,IAAM,iBAAiB,GAAG,QAAQ,CAAC,iBAAiB,IAAI,KAAK,CAAC;QAE9D,IAAI,KAAK,GAAQ,EAAE,CAAC;QACpB,IAAI,UAAU,GAAG,EAAE,CAAC;QACpB,IAAI,MAAM,GAAG,CAAC,CAAC;QACf,IAAM,WAAW,GAAG,QAAQ,CAAC,WAAW,CAAC;QACzC,IAAI,QAAuB,CAAC;QAC5B,IAAI,eAAe,GAAG,CAAC,CAAC;QACxB,IAAI,aAAiC,CAAC;QAEtC,IAAI,QAAQ,CAAC,SAAS,KAAK,SAAS,EAAE;YAClC,MAAM,GAAG,QAAQ,CAAC,SAAS,CAAC;SAC/B;QAED,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE;YACjB,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;SACtC;QAED,IAAM,KAAK,GAA2C,QAAQ,CAAC,KAAK,CAAC;QAErE,SAAS,CAAC,SAAS,GAAG,eAAe,IAAI,QAAQ,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC;QACnE,SAAS,CAAC,YAAY,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;QAE1C,KAAK,CAAC,YAAY,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;QACvC,KAAK,CAAC,YAAY,CAAC,eAAe,EAAE,OAAO,CAAC,CAAC;QAC7C,KAAK,CAAC,YAAY,CAAC,mBAAmB,EAAE,MAAM,CAAC,CAAC;QAChD,KAAK,CAAC,YAAY,CAAC,eAAe,EAAE,SAAS,CAAC,EAAE,CAAC,CAAC;QAClD,KAAK,CAAC,YAAY,CAAC,WAAW,EAAE,SAAS,CAAC,EAAE,CAAC,CAAC;QAC9C,KAAK,CAAC,YAAY,CAAC,uBAAuB,EAAE,EAAE,CAAC,CAAC;QAChD,KAAK,CAAC,YAAY,CAAC,eAAe,EAAE,SAAS,CAAC,CAAC;;QAG/C,cAAc,CAAC,QAAQ,GAAG,UAAU,CAAC;;;;QAKrC,SAAS,GAAG;YACR,OAAO,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;SAC5E;;;;QAKD,SAAS,MAAM;YACX,IAAM,MAAM,GAAG,SAAS,CAAC,UAAU,CAAC;YACpC,IAAI,MAAM,EAAE;gBACR,MAAM,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;aACjC;SACJ;;;;QAKD,SAAS,kBAAkB;YACvB,IAAI,aAAa,EAAE;gBACf,MAAM,CAAC,YAAY,CAAC,aAAa,CAAC,CAAC;aACtC;SACJ;;;;QAKD,SAAS,MAAM;YACX,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE;gBACvB,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;aACnC;SACJ;;;;QAKD,SAAS,kBAAkB;YACvB,OAAO,CAAC,CAAC,SAAS,CAAC,UAAU,CAAC;SACjC;;;;QAKD,SAAS,KAAK;;YAEV,eAAe,EAAE,CAAC;YAElB,KAAK,GAAG,EAAE,CAAC;YACX,UAAU,GAAG,EAAE,CAAC;YAChB,QAAQ,GAAG,SAAS,CAAC;YACrB,KAAK,CAAC,YAAY,CAAC,uBAAuB,EAAE,EAAE,CAAC,CAAC;YAChD,KAAK,CAAC,YAAY,CAAC,eAAe,EAAE,OAAO,CAAC,CAAC;YAC7C,MAAM,EAAE,CAAC;SACZ;;;;QAKD,SAAS,cAAc;YACnB,IAAI,CAAC,kBAAkB,EAAE,EAAE;gBACvB,OAAO;aACV;YAED,KAAK,CAAC,YAAY,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC;YAE5C,cAAc,CAAC,MAAM,GAAG,MAAM,CAAC;YAC/B,cAAc,CAAC,KAAK,GAAG,KAAK,CAAC,WAAW,GAAG,IAAI,CAAC;YAEhD,IAAI,SAAS,GAAG,CAAC,CAAC;YAClB,IAAI,SAA2C,CAAC;YAEhD,SAAS,IAAI;gBACT,IAAM,KAAK,GAAG,GAAG,CAAC,eAA8B,CAAC;gBACjD,IAAM,SAAS,GAAG,KAAK,CAAC,SAAS,IAAI,GAAG,CAAC,IAAI,CAAC,SAAS,IAAI,CAAC,CAAC;gBAC7D,IAAM,UAAU,GAAG,KAAK,CAAC,UAAU,IAAI,GAAG,CAAC,IAAI,CAAC,UAAU,IAAI,CAAC,CAAC;gBAChE,IAAM,SAAS,GAAG,MAAM,CAAC,WAAW,IAAI,KAAK,CAAC,SAAS,CAAC;gBACxD,IAAM,UAAU,GAAG,MAAM,CAAC,WAAW,IAAI,KAAK,CAAC,UAAU,CAAC;gBAE1D,SAAS,GAAG,KAAK,CAAC,qBAAqB,EAAE,CAAC;gBAE1C,IAAM,GAAG,GAAG,SAAS,CAAC,GAAG,GAAG,KAAK,CAAC,YAAY,GAAG,SAAS,GAAG,SAAS,CAAC;gBACvE,IAAM,IAAI,GAAG,SAAS,CAAC,IAAI,GAAG,UAAU,GAAG,UAAU,CAAC;gBAEtD,cAAc,CAAC,GAAG,GAAG,GAAG,GAAG,IAAI,CAAC;gBAChC,cAAc,CAAC,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;gBAElC,SAAS,GAAG,MAAM,CAAC,WAAW,IAAI,SAAS,CAAC,GAAG,GAAG,KAAK,CAAC,YAAY,CAAC,CAAC;gBAEtE,IAAI,SAAS,GAAG,CAAC,EAAE;oBACf,SAAS,GAAG,CAAC,CAAC;iBACjB;gBAED,cAAc,CAAC,GAAG,GAAG,GAAG,GAAG,IAAI,CAAC;gBAChC,cAAc,CAAC,MAAM,GAAG,EAAE,CAAC;gBAC3B,cAAc,CAAC,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;gBAClC,cAAc,CAAC,SAAS,GAAG,SAAS,GAAG,IAAI,CAAC;aAC/C;;YAGD,IAAI,EAAE,CAAC;YACP,IAAI,EAAE,CAAC;YAEP,IAAI,QAAQ,CAAC,SAAS,IAAI,SAAS,EAAE;gBACjC,QAAQ,CAAC,SAAS,CAAC,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC;aAC9D;SACJ;;;;QAKD,SAAS,MAAM;;YAGX,OAAO,SAAS,CAAC,UAAU,EAAE;gBACzB,SAAS,CAAC,WAAW,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;aAC/C;YAED,KAAK,CAAC,YAAY,CAAC,uBAAuB,EAAE,EAAE,CAAC,CAAC;;YAGhD,IAAI,MAAM,GAAG,UAAU,IAAO,EAAE,CAAS,EAAE,EAAU;gBACjD,IAAM,WAAW,GAAG,GAAG,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;gBAC7C,WAAW,CAAC,WAAW,GAAG,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC;gBAC3C,OAAO,WAAW,CAAC;aACtB,CAAC;YACF,IAAI,QAAQ,CAAC,MAAM,EAAE;gBACjB,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC;aAC5B;;YAGD,IAAI,WAAW,GAAG,UAAU,SAAiB,EAAE,CAAS;gBACpD,IAAM,QAAQ,GAAG,GAAG,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;gBAC1C,QAAQ,CAAC,WAAW,GAAG,SAAS,CAAC;gBACjC,OAAO,QAAQ,CAAC;aACnB,CAAC;YACF,IAAI,QAAQ,CAAC,WAAW,EAAE;gBACtB,WAAW,GAAG,QAAQ,CAAC,WAAW,CAAC;aACtC;YAED,IAAM,QAAQ,GAAG,GAAG,CAAC,sBAAsB,EAAE,CAAC;YAC9C,IAAI,SAAS,GAAG,GAAG,EAAE,CAAC;YAEtB,KAAK,CAAC,OAAO,CAAC,UAAU,IAAO,EAAE,KAAa;gBAC1C,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS,EAAE;oBACxC,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC;oBACvB,IAAM,QAAQ,GAAG,WAAW,CAAC,IAAI,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;oBACrD,IAAI,QAAQ,EAAE;wBACV,QAAQ,CAAC,SAAS,IAAI,QAAQ,CAAC;wBAC/B,QAAQ,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;qBAClC;iBACJ;gBACD,IAAM,GAAG,GAAG,MAAM,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,CAAC,CAAC;gBAC5C,IAAI,GAAG,EAAE;oBACL,GAAG,CAAC,EAAE,GAAM,SAAS,CAAC,EAAE,SAAI,KAAO,CAAC;oBACpC,GAAG,CAAC,YAAY,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;oBACnC,GAAG,CAAC,gBAAgB,CAAC,OAAO,EAAE,UAAU,EAAc;wBAClD,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;wBAC/B,KAAK,EAAE,CAAC;wBACR,EAAE,CAAC,cAAc,EAAE,CAAC;wBACpB,EAAE,CAAC,eAAe,EAAE,CAAC;qBACxB,CAAC,CAAC;oBACH,IAAI,IAAI,KAAK,QAAQ,EAAE;wBACnB,GAAG,CAAC,SAAS,IAAI,WAAW,CAAC;wBAC7B,GAAG,CAAC,YAAY,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC;wBAC1C,KAAK,CAAC,YAAY,CAAC,uBAAuB,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;qBACvD;oBACD,QAAQ,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;iBAC7B;aACJ,CAAC,CAAC;YACH,SAAS,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;YAChC,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE;gBAClB,IAAI,QAAQ,CAAC,QAAQ,EAAE;oBACnB,IAAM,KAAK,GAAG,GAAG,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;oBACvC,KAAK,CAAC,EAAE,GAAM,SAAS,CAAC,EAAE,SAAI,GAAG,EAAI,CAAC;oBACtC,KAAK,CAAC,SAAS,GAAG,OAAO,CAAC;oBAC1B,KAAK,CAAC,WAAW,GAAG,QAAQ,CAAC,QAAQ,CAAC;oBACtC,SAAS,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;oBAC7B,KAAK,CAAC,YAAY,CAAC,uBAAuB,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC;iBACzD;qBAAM;oBACH,KAAK,EAAE,CAAC;oBACR,OAAO;iBACV;aACJ;YAED,MAAM,EAAE,CAAC;YACT,cAAc,EAAE,CAAC;YAEjB,YAAY,EAAE,CAAC;SAClB;QAED,SAAS,iBAAiB;YACtB,IAAI,kBAAkB,EAAE,EAAE;gBACtB,MAAM,EAAE,CAAC;aACZ;SACJ;QAED,SAAS,kBAAkB;YACvB,iBAAiB,EAAE,CAAC;SACvB;QAED,SAAS,kBAAkB,CAAC,CAAQ;YAChC,IAAI,CAAC,CAAC,MAAM,KAAK,SAAS,EAAE;gBACxB,iBAAiB,EAAE,CAAC;aACvB;iBAAM;gBACH,CAAC,CAAC,cAAc,EAAE,CAAC;aACtB;SACJ;QAED,SAAS,iBAAiB;YACtB,UAAU,kBAAuB,CAAC;SACrC;;;;QAKD,SAAS,YAAY;YACjB,IAAM,QAAQ,GAAG,SAAS,CAAC,sBAAsB,CAAC,UAAU,CAAC,CAAC;YAC9D,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE;gBACrB,IAAI,OAAO,GAAG,QAAQ,CAAC,CAAC,CAAmB,CAAC;;gBAG5C,IAAM,QAAQ,GAAG,OAAO,CAAC,sBAAwC,CAAC;gBAClE,IAAI,QAAQ,IAAI,QAAQ,CAAC,SAAS,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,sBAAsB,EAAE;oBAC5F,OAAO,GAAG,QAAQ,CAAC;iBACtB;gBAED,IAAI,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC,SAAS,EAAE;oBACzC,SAAS,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC;iBAC3C;qBAAM;oBACH,IAAM,YAAY,GAAG,OAAO,CAAC,SAAS,GAAG,OAAO,CAAC,YAAY,CAAC;oBAC9D,IAAM,eAAe,GAAG,SAAS,CAAC,SAAS,GAAG,SAAS,CAAC,YAAY,CAAC;oBACrE,IAAI,YAAY,GAAG,eAAe,EAAE;wBAChC,SAAS,CAAC,SAAS,IAAI,YAAY,GAAG,eAAe,CAAC;qBACzD;iBACJ;aACJ;SACJ;;;;QAKD,SAAS,UAAU;YACf,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE;gBAClB,QAAQ,GAAG,SAAS,CAAC;aACxB;iBAAM;gBACH,IAAI,QAAQ,KAAK,KAAK,CAAC,CAAC,CAAC,EAAE;oBACvB,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;iBACtC;qBAAM;oBACH,KAAK,IAAI,CAAC,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE;wBACvC,IAAI,QAAQ,KAAK,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE;4BAClC,QAAQ,GAAG,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;4BACxB,MAAM;yBACT;qBACJ;iBACJ;aACJ;SACJ;;;;QAKD,SAAS,UAAU;YACf,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE;gBAClB,QAAQ,GAAG,SAAS,CAAC;aACxB;YACD,IAAI,CAAC,QAAQ,IAAI,QAAQ,KAAK,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE;gBACnD,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;gBACpB,OAAO;aACV;YACD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;gBACzC,IAAI,QAAQ,KAAK,KAAK,CAAC,CAAC,CAAC,EAAE;oBACvB,QAAQ,GAAG,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;oBACxB,MAAM;iBACT;aACJ;SACJ;QAED,SAAS,mBAAmB,CAAC,EAAiB;YAC1C,IAAM,GAAG,GAAG,EAAE,CAAC,GAAG,CAAC;YAEnB,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,WAAW,IAAI,GAAG,KAAK,QAAQ,EAAE;gBAC9D,IAAM,oBAAoB,GAAG,kBAAkB,EAAE,CAAC;gBAElD,IAAI,GAAG,KAAK,QAAQ,EAAE;oBAClB,KAAK,EAAE,CAAC;iBACX;qBAAM;oBACH,IAAI,CAAC,oBAAoB,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE;wBAC3C,OAAO;qBACV;oBACD,GAAG,KAAK,SAAS;0BACX,UAAU,EAAE;0BACZ,UAAU,EAAE,CAAC;oBACnB,MAAM,EAAE,CAAC;iBACZ;gBAED,EAAE,CAAC,cAAc,EAAE,CAAC;gBACpB,IAAI,oBAAoB,EAAE;oBACtB,EAAE,CAAC,eAAe,EAAE,CAAC;iBACxB;gBAED,OAAO;aACV;YAED,IAAI,GAAG,KAAK,OAAO,EAAE;gBACjB,IAAI,QAAQ,EAAE;oBACV,QAAQ,CAAC,QAAQ,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;oBACnC,KAAK,EAAE,CAAC;iBACX;gBAED,IAAI,aAAa,EAAE;oBACf,EAAE,CAAC,cAAc,EAAE,CAAC;iBACvB;aACJ;SACJ;QAED,SAAS,iBAAiB;YACtB,IAAI,WAAW,EAAE;gBACb,UAAU,eAAoB,CAAC;aAClC;SACJ;QAED,SAAS,UAAU,CAAC,OAAqB;;;;YAIrC,IAAM,oBAAoB,GAAG,EAAE,eAAe,CAAC;YAE/C,IAAM,SAAS,GAAG,KAAK,CAAC,KAAK,CAAC;YAC9B,IAAM,SAAS,GAAG,KAAK,CAAC,cAAc,IAAI,CAAC,CAAC;YAE5C,IAAI,SAAS,CAAC,MAAM,IAAI,MAAM,IAAI,OAAO,oBAAyB;gBAC9D,kBAAkB,EAAE,CAAC;gBACrB,aAAa,GAAG,MAAM,CAAC,UAAU,CAAC;oBAC9B,QAAQ,CAAC,KAAK,CAAC,SAAS,EAAE,UAAU,QAAqB;wBACrD,IAAI,eAAe,KAAK,oBAAoB,IAAI,QAAQ,EAAE;4BACtD,KAAK,GAAG,QAAQ,CAAC;4BACjB,UAAU,GAAG,SAAS,CAAC;4BACvB,QAAQ,GAAG,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,iBAAiB,IAAI,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;4BAC1E,MAAM,EAAE,CAAC;yBACZ;qBACJ,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC;iBAC1B,EAAE,OAAO,yBAA8B,OAAO,qBAA0B,cAAc,GAAG,CAAC,CAAC,CAAC;aAChG;iBAAM;gBACH,KAAK,EAAE,CAAC;aACX;SACJ;QAED,SAAS,iBAAiB,CAAC,CAAgB;YACvC,IAAI,QAAQ,CAAC,KAAK,EAAE;gBAChB,QAAQ,CAAC,KAAK,CAAC;oBACX,KAAK,EAAE,CAAC;oBACR,KAAK,EAAE,cAAM,OAAA,UAAU,kBAAuB,GAAA;iBACjD,CAAC,CAAC;gBACH,OAAO;aACV;YAED,IAAI,CAAC,kBAAkB,EAAE,IAAI,CAAC,CAAC,GAAG,KAAK,WAAW,EAAE;gBAChD,UAAU,kBAAuB,CAAC;aACrC;SACJ;QAED,SAAS,iBAAiB,CAAC,CAAa;YACpC,QAAQ,CAAC,KAAK,IAAI,QAAQ,CAAC,KAAK,CAAC;gBAC7B,KAAK,EAAE,CAAC;gBACR,KAAK,EAAE,cAAM,OAAA,UAAU,eAAoB,GAAA;aAC9C,CAAC,CAAC;SACN;QAED,SAAS,gBAAgB;;YAErB,UAAU,CAAC;gBACP,IAAI,GAAG,CAAC,aAAa,KAAK,KAAK,EAAE;oBAC7B,KAAK,EAAE,CAAC;iBACX;aACJ,EAAE,GAAG,CAAC,CAAC;SACX;;;;QAKD,SAAS,CAAC,gBAAgB,CAAC,WAAW,EAAE,UAAU,GAAU;YACxD,GAAG,CAAC,eAAe,EAAE,CAAC;YACtB,GAAG,CAAC,cAAc,EAAE,CAAC;SACxB,CAAC,CAAC;;;;;QAMH,SAAS,CAAC,gBAAgB,CAAC,OAAO,EAAE,cAAM,OAAA,KAAK,CAAC,KAAK,EAAE,GAAA,CAAC,CAAC;;;;QAKzD,SAAS,OAAO;YACZ,KAAK,CAAC,mBAAmB,CAAC,OAAO,EAAE,iBAAiB,CAAC,CAAC;YACtD,KAAK,CAAC,mBAAmB,CAAC,OAAO,EAAE,iBAAuD,CAAC,CAAA;YAC3F,KAAK,CAAC,mBAAmB,CAAC,OAAO,EAAE,iBAAuD,CAAC,CAAA;YAC3F,KAAK,CAAC,mBAAmB,CAAC,SAAS,EAAE,mBAAyD,CAAC,CAAC;YAChG,KAAK,CAAC,mBAAmB,CAAC,OAAO,EAAE,iBAAuD,CAAC,CAAC;YAC5F,KAAK,CAAC,mBAAmB,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;YACpD,MAAM,CAAC,mBAAmB,CAAC,QAAQ,EAAE,kBAAkB,CAAC,CAAC;YACzD,GAAG,CAAC,mBAAmB,CAAC,QAAQ,EAAE,kBAAkB,EAAE,IAAI,CAAC,CAAC;YAC5D,KAAK,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;YAC9B,KAAK,CAAC,eAAe,CAAC,eAAe,CAAC,CAAC;YACvC,KAAK,CAAC,eAAe,CAAC,mBAAmB,CAAC,CAAC;YAC3C,KAAK,CAAC,eAAe,CAAC,eAAe,CAAC,CAAC;YACvC,KAAK,CAAC,eAAe,CAAC,uBAAuB,CAAC,CAAC;YAC/C,KAAK,CAAC,eAAe,CAAC,WAAW,CAAC,CAAC;YACnC,KAAK,CAAC,eAAe,CAAC,eAAe,CAAC,CAAC;YACvC,kBAAkB,EAAE,CAAC;YACrB,KAAK,EAAE,CAAC;SACX;;QAGD,KAAK,CAAC,gBAAgB,CAAC,OAAO,EAAE,iBAAuD,CAAC,CAAC;QACzF,KAAK,CAAC,gBAAgB,CAAC,OAAO,EAAE,iBAAuD,CAAC,CAAC;QACzF,KAAK,CAAC,gBAAgB,CAAC,SAAS,EAAE,mBAAyD,CAAC,CAAC;QAC7F,KAAK,CAAC,gBAAgB,CAAC,OAAO,EAAE,iBAAuD,CAAC,CAAC;QACzF,KAAK,CAAC,gBAAgB,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;QACjD,KAAK,CAAC,gBAAgB,CAAC,OAAO,EAAE,iBAAiB,CAAC,CAAC;QACnD,MAAM,CAAC,gBAAgB,CAAC,QAAQ,EAAE,kBAAkB,CAAC,CAAC;QACtD,GAAG,CAAC,gBAAgB,CAAC,QAAQ,EAAE,kBAAkB,EAAE,IAAI,CAAC,CAAC;QAEzD,OAAO;YACH,OAAO,SAAA;SACV,CAAC;IACN;;;;;;;;"} \ No newline at end of file diff --git a/autocomplete.min.js b/autocomplete.min.js index 21b3796..b4b03a3 100644 --- a/autocomplete.min.js +++ b/autocomplete.min.js @@ -1,2 +1,2 @@ -!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).autocomplete=t()}(this,(function(){"use strict";return function(e){var t=document,n=e.container||t.createElement("div");n.id=n.id||"autocomplete-"+m();var i,r,o=n.style,a=e.debounceWaitMs||0,s=e.preventSubmit||!1,u=e.disableAutoSelect||!1,d=[],c="",l=2,f=e.showOnFocus,p=0;if(void 0!==e.minLength&&(l=e.minLength),!e.input)throw new Error("input undefined");var v=e.input;function m(){return Date.now().toString(36)+Math.random().toString(36).substring(2)}function b(){r&&window.clearTimeout(r)}function g(){return!!n.parentNode}function h(){var e;p++,d=[],c="",i=void 0,v.setAttribute("aria-activedescendant",""),v.setAttribute("aria-expanded","false"),(e=n.parentNode)&&e.removeChild(n)}function E(){for(;n.firstChild;)n.removeChild(n.firstChild);v.setAttribute("aria-activedescendant","");var r=function(e,n,i){var r=t.createElement("div");return r.textContent=e.label||"",r};e.render&&(r=e.render);var a=function(e,n){var i=t.createElement("div");return i.textContent=e,i};e.renderGroup&&(a=e.renderGroup);var s=t.createDocumentFragment(),u="#9?$";if(d.forEach((function(t,o){if(t.group&&t.group!==u){u=t.group;var d=a(t.group,c);d&&(d.className+=" group",s.appendChild(d))}var l=r(t,c,o);l&&(l.id=n.id+"_"+o,l.setAttribute("role","option"),l.addEventListener("click",(function(n){e.onSelect(t,v),h(),n.preventDefault(),n.stopPropagation()})),t===i&&(l.className+=" selected",l.setAttribute("aria-selected","true"),v.setAttribute("aria-activedescendant",l.id)),s.appendChild(l))})),n.appendChild(s),d.length<1){if(!e.emptyMsg)return void h();var l=t.createElement("div");l.id=n.id+"_"+m(),l.className="empty",l.textContent=e.emptyMsg,n.appendChild(l),v.setAttribute("aria-activedescendant",l.id)}n.parentNode||t.body.appendChild(n),function(){if(g()){v.setAttribute("aria-expanded","true"),o.height="auto",o.width=v.offsetWidth+"px";var i,r=0;a(),a(),e.customize&&i&&e.customize(v,i,n,r)}function a(){var e=t.documentElement,n=e.clientTop||t.body.clientTop||0,a=e.clientLeft||t.body.clientLeft||0,s=window.pageYOffset||e.scrollTop,u=window.pageXOffset||e.scrollLeft,d=(i=v.getBoundingClientRect()).top+v.offsetHeight+s-n,c=i.left+u-a;o.top=d+"px",o.left=c+"px",(r=window.innerHeight-(i.top+v.offsetHeight))<0&&(r=0),o.top=d+"px",o.bottom="",o.left=c+"px",o.maxHeight=r+"px"}}(),function(){var e=n.getElementsByClassName("selected");if(e.length>0){var t=e[0],i=t.previousElementSibling;if(i&&-1!==i.className.indexOf("group")&&!i.previousElementSibling&&(t=i),t.offsetTopo&&(n.scrollTop+=r-o)}}}()}function w(){g()&&E()}function A(){w()}function L(e){e.target!==n?w():e.preventDefault()}function y(){T(0)}function x(t){var n=t.key;if("ArrowUp"===n||"ArrowDown"===n||"Escape"===n){var r=g();if("Escape"===n)h();else{if(!r||d.length<1)return;"ArrowUp"===n?function(){if(d.length<1)i=void 0;else if(i===d[0])i=d[d.length-1];else for(var e=d.length-1;e>0;e--)if(i===d[e]||1===e){i=d[e-1];break}}():function(){if(d.length<1&&(i=void 0),i&&i!==d[d.length-1]){for(var e=0;e=l||1===t?(b(),r=window.setTimeout((function(){e.fetch(o,(function(e){p===n&&e&&(c=o,i=(d=e).length<1||u?void 0:d[0],E())}),t,s)}),0===t||2===t?a:0)):h()}function C(t){e.keyup?e.keyup({event:t,fetch:function(){return T(0)}}):g()||"ArrowDown"!==t.key||T(0)}function N(t){e.click&&e.click({event:t,fetch:function(){return T(2)}})}function D(){setTimeout((function(){t.activeElement!==v&&h()}),200)}return n.className="autocomplete "+(e.className||""),n.setAttribute("role","listbox"),v.setAttribute("role","combobox"),v.setAttribute("aria-expanded","false"),v.setAttribute("aria-autocomplete","list"),v.setAttribute("aria-controls",n.id),v.setAttribute("aria-owns",n.id),v.setAttribute("aria-activedescendant",""),v.setAttribute("aria-haspopup","listbox"),o.position="absolute",n.addEventListener("mousedown",(function(e){e.stopPropagation(),e.preventDefault()})),n.addEventListener("focus",(function(){return v.focus()})),v.addEventListener("keyup",C),v.addEventListener("click",N),v.addEventListener("keydown",x),v.addEventListener("input",y),v.addEventListener("blur",D),v.addEventListener("focus",k),window.addEventListener("resize",A),t.addEventListener("scroll",L,!0),{destroy:function(){v.removeEventListener("focus",k),v.removeEventListener("keyup",C),v.removeEventListener("click",N),v.removeEventListener("keydown",x),v.removeEventListener("input",y),v.removeEventListener("blur",D),window.removeEventListener("resize",A),t.removeEventListener("scroll",L,!0),v.removeAttribute("role"),v.removeAttribute("aria-expanded"),v.removeAttribute("aria-autocomplete"),v.removeAttribute("aria-controls"),v.removeAttribute("aria-activedescendant"),v.removeAttribute("aria-owns"),v.removeAttribute("aria-haspopup"),b(),h()}}}})); +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).autocomplete=t()}(this,(function(){"use strict";return function(e){var t=document,n=e.container||t.createElement("div");n.id=n.id||"autocomplete-"+m();var i,r,o=n.style,a=e.debounceWaitMs||0,s=e.preventSubmit||!1,u=e.disableAutoSelect||!1,d=[],c="",l=2,f=e.showOnFocus,p=0;if(void 0!==e.minLength&&(l=e.minLength),!e.input)throw new Error("input undefined");var v=e.input;function m(){return Date.now().toString(36)+Math.random().toString(36).substring(2)}function b(){r&&window.clearTimeout(r)}function g(){return!!n.parentNode}function h(){var e;p++,d=[],c="",i=void 0,v.setAttribute("aria-activedescendant",""),v.setAttribute("aria-expanded","false"),(e=n.parentNode)&&e.removeChild(n)}function E(){for(;n.firstChild;)n.removeChild(n.firstChild);v.setAttribute("aria-activedescendant","");var r=function(e,n,i){var r=t.createElement("div");return r.textContent=e.label||"",r};e.render&&(r=e.render);var a=function(e,n){var i=t.createElement("div");return i.textContent=e,i};e.renderGroup&&(a=e.renderGroup);var s=t.createDocumentFragment(),u=m();if(d.forEach((function(t,o){if(t.group&&t.group!==u){u=t.group;var d=a(t.group,c);d&&(d.className+=" group",s.appendChild(d))}var l=r(t,c,o);l&&(l.id=n.id+"_"+o,l.setAttribute("role","option"),l.addEventListener("click",(function(n){e.onSelect(t,v),h(),n.preventDefault(),n.stopPropagation()})),t===i&&(l.className+=" selected",l.setAttribute("aria-selected","true"),v.setAttribute("aria-activedescendant",l.id)),s.appendChild(l))})),n.appendChild(s),d.length<1){if(!e.emptyMsg)return void h();var l=t.createElement("div");l.id=n.id+"_"+m(),l.className="empty",l.textContent=e.emptyMsg,n.appendChild(l),v.setAttribute("aria-activedescendant",l.id)}n.parentNode||t.body.appendChild(n),function(){if(g()){v.setAttribute("aria-expanded","true"),o.height="auto",o.width=v.offsetWidth+"px";var i,r=0;a(),a(),e.customize&&i&&e.customize(v,i,n,r)}function a(){var e=t.documentElement,n=e.clientTop||t.body.clientTop||0,a=e.clientLeft||t.body.clientLeft||0,s=window.pageYOffset||e.scrollTop,u=window.pageXOffset||e.scrollLeft,d=(i=v.getBoundingClientRect()).top+v.offsetHeight+s-n,c=i.left+u-a;o.top=d+"px",o.left=c+"px",(r=window.innerHeight-(i.top+v.offsetHeight))<0&&(r=0),o.top=d+"px",o.bottom="",o.left=c+"px",o.maxHeight=r+"px"}}(),function(){var e=n.getElementsByClassName("selected");if(e.length>0){var t=e[0],i=t.previousElementSibling;if(i&&-1!==i.className.indexOf("group")&&!i.previousElementSibling&&(t=i),t.offsetTopo&&(n.scrollTop+=r-o)}}}()}function w(){g()&&E()}function A(){w()}function L(e){e.target!==n?w():e.preventDefault()}function y(){T(0)}function x(t){var n=t.key;if("ArrowUp"===n||"ArrowDown"===n||"Escape"===n){var r=g();if("Escape"===n)h();else{if(!r||d.length<1)return;"ArrowUp"===n?function(){if(d.length<1)i=void 0;else if(i===d[0])i=d[d.length-1];else for(var e=d.length-1;e>0;e--)if(i===d[e]||1===e){i=d[e-1];break}}():function(){if(d.length<1&&(i=void 0),i&&i!==d[d.length-1]){for(var e=0;e=l||1===t?(b(),r=window.setTimeout((function(){e.fetch(o,(function(e){p===n&&e&&(c=o,i=(d=e).length<1||u?void 0:d[0],E())}),t,s)}),0===t||2===t?a:0)):h()}function C(t){e.keyup?e.keyup({event:t,fetch:function(){return T(0)}}):g()||"ArrowDown"!==t.key||T(0)}function N(t){e.click&&e.click({event:t,fetch:function(){return T(2)}})}function D(){setTimeout((function(){t.activeElement!==v&&h()}),200)}return n.className="autocomplete "+(e.className||""),n.setAttribute("role","listbox"),v.setAttribute("role","combobox"),v.setAttribute("aria-expanded","false"),v.setAttribute("aria-autocomplete","list"),v.setAttribute("aria-controls",n.id),v.setAttribute("aria-owns",n.id),v.setAttribute("aria-activedescendant",""),v.setAttribute("aria-haspopup","listbox"),o.position="absolute",n.addEventListener("mousedown",(function(e){e.stopPropagation(),e.preventDefault()})),n.addEventListener("focus",(function(){return v.focus()})),v.addEventListener("keyup",C),v.addEventListener("click",N),v.addEventListener("keydown",x),v.addEventListener("input",y),v.addEventListener("blur",D),v.addEventListener("focus",k),window.addEventListener("resize",A),t.addEventListener("scroll",L,!0),{destroy:function(){v.removeEventListener("focus",k),v.removeEventListener("keyup",C),v.removeEventListener("click",N),v.removeEventListener("keydown",x),v.removeEventListener("input",y),v.removeEventListener("blur",D),window.removeEventListener("resize",A),t.removeEventListener("scroll",L,!0),v.removeAttribute("role"),v.removeAttribute("aria-expanded"),v.removeAttribute("aria-autocomplete"),v.removeAttribute("aria-controls"),v.removeAttribute("aria-activedescendant"),v.removeAttribute("aria-owns"),v.removeAttribute("aria-haspopup"),b(),h()}}}})); //# sourceMappingURL=autocomplete.min.js.map diff --git a/autocomplete.min.js.map b/autocomplete.min.js.map index 7898c0a..ce50e45 100644 --- a/autocomplete.min.js.map +++ b/autocomplete.min.js.map @@ -1 +1 @@ -{"version":3,"file":"autocomplete.min.js","sources":["autocomplete.ts"],"sourcesContent":["/*\n * https://github.com/kraaden/autocomplete\n * Copyright (c) 2016 Denys Krasnoshchok\n * MIT License\n */\n\nexport const enum EventTrigger {\n Keyboard = 0,\n Focus = 1,\n Mouse = 2\n}\n\nexport interface AutocompleteItem {\n label?: string;\n group?: string;\n}\n\nexport interface AutocompleteEvent {\n event: T;\n fetch: () => void;\n}\n\nexport interface AutocompleteSettings {\n /**\n * Autocomplete will be attached to this element.\n */\n input: HTMLInputElement | HTMLTextAreaElement;\n\n /**\n * Provide your own container for the widget.\n * If not specified, a new DIV element will be created.\n */\n container?: HTMLDivElement;\n\n /**\n * This method allows you to override the default rendering function for items.\n * It must return a DIV element or undefined to skip rendering.\n */\n render?: (item: T, currentValue: string, index: number) => HTMLDivElement | undefined;\n\n /**\n * This method allows you to override the default rendering function for item groups.\n * It must return a DIV element or undefined to skip rendering.\n */\n renderGroup?: (name: string, currentValue: string) => HTMLDivElement | undefined;\n\n /**\n * If specified, the autocomplete DOM element will have this class assigned to it.\n */\n className?: string;\n\n /**\n * Specify the minimum text length required to show autocomplete.\n */\n minLength?: number;\n\n /**\n * The message that will be showed when there are no suggestions that match the entered value.\n */\n emptyMsg?: string;\n\n /**\n * This method will be called when user choose an item in autocomplete. The selected item will be passed as the first parameter.\n */\n onSelect: (item: T, input: HTMLInputElement | HTMLTextAreaElement) => void;\n\n /**\n * Show autocomplete on focus event. Focus event will ignore the `minLength` property and will always call `fetch`.\n */\n showOnFocus?: boolean;\n\n /**\n * This method will be called to prepare suggestions and then pass them to autocomplete.\n * @param {string} text - text in the input field\n * @param {(items: T[] | false) => void} update - a callback function that must be called after suggestions are prepared\n * @param {EventTrigger} trigger - type of the event that triggered the fetch\n * @param {number} cursorPos - position of the cursor in the input field\n */\n fetch: (text: string, update: (items: T[] | false) => void, trigger: EventTrigger, cursorPos: number) => void;\n\n /**\n * Enforces that the fetch function will only be called once within the specified time frame (in milliseconds) and\n * delays execution. This prevents flooding your server with AJAX requests.\n */\n debounceWaitMs?: number;\n\n /**\n * Callback for additional autocomplete customization\n * @param {HTMLInputElement | HTMLTextAreaElement} input - input box associated with autocomplete\n * @param {ClientRect | DOMRect} inputRect - size of the input box and its position relative to the viewport\n * @param {HTMLDivElement} container - container with suggestions\n * @param {number} maxHeight - max height that can be used by autocomplete\n */\n customize?: (input: HTMLInputElement | HTMLTextAreaElement, inputRect: ClientRect | DOMRect, container: HTMLDivElement, maxHeight: number) => void;\n\n /**\n * Prevents automatic form submit when ENTER is pressed\n */\n preventSubmit?: boolean;\n\n /**\n * Prevents the first item in the list from being selected automatically. This option allows you\n * to submit a custom text by pressing ENTER even when autocomplete is displayed.\n */\n disableAutoSelect?: boolean;\n\n /**\n * Provide your keyup event handler to display autocomplete when a key is pressed that doesn't modify the content. You can also perform some additional actions.\n */\n keyup?: (e: AutocompleteEvent) => void;\n\n /**\n * Allows to display autocomplete on mouse clicks or perform some additional actions.\n */\n click?: (e: AutocompleteEvent) => void;\n}\n\nexport interface AutocompleteResult {\n destroy: () => void;\n}\n\nexport default function autocomplete(settings: AutocompleteSettings): AutocompleteResult {\n\n // just an alias to minimize JS file size\n const doc = document;\n\n const container: HTMLDivElement = settings.container || doc.createElement(\"div\");\n container.id = container.id || \"autocomplete-\" + uid();\n const containerStyle = container.style;\n const debounceWaitMs = settings.debounceWaitMs || 0;\n const preventSubmit = settings.preventSubmit || false;\n const disableAutoSelect = settings.disableAutoSelect || false;\n\n let items: T[] = [];\n let inputValue = \"\";\n let minLen = 2;\n const showOnFocus = settings.showOnFocus;\n let selected: T | undefined;\n let keypressCounter = 0;\n let debounceTimer: number | undefined;\n\n if (settings.minLength !== undefined) {\n minLen = settings.minLength;\n }\n\n if (!settings.input) {\n throw new Error(\"input undefined\");\n }\n\n const input: HTMLInputElement | HTMLTextAreaElement = settings.input;\n\n container.className = \"autocomplete \" + (settings.className || \"\");\n container.setAttribute(\"role\", \"listbox\");\n\n input.setAttribute(\"role\", \"combobox\");\n input.setAttribute(\"aria-expanded\", \"false\");\n input.setAttribute(\"aria-autocomplete\", \"list\");\n input.setAttribute(\"aria-controls\", container.id);\n input.setAttribute(\"aria-owns\", container.id);\n input.setAttribute(\"aria-activedescendant\", \"\");\n input.setAttribute(\"aria-haspopup\", \"listbox\");\n\n // IOS implementation for fixed positioning has many bugs, so we will use absolute positioning\n containerStyle.position = \"absolute\";\n\n /**\n * Generate a unique ID\n */\n function uid(): string {\n return Date.now().toString(36) + Math.random().toString(36).substring(2);\n }\n\n /**\n * Detach the container from DOM\n */\n function detach(): void {\n const parent = container.parentNode;\n if (parent) {\n parent.removeChild(container);\n }\n }\n\n /**\n * Clear debouncing timer if assigned\n */\n function clearDebounceTimer(): void {\n if (debounceTimer) {\n window.clearTimeout(debounceTimer);\n }\n }\n\n /**\n * Attach the container to DOM\n */\n function attach(): void {\n if (!container.parentNode) {\n doc.body.appendChild(container);\n }\n }\n\n /**\n * Check if container for autocomplete is displayed\n */\n function containerDisplayed(): boolean {\n return !!container.parentNode;\n }\n\n /**\n * Clear autocomplete state and hide container\n */\n function clear(): void {\n // prevent the update call if there are pending AJAX requests\n keypressCounter++;\n\n items = [];\n inputValue = \"\";\n selected = undefined;\n input.setAttribute(\"aria-activedescendant\", \"\");\n input.setAttribute(\"aria-expanded\", \"false\");\n detach();\n }\n\n /**\n * Update autocomplete position\n */\n function updatePosition(): void {\n if (!containerDisplayed()) {\n return;\n }\n\n input.setAttribute(\"aria-expanded\", \"true\");\n\n containerStyle.height = \"auto\";\n containerStyle.width = input.offsetWidth + \"px\";\n\n let maxHeight = 0;\n let inputRect: ClientRect | DOMRect | undefined;\n\n function calc() {\n const docEl = doc.documentElement as HTMLElement;\n const clientTop = docEl.clientTop || doc.body.clientTop || 0;\n const clientLeft = docEl.clientLeft || doc.body.clientLeft || 0;\n const scrollTop = window.pageYOffset || docEl.scrollTop;\n const scrollLeft = window.pageXOffset || docEl.scrollLeft;\n\n inputRect = input.getBoundingClientRect();\n\n const top = inputRect.top + input.offsetHeight + scrollTop - clientTop;\n const left = inputRect.left + scrollLeft - clientLeft;\n\n containerStyle.top = top + \"px\";\n containerStyle.left = left + \"px\";\n\n maxHeight = window.innerHeight - (inputRect.top + input.offsetHeight);\n\n if (maxHeight < 0) {\n maxHeight = 0;\n }\n\n containerStyle.top = top + \"px\";\n containerStyle.bottom = \"\";\n containerStyle.left = left + \"px\";\n containerStyle.maxHeight = maxHeight + \"px\";\n }\n\n // the calc method must be called twice, otherwise the calculation may be wrong on resize event (chrome browser)\n calc();\n calc();\n\n if (settings.customize && inputRect) {\n settings.customize(input, inputRect, container, maxHeight);\n }\n }\n\n /**\n * Redraw the autocomplete div element with suggestions\n */\n function update(): void {\n\n // delete all children from autocomplete DOM container\n while (container.firstChild) {\n container.removeChild(container.firstChild);\n }\n\n input.setAttribute(\"aria-activedescendant\", \"\");\n\n // function for rendering autocomplete suggestions\n let render = function (item: T, _: string, __: number): HTMLDivElement | undefined {\n const itemElement = doc.createElement(\"div\");\n itemElement.textContent = item.label || \"\";\n return itemElement;\n };\n if (settings.render) {\n render = settings.render;\n }\n\n // function to render autocomplete groups\n let renderGroup = function (groupName: string, _: string): HTMLDivElement | undefined {\n const groupDiv = doc.createElement(\"div\");\n groupDiv.textContent = groupName;\n return groupDiv;\n };\n if (settings.renderGroup) {\n renderGroup = settings.renderGroup;\n }\n\n const fragment = doc.createDocumentFragment();\n let prevGroup = \"#9?$\";\n\n items.forEach(function (item: T, index: number): void {\n if (item.group && item.group !== prevGroup) {\n prevGroup = item.group;\n const groupDiv = renderGroup(item.group, inputValue);\n if (groupDiv) {\n groupDiv.className += \" group\";\n fragment.appendChild(groupDiv);\n }\n }\n const div = render(item, inputValue, index);\n if (div) {\n div.id = `${container.id}_${index}`;\n div.setAttribute(\"role\", \"option\");\n div.addEventListener(\"click\", function (ev: MouseEvent): void {\n settings.onSelect(item, input);\n clear();\n ev.preventDefault();\n ev.stopPropagation();\n });\n if (item === selected) {\n div.className += \" selected\";\n div.setAttribute(\"aria-selected\", \"true\");\n input.setAttribute(\"aria-activedescendant\", div.id);\n }\n fragment.appendChild(div);\n }\n });\n container.appendChild(fragment);\n if (items.length < 1) {\n if (settings.emptyMsg) {\n const empty = doc.createElement(\"div\");\n empty.id = `${container.id}_${uid()}`;\n empty.className = \"empty\";\n empty.textContent = settings.emptyMsg;\n container.appendChild(empty);\n input.setAttribute(\"aria-activedescendant\", empty.id);\n } else {\n clear();\n return;\n }\n }\n\n attach();\n updatePosition();\n\n updateScroll();\n }\n\n function updateIfDisplayed(): void {\n if (containerDisplayed()) {\n update();\n }\n }\n\n function resizeEventHandler(): void {\n updateIfDisplayed();\n }\n\n function scrollEventHandler(e: Event): void {\n if (e.target !== container) {\n updateIfDisplayed();\n } else {\n e.preventDefault();\n }\n }\n\n function inputEventHandler(): void {\n startFetch(EventTrigger.Keyboard);\n }\n\n /**\n * Automatically move scroll bar if selected item is not visible\n */\n function updateScroll(): void {\n const elements = container.getElementsByClassName(\"selected\");\n if (elements.length > 0) {\n let element = elements[0] as HTMLDivElement;\n\n // make group visible\n const previous = element.previousElementSibling as HTMLDivElement;\n if (previous && previous.className.indexOf(\"group\") !== -1 && !previous.previousElementSibling) {\n element = previous;\n }\n\n if (element.offsetTop < container.scrollTop) {\n container.scrollTop = element.offsetTop;\n } else {\n const selectBottom = element.offsetTop + element.offsetHeight;\n const containerBottom = container.scrollTop + container.offsetHeight;\n if (selectBottom > containerBottom) {\n container.scrollTop += selectBottom - containerBottom;\n }\n }\n }\n }\n\n /**\n * Select the previous item in suggestions\n */\n function selectPrev(): void {\n if (items.length < 1) {\n selected = undefined;\n } else {\n if (selected === items[0]) {\n selected = items[items.length - 1];\n } else {\n for (let i = items.length - 1; i > 0; i--) {\n if (selected === items[i] || i === 1) {\n selected = items[i - 1];\n break;\n }\n }\n }\n }\n }\n\n /**\n * Select the next item in suggestions\n */\n function selectNext(): void {\n if (items.length < 1) {\n selected = undefined;\n }\n if (!selected || selected === items[items.length - 1]) {\n selected = items[0];\n return;\n }\n for (let i = 0; i < (items.length - 1); i++) {\n if (selected === items[i]) {\n selected = items[i + 1];\n break;\n }\n }\n }\n\n function keydownEventHandler(ev: KeyboardEvent): void {\n const key = ev.key;\n\n if (key === \"ArrowUp\" || key === \"ArrowDown\" || key === \"Escape\") {\n const containerIsDisplayed = containerDisplayed();\n\n if (key === \"Escape\") {\n clear();\n } else {\n if (!containerIsDisplayed || items.length < 1) {\n return;\n }\n key === \"ArrowUp\"\n ? selectPrev()\n : selectNext();\n update();\n }\n\n ev.preventDefault();\n if (containerIsDisplayed) {\n ev.stopPropagation();\n }\n\n return;\n }\n\n if (key === 'Enter') {\n if (selected) {\n settings.onSelect(selected, input);\n clear();\n }\n\n if (preventSubmit) {\n ev.preventDefault();\n }\n }\n }\n\n function focusEventHandler(): void {\n if (showOnFocus) {\n startFetch(EventTrigger.Focus);\n }\n }\n\n function startFetch(trigger: EventTrigger) {\n // If multiple keys were pressed, before we get an update from server,\n // this may cause redrawing autocomplete multiple times after the last key was pressed.\n // To avoid this, the number of times keyboard was pressed will be saved and checked before redraw.\n const savedKeypressCounter = ++keypressCounter;\n\n const inputText = input.value;\n const cursorPos = input.selectionStart || 0;\n\n if (inputText.length >= minLen || trigger === EventTrigger.Focus) {\n clearDebounceTimer();\n debounceTimer = window.setTimeout(function (): void {\n settings.fetch(inputText, function (elements: T[] | false): void {\n if (keypressCounter === savedKeypressCounter && elements) {\n items = elements;\n inputValue = inputText;\n selected = (items.length < 1 || disableAutoSelect) ? undefined : items[0];\n update();\n }\n }, trigger, cursorPos);\n }, trigger === EventTrigger.Keyboard || trigger === EventTrigger.Mouse ? debounceWaitMs : 0);\n } else {\n clear();\n }\n }\n\n function keyupEventHandler(e: KeyboardEvent) {\n if (settings.keyup) {\n settings.keyup({\n event: e,\n fetch: () => startFetch(EventTrigger.Keyboard)\n });\n return;\n }\n\n if (!containerDisplayed() && e.key === \"ArrowDown\") {\n startFetch(EventTrigger.Keyboard);\n }\n }\n\n function clickEventHandler(e: MouseEvent) {\n settings.click && settings.click({\n event: e,\n fetch: () => startFetch(EventTrigger.Mouse)\n });\n }\n\n function blurEventHandler() {\n // we need to delay clear, because when we click on an item, blur will be called before click and remove items from DOM\n setTimeout(() => {\n if (doc.activeElement !== input) {\n clear();\n }\n }, 200);\n }\n\n /**\n * Fixes #26: on long clicks focus will be lost and onSelect method will not be called\n */\n container.addEventListener(\"mousedown\", function (evt: Event) {\n evt.stopPropagation();\n evt.preventDefault();\n });\n\n /**\n * Fixes #30: autocomplete closes when scrollbar is clicked in IE\n * See: https://stackoverflow.com/a/9210267/13172349\n */\n container.addEventListener(\"focus\", () => input.focus());\n\n /**\n * This function will remove DOM elements and clear event handlers\n */\n function destroy(): void {\n input.removeEventListener(\"focus\", focusEventHandler);\n input.removeEventListener(\"keyup\", keyupEventHandler as EventListenerOrEventListenerObject)\n input.removeEventListener(\"click\", clickEventHandler as EventListenerOrEventListenerObject)\n input.removeEventListener(\"keydown\", keydownEventHandler as EventListenerOrEventListenerObject);\n input.removeEventListener(\"input\", inputEventHandler as EventListenerOrEventListenerObject);\n input.removeEventListener(\"blur\", blurEventHandler);\n window.removeEventListener(\"resize\", resizeEventHandler);\n doc.removeEventListener(\"scroll\", scrollEventHandler, true);\n input.removeAttribute(\"role\");\n input.removeAttribute(\"aria-expanded\");\n input.removeAttribute(\"aria-autocomplete\");\n input.removeAttribute(\"aria-controls\");\n input.removeAttribute(\"aria-activedescendant\");\n input.removeAttribute(\"aria-owns\");\n input.removeAttribute(\"aria-haspopup\");\n clearDebounceTimer();\n clear();\n }\n\n // setup event handlers\n input.addEventListener(\"keyup\", keyupEventHandler as EventListenerOrEventListenerObject);\n input.addEventListener(\"click\", clickEventHandler as EventListenerOrEventListenerObject);\n input.addEventListener(\"keydown\", keydownEventHandler as EventListenerOrEventListenerObject);\n input.addEventListener(\"input\", inputEventHandler as EventListenerOrEventListenerObject);\n input.addEventListener(\"blur\", blurEventHandler);\n input.addEventListener(\"focus\", focusEventHandler);\n window.addEventListener(\"resize\", resizeEventHandler);\n doc.addEventListener(\"scroll\", scrollEventHandler, true);\n\n return {\n destroy\n };\n}\n"],"names":["settings","doc","document","container","createElement","id","uid","selected","debounceTimer","containerStyle","style","debounceWaitMs","preventSubmit","disableAutoSelect","items","inputValue","minLen","showOnFocus","keypressCounter","undefined","minLength","input","Error","Date","now","toString","Math","random","substring","clearDebounceTimer","window","clearTimeout","containerDisplayed","parentNode","clear","parent","setAttribute","removeChild","update","firstChild","render","item","_","__","itemElement","textContent","label","renderGroup","groupName","groupDiv","fragment","createDocumentFragment","prevGroup","forEach","index","group","className","appendChild","div","addEventListener","ev","onSelect","preventDefault","stopPropagation","length","emptyMsg","empty","body","height","width","offsetWidth","inputRect","maxHeight","calc","customize","docEl","documentElement","clientTop","clientLeft","scrollTop","pageYOffset","scrollLeft","pageXOffset","top","getBoundingClientRect","offsetHeight","left","innerHeight","bottom","updatePosition","elements","getElementsByClassName","element","previous","previousElementSibling","indexOf","offsetTop","selectBottom","containerBottom","updateScroll","updateIfDisplayed","resizeEventHandler","scrollEventHandler","e","target","inputEventHandler","startFetch","keydownEventHandler","key","containerIsDisplayed","i","selectPrev","selectNext","focusEventHandler","trigger","savedKeypressCounter","inputText","value","cursorPos","selectionStart","setTimeout","fetch","keyupEventHandler","keyup","event","clickEventHandler","click","blurEventHandler","activeElement","position","evt","focus","destroy","removeEventListener","removeAttribute"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"autocomplete.min.js","sources":["autocomplete.ts"],"sourcesContent":["/*\n * https://github.com/kraaden/autocomplete\n * Copyright (c) 2016 Denys Krasnoshchok\n * MIT License\n */\n\nexport const enum EventTrigger {\n Keyboard = 0,\n Focus = 1,\n Mouse = 2\n}\n\nexport interface AutocompleteItem {\n label?: string;\n group?: string;\n}\n\nexport interface AutocompleteEvent {\n /**\n * Native event object passed by browser to the event handler\n */\n event: T;\n /**\n * Fetch data and display autocomplete\n */\n fetch: () => void;\n}\n\nexport interface AutocompleteSettings {\n /**\n * Autocomplete will be attached to this element.\n */\n input: HTMLInputElement | HTMLTextAreaElement;\n\n /**\n * Provide your own container for the widget.\n * If not specified, a new DIV element will be created.\n */\n container?: HTMLDivElement;\n\n /**\n * This method allows you to override the default rendering function for items.\n * It must return a DIV element or undefined to skip rendering.\n */\n render?: (item: T, currentValue: string, index: number) => HTMLDivElement | undefined;\n\n /**\n * This method allows you to override the default rendering function for item groups.\n * It must return a DIV element or undefined to skip rendering.\n */\n renderGroup?: (name: string, currentValue: string) => HTMLDivElement | undefined;\n\n /**\n * If specified, the autocomplete DOM element will have this class assigned to it.\n */\n className?: string;\n\n /**\n * Specify the minimum text length required to show autocomplete.\n */\n minLength?: number;\n\n /**\n * The message that will be showed when there are no suggestions that match the entered value.\n */\n emptyMsg?: string;\n\n /**\n * This method will be called when user choose an item in autocomplete. The selected item will be passed as the first parameter.\n */\n onSelect: (item: T, input: HTMLInputElement | HTMLTextAreaElement) => void;\n\n /**\n * Show autocomplete on focus event. Focus event will ignore the `minLength` property and will always call `fetch`.\n */\n showOnFocus?: boolean;\n\n /**\n * This method will be called to prepare suggestions and then pass them to autocomplete.\n * @param {string} text - text in the input field\n * @param {(items: T[] | false) => void} update - a callback function that must be called after suggestions are prepared\n * @param {EventTrigger} trigger - type of the event that triggered the fetch\n * @param {number} cursorPos - position of the cursor in the input field\n */\n fetch: (text: string, update: (items: T[] | false) => void, trigger: EventTrigger, cursorPos: number) => void;\n\n /**\n * Enforces that the fetch function will only be called once within the specified time frame (in milliseconds) and\n * delays execution. This prevents flooding your server with AJAX requests.\n */\n debounceWaitMs?: number;\n\n /**\n * Callback for additional autocomplete customization\n * @param {HTMLInputElement | HTMLTextAreaElement} input - input box associated with autocomplete\n * @param {ClientRect | DOMRect} inputRect - size of the input box and its position relative to the viewport\n * @param {HTMLDivElement} container - container with suggestions\n * @param {number} maxHeight - max height that can be used by autocomplete\n */\n customize?: (input: HTMLInputElement | HTMLTextAreaElement, inputRect: ClientRect | DOMRect, container: HTMLDivElement, maxHeight: number) => void;\n\n /**\n * Prevents automatic form submit when ENTER is pressed\n */\n preventSubmit?: boolean;\n\n /**\n * Prevents the first item in the list from being selected automatically. This option allows you\n * to submit a custom text by pressing ENTER even when autocomplete is displayed.\n */\n disableAutoSelect?: boolean;\n\n /**\n * Provide your keyup event handler to display autocomplete when a key is pressed that doesn't modify the content. You can also perform some additional actions.\n */\n keyup?: (e: AutocompleteEvent) => void;\n\n /**\n * Allows to display autocomplete on mouse clicks or perform some additional actions.\n */\n click?: (e: AutocompleteEvent) => void;\n}\n\nexport interface AutocompleteResult {\n /**\n * Remove event handlers, DOM elements and ARIA/accessibility attributes created by the widget.\n */\n destroy: () => void;\n}\n\nexport default function autocomplete(settings: AutocompleteSettings): AutocompleteResult {\n\n // just an alias to minimize JS file size\n const doc = document;\n\n const container: HTMLDivElement = settings.container || doc.createElement(\"div\");\n container.id = container.id || \"autocomplete-\" + uid();\n const containerStyle = container.style;\n const debounceWaitMs = settings.debounceWaitMs || 0;\n const preventSubmit = settings.preventSubmit || false;\n const disableAutoSelect = settings.disableAutoSelect || false;\n\n let items: T[] = [];\n let inputValue = \"\";\n let minLen = 2;\n const showOnFocus = settings.showOnFocus;\n let selected: T | undefined;\n let keypressCounter = 0;\n let debounceTimer: number | undefined;\n\n if (settings.minLength !== undefined) {\n minLen = settings.minLength;\n }\n\n if (!settings.input) {\n throw new Error(\"input undefined\");\n }\n\n const input: HTMLInputElement | HTMLTextAreaElement = settings.input;\n\n container.className = \"autocomplete \" + (settings.className || \"\");\n container.setAttribute(\"role\", \"listbox\");\n\n input.setAttribute(\"role\", \"combobox\");\n input.setAttribute(\"aria-expanded\", \"false\");\n input.setAttribute(\"aria-autocomplete\", \"list\");\n input.setAttribute(\"aria-controls\", container.id);\n input.setAttribute(\"aria-owns\", container.id);\n input.setAttribute(\"aria-activedescendant\", \"\");\n input.setAttribute(\"aria-haspopup\", \"listbox\");\n\n // IOS implementation for fixed positioning has many bugs, so we will use absolute positioning\n containerStyle.position = \"absolute\";\n\n /**\n * Generate a unique ID\n */\n function uid(): string {\n return Date.now().toString(36) + Math.random().toString(36).substring(2);\n }\n\n /**\n * Detach the container from DOM\n */\n function detach(): void {\n const parent = container.parentNode;\n if (parent) {\n parent.removeChild(container);\n }\n }\n\n /**\n * Clear debouncing timer if assigned\n */\n function clearDebounceTimer(): void {\n if (debounceTimer) {\n window.clearTimeout(debounceTimer);\n }\n }\n\n /**\n * Attach the container to DOM\n */\n function attach(): void {\n if (!container.parentNode) {\n doc.body.appendChild(container);\n }\n }\n\n /**\n * Check if container for autocomplete is displayed\n */\n function containerDisplayed(): boolean {\n return !!container.parentNode;\n }\n\n /**\n * Clear autocomplete state and hide container\n */\n function clear(): void {\n // prevent the update call if there are pending AJAX requests\n keypressCounter++;\n\n items = [];\n inputValue = \"\";\n selected = undefined;\n input.setAttribute(\"aria-activedescendant\", \"\");\n input.setAttribute(\"aria-expanded\", \"false\");\n detach();\n }\n\n /**\n * Update autocomplete position\n */\n function updatePosition(): void {\n if (!containerDisplayed()) {\n return;\n }\n\n input.setAttribute(\"aria-expanded\", \"true\");\n\n containerStyle.height = \"auto\";\n containerStyle.width = input.offsetWidth + \"px\";\n\n let maxHeight = 0;\n let inputRect: ClientRect | DOMRect | undefined;\n\n function calc() {\n const docEl = doc.documentElement as HTMLElement;\n const clientTop = docEl.clientTop || doc.body.clientTop || 0;\n const clientLeft = docEl.clientLeft || doc.body.clientLeft || 0;\n const scrollTop = window.pageYOffset || docEl.scrollTop;\n const scrollLeft = window.pageXOffset || docEl.scrollLeft;\n\n inputRect = input.getBoundingClientRect();\n\n const top = inputRect.top + input.offsetHeight + scrollTop - clientTop;\n const left = inputRect.left + scrollLeft - clientLeft;\n\n containerStyle.top = top + \"px\";\n containerStyle.left = left + \"px\";\n\n maxHeight = window.innerHeight - (inputRect.top + input.offsetHeight);\n\n if (maxHeight < 0) {\n maxHeight = 0;\n }\n\n containerStyle.top = top + \"px\";\n containerStyle.bottom = \"\";\n containerStyle.left = left + \"px\";\n containerStyle.maxHeight = maxHeight + \"px\";\n }\n\n // the calc method must be called twice, otherwise the calculation may be wrong on resize event (chrome browser)\n calc();\n calc();\n\n if (settings.customize && inputRect) {\n settings.customize(input, inputRect, container, maxHeight);\n }\n }\n\n /**\n * Redraw the autocomplete div element with suggestions\n */\n function update(): void {\n\n // delete all children from autocomplete DOM container\n while (container.firstChild) {\n container.removeChild(container.firstChild);\n }\n\n input.setAttribute(\"aria-activedescendant\", \"\");\n\n // function for rendering autocomplete suggestions\n let render = function (item: T, _: string, __: number): HTMLDivElement | undefined {\n const itemElement = doc.createElement(\"div\");\n itemElement.textContent = item.label || \"\";\n return itemElement;\n };\n if (settings.render) {\n render = settings.render;\n }\n\n // function to render autocomplete groups\n let renderGroup = function (groupName: string, _: string): HTMLDivElement | undefined {\n const groupDiv = doc.createElement(\"div\");\n groupDiv.textContent = groupName;\n return groupDiv;\n };\n if (settings.renderGroup) {\n renderGroup = settings.renderGroup;\n }\n\n const fragment = doc.createDocumentFragment();\n let prevGroup = uid();\n\n items.forEach(function (item: T, index: number): void {\n if (item.group && item.group !== prevGroup) {\n prevGroup = item.group;\n const groupDiv = renderGroup(item.group, inputValue);\n if (groupDiv) {\n groupDiv.className += \" group\";\n fragment.appendChild(groupDiv);\n }\n }\n const div = render(item, inputValue, index);\n if (div) {\n div.id = `${container.id}_${index}`;\n div.setAttribute(\"role\", \"option\");\n div.addEventListener(\"click\", function (ev: MouseEvent): void {\n settings.onSelect(item, input);\n clear();\n ev.preventDefault();\n ev.stopPropagation();\n });\n if (item === selected) {\n div.className += \" selected\";\n div.setAttribute(\"aria-selected\", \"true\");\n input.setAttribute(\"aria-activedescendant\", div.id);\n }\n fragment.appendChild(div);\n }\n });\n container.appendChild(fragment);\n if (items.length < 1) {\n if (settings.emptyMsg) {\n const empty = doc.createElement(\"div\");\n empty.id = `${container.id}_${uid()}`;\n empty.className = \"empty\";\n empty.textContent = settings.emptyMsg;\n container.appendChild(empty);\n input.setAttribute(\"aria-activedescendant\", empty.id);\n } else {\n clear();\n return;\n }\n }\n\n attach();\n updatePosition();\n\n updateScroll();\n }\n\n function updateIfDisplayed(): void {\n if (containerDisplayed()) {\n update();\n }\n }\n\n function resizeEventHandler(): void {\n updateIfDisplayed();\n }\n\n function scrollEventHandler(e: Event): void {\n if (e.target !== container) {\n updateIfDisplayed();\n } else {\n e.preventDefault();\n }\n }\n\n function inputEventHandler(): void {\n startFetch(EventTrigger.Keyboard);\n }\n\n /**\n * Automatically move scroll bar if selected item is not visible\n */\n function updateScroll(): void {\n const elements = container.getElementsByClassName(\"selected\");\n if (elements.length > 0) {\n let element = elements[0] as HTMLDivElement;\n\n // make group visible\n const previous = element.previousElementSibling as HTMLDivElement;\n if (previous && previous.className.indexOf(\"group\") !== -1 && !previous.previousElementSibling) {\n element = previous;\n }\n\n if (element.offsetTop < container.scrollTop) {\n container.scrollTop = element.offsetTop;\n } else {\n const selectBottom = element.offsetTop + element.offsetHeight;\n const containerBottom = container.scrollTop + container.offsetHeight;\n if (selectBottom > containerBottom) {\n container.scrollTop += selectBottom - containerBottom;\n }\n }\n }\n }\n\n /**\n * Select the previous item in suggestions\n */\n function selectPrev(): void {\n if (items.length < 1) {\n selected = undefined;\n } else {\n if (selected === items[0]) {\n selected = items[items.length - 1];\n } else {\n for (let i = items.length - 1; i > 0; i--) {\n if (selected === items[i] || i === 1) {\n selected = items[i - 1];\n break;\n }\n }\n }\n }\n }\n\n /**\n * Select the next item in suggestions\n */\n function selectNext(): void {\n if (items.length < 1) {\n selected = undefined;\n }\n if (!selected || selected === items[items.length - 1]) {\n selected = items[0];\n return;\n }\n for (let i = 0; i < (items.length - 1); i++) {\n if (selected === items[i]) {\n selected = items[i + 1];\n break;\n }\n }\n }\n\n function keydownEventHandler(ev: KeyboardEvent): void {\n const key = ev.key;\n\n if (key === \"ArrowUp\" || key === \"ArrowDown\" || key === \"Escape\") {\n const containerIsDisplayed = containerDisplayed();\n\n if (key === \"Escape\") {\n clear();\n } else {\n if (!containerIsDisplayed || items.length < 1) {\n return;\n }\n key === \"ArrowUp\"\n ? selectPrev()\n : selectNext();\n update();\n }\n\n ev.preventDefault();\n if (containerIsDisplayed) {\n ev.stopPropagation();\n }\n\n return;\n }\n\n if (key === 'Enter') {\n if (selected) {\n settings.onSelect(selected, input);\n clear();\n }\n\n if (preventSubmit) {\n ev.preventDefault();\n }\n }\n }\n\n function focusEventHandler(): void {\n if (showOnFocus) {\n startFetch(EventTrigger.Focus);\n }\n }\n\n function startFetch(trigger: EventTrigger) {\n // If multiple keys were pressed, before we get an update from server,\n // this may cause redrawing autocomplete multiple times after the last key was pressed.\n // To avoid this, the number of times keyboard was pressed will be saved and checked before redraw.\n const savedKeypressCounter = ++keypressCounter;\n\n const inputText = input.value;\n const cursorPos = input.selectionStart || 0;\n\n if (inputText.length >= minLen || trigger === EventTrigger.Focus) {\n clearDebounceTimer();\n debounceTimer = window.setTimeout(function (): void {\n settings.fetch(inputText, function (elements: T[] | false): void {\n if (keypressCounter === savedKeypressCounter && elements) {\n items = elements;\n inputValue = inputText;\n selected = (items.length < 1 || disableAutoSelect) ? undefined : items[0];\n update();\n }\n }, trigger, cursorPos);\n }, trigger === EventTrigger.Keyboard || trigger === EventTrigger.Mouse ? debounceWaitMs : 0);\n } else {\n clear();\n }\n }\n\n function keyupEventHandler(e: KeyboardEvent) {\n if (settings.keyup) {\n settings.keyup({\n event: e,\n fetch: () => startFetch(EventTrigger.Keyboard)\n });\n return;\n }\n\n if (!containerDisplayed() && e.key === \"ArrowDown\") {\n startFetch(EventTrigger.Keyboard);\n }\n }\n\n function clickEventHandler(e: MouseEvent) {\n settings.click && settings.click({\n event: e,\n fetch: () => startFetch(EventTrigger.Mouse)\n });\n }\n\n function blurEventHandler() {\n // we need to delay clear, because when we click on an item, blur will be called before click and remove items from DOM\n setTimeout(() => {\n if (doc.activeElement !== input) {\n clear();\n }\n }, 200);\n }\n\n /**\n * Fixes #26: on long clicks focus will be lost and onSelect method will not be called\n */\n container.addEventListener(\"mousedown\", function (evt: Event) {\n evt.stopPropagation();\n evt.preventDefault();\n });\n\n /**\n * Fixes #30: autocomplete closes when scrollbar is clicked in IE\n * See: https://stackoverflow.com/a/9210267/13172349\n */\n container.addEventListener(\"focus\", () => input.focus());\n\n /**\n * This function will remove DOM elements and clear event handlers\n */\n function destroy(): void {\n input.removeEventListener(\"focus\", focusEventHandler);\n input.removeEventListener(\"keyup\", keyupEventHandler as EventListenerOrEventListenerObject)\n input.removeEventListener(\"click\", clickEventHandler as EventListenerOrEventListenerObject)\n input.removeEventListener(\"keydown\", keydownEventHandler as EventListenerOrEventListenerObject);\n input.removeEventListener(\"input\", inputEventHandler as EventListenerOrEventListenerObject);\n input.removeEventListener(\"blur\", blurEventHandler);\n window.removeEventListener(\"resize\", resizeEventHandler);\n doc.removeEventListener(\"scroll\", scrollEventHandler, true);\n input.removeAttribute(\"role\");\n input.removeAttribute(\"aria-expanded\");\n input.removeAttribute(\"aria-autocomplete\");\n input.removeAttribute(\"aria-controls\");\n input.removeAttribute(\"aria-activedescendant\");\n input.removeAttribute(\"aria-owns\");\n input.removeAttribute(\"aria-haspopup\");\n clearDebounceTimer();\n clear();\n }\n\n // setup event handlers\n input.addEventListener(\"keyup\", keyupEventHandler as EventListenerOrEventListenerObject);\n input.addEventListener(\"click\", clickEventHandler as EventListenerOrEventListenerObject);\n input.addEventListener(\"keydown\", keydownEventHandler as EventListenerOrEventListenerObject);\n input.addEventListener(\"input\", inputEventHandler as EventListenerOrEventListenerObject);\n input.addEventListener(\"blur\", blurEventHandler);\n input.addEventListener(\"focus\", focusEventHandler);\n window.addEventListener(\"resize\", resizeEventHandler);\n doc.addEventListener(\"scroll\", scrollEventHandler, true);\n\n return {\n destroy\n };\n}\n"],"names":["settings","doc","document","container","createElement","id","uid","selected","debounceTimer","containerStyle","style","debounceWaitMs","preventSubmit","disableAutoSelect","items","inputValue","minLen","showOnFocus","keypressCounter","undefined","minLength","input","Error","Date","now","toString","Math","random","substring","clearDebounceTimer","window","clearTimeout","containerDisplayed","parentNode","clear","parent","setAttribute","removeChild","update","firstChild","render","item","_","__","itemElement","textContent","label","renderGroup","groupName","groupDiv","fragment","createDocumentFragment","prevGroup","forEach","index","group","className","appendChild","div","addEventListener","ev","onSelect","preventDefault","stopPropagation","length","emptyMsg","empty","body","height","width","offsetWidth","inputRect","maxHeight","calc","customize","docEl","documentElement","clientTop","clientLeft","scrollTop","pageYOffset","scrollLeft","pageXOffset","top","getBoundingClientRect","offsetHeight","left","innerHeight","bottom","updatePosition","elements","getElementsByClassName","element","previous","previousElementSibling","indexOf","offsetTop","selectBottom","containerBottom","updateScroll","updateIfDisplayed","resizeEventHandler","scrollEventHandler","e","target","inputEventHandler","startFetch","keydownEventHandler","key","containerIsDisplayed","i","selectPrev","selectNext","focusEventHandler","trigger","savedKeypressCounter","inputText","value","cursorPos","selectionStart","setTimeout","fetch","keyupEventHandler","keyup","event","clickEventHandler","click","blurEventHandler","activeElement","position","evt","focus","destroy","removeEventListener","removeAttribute"],"mappings":"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"} \ No newline at end of file diff --git a/autocomplete.ts b/autocomplete.ts index d6f7218..0721262 100644 --- a/autocomplete.ts +++ b/autocomplete.ts @@ -16,7 +16,13 @@ export interface AutocompleteItem { } export interface AutocompleteEvent { + /** + * Native event object passed by browser to the event handler + */ event: T; + /** + * Fetch data and display autocomplete + */ fetch: () => void; } @@ -116,6 +122,9 @@ export interface AutocompleteSettings { } export interface AutocompleteResult { + /** + * Remove event handlers, DOM elements and ARIA/accessibility attributes created by the widget. + */ destroy: () => void; } @@ -305,7 +314,7 @@ export default function autocomplete(settings: Autoc } const fragment = doc.createDocumentFragment(); - let prevGroup = "#9?$"; + let prevGroup = uid(); items.forEach(function (item: T, index: number): void { if (item.group && item.group !== prevGroup) {