diff --git a/public/autocomplete/LICENSE b/public/autocomplete/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/public/autocomplete/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/public/autocomplete/README.md b/public/autocomplete/README.md new file mode 100644 index 0000000..73d2f28 --- /dev/null +++ b/public/autocomplete/README.md @@ -0,0 +1,124 @@ +# autoComplete.js :sparkles: + +[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) +[![GitHub package.json version](https://img.shields.io/github/package-json/v/TarekRaafat/autoComplete.js)](https://badge.fury.io/gh/tarekraafat%2FautoComplete.js) +[![npm](https://img.shields.io/npm/v/@tarekraafat/autocomplete.js)](https://badge.fury.io/js/%40tarekraafat%2Fautocomplete.js) +![GitHub top language](https://img.shields.io/github/languages/top/TarekRaafat/autoComplete.js?color=yellow) +![\[Zero Dependencies\]()](https://img.shields.io/badge/Dependencies-0-blue.svg) +![\[Size\]()](https://img.shields.io/badge/Size-10%20KB-green.svg) +![Maintained](https://img.shields.io/badge/Maintained%3F-yes-success) +[![](https://data.jsdelivr.com/v1/package/npm/@tarekraafat/autocomplete.js/badge)](https://www.jsdelivr.com/package/npm/@tarekraafat/autocomplete.js) +![npm](https://img.shields.io/npm/dm/@tarekraafat/autocomplete.js?label=npm) +[![Open Source Love](https://badges.frapsoft.com/os/v1/open-source.svg?v=103)](https://github.com/TarekRaafat/autoComplete.js) + +
+
+

+ + autoComplete.js Design + +

+
+
+ +> Simple autocomplete pure vanilla Javascript library. :rocket: Live Demo **v8.3** + +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, made for users and developers in mind. + +## Features + +- Pure Vanilla Javascript +- Zero Dependencies +- Simple & Easy to use +- Extremely Lightweight +- Blazing Fast +- Versatile +- Hackable & highly customizable + +## [![autoComplete.js Code Example](./docs/img/autoComplete.init.png "autoComplete.js Code Example")](https://codepen.io/tarekraafat/pen/rQopdW?editors=0010) + +## Get Started + +### Clone: + +- Clone autoComplete.js to your local machine + +```shell +git clone https://github.com/TarekRaafat/autoComplete.js.git +``` + +### Installation: + +- jsDelivr CDN + +`CSS` + +```html + +``` + +`JS` + +```html + +``` + +- npm install `(Node Package Manager)` + +```shell +npm i @tarekraafat/autocomplete +``` + +- Yarn install `(Javascript Package Manager)` + +```shell +yarn add @tarekraafat/autocomplete +``` + + + +## Documentation: + +- For more details check out **autoComplete.js** **docs** :notebook_with_decorative_cover: + + + +## Third-Party Plugins: + +- [Contao autoComplete.js Bundle](https://github.com/heimrichhannot/contao-autocompletejs-bundle) by [@heimrichhannot](https://github.com/heimrichhannot) +- [Contentful Fontawesome autoComplete Field](https://github.com/ModiiMedia/contentful-fontawesome-autocomplete-field) by [@ModiiMedia](https://github.com/ModiiMedia) + +* * * + +## Support + +For general questions about autoComplete.js, tweet at [@TarekRaafat]. + +For technical questions, you should post a question on [Stack Overflow] and tag +it with [autoComplete.js][so tag]. + + + +[stack overflow]: https://stackoverflow.com/ + +[@tarekraafat]: https://twitter.com/TarekRaafat + +[so tag]: https://stackoverflow.com/questions/tagged/autoComplete.js + +* * * + +## Author + +**Tarek Raafat** + +- Email: tarek.m.raafat@gmail.com +- Website: +- Github: + +Distributed under the Apache 2.0 license. See `Apache 2.0` for more information. + +* * * + +## License + +Apache 2.0 © [Tarek Raafat](http://www.tarekraafat.com) diff --git a/public/autocomplete/dist/css/autoComplete.css b/public/autocomplete/dist/css/autoComplete.css new file mode 100644 index 0000000..6828992 --- /dev/null +++ b/public/autocomplete/dist/css/autoComplete.css @@ -0,0 +1,153 @@ +.autoComplete_wrapper { + position: relative; + display: inline-block; + width: 370px; +} + +/*#autoComplete { + height: 3rem; + margin: 0; + padding: 0 2rem 0 3.2rem; + box-sizing: border-box; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + font-size: 1rem; + text-overflow: ellipsis; + color: rgba(11, 94, 215, 0.3); + outline: none; + border-radius: 10rem; + border: 0; + border: 0.05rem solid rgba(255, 122, 122, 0.5); + background-image: url(./images/search.svg); + background-size: 1.4rem; + background-position: left 1.05rem top 0.8rem; + background-repeat: no-repeat; + background-origin: border-box; + background-color: #fff; + transition: all 0.4s ease; + -webkit-transition: all -webkit-transform 0.4s ease; +}*/ + +#autoComplete::placeholder { + color: rgba(11, 94, 215, 0.5); + transition: all 0.3s ease; + -webkit-transition: all -webkit-transform 0.3s ease; +} + +#autoComplete:hover::placeholder { + color: rgba(11, 94, 215, 0.6); + transition: all 0.3s ease; + -webkit-transition: all -webkit-transform 0.3s ease; +} + +#autoComplete:focus::placeholder { + padding: 0.1rem 0.6rem; + font-size: 0.95rem; + color: rgba(11, 94, 215, 0.4); +} + +#autoComplete:focus::selection { + background-color: rgba(11, 94, 215, 0.15); +} + +#autoComplete::selection { + background-color: rgba(11, 94, 215, 0.15); +} + +#autoComplete:hover { + color: rgba(11, 94, 215, 0.8); + transition: all 0.3s ease; + -webkit-transition: all -webkit-transform 0.3s ease; +} + +#autoComplete:focus { + color: rgba(11, 94, 215, 1); + border: 0.06rem solid rgba(11, 94, 215, 0.8); +} + +#autoComplete_list { + position: absolute; + top: 100%; + left: 0; + right: 0; + padding: 0; + margin-top: 0.5rem; + border-radius: 0.6rem; + background-color: #fff; + box-shadow: 0 3px 6px rgba(149, 157, 165, 0.15); + border: 1px solid rgba(33, 33, 33, 0.07); + z-index: 1000; + outline: none; +} + +.autoComplete_result { + margin: 0.3rem; + padding: 0.3rem 0.5rem; + list-style: none; + text-align: left; + font-size: 1rem; + color: #212121; + transition: all 0.1s ease-in-out; + border-radius: 0.35rem; + background-color: rgba(255, 255, 255, 1); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + transition: all 0.2s ease; +} + +.autoComplete_result::selection { + color: rgba(#ffffff, 0); + background-color: rgba(#ffffff, 0); +} + +.autoComplete_result:hover { + cursor: pointer; + background-color: rgba(11, 94, 215, 0.15); +} + +.autoComplete_highlighted { + color: rgba(11, 94, 215, 1); + font-weight: bold; +} + +.autoComplete_highlighted::selection { + color: rgba(#ffffff, 0); + background-color: rgba(#ffffff, 0); +} + +.autoComplete_selected { + cursor: pointer; + background-color: rgba(11, 94, 215, 0.15); +} + +@media only screen and (max-width: 600px) { + .autoComplete_wrapper { + width: 320px; + } + + #autoComplete { + width: 18rem; + background-size: 1.6rem; + background-position: left 1.1rem top 0.75rem; + } + + #autoComplete_list { + margin: 10px; + } +} + +@media screen and (-webkit-min-device-pixel-ratio: 0) { + #autoComplete { + border-width: 1px; + } +} + +@-moz-document url-prefix() { + #autoComplete { + background-size: 1.2rem; + background-origin: border-box; + border-width: 1px; + background-position: left 1.1rem top 0.8rem; + } +} diff --git a/public/autocomplete/dist/css/images/magnifier.svg b/public/autocomplete/dist/css/images/magnifier.svg new file mode 100644 index 0000000..2a0ee79 --- /dev/null +++ b/public/autocomplete/dist/css/images/magnifier.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/public/autocomplete/dist/css/images/search.svg b/public/autocomplete/dist/css/images/search.svg new file mode 100644 index 0000000..8063ea1 --- /dev/null +++ b/public/autocomplete/dist/css/images/search.svg @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/public/autocomplete/dist/js/autoComplete.js b/public/autocomplete/dist/js/autoComplete.js new file mode 100644 index 0000000..d1b57ec --- /dev/null +++ b/public/autocomplete/dist/js/autoComplete.js @@ -0,0 +1,618 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.autoComplete = factory()); +}(this, (function () { 'use strict'; + + function _classCallCheck(instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError("Cannot call a class as a function"); + } + } + + function _defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i]; + descriptor.enumerable = descriptor.enumerable || false; + descriptor.configurable = true; + if ("value" in descriptor) descriptor.writable = true; + Object.defineProperty(target, descriptor.key, descriptor); + } + } + + function _createClass(Constructor, protoProps, staticProps) { + if (protoProps) _defineProperties(Constructor.prototype, protoProps); + if (staticProps) _defineProperties(Constructor, staticProps); + return Constructor; + } + + function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + + return obj; + } + + function ownKeys(object, enumerableOnly) { + var keys = Object.keys(object); + + if (Object.getOwnPropertySymbols) { + var symbols = Object.getOwnPropertySymbols(object); + if (enumerableOnly) symbols = symbols.filter(function (sym) { + return Object.getOwnPropertyDescriptor(object, sym).enumerable; + }); + keys.push.apply(keys, symbols); + } + + return keys; + } + + function _objectSpread2(target) { + for (var i = 1; i < arguments.length; i++) { + var source = arguments[i] != null ? arguments[i] : {}; + + if (i % 2) { + ownKeys(Object(source), true).forEach(function (key) { + _defineProperty(target, key, source[key]); + }); + } else if (Object.getOwnPropertyDescriptors) { + Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); + } else { + ownKeys(Object(source)).forEach(function (key) { + Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); + }); + } + } + + return target; + } + + function _unsupportedIterableToArray(o, minLen) { + if (!o) return; + if (typeof o === "string") return _arrayLikeToArray(o, minLen); + var n = Object.prototype.toString.call(o).slice(8, -1); + if (n === "Object" && o.constructor) n = o.constructor.name; + if (n === "Map" || n === "Set") return Array.from(o); + if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); + } + + function _arrayLikeToArray(arr, len) { + if (len == null || len > arr.length) len = arr.length; + + for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; + + return arr2; + } + + function _createForOfIteratorHelper(o, allowArrayLike) { + var it; + + if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { + if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { + if (it) o = it; + var i = 0; + + var F = function () {}; + + return { + s: F, + n: function () { + if (i >= o.length) return { + done: true + }; + return { + done: false, + value: o[i++] + }; + }, + e: function (e) { + throw e; + }, + f: F + }; + } + + 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 normalCompletion = true, + didErr = false, + err; + return { + s: function () { + it = o[Symbol.iterator](); + }, + n: function () { + var step = it.next(); + normalCompletion = step.done; + return step; + }, + e: function (e) { + didErr = true; + err = e; + }, + f: function () { + try { + if (!normalCompletion && it.return != null) it.return(); + } finally { + if (didErr) throw err; + } + } + }; + } + + var inputComponent = (function (config) { + config.inputField.setAttribute("type", "text"); + config.inputField.setAttribute("role", "combobox"); + config.inputField.setAttribute("aria-haspopup", true); + config.inputField.setAttribute("aria-expanded", false); + config.inputField.setAttribute("aria-controls", config.resultsList.idName); + config.inputField.setAttribute("aria-autocomplete", "both"); + }); + + var createList = (function (config) { + var list = document.createElement(config.resultsList.element); + list.setAttribute("id", config.resultsList.idName); + list.setAttribute("aria-label", config.name); + list.setAttribute("class", config.resultsList.className); + list.setAttribute("role", "listbox"); + list.setAttribute("tabindex", "-1"); + if (config.resultsList.container) config.resultsList.container(list); + var destination = "string" === typeof config.resultsList.destination ? document.querySelector(config.resultsList.destination) : config.resultsList.destination(); + destination.insertAdjacentElement(config.resultsList.position, list); + return list; + }); + + var createItem = (function (item, index, config) { + var result = document.createElement(config.resultItem.element); + result.setAttribute("id", "".concat(config.resultItem.idName, "_").concat(index)); + result.setAttribute("class", config.resultItem.className); + result.setAttribute("role", "option"); + result.innerHTML = item.match; + if (config.resultItem.content) config.resultItem.content(item, result); + return result; + }); + + var closeAllLists = function closeAllLists(config, element) { + var list = document.getElementsByClassName(config.resultsList.className); + for (var index = 0; index < list.length; index++) { + if (element !== list[index] && element !== config.inputField) list[index].parentNode.removeChild(list[index]); + } + config.inputField.removeAttribute("aria-activedescendant"); + config.inputField.setAttribute("aria-expanded", false); + }; + var generateList = function generateList(config, data, matches) { + var list = createList(config); + config.inputField.setAttribute("aria-expanded", true); + var _loop = function _loop(index) { + var item = data.results[index]; + var resultItem = createItem(item, index, config); + resultItem.addEventListener("click", function (event) { + var dataFeedback = { + event: event, + matches: matches, + input: data.input, + query: data.query, + results: data.results, + selection: _objectSpread2(_objectSpread2({}, item), {}, { + index: index + }) + }; + if (config.onSelection) config.onSelection(dataFeedback); + }); + list.appendChild(resultItem); + }; + for (var index = 0; index < data.results.length; index++) { + _loop(index); + } + return list; + }; + + var eventEmitter = (function (target, detail, name) { + target.dispatchEvent(new CustomEvent(name, { + bubbles: true, + detail: detail, + cancelable: true + })); + }); + + var navigate = function navigate(config, dataFeedback) { + var currentFocus = -1; + var update = function update(event, list, state, config) { + event.preventDefault(); + if (state) { + currentFocus++; + } else { + currentFocus--; + } + addActive(list); + config.inputField.setAttribute("aria-activedescendant", list[currentFocus].id); + eventEmitter(event.srcElement, _objectSpread2(_objectSpread2({ + event: event + }, dataFeedback), {}, { + selection: dataFeedback.results[currentFocus] + }), "navigation"); + }; + var removeActive = function removeActive(list) { + for (var index = 0; index < list.length; index++) { + list[index].removeAttribute("aria-selected"); + list[index].classList.remove("autoComplete_selected"); + } + }; + var addActive = function addActive(list) { + if (!list) return false; + removeActive(list); + if (currentFocus >= list.length) currentFocus = 0; + if (currentFocus < 0) currentFocus = list.length - 1; + list[currentFocus].setAttribute("aria-selected", "true"); + list[currentFocus].classList.add("autoComplete_selected"); + }; + var navigation = function navigation(event) { + var list = document.getElementById(config.resultsList.idName); + if (!list) return config.inputField.removeEventListener("keydown", navigate); + list = list.getElementsByTagName(config.resultItem.element); + if (event.keyCode === 27) { + config.inputField.value = ""; + closeAllLists(config); + } else if (event.keyCode === 40 || event.keyCode === 9) { + update(event, list, true, config); + } else if (event.keyCode === 38 || event.keyCode === 9) { + update(event, list, false, config); + } else if (event.keyCode === 13) { + event.preventDefault(); + if (currentFocus > -1) { + if (list) list[currentFocus].click(); + } + } + }; + var navigate = config.resultsList.navigation || navigation; + if (config.inputField.autoCompleteNavigate) config.inputField.removeEventListener("keydown", config.inputField.autoCompleteNavigate); + config.inputField.autoCompleteNavigate = navigate; + config.inputField.addEventListener("keydown", navigate); + }; + + var searchEngine = (function (query, record, config) { + var recordLowerCase = config.diacritics ? record.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").normalize("NFC") : record.toLowerCase(); + if (config.searchEngine === "loose") { + query = query.replace(/ /g, ""); + var match = []; + var searchPosition = 0; + for (var number = 0; number < recordLowerCase.length; number++) { + var recordChar = record[number]; + if (searchPosition < query.length && recordLowerCase[number] === query[searchPosition]) { + recordChar = config.highlight ? "".concat(recordChar, "") : recordChar; + searchPosition++; + } + match.push(recordChar); + } + if (searchPosition === query.length) { + return match.join(""); + } + } else { + if (recordLowerCase.includes(query)) { + var pattern = new RegExp("".concat(query), "i"); + query = pattern.exec(record); + var _match = config.highlight ? record.replace(query, "".concat(query, "")) : record; + return _match; + } + } + }); + + var getInputValue = function getInputValue(inputField) { + return inputField instanceof HTMLInputElement || inputField instanceof HTMLTextAreaElement ? inputField.value.toLowerCase() : inputField.innerHTML.toLowerCase(); + }; + var prepareQueryValue = function prepareQueryValue(inputValue, config) { + return config.query && config.query.manipulate ? config.query.manipulate(inputValue) : config.diacritics ? inputValue.normalize("NFD").replace(/[\u0300-\u036f]/g, "").normalize("NFC") : inputValue; + }; + var checkTriggerCondition = function checkTriggerCondition(config, queryValue) { + return config.trigger.condition ? config.trigger.condition(queryValue) : queryValue.length >= config.threshold && queryValue.replace(/ /g, "").length; + }; + var listMatchingResults = function listMatchingResults(config, query) { + var resList = []; + var _loop = function _loop(index) { + var record = config.data.store[index]; + var search = function search(key) { + var recordValue = (key ? record[key] : record).toString(); + if (recordValue) { + var match = typeof config.searchEngine === "function" ? config.searchEngine(query, recordValue) : searchEngine(query, recordValue, config); + if (match && key) { + resList.push({ + key: key, + index: index, + match: match, + value: record + }); + } else if (match && !key) { + resList.push({ + index: index, + match: match, + value: record + }); + } + } + }; + if (config.data.key) { + var _iterator = _createForOfIteratorHelper(config.data.key), + _step; + try { + for (_iterator.s(); !(_step = _iterator.n()).done;) { + var key = _step.value; + search(key); + } + } catch (err) { + _iterator.e(err); + } finally { + _iterator.f(); + } + } else { + search(); + } + }; + for (var index = 0; index < config.data.store.length; index++) { + _loop(index); + } + var list = config.sort ? resList.sort(config.sort) : resList; + return list; + }; + + var debouncer = (function (callback, delay) { + var inDebounce; + return function () { + var context = this; + var args = arguments; + clearTimeout(inDebounce); + inDebounce = setTimeout(function () { + return callback.apply(context, args); + }, delay); + }; + }); + + var autoComplete = function () { + function autoComplete(config) { + _classCallCheck(this, autoComplete); + var _config$name = config.name, + name = _config$name === void 0 ? "Search" : _config$name, + _config$selector = config.selector, + selector = _config$selector === void 0 ? "#autoComplete" : _config$selector, + _config$observer = config.observer, + observer = _config$observer === void 0 ? false : _config$observer, + _config$data = config.data, + src = _config$data.src, + key = _config$data.key, + _config$data$cache = _config$data.cache, + cache = _config$data$cache === void 0 ? false : _config$data$cache, + store = _config$data.store, + results = _config$data.results, + query = config.query, + _config$trigger = config.trigger; + _config$trigger = _config$trigger === void 0 ? {} : _config$trigger; + var _config$trigger$event = _config$trigger.event, + event = _config$trigger$event === void 0 ? ["input"] : _config$trigger$event, + _config$trigger$condi = _config$trigger.condition, + condition = _config$trigger$condi === void 0 ? false : _config$trigger$condi, + _config$searchEngine = config.searchEngine, + searchEngine = _config$searchEngine === void 0 ? "strict" : _config$searchEngine, + _config$diacritics = config.diacritics, + diacritics = _config$diacritics === void 0 ? false : _config$diacritics, + _config$threshold = config.threshold, + threshold = _config$threshold === void 0 ? 1 : _config$threshold, + _config$debounce = config.debounce, + debounce = _config$debounce === void 0 ? 0 : _config$debounce, + _config$resultsList = config.resultsList; + _config$resultsList = _config$resultsList === void 0 ? {} : _config$resultsList; + var _config$resultsList$r = _config$resultsList.render, + render = _config$resultsList$r === void 0 ? true : _config$resultsList$r, + _config$resultsList$c = _config$resultsList.container, + container = _config$resultsList$c === void 0 ? false : _config$resultsList$c, + destination = _config$resultsList.destination, + _config$resultsList$p = _config$resultsList.position, + position = _config$resultsList$p === void 0 ? "afterend" : _config$resultsList$p, + _config$resultsList$e = _config$resultsList.element, + resultsListElement = _config$resultsList$e === void 0 ? "ul" : _config$resultsList$e, + _config$resultsList$i = _config$resultsList.idName, + resultsListId = _config$resultsList$i === void 0 ? "autoComplete_list" : _config$resultsList$i, + _config$resultsList$c2 = _config$resultsList.className, + resultsListClass = _config$resultsList$c2 === void 0 ? "autoComplete_list" : _config$resultsList$c2, + _config$resultsList$n = _config$resultsList.navigation, + navigation = _config$resultsList$n === void 0 ? false : _config$resultsList$n, + _config$sort = config.sort, + sort = _config$sort === void 0 ? false : _config$sort, + placeHolder = config.placeHolder, + _config$maxResults = config.maxResults, + maxResults = _config$maxResults === void 0 ? 5 : _config$maxResults, + _config$resultItem = config.resultItem; + _config$resultItem = _config$resultItem === void 0 ? {} : _config$resultItem; + var _config$resultItem$co = _config$resultItem.content, + content = _config$resultItem$co === void 0 ? false : _config$resultItem$co, + _config$resultItem$el = _config$resultItem.element, + resultItemElement = _config$resultItem$el === void 0 ? "li" : _config$resultItem$el, + _config$resultItem$id = _config$resultItem.idName, + resultItemId = _config$resultItem$id === void 0 ? "autoComplete_result" : _config$resultItem$id, + _config$resultItem$cl = _config$resultItem.className, + resultItemClass = _config$resultItem$cl === void 0 ? "autoComplete_result" : _config$resultItem$cl, + noResults = config.noResults, + _config$highlight = config.highlight, + highlight = _config$highlight === void 0 ? false : _config$highlight, + feedback = config.feedback, + onSelection = config.onSelection; + this.name = name; + this.selector = selector; + this.observer = observer; + this.data = { + src: src, + key: key, + cache: cache, + store: store, + results: results + }; + this.query = query; + this.trigger = { + event: event, + condition: condition + }; + this.searchEngine = searchEngine; + this.diacritics = diacritics; + this.threshold = threshold; + this.debounce = debounce; + this.resultsList = { + render: render, + container: container, + destination: destination || this.selector, + position: position, + element: resultsListElement, + idName: resultsListId, + className: resultsListClass, + navigation: navigation + }; + this.sort = sort; + this.placeHolder = placeHolder; + this.maxResults = maxResults; + this.resultItem = { + content: content, + element: resultItemElement, + idName: resultItemId, + className: resultItemClass + }; + this.noResults = noResults; + this.highlight = highlight; + this.feedback = feedback; + this.onSelection = onSelection; + this.inputField = typeof this.selector === "string" ? document.querySelector(this.selector) : this.selector(); + this.observer ? this.preInit() : this.init(); + } + _createClass(autoComplete, [{ + key: "start", + value: function start(input, query) { + var _this = this; + var results = this.data.results ? this.data.results(listMatchingResults(this, query)) : listMatchingResults(this, query); + var dataFeedback = { + input: input, + query: query, + matches: results, + results: results.slice(0, this.maxResults) + }; + eventEmitter(this.inputField, dataFeedback, "results"); + if (!results.length) return this.noResults ? this.noResults(dataFeedback, generateList) : null; + if (!this.resultsList.render) return this.feedback(dataFeedback); + results.length ? generateList(this, dataFeedback, results) : null; + eventEmitter(this.inputField, dataFeedback, "rendered"); + navigate(this, dataFeedback); + document.addEventListener("click", function (event) { + return closeAllLists(_this, event.target); + }); + } + }, { + key: "dataStore", + value: function dataStore() { + var _this2 = this; + return new Promise(function ($return, $error) { + if (_this2.data.cache && _this2.data.store) return $return(null); + return new Promise(function ($return, $error) { + if (typeof _this2.data.src === "function") { + return _this2.data.src().then($return, $error); + } + return $return(_this2.data.src); + }).then(function ($await_5) { + try { + _this2.data.store = $await_5; + eventEmitter(_this2.inputField, _this2.data.store, "fetch"); + return $return(); + } catch ($boundEx) { + return $error($boundEx); + } + }, $error); + }); + } + }, { + key: "compose", + value: function compose() { + var _this3 = this; + return new Promise(function ($return, $error) { + var input, query, triggerCondition; + input = getInputValue(_this3.inputField); + query = prepareQueryValue(input, _this3); + triggerCondition = checkTriggerCondition(_this3, query); + if (triggerCondition) { + return _this3.dataStore().then(function ($await_6) { + try { + closeAllLists(_this3); + _this3.start(input, query); + return $If_3.call(_this3); + } catch ($boundEx) { + return $error($boundEx); + } + }, $error); + } else { + closeAllLists(_this3); + return $If_3.call(_this3); + } + function $If_3() { + return $return(); + } + }); + } + }, { + key: "init", + value: function init() { + var _this4 = this; + inputComponent(this); + if (this.placeHolder) this.inputField.setAttribute("placeholder", this.placeHolder); + this.hook = debouncer(function () { + _this4.compose(); + }, this.debounce); + this.trigger.event.forEach(function (eventType) { + _this4.inputField.removeEventListener(eventType, _this4.hook); + _this4.inputField.addEventListener(eventType, _this4.hook); + }); + eventEmitter(this.inputField, null, "init"); + } + }, { + key: "preInit", + value: function preInit() { + var _this5 = this; + var config = { + childList: true, + subtree: true + }; + var callback = function callback(mutationsList, observer) { + var _iterator = _createForOfIteratorHelper(mutationsList), + _step; + try { + for (_iterator.s(); !(_step = _iterator.n()).done;) { + var mutation = _step.value; + if (_this5.inputField) { + observer.disconnect(); + eventEmitter(_this5.inputField, null, "connect"); + _this5.init(); + } + } + } catch (err) { + _iterator.e(err); + } finally { + _iterator.f(); + } + }; + var observer = new MutationObserver(callback); + observer.observe(document, config); + } + }, { + key: "unInit", + value: function unInit() { + this.inputField.removeEventListener("input", this.hook); + eventEmitter(this.inputField, null, "unInit"); + } + }]); + return autoComplete; + }(); + + return autoComplete; + +}))); diff --git a/public/autocomplete/dist/js/autoComplete.min.js b/public/autocomplete/dist/js/autoComplete.min.js new file mode 100644 index 0000000..9694263 --- /dev/null +++ b/public/autocomplete/dist/js/autoComplete.min.js @@ -0,0 +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("type","text"),e.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(); diff --git a/public/autocomplete/dist/js/index.js b/public/autocomplete/dist/js/index.js new file mode 100644 index 0000000..23ef04f --- /dev/null +++ b/public/autocomplete/dist/js/index.js @@ -0,0 +1,67 @@ +new autoComplete({ + data: { // Data src [Array, Function, Async] | (REQUIRED) + src: async () => { + // API key token + // const token = "this_is_the_API_token_number"; + // User search query + const query = document.querySelector("#autoComplete").value; + // Fetch External Data Source + const source = await fetch(`http://localhost/api/pokemon.json?nom=${query}`); + // Format data into JSON + const data = await source.json(); + // Return Fetched data + return data; + }, + key: ["nom"], + cache: false + }, + query: { // Query Interceptor | (Optional) + manipulate: (query) => { + return query.replace("Pikachu", "Charmander"); + } + }, + sort: (a, b) => { // Sort rendered results ascendingly | (Optional) + if (a.match < b.match) return -1; + if (a.match > b.match) return 1; + return 0; + }, + placeHolder: "Saisir un nom de pokemon, type, generation...", // Place Holder text | (Optional) + selector: "#autoComplete", // Input field selector | (Optional) + observer: true, // Input field observer | (Optional) + threshold: 3, // Min. Chars length to start Engine | (Optional) + debounce: 300, // Post duration for engine to start | (Optional) + searchEngine: "strict", // Search Engine type/mode | (Optional) + resultsList: { // Rendered results list object | (Optional) + container: source => { + source.setAttribute("id", "pokemon_list"); + }, + destination: "#autoComplete", + position: "afterend", + element: "ul" + }, + maxResults: 5, // Max. number of rendered results | (Optional) + highlight: { + render: true, // Highlight matching results | (Optional) + }, + resultItem: { // Rendered result item | (Optional) + content: (data, source) => { + source.innerHTML = data.match; + }, + element: "li" + }, + noResults: (dataFeedback, generateList) => { + // Generate autoComplete List + generateList(autoCompleteJS, dataFeedback, dataFeedback.results); + // No Results List Item + const result = document.createElement("li"); + result.setAttribute("class", "no_result"); + result.setAttribute("tabindex", "1"); + result.innerHTML = `Found No Results for "${dataFeedback.query}"`; + document.querySelector(`#${autoCompleteJS.resultsList.idName}`).appendChild(result); + }, + onSelection: feedback => { // Action script onSelection event | (Optional) + const resultNameFromAutocomplete = feedback.matches[0].value.nom + document.getElementById("autoComplete").value = resultNameFromAutocomplete + window.location = "/pokemon?keyword=" + resultNameFromAutocomplete + } +}); \ No newline at end of file diff --git a/public/js/customButton.js b/public/js/customButton.js index 8a85ad9..2096621 100644 --- a/public/js/customButton.js +++ b/public/js/customButton.js @@ -3,3 +3,4 @@ event.preventDefault() window.location = "/pokemon" }) + diff --git a/templates/pokemon/index.html.twig b/templates/pokemon/index.html.twig index 87f461d..da21269 100644 --- a/templates/pokemon/index.html.twig +++ b/templates/pokemon/index.html.twig @@ -5,6 +5,7 @@ {% block stylesheets %} +