PageSourceSearch

https://www.payby.me/assets/vendor/multiple-select/multiple-select.js

js payby.me collected 2026-10-02 06:14:46 UTC 12,013 bytes, 326 lines download raw bytes

1const MultiSelectDropdown = (params) => {
2  let config = {
3    search: true,
4    hideX: false,
5    useStyles: true,
6    placeholder: 'Select...',
7    txtSelected: 'Selected',
8    txtAll: 'All',
9    txtRemove: 'Remove',
10    txtSearch: 'Search...',
11    minWidth: '160px',
12    maxWidth: '360px',
13    maxHeight: '180px',
14    borderRadius: 6,
15    ...params
16  };
17
18  const newElement = (tag, params) => {
19      let element = document.createElement(tag);
20    if (params) {
21      Object.keys(params).forEach((key) => {
22        if (key === 'class') {
23          Array.isArray(params[key])
24            ? params[key].forEach((o) => (o !== '' ? element.classList.add(o) : 0))
25            : params[key] !== ''
26            ? element.classList.add(params[key])
27            : 0;
28        } else if (key === 'style') {
29          Object.keys(params[key]).forEach((value) => {
30            element.style[value] = params[key][value];
31          });
32        } else if (key === 'text') {
33          params[key] === '' ? (element.innerHTML = ' ') : (element.innerText = params[key]);
34        } else {
35          element[key] = params[key];
36        }
37      });
38    }
39    return element;
40  };
41
42  document.querySelectorAll('select[multiple]').forEach((multiSelect) => {
43    let div = newElement('div', { class: 'multiselect-dropdown' });
44    multiSelect.style.display = 'none';
45    multiSelect.parentNode.insertBefore(div, multiSelect.nextSibling);
46    let dropdownListWrapper = newElement('div', { class: 'multiselect-dropdown-list-wrapper' });
47    let dropdownList = newElement('div', { class: 'multiselect-dropdown-list' });
48    let search = newElement('input', {
49      class: ['multiselect-dropdown-search'].concat([config.searchInput?.class ?? 'form-control']),
50      style: {
51        width: '100%',
52        display: config.search ? 'block' : multiSelect.attributes.search?.value === 'true' ? 'block' : 'none'
53      },
54      placeholder: config.txtSearch
55    });
56    dropdownListWrapper.appendChild(search);
57    div.appendChild(dropdownListWrapper);
58    dropdownListWrapper.appendChild(dropdownList);
59
60    multiSelect.loadOptions = () => {
61      dropdownList.innerHTML = '';
62
63      if (config.selectAll || multiSelect.attributes['select-all']?.value === 'true') {
64        let optionElementAll = newElement('div', { class: 'multiselect-dropdown-all-selector' });
65        let optionCheckbox = newElement('input', { type: 'checkbox' });
66        optionElementAll.appendChild(optionCheckbox);
67        optionElementAll.appendChild(newElement('label', { text: config.txtAll }));
68
69        optionElementAll.addEventListener('click', () => {
70          optionElementAll.classList.toggle('checked');
71          optionElementAll.querySelector('input').checked = !optionElementAll.querySelector('input').checked;
72
73          let ch = optionElementAll.querySelector('input').checked;
74          dropdownList.querySelectorAll(':scope > div:not(.multiselect-dropdown-all-selector)').forEach((i) => {
75            if (i.style.display !== 'none') {
76              i.querySelector('input').checked = ch;
77              i.optEl.selected = ch;
78            }
79          });
80
81          multiSelect.dispatchEvent(new Event('change'));
82        });
83        optionCheckbox.addEventListener('click', () => {
84          optionCheckbox.checked = !optionCheckbox.checked;
85        });
86
87        dropdownList.appendChild(optionElementAll);
88      }
89
90      Array.from(multiSelect.options).map((option) => {
91        let optionElement = newElement('div', { class: option.selected ? 'checked' : '', srcElement: option });
92        let optionCheckbox = newElement('input', { type: 'checkbox', checked: option.selected });
93        optionElement.appendChild(optionCheckbox);
94        optionElement.appendChild(newElement('label', { text: option.text }));
95
96        optionElement.addEventListener('click', () => {
97          optionElement.classList.toggle('checked');
98          optionElement.querySelector('input').checked = !optionElement.querySelector('input').checked;
99          optionElement.srcElement.selected = !optionElement.srcElement.selected;
100          multiSelect.dispatchEvent(new Event('change'));
101        });
102        optionCheckbox.addEventListener('click', () => {
103          optionCheckbox.checked = !optionCheckbox.checked;
104        });
105        option.optionElement = optionElement;
106        dropdownList.appendChild(optionElement);
107      });
108      div.dropdownListWrapper = dropdownListWrapper;
109
110      div.refresh = () => {
111        div.querySelectorAll('span.optext, span.placeholder').forEach((placeholder) => div.removeChild(placeholder));
112        let selected = Array.from(multiSelect.selectedOptions);
113        if (selected.length > (multiSelect.attributes['max-items']?.value ?? 5)) {
114          div.appendChild(
115            newElement('span', {
116              class: ['optext', 'maxselected'],
117              text: selected.length + ' ' + config.txtSelected
118            })
119          );
120        } else {
121          selected.map((option) => {
122            let span = newElement('span', {
123              class: 'optext',
124              text: option.text,
125              srcElement: option
126            });
127            if (!config.hideX) {
128              span.appendChild(
129                newElement('span', {
130                  class: 'optdel',
131                  text: '🗙',
132                  title: config.txtRemove,
133                  onclick: (e) => {
134                    span.srcElement.optionElement.dispatchEvent(new Event('click'));
135                    div.refresh();
136                    e.stopPropagation();
137                  }
138                })
139              );
140            }
141            div.appendChild(span);
142          });
143        }
144        if (multiSelect.selectedOptions?.length === 0) {
145          div.appendChild(
146            newElement('span', {
147              class: 'placeholder',
148              text: multiSelect.attributes?.placeholder?.value ?? config.placeholder
149            })
150          );
151        }
152      };
153      div.refresh();
154    };
155    multiSelect.loadOptions();
156
157    search.addEventListener('input', () => {
158      dropdownList.querySelectorAll(':scope div:not(.multiselect-dropdown-all-selector)').forEach((div) => {
159        let innerText = div.querySelector('label').innerText.toLowerCase();
160        div.style.display = innerText.includes(search.value.toLowerCase()) ? 'flex' : 'none';
161      });
162    });
163
164    div.addEventListener('click', () => {
165      div.dropdownListWrapper.style.display = 'block';
166      search.focus();
167      search.select();
168    });
169
170    document.addEventListener('click', (e) => {
171      if (!div.contains(e.target)) {
172        dropdownListWrapper.style.display = 'none';
173        div.refresh();
174      }
175    });
176  });
177
178  const createStyles = () => {
179    let styles = {
180      ':root': {
181        '--color-background': '#ffffff',
182        '--color-border': '#efeff2',
183        '--color-background--option': '#d6dde6',
184        '--color-background--option--hover': '#cbd5e0a1',
185        '--color-text--normal': '#0c0c0c',
186        '--color-text--grey': '#24262c',
187        '--color-text--red': '#cc6666',
188        '--color-text--placeholder': '#ced4da',
189        '--border-radius--base': `${parseInt(config.borderRadius)}px` || '6px',
190        '--border-radius--small': `${parseInt(config.borderRadius) * 0.75}px` || '4px'
191      },
192      '.multiselect-dropdown': {
193        position: 'relative',
194        display: 'inline-flex',
195        'flex-wrap': 'wrap',
196        padding: '12px 38px 10px 12px',
197        gap: '6px',
198        'border-radius': 'var(--border-radius--base)',
199        border: 'solid 1px var(--color-border)',
200        background: 'var(--color-background)',
201        'background-image':
202          "url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e\")",
203        'background-repeat': 'no-repeat',
204        'background-position': 'right 6px center',
205        'background-size': '16px 12px',
206        'min-width': `${config.minWidth}` ?? '140px',
207        cursor: 'pointer'
208      },
209      'span.optext, span.placeholder': {
210        display: 'inline-flex',
211        'justify-content': 'center',
212        'align-items': 'center',
213        'font-size': '16px',
214        'border-radius': 'var(--border-radius--small)'
215      },
216      'span.optext': {
217        'background-color': 'var(--color-background--option)',
218        padding: '0 12px 2px 6px',
219        cursor: 'default',
220        '-webkit-user-select': 'none',
221        '-moz-user-select': 'none',
222        '-ms-user-select': 'none',
223        'user-select': 'none'
224      },
225      'span.optext .optdel': {
226        float: 'right',
227        margin: '0 -6px 1px 6px',
228        'font-size': '12px',
229        cursor: 'pointer',
230        color: 'var(--color-text--grey)'
231      },
232      'span.optext .optdel:hover': {
233        color: 'var(--color-text--red)'
234      },
235      'span.placeholder': {
236          color: 'var(--color-text--placeholder)'
237      },
238      '.multiselect-dropdown-list-wrapper': {
239        'z-index': 100,
240        'border-radius': 'var(--border-radius--base)',
241        border: 'solid 1px var(--color-border)',
242        display: 'none',
243        margin: '-1px',
244        position: 'absolute',
245        top: 0,
246        left: 0,
247        right: 0,
248        background: 'var(--color-background)'
249      },
250      '.multiselect-dropdown-search': {
251        padding: '5px 6px 6px 5px',
252        'border-top-left-radius': 'var(--border-radius--base)',
253        'border-top-right-radius': 'var(--border-radius--base)',
254        border: 'solid 1px transparent',
255        'border-bottom': 'solid 1px var(--color-border)',
256        'font-size': 'inherit'
257      },
258      '.multiselect-dropdown-search::placeholder': {
259        color: 'var(--color-text--placeholder)',
260        'font-size': '16px'
261      },
262      '.multiselect-dropdown-search:focus, .multiselect-dropdown-search:focus-visible': {
263        outline: 'none'
264      },
265      '.multiselect-dropdown-list': {
266        'overflow-y': 'auto',
267        'overflow-x': 'hidden',
268        height: '100%',
269        'max-height': `${config.maxHeight}` ?? '160px'
270      },
271      '.multiselect-dropdown-list::-webkit-scrollbar': {
272        width: '4px'
273      },
274      '.multiselect-dropdown-list::-webkit-scrollbar-thumb': {
275        'background-color': 'var(--color-background--option)',
276        'border-radius': '1000px'
277      },
278      '.multiselect-dropdown-list div, .multiselect-dropdown-list div > input, .multiselect-dropdown-list div > label':
279        {
280          cursor: 'pointer',
281          'border-radius': 'var(--border-radius--base)'
282        },
283      '.multiselect-dropdown-list div': {
284        display: 'flex',
285        'align-items': 'center',
286        'justify-content': 'flex-start',
287        'column-gap': '6px',
288        padding: '6px',
289        margin: '6px 8px 6px 6px',
290        transition: '100ms cubic-bezier(0.455, 0.03, 0.515, 0.955)'
291      },
292      '.multiselect-dropdown-list div:hover': {
293        'background-color': 'var(--color-background--option--hover)'
294      },
295      '.multiselect-dropdown-list-input': {
296        height: '14px',
297        width: '14px',
298        border: 'solid 1px var(--color-text--grey)',
299        margin: 0
300      },
301      '.multiselect-dropdown span.maxselected': {
302        width: '100%'
303      },
304      '.multiselect-dropdown-all-selector': {
305        'border-bottom': 'solid 1px var(--color-border)'
306      }
307    };
308    const style = document.createElement('style');
309    style.setAttribute('type', 'text/css');
310    style.innerHTML = `${Object.keys(styles)
311      .map(
312        (selector) =>
313          `${selector} { ${Object.keys(styles[selector])
314            .map((property) => `${property}: ${styles[selector][property]}`)
315            .join('; ')} }`
316      )
317      .join('\n')}`;
318    document.head.appendChild(style);
319  };
320
321  config.useStyles && createStyles();
322};
323
324window.addEventListener('load', () => {
325  MultiSelectDropdown(window.MultiSelectDropdownOptions);
326});

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.