1var style = document.createElement('style'); 2style.setAttribute("id", "multiselect_dropdown_styles"); 3style.innerHTML = ` 4.multiselect-dropdown{ 5 display: inline-block; 6 padding: 2px 5px 0px 5px; 7 border-radius: 4px; 8 border: solid 1px #ced4da; 9 background-color: white; 10 position: relative; 11 background-image: 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"); 12 background-repeat: no-repeat; 13 background-position: right .75rem center; 14 background-size: 16px 12px; 15} 16.multiselect-dropdown span.optext, .multiselect-dropdown span.placeholder{ 17 margin-right:0.5em; 18 margin-bottom:2px; 19 padding:1px 0; 20 border-radius: 4px; 21 display:inline-block; 22} 23.multiselect-dropdown span.optext{ 24 background-color:lightgray; 25 padding:1px 0.75em; 26} 27.multiselect-dropdown span.optext .optdel { 28 float: right; 29 margin: 0 -6px 1px 5px; 30 font-size: 0.7em; 31 margin-top: 2px; 32 cursor: pointer; 33 color: #666; 34} 35.multiselect-dropdown span.optext .optdel:hover { color: #c66;} 36.multiselect-dropdown span.placeholder{ 37 color:#ced4da; 38} 39.multiselect-dropdown-list-wrapper{ 40 box-shadow: gray 0 3px 8px; 41 z-index: 100; 42 padding:2px; 43 border-radius: 4px; 44 border: solid 1px #ced4da; 45 display: none; 46 margin: -1px; 47 position: absolute; 48 top:0; 49 left: 0; 50 right: 0; 51 background: white; 52} 53.multiselect-dropdown-list-wrapper .multiselect-dropdown-search{ 54 margin-bottom:5px; 55} 56.multiselect-dropdown-list{ 57 padding:2px; 58 height: 15rem; 59 overflow-y:auto; 60 overflow-x: hidden; 61} 62.multiselect-dropdown-list::-webkit-scrollbar {
63 width: 6px; 64} 65.multiselect-dropdown-list::-webkit-scrollbar-thumb { 66 background-color: #bec4ca; 67 border-radius:3px; 68} 69 70.multiselect-dropdown-list div{ 71 padding: 5px; 72} 73.multiselect-dropdown-list input{ 74 height: 1.15em; 75 width: 1.15em; 76 margin-right: 0.35em; 77} 78.multiselect-dropdown-list div.checked{ 79} 80.multiselect-dropdown-list div:hover{ 81 background-color: #ced4da; 82} 83.multiselect-dropdown span.maxselected {width:100%;} 84.multiselect-dropdown-all-selector {border-bottom:solid 1px #999;} 85`; 86document.head.appendChild(style); 87 88function MultiselectDropdown(options) { 89 var config = { 90 search: true, 91 height: '15rem', 92 placeholder: 'select', 93 txtSelected: 'selected', 94 txtAll: 'All', 95 txtRemove: 'Remove', 96 txtSearch: 'search', 97 ...options 98 }; 99 function newEl(tag, attrs) { 100 var e = document.createElement(tag); 101 if (attrs !== undefined) Object.keys(attrs).forEach(k => { 102 if (k === 'class') { Array.isArray(attrs[k]) ? attrs[k].forEach(o => o !== '' ? e.classList.add(o) : 0) : (attrs[k] !== '' ? e.classList.add(attrs[k]) : 0) } 103 else if (k === 'style') { 104 Object.keys(attrs[k]).forEach(ks => { 105 e.style[ks] = attrs[k][ks]; 106 }); 107 } 108 else if (k === 'text') { attrs[k] === '' ? e.innerHTML = ' ' : e.innerText = attrs[k] } 109 else e[k] = attrs[k]; 110 }); 111 return e; 112 } 113 114 115 document.querySelectorAll("select[multiple]").forEach((el, k) => { 116 117 var div = newEl('div', { class: 'multiselect-dropdown', style: { width: config.style?.width ?? el.clientWidth + 'px', padding: config.style?.padding ?? '' } }); 118 el.style.display = 'none'; 119 el.parentNode.insertBefore(div, el.nextSibling); 120 var listWrap = newEl('div', { class: 'multiselect-dropdown-list-wrapper' }); 121 var list = newEl('div', { class: 'multiselect-dropdown-list', style: { height: config.height } }); 122 var search = newEl('input', { class: ['multiselect-dropdown-search'].concat([config.searchInput?.class ?? 'form-control']), style: { width: '100%', display: el.attributes['multiselect-search']?.value === 'true' ? 'block' : 'none' }, placeholder: config.txtSearch }); 123 listWrap.appendChild(search); 124 div.appendChild(listWrap); 125 listWrap.appendChild(list); 126 127 el.loadOptions = () => { 128 list.innerHTML = ''; 129 130 if (el.attributes['multiselect-select-all']?.value == 'true') { 131 var op = newEl('div', { class: 'multiselect-dropdown-all-selector' }) 132 var ic = newEl('input', { type: 'checkbox' }); 133 op.appendChild(ic); 134 op.appendChild(newEl('label', { text: config.txtAll })); 135 136 op.addEventListener('click', () => { 137 op.classList.toggle('checked'); 138 op.querySelector("input").checked = !op.querySelector("input").checked; 139 140 var ch = op.querySelector("input").checked; 141 list.querySelectorAll(":scope > div:not(.multiselect-dropdown-all-selector)") 142 .forEach(i => { if (i.style.display !== 'none') { i.querySelector("input").checked = ch; i.optEl.selected = ch } }); 143 144 el.dispatchEvent(new Event('change')); 145 }); 146 ic.addEventListener('click', (ev) => { 147 ic.checked = !ic.checked; 148 }); 149 el.addEventListener('change', (ev) => { 150 let itms = Array.from(list.querySelectorAll(":scope > div:not(.multiselect-dropdown-all-selector)")).filter(e => e.style.display !== 'none') 151 let existsNotSelected = itms.find(i => !i.querySelector("input").checked); 152 if (ic.checked && existsNotSelected) ic.checked = false; 153 else if (ic.checked == false && existsNotSelected === undefined) ic.checked = true; 154 }); 155 156 list.appendChild(op); 157 } 158 159 Array.from(el.options).map(o => { 160 var op = newEl('div', { class: o.selected ? 'checked' : '', optEl: o }) 161 var ic = newEl('input', { type: 'checkbox', checked: o.selected }); 162 op.appendChild(ic); 163 op.appendChild(newEl('label', { text: o.text })); 164 165 op.addEventListener('click', () => { 166 op.classList.toggle('checked'); 167 op.querySelector("input").checked = !op.querySelector("input").checked; 168 op.optEl.selected = !!!op.optEl.selected; 169 el.dispatchEvent(new Event('change')); 170 }); 171 ic.addEventListener('click', (ev) => { 172 ic.checked = !ic.checked; 173 }); 174 o.listitemEl = op; 175 list.appendChild(op); 176 }); 177 div.listEl = listWrap; 178 179 div.refresh = () => { 180 div.querySelectorAll('span.optext, span.placeholder').forEach(t => div.removeChild(t)); 181 var sels = Array.from(el.selectedOptions); 182 if (sels.length > (el.attributes['multiselect-max-items']?.value ?? 5)) { 183 div.appendChild(newEl('span', { class: ['optext', 'maxselected'], text: sels.length + ' ' + config.txtSelected })); 184 } 185 else { 186 sels.map(x => {
187 var c = newEl('span', { class: 'optext', text: x.text, srcOption: x }); 188 if ((el.attributes['multiselect-hide-x']?.value !== 'true')) 189 c.appendChild(newEl('span', { class: 'optdel', text: 'x', title: config.txtRemove, onclick: (ev) => { c.srcOption.listitemEl.dispatchEvent(new Event('click')); div.refresh(); ev.stopPropagation(); } })); 190 191 div.appendChild(c); 192 }); 193 } 194 if (0 == el.selectedOptions.length) div.appendChild(newEl('span', { class: 'placeholder', text: el.attributes['placeholder']?.value ?? config.placeholder })); 195 }; 196 div.refresh(); 197 } 198 el.loadOptions(); 199 200 search.addEventListener('input', () => { 201 list.querySelectorAll(":scope div:not(.multiselect-dropdown-all-selector)").forEach(d => { 202 var txt = d.querySelector("label").innerText.toUpperCase(); 203 d.style.display = txt.includes(search.value.toUpperCase()) ? 'block' : 'none'; 204 }); 205 }); 206 207 div.addEventListener('click', () => { 208 div.listEl.style.display = 'block'; 209 search.focus(); 210 search.select(); 211 }); 212 213 document.addEventListener('click', function (event) { 214 if (!div.contains(event.target)) { 215 listWrap.style.display = 'none'; 216 div.refresh(); 217 } 218 }); 219 }); 220} 221 222window.addEventListener('load', () => { 223 MultiselectDropdown(window.MultiselectDropdownOptions); 224});
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.