Add support for setting Source and Target sysID (#2886)
[ExpressLRS.git] / src / html / libs.js
blobe4599a91d0574259e27c38a540bcfab224622e40
1 /* eslint-disable max-len */
2 /* eslint-disable comma-dangle */
3 /* eslint-disable require-jsdoc */
5 // =========================================================
7 // Alert box design by Igor Ferrão de Souza: https://www.linkedin.com/in/igor-ferr%C3%A3o-de-souza-4122407b/
9 // eslint-disable-next-line no-unused-vars
10 function cuteAlert({
11   type,
12   title,
13   message,
14   buttonText = 'OK',
15   confirmText = 'OK',
16   cancelText = 'Cancel',
17   closeStyle,
18 }) {
19   return new Promise((resolve) => {
20     setInterval(() => {}, 5000);
21     const body = document.querySelector('body');
23     let closeStyleTemplate = 'alert-close';
24     if (closeStyle === 'circle') {
25       closeStyleTemplate = 'alert-close-circle';
26     }
28     let btnTemplate = `<button class="alert-button ${type}-bg ${type}-btn mui-btn mui-btn--primary">${buttonText}</button>`;
29     if (type === 'question') {
30       btnTemplate = `
31 <div class="question-buttons">
32   <button class="confirm-button error-bg error-btn mui-btn mui-btn--danger">${confirmText}</button>
33   <button class="cancel-button question-bg question-btn mui-btn">${cancelText}</button>
34 </div>
36     }
38     let svgTemplate = `
39 <svg class="alert-img" xmlns="http://www.w3.org/2000/svg" fill="#fff" viewBox="0 0 52 52" xmlns:v="https://vecta.io/nano">
40 <path d="M26 0C11.664 0 0 11.663 0 26s11.664 26 26 26 26-11.663 26-26S40.336 0 26 0zm0 50C12.767 50 2 39.233 2 26S12.767 2 26 2s24 10.767 24 24-10.767 24-24
41 24zm9.707-33.707a1 1 0 0 0-1.414 0L26 24.586l-8.293-8.293a1 1 0 0 0-1.414 1.414L24.586 26l-8.293 8.293a1 1 0 0 0 0 1.414c.195.195.451.293.707.293s.512-.098.707
42 -.293L26 27.414l8.293 8.293c.195.195.451.293.707.293s.512-.098.707-.293a1 1 0 0 0 0-1.414L27.414 26l8.293-8.293a1 1 0 0 0 0-1.414z"/>
43 </svg>
45     if (type === 'success') {
46       svgTemplate = `
47 <svg class="alert-img" xmlns="http://www.w3.org/2000/svg" fill="#fff" viewBox="0 0 52 52" xmlns:v="https://vecta.io/nano">
48 <path d="M26 0C11.664 0 0 11.663 0 26s11.664 26 26 26 26-11.663 26-26S40.336 0 26 0zm0 50C12.767 50 2 39.233 2 26S12.767 2 26 2s24 10.767 24 24-10.767 24-24
49 24zm12.252-34.664l-15.369 17.29-9.259-7.407a1 1 0 0 0-1.249 1.562l10 8a1 1 0 0 0 1.373-.117l16-18a1 1 0 1 0-1.496-1.328z"/>
50 </svg>
52     }
53     if (type === 'info') {
54       svgTemplate = `
55 <svg class="alert-img" xmlns="http://www.w3.org/2000/svg" fill="#fff" viewBox="0 0 64 64" xmlns:v="https://vecta.io/nano">
56 <path d="M38.535 47.606h-4.08V28.447a1 1 0 0 0-1-1h-4.52a1 1 0 1 0 0 2h3.52v18.159h-5.122a1 1 0 1 0 0 2h11.202a1 1 0 1 0 0-2z"/>
57 <circle cx="32" cy="18" r="3"/><path d="M32 0C14.327 0 0 14.327 0 32s14.327 32 32 32 32-14.327 32-32S49.673 0 32 0zm0 62C15.458 62 2 48.542 2 32S15.458 2 32 2s30 13.458 30 30-13.458 30-30 30z"/>
58 </svg>
60     }
62     const template = `
63 <div class="alert-wrapper">
64   <div class="alert-frame">
65     <div class="alert-header ${type}-bg">
66       <span class="${closeStyleTemplate}">X</span>
67       ${svgTemplate}
68     </div>
69     <div class="alert-body">
70       <span class="alert-title">${title}</span>
71       <span class="alert-message">${message}</span>
72       ${btnTemplate}
73     </div>
74   </div>
75 </div>
78     body.insertAdjacentHTML('afterend', template);
80     const alertWrapper = document.querySelector('.alert-wrapper');
81     const alertFrame = document.querySelector('.alert-frame');
82     const alertClose = document.querySelector(`.${closeStyleTemplate}`);
84     function resolveIt() {
85       alertWrapper.remove();
86       resolve();
87     }
88     function confirmIt() {
89       alertWrapper.remove();
90       resolve('confirm');
91     }
92     function stopProp(e) {
93       e.stopPropagation();
94     }
96     if (type === 'question') {
97       const confirmButton = document.querySelector('.confirm-button');
98       const cancelButton = document.querySelector('.cancel-button');
100       confirmButton.addEventListener('click', confirmIt);
101       cancelButton.addEventListener('click', resolveIt);
102     } else {
103       const alertButton = document.querySelector('.alert-button');
105       alertButton.addEventListener('click', resolveIt);
106     }
108     alertClose.addEventListener('click', resolveIt);
109     alertWrapper.addEventListener('click', resolveIt);
110     alertFrame.addEventListener('click', stopProp);
111   });
115 // =========================================================
116 // Autocomplete handler
118 // eslint-disable-next-line no-unused-vars
119 function autocomplete(inp, arr) {
120   /* the autocomplete function takes two arguments,
121   the text field element and an array of possible autocompleted values: */
122   let currentFocus;
124   /* execute a function when someone writes in the text field: */
125   function handler(e) {
126     let b;
127     const val = this.value;
129     /* close any already open lists of autocompleted values */
130     closeAllLists();
131     currentFocus = -1;
132     /* create a DIV element that will contain the items (values): */
133     const a = document.createElement('DIV');
134     a.setAttribute('id', this.id + 'autocomplete-list');
135     a.setAttribute('class', 'autocomplete-items');
136     /* append the DIV element as a child of the autocomplete container: */
137     this.parentNode.appendChild(a);
138     /* for each item in the array... */
139     for (let i = 0; i < arr.length; i++) {
140       /* check if the item starts with the same letters as the text field value :*/
141       if (arr[i].substr(0, val.length).toUpperCase() == val.toUpperCase()) {
142         /* create a DIV element for each matching element: */
143         b = document.createElement('DIV');
144         /* make the matching letters bold :*/
145         b.innerHTML = '<strong>' + arr[i].substr(0, val.length) + '</strong>';
146         b.innerHTML += arr[i].substr(val.length);
147         /* insert a input field that will hold the current array item's value: */
148         b.innerHTML += '<input type="hidden" value="' + arr[i] + '">';
149         /* execute a function when someone clicks on the item value (DIV element): */
150         b.addEventListener('click', ((arg) => (e) => {
151           /* insert the value for the autocomplete text field: */
152           inp.value = arg.getElementsByTagName('input')[0].value;
153           /* close the list of autocompleted values,
154           (or any other open lists of autocompleted values: */
155           closeAllLists();
156         })(b));
157         a.appendChild(b);
158       }
159     }
160   }
161   inp.addEventListener('input', handler);
162   inp.addEventListener('click', handler);
164   /* execute a function presses a key on the keyboard: */
165   inp.addEventListener('keydown', (e) => {
166     let x = _(this.id + 'autocomplete-list');
167     if (x) x = x.getElementsByTagName('div');
168     if (e.keyCode == 40) {
169       /* If the arrow DOWN key is pressed,
170       increase the currentFocus variable: */
171       currentFocus++;
172       /* and and make the current item more visible: */
173       addActive(x);
174     } else if (e.keyCode == 38) { // up
175       /* If the arrow UP key is pressed,
176       decrease the currentFocus variable: */
177       currentFocus--;
178       /* and and make the current item more visible: */
179       addActive(x);
180     } else if (e.keyCode == 13) {
181       /* If the ENTER key is pressed, prevent the form from being submitted, */
182       e.preventDefault();
183       if (currentFocus > -1) {
184         /* and simulate a click on the "active" item: */
185         if (x) x[currentFocus].click();
186       }
187     }
188   });
189   function addActive(x) {
190     /* a function to classify an item as "active": */
191     if (!x) return false;
192     /* start by removing the "active" class on all items: */
193     removeActive(x);
194     if (currentFocus >= x.length) currentFocus = 0;
195     if (currentFocus < 0) currentFocus = (x.length - 1);
196     /* add class "autocomplete-active": */
197     x[currentFocus].classList.add('autocomplete-active');
198   }
199   function removeActive(x) {
200     /* a function to remove the "active" class from all autocomplete items: */
201     for (let i = 0; i < x.length; i++) {
202       x[i].classList.remove('autocomplete-active');
203     }
204   }
205   function closeAllLists(elmnt) {
206     /* close all autocomplete lists in the document,
207     except the one passed as an argument: */
208     const x = document.getElementsByClassName('autocomplete-items');
209     for (let i = 0; i < x.length; i++) {
210       if (elmnt != x[i] && elmnt != inp) {
211         x[i].parentNode.removeChild(x[i]);
212       }
213     }
214   }
215   /* execute a function when someone clicks in the document: */
216   document.addEventListener('click', (e) => {
217     closeAllLists(e.target);
218   });