1/** 2 * Copyright (c) 2024-2025, The Linux Foundation. 3 * SPDX-License-Identifier: Apache-2.0 4 */ 5 6function toggleDisplayMode(btn) { 7 const catalog = document.getElementById("catalog"); 8 catalog.classList.toggle("compact"); 9 btn.classList.toggle("fa-bars"); 10 btn.classList.toggle("fa-th"); 11 btn.textContent = catalog.classList.contains("compact") 12 ? " Switch to Card View" 13 : " Switch to Compact View"; 14} 15 16function populateFormFromURL() { 17 const params = ["name", "arch", "vendor", "soc"]; 18 const hashParams = new URLSearchParams(window.location.hash.slice(1)); 19 params.forEach((param) => { 20 const element = document.getElementById(param); 21 if (hashParams.has(param)) { 22 const value = hashParams.get(param); 23 if (param === "soc") { 24 value.split(",").forEach(soc => 25 element.querySelector(`option[value="${soc}"]`).selected = true); 26 } else { 27 element.value = value; 28 } 29 } 30 }); 31 32 // Restore supported features from URL 33 if (hashParams.has("features")) { 34 const features = hashParams.get("features").split(","); 35 setTimeout(() => { 36 features.forEach(feature => { 37 const tagContainer = document.getElementById('tag-container'); 38 const tagInput = document.getElementById('tag-input'); 39 40 const tagElement = document.createElement('span'); 41 tagElement.classList.add('tag'); 42 tagElement.textContent = feature; 43 tagElement.onclick = () => { 44 const selectedTags = [...document.querySelectorAll('.tag')].map(tag => tag.textContent); 45 selectedTags.splice(selectedTags.indexOf(feature), 1); 46 tagElement.remove(); 47 filterBoards(); 48 }; 49 tagContainer.insertBefore(tagElement, tagInput); 50 }); 51 filterBoards(); 52 }, 0); 53 } 54 55 filterBoards(); 56} 57 58function updateURL() { 59 const params = ["name", "arch", "vendor", "soc"]; 60 const hashParams = new URLSearchParams(window.location.hash.slice(1)); 61 62 params.forEach((param) => { 63 const element = document.getElementById(param); 64 if (param === "soc") { 65 const selectedSocs = [...element.selectedOptions].map(({ value }) => value); 66 selectedSocs.length ? hashParams.set(param, selectedSocs.join(",")) : hashParams.delete(param); 67 } 68 else { 69 element.value ? hashParams.set(param, element.value) : hashParams.delete(param); 70 } 71 }); 72 73 // Add supported features to URL 74 const selectedTags = [...document.querySelectorAll('.tag')].map(tag => tag.textContent); 75 selectedTags.length ? hashParams.set("features", selectedTags.join(",")) : hashParams.delete("features"); 76 77 window.history.replaceState({}, "", `#${hashParams.toString()}`); 78} 79 80function fillSocFamilySelect() { 81 const socFamilySelect = document.getElementById("family"); 82 83 Object.keys(socs_data).sort().forEach(f => { 84 socFamilySelect.add(new Option(f)); 85 }); 86} 87 88function fillSocSeriesSelect(families, selectOnFill = false) { 89 const socSeriesSelect = document.getElementById("series"); 90 91 families = families?.length ? families : Object.keys(socs_data); 92 let allSeries = [...new Set(families.flatMap(f => Object.keys(socs_data[f])))]; 93 94 socSeriesSelect.innerHTML = ""; 95 allSeries.sort().map(s => { 96 const option = new Option(s, s, selectOnFill, selectOnFill); 97 socSeriesSelect.add(option); 98 }); 99} 100 101function fillSocSocSelect(families, series = undefined, selectOnFill = false) { 102 const socSocSelect = document.getElementById("soc"); 103 104 families = families?.length ? families : Object.keys(socs_data); 105 series = series?.length ? series : families.flatMap(f => Object.keys(socs_data[f])); 106 matchingSocs = [...new Set(families.flatMap(f => series.flatMap(s => socs_data[f][s] || [])))]; 107 108 socSocSelect.innerHTML = ""; 109 matchingSocs.sort().forEach((soc) => { 110 socSocSelect.add(new Option(soc, soc, selectOnFill, selectOnFill)); 111 }); 112} 113 114function setupHWCapabilitiesField() { 115 let selectedTags = []; 116 117 const tagContainer = document.getElementById('tag-container'); 118 const tagInput = document.getElementById('tag-input'); 119 const datalist = document.getElementById('tag-list'); 120 121 const tagCounts = Array.from(document.querySelectorAll('.board-card')).reduce((acc, board) => { 122 board.getAttribute('data-supported-features').split(' ').forEach(tag => { 123 acc[tag] = (acc[tag] || 0) + 1; 124 }); 125 return acc; 126 }, {}); 127 128 const allTags = Object.keys(tagCounts).sort(); 129 130 function addTag(tag) { 131 if (selectedTags.includes(tag) || tag === "" || !allTags.includes(tag)) return; 132 selectedTags.push(tag); 133 134 const tagElement = document.createElement('span'); 135 tagElement.classList.add('tag'); 136 tagElement.textContent = tag; 137 tagElement.onclick = () => removeTag(tag); 138 tagContainer.insertBefore(tagElement, tagInput); 139 140 tagInput.value = ''; 141 updateDatalist(); 142 } 143 144 function removeTag(tag) { 145 selectedTags = selectedTags.filter(t => t !== tag); 146 document.querySelectorAll('.tag').forEach(el => { 147 if (el.textContent.includes(tag)) el.remove(); 148 }); 149 updateDatalist(); 150 } 151 152 function updateDatalist() { 153 datalist.innerHTML = ''; 154 const filteredTags = allTags.filter(tag => !selectedTags.includes(tag)); 155 156 filteredTags.forEach(tag => { 157 const option = document.createElement('option'); 158 option.value = tag; 159 datalist.appendChild(option); 160 }); 161 162 filterBoards(); 163 } 164 165 tagInput.addEventListener('input', () => { 166 if (allTags.includes(tagInput.value)) { 167 addTag(tagInput.value); 168 } 169 }); 170 171 // Add tag when pressing the Enter key 172 tagInput.addEventListener('keydown', (e) => { 173 if (e.key === 'Enter' && allTags.includes(tagInput.value)) { 174 addTag(tagInput.value); 175 e.preventDefault(); 176 } 177 }); 178 179 // Delete tag when pressing the Backspace key 180 tagInput.addEventListener('keydown', (e) => { 181 if (e.key === 'Backspace' && tagInput.value === '' && selectedTags.length > 0) { 182 removeTag(selectedTags[selectedTags.length - 1]); 183 } 184 }); 185 186 updateDatalist(); 187} 188 189document.addEventListener("DOMContentLoaded", function () { 190 const form = document.querySelector(".filter-form"); 191 192 // sort vendors alphabetically 193 vendorSelect = document.getElementById("vendor"); 194 vendorOptions = Array.from(vendorSelect.options).slice(1); 195 vendorOptions.sort((a, b) => a.text.localeCompare(b.text)); 196 while (vendorSelect.options.length > 1) { 197 vendorSelect.remove(1); 198 } 199 vendorOptions.forEach((option) => { 200 vendorSelect.appendChild(option); 201 }); 202 203 fillSocFamilySelect(); 204 fillSocSeriesSelect(); 205 fillSocSocSelect(); 206 populateFormFromURL(); 207 208 setupHWCapabilitiesField(); 209 210 socFamilySelect = document.getElementById("family"); 211 socFamilySelect.addEventListener("change", () => { 212 const selectedFamilies = [...socFamilySelect.selectedOptions].map(({ value }) => value); 213 fillSocSeriesSelect(selectedFamilies, true); 214 fillSocSocSelect(selectedFamilies, undefined, true); 215 filterBoards(); 216 }); 217 218 socSeriesSelect = document.getElementById("series"); 219 socSeriesSelect.addEventListener("change", () => { 220 const selectedFamilies = [...socFamilySelect.selectedOptions].map(({ value }) => value); 221 const selectedSeries = [...socSeriesSelect.selectedOptions].map(({ value }) => value); 222 fillSocSocSelect(selectedFamilies, selectedSeries, true); 223 filterBoards(); 224 }); 225 226 socSocSelect = document.getElementById("soc"); 227 socSocSelect.addEventListener("change", () => { 228 filterBoards(); 229 }); 230 231 form.addEventListener("input", function () { 232 filterBoards(); 233 }); 234 235 form.addEventListener("submit", function (event) { 236 event.preventDefault(); 237 }); 238 239 filterBoards(); 240}); 241 242function resetForm() { 243 const form = document.querySelector(".filter-form"); 244 form.reset(); 245 fillSocFamilySelect(); 246 fillSocSeriesSelect(); 247 fillSocSocSelect(); 248 249 // Clear supported features 250 document.querySelectorAll('.tag').forEach(tag => tag.remove()); 251 document.getElementById('tag-input').value = ''; 252 253 filterBoards(); 254} 255 256function updateBoardCount() { 257 const boards = document.getElementsByClassName("board-card"); 258 const visibleBoards = Array.from(boards).filter( 259 (board) => !board.classList.contains("hidden") 260 ).length; 261 const totalBoards = boards.length; 262 document.getElementById("nb-matches").textContent = `Showing ${visibleBoards} of ${totalBoards}`; 263} 264 265function filterBoards() { 266 const nameInput = document.getElementById("name").value.toLowerCase(); 267 const archSelect = document.getElementById("arch").value; 268 const vendorSelect = document.getElementById("vendor").value; 269 const socSocSelect = document.getElementById("soc"); 270 271 const selectedTags = [...document.querySelectorAll('.tag')].map(tag => tag.textContent); 272 273 const resetFiltersBtn = document.getElementById("reset-filters"); 274 if (nameInput || archSelect || vendorSelect || socSocSelect.selectedOptions.length || selectedTags.length) { 275 resetFiltersBtn.classList.remove("btn-disabled"); 276 } else { 277 resetFiltersBtn.classList.add("btn-disabled"); 278 } 279 280 const boards = document.getElementsByClassName("board-card"); 281 282 Array.from(boards).forEach(function (board) { 283 const boardName = board.getAttribute("data-name").toLowerCase(); 284 const boardArchs = board.getAttribute("data-arch").split(" "); 285 const boardVendor = board.getAttribute("data-vendor"); 286 const boardSocs = board.getAttribute("data-socs").split(" "); 287 const boardSupportedFeatures = board.getAttribute("data-supported-features").split(" "); 288 289 let matches = true; 290 291 const selectedSocs = [...socSocSelect.selectedOptions].map(({ value }) => value); 292 293 matches = 294 !(nameInput && !boardName.includes(nameInput)) && 295 !(archSelect && !boardArchs.includes(archSelect)) && 296 !(vendorSelect && boardVendor !== vendorSelect) && 297 (selectedSocs.length === 0 || selectedSocs.some((soc) => boardSocs.includes(soc))) && 298 (selectedTags.length === 0 || selectedTags.every((tag) => boardSupportedFeatures.includes(tag))); 299 300 board.classList.toggle("hidden", !matches); 301 }); 302 303 updateURL(); 304 updateBoardCount(); 305} 306