├── docs ├── .nojekyll ├── favicon.ico ├── img │ ├── autoComplete.js.png │ ├── icons │ │ ├── apple-icon.png │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── favicon-96x96.png │ │ ├── ms-icon-70x70.png │ │ ├── ms-icon-144x144.png │ │ ├── ms-icon-150x150.png │ │ ├── ms-icon-310x310.png │ │ ├── android-icon-36x36.png │ │ ├── android-icon-48x48.png │ │ ├── android-icon-72x72.png │ │ ├── android-icon-96x96.png │ │ ├── apple-icon-114x114.png │ │ ├── apple-icon-120x120.png │ │ ├── apple-icon-144x144.png │ │ ├── apple-icon-152x152.png │ │ ├── apple-icon-180x180.png │ │ ├── apple-icon-57x57.png │ │ ├── apple-icon-60x60.png │ │ ├── apple-icon-72x72.png │ │ ├── apple-icon-76x76.png │ │ ├── android-icon-144x144.png │ │ ├── android-icon-192x192.png │ │ └── apple-icon-precomposed.png │ ├── logos │ │ ├── cdnjs_logo.png │ │ ├── html_logo.png │ │ ├── unpkg_logo.png │ │ ├── yarn_logo.png │ │ ├── codepen_logo.png │ │ ├── jsdelivr_logo.png │ │ ├── nodejs_logo.png │ │ └── javascript_logo.png │ ├── autoComplete.init.png │ ├── autoComplete.js_Preview.png │ ├── autoComplete.js_WidePreview.png │ └── autoComplete.js.svg ├── browserconfig.xml ├── _coverpage.md ├── manifest.json ├── demo │ ├── css │ │ └── main.css │ ├── js │ │ └── index.js │ └── index.html ├── index.html └── releases.md ├── dist ├── js │ ├── autoComplete.js.gz │ ├── autoComplete.min.js.gz │ ├── index.js │ ├── autoComplete.min.js │ └── autoComplete.js ├── css │ ├── images │ │ ├── magnifier.svg │ │ └── search.svg │ ├── autoComplete.02.css │ ├── autoComplete.01.css │ ├── main.css │ └── autoComplete.css ├── index.html └── db │ └── test.json ├── .github ├── FUNDING.yml └── ISSUE_TEMPLATE │ ├── feature-request.md │ └── bug-report.md ├── .editorconfig ├── .vscode └── settings.json ├── src ├── utils │ ├── debouncer.js │ └── eventEmitter.js ├── components │ ├── Input.js │ ├── Item.js │ └── List.js ├── services │ └── search.js ├── controllers │ ├── listController.js │ ├── dataController.js │ └── navigationController.js └── autoComplete.js ├── .gitignore ├── package.json ├── rollup.config.js ├── README.md └── LICENSE /docs/.nojekyll: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wikia/autoComplete.js/master/docs/favicon.ico -------------------------------------------------------------------------------- /dist/js/autoComplete.js.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wikia/autoComplete.js/master/dist/js/autoComplete.js.gz -------------------------------------------------------------------------------- /docs/img/autoComplete.js.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wikia/autoComplete.js/master/docs/img/autoComplete.js.png -------------------------------------------------------------------------------- /docs/img/icons/apple-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wikia/autoComplete.js/master/docs/img/icons/apple-icon.png -------------------------------------------------------------------------------- /docs/img/logos/cdnjs_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wikia/autoComplete.js/master/docs/img/logos/cdnjs_logo.png -------------------------------------------------------------------------------- /docs/img/logos/html_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wikia/autoComplete.js/master/docs/img/logos/html_logo.png -------------------------------------------------------------------------------- /docs/img/logos/unpkg_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wikia/autoComplete.js/master/docs/img/logos/unpkg_logo.png -------------------------------------------------------------------------------- /docs/img/logos/yarn_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wikia/autoComplete.js/master/docs/img/logos/yarn_logo.png -------------------------------------------------------------------------------- /dist/js/autoComplete.min.js.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wikia/autoComplete.js/master/dist/js/autoComplete.min.js.gz -------------------------------------------------------------------------------- /docs/img/autoComplete.init.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wikia/autoComplete.js/master/docs/img/autoComplete.init.png -------------------------------------------------------------------------------- /docs/img/icons/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wikia/autoComplete.js/master/docs/img/icons/favicon-16x16.png -------------------------------------------------------------------------------- /docs/img/icons/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wikia/autoComplete.js/master/docs/img/icons/favicon-32x32.png -------------------------------------------------------------------------------- /docs/img/icons/favicon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wikia/autoComplete.js/master/docs/img/icons/favicon-96x96.png -------------------------------------------------------------------------------- /docs/img/icons/ms-icon-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wikia/autoComplete.js/master/docs/img/icons/ms-icon-70x70.png -------------------------------------------------------------------------------- /docs/img/logos/codepen_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wikia/autoComplete.js/master/docs/img/logos/codepen_logo.png -------------------------------------------------------------------------------- /docs/img/logos/jsdelivr_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wikia/autoComplete.js/master/docs/img/logos/jsdelivr_logo.png -------------------------------------------------------------------------------- /docs/img/logos/nodejs_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wikia/autoComplete.js/master/docs/img/logos/nodejs_logo.png -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | liberapay: TarekRaafat 4 | patreon: TarekRaafat 5 | -------------------------------------------------------------------------------- /docs/img/icons/ms-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wikia/autoComplete.js/master/docs/img/icons/ms-icon-144x144.png -------------------------------------------------------------------------------- /docs/img/icons/ms-icon-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wikia/autoComplete.js/master/docs/img/icons/ms-icon-150x150.png -------------------------------------------------------------------------------- /docs/img/icons/ms-icon-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wikia/autoComplete.js/master/docs/img/icons/ms-icon-310x310.png -------------------------------------------------------------------------------- /docs/img/logos/javascript_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wikia/autoComplete.js/master/docs/img/logos/javascript_logo.png -------------------------------------------------------------------------------- /docs/img/autoComplete.js_Preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wikia/autoComplete.js/master/docs/img/autoComplete.js_Preview.png -------------------------------------------------------------------------------- /docs/img/icons/android-icon-36x36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wikia/autoComplete.js/master/docs/img/icons/android-icon-36x36.png -------------------------------------------------------------------------------- /docs/img/icons/android-icon-48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wikia/autoComplete.js/master/docs/img/icons/android-icon-48x48.png -------------------------------------------------------------------------------- /docs/img/icons/android-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wikia/autoComplete.js/master/docs/img/icons/android-icon-72x72.png -------------------------------------------------------------------------------- /docs/img/icons/android-icon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wikia/autoComplete.js/master/docs/img/icons/android-icon-96x96.png -------------------------------------------------------------------------------- /docs/img/icons/apple-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wikia/autoComplete.js/master/docs/img/icons/apple-icon-114x114.png -------------------------------------------------------------------------------- /docs/img/icons/apple-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wikia/autoComplete.js/master/docs/img/icons/apple-icon-120x120.png -------------------------------------------------------------------------------- /docs/img/icons/apple-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wikia/autoComplete.js/master/docs/img/icons/apple-icon-144x144.png -------------------------------------------------------------------------------- /docs/img/icons/apple-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wikia/autoComplete.js/master/docs/img/icons/apple-icon-152x152.png -------------------------------------------------------------------------------- /docs/img/icons/apple-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wikia/autoComplete.js/master/docs/img/icons/apple-icon-180x180.png -------------------------------------------------------------------------------- /docs/img/icons/apple-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wikia/autoComplete.js/master/docs/img/icons/apple-icon-57x57.png -------------------------------------------------------------------------------- /docs/img/icons/apple-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wikia/autoComplete.js/master/docs/img/icons/apple-icon-60x60.png -------------------------------------------------------------------------------- /docs/img/icons/apple-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wikia/autoComplete.js/master/docs/img/icons/apple-icon-72x72.png -------------------------------------------------------------------------------- /docs/img/icons/apple-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wikia/autoComplete.js/master/docs/img/icons/apple-icon-76x76.png -------------------------------------------------------------------------------- /docs/img/icons/android-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wikia/autoComplete.js/master/docs/img/icons/android-icon-144x144.png -------------------------------------------------------------------------------- /docs/img/icons/android-icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wikia/autoComplete.js/master/docs/img/icons/android-icon-192x192.png -------------------------------------------------------------------------------- /docs/img/autoComplete.js_WidePreview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wikia/autoComplete.js/master/docs/img/autoComplete.js_WidePreview.png -------------------------------------------------------------------------------- /docs/img/icons/apple-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wikia/autoComplete.js/master/docs/img/icons/apple-icon-precomposed.png -------------------------------------------------------------------------------- /docs/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | #ffffff 3 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # top-most EditorConfig file 2 | root = true 3 | 4 | # Unix-style newlines with a newline ending every file 5 | [*] 6 | charset = utf-8 7 | tab_width = 2 8 | indent_size = 2 9 | indent_style = space 10 | insert_final_newline = true 11 | 12 | # Indentation override for all JS under lib directory 13 | [*.js] 14 | max_line_length = 120 15 | trim_trailing_whitespace = true 16 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cSpell.words": [ 3 | "Raafat", 4 | "Tarek", 5 | "activedescendant", 6 | "afterend", 7 | "autocapitalize", 8 | "autocorrect", 9 | "combobox", 10 | "debouncer", 11 | "haspopup", 12 | "labelledby", 13 | "listbox", 14 | "nodent", 15 | "sourcemap", 16 | "tabindex", 17 | "tarekraafat", 18 | "textbox", 19 | "toplevel", 20 | "uninit" 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /src/utils/debouncer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Debouncer 3 | * 4 | * @param {Function} callback - The callback function 5 | * @param {Number} delay - The delay number value 6 | * 7 | * @returns {Function} - The callback function wrapped in `setTimeout` function 8 | */ 9 | export default (callback, delay) => { 10 | let inDebounce; 11 | return function () { 12 | const context = this; 13 | const args = arguments; 14 | clearTimeout(inDebounce); 15 | inDebounce = setTimeout(() => callback.apply(context, args), delay); 16 | }; 17 | }; 18 | -------------------------------------------------------------------------------- /src/utils/eventEmitter.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Event emitter/dispatcher `Function` 3 | * 4 | * @param {Element} target - The target element to listen for 5 | * @param {Object} detail - The detail object containing relevant information or null 6 | * @param {String} name - The name of the event fired 7 | * 8 | */ 9 | export default (target, detail, name) => { 10 | // Dispatch event on input 11 | target.dispatchEvent( 12 | new CustomEvent(name, { 13 | bubbles: true, 14 | detail, 15 | cancelable: true, 16 | }) 17 | ); 18 | }; 19 | -------------------------------------------------------------------------------- /src/components/Input.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Input field element 3 | * 4 | * @param {Object} config - autoComplete configurations 5 | * 6 | */ 7 | export default (config) => { 8 | // General attributes 9 | // ARIA attributes 10 | config.inputField.setAttribute("role", "combobox"); 11 | config.inputField.setAttribute("aria-haspopup", true); 12 | config.inputField.setAttribute("aria-expanded", false); 13 | config.inputField.setAttribute("aria-controls", config.resultsList.idName); 14 | config.inputField.setAttribute("aria-autocomplete", "both"); 15 | }; 16 | -------------------------------------------------------------------------------- /dist/css/images/magnifier.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/components/Item.js: -------------------------------------------------------------------------------- 1 | /** 2 | * List Item element creator 3 | * 4 | * @param {String} itemValue - The item string value 5 | * @param {Number} resultIndex - The result item index 6 | * @param {Object} config - autoComplete configurations 7 | * 8 | * @returns {Element} - The created result item element 9 | */ 10 | export default (item, index, config) => { 11 | // Create a DIV element for each matching result item 12 | const result = document.createElement(config.resultItem.element); 13 | result.setAttribute("id", `${config.resultItem.idName}_${index}`); 14 | result.setAttribute("class", config.resultItem.className); 15 | result.setAttribute("role", "option"); 16 | result.innerHTML = item.match; 17 | if (config.resultItem.content) config.resultItem.content(item, result); 18 | 19 | return result; 20 | }; 21 | -------------------------------------------------------------------------------- /src/components/List.js: -------------------------------------------------------------------------------- 1 | /** 2 | * List element creator 3 | * 4 | * @param {Object} config - autoComplete configurations 5 | * 6 | * @returns {Element} - The created list element 7 | */ 8 | export default (config) => { 9 | // Create a DIV element that will contain the results items 10 | const list = document.createElement(config.resultsList.element); 11 | list.setAttribute("id", config.resultsList.idName); 12 | list.setAttribute("aria-label", config.name); 13 | list.setAttribute("class", config.resultsList.className); 14 | list.setAttribute("role", "listbox"); 15 | list.setAttribute("tabindex", "-1"); 16 | if (config.resultsList.container) config.resultsList.container(list); 17 | const destination = 18 | "string" === typeof config.resultsList.destination 19 | ? document.querySelector(config.resultsList.destination) 20 | : config.resultsList.destination(); 21 | // Append the DIV element as a child of autoComplete container 22 | destination.insertAdjacentElement(config.resultsList.position, list); 23 | 24 | return list; 25 | }; 26 | -------------------------------------------------------------------------------- /dist/css/images/search.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature Request 3 | about: Suggest an idea (or improvement for an existing feature) 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | 17 | 18 | **Is your feature request related to a problem? Please describe.** 19 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 20 | 21 | **Thoroughly Describe the solution you'd like** 22 | A clear and concise description of what you want to happen. 23 | 24 | **Please provide a few use cases for this feature** 25 | 1. ... 26 | 2. ... 27 | 28 | **Please Describe alternatives you've considered** 29 | A clear and concise description of any alternative solutions or features you've considered. 30 | 31 | **Additional context** 32 | Add any other context or screenshots about the feature request here. 33 | -------------------------------------------------------------------------------- /docs/_coverpage.md: -------------------------------------------------------------------------------- 1 | 2 | autoComplete.js Logo 3 | 4 | 5 | > Simple autocomplete pure vanilla Javascript library. Live Demo **v8.3** 6 | 7 | autoComplete.js is a simple pure vanilla Javascript library that's progressively designed for speed,
high versatility and seamless integration with a wide range of projects & systems. 8 | 9 | autoComplete.js - Simple autocomplete pure vanilla Javascript library. | Product Hunt Embed 10 | 11 |
12 | 13 |
14 | 15 |
16 | 17 | 18 | 19 | [GitHub](https://github.com/TarekRaafat/autoComplete.js) 20 | [Get Started](#introduction) 21 | 22 | ![color](#fff) 23 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug Report 3 | about: Something broken or is not functioning as expected. 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | 14 | 15 | - [ ] **System Information** 16 | - [ ] Browser type and version 17 | - [ ] OS type and version 18 | - [ ] WINDOWS: be sure to indicate which terminal you're using -- (i.e., cmd.exe, powershell, git- bash, cygwin, Ubuntu via windows subsystem for linux, etc...) 19 | - [ ] Node version (IF applicable) 20 | - [ ] Any error messages that may be in the console where you ran npm start 21 | - [ ] Any error messages in the JS console 22 | 23 | - [ ] **Describe the bug** 24 | 25 | 26 | - [ ] **To Reproduce** 27 | Steps to reproduce the behavior: 79 | 80 | ## Documentation: 81 | 82 | - For more details check out **autoComplete.js** **docs** :notebook_with_decorative_cover: 83 | 84 | 85 | 86 | ## Third-Party Plugins: 87 | 88 | - [Contao autoComplete.js Bundle](https://github.com/heimrichhannot/contao-autocompletejs-bundle) by [@heimrichhannot](https://github.com/heimrichhannot) 89 | 90 | * * * 91 | 92 | ## Support 93 | 94 | For general questions about autoComplete.js, tweet at [@TarekRaafat]. 95 | 96 | For technical questions, you should post a question on [Stack Overflow] and tag 97 | it with [autoComplete.js][so tag]. 98 | 99 | 100 | 101 | [stack overflow]: https://stackoverflow.com/ 102 | 103 | [@tarekraafat]: https://twitter.com/TarekRaafat 104 | 105 | [so tag]: https://stackoverflow.com/questions/tagged/autoComplete.js 106 | 107 | * * * 108 | 109 | ## Author 110 | 111 | **Tarek Raafat** 112 | 113 | - Email: tarek.m.raafat@gmail.com 114 | - Website: 115 | - Github: 116 | 117 | Distributed under the Apache 2.0 license. See `Apache 2.0` for more information. 118 | 119 | * * * 120 | 121 | ## License 122 | 123 | Apache 2.0 © [Tarek Raafat](http://www.tarekraafat.com) 124 | -------------------------------------------------------------------------------- /src/controllers/navigationController.js: -------------------------------------------------------------------------------- 1 | import { closeAllLists } from "./listController"; 2 | import eventEmitter from "../utils/eventEmitter"; 3 | 4 | /** 5 | * List navigation function initializer 6 | * 7 | * @param {Object} config - autoComplete configurations 8 | * @param {Object} dataFeedback - The available data object 9 | * 10 | */ 11 | const navigate = (config, dataFeedback) => { 12 | // Reset focus state 13 | let currentFocus = -1; 14 | 15 | /** 16 | * Update list item state 17 | * 18 | * @param {Object} event - The `keydown` event Object 19 | * @param {Array } list - The array of list items 20 | * @param {Boolean} state - Of the arrow Down/Up 21 | * @param {Object} config - autoComplete configurations 22 | * 23 | */ 24 | const update = (event, list, state, config) => { 25 | event.preventDefault(); 26 | if (state) { 27 | // If the arrow DOWN or TAB key is pressed 28 | // increase the currentFocus 29 | currentFocus++; 30 | } else { 31 | // If the arrow UP or TAB key is pressed 32 | // decrease the currentFocus 33 | currentFocus--; 34 | } 35 | // and add "active" class to the list item 36 | addActive(list); 37 | config.inputField.setAttribute("aria-activedescendant", list[currentFocus].id); 38 | /** 39 | * @emits {navigation} Emits Event on results list navigation 40 | **/ 41 | eventEmitter( 42 | event.srcElement, 43 | { event, ...dataFeedback, selection: dataFeedback.results[currentFocus] }, 44 | "navigation" 45 | ); 46 | }; 47 | 48 | /** 49 | * Remove list item active state 50 | * 51 | * @param {Array } list - The array of list items 52 | * 53 | */ 54 | const removeActive = (list) => { 55 | // Remove "active" class from all list items 56 | for (let index = 0; index < list.length; index++) { 57 | // Remove "active" class from the item 58 | list[index].removeAttribute("aria-selected"); 59 | // list[index].setAttribute("aria-selected", "false"); 60 | if (config.selection.className !== "") { 61 | list[index].classList.remove(config.selection.className); 62 | } 63 | } 64 | }; 65 | 66 | /** 67 | * Add active state to list item 68 | * 69 | * @param {Array } list - The array of list items 70 | * 71 | */ 72 | const addActive = (list) => { 73 | // Add "active" class to a list item 74 | if (!list) return false; 75 | // Remove "active" class from all list items 76 | removeActive(list); 77 | if (currentFocus >= list.length) currentFocus = 0; 78 | if (currentFocus < 0) currentFocus = list.length - 1; 79 | // Add "active" class to the item 80 | list[currentFocus].setAttribute("aria-selected", "true"); 81 | if (config.selection.className !== "") { 82 | list[currentFocus].classList.add(config.selection.className); 83 | } 84 | }; 85 | 86 | /** 87 | * List Navigation Function 88 | * 89 | * @param {Object} event - The `keydown` event Object 90 | * 91 | */ 92 | const navigation = (event) => { 93 | let list = document.getElementById(config.resultsList.idName); 94 | 95 | if (!list) return config.inputField.removeEventListener("keydown", navigate); 96 | 97 | list = list.getElementsByTagName(config.resultItem.element); 98 | 99 | if (event.keyCode === 27) { 100 | // If the ESC key is pressed 101 | // Clear Input value 102 | config.inputField.value = ""; 103 | // Closes open lists 104 | closeAllLists(config); 105 | } else if (event.keyCode === 40 || event.keyCode === 9) { 106 | // Update list items state 107 | update(event, list, true, config); 108 | } else if (event.keyCode === 38 || event.keyCode === 9) { 109 | // Update list items state 110 | update(event, list, false, config); 111 | } else if (event.keyCode === 13) { 112 | // If the ENTER key is pressed 113 | // prevent the form from its default behaviour "being submitted" 114 | event.preventDefault(); 115 | if (currentFocus > -1) { 116 | // and simulate a click on the selected "active" item 117 | if (list) list[currentFocus].click(); 118 | } 119 | } 120 | }; 121 | 122 | const navigate = config.resultsList.navigation || navigation; 123 | 124 | // Remove previous keydown listener 125 | if (config.inputField.autoCompleteNavigate) 126 | config.inputField.removeEventListener("keydown", config.inputField.autoCompleteNavigate); 127 | config.inputField.autoCompleteNavigate = navigate; 128 | 129 | /** 130 | * @listens {keydown} Listens to all `keydown` events on the input field 131 | **/ 132 | config.inputField.addEventListener("keydown", navigate); 133 | }; 134 | 135 | export { navigate }; 136 | -------------------------------------------------------------------------------- /docs/demo/js/index.js: -------------------------------------------------------------------------------- 1 | // The autoComplete.js Engine instance creator 2 | const autoCompleteJS = new autoComplete({ 3 | name: "food & drinks", 4 | data: { 5 | src: async function () { 6 | // Loading placeholder text 7 | document.querySelector("#autoComplete").setAttribute("placeholder", "Loading..."); 8 | // Fetch External Data Source 9 | const source = await fetch("../demo/db/generic.json"); 10 | const data = await source.json(); 11 | // Post Loading placeholder text 12 | document.querySelector("#autoComplete").setAttribute("placeholder", autoCompleteJS.placeHolder); 13 | // Returns Fetched data 14 | return data; 15 | }, 16 | key: ["food", "cities", "animals"], 17 | results: (list) => { 18 | // Filter duplicates 19 | const filteredResults = Array.from(new Set(list.map((value) => value.match))).map((food) => { 20 | return list.find((value) => value.match === food); 21 | }); 22 | 23 | return filteredResults; 24 | }, 25 | }, 26 | trigger: { 27 | event: ["input", "focus"], 28 | }, 29 | placeHolder: "Search for Food & Drinks!", 30 | searchEngine: "strict", 31 | highlight: true, 32 | maxResults: 5, 33 | resultItem: { 34 | content: (data, element) => { 35 | // Modify Results Item Style 36 | element.style = "display: flex; justify-content: space-between;"; 37 | // Modify Results Item Content 38 | element.innerHTML = ` 39 | 40 | ${data.match} 41 | 42 | 43 | ${data.key} 44 | `; 45 | }, 46 | }, 47 | noResults: (dataFeedback, generateList) => { 48 | // Generate autoComplete List 49 | generateList(autoCompleteJS, dataFeedback, dataFeedback.results); 50 | // No Results List Item 51 | const result = document.createElement("li"); 52 | result.setAttribute("class", "no_result"); 53 | result.setAttribute("tabindex", "1"); 54 | result.innerHTML = `Found No Results for "${dataFeedback.query}"`; 55 | document.querySelector(`#${autoCompleteJS.resultsList.idName}`).appendChild(result); 56 | }, 57 | onSelection: (feedback) => { 58 | document.querySelector("#autoComplete").blur(); 59 | // Prepare User's Selected Value 60 | const selection = feedback.selection.value[feedback.selection.key]; 61 | // Render selected choice to selection div 62 | document.querySelector(".selection").innerHTML = selection; 63 | // Replace Input value with the selected value 64 | document.querySelector("#autoComplete").value = selection; 65 | // Console log autoComplete data feedback 66 | console.log(feedback); 67 | }, 68 | }); 69 | 70 | // Toggle Search Engine Type/Mode 71 | document.querySelector(".toggler").addEventListener("click", function () { 72 | // Holds the toggle button alignment 73 | const toggle = document.querySelector(".toggle").style.justifyContent; 74 | 75 | if (toggle === "flex-start" || toggle === "") { 76 | // Set Search Engine mode to Loose 77 | document.querySelector(".toggle").style.justifyContent = "flex-end"; 78 | document.querySelector(".toggler").innerHTML = "Loose"; 79 | autoCompleteJS.searchEngine = "loose"; 80 | } else { 81 | // Set Search Engine mode to Strict 82 | document.querySelector(".toggle").style.justifyContent = "flex-start"; 83 | document.querySelector(".toggler").innerHTML = "Strict"; 84 | autoCompleteJS.searchEngine = "strict"; 85 | } 86 | }); 87 | 88 | // Toggle results list and other elements 89 | const action = function (action) { 90 | const github = document.querySelector(".github-corner"); 91 | const title = document.querySelector("h1"); 92 | const mode = document.querySelector(".mode"); 93 | const selection = document.querySelector(".selection"); 94 | const footer = document.querySelector(".footer"); 95 | 96 | if (action === "dim") { 97 | github.style.opacity = 1; 98 | title.style.opacity = 1; 99 | mode.style.opacity = 1; 100 | selection.style.opacity = 1; 101 | footer.style.opacity = 1; 102 | } else { 103 | github.style.opacity = 0.1; 104 | title.style.opacity = 0.3; 105 | mode.style.opacity = 0.2; 106 | selection.style.opacity = 0.1; 107 | footer.style.opacity = 0.1; 108 | } 109 | }; 110 | 111 | // Toggle event for search input 112 | // showing & hiding results list onfocus/blur 113 | ["focus", "blur"].forEach(function (eventType) { 114 | document.querySelector("#autoComplete").addEventListener(eventType, function () { 115 | // Hide results list & show other elements 116 | if (eventType === "blur") { 117 | action("dim"); 118 | } else if (eventType === "focus") { 119 | // Show results list & hide other elements 120 | action("light"); 121 | } 122 | }); 123 | }); 124 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | autoComplete.js - Vanilla Javascript library 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 54 | 55 | 56 | 57 | 58 |
59 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /dist/js/index.js: -------------------------------------------------------------------------------- 1 | // // autoComplete.js input eventListener on connect event 2 | // document.querySelector("#autoComplete").addEventListener("connect", function (event) { 3 | // console.log(event); 4 | // }); 5 | // autoComplete.js input eventListener on initialization event 6 | document.querySelector("#autoComplete").addEventListener("init", function (event) { 7 | console.log(event); 8 | }); 9 | // // autoComplete.js input eventListener on input event 10 | // document.querySelector("#autoComplete").addEventListener("input", function (event) { 11 | // console.log(event); 12 | // }); 13 | // // autoComplete.js input eventListener on data response event 14 | // document.querySelector("#autoComplete").addEventListener("fetch", function (event) { 15 | // console.log(event.detail); 16 | // }); 17 | // // autoComplete.js input eventListener on search results event 18 | // document.querySelector("#autoComplete").addEventListener("results", function (event) { 19 | // console.log(event.detail); 20 | // }); 21 | // // autoComplete.js input eventListener on post results list rendering event 22 | // document.querySelector("#autoComplete").addEventListener("rendered", function (event) { 23 | // console.log(event.detail); 24 | // }); 25 | // // autoComplete.js input eventListener on results list navigation 26 | // document.querySelector("#autoComplete").addEventListener("navigation", function (event) { 27 | // console.log(event.detail); 28 | // }); 29 | // // autoComplete.js input eventListener on post un-initialization event 30 | // document.querySelector("#autoComplete").addEventListener("unInit", function (event) { 31 | // console.log(event); 32 | // }); 33 | 34 | // The autoComplete.js Engine instance creator 35 | const autoCompleteJS = new autoComplete({ 36 | name: "food & drinks", 37 | selector: "#autoComplete", 38 | observer: false, 39 | data: { 40 | src: async () => { 41 | // Loading placeholder text 42 | document.querySelector("#autoComplete").setAttribute("placeholder", "Loading..."); 43 | 44 | if (!JSON.parse(localStorage.getItem("acData"))) { 45 | // Fetch External Data Source 46 | const source = await fetch("./db/generic.json"); 47 | const data = await source.json(); 48 | // Saves the fetched data into local storage 49 | localStorage.setItem("acData", JSON.stringify(data)); 50 | // Retrieve the cached data from local storage 51 | const localData = JSON.parse(localStorage.getItem("acData")); 52 | // Post Loading placeholder text 53 | document.querySelector("#autoComplete").setAttribute("placeholder", autoCompleteJS.placeHolder); 54 | // Returns Fetched data 55 | return localData; 56 | } 57 | 58 | // Post Loading placeholder text 59 | document.querySelector("#autoComplete").setAttribute("placeholder", autoCompleteJS.placeHolder); 60 | 61 | return JSON.parse(localStorage.getItem("acData")); 62 | }, 63 | key: ["food", "cities", "animals"], 64 | cache: true, 65 | results: (list) => { 66 | // Filter duplicates 67 | const filteredResults = Array.from(new Set(list.map((value) => value.match))).map((food) => { 68 | return list.find((value) => value.match === food); 69 | }); 70 | 71 | return filteredResults; 72 | }, 73 | }, 74 | searchEngine: "strict", 75 | placeHolder: "Search for Food & Drinks!", 76 | maxResults: 5, 77 | sort: (a, b) => { 78 | if (a.match < b.match) return -1; 79 | if (a.match > b.match) return 1; 80 | return 0; 81 | }, 82 | highlight: true, 83 | debounce: 100, 84 | threshold: 1, 85 | trigger: { 86 | event: ["input", "focus"], 87 | }, 88 | resultItem: { 89 | content: (data, element) => { 90 | // Modify Results Item Style 91 | element.style = "display: flex; justify-content: space-between;"; 92 | // Modify Results Item Content 93 | element.innerHTML = ` 94 | 95 | ${data.match} 96 | 97 | 98 | ${data.key} 99 | `; 100 | }, 101 | }, 102 | noResults: (dataFeedback, generateList) => { 103 | // Generate autoComplete List 104 | generateList(autoCompleteJS, dataFeedback, dataFeedback.results); 105 | // No Results List Item 106 | const result = document.createElement("li"); 107 | result.setAttribute("class", "no_result"); 108 | result.setAttribute("tabindex", "1"); 109 | result.innerHTML = `Found No Results for "${dataFeedback.query}"`; 110 | document.querySelector(`#${autoCompleteJS.resultsList.idName}`).appendChild(result); 111 | }, 112 | feedback: (data) => { 113 | console.log(data); 114 | }, 115 | onSelection: (feedback) => { 116 | document.querySelector("#autoComplete").blur(); 117 | // Prepare User's Selected Value 118 | const selection = feedback.selection.value[feedback.selection.key]; 119 | // Render selected choice to selection div 120 | document.querySelector(".selection").innerHTML = selection; 121 | // Replace Input value with the selected value 122 | document.querySelector("#autoComplete").value = selection; 123 | // Console log autoComplete data feedback 124 | console.log(feedback); 125 | }, 126 | }); 127 | 128 | // autoComplete.unInit(); 129 | 130 | // Toggle Search Engine Type/Mode 131 | document.querySelector(".toggler").addEventListener("click", function () { 132 | // Holds the toggle button alignment 133 | const toggle = document.querySelector(".toggle").style.justifyContent; 134 | 135 | if (toggle === "flex-start" || toggle === "") { 136 | // Set Search Engine mode to Loose 137 | document.querySelector(".toggle").style.justifyContent = "flex-end"; 138 | document.querySelector(".toggler").innerHTML = "Loose"; 139 | autoCompleteJS.searchEngine = "loose"; 140 | } else { 141 | // Set Search Engine mode to Strict 142 | document.querySelector(".toggle").style.justifyContent = "flex-start"; 143 | document.querySelector(".toggler").innerHTML = "Strict"; 144 | autoCompleteJS.searchEngine = "strict"; 145 | } 146 | }); 147 | 148 | // Toggle results list and other elements 149 | const action = function (action) { 150 | const github = document.querySelector(".github-corner"); 151 | const title = document.querySelector("h1"); 152 | const mode = document.querySelector(".mode"); 153 | const selection = document.querySelector(".selection"); 154 | const footer = document.querySelector(".footer"); 155 | 156 | if (action === "dim") { 157 | github.style.opacity = 1; 158 | title.style.opacity = 1; 159 | mode.style.opacity = 1; 160 | selection.style.opacity = 1; 161 | footer.style.opacity = 1; 162 | } else { 163 | github.style.opacity = 0.1; 164 | title.style.opacity = 0.3; 165 | mode.style.opacity = 0.2; 166 | selection.style.opacity = 0.1; 167 | footer.style.opacity = 0.1; 168 | } 169 | }; 170 | 171 | // Toggle event for search input 172 | // showing & hiding results list onfocus/blur 173 | ["focus", "blur"].forEach(function (eventType) { 174 | document.querySelector("#autoComplete").addEventListener(eventType, function () { 175 | // Hide results list & show other elements 176 | if (eventType === "blur") { 177 | action("dim"); 178 | } else if (eventType === "focus") { 179 | // Show results list & hide other elements 180 | action("light"); 181 | } 182 | }); 183 | }); 184 | -------------------------------------------------------------------------------- /docs/demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 16 | 17 | 18 | autoComplete.js - Vanilla Javascript library 19 | 21 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 36 | 37 | 38 | 39 | 40 | 41 | 43 | 44 | 46 | 48 | 50 | 52 | 54 | 56 | 58 | 60 | 62 | 64 | 66 | 68 | 70 | 71 | 72 | 73 | 74 | 76 | 77 | 78 | 79 | 80 | 81 |
82 | 93 | 126 |
127 |

128 | autoComplete.js 129 |

130 |
131 |
132 |
133 | 135 |
136 |
137 |

mode

138 |
139 |
Strict
140 |
141 |
142 |
143 |
144 |
145 |
146 | Copyright © 147 | 148 | 149 | Tarek Raafat All Rights 150 | Reserved 151 |
152 |
153 |
154 | 155 | 156 | 157 | 158 | -------------------------------------------------------------------------------- /dist/js/autoComplete.min.js: -------------------------------------------------------------------------------- 1 | var a,b;a=this,b=function(){"use strict";function e(e,t){for(var n=0;ne.length)&&(t=e.length);for(var n=0,i=new Array(t);n=e.length?{done:!0}:{done:!1,value:e[i++]}},e:function(e){throw e},f:t}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var r,o=!0,s=!1;return{s:function(){n=e[Symbol.iterator]()},n:function(){var e=n.next();return o=e.done,e},e:function(e){s=!0,r=e},f:function(){try{o||null==n.return||n.return()}finally{if(s)throw r}}}}function u(e,t){for(var n=document.getElementsByClassName(e.resultsList.className),i=0;i=e.length?0:o)<0?e.length-1:o].setAttribute("aria-selected","true"),e[o].classList.add("autoComplete_selected")},a=n.resultsList.navigation||function(e){var t=document.getElementById(n.resultsList.idName);if(!t)return n.inputField.removeEventListener("keydown",a);t=t.getElementsByTagName(n.resultItem.element),27===e.keyCode?(n.inputField.value="",u(n)):40===e.keyCode||9===e.keyCode?i(e,t,!0,n):38===e.keyCode||9===e.keyCode?i(e,t,!1,n):13===e.keyCode&&(e.preventDefault(),-1'.concat(a,""):a,o++),r.push(a)}if(o===e.length)return r.join("")}else if(i.includes(e))return e=new RegExp("".concat(e),"i").exec(t),n.highlight?t.replace(e,''.concat(e,"")):t}(s,t,o))&&e?a.push({key:e,index:n,match:t,value:i}):t&&!e&&a.push({index:n,match:t,value:i}))}var i=o.data.store[n];if(o.data.key){var t,r=l(o.data.key);try{for(r.s();!(t=r.n()).done;)e(t.value)}catch(e){r.e(e)}finally{r.f()}}else e()},t=0;t=r.threshold&&o.replace(/ /g,"").length)?a.dataStore().then(function(e){try{return u(a),a.start(n,i),s.call(a)}catch(e){return t(e)}},t):(u(a),s.call(a));function s(){return e()}})}},{key:"init",value:function(){var e,n,i,r,t=this;(e=this).inputField.setAttribute("role","combobox"),e.inputField.setAttribute("aria-haspopup",!0),e.inputField.setAttribute("aria-expanded",!1),e.inputField.setAttribute("aria-controls",e.resultsList.idName),e.inputField.setAttribute("aria-autocomplete","both"),this.placeHolder&&this.inputField.setAttribute("placeholder",this.placeHolder),this.hook=(n=function(){t.compose()},i=this.debounce,function(){var e=this,t=arguments;clearTimeout(r),r=setTimeout(function(){return n.apply(e,t)},i)}),this.trigger.event.forEach(function(e){t.inputField.removeEventListener(e,t.hook),t.inputField.addEventListener(e,t.hook)}),d(this.inputField,null,"init")}},{key:"preInit",value:function(){var r=this;new MutationObserver(function(e,t){var n,i=l(e);try{for(i.s();!(n=i.n()).done;){n.value;r.inputField&&(t.disconnect(),d(r.inputField,null,"connect"),r.init())}}catch(e){i.e(e)}finally{i.f()}}).observe(document,{childList:!0,subtree:!0})}},{key:"unInit",value:function(){this.inputField.removeEventListener("input",this.hook),d(this.inputField,null,"unInit")}}])&&e(n.prototype,i),h&&e(n,h),T},"object"==typeof exports&&"undefined"!=typeof module?module.exports=b():"function"==typeof define&&define.amd?define(b):(a="undefined"!=typeof globalThis?globalThis:a||self).autoComplete=b(); 2 | -------------------------------------------------------------------------------- /src/autoComplete.js: -------------------------------------------------------------------------------- 1 | import inputComponent from "./components/Input"; 2 | import { generateList, closeAllLists } from "./controllers/listController"; 3 | import { navigate } from "./controllers/navigationController"; 4 | import { 5 | getInputValue, 6 | prepareQueryValue, 7 | checkTriggerCondition, 8 | listMatchingResults, 9 | } from "./controllers/dataController"; 10 | import debouncer from "./utils/debouncer"; 11 | import eventEmitter from "./utils/eventEmitter"; 12 | 13 | /** 14 | * @desc This is autoComplete.js 15 | * @version 8.3.2 16 | * @example let autoCompleteJS = new autoComplete({config}); 17 | */ 18 | export default class autoComplete { 19 | constructor(config) { 20 | // Deconstructing config values 21 | const { 22 | name = "Search", 23 | selector = "#autoComplete", // User input selector 24 | observer = false, // Input field Observer 25 | data: { 26 | src, // Data src selection 27 | key, // Data src key selection 28 | cache = false, // Flag to cache data src 29 | store, // Data feedback store 30 | results, // Data feedback matching results 31 | }, 32 | query, // Query interceptor function 33 | trigger: { 34 | event = ["input"], // autoComplete event 35 | condition = false, // condition trigger 36 | } = {}, 37 | searchEngine = "strict", // Search engine type 38 | diacritics = false, // Diacritics to be ignored 39 | threshold = 1, // Minimum characters length before engine starts rendering 40 | debounce = 0, // Minimum duration for API calls debounce 41 | resultsList: { 42 | render: resultsListRender = true, 43 | container = false, 44 | destination, // Results list selector 45 | position = "afterend", // Results list position 46 | element: resultsListElement = "ul", // Results list element tag 47 | idName: resultsListId = "autoComplete_list", 48 | className: resultsListClass = "autoComplete_list", 49 | navigation = false, // Results list navigation 50 | } = {}, 51 | sort = false, // Sorting results list 52 | placeHolder, // Placeholder text 53 | maxResults = 5, // Maximum number of results to show 54 | resultItem: { 55 | content = false, // Result item function 56 | element: resultItemElement = "li", // Result item element tag 57 | idName: resultItemId = "autoComplete_result", 58 | className: resultItemClass = "autoComplete_result", 59 | } = {}, 60 | noResults, // No results action 61 | selection: { 62 | className: selectionClass = "autoComplete_selected", 63 | } = {}, 64 | highlight: { 65 | render: highlightRender = false, 66 | className: highlightClass = "autoComplete_highlighted", 67 | } = {}, 68 | feedback, // Data feedback 69 | onSelection, // Action function on result selection 70 | } = config; 71 | 72 | // Assigning config values to properties 73 | this.name = name; 74 | this.selector = selector; 75 | this.observer = observer; 76 | this.data = { 77 | src, 78 | key, 79 | cache, 80 | store, 81 | results, 82 | }; 83 | this.query = query; 84 | this.trigger = { 85 | event, 86 | condition, 87 | }; 88 | this.searchEngine = searchEngine; 89 | this.diacritics = diacritics; 90 | this.threshold = threshold; 91 | this.debounce = debounce; 92 | this.resultsList = { 93 | render: resultsListRender, 94 | container, 95 | destination: destination || this.selector, 96 | position, 97 | element: resultsListElement, 98 | idName: resultsListId, 99 | className: resultsListClass, 100 | navigation, 101 | }; 102 | this.sort = sort; 103 | this.placeHolder = placeHolder; 104 | this.maxResults = maxResults; 105 | this.resultItem = { 106 | content, 107 | element: resultItemElement, 108 | idName: resultItemId, 109 | className: resultItemClass, 110 | }; 111 | this.noResults = noResults; 112 | this.selection = { 113 | className: selectionClass, 114 | }; 115 | this.highlight = { 116 | render: highlightRender, 117 | className: highlightClass, 118 | } 119 | this.feedback = feedback; 120 | this.onSelection = onSelection; 121 | // Assign the input field selector 122 | this.inputField = typeof this.selector === "string" ? document.querySelector(this.selector) : this.selector(); 123 | // Invoke preInit if enabled 124 | // or initiate autoComplete instance directly 125 | this.observer ? this.preInit() : this.init(); 126 | } 127 | 128 | // Run autoComplete processes 129 | start(input, query) { 130 | // - Match query with existing value 131 | // Returns matching results list 132 | const results = this.data.results 133 | ? this.data.results(listMatchingResults(this, query)) 134 | : listMatchingResults(this, query); 135 | // - Prepare data feedback object 136 | const dataFeedback = { input, query, matches: results, results: results.slice(0, this.maxResults) }; 137 | /** 138 | * @emits {response} Emits Event on search response 139 | **/ 140 | eventEmitter(this.inputField, dataFeedback, "results"); 141 | // - Checks if there are NO results 142 | // Runs noResults action function 143 | if (!results.length) return this.noResults ? this.noResults(dataFeedback, generateList) : null; 144 | // - If resultsList set not to render 145 | if (!this.resultsList.render) return this.feedback(dataFeedback); 146 | // - Generate & Render results list 147 | const list = results.length ? generateList(this, dataFeedback, results) : null; 148 | /** 149 | * @emits {rendered} Emits Event after results list rendering 150 | **/ 151 | eventEmitter(this.inputField, dataFeedback, "rendered"); 152 | // - Initialize navigation 153 | navigate(this, dataFeedback); 154 | /** 155 | * @desc 156 | * Listens for all `click` events in the document 157 | * and closes this menu if clicked outside the list and input field 158 | * @listens {click} Listens to all `click` events on the document 159 | **/ 160 | document.addEventListener("click", (event) => closeAllLists(this, event.target)); 161 | } 162 | 163 | async dataStore() { 164 | // Check if cache is NOT true 165 | // and store is empty 166 | if (this.data.cache && this.data.store) return null; 167 | // Fetch new data from source and store it 168 | this.data.store = typeof this.data.src === "function" ? await this.data.src() : this.data.src; 169 | /** 170 | * @emits {request} Emits Event on data response 171 | **/ 172 | eventEmitter(this.inputField, this.data.store, "fetch"); 173 | } 174 | 175 | // Run autoComplete composer 176 | async compose() { 177 | // 0- Prepare raw input value 178 | const input = getInputValue(this.inputField); 179 | // 1- Prepare manipulated query input value 180 | const query = prepareQueryValue(input, this); 181 | // 2- Get trigger condition 182 | const triggerCondition = checkTriggerCondition(this, query); 183 | // 3- Check triggering condition 184 | if (triggerCondition) { 185 | // 4- Prepare the data 186 | await this.dataStore(); 187 | // 5- Close all open lists 188 | closeAllLists(this); 189 | // 6- Start autoComplete engine 190 | this.start(input, query); 191 | } else { 192 | // 4- Close all open lists 193 | closeAllLists(this); 194 | } 195 | } 196 | 197 | // Initialization stage 198 | init() { 199 | // Set input field attributes 200 | inputComponent(this); 201 | // Set placeholder attribute value 202 | if (this.placeHolder) this.inputField.setAttribute("placeholder", this.placeHolder); 203 | // Run executer 204 | this.hook = debouncer(() => { 205 | // - Prepare autoComplete processes 206 | this.compose(); 207 | }, this.debounce); 208 | /** 209 | * @listens {input} Listens to all `input` events on the input field 210 | **/ 211 | this.trigger.event.forEach((eventType) => { 212 | this.inputField.removeEventListener(eventType, this.hook); 213 | this.inputField.addEventListener(eventType, this.hook); 214 | }); 215 | /** 216 | * @emits {init} Emits Event on Initialization 217 | **/ 218 | eventEmitter(this.inputField, null, "init"); 219 | } 220 | 221 | // Pre-Initialization stage 222 | preInit() { 223 | // Observe DOM changes 224 | // Options for the observer (which mutations to observe) 225 | const config = { childList: true, subtree: true }; 226 | // Callback function to execute when mutations are observed 227 | const callback = (mutationsList, observer) => { 228 | // Traditional 'for loops' for IE 11 229 | for (let mutation of mutationsList) { 230 | // Check if this is the selected input field 231 | if (this.inputField) { 232 | // If yes disconnect the observer 233 | observer.disconnect(); 234 | /** 235 | * @emits {connect} Emits Event on connection 236 | **/ 237 | eventEmitter(this.inputField, null, "connect"); 238 | // Initialize autoComplete 239 | this.init(); 240 | } 241 | } 242 | }; 243 | // Create an observer instance linked to the callback function 244 | const observer = new MutationObserver(callback); 245 | // Start observing the target node for configured mutations 246 | // The entire document will be observed for mutations 247 | observer.observe(document, config); 248 | } 249 | 250 | // Un-initialize autoComplete 251 | unInit() { 252 | this.inputField.removeEventListener("input", this.hook); 253 | /** 254 | * @emits {detached} Emits Event on input eventListener detachment 255 | **/ 256 | eventEmitter(this.inputField, null, "unInit"); 257 | } 258 | } 259 | -------------------------------------------------------------------------------- /docs/img/autoComplete.js.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /docs/releases.md: -------------------------------------------------------------------------------- 1 | ## Versioning 2 | 3 | * * * 4 | 5 | For transparency and insight into the release cycle, releases will be numbered 6 | with the following format: 7 | 8 | `..` 9 | 10 | And constructed with the following guidelines: 11 | 12 | - Breaking backwards compatibility bumps the major 13 | - New additions without breaking backwards compatibility bumps the minor 14 | - Bug fixes and misc changes bump the patch 15 | 16 | For more information on semantic versioning, please visit . 17 | 18 | Release flags: 19 | 20 | - `[Experimental]`: Under testing and might be deprecated at any point 21 | - `[Deprecated]`: Not developed / supported anymore, might be removed at any point 22 | - `[Removed]`: Completely gone, no longer exists 23 | - `[Changed]`: A change in the API 24 | 25 | * * * 26 | 27 | ## Release Notes 28 | 29 | - v8.3.2 ✨ 30 | - 🔧 Fix: `selector` as a function breaks when `observer` is true (Thanks 👍 @brunobg) #179 31 | 32 | - v8.3.1 33 | - 🔧 Fix: Keyboard navigation selection reset (Thanks 👍 @marsimeau) #177 34 | 35 | - v8.3.0 36 | - ➕ Added: `event` object to the `onSelection` data `feedback` (Thanks 👍 @Liano) #176 37 | 38 | - v8.2.3 39 | - 🔧 Fix: `resultItem` ID `setAttribute` to be `idName` instead of `className` (Thanks 👍 @marsimeau) #173 40 | 41 | - v8.2.2 42 | - 🔧 Fix: `diacritics` composite characters do not match (Thanks 👍 @ikemo3 @Michin0suke @bravik) #169 #171 43 | 44 | - v8.2.1 45 | - 🔝 Updated: `package.json` node engine version from `12` to `>=12` (Thanks 👍 @mynameisbogdan) #164 46 | - 🔝 Updated: `package.json` npm engine version from `6` to `>=6` 47 | 48 | - v8.2.0 49 | - ➕ Added: `data.results` API to access and manipulate data feedback matching results 50 | - 🔧 Fixed: `resultItem.content` API `data` params to pass the entire item data (Thanks 👍 @jwendel) #163 51 | 52 | - v8.1.1 53 | - 🔧 Fixed: `selector` API to accept function (Thanks 👍 @goaround) #160 #161 54 | - 🔧 Fixed: `resultsList` destination API to accept function (Thanks 👍 @goaround) #160 #162 55 | 56 | - v8.1.0 57 | - ➕ Added: `observer` Controller API [Turned off by default] #149 58 | - ➕ Added: New Light Style [autoComplete.02.css] 59 | - 🔧 Fixed: Main build `live reload` issue (Thanks 👍 @lougroshek) #155 60 | 61 | - v8.0.4 62 | - Fixed: Remove prior event listeners before adding new one in `init` (Thanks 👍 @RobinLawinsky) #153 63 | 64 | - v8.0.3 65 | - Fixed: Custom results list rendering destination (Thanks 👍 @RobinLawinsky) #150 66 | 67 | - v8.0.2 68 | - Enhanced: Input Field Assignment Order 69 | - Fixed: Trigger Event API 70 | 71 | - v8.0.1 72 | - Build Update 73 | 74 | - v8.0.0 75 | - Whole New More Modern Architecture Design 76 | - Added: High Quality Accessibility ([WAI-ARIA 1.2](https://www.w3.org/TR/wai-aria-practices-1.2/examples/combobox/combobox-autocomplete-both.html)) Support 77 | - Added: Life Cycle Events (Thanks 👍 @zippy84) #89 78 | - Added: `init/unInit` methods  (Thanks 👍 @Keagel) #115 79 | - Added: Input Field Observing Functionality 80 | - Added: `Diacritics` 2 way Support (Thanks 👍 @batcaverna, @svkmedia) #77 #93 81 | - Added: API for Controlling `Classes` & `IDs` (Thanks 👍 @xtellurian, @Lirux) #73 82 | - Added: New neutral/non-opinionated Style (Thanks 👍 @luizbills) #92 83 | - Enhanced: `autoComplete.js` Internal Flow 84 | - Enhanced: `data` Fetching 85 | - Enhanced: `data` Storing 86 | - Enhanced: `resultsList` Navigation 87 | - Enhanced: `resultsList` Rendering (Thanks 👍 @eballeste) #105 #139 #126 88 | - Fixed: `resultsList` element visibility in idle state (Thanks 👍 @digiiitalmb) #100 89 | - Fixed: `query` threshold length accuracy #142 90 | - Fixed: Calling `dataSrc` on each trigger (Thanks 👍 @thomasphilibert) #106 91 | - Fixed: Right-click behavior on `resultsList` (Thanks 👍 @drankje) #94 92 | - Fixed: Cursor relocation on keyboard `keyUp` or `keyDown` (Thanks 👍 @cadday) #117 93 | - Fixed: `data` as a `Number` parsing issue (Thanks 👍 @andresfdel17) #132 94 | - Fixed: `autoComplete.js` interference with native keyboard events (Thanks 👍 @eballeste) #104 95 | - Fixed: Keyboard events stops working when `onSelection` not defined (Thanks 👍 @AustinGrey) #130 96 | 97 | - v7.2.0 98 | - Added support to `textarea` input field (Thanks 👍 @EmilStenstrom) 99 | 100 | 101 | - v7.1.3 102 | - Enhanced mouse selection (Thanks 👍 @adan-ferguson) 103 | 104 | 105 | - v7.1.2 106 | - Fixed error behavior occurs when searching (Empty, False, Null) record 107 | 108 | 109 | - v7.1.1 110 | - `resList` now is fully created in `DocumentFragment` before rendering for better performance (Thanks 👍 @asafwat) 111 | - `config` parameters restructure (Thanks 👍 @asafwat) 112 | - Reduced `autoComplete.js` weight 113 | 114 | - v7.1.0 115 | - New improved Navigation logic (Thanks 👍 @mtomov) 116 | - `shadowRoot` API support `[Removed]` 117 | - Enhanced `resList.navigation` API data feedback 118 | - Styling `[Changed]` 119 | - Major code Refactor & Optimizations 120 | - Faster performance 121 | - Reduced `autoComplete.js` weight 122 | 123 | - v7.0.3 124 | - Duplicate values selection bug fix (Thanks 👍 @plungerman) 125 | 126 | - v7.0.2 127 | - Data Promise bug fix (Thanks 👍 @braco) 128 | - Remote API duplicate calls fix (Thanks 👍 @srinivas025, @argebynogame) 129 | - `trigger.condition` enhancement (Thanks 👍 @sakuraineed) 130 | - Code Refactor for faster performance and lighter weight 131 | 132 | - v7.0.1 133 | - `api multiple calls` issue fix (Thanks 👍 @srinivas025) 134 | 135 | - v7.0.0 136 | - New API for results list navigation `resultsList.navigation` (Thanks 👍 @fredluetkemeier) 137 | - New API for autoComplete.js engine `trigger.event` (Thanks 👍 @fredluetkemeier) 138 | - New API for autoComplete.js engine `trigger.condition` 139 | - Added Support to `Shadow DOM` expanding customizability (Thanks 👍 @MSDevs) 140 | - Node Element Support for Input Selector (Thanks 👍 @jkhaui) 141 | - Empty record issue fix (Thanks 👍 @Platon) 142 | - `customEngine` API `[Removed]` 143 | - `customEngine` merged with `searchEngine` API key for more convenience `[Changed]` 144 | - Code Optimizations 145 | 146 | - v6.1.0 147 | - Use Custom Search Algorithm via `customEngine` method (Thanks @hwangm) 148 | 149 | - v6.0.0 150 | - `CustomEvent` & `Closest` method IE compatibility (Thanks @g-viet) 151 | - Query interception (Thanks @barns101) 152 | - Simplified `resultsList` & `resultItem` 153 | - `EventEmitter` fires on clearing input field 154 | - `EventEmitter` now has `input` method for row user’s input 155 | - `EventEmitter` now has `query` method for intercepted user’s input 156 | 157 | - v5.3.0 158 | - Get results from `eventEmitter` without rendering list through `resultsList.render` API 159 | - EventEmitter name `type` changed to `autoComplete` `[Changed]` 160 | 161 | - v5.2.0 162 | - Added Event Emitter on `noResults` event 163 | 164 | - v5.1.2 165 | - `noResults` API unset error bug fix 166 | 167 | - v5.1.1 168 | - `UpperCase` query bug fix 169 | 170 | - v5.1.0 171 | - Added `noResults` open API for No Results (Thanks @chabri) 172 | - HTML elements `ContentEditable` Input Support (Thanks @philippejadin) 173 | 174 | - v5.0.0 175 | - Large datasets handeling (Thanks @Johann-S) 176 | - API Data fetching & Dynamic Data reloading (Thanks @Brostafa) 177 | - Debouncing API Calls 178 | - Custom `resultsList` & `resultItem` Elements (Thanks @Johann-S) 179 | - Bug fixes 180 | - Code Clean Up 181 | 182 | - v4.0.0 183 | - Multiple searchable `keys` for data `src` (Thanks @Johann-S) 184 | - Rendered `results` in original case (Thanks @nickbp12) 185 | - Improved Development Environment (Thanks @ziishaned) 186 | - IE 11 fix (Thanks @maciekgrzybek) 187 | - Improved returned data object `onSelection` 188 | - Index of result data value 189 | - Total number of matching results 190 | - Key of result data value (If multiple keys) 191 | - HTML element of selected result 192 | - Sort rendered `results` API 193 | - Enhanced `results` navigation adding `ArrowRight` key for selection 194 | - Added `event` emitter on input field type name `type` returns 195 | - Query 196 | - Number of matching results 197 | - Rendered results 198 | - Keyboard event 199 | - Code Clean Up 200 | 201 | - v3.2.2 202 | - Fixed bug with `hightlight` API default value during `strict` engine mode 203 | - Fixed bug with `resultsList` API default value when not configured 204 | 205 | - v3.2.1 206 | - Isolated `resultsList` value for multiple instances **(Thanks @albu77)** 207 | 208 | - v3.2.0 209 | - API Enhancements over rendered results list container `resultsList > container` function **(Thanks @albu77)** 210 | 211 | - v3.1.0 212 | - Added API for rendered results list container `resultsList > container` function **(Thanks @albu77)** 213 | - API Enhancements 214 | 215 | - v3.0.0 216 | - Added API for rendered result item `resultItem` function 217 | - `renderResults` API name changed to `resultsList` `[Changed]` 218 | 219 | - v2.1.0 220 | - Added support for Keyboard `(Arrow)` & `(Tab)` Navigation 221 | - Selection event object returns in data feedback **(Thanks @alvaaz)** 222 | - Added `Function` support to `selector` for detached DOM rendering **(Thanks @mikob)** 223 | - Added `Function` support to `renderResults` for detached DOM rendering 224 | - Fixed Placeholder if not set doesn't overwrite external assigned values **(Thanks @mikob)** 225 | - Replaced `id` with `data-attribute` **(Thanks @mikob)** 226 | - Input doesn't clear automatically `onSelection` event **(Thanks @mikob)** 227 | - Place Holder doesn't assign selected value `onSelection` event automatically 228 | - Error message rendered on Engine failure in the body for End-User `[Removed]` **(Thanks @mikob)** 229 | - API `data-attribute` setting `[Removed]` 230 | - Optimizations 231 | - Style Enhancements 232 | 233 | - v2.0.1 234 | - Fixed multiple space input issue **(Thanks @DevOsamaMohamed)** 235 | - Fixed remote data source excessive requests **(Thanks @DevOsamaMohamed)** 236 | - Optimizations for performance enhancements 237 | - Reduced Weight 238 | 239 | - v2.0.0 240 | - Added support for array of `Objects` & `JSON` as data source with `Key` selection 241 | - Added support for external data source via `Promises` & `Async/Await` function 242 | - Added more comprehensive and usable data feedback on user selection `(User Input, Results List, User Selection)` Object 243 | - `dataSrc` method `CHANGED` to object method `data` with two new methods `src` & `key` ([Check API Configurations](/?id=api-configurations)) 244 | - Highlighted Results class name `CHANGED` from `.autoComplete_highlighted_result` to `autoComplete_highlighted` 245 | - Many Optimizations for better performance 246 | - Scroll Infinite results style in [Demo](https://tarekraafat.github.io/autoComplete.js/demo/) 247 | 248 | - v1.5.4 249 | - Gzipped options for both builds are ready `(2KB) non-minified` & `(1KB) minified` 250 | - Fixed styling issue with selections last selection child on mobile devices 251 | 252 | - v1.5.3 253 | - Added `threshold` for minimum characters length before Engine starts rendering suggestions 254 | - Optimizations for better performance 255 | - Reduced Weight to `(3KB)` minified 256 | 257 | - v1.5.2 258 | - Fixed `onSelection` null action issue 259 | - Placeholder keeps value of last selection `[Removed]` 260 | - Fully isolated UI from Logic 261 | - Some code cleanup & optimizations 262 | - Reduced Weight 263 | 264 | - v1.5.1 265 | - Bug fixes 266 | 267 | - v1.5.0 268 | - Ability to change results destination & position `renderResults` 269 | - Optimizations for faster performance & lighter weight 270 | - Enhanced error handling capabilities 271 | - Styles fixes for better cross browser compatibility 272 | - Added detailed documentation 273 | 274 | - v1.4.1 275 | - Bug Fixes 276 | 277 | - v1.4.0 278 | - Added new type/mode of Search Engine `strict` 279 | - Detached the results list style behavior from code 280 | - `Placeholder max. length` option `[Removed]` 281 | - Optimizations for higher performance & lighter weight 282 | 283 | - v1.3.1 284 | - Refactored for higher speed & smaller footprint 285 | - Bug fixes & Optimizations 286 | 287 | - v1.3.0 288 | - Added new `datasrc` a function that returns `Array` 289 | 290 | - v1.2.1 291 | - Added Placeholder text maximum length option `[Experimental]` 292 | - Added new style sheet variation 293 | 294 | - v1.2.0 295 | - Redesigned the entire search engine for better results & experience 296 | - Added support for Multi-keyword search 297 | - Fixed issue with Capital letters reflects in results 298 | 299 | - v1.1.0 300 | - Reduced the library size 97% `(101KB -> 4KB)` 301 | - Introducing 2 different versions of the library `(pure, minified)` 302 | - Replaced webpack with Rollup for better bundling 303 | - Fixed some bugs caused problems with node apps 304 | 305 | - v1.0.3 306 | - Refactored & Optimized to reduce size and enhance performance 307 | 308 | - v1.0.2 309 | - Fixed the library name in the webpack.config.js file 310 | 311 | - v1.0.1 312 | - Optimizations Reduced the library weight by 1KB 313 | 314 | - v1.0.0 315 | - Add customized data attribute tag for generated results 316 | - Highlight matching results from the results list 317 | - Set maximum number for shown results 318 | - Add placeholder text to the input field 319 | - Placeholder keeps the last selection value saved 320 | -------------------------------------------------------------------------------- /dist/js/autoComplete.js: -------------------------------------------------------------------------------- 1 | (function (global, factory) { 2 | typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : 3 | typeof define === 'function' && define.amd ? define(factory) : 4 | (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.autoComplete = factory()); 5 | }(this, (function () { 'use strict'; 6 | 7 | function _classCallCheck(instance, Constructor) { 8 | if (!(instance instanceof Constructor)) { 9 | throw new TypeError("Cannot call a class as a function"); 10 | } 11 | } 12 | 13 | function _defineProperties(target, props) { 14 | for (var i = 0; i < props.length; i++) { 15 | var descriptor = props[i]; 16 | descriptor.enumerable = descriptor.enumerable || false; 17 | descriptor.configurable = true; 18 | if ("value" in descriptor) descriptor.writable = true; 19 | Object.defineProperty(target, descriptor.key, descriptor); 20 | } 21 | } 22 | 23 | function _createClass(Constructor, protoProps, staticProps) { 24 | if (protoProps) _defineProperties(Constructor.prototype, protoProps); 25 | if (staticProps) _defineProperties(Constructor, staticProps); 26 | return Constructor; 27 | } 28 | 29 | function _defineProperty(obj, key, value) { 30 | if (key in obj) { 31 | Object.defineProperty(obj, key, { 32 | value: value, 33 | enumerable: true, 34 | configurable: true, 35 | writable: true 36 | }); 37 | } else { 38 | obj[key] = value; 39 | } 40 | 41 | return obj; 42 | } 43 | 44 | function ownKeys(object, enumerableOnly) { 45 | var keys = Object.keys(object); 46 | 47 | if (Object.getOwnPropertySymbols) { 48 | var symbols = Object.getOwnPropertySymbols(object); 49 | if (enumerableOnly) symbols = symbols.filter(function (sym) { 50 | return Object.getOwnPropertyDescriptor(object, sym).enumerable; 51 | }); 52 | keys.push.apply(keys, symbols); 53 | } 54 | 55 | return keys; 56 | } 57 | 58 | function _objectSpread2(target) { 59 | for (var i = 1; i < arguments.length; i++) { 60 | var source = arguments[i] != null ? arguments[i] : {}; 61 | 62 | if (i % 2) { 63 | ownKeys(Object(source), true).forEach(function (key) { 64 | _defineProperty(target, key, source[key]); 65 | }); 66 | } else if (Object.getOwnPropertyDescriptors) { 67 | Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); 68 | } else { 69 | ownKeys(Object(source)).forEach(function (key) { 70 | Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); 71 | }); 72 | } 73 | } 74 | 75 | return target; 76 | } 77 | 78 | function _unsupportedIterableToArray(o, minLen) { 79 | if (!o) return; 80 | if (typeof o === "string") return _arrayLikeToArray(o, minLen); 81 | var n = Object.prototype.toString.call(o).slice(8, -1); 82 | if (n === "Object" && o.constructor) n = o.constructor.name; 83 | if (n === "Map" || n === "Set") return Array.from(o); 84 | if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); 85 | } 86 | 87 | function _arrayLikeToArray(arr, len) { 88 | if (len == null || len > arr.length) len = arr.length; 89 | 90 | for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; 91 | 92 | return arr2; 93 | } 94 | 95 | function _createForOfIteratorHelper(o, allowArrayLike) { 96 | var it; 97 | 98 | if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { 99 | if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { 100 | if (it) o = it; 101 | var i = 0; 102 | 103 | var F = function () {}; 104 | 105 | return { 106 | s: F, 107 | n: function () { 108 | if (i >= o.length) return { 109 | done: true 110 | }; 111 | return { 112 | done: false, 113 | value: o[i++] 114 | }; 115 | }, 116 | e: function (e) { 117 | throw e; 118 | }, 119 | f: F 120 | }; 121 | } 122 | 123 | throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); 124 | } 125 | 126 | var normalCompletion = true, 127 | didErr = false, 128 | err; 129 | return { 130 | s: function () { 131 | it = o[Symbol.iterator](); 132 | }, 133 | n: function () { 134 | var step = it.next(); 135 | normalCompletion = step.done; 136 | return step; 137 | }, 138 | e: function (e) { 139 | didErr = true; 140 | err = e; 141 | }, 142 | f: function () { 143 | try { 144 | if (!normalCompletion && it.return != null) it.return(); 145 | } finally { 146 | if (didErr) throw err; 147 | } 148 | } 149 | }; 150 | } 151 | 152 | var inputComponent = (function (config) { 153 | config.inputField.setAttribute("role", "combobox"); 154 | config.inputField.setAttribute("aria-haspopup", true); 155 | config.inputField.setAttribute("aria-expanded", false); 156 | config.inputField.setAttribute("aria-controls", config.resultsList.idName); 157 | config.inputField.setAttribute("aria-autocomplete", "both"); 158 | }); 159 | 160 | var createList = (function (config) { 161 | var list = document.createElement(config.resultsList.element); 162 | list.setAttribute("id", config.resultsList.idName); 163 | list.setAttribute("aria-label", config.name); 164 | list.setAttribute("class", config.resultsList.className); 165 | list.setAttribute("role", "listbox"); 166 | list.setAttribute("tabindex", "-1"); 167 | if (config.resultsList.container) config.resultsList.container(list); 168 | var destination = "string" === typeof config.resultsList.destination ? document.querySelector(config.resultsList.destination) : config.resultsList.destination(); 169 | destination.insertAdjacentElement(config.resultsList.position, list); 170 | return list; 171 | }); 172 | 173 | var createItem = (function (item, index, config) { 174 | var result = document.createElement(config.resultItem.element); 175 | result.setAttribute("id", "".concat(config.resultItem.idName, "_").concat(index)); 176 | result.setAttribute("class", config.resultItem.className); 177 | result.setAttribute("role", "option"); 178 | result.innerHTML = item.match; 179 | if (config.resultItem.content) config.resultItem.content(item, result); 180 | return result; 181 | }); 182 | 183 | var closeAllLists = function closeAllLists(config, element) { 184 | var list = document.getElementsByClassName(config.resultsList.className); 185 | for (var index = 0; index < list.length; index++) { 186 | if (element !== list[index] && element !== config.inputField) list[index].parentNode.removeChild(list[index]); 187 | } 188 | config.inputField.removeAttribute("aria-activedescendant"); 189 | config.inputField.setAttribute("aria-expanded", false); 190 | }; 191 | var generateList = function generateList(config, data, matches) { 192 | var list = createList(config); 193 | config.inputField.setAttribute("aria-expanded", true); 194 | var _loop = function _loop(index) { 195 | var item = data.results[index]; 196 | var resultItem = createItem(item, index, config); 197 | resultItem.addEventListener("click", function (event) { 198 | var dataFeedback = { 199 | event: event, 200 | matches: matches, 201 | input: data.input, 202 | query: data.query, 203 | results: data.results, 204 | selection: _objectSpread2(_objectSpread2({}, item), {}, { 205 | index: index 206 | }) 207 | }; 208 | if (config.onSelection) config.onSelection(dataFeedback); 209 | }); 210 | list.appendChild(resultItem); 211 | }; 212 | for (var index = 0; index < data.results.length; index++) { 213 | _loop(index); 214 | } 215 | return list; 216 | }; 217 | 218 | var eventEmitter = (function (target, detail, name) { 219 | target.dispatchEvent(new CustomEvent(name, { 220 | bubbles: true, 221 | detail: detail, 222 | cancelable: true 223 | })); 224 | }); 225 | 226 | var navigate = function navigate(config, dataFeedback) { 227 | var currentFocus = -1; 228 | var update = function update(event, list, state, config) { 229 | event.preventDefault(); 230 | if (state) { 231 | currentFocus++; 232 | } else { 233 | currentFocus--; 234 | } 235 | addActive(list); 236 | config.inputField.setAttribute("aria-activedescendant", list[currentFocus].id); 237 | eventEmitter(event.srcElement, _objectSpread2(_objectSpread2({ 238 | event: event 239 | }, dataFeedback), {}, { 240 | selection: dataFeedback.results[currentFocus] 241 | }), "navigation"); 242 | }; 243 | var removeActive = function removeActive(list) { 244 | for (var index = 0; index < list.length; index++) { 245 | list[index].removeAttribute("aria-selected"); 246 | list[index].classList.remove("autoComplete_selected"); 247 | } 248 | }; 249 | var addActive = function addActive(list) { 250 | if (!list) return false; 251 | removeActive(list); 252 | if (currentFocus >= list.length) currentFocus = 0; 253 | if (currentFocus < 0) currentFocus = list.length - 1; 254 | list[currentFocus].setAttribute("aria-selected", "true"); 255 | list[currentFocus].classList.add("autoComplete_selected"); 256 | }; 257 | var navigation = function navigation(event) { 258 | var list = document.getElementById(config.resultsList.idName); 259 | if (!list) return config.inputField.removeEventListener("keydown", navigate); 260 | list = list.getElementsByTagName(config.resultItem.element); 261 | if (event.keyCode === 27) { 262 | config.inputField.value = ""; 263 | closeAllLists(config); 264 | } else if (event.keyCode === 40 || event.keyCode === 9) { 265 | update(event, list, true, config); 266 | } else if (event.keyCode === 38 || event.keyCode === 9) { 267 | update(event, list, false, config); 268 | } else if (event.keyCode === 13) { 269 | event.preventDefault(); 270 | if (currentFocus > -1) { 271 | if (list) list[currentFocus].click(); 272 | } 273 | } 274 | }; 275 | var navigate = config.resultsList.navigation || navigation; 276 | if (config.inputField.autoCompleteNavigate) config.inputField.removeEventListener("keydown", config.inputField.autoCompleteNavigate); 277 | config.inputField.autoCompleteNavigate = navigate; 278 | config.inputField.addEventListener("keydown", navigate); 279 | }; 280 | 281 | var searchEngine = (function (query, record, config) { 282 | var recordLowerCase = config.diacritics ? record.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").normalize("NFC") : record.toLowerCase(); 283 | if (config.searchEngine === "loose") { 284 | query = query.replace(/ /g, ""); 285 | var match = []; 286 | var searchPosition = 0; 287 | for (var number = 0; number < recordLowerCase.length; number++) { 288 | var recordChar = record[number]; 289 | if (searchPosition < query.length && recordLowerCase[number] === query[searchPosition]) { 290 | recordChar = config.highlight ? "".concat(recordChar, "") : recordChar; 291 | searchPosition++; 292 | } 293 | match.push(recordChar); 294 | } 295 | if (searchPosition === query.length) { 296 | return match.join(""); 297 | } 298 | } else { 299 | if (recordLowerCase.includes(query)) { 300 | var pattern = new RegExp("".concat(query), "i"); 301 | query = pattern.exec(record); 302 | var _match = config.highlight ? record.replace(query, "".concat(query, "")) : record; 303 | return _match; 304 | } 305 | } 306 | }); 307 | 308 | var getInputValue = function getInputValue(inputField) { 309 | return inputField instanceof HTMLInputElement || inputField instanceof HTMLTextAreaElement ? inputField.value.toLowerCase() : inputField.innerHTML.toLowerCase(); 310 | }; 311 | var prepareQueryValue = function prepareQueryValue(inputValue, config) { 312 | return config.query && config.query.manipulate ? config.query.manipulate(inputValue) : config.diacritics ? inputValue.normalize("NFD").replace(/[\u0300-\u036f]/g, "").normalize("NFC") : inputValue; 313 | }; 314 | var checkTriggerCondition = function checkTriggerCondition(config, queryValue) { 315 | return config.trigger.condition ? config.trigger.condition(queryValue) : queryValue.length >= config.threshold && queryValue.replace(/ /g, "").length; 316 | }; 317 | var listMatchingResults = function listMatchingResults(config, query) { 318 | var resList = []; 319 | var _loop = function _loop(index) { 320 | var record = config.data.store[index]; 321 | var search = function search(key) { 322 | var recordValue = (key ? record[key] : record).toString(); 323 | if (recordValue) { 324 | var match = typeof config.searchEngine === "function" ? config.searchEngine(query, recordValue) : searchEngine(query, recordValue, config); 325 | if (match && key) { 326 | resList.push({ 327 | key: key, 328 | index: index, 329 | match: match, 330 | value: record 331 | }); 332 | } else if (match && !key) { 333 | resList.push({ 334 | index: index, 335 | match: match, 336 | value: record 337 | }); 338 | } 339 | } 340 | }; 341 | if (config.data.key) { 342 | var _iterator = _createForOfIteratorHelper(config.data.key), 343 | _step; 344 | try { 345 | for (_iterator.s(); !(_step = _iterator.n()).done;) { 346 | var key = _step.value; 347 | search(key); 348 | } 349 | } catch (err) { 350 | _iterator.e(err); 351 | } finally { 352 | _iterator.f(); 353 | } 354 | } else { 355 | search(); 356 | } 357 | }; 358 | for (var index = 0; index < config.data.store.length; index++) { 359 | _loop(index); 360 | } 361 | var list = config.sort ? resList.sort(config.sort) : resList; 362 | return list; 363 | }; 364 | 365 | var debouncer = (function (callback, delay) { 366 | var inDebounce; 367 | return function () { 368 | var context = this; 369 | var args = arguments; 370 | clearTimeout(inDebounce); 371 | inDebounce = setTimeout(function () { 372 | return callback.apply(context, args); 373 | }, delay); 374 | }; 375 | }); 376 | 377 | var autoComplete = function () { 378 | function autoComplete(config) { 379 | _classCallCheck(this, autoComplete); 380 | var _config$name = config.name, 381 | name = _config$name === void 0 ? "Search" : _config$name, 382 | _config$selector = config.selector, 383 | selector = _config$selector === void 0 ? "#autoComplete" : _config$selector, 384 | _config$observer = config.observer, 385 | observer = _config$observer === void 0 ? false : _config$observer, 386 | _config$data = config.data, 387 | src = _config$data.src, 388 | key = _config$data.key, 389 | _config$data$cache = _config$data.cache, 390 | cache = _config$data$cache === void 0 ? false : _config$data$cache, 391 | store = _config$data.store, 392 | results = _config$data.results, 393 | query = config.query, 394 | _config$trigger = config.trigger; 395 | _config$trigger = _config$trigger === void 0 ? {} : _config$trigger; 396 | var _config$trigger$event = _config$trigger.event, 397 | event = _config$trigger$event === void 0 ? ["input"] : _config$trigger$event, 398 | _config$trigger$condi = _config$trigger.condition, 399 | condition = _config$trigger$condi === void 0 ? false : _config$trigger$condi, 400 | _config$searchEngine = config.searchEngine, 401 | searchEngine = _config$searchEngine === void 0 ? "strict" : _config$searchEngine, 402 | _config$diacritics = config.diacritics, 403 | diacritics = _config$diacritics === void 0 ? false : _config$diacritics, 404 | _config$threshold = config.threshold, 405 | threshold = _config$threshold === void 0 ? 1 : _config$threshold, 406 | _config$debounce = config.debounce, 407 | debounce = _config$debounce === void 0 ? 0 : _config$debounce, 408 | _config$resultsList = config.resultsList; 409 | _config$resultsList = _config$resultsList === void 0 ? {} : _config$resultsList; 410 | var _config$resultsList$r = _config$resultsList.render, 411 | render = _config$resultsList$r === void 0 ? true : _config$resultsList$r, 412 | _config$resultsList$c = _config$resultsList.container, 413 | container = _config$resultsList$c === void 0 ? false : _config$resultsList$c, 414 | destination = _config$resultsList.destination, 415 | _config$resultsList$p = _config$resultsList.position, 416 | position = _config$resultsList$p === void 0 ? "afterend" : _config$resultsList$p, 417 | _config$resultsList$e = _config$resultsList.element, 418 | resultsListElement = _config$resultsList$e === void 0 ? "ul" : _config$resultsList$e, 419 | _config$resultsList$i = _config$resultsList.idName, 420 | resultsListId = _config$resultsList$i === void 0 ? "autoComplete_list" : _config$resultsList$i, 421 | _config$resultsList$c2 = _config$resultsList.className, 422 | resultsListClass = _config$resultsList$c2 === void 0 ? "autoComplete_list" : _config$resultsList$c2, 423 | _config$resultsList$n = _config$resultsList.navigation, 424 | navigation = _config$resultsList$n === void 0 ? false : _config$resultsList$n, 425 | _config$sort = config.sort, 426 | sort = _config$sort === void 0 ? false : _config$sort, 427 | placeHolder = config.placeHolder, 428 | _config$maxResults = config.maxResults, 429 | maxResults = _config$maxResults === void 0 ? 5 : _config$maxResults, 430 | _config$resultItem = config.resultItem; 431 | _config$resultItem = _config$resultItem === void 0 ? {} : _config$resultItem; 432 | var _config$resultItem$co = _config$resultItem.content, 433 | content = _config$resultItem$co === void 0 ? false : _config$resultItem$co, 434 | _config$resultItem$el = _config$resultItem.element, 435 | resultItemElement = _config$resultItem$el === void 0 ? "li" : _config$resultItem$el, 436 | _config$resultItem$id = _config$resultItem.idName, 437 | resultItemId = _config$resultItem$id === void 0 ? "autoComplete_result" : _config$resultItem$id, 438 | _config$resultItem$cl = _config$resultItem.className, 439 | resultItemClass = _config$resultItem$cl === void 0 ? "autoComplete_result" : _config$resultItem$cl, 440 | noResults = config.noResults, 441 | _config$highlight = config.highlight, 442 | highlight = _config$highlight === void 0 ? false : _config$highlight, 443 | feedback = config.feedback, 444 | onSelection = config.onSelection; 445 | this.name = name; 446 | this.selector = selector; 447 | this.observer = observer; 448 | this.data = { 449 | src: src, 450 | key: key, 451 | cache: cache, 452 | store: store, 453 | results: results 454 | }; 455 | this.query = query; 456 | this.trigger = { 457 | event: event, 458 | condition: condition 459 | }; 460 | this.searchEngine = searchEngine; 461 | this.diacritics = diacritics; 462 | this.threshold = threshold; 463 | this.debounce = debounce; 464 | this.resultsList = { 465 | render: render, 466 | container: container, 467 | destination: destination || this.selector, 468 | position: position, 469 | element: resultsListElement, 470 | idName: resultsListId, 471 | className: resultsListClass, 472 | navigation: navigation 473 | }; 474 | this.sort = sort; 475 | this.placeHolder = placeHolder; 476 | this.maxResults = maxResults; 477 | this.resultItem = { 478 | content: content, 479 | element: resultItemElement, 480 | idName: resultItemId, 481 | className: resultItemClass 482 | }; 483 | this.noResults = noResults; 484 | this.highlight = highlight; 485 | this.feedback = feedback; 486 | this.onSelection = onSelection; 487 | this.inputField = typeof this.selector === "string" ? document.querySelector(this.selector) : this.selector(); 488 | this.observer ? this.preInit() : this.init(); 489 | } 490 | _createClass(autoComplete, [{ 491 | key: "start", 492 | value: function start(input, query) { 493 | var _this = this; 494 | var results = this.data.results ? this.data.results(listMatchingResults(this, query)) : listMatchingResults(this, query); 495 | var dataFeedback = { 496 | input: input, 497 | query: query, 498 | matches: results, 499 | results: results.slice(0, this.maxResults) 500 | }; 501 | eventEmitter(this.inputField, dataFeedback, "results"); 502 | if (!results.length) return this.noResults ? this.noResults(dataFeedback, generateList) : null; 503 | if (!this.resultsList.render) return this.feedback(dataFeedback); 504 | results.length ? generateList(this, dataFeedback, results) : null; 505 | eventEmitter(this.inputField, dataFeedback, "rendered"); 506 | navigate(this, dataFeedback); 507 | document.addEventListener("click", function (event) { 508 | return closeAllLists(_this, event.target); 509 | }); 510 | } 511 | }, { 512 | key: "dataStore", 513 | value: function dataStore() { 514 | var _this2 = this; 515 | return new Promise(function ($return, $error) { 516 | if (_this2.data.cache && _this2.data.store) return $return(null); 517 | return new Promise(function ($return, $error) { 518 | if (typeof _this2.data.src === "function") { 519 | return _this2.data.src().then($return, $error); 520 | } 521 | return $return(_this2.data.src); 522 | }).then(function ($await_5) { 523 | try { 524 | _this2.data.store = $await_5; 525 | eventEmitter(_this2.inputField, _this2.data.store, "fetch"); 526 | return $return(); 527 | } catch ($boundEx) { 528 | return $error($boundEx); 529 | } 530 | }, $error); 531 | }); 532 | } 533 | }, { 534 | key: "compose", 535 | value: function compose() { 536 | var _this3 = this; 537 | return new Promise(function ($return, $error) { 538 | var input, query, triggerCondition; 539 | input = getInputValue(_this3.inputField); 540 | query = prepareQueryValue(input, _this3); 541 | triggerCondition = checkTriggerCondition(_this3, query); 542 | if (triggerCondition) { 543 | return _this3.dataStore().then(function ($await_6) { 544 | try { 545 | closeAllLists(_this3); 546 | _this3.start(input, query); 547 | return $If_3.call(_this3); 548 | } catch ($boundEx) { 549 | return $error($boundEx); 550 | } 551 | }, $error); 552 | } else { 553 | closeAllLists(_this3); 554 | return $If_3.call(_this3); 555 | } 556 | function $If_3() { 557 | return $return(); 558 | } 559 | }); 560 | } 561 | }, { 562 | key: "init", 563 | value: function init() { 564 | var _this4 = this; 565 | inputComponent(this); 566 | if (this.placeHolder) this.inputField.setAttribute("placeholder", this.placeHolder); 567 | this.hook = debouncer(function () { 568 | _this4.compose(); 569 | }, this.debounce); 570 | this.trigger.event.forEach(function (eventType) { 571 | _this4.inputField.removeEventListener(eventType, _this4.hook); 572 | _this4.inputField.addEventListener(eventType, _this4.hook); 573 | }); 574 | eventEmitter(this.inputField, null, "init"); 575 | } 576 | }, { 577 | key: "preInit", 578 | value: function preInit() { 579 | var _this5 = this; 580 | var config = { 581 | childList: true, 582 | subtree: true 583 | }; 584 | var callback = function callback(mutationsList, observer) { 585 | var _iterator = _createForOfIteratorHelper(mutationsList), 586 | _step; 587 | try { 588 | for (_iterator.s(); !(_step = _iterator.n()).done;) { 589 | var mutation = _step.value; 590 | if (_this5.inputField) { 591 | observer.disconnect(); 592 | eventEmitter(_this5.inputField, null, "connect"); 593 | _this5.init(); 594 | } 595 | } 596 | } catch (err) { 597 | _iterator.e(err); 598 | } finally { 599 | _iterator.f(); 600 | } 601 | }; 602 | var observer = new MutationObserver(callback); 603 | observer.observe(document, config); 604 | } 605 | }, { 606 | key: "unInit", 607 | value: function unInit() { 608 | this.inputField.removeEventListener("input", this.hook); 609 | eventEmitter(this.inputField, null, "unInit"); 610 | } 611 | }]); 612 | return autoComplete; 613 | }(); 614 | 615 | return autoComplete; 616 | 617 | }))); 618 | -------------------------------------------------------------------------------- /dist/db/test.json: -------------------------------------------------------------------------------- 1 | [ 2 | "Sauce - Thousand Island", 3 | "Wild Boar - Tenderloin", 4 | "Goat - Whole Cut", 5 | "Cherries - Bing, Canned", 6 | "Table Cloth 62x120 Colour", 7 | "Bag - Clear 7 Lb", 8 | "Kippers - Smoked", 9 | "Pasta - Ravioli", 10 | "Capers - Pickled", 11 | "Ice Cream Bar - Oreo Sandwich", 12 | "Worcestershire Sauce", 13 | "Chicken - Whole", 14 | "Cabbage - Red", 15 | "Crab - Meat", 16 | "Star Anise, Whole", 17 | "Wine - Cave Springs Dry Riesling", 18 | "Sobe - Orange Carrot", 19 | "Longos - Chicken Cordon Bleu", 20 | "Soup - Campbells Chicken", 21 | "Vermouth - Sweet, Cinzano", 22 | "V8 - Tropical Blend", 23 | "Wine - Casillero Deldiablo", 24 | "Remy Red Berry Infusion", 25 | "Danishes - Mini Raspberry", 26 | "Pork - Suckling Pig", 27 | "Flour - Masa De Harina Mexican", 28 | "Puree - Raspberry", 29 | "Hot Chocolate - Individual", 30 | "Wine - Magnotta - Pinot Gris Sr", 31 | "Chicken - Whole Fryers", 32 | "Lobster - Base", 33 | "Foil - 4oz Custard Cup", 34 | "Pie Filling - Apple", 35 | "Vermouth - Sweet, Cinzano", 36 | "Bread - Corn Muffaletta", 37 | "Jam - Blackberry, 20 Ml Jar", 38 | "Pie Pecan", 39 | "Soup - Tomato Mush. Florentine", 40 | "Beer - True North Strong Ale", 41 | "Lemonade - Island Tea, 591 Ml", 42 | "Grenadine", 43 | "Bonito Flakes - Toku Katsuo", 44 | "Coconut Milk - Unsweetened", 45 | "Red Snapper - Fillet, Skin On", 46 | "Potatoes - Idaho 80 Count", 47 | "Table Cloth - 53x69 Colour", 48 | "Cloves - Ground", 49 | "Lychee - Canned", 50 | "Appetizer - Assorted Box", 51 | "Bread - Petit Baguette", 52 | "Sproutsmustard Cress", 53 | "Squid Ink", 54 | "Puree - Kiwi", 55 | "Coffee - Beans, Whole", 56 | "Sea Urchin", 57 | "Placemat - Scallop, White", 58 | "Coriander - Seed", 59 | "Bread - Crusty Italian Poly", 60 | "Veal - Round, Eye Of", 61 | "Cheese - Grana Padano", 62 | "Lighter - Bbq", 63 | "Pastry - Carrot Muffin - Mini", 64 | "Crab - Soft Shell", 65 | "Lettuce - Romaine, Heart", 66 | "Roe - Lump Fish, Red", 67 | "Soup - Campbells, Creamy", 68 | "Schnappes Peppermint - Walker", 69 | "Sunflower Seed Raw", 70 | "Longos - Lasagna Beef", 71 | "Oil - Shortening,liqud, Fry", 72 | "Cake - Cake Sheet Macaroon", 73 | "Rabbit - Saddles", 74 | "Pasta - Cheese / Spinach Bauletti", 75 | "Container - Foam Dixie 12 Oz", 76 | "Wine - Alsace Gewurztraminer", 77 | "Mcguinness - Blue Curacao", 78 | "Smoked Paprika", 79 | "Kellogs Cereal In A Cup", 80 | "Jolt Cola - Red Eye", 81 | "Lobster - Canned Premium", 82 | "Cheese - Taleggio D.o.p.", 83 | "Gingerale - Schweppes, 355 Ml", 84 | "Calypso - Pineapple Passion", 85 | "Bread - Roll, Calabrese", 86 | "Anchovy Paste - 56 G Tube", 87 | "Bread - 10 Grain Parisian", 88 | "Wine - White, Riesling, Semi - Dry", 89 | "Ecolab - Ster Bac", 90 | "Napkin - Beverage 1 Ply", 91 | "Wine - Taylors Reserve", 92 | "Pork - Butt, Boneless", 93 | "Melon - Honey Dew", 94 | "Curry Powder Madras", 95 | "Leeks - Large", 96 | "Pastry - Trippleberry Muffin - Mini", 97 | "Appetizer - Smoked Salmon / Dill", 98 | "Soup - Cream Of Broccoli", 99 | "Pepper - White, Ground", 100 | "Lemon Tarts", 101 | "Beer - Paulaner Hefeweisse", 102 | "Mountain Dew", 103 | "Paper Towel Touchless", 104 | "Pizza Pizza Dough", 105 | "Cinnamon Rolls", 106 | "Bread - Raisin Walnut Oval", 107 | "Lid Coffee Cup 8oz Blk", 108 | "Pancetta", 109 | "Cookies - Englishbay Oatmeal", 110 | "Cinnamon - Ground", 111 | "Tea - Herbal - 6 Asst", 112 | "Clams - Littleneck, Whole", 113 | "Chinese Foods - Thick Noodles", 114 | "Icecream Bar - Del Monte", 115 | "Beef - Eye Of Round", 116 | "Soup - Base Broth Beef", 117 | "Cheese - Parmigiano Reggiano", 118 | "Dikon", 119 | "Carbonated Water - Raspberry", 120 | "Wine - Chateau Aqueria Tavel", 121 | "Apple - Granny Smith", 122 | "Madeira", 123 | "Sauce - Plum", 124 | "Cake - Dulce De Leche", 125 | "Jam - Apricot", 126 | "Milk - 2%", 127 | "Pie Shells 10", 128 | "Tea - Herbal - 6 Asst", 129 | "Flower - Dish Garden", 130 | "Wine - Red, Mosaic Zweigelt", 131 | "Carbonated Water - Blackberry", 132 | "Almonds Ground Blanched", 133 | "Cheese - Swiss", 134 | "Paper - Brown Paper Mini Cups", 135 | "Soup Campbells", 136 | "Wine - Winzer Krems Gruner", 137 | "Wine - Segura Viudas Aria Brut", 138 | "Sugar - Brown, Individual", 139 | "Wine - Alsace Riesling Reserve", 140 | "Bread - Pumpernickel", 141 | "Lamb - Bones", 142 | "Beef - Roasted, Cooked", 143 | "Crackers - Graham", 144 | "Vodka - Smirnoff", 145 | "Bay Leaf Ground", 146 | "Berry Brulee", 147 | "Table Cloth 91x91 Colour", 148 | "Oil - Canola", 149 | "Sauce - Oyster", 150 | "Sausage - Breakfast", 151 | "Cookies - Assorted", 152 | "Wine - Barossa Valley Estate", 153 | "Capon - Breast, Wing On", 154 | "Cabbage - Savoy", 155 | "Cheese - Cheddar With Claret", 156 | "Lamb - Whole, Fresh", 157 | "Icecream - Dstk Cml And Fdg", 158 | "Initation Crab Meat", 159 | "Cake - Night And Day Choclate", 160 | "Cranberry Foccacia", 161 | "Beef - Tenderloin Tails", 162 | "Creme De Menth - White", 163 | "Olives - Green, Pitted", 164 | "Crab - Back Fin Meat, Canned", 165 | "Knife Plastic - White", 166 | "Dc - Sakura Fu", 167 | "Grapes - Green", 168 | "Ice Cream - Chocolate", 169 | "Mangoes", 170 | "Doilies - 12, Paper", 171 | "Broom - Push", 172 | "Pasta - Detalini, White, Fresh", 173 | "Guava", 174 | "Corn Syrup", 175 | "Lotus Rootlets - Canned", 176 | "Longos - Grilled Chicken With", 177 | "Pepsi - Diet, 355 Ml", 178 | "Swiss Chard", 179 | "Red Snapper - Fillet, Skin On", 180 | "Soupcontfoam16oz 116con", 181 | "Pastry - Butterscotch Baked", 182 | "Cocoa Butter", 183 | "Squash - Butternut", 184 | "Steam Pan Full Lid", 185 | "Lemons", 186 | "Beans - Yellow", 187 | "Lettuce - Iceberg", 188 | "Beef - Top Butt", 189 | "Lemons", 190 | "Soup - Verve - Chipotle Chicken", 191 | "Soup - Knorr, Chicken Gumbo", 192 | "Blackberries", 193 | "Tumeric", 194 | "Yokaline", 195 | "Prunes - Pitted", 196 | "Pastry - Chocolate Chip Muffin", 197 | "Garlic Powder", 198 | "Chicken - Bones", 199 | "Soup - Campbells, Beef Barley", 200 | "Quail - Jumbo Boneless", 201 | "Crawfish", 202 | "Allspice - Jamaican", 203 | "Muffin Mix - Blueberry", 204 | "Magnotta Bel Paese Red", 205 | "Coconut - Shredded, Unsweet", 206 | "Appetizer - Shrimp Puff", 207 | "Squid - Breaded", 208 | "Lobster - Tail 6 Oz", 209 | "Doilies - 12, Paper", 210 | "Pur Value", 211 | "Lettuce - Boston Bib - Organic", 212 | "Sesame Seed", 213 | "Seabream Whole Farmed", 214 | "Potatoes - Idaho 100 Count", 215 | "Kiwi Gold Zespri", 216 | "Nantucket Pine Orangebanana", 217 | "Napkin - Dinner, White", 218 | "Lemonade - Kiwi, 591 Ml", 219 | "Five Alive Citrus", 220 | "Beef Dry Aged Tenderloin Aaa", 221 | "Tea - Green", 222 | "Coffee Guatemala Dark", 223 | "Whmis Spray Bottle Graduated", 224 | "Mushroom - Portebello", 225 | "Cookies Cereal Nut", 226 | "Tea - Honey Green Tea", 227 | "Sauce - Chili", 228 | "Juice - Pineapple, 48 Oz", 229 | "Sprouts - Bean", 230 | "Pork Casing", 231 | "Soap - Pine Sol Floor Cleaner", 232 | "Gelatine Powder", 233 | "Sauce - Apple, Unsweetened", 234 | "Sauce - Roasted Red Pepper", 235 | "Sauce - Fish 25 Ozf Bottle", 236 | "Beans - Black Bean, Canned", 237 | "Pate Pans Yellow", 238 | "Bread - Roll, Soft White Round", 239 | "Tomato - Plum With Basil", 240 | "Pork - Bacon,back Peameal", 241 | "Port - 74 Brights", 242 | "Mustard - Individual Pkg", 243 | "Cheese - Grana Padano", 244 | "Wine - Casablanca Valley", 245 | "Rhubarb", 246 | "Shallots", 247 | "Crackers - Graham", 248 | "Sugar - Fine", 249 | "Ice Cream Bar - Drumstick", 250 | "Muffin - Mix - Bran And Maple 15l", 251 | "Cauliflower", 252 | "Truffle Shells - Semi - Sweet", 253 | "Leeks - Baby, White", 254 | "Wine - Chateau Aqueria Tavel", 255 | "Cumin - Whole", 256 | "Clementine", 257 | "Oil - Safflower", 258 | "Pepper - Gypsy Pepper", 259 | "Island Oasis - Lemonade", 260 | "Champagne - Brights, Dry", 261 | "Wasabi Paste", 262 | "Chilli Paste, Ginger Garlic", 263 | "Tea - Herbal - 6 Asst", 264 | "Veal - Shank, Pieces", 265 | "Olives - Morracan Dired", 266 | "Cabbage Roll", 267 | "Cheese - St. Paulin", 268 | "Flower - Potmums", 269 | "Potatoes - Mini White 3 Oz", 270 | "Wine - Blue Nun Qualitatswein", 271 | "Parsnip", 272 | "Soup Campbells Split Pea And Ham", 273 | "Garlic Powder", 274 | "Bread - Multigrain Oval", 275 | "Pork - Chop, Frenched", 276 | "Appetizer - Mango Chevre", 277 | "Oil - Shortening - All - Purpose", 278 | "Cheese - Brick With Pepper", 279 | "Chick Peas - Dried", 280 | "Yoghurt Tubes", 281 | "Longos - Lasagna Veg", 282 | "Sobe - Orange Carrot", 283 | "Lemon Balm - Fresh", 284 | "Wine - Clavet Saint Emilion", 285 | "Chinese Foods - Chicken Wing", 286 | "Sobe - Green Tea", 287 | "Pepper - Scotch Bonnet", 288 | "Clams - Bay", 289 | "Tabasco Sauce, 2 Oz", 290 | "Soup - Campbells, Spinach Crm", 291 | "Chicken - Ground", 292 | "Pork - Side Ribs", 293 | "Sauce - Sesame Thai Dressing", 294 | "Onions - White", 295 | "Pork - Sausage Casing", 296 | "Wine - Red, Gamay Noir", 297 | "Beef - Ground Lean Fresh", 298 | "Ginger - Fresh", 299 | "Soup - Campbells Broccoli", 300 | "Bread - White Epi Baguette", 301 | "Sobe - Berry Energy", 302 | "Cheese - Brie Roitelet", 303 | "Wine - Delicato Merlot", 304 | "Mushroom - Chanterelle, Dry", 305 | "Laundry - Bag Cloth", 306 | "Wasabi Powder", 307 | "Cake - Mini Cheesecake", 308 | "Cheese - Goat With Herbs", 309 | "Bay Leaf", 310 | "Madeira", 311 | "Steamers White", 312 | "Apple - Royal Gala", 313 | "Bread Roll Foccacia", 314 | "Bread Crumbs - Panko", 315 | "Soup - Tomato Mush. Florentine", 316 | "Pail For Lid 1537", 317 | "Sour Cream", 318 | "Dr. Pepper - 355ml", 319 | "Yogurt - Cherry, 175 Gr", 320 | "Crackers - Soda / Saltins", 321 | "Soup - French Onion, Dry", 322 | "Trueblue - Blueberry 12x473ml", 323 | "Beef - Short Loin", 324 | "Wine - Savigny - Les - Beaune", 325 | "Onions - Pearl", 326 | "Water - Spring Water 500ml", 327 | "Radish - Pickled", 328 | "Crab - Meat", 329 | "Fennel - Seeds", 330 | "Cheese - St. Andre", 331 | "Doilies - 5, Paper", 332 | "Sambuca Cream", 333 | "Scallops - Live In Shell", 334 | "Veal - Eye Of Round", 335 | "Gatorade - Xfactor Berry", 336 | "Glucose", 337 | "Bread - Bistro White", 338 | "Pear - Prickly", 339 | "Chocolate - Semi Sweet", 340 | "Chicken - Leg / Back Attach", 341 | "Spice - Onion Powder Granulated", 342 | "Shrimp - Black Tiger 26/30", 343 | "Creme De Cacao Mcguines", 344 | "Melon - Watermelon Yellow", 345 | "Wine - Marlbourough Sauv Blanc", 346 | "Flour - Chickpea", 347 | "Jam - Raspberry,jar", 348 | "Bay Leaf", 349 | "Wine - Chardonnay South", 350 | "Juice - Lime", 351 | "Syrup - Monin - Granny Smith", 352 | "Olives - Black, Pitted", 353 | "Bread Foccacia Whole", 354 | "Lentils - Green Le Puy", 355 | "Coriander - Ground", 356 | "Beef - Diced", 357 | "Steel Wool S.o.s", 358 | "Flour - Teff", 359 | "Towel Dispenser", 360 | "Currants", 361 | "Olives - Nicoise", 362 | "Pepper - Chili Powder", 363 | "Sproutsmustard Cress", 364 | "Campari", 365 | "Sauce - Mint", 366 | "Squid U5 - Thailand", 367 | "Pork Casing", 368 | "Cut Wakame - Hanawakaba", 369 | "Yucca", 370 | "Smoked Tongue", 371 | "Magnotta Bel Paese Red", 372 | "Chicken - Whole", 373 | "Rum - Coconut, Malibu", 374 | "Sauce - Hoisin", 375 | "Extract - Raspberry", 376 | "Ginsing - Fresh", 377 | "Table Cloth 81x81 Colour", 378 | "Sprite, Diet - 355ml", 379 | "Pepper - Chilli Seeds Mild", 380 | "Lid Coffee Cup 8oz Blk", 381 | "Syrup - Monin - Granny Smith", 382 | "Beef - Tenderloin - Aa", 383 | "Chinese Foods - Cantonese", 384 | "Juice - Apple, 341 Ml", 385 | "Grapefruit - Pink", 386 | "Tart Shells - Barquettes, Savory", 387 | "Shrimp - Baby, Warm Water", 388 | "Chocolate Bar - Coffee Crisp", 389 | "Sambuca - Opal Nera", 390 | "Tea - Apple Green Tea", 391 | "Sauce - Caesar Dressing", 392 | "Food Colouring - Orange", 393 | "Wine - Chenin Blanc K.w.v.", 394 | "Olives - Stuffed", 395 | "Flour - Fast / Rapid", 396 | "Sauce - Caesar Dressing", 397 | "Shark - Loin", 398 | "Filling - Mince Meat", 399 | "Pimento - Canned", 400 | "Beef - Bones, Marrow", 401 | "Tarts Assorted", 402 | "Muffin Mix - Oatmeal", 403 | "Muffin Batt - Blueberry Passion", 404 | "Creme De Cacao White", 405 | "Beer - Labatt Blue", 406 | "Cheese - Mozzarella, Shredded", 407 | "Wine - Saint Emilion Calvet", 408 | "Fish - Base, Bouillion", 409 | "Cookie Choc", 410 | "Pepper - Roasted Red", 411 | "Mushroom - Morels, Dry", 412 | "Soup - Campbells Pasta Fagioli", 413 | "Wine - Saint - Bris 2002, Sauv", 414 | "Cabbage - Red", 415 | "Melon - Honey Dew", 416 | "Wine - Chardonnay Mondavi", 417 | "Pasta - Gnocchi, Potato", 418 | "Oven Mitts 17 Inch", 419 | "Schnappes - Peach, Walkers", 420 | "Truffle Cups - White Paper", 421 | "Wine - Red, Colio Cabernet", 422 | "Mushroom - Porcini Frozen", 423 | "Cheese - Montery Jack", 424 | "Foil - Round Foil", 425 | "Potatoes - Pei 10 Oz", 426 | "Longos - Burritos", 427 | "Wine - Masi Valpolocell", 428 | "Juice - Propel Sport", 429 | "Cheese Cheddar Processed", 430 | "Food Colouring - Orange", 431 | "Longos - Grilled Veg Sandwiches", 432 | "Macaroons - Homestyle Two Bit", 433 | "Juice - Apple 284ml", 434 | "Yogurt - Banana, 175 Gr", 435 | "Sea Bass - Fillets", 436 | "Lamb Rack - Ontario", 437 | "Eggroll", 438 | "Fiddlehead - Frozen", 439 | "Salt - Rock, Course", 440 | "Pork - Sausage Casing", 441 | "Compound - Orange", 442 | "Beans - Navy, Dry", 443 | "Pastry - Trippleberry Muffin - Mini", 444 | "Juice - V8 Splash", 445 | "Chocolate Liqueur - Godet White", 446 | "Pork - Belly Fresh", 447 | "Potatoes - Fingerling 4 Oz", 448 | "Longan", 449 | "Capers - Pickled", 450 | "Turnip - Mini", 451 | "Egg - Salad Premix", 452 | "Towel Dispenser", 453 | "Wine - Taylors Reserve", 454 | "Halibut - Whole, Fresh", 455 | "Longos - Grilled Salmon With Bbq", 456 | "Oregano - Dry, Rubbed", 457 | "Pepper - Scotch Bonnet", 458 | "Jolt Cola - Red Eye", 459 | "Sugar - Monocystal / Rock", 460 | "Wooden Mop Handle", 461 | "Sausage - Meat", 462 | "Juice - Cranberry 284ml", 463 | "Cake Circle, Foil, Scallop", 464 | "Ecolab - Orange Frc, Cleaner", 465 | "Seedlings - Buckwheat, Organic", 466 | "Vinegar - Balsamic, White", 467 | "Iced Tea - Lemon, 460 Ml", 468 | "Lettuce - Red Leaf", 469 | "Cookie Dough - Chocolate Chip", 470 | "Sausage - Meat", 471 | "Garam Masala Powder", 472 | "Squid - Breaded", 473 | "Spring Roll Veg Mini", 474 | "Chocolate Eclairs", 475 | "Lamb - Shoulder, Boneless", 476 | "Lighter - Bbq", 477 | "Stock - Beef, White", 478 | "Wine - Kwv Chenin Blanc South", 479 | "Pepper - White, Ground", 480 | "Plate Foam Laminated 9in Blk", 481 | "Pasta - Fettuccine, Egg, Fresh", 482 | "Pork - Butt, Boneless", 483 | "Apple - Custard", 484 | "Sauce - Demi Glace", 485 | "Napkin - Beverage 1 Ply", 486 | "Wine - Masi Valpolocell", 487 | "Bread - Mini Hamburger Bun", 488 | "Extract - Lemon", 489 | "Compound - Orange", 490 | "Soap - Hand Soap", 491 | "Longos - Grilled Chicken With", 492 | "Lettuce - Treviso", 493 | "Wine - Cotes Du Rhone Parallele", 494 | "Grapes - Black", 495 | "Ezy Change Mophandle", 496 | "Peas - Frozen", 497 | "Apricots Fresh", 498 | "Venison - Denver Leg Boneless", 499 | "Wine - Fat Bastard Merlot", 500 | "Ice Cream Bar - Drumstick", 501 | "Dome Lid Clear P92008h", 502 | "Pepper - Yellow Bell", 503 | "Yogurt - Peach, 175 Gr", 504 | "Squash - Butternut", 505 | "Squash - Butternut", 506 | "Chinese Foods - Plain Fried Rice", 507 | "Foil Wrap", 508 | "Vector Energy Bar", 509 | "Milk Powder", 510 | "Danishes - Mini Cheese", 511 | "Lamb - Bones", 512 | "Lamb - Leg, Boneless", 513 | "Towels - Paper / Kraft", 514 | "Appetizer - Sausage Rolls", 515 | "Banana Turning", 516 | "Pastry - Lemon Danish - Mini", 517 | "Turkey - Oven Roast Breast", 518 | "Emulsifier", 519 | "Table Cloth - 53x69 Colour", 520 | "Cheese - Asiago", 521 | "Fennel - Seeds", 522 | "Plums - Red", 523 | "Soup - Campbells Chili Veg", 524 | "Halibut - Steaks", 525 | "Wine - Magnotta, Merlot Sr Vqa", 526 | "Cornish Hen", 527 | "Cocoa Butter", 528 | "Dikon", 529 | "Tuna - Fresh", 530 | "Pastry - Baked Scones - Mini", 531 | "Clam Nectar", 532 | "Apple - Royal Gala", 533 | "Crawfish", 534 | "Truffle Cups - Brown", 535 | "Wine - Red, Pinot Noir, Chateau", 536 | "Scotch - Queen Anne", 537 | "Browning Caramel Glace", 538 | "Gelatine Leaves - Bulk", 539 | "Chips - Potato Jalapeno", 540 | "Soho Lychee Liqueur", 541 | "Momiji Oroshi Chili Sauce", 542 | "Tart Shells - Sweet, 2", 543 | "Pears - Fiorelle", 544 | "Flour - All Purpose", 545 | "Soup - Campbellschix Stew", 546 | "Oregano - Fresh", 547 | "Daves Island Stinger", 548 | "Scallop - St. Jaques", 549 | "Sauce - Cranberry", 550 | "Wasabi Paste", 551 | "Tamarind Paste", 552 | "Foil - Round Foil", 553 | "Vinegar - White", 554 | "Lambcasing", 555 | "Swiss Chard - Red", 556 | "Country Roll", 557 | "Kahlua", 558 | "Mushroom - Chanterelle Frozen", 559 | "Cheese - Provolone", 560 | "Wine - Red, Pinot Noir, Chateau", 561 | "Table Cloth 81x81 Colour", 562 | "Cognac - Courvaisier", 563 | "Towel Multifold", 564 | "Bread - Crusty Italian Poly", 565 | "Eggwhite Frozen", 566 | "Dried Peach", 567 | "Steampan Lid", 568 | "Cheese - Brie,danish", 569 | "Tart Shells - Savory, 3", 570 | "Hog / Sausage Casing - Pork", 571 | "Sachet", 572 | "Puree - Strawberry", 573 | "Food Colouring - Red", 574 | "Yogurt - Banana, 175 Gr", 575 | "Blue Curacao - Marie Brizard", 576 | "Relish", 577 | "Goat - Whole Cut", 578 | "Potatoes - Idaho 80 Count", 579 | "Milk - Chocolate 250 Ml", 580 | "Tea - Apple Green Tea", 581 | "Bacardi Raspberry", 582 | "Food Colouring - Green", 583 | "Pate Pans Yellow", 584 | "Ice Cream - Turtles Stick Bar", 585 | "Bread - Bagels, Mini", 586 | "Carbonated Water - Blackberry", 587 | "Pastry - Baked Scones - Mini", 588 | "Pork - Back, Short Cut, Boneless", 589 | "Chicken - Diced, Cooked", 590 | "Muffin Batt - Ban Dream Zero", 591 | "Wine - Shiraz Wolf Blass Premium", 592 | "Yucca", 593 | "Soup - Campbells, Classic Chix", 594 | "Chocolate Bar - Coffee Crisp", 595 | "Dr. Pepper - 355ml", 596 | "Shrimp - 31/40", 597 | "Brocolinni - Gaylan, Chinese", 598 | "Beans - Kidney, Canned", 599 | "Mix - Cocktail Ice Cream", 600 | "Longos - Grilled Veg Sandwiches", 601 | "Tomatoes - Yellow Hot House", 602 | "Bread - Raisin Walnut Pull", 603 | "Cut Wakame - Hanawakaba", 604 | "Turnip - White", 605 | "Icecream - Dstk Cml And Fdg", 606 | "Turkey - Breast, Double", 607 | "Bread - Dark Rye, Loaf", 608 | "Beans - Fava Fresh", 609 | "Cherries - Fresh", 610 | "Appetizer - Escargot Puff", 611 | "Soap - Hand Soap", 612 | "Cotton Wet Mop 16 Oz", 613 | "Wine - Rioja Campo Viejo", 614 | "Eggs - Extra Large", 615 | "Veal Inside - Provimi", 616 | "Turnip - Wax", 617 | "Swiss Chard - Red", 618 | "Salmon - Atlantic, Fresh, Whole", 619 | "Gatorade - Xfactor Berry", 620 | "Juice - Clam, 46 Oz", 621 | "Pepper - Scotch Bonnet", 622 | "Chicken - Ground", 623 | "Ecolab - Ster Bac", 624 | "Salmon - Whole, 4 - 6 Pounds", 625 | "Rice - 7 Grain Blend", 626 | "Wanton Wrap", 627 | "Wine - Cotes Du Rhone", 628 | "Pepper - Green, Chili", 629 | "Beef - Bresaola", 630 | "Wine - Prosecco Valdobiaddene", 631 | "Crackers - Trio", 632 | "Oil - Safflower", 633 | "Bread - Rolls, Rye", 634 | "Brownies - Two Bite, Chocolate", 635 | "Potatoes - Pei 10 Oz", 636 | "Wine - Gato Negro Cabernet", 637 | "Bagel - Everything", 638 | "Wine - Riesling Dr. Pauly", 639 | "Pepper - Orange", 640 | "Croissant, Raw - Mini", 641 | "Turkey - Oven Roast Breast", 642 | "Salt - Table", 643 | "Beer - Mill St Organic", 644 | "Bagel - Everything Presliced", 645 | "Chivas Regal - 12 Year Old", 646 | "Dc - Sakura Fu", 647 | "Longos - Penne With Pesto", 648 | "Wine - Saint - Bris 2002, Sauv", 649 | "Galliano", 650 | "Seedlings - Buckwheat, Organic", 651 | "Mustard Prepared", 652 | "Beef - Inside Round", 653 | "Mayonnaise", 654 | "Oranges", 655 | "Soho Lychee Liqueur", 656 | "Truffle Paste", 657 | "Pork - Bones", 658 | "Spice - Pepper Portions", 659 | "Soup - Cream Of Potato / Leek", 660 | "Foam Espresso Cup Plain White", 661 | "Spice - Onion Powder Granulated", 662 | "Beef - Tenderloin", 663 | "Pickles - Gherkins", 664 | "Sponge Cake Mix - Vanilla", 665 | "Turkey Tenderloin Frozen", 666 | "Cheese - Le Cru Du Clocher", 667 | "Pears - Anjou", 668 | "Blackberries", 669 | "Truffle - Whole Black Peeled", 670 | "Wine - Placido Pinot Grigo", 671 | "Soup Campbells Turkey Veg.", 672 | "Coconut - Whole", 673 | "Kellogs Special K Cereal", 674 | "Cookie - Oatmeal", 675 | "Potatoes - Pei 10 Oz", 676 | "Wanton Wrap", 677 | "Beef - Montreal Smoked Brisket", 678 | "Snapple - Iced Tea Peach", 679 | "Muffins - Assorted", 680 | "Bread - Olive Dinner Roll", 681 | "Chervil - Fresh", 682 | "Dill - Primerba, Paste", 683 | "Lamb - Ground", 684 | "Soup - Campbells Mac N Cheese", 685 | "Bamboo Shoots - Sliced", 686 | "Pate Pans Yellow", 687 | "Sherbet - Raspberry", 688 | "Calypso - Lemonade", 689 | "Cranberries - Fresh", 690 | "Danishes - Mini Raspberry", 691 | "Wine - Redchard Merritt", 692 | "Melon - Cantaloupe", 693 | "Tea - Camomele", 694 | "Zucchini - Mini, Green", 695 | "Creme De Cacao White", 696 | "Sausage - Meat", 697 | "Silicone Paper 16.5x24", 698 | "Chinese Foods - Cantonese", 699 | "Barramundi", 700 | "Sugar - Splenda Sweetener", 701 | "Edible Flower - Mixed", 702 | "Nut - Pine Nuts, Whole", 703 | "Chestnuts - Whole,canned", 704 | "Juice Peach Nectar", 705 | "Roe - Lump Fish, Black", 706 | "Lobster - Tail, 3 - 4 Oz", 707 | "Dikon", 708 | "Snapple Raspberry Tea", 709 | "Chips - Miss Vickies", 710 | "Wine - Prem Select Charddonany", 711 | "Gelatine Leaves - Envelopes", 712 | "Soup Campbells - Tomato Bisque", 713 | "Pail With Metal Handle 16l White", 714 | "Coconut - Creamed, Pure", 715 | "Beer - Sleemans Honey Brown", 716 | "Filo Dough", 717 | "Appetizer - Smoked Salmon / Dill", 718 | "External Supplier", 719 | "Table Cloth 120 Round White", 720 | "Cinnamon Buns Sticky", 721 | "Lettuce - Lambs Mash", 722 | "Wine - Masi Valpolocell", 723 | "Bread - Multigrain, Loaf", 724 | "Laundry - Bag Cloth", 725 | "Muffin - Carrot Individual Wrap", 726 | "Foam Espresso Cup Plain White", 727 | "Corn - Cream, Canned", 728 | "Salt - Kosher", 729 | "Flour - Semolina", 730 | "Cheese - Roquefort Pappillon", 731 | "Milk - Buttermilk", 732 | "Curry Powder", 733 | "Squash - Butternut", 734 | "Chicken Breast Halal", 735 | "Cookie Trail Mix", 736 | "Tequila Rose Cream Liquor", 737 | "Wine - Rubyport", 738 | "Cheese - Boursin, Garlic / Herbs", 739 | "Wine - Rhine Riesling Wolf Blass", 740 | "Fond - Chocolate", 741 | "Sage Derby", 742 | "Pastry - Banana Tea Loaf", 743 | "Tarragon - Primerba, Paste", 744 | "Tarts Assorted", 745 | "Flower - Daisies", 746 | "Pepper - Black, Whole", 747 | "Croissant, Raw - Mini", 748 | "Vinegar - Sherry", 749 | "Juice - Tomato, 48 Oz", 750 | "Lettuce - Sea / Sea Asparagus", 751 | "Pate - Liver", 752 | "Chips - Miss Vickies", 753 | "Tabasco Sauce, 2 Oz", 754 | "Tortillas - Flour, 12", 755 | "Wine - Cave Springs Dry Riesling", 756 | "Muffin Batt - Blueberry Passion", 757 | "Lamb Tenderloin Nz Fr", 758 | "Jerusalem Artichoke", 759 | "Lobster - Live", 760 | "Yoghurt Tubes", 761 | "Sauce - Black Current, Dry Mix", 762 | "Soup - Campbells Broccoli", 763 | "Latex Rubber Gloves Size 9", 764 | "Tuna - Yellowfin", 765 | "Turkey - Breast, Bone - In", 766 | "Pasta - Fettuccine, Egg, Fresh", 767 | "Pork - Back, Long Cut, Boneless", 768 | "Rice Pilaf, Dry,package", 769 | "Bread - Corn Muffaleta Onion", 770 | "Fish - Soup Base, Bouillon", 771 | "Chocolate Bar - Reese Pieces", 772 | "Sesame Seed Black", 773 | "Muffin Mix - Lemon Cranberry", 774 | "Extract - Vanilla,artificial", 775 | "Island Oasis - Banana Daiquiri", 776 | "Sage Derby", 777 | "Carbonated Water - White Grape", 778 | "Macaroons - Homestyle Two Bit", 779 | "Grenadillo", 780 | "Sauce - Ranch Dressing", 781 | "Pepper - Cayenne", 782 | "Table Cloth 90x90 Colour", 783 | "Water - Aquafina Vitamin", 784 | "Chips - Doritos", 785 | "Wine - Shiraz South Eastern", 786 | "Beef - Rib Roast, Cap On", 787 | "Pasta - Tortellini, Fresh", 788 | "Noodles - Cellophane, Thin", 789 | "Asparagus - Frozen", 790 | "Cheese - Cheddar, Medium", 791 | "Beef - Cow Feet Split", 792 | "Milk - Condensed", 793 | "Figs", 794 | "Trout Rainbow Whole", 795 | "Vodka - Hot, Lnferno", 796 | "Cognac - Courvaisier", 797 | "Wine - Soave Folonari", 798 | "Mcgillicuddy Vanilla Schnap", 799 | "Honey - Lavender", 800 | "Coconut Milk - Unsweetened", 801 | "Coke - Classic, 355 Ml", 802 | "Salt - Seasoned", 803 | "Soup - Campbells Mac N Cheese", 804 | "Longos - Cheese Tortellini", 805 | "Beer - Guiness", 806 | "Pate Pans Yellow", 807 | "Carrots - Purple, Organic", 808 | "Squash - Pattypan, Yellow", 809 | "Maple Syrup", 810 | "Langers - Mango Nectar", 811 | "French Pastry - Mini Chocolate", 812 | "Cheese - Perron Cheddar", 813 | "Beans - Black Bean, Preserved", 814 | "Wine - Chianti Classica Docg", 815 | "Appetizer - Tarragon Chicken", 816 | "Beef Cheek Fresh", 817 | "Pasta - Rotini, Dry", 818 | "Oil - Truffle, White", 819 | "Asparagus - Green, Fresh", 820 | "Cheese - Gouda Smoked", 821 | "Nori Sea Weed - Gold Label", 822 | "Wine - Chardonnay South", 823 | "Wine - Bourgogne 2002, La", 824 | "Avocado", 825 | "Yoplait Drink", 826 | "Galliano", 827 | "Apple - Delicious, Red", 828 | "Bagel - Everything Presliced", 829 | "Mop Head - Cotton, 24 Oz", 830 | "Chicken - Base, Ultimate", 831 | "Pastry - Chocolate Chip Muffin", 832 | "Nantucket Orange Juice", 833 | "Fond - Neutral", 834 | "Ham - Procutinni", 835 | "Wine - Sake", 836 | "Longos - Lasagna Veg", 837 | "Juice - Cranberry, 341 Ml", 838 | "Foie Gras", 839 | "Bread - Calabrese Baguette", 840 | "Orange Roughy 6/8 Oz", 841 | "Cheese - Victor Et Berthold", 842 | "Tomato - Plum With Basil", 843 | "Tuna - Loin", 844 | "Flour - Cake", 845 | "Chocolate - Chips Compound", 846 | "Sobe - Liz Blizz", 847 | "Cabbage - Nappa", 848 | "Pork - Back Ribs", 849 | "Corn Shoots", 850 | "Wine - Winzer Krems Gruner", 851 | "Cheese - Romano, Grated", 852 | "Beans - Navy, Dry", 853 | "Wine - Charddonnay Errazuriz", 854 | "Garam Marsala", 855 | "Muffin Hinge - 211n", 856 | "Wine - Charddonnay Errazuriz", 857 | "Milk - Skim", 858 | "Lobster - Base", 859 | "Toothpick Frilled", 860 | "Nestea - Iced Tea", 861 | "White Baguette", 862 | "Veal - Striploin", 863 | "Placemat - Scallop, White", 864 | "Gelatine Leaves - Bulk", 865 | "Ice Cream - Strawberry", 866 | "Beef - Roasted, Cooked", 867 | "Pepper - Cubanelle", 868 | "Wine - Prosecco Valdobiaddene", 869 | "Muffin Hinge 117n", 870 | "Tortillas - Flour, 8", 871 | "Chicken Breast Halal", 872 | "Juice - Apple Cider", 873 | "Anchovy Fillets", 874 | "Tuna - Sushi Grade", 875 | "Rice - Sushi", 876 | "Veal - Brisket, Provimi, Bone - In", 877 | "Icecream - Dibs", 878 | "Scallops - 20/30", 879 | "Scallops - In Shell", 880 | "Mushroom - Porcini Frozen", 881 | "Alize Red Passion", 882 | "Muffin - Mix - Strawberry Rhubarb", 883 | "Puree - Raspberry", 884 | "Coffee - Espresso", 885 | "Onion - Dried", 886 | "Wine - Dubouef Macon - Villages", 887 | "Scallops - In Shell", 888 | "Steampan - Foil", 889 | "Sauce - Sesame Thai Dressing", 890 | "Rappini - Andy Boy", 891 | "Steam Pan Full Lid", 892 | "Cup - 8oz Coffee Perforated", 893 | "Pepper - Red Thai", 894 | "Lobak", 895 | "Juice - Mango", 896 | "Parsley Italian - Fresh", 897 | "Shrimp - Black Tiger 6 - 8", 898 | "Juice - Cranberry, 341 Ml", 899 | "Pork - Belly Fresh", 900 | "Yeast Dry - Fermipan", 901 | "Rice - 7 Grain Blend", 902 | "Fireball Whisky", 903 | "Ice Cream - Chocolate", 904 | "Oil - Truffle, Black", 905 | "Puff Pastry - Slab", 906 | "Salmon Atl.whole 8 - 10 Lb", 907 | "Truffle Shells - White Chocolate", 908 | "Tea - Mint", 909 | "Bread - 10 Grain", 910 | "Wine - Red Oakridge Merlot", 911 | "Appetizer - Escargot Puff", 912 | "Wine - Barbera Alba Doc 2001", 913 | "Pears - Bosc", 914 | "Mushroom - White Button", 915 | "Chocolate - Sugar Free Semi Choc", 916 | "Scampi Tail", 917 | "Aspic - Clear", 918 | "Silicone Parch. 16.3x24.3", 919 | "Ice Cream - Strawberry", 920 | "Chicken - Livers", 921 | "Scotch - Queen Anne", 922 | "Wine - Remy Pannier Rose", 923 | "Beef - Texas Style Burger", 924 | "Wine - Cotes Du Rhone Parallele", 925 | "Veal - Liver", 926 | "Grapefruit - Pink", 927 | "Juice - Clamato, 341 Ml", 928 | "Puree - Strawberry", 929 | "Temperature Recording Station", 930 | "Fireball Whisky", 931 | "Garbage Bags - Clear", 932 | "Cake - French Pear Tart", 933 | "Tomato Paste", 934 | "Initation Crab Meat", 935 | "Cut Wakame - Hanawakaba", 936 | "Wine - Gato Negro Cabernet", 937 | "Cheese - Goat", 938 | "Mushroom - Chanterelle, Dry", 939 | "Chip - Potato Dill Pickle", 940 | "Tart Shells - Sweet, 2", 941 | "Shrimp - 16/20, Iqf, Shell On", 942 | "Yogurt - Cherry, 175 Gr", 943 | "Sobe - Berry Energy", 944 | "Vermouth - White, Cinzano", 945 | "Fennel - Seeds", 946 | "Potatoes - Pei 10 Oz", 947 | "Orange - Tangerine", 948 | "Juice - Grapefruit, 341 Ml", 949 | "Oyster - In Shell", 950 | "Pepper - Julienne, Frozen", 951 | "Cake - Cheese Cake 9 Inch", 952 | "Salmon - Smoked, Sliced", 953 | "Beer - Corona", 954 | "Sardines", 955 | "Spaghetti Squash", 956 | "Allspice - Jamaican", 957 | "Ranchero - Primerba, Paste", 958 | "Peas - Frozen", 959 | "Turkey - Breast, Double", 960 | "Wine - Rosso Del Veronese Igt", 961 | "Flower - Commercial Bronze", 962 | "Swiss Chard - Red", 963 | "Pork - Smoked Kassler", 964 | "Wine - Acient Coast Caberne", 965 | "Beef - Tenderlion, Center Cut", 966 | "Capon - Whole", 967 | "Pernod", 968 | "Mint - Fresh", 969 | "Shrimp - Black Tiger 26/30", 970 | "Sugar Thermometer", 971 | "Quinoa", 972 | "Bread - Multigrain, Loaf", 973 | "Wild Boar - Tenderloin", 974 | "Juice - Cranberry, 341 Ml", 975 | "Salt - Table", 976 | "Soup - Campbells Chili Veg", 977 | "Beef - Ox Tongue", 978 | "Coke - Diet, 355 Ml", 979 | "Ecolab - Hobart Washarm End Cap", 980 | "Chestnuts - Whole,canned", 981 | "Tart Shells - Savory, 4", 982 | "Brandy - Bar", 983 | "Carbonated Water - Orange", 984 | "Beer - Labatt Blue", 985 | "Clams - Littleneck, Whole", 986 | "Extract - Vanilla,artificial", 987 | "Veal - Kidney", 988 | "Doilies - 8, Paper", 989 | "Juice - Lemon", 990 | "Dry Ice", 991 | "Flour - So Mix Cake White", 992 | "Wine - Tribal Sauvignon", 993 | "Wine - Casillero Deldiablo", 994 | "Veal - Loin", 995 | "Oil - Margarine", 996 | "Lemon Pepper", 997 | "Jagermeister", 998 | "Chocolate Bar - Oh Henry", 999 | "Trueblue - Blueberry Cranberry", 1000 | "Wine - Red, Pelee Island Merlot", 1001 | "Wine - White, Ej" 1002 | ] 1003 | --------------------------------------------------------------------------------