PageSourceSearch

https://www.inesc-id.pt/wp-content/plugins/inesc-pages/js/intranet…/javascript/jsFilter_main.js?ver=1790846058

js inesc-id.pt collected 2026-10-01 09:20:18 UTC 34,658 bytes, 1,016 lines download raw bytes

1// First function run if not run locally, it initializes everything
2//  - data: the fetched dataset from the API
3//  - cardInfo: person info if in the person context
4function jsFilter_initList(data, cardInfo) {
5  jsFilter_initEventHandlers();
6  //data = jsFilter_removeDuplicatesObjects(data);
7  jsFilter_putLoadingScreen();
8  setTimeout(() => {
9    jsFilter_intranetAdaptations(data, cardInfo);
10
11    jsFilter_initNavButtons(cardInfo);
12
13    const listingPanel = document.getElementById("listingPanel");
14
15    // On a person's page (author profile shown above), leave extra vertical
16    // space between their information and the lists below.
17    if (
18      jsFilter_metaFilters["bibtexRankedAuthorsIdName"] != null &&
19      jsFilter_metaFilters["bibtexRankedAuthorsIdName"] != ""
20    ) {
21      listingPanel.classList.add("member-listingJS");
22    }
23
24    const infoPanel = iidHTML("div", "");
25    infoPanel.id = "infoPanel";
26
27    listingPanel.appendChild(infoPanel);
28
29    if (data.length == 0) {
30      jsFilter_removeLoadingScreen();
31      const noInfo = iidHTML("h2", "");
32      switch (jsFilter_currentContext) {
33        case "Publications":
34          noInfo.textContent = "No publications available.";
35          infoPanel.appendChild(noInfo);
36          break;
37        case "Supervision":
38          noInfo.textContent = "No supervisions available.";
39          infoPanel.appendChild(noInfo);
40          break;
41        case "Projects":
42          noInfo.textContent = "No projects available.";
43          infoPanel.appendChild(noInfo);
44          break;
45        case "About":
46          const biographyHeader = iidHTML("h2", "Biography");
47          if (
48            cardInfo["biography"] == null ||
49            cardInfo["biography"] == "" ||
50            cardInfo["biography"] == "<p><span style="
51          ) {
52            biographyHeader.textContent = "No biography available.";
53            infoPanel.appendChild(biographyHeader);
54          } else {
55            const biography = iidHTML("div", "");
56            biography.innerHTML = cardInfo["biography"];
57            infoPanel.appendChild(biographyHeader);
58            infoPanel.appendChild(biography);
59          }
60          if (
61            !(
62              cardInfo["mainInterests"] == null ||
63              cardInfo["mainInterests"] == "" ||
64              cardInfo["mainInterests"] == "<p><span style="
65            )
66          ) {
67            const mainInterestsHeader = iidHTML( "h2", "Main Interests");
68            const mainInterests = iidHTML("div", "");
69            mainInterests.innerHTML = cardInfo["mainInterests"];
70            infoPanel.appendChild(mainInterestsHeader);
71            infoPanel.appendChild(mainInterests);
72          }
73          break;
74
75        default:
76          break;
77      }
78    } else {
79      const title = iidHTML("h2", "");
80      title.id = "title";
81
82      const orderDIV = iidHTML("div", "");
83      orderDIV.id = "orderDIV";
84
85      infoPanel.appendChild(title);
86      infoPanel.appendChild(orderDIV);
87
88      // Group the heading, count, and ordering controls into a single top bar.
89      // The page heading (a plain title, not an author card) moves into the
90      // bar; author pages have no heading here and keep their card above.
91      if (
92        jsFilter_currentContext === "Publications" ||
93        jsFilter_currentContext === "People" ||
94        jsFilter_currentContext === "Supervision" ||
95        jsFilter_currentContext === "Projects"
96      ) {
97        infoPanel.classList.add("listing-topbarJS");
98        const heading = document.querySelector("#authorNav h1");
99        if (jsFilter_isGlobalAllListing()) {
100          // The combined #title ("Publications 2013-2016 (386)") becomes the
101          // title here, replacing the "All X" heading.
102          title.classList.add("listing-title-mainJS");
103          if (heading != null)
104            document.getElementById("authorNav").style.display = "none";
105        } else if (heading != null) {
106          infoPanel.insertBefore(heading, infoPanel.firstChild);
107          document.getElementById("authorNav").style.display = "none";
108        }
109      }
110
111      const itemList = iidHTML("div", "");
112      itemList.id = "itemList";
113
114      listingPanel.appendChild(itemList);
115
116      const filterPanel = iidHTML("div", "");
117      filterPanel.id = "filter-panel";
118
119      const rightSide = document.getElementById("rightSide");
120
121      rightSide.appendChild(filterPanel);
122
123      jsFilter_originalData = jsFilter_stringDataToInt(
124        data,
125        jsFilter_orderMapping[jsFilter_currentContext]["Year"]
126      );
127      jsFilter_initOrderBy();
128      jsFilter_initFilterPanel(jsFilter_contexMapping[jsFilter_currentContext]);
129      jsFilter_createList(jsFilter_originalData);
130      let needsReCalculate = false;
131      Object.keys(jsFilter_currentFilters).forEach((key) => {
132        if (jsFilter_currentFilters[key].length > 0) needsReCalculate = true;
133      });
134      setTimeout(() => {
135        if (needsReCalculate) {
136          jsFilter_reCalculateFilteredList();
137        }
138        jsFilter_removeLoadingScreen();
139      }, 100);
140    }
141  }, 100);
142}
143
144// Function that intializes the top part of the page, including the buttons.
145//  - cardInfo: person info if in the person context
146async function jsFilter_initNavButtons(cardInfo) {
147  jsFilter_putLoadingScreen();
148  const mediaQuery = window.matchMedia("(max-width: 700px)");
149  if (mediaQuery.matches)
150    d3.select("#page-container").style("margin-top", "25px");
151  var buttonAbout;
152  var buttonPeople;
153  var buttonPublications;
154  var buttonSupervision;
155  var buttonProjects;
156
157  const body = document.getElementById("mainBody");
158
159  const leftSide = iidHTML("div", "", {}, {});
160  leftSide.id = "leftSide";
161
162  const navPanel = iidHTML("div", "");
163  navPanel.id = "navPanel";
164
165  const authorNav = iidHTML("div", "");
166  authorNav.id = "authorNav";
167
168  const buttonsNav = iidHTML("div", "");
169  buttonsNav.id = "buttonsNav";
170
171  navPanel.appendChild(authorNav);
172  navPanel.appendChild(buttonsNav);
173
174  const listingPanel = iidHTML("div", "");
175  listingPanel.id = "listingPanel";
176
177  const mobileButtonDIV = iidHTML("div");
178  mobileButtonDIV.id = "mobileButtonDIV";
179
180  const sidebarInfo = iidHTML( "strong", "◀ Click to filter", {},
181    {
182      click: function () {
183        if (d3.select("#rightSide").style("left") != "0%") {
184          d3.select("#rightSide")
185            .transition()
186            .duration(500)
187            .style("left", "0%");
188          document.getElementById("sidebarInfo").textContent = "▶ Click to filter";
189        } else {
190          d3.select("#rightSide")
191            .transition()
192            .duration(500)
193            .style("left", "-100%");
194          document.getElementById("sidebarInfo").textContent = "◀ Click to filter";
195        }
196      },
197    },
198    ["sideBar"]
199  );
200  sidebarInfo.id = "sidebarInfo";
201
202  mobileButtonDIV.appendChild(sidebarInfo);
203
204  if (jsFilter_currentContext != "About") body.appendChild(mobileButtonDIV);
205
206  leftSide.appendChild(navPanel);
207  leftSide.appendChild(listingPanel);
208
209  const rightSide = iidHTML("div", "");
210  rightSide.id = "rightSide";
211
212  body.appendChild(leftSide);
213  body.appendChild(rightSide);
214
215  buttonAbout = iidHTML(
216    "button",
217    "About",
218    {},
219    {
220      click: function () {
221        jsFilter_putLoadingScreen();
222        setTimeout(() => {
223          window.location.href = jsFilter_urlREF + "member/" + jsFilter_metaFilters["bibtexRankedAuthorsIdName"] + "/";
224        }, 100);
225      },
226    },
227    ["buttonsNav"]
228  );
229  buttonAbout.id = "buttonAbout";
230
231  buttonPeople = iidHTML( "button", "People", {},
232    {
233      click: function () {
234        jsFilter_putLoadingScreen();
235        setTimeout(() => {
236          window.location.href = jsFilter_urlREF + "ou/" + jsFilter_metaFilters["organizationalUnitIdAcronymName"] + "/members/";
237        }, 100);
238      },
239    },
240    ["buttonsNav"]
241  );
242  buttonPeople.id = "buttonPeople";
243
244  buttonPublications = iidHTML( "button", "Publications", {},
245    {
246      click: function () {
247        jsFilter_putLoadingScreen();
248        setTimeout(() => {
249          if (
250            jsFilter_metaFilters["bibtexRankedAuthorsIdName"] != "" &&
251            jsFilter_metaFilters["bibtexRankedAuthorsIdName"] != null
252          ) {
253            window.location.href = jsFilter_urlREF + "member/" + jsFilter_metaFilters["bibtexRankedAuthorsIdName"] + "/publications/";
254          } else {
255            window.location.href = jsFilter_urlREF + "ou/" + jsFilter_metaFilters["organizationalUnitIdAcronymName"] + "/publications/";
256          }
257        }, 100);
258      },
259    },
260    ["buttonsNav"]
261  );
262  buttonPublications.id = "buttonPublications";
263
264  buttonSupervision = iidHTML( "button", "Supervisions", {},
265    {
266      click: function () {
267        jsFilter_putLoadingScreen();
268        setTimeout(() => {
269          if (
270            jsFilter_metaFilters["bibtexRankedAuthorsIdName"] != "" &&
271            jsFilter_metaFilters["bibtexRankedAuthorsIdName"] != null
272          ) {
273            window.location.href = jsFilter_urlREF + "member/" + jsFilter_metaFilters["bibtexRankedAuthorsIdName"] + "/supervisions/";
274          } else {
275            window.location.href = jsFilter_urlREF + "ou/" + jsFilter_metaFilters["organizationalUnitIdAcronymName"] + "/supervisions/";
276          }
277        }, 100);
278      },
279    },
280    ["buttonsNav"]
281  );
282  buttonSupervision.id = "buttonSupervision";
283
284  buttonProjects = iidHTML( "button", "Projects", {},
285    {
286      click: function () {
287        jsFilter_putLoadingScreen();
288        setTimeout(() => {
289          if (
290            jsFilter_metaFilters["bibtexRankedAuthorsIdName"] != "" &&
291            jsFilter_metaFilters["bibtexRankedAuthorsIdName"] != null
292          ) {
293            window.location.href = jsFilter_urlREF + "member/" + jsFilter_metaFilters["bibtexRankedAuthorsIdName"] + "/projects/";
294          } else {
295            window.location.href = jsFilter_urlREF + "ou/" + jsFilter_metaFilters["organizationalUnitIdAcronymName"] + "/projects/";
296          }
297        }, 100);
298      },
299    },
300    ["buttonsNav"]
301  );
302  buttonProjects.id = "buttonProjects";
303
304  if (
305    jsFilter_metaFilters["bibtexRankedAuthorsIdName"] != "" &&
306    jsFilter_metaFilters["bibtexRankedAuthorsIdName"] != null
307  ) {
308    buttonPeople.style.display = "none";
309    var author = cardInfo;
310    const card = jsFilter_createCard(author);
311    document.getElementById("authorNav").appendChild(card);
312  } else {
313    buttonAbout.style.display = "none";
314    if (jsFilter_metaFilters["organizationalUnitIdAcronymName"] == "") {
315      buttonPeople.style.display = "none";
316      buttonPublications.style.display = "none";
317      buttonSupervision.style.display = "none";
318      buttonProjects.style.display = "none";
319      if (
320        jsFilter_metaFilters["newRoleTypesId"] != "" &&
321        jsFilter_metaFilters["newRoleTypesId"] != null
322      ) {
323        const peopleHeader = iidHTML( "h1", jsFilter_people_types_group[jsFilter_metaFilters["newRoleTypesId"]]);
324        authorNav.appendChild(peopleHeader);
325      } else if (
326        jsFilter_metaFilters["typeId"] != "" &&
327        jsFilter_metaFilters["typeId"] != null
328      ) {
329        const peopleHeader = iidHTML( "h1", jsFilter_projectTypes[jsFilter_metaFilters["typeId"]]);
330        authorNav.appendChild(peopleHeader);
331      } else {
332        var headerText;
333        switch (jsFilter_currentContext) {
334          case "Publications":
335            headerText = "All Publications";
336            break;
337          case "Supervision":
338            headerText = "All Supervisions";
339            break;
340          case "Projects":
341            headerText = "All Projects";
342            break;
343          case "People":
344            headerText = "All People";
345            break;
346        }
347        const globalHeader = iidHTML("h1", headerText);
348        authorNav.appendChild(globalHeader);
349      }
350    } else {
351      const saHeader = iidHTML( "h1", jsFilter_peopleScientificAreasComplete[ jsFilter_metaFilters["organizationalUnitIdAcronymName"] ]);
352      authorNav.appendChild(saHeader);
353      buttonPeople.style.display = "block";
354      buttonPublications.style.display = "block";
355      buttonSupervision.style.display = "block";
356      buttonProjects.style.display = "block";
357    }
358  }
359
360  var buttonArray = [
361    buttonAbout,
362    buttonPeople,
363    buttonPublications,
364    buttonSupervision,
365    buttonProjects,
366  ];
367
368  const changeActive = function () {
369    buttonArray.forEach((element) => {
370      if (element != null) {
371        element.classList.remove("buttonSectionActive");
372        element.classList.add("buttonSectionInactive");
373      }
374    });
375    if (jsFilter_currentContext == "About") {
376      buttonAbout.classList.remove("buttonSectionInactive");
377      buttonAbout.classList.add("buttonSectionActive");
378    } else if (jsFilter_currentContext == "People") {
379      buttonPeople.classList.remove("buttonSectionInactive");
380      buttonPeople.classList.add("buttonSectionActive");
381    } else if (jsFilter_currentContext == "Publications") {
382      buttonPublications.classList.remove("buttonSectionInactive");
383      buttonPublications.classList.add("buttonSectionActive");
384    } else if (jsFilter_currentContext == "Supervision") {
385      buttonSupervision.classList.remove("buttonSectionInactive");
386      buttonSupervision.classList.add("buttonSectionActive");
387    } else if (jsFilter_currentContext == "Projects") {
388      buttonProjects.classList.remove("buttonSectionInactive");
389      buttonProjects.classList.add("buttonSectionActive");
390    }
391  };
392
393  changeActive();
394
395  buttonsNav.appendChild(buttonAbout);
396  buttonsNav.appendChild(buttonPeople);
397  buttonsNav.appendChild(buttonPublications);
398  buttonsNav.appendChild(buttonSupervision);
399  buttonsNav.appendChild(buttonProjects);
400}
401
402// First function run if run locally, it starts to fetch information depending on the meta filters
403async function jsFilter_init() {
404  jsFilter_putLoadingScreen();
405  setTimeout(async () => {
406    switch (jsFilter_currentContext) {
407      case "Publications":
408        if (jsFilter_metaFilters["bibtexRankedAuthorsIdName"] != "") {
409          jsFilter_getPublicationsByAuthor( jsFilter_metaFilters["bibtexRankedAuthorsIdName"]);
410        } else if ( jsFilter_metaFilters["organizationalUnitIdAcronymName"] != "") {
411          jsFilter_getPublicationsByOUID( jsFilter_metaFilters["organizationalUnitIdAcronymName"]);
412        } else {
413          jsFilter_getPublications();
414        }
415        break;
416
417      case "Supervision":
418        if (jsFilter_metaFilters["bibtexRankedAuthorsIdName"] != "") {
419          jsFilter_getSupervisionByAuthor( jsFilter_metaFilters["bibtexRankedAuthorsIdName"]);
420        } else if ( jsFilter_metaFilters["organizationalUnitIdAcronymName"] != "") {
421          jsFilter_getSupervisionByOUID( jsFilter_metaFilters["organizationalUnitIdAcronymName"]);
422        } else {
423          jsFilter_getSupervision();
424        }
425        break;
426
427      case "Projects":
428        if (jsFilter_metaFilters["bibtexRankedAuthorsIdName"] != "") {
429          jsFilter_getProjectsByAuthor( jsFilter_metaFilters["bibtexRankedAuthorsIdName"]);
430        } else if ( jsFilter_metaFilters["organizationalUnitIdAcronymName"] != "") {
431          jsFilter_getProjectsByOUID( jsFilter_metaFilters["organizationalUnitIdAcronymName"]);
432        } else {
433          jsFilter_getProjects();
434        }
435        break;
436
437      case "People":
438        if (jsFilter_metaFilters["organizationalUnitIdAcronymName"] != "") {
439          jsFilter_getPeopleByOUID( jsFilter_metaFilters["organizationalUnitIdAcronymName"]);
440        } else {
441          jsFilter_getPeople();
442        }
443        break;
444
445      case "About":
446        var author = await jsFilter_getSingleAuthor( jsFilter_metaFilters["bibtexRankedAuthorsIdName"]);
447        jsFilter_initList([], author);
448        break;
449
450      default:
451        break;
452    }
453  }, 100);
454}
455
456// Function that inializes the "order by" section
457function jsFilter_initOrderBy() {
458  jsFilter_currentOrder = jsFilter_currentOrderMapping[jsFilter_currentContext];
459
460  const orderDIV = document.getElementById("orderDIV");
461  const orderByText = iidHTML( "strong", "Order by:", {}, {}, ["orderByText"]);
462
463  const dropDown = iidHTML( "select", "", {},
464    {
465      change: function (event) {
466        jsFilter_putLoadingScreen();
467        setTimeout(() => {
468          jsFilter_currentOrder[0] = event.target.value;
469          jsFilter_updateList(jsFilter_filteredData);
470          jsFilter_removeLoadingScreen();
471        }, 100);
472      },
473    },
474    ["buttonSection"]
475  );
476
477  Object.keys(jsFilter_orderTextMapping[jsFilter_currentContext]).forEach(
478    (key) => {
479      const titleOption = iidHTML("option", "");
480      titleOption.value = key;
481      titleOption.textContent = jsFilter_orderTextMapping[jsFilter_currentContext][key];
482      dropDown.appendChild(titleOption);
483      dropDown.value = jsFilter_currentOrder[0];
484    }
485  );
486
487  const arrow = iidHTML( "button", jsFilter_currentOrder[1], {},
488    {
489      click: function () {
490        jsFilter_putLoadingScreen();
491        setTimeout(() => {
492          this.textContent = jsFilter_currentOrder[1] === "↓" ? "↑" : "↓";
493          jsFilter_currentOrder[1] = this.textContent;
494          jsFilter_updateList(jsFilter_filteredData);
495          jsFilter_removeLoadingScreen();
496        }, 100);
497      },
498    },
499    ["orderArrow"]
500  );
501
502  orderDIV.appendChild(orderByText);
503  orderDIV.appendChild(dropDown);
504  orderDIV.appendChild(arrow);
505}
506
507// Function that inializes the filtering panel
508function jsFilter_initFilterPanel(filters) {
509  jsFilter_checkMetaFilters(filters);
510  Object.keys(filters).forEach((key) => {
511    if (
512      filters[key][1] != "none" &&
513      (jsFilter_metaFilters[filters[key][0]] == "" ||
514        jsFilter_metaFilters[filters[key][0]] == null ||
515        jsFilter_metaFilters[filters[key][0]].split("|").length > 1)
516    ) {
517      const filterSection = document.createElement("div");
518      filterSection.classList.add("filter-section");
519      filterSection.id = key;
520      const heading = document.createElement("h2");
521      heading.textContent = key.replace("_", " ");
522      filterSection.appendChild(heading);
523      document.getElementById("filter-panel").appendChild(filterSection);
524
525      jsFilter_currentFilters[filters[key][0]] = [];
526
527      switch (filters[key][1]) {
528        case "searchBar":
529          jsFilter_createSearchBar(key, key + "DIV", filters[key][0]);
530          break;
531        case "checkBox":
532          jsFilter_createCheckBoxCategory(key, key + "DIV", filters[key][0]);
533          break;
534        case "slider":
535          // jsFilter_createSlider(key, key + "DIV", filters[key][0]);
536          jsFilter_createSlider2(key, key + "DIV", filters[key][0]);
537          break;
538        case "none":
539          break;
540        default:
541          console.warn(`Unknown filter type for key: ${key}`);
542          break;
543      }
544    } else {
545      jsFilter_currentFilters[filters[key][0]] = [];
546    }
547  });
548}
549
550// Function that filters the main dataset according to meta filters and drawer category.
551function jsFilter_checkMetaFilters(filters) {
552  const drawers = jsFilter_getList(
553    jsFilter_originalData,
554    jsFilter_drawerMapping[jsFilter_currentContext],
555    jsFilter_contextMappingRecursive[jsFilter_currentContext][
556      jsFilter_drawerMapping[jsFilter_currentContext]
557    ]
558  );
559  jsFilter_originalData = jsFilter_originalData.filter(function (obj) {
560    var keep = false;
561    if (Array.isArray(obj[jsFilter_drawerMapping[jsFilter_currentContext]])) {
562      obj[jsFilter_drawerMapping[jsFilter_currentContext]].forEach((elem) => {
563        if (
564          drawers.includes(
565            elem[
566              jsFilter_contextMappingRecursive[jsFilter_currentContext][
567                jsFilter_drawerMapping[jsFilter_currentContext]
568              ]
569            ]
570          )
571        )
572          keep = true;
573      });
574    } else {
575      if (
576        drawers.includes(obj[jsFilter_drawerMapping[jsFilter_currentContext]])
577      )
578        keep = true;
579    }
580    return keep;
581  });
582  Object.keys(filters).forEach((key) => {
583    if (
584      jsFilter_metaFilters[filters[key][0]] != null &&
585      jsFilter_metaFilters[filters[key][0]] != ""
586    ) {
587      if (jsFilter_metaFilters[filters[key][0]].split("|").length > 0) {
588        jsFilter_originalData = jsFilter_originalData.filter(function (obj) {
589          var keep = false;
590          jsFilter_metaFilters[filters[key][0]]
591            .split("|")
592            .forEach((filterId) => {
593              if (Array.isArray(obj[filters[key][0]])) {
594                obj[filters[key][0]].forEach((elem) => {
595                  if (
596                    elem[
597                      jsFilter_contextMappingRecursive[jsFilter_currentContext][
598                        filters[key][0]
599                      ]
600                    ] == filterId
601                  )
602                    keep = true;
603                });
604              } else {
605                if (obj[filters[key][0]] == filterId) keep = true;
606              }
607            });
608          return keep;
609        });
610      }
611    }
612  });
613  jsFilter_filteredData = jsFilter_originalData;
614  jsFilter_currentData = jsFilter_originalData;
615}
616
617// Function that fetches the data via the API
618async function jsFilter_fetchData(phpFile, apiURL) {
619  const fullApiURL = `${phpFile}?apiURL=${encodeURIComponent(apiURL)}`;
620  try {
621    const response = await fetch(fullApiURL);
622    if (!response.ok) {
623      throw new Error(
624        `Failed to fetch data from ${apiURL}. HTTP error! Status: ${response.status}`
625      );
626    }
627    return await response.json();
628  } catch (error) {
629    console.error("Fetch error:", error);
630    throw error;
631  }
632}
633
634// Function that orders the displayed items from the arrayOfObjects
635function jsFilter_orderList(arrayOfObjects) {
636  const bibTag =
637    jsFilter_orderMapping[jsFilter_currentContext][jsFilter_currentOrder[0]];
638
639  if (typeof arrayOfObjects[0][bibTag] === "number") {
640    arrayOfObjects.sort(function (a, b) {
641      return jsFilter_currentOrder[1] === "↓"
642        ? b[bibTag] - a[bibTag]
643        : a[bibTag] - b[bibTag];
644    });
645  } else {
646    arrayOfObjects.sort(function (a, b) {
647      return jsFilter_currentOrder[1] === "↑"
648        ? a[bibTag].localeCompare(b[bibTag])
649        : b[bibTag].localeCompare(a[bibTag]);
650    });
651  }
652}
653
654// True only on the global "All X" listing (no author/OU/role/type filter),
655// where the heading is replaced by the combined "<Type> <period> (<count>)"
656// title.
657function jsFilter_isGlobalAllListing() {
658  return (
659    (jsFilter_metaFilters["bibtexRankedAuthorsIdName"] == null ||
660      jsFilter_metaFilters["bibtexRankedAuthorsIdName"] === "") &&
661    (jsFilter_metaFilters["organizationalUnitIdAcronymName"] == null ||
662      jsFilter_metaFilters["organizationalUnitIdAcronymName"] === "") &&
663    (jsFilter_metaFilters["newRoleTypesId"] == null ||
664      jsFilter_metaFilters["newRoleTypesId"] === "") &&
665    (jsFilter_metaFilters["typeId"] == null ||
666      jsFilter_metaFilters["typeId"] === "")
667  );
668}
669
670// Builds the listing title, e.g. "Publications 2013-2016 (386)". The period is
671// the year range currently selected on the slider (a-b, forced to a <= b);
672// contexts without a year (People) omit it.
673function jsFilter_buildListingTitle(count) {
674  const typeLabel = jsFilter_listingTypeLabel[jsFilter_currentContext] || "";
675  const yearMapping = jsFilter_orderMapping[jsFilter_currentContext];
676  const yearTag = yearMapping ? yearMapping["Year"] : null;
677  let period = "";
678  if (
679    yearTag &&
680    Array.isArray(jsFilter_currentFilters[yearTag]) &&
681    jsFilter_currentFilters[yearTag].length === 2
682  ) {
683    let a = jsFilter_currentFilters[yearTag][1];
684    let b = jsFilter_currentFilters[yearTag][0];
685    if (a != null && b != null) {
686      if (a > b) {
687        const tmp = a;
688        a = b;
689        b = tmp;
690      }
691      period = a === b ? " " + a : " " + a + "-" + b;
692    }
693  }
694  return typeLabel + period + " (" + count + ")";
695}
696
697// Function that updates the displayed items from the arrayOfObjects
698function jsFilter_updateList(arrayOfObjects) {
699  if (arrayOfObjects.length > 0) jsFilter_orderList(arrayOfObjects);
700
701  if (jsFilter_toLog) console.log(arrayOfObjects);
702
703  const drawers = document
704    .getElementById("itemList")
705    .querySelectorAll("button.collapsibleJS");
706
707  var totalItems = [];
708
709  drawers.forEach((drawer) => {
710    const drawerID = drawer.id.split("Button")[0];
711    const drawerData = arrayOfObjects.filter((item) => {
712      if (
713        Array.isArray(item[jsFilter_drawerMapping[jsFilter_currentContext]])
714      ) {
715        var result = false;
716        item[jsFilter_drawerMapping[jsFilter_currentContext]].forEach(
717          (secondItem) => {
718            if (
719              secondItem[
720                jsFilter_contextMappingRecursive[jsFilter_currentContext][
721                  jsFilter_drawerMapping[jsFilter_currentContext]
722                ]
723              ] == drawerID
724            )
725              result = true;
726          }
727        );
728        return result;
729      } else
730        return (
731          item[jsFilter_drawerMapping[jsFilter_currentContext]] === drawerID
732        );
733    });
734
735    // The banner/content live inside a delimited section wrapper (publications
736    // and people); hide the wrapper too so filtered-out drawers leave no empty
737    // box behind.
738    const section = drawer.closest(".drawer-sectionJS");
739
740    if (
741      drawerData.length > 0 &&
742      (jsFilter_currentFilters[ jsFilter_drawerMapping[jsFilter_currentContext] ].includes(drawerID) ||
743        jsFilter_currentFilters[jsFilter_drawerMapping[jsFilter_currentContext]] .length == 0)
744    ) {
745      totalItems = totalItems.concat(drawerData);
746      var buttonText;
747      if ( jsFilter_propertyMapping[jsFilter_currentContext][drawerID].substring( 1, 3) == " -") {
748        buttonText = jsFilter_propertyMapping[jsFilter_currentContext][drawerID].substring( 3);
749      } else {
750        buttonText = jsFilter_propertyMapping[jsFilter_currentContext][drawerID];
751      }
752
753      const drawerContent = drawer.nextElementSibling;
754      drawerContent.style.display = "flex";
755      if (drawerContent.style.maxHeight == "0px")
756        drawer.textContent = `${buttonText} (${drawerData.length}) ▲`;
757      else drawer.textContent = `${buttonText} (${drawerData.length}) ▼`;
758      drawer.style.display = "inline-block";
759      if (section) section.style.display = "";
760    } else {
761      drawer.style.display = "none";
762      const drawerContent = drawer.nextElementSibling;
763      drawerContent.style.display = "none";
764      if (section) section.style.display = "none";
765    }
766
767    switch (jsFilter_currentContext) {
768      case "Publications":
769        jsFilter_createListPublications( drawer.nextElementSibling, drawerData, drawerID);
770        break;
771      case "Supervision":
772        jsFilter_createListSupervision(drawer.nextElementSibling, drawerData);
773        break;
774      case "Projects":
775        jsFilter_createListProjects(drawer.nextElementSibling, drawerData);
776        break;
777      case "People":
778        jsFilter_createListPeople(drawer.nextElementSibling, drawerData);
779        break;
780      default:
781        break;
782    }
783  });
784
785  const titleElement = document.getElementById("title");
786  const totalCount = jsFilter_removeDuplicatesObjects(totalItems).length;
787  titleElement.textContent = jsFilter_buildListingTitle(totalCount);
788}
789
790// Function that creates the displayed items from the arrayOfObjects
791function jsFilter_createList(arrayOfObjects) {
792  jsFilter_orderList(arrayOfObjects);
793
794  if (jsFilter_toLog) console.log(arrayOfObjects);
795
796  var drawers = jsFilter_getList(
797    arrayOfObjects,
798    jsFilter_drawerMapping[jsFilter_currentContext],
799    jsFilter_contextMappingRecursive[jsFilter_currentContext][
800      jsFilter_drawerMapping[jsFilter_currentContext]
801    ]
802  );
803
804  if ( jsFilter_metaFilters[jsFilter_drawerMapping[jsFilter_currentContext]] != null &&
805    jsFilter_metaFilters[jsFilter_drawerMapping[jsFilter_currentContext]] != "")
806    drawers = drawers.filter(function (elem) {
807      return jsFilter_metaFilters[ jsFilter_drawerMapping[jsFilter_currentContext] ] .split("|") .includes(elem);
808    });
809
810  var totalItems = [];
811
812  const itemListContainer = document.getElementById("itemList");
813  itemListContainer.innerHTML = "";
814
815  drawers.forEach((drawer, ite) => {
816    const tempArray = arrayOfObjects.filter(function (item) {
817      if (
818        Array.isArray(item[jsFilter_drawerMapping[jsFilter_currentContext]])
819      ) {
820        var result = false;
821        item[jsFilter_drawerMapping[jsFilter_currentContext]].forEach(
822          (secondItem) => {
823            if (
824              secondItem[
825                jsFilter_contextMappingRecursive[jsFilter_currentContext][
826                  jsFilter_drawerMapping[jsFilter_currentContext]
827                ]
828              ] == drawer
829            )
830              result = true;
831          }
832        );
833        return result;
834      } else return item[jsFilter_drawerMapping[jsFilter_currentContext]] === drawer;
835    });
836
837    totalItems = totalItems.concat(tempArray);
838
839    var buttonText;
840
841    if (jsFilter_propertyMapping[jsFilter_currentContext][drawer] == null)
842      return;
843    if (
844      jsFilter_propertyMapping[jsFilter_currentContext][drawer].substring(
845        1,
846        3
847      ) == " -"
848    ) {
849      buttonText =
850        jsFilter_propertyMapping[jsFilter_currentContext][drawer].substring(3);
851    } else {
852      buttonText = jsFilter_propertyMapping[jsFilter_currentContext][drawer];
853    }
854
855    const button = iidHTML(
856      "button",
857      buttonText + " (" + tempArray.length + ") ▼",
858      { marginTop: ite > 0 ? "5px" : "" },
859      {
860        click: function () {
861          this.classList.toggle("active");
862          const content = this.nextElementSibling;
863          if (content.style.maxHeight == "none") {
864            this.textContent = this.textContent.slice(0, -1) + "▲";
865            content.style.maxHeight = 0;
866          } else {
867            this.textContent = this.textContent.slice(0, -1) + "▼";
868            content.style.maxHeight = "none";
869          }
870        },
871      },
872      ["collapsibleJS"]
873    );
874    button.id = drawer + "Button";
875
876    const div = iidHTML("div", "", {}, {}, ["contentJS"]);
877
878    if (
879      jsFilter_currentContext === "Publications" ||
880      jsFilter_currentContext === "People" ||
881      jsFilter_currentContext === "Supervision" ||
882      jsFilter_currentContext === "Projects"
883    ) {
884      // Wrap the banner and its cards in a single delimited, collapsible
885      // section. The banner stays the content's previous sibling, so the
886      // collapse/filter logic keeps working unchanged.
887      button.style.marginTop = "0px";
888      button.classList.add("drawer-bannerJS");
889      div.classList.add("drawer-section-contentJS");
890      const section = iidHTML("div", "", {}, {}, ["drawer-sectionJS"]);
891      section.appendChild(button);
892      section.appendChild(div);
893      itemListContainer.appendChild(section);
894    } else {
895      itemListContainer.appendChild(button);
896      itemListContainer.appendChild(div);
897    }
898
899    switch (jsFilter_currentContext) {
900      case "Publications":
901        jsFilter_createListPublications(div, tempArray, drawer);
902        break;
903      case "Supervision":
904        jsFilter_createListSupervision(div, tempArray);
905        break;
906      case "Projects":
907        jsFilter_createListProjects(div, tempArray);
908        break;
909      case "People":
910        div.style.display = "flex";
911        div.style.flexWrap = "wrap";
912        jsFilter_createListPeople(div, tempArray);
913        break;
914
915      default:
916        break;
917    }
918
919    button.classList.toggle("active");
920    const content = button.nextElementSibling;
921    if (content.style.maxHeight == "none") content.style.maxHeight = 0;
922    else content.style.maxHeight = "none";
923  });
924
925  const titleElement = document.getElementById("title");
926  titleElement.style.textAlign = "left";
927  const totalCount = jsFilter_removeDuplicatesObjects(totalItems).length;
928  titleElement.textContent = jsFilter_buildListingTitle(totalCount);
929}
930
931// Hardcoded adaptations from the intranet that were not be made generic.
932function jsFilter_intranetAdaptations(data, cardInfo) {
933  if (cardInfo != null) {
934    var tempArraySA = [];
935    if (!Array.isArray(cardInfo["activeOUsId"])) {
936      var tempArrayObj = [];
937      Object.keys(cardInfo["activeOUsId"]).forEach((key) => {
938        tempArrayObj.push(cardInfo["activeOUsId"][key]);
939      });
940      cardInfo["activeOUsId"] = tempArrayObj;
941    }
942    cardInfo["activeOUsId"] = jsFilter_removeDuplicatesArray(
943      cardInfo["activeOUsId"]
944    );
945    cardInfo["activeOUsId"].forEach((element2) => {
946      if (jsFilter_peopleScientificAreas[element2] != null)
947        tempArraySA.push({ item1: element2 });
948    });
949    cardInfo["organizationalUnitIdAcronymName"] = tempArraySA;
950  }
951
952  switch (jsFilter_currentContext) {
953    case "Publications":
954      break;
955
956    case "Supervision":
957      data = jsFilter_stringDataToInt(data, "endTime");
958      data.forEach((element) => {
959        if (element["endTime"] == null) element["endTime"] = "Ongoing";
960        else element["endTime"] = "Ended";
961      });
962      break;
963
964    case "Projects":
965      data = jsFilter_stringDataToInt(data, "endDate");
966      data.forEach((element) => {
967        var tempArrayTL = [];
968        element["thematicLinesId"].forEach((element2) => {
969          if (jsFilter_tematicLines[element2] != null)
970            tempArrayTL.push({ item1: element2 });
971        });
972        element["thematicLinesId"] = tempArrayTL;
973      });
974      break;
975
976    case "People":
977      jsFilter_calculateRoles(data);
978      data.forEach((element) => {
979        var tempArraySA = [];
980        element["activeOUsId"] = jsFilter_removeDuplicatesArray(
981          element["activeOUsId"]
982        );
983        element["activeOUsId"].forEach((element2) => {
984          if (jsFilter_peopleScientificAreas[element2] != null)
985            tempArraySA.push({ item1: element2 });
986        });
987        element["organizationalUnitIdAcronymName"] = tempArraySA;
988
989        var tempArrayTL = [];
990        element["rankedOUsId"] = jsFilter_removeDuplicatesArray(
991          element["rankedOUsId"]
992        );
993        element["rankedOUsId"].forEach((element2) => {
994          if (jsFilter_tematicLines[element2] != null)
995            tempArrayTL.push({ item1: element2 });
996        });
997        element["rankedOUsId"] = tempArrayTL;
998      });
999      break;
1000
1001    default:
1002      break;
1003  }
1004}
1005
1006// Events that are started with the plugin
1007function jsFilter_initEventHandlers() {
1008  document.addEventListener("touchstart", function (event) {
1009    touchStartX = event.touches[0].clientX;
1010  });
1011
1012  document.addEventListener("touchend", function (event) {
1013    touchEndX = event.changedTouches[0].clientX;
1014    jsFilter_handleSwipe(event);
1015  });
1016}

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.