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