PageSourceSearch

https://nouvelles.umontreal.ca/_assets/873403320408970952938ec17a9…_nouvelles_suggestions-debounce_BKX-mpnn.js

js umontreal.ca collected 2026-10-01 07:34:31 UTC 17,508 bytes, 564 lines download raw bytes

1const _ = {
2  /**
3   * URL pour les requêtes de facettes
4   */
5  ajaxFacetsUrl: "/recherche/recherche-facettes.json",
6  /**
7   * URL pour les requêtes de résultats
8   */
9  ajaxResultsUrl: "/recherche/recherche-resultats.html",
10  /**
11   * URL pour les requêtes de suggestions
12   */
13  ajaxSuggestionsUrl: "/recherche/recherche-suggestions.json",
14  /**
15   * Activer ou désactiver l'autocomplétion dans la barre de
16   */
17  suggestionsEnabled: !1,
18  /**
19   * Si on veut utiliser la version compacte et combinée des paramètre dans l'URL.
20   * Si on active les options "mask" et "concat" de l'extension SOLR de TYPO3.
21   */
22  compactQueryStringEnabled: !1,
23  /**
24   * Activer ou désactiver le mode debug
25   *  - active logExecutionTimeInConsole() : pour les requêtes pour des suggestions dans l'autocomplétion
26   */
27  debug: !1,
28  /**
29   *  Si on veut appliquer un filtre par défaut pour les résultats
30   *  Pour les sites autres que Calendrier, on va probablement toujours être en "none".
31   *
32   *  "none" : on recherche dans tous les résultats.
33   *  "futureOnly" : on recherche dans les résultats dont la date est supérieur à la date actuelle.
34   */
35  defaultFilter: "none",
36  /**
37   * Si on veut appliquer une option de facette par défaut pour les résultats.
38   * ex. types:udem_article
39   */
40  defaultSelectedOption: "",
41  /**
42   * Champ SOLR utilisé pour appliquer le filtre "futureOnly"
43   */
44  futureOnlyFilterField: "date",
45  /**
46   * ID du conteneur de suggestions dans le header
47   */
48  headerSuggestionContainerId: "#tx-udem-solr-header-suggestions-container",
49  /**
50   * ID de l'input de recherche dans l'entête
51   */
52  headerSearchInputId: "#menu-search-input",
53  /**
54   * Libellés (et traduction) que l'on peut modifier dans les configs
55   * Il y a aussi des traductions dans le module locale.js. On pourrait les fusionner(todo).
56   * Pour le moment, ça permet de remplacer "Documents" par "Activités" pour le calendrier.
57   */
58  locale: {
59    suggestionsDocumentsLabel: {
60      en: "Documents",
61      fr: "Documents"
62    }
63  },
64  /**
65   * Langue d'affichage du site
66   */
67  lng: "fr",
68  /**
69   * Mobile breakpoint
70   */
71  mobileBreakpoint: 576,
72  /**
73   * Nombre de caractères minimum pour afficher des suggestions
74   */
75  minSearchLength: 2,
76  /**
77   * Nombre de caractères maximum pour faire des recherches et pour afficher des suggestions
78   * 125 : basé sur la longueur des titres des News et des Events en BD
79   */
80  maxSearchLength: 125,
81  /**
82   * Hook qui permet de définir une fonction pour modifier le html des résultats juste avant qu'ils soient affichés
83   */
84  parseResultsFn: null,
85  /**
86   * URL de la page de résultats
87   */
88  resultListingUrl: "/recherche",
89  /**
90   * Paramètre GET utilisé pour les requêtes SOLR
91   */
92  solrGetParameter: "tx_solr",
93  /**
94   * Le nombre de suggestions à afficher par facette
95   */
96  suggestionsFacetLimit: 5,
97  /**
98   * Ouvrir les suggestions de documents dans un nouvel onglet.
99   */
100  suggestionsOpenInNewTab: !1,
101  /**
102   * PID du dossier des enregistrements.
103   * On veut afficher seulement des suggestions à partir de ce dossier.
104   * Nécessaire, car le middleware bypass l'environnement de TYPO3 et les configurations de l'extension SOLR défini en typoscript.
105   * */
106  suggestionsRootline: "0",
107  /**
108   * Type de suggestions à afficher si l'autocomplétion est activée
109   *      document : suggestions de documents / résultats
110   *      facet : suggestions de facettes
111   *      search : suggestions de recherche. Lorsqu'on utilise le middleware d'UdemSolr, on n'a pas de suggestions de recherche (search).
112   *      terms : Suggestion à partir de la fonction "Terms" de Solr. Ex. Profs pour UdemNouvelles
113   */
114  suggestionsTypes: ["document", "facet", "profs", "search", "terms"],
115  /**
116   *  Si on veut appliquer un filtre par défaut différent pour les suggestions. Pas sûr que ça soit utile, mais au cas...
117   *  Pour les sites autres que Calendrier, on va probablement toujours être en "none".
118   *
119   *  "none" : on fait des suggestions à partir de tous les résultats.
120   *  "futureOnly" : on fait des suggestions à partir des résultats futurs seulement.
121   *  "matchSearchDefault" : on fait des suggestions avec la même configuration que pour la recherche (defaultFilter).
122   */
123  suggestionsDefaultFilter: "matchDefaultFilter",
124  /**
125   * SI on applique un filtre supplémentaire en JS sur les suggestions qu'on reçoit
126   */
127  suggestionsJsFilterEnabled: !0,
128  /**
129   * Si on veut obtenir les suggestions en utilisant le middleware d'UdemSolr au lieu d'utiliser l'extension SOLR,
130   * Si on utilise le middleware :
131   *  - on change le paramètre "tx_solr[queryString]" pour "tx_udem_suggestions"
132   *  - on ajoute le paramètre "rootline".
133   */
134  useSuggestionsMiddleware: !0
135};
136let d = {};
137function we(e) {
138  B(e);
139}
140function a(e) {
141  if (d[e] === void 0)
142    throw new Error(`La configuration "${e}" n'existe pas.`);
143  return d[e];
144}
145function G(e) {
146  const t = a("lng");
147  return d.locale[e] && d.locale[e][t] !== void 0 ? d.locale[e][t] : e;
148}
149function B(e) {
150  d = { ..._, ...e };
151}
152let M = 0;
153function j(e) {
154  return btoa(encodeURIComponent(e));
155}
156async function x(e, t) {
157  const n = await fetch(e, {
158    method: "GET",
159    signal: t
160  });
161  if (!n.ok)
162    throw new Error("FetchResponseError");
163  return n.text();
164}
165function K(e) {
166  return (/* @__PURE__ */ new Date()).getTime() - e;
167}
168function Fe(e) {
169  return String(e).replace(/[^\w. ]/gi, function(t) {
170    return "&#" + t.charCodeAt(0) + ";";
171  });
172}
173function J(e) {
174  return String(e).replace(/[<>&]/g, function(t) {
175    return "&#" + t.charCodeAt(0) + ";";
176  });
177}
178function g(e, t, n = !1) {
179  n && console.log(e + " - " + K(t) + "ms");
180}
181function Ee(e, t) {
182  const n = e.dataset.url;
183  if (!n)
184    return;
185  e.classList.contains("document") && a("suggestionsOpenInNewTab") || t.ctrlKey || t.metaKey ? window.open(n, "_blank", "noopener") : window.location.href = n;
186}
187function h(e, t = !0) {
188  return t ? Q(e.normalize("NFD").replace(/[\u0300-\u036f]/g, "")) : e.normalize("NFD").replace(/[\u0300-\u036f]/g, "");
189}
190function Q(e) {
191  if (typeof e != "string")
192    throw new TypeError("Le texte doit être une chaîne de caractères.");
193  const t = /[’`‘]/g;
194  return e.replace(t, "'");
195}
196function ve(e, t) {
197  let s = new URL(window.location.href).searchParams.get(e);
198  return s ? s.replace(/</g, "&lt;").replace(/>/g, "&gt;") : t;
199}
200function Ce(e) {
201  window.history.pushState("", "", window.location.href.split("?")[0] + e);
202}
203function Te(e, t) {
204  let n = new URL(window.location.href);
205  n.searchParams.set(e, t), window.history.pushState("", "", n);
206}
207function W(e) {
208  M = e;
209}
210function m() {
211  return M;
212}
213const v = {
214  error: {
215    en: "An error happened.",
216    fr: "Une erreur est survenue."
217  },
218  result: {
219    en: "result",
220    fr: "résultat"
221  },
222  results: {
223    en: "results",
224    fr: "résultats"
225  },
226  retry: {
227    en: "Retry",
228    fr: "Réessayer"
229  },
230  /* Options sélectionnées */
231  remove: {
232    en: "Remove",
233    fr: "Retirer"
234  },
235  selectedOptionsKeyword: {
236    en: "Keyword: ",
237    fr: "Mot clé : "
238  },
239  selectedOptionsRemoveAll: {
240    en: "Reset",
241    fr: "Réinitialiser"
242  },
243  selectedOptionsSearchedTerm: {
244    en: "Search: ",
245    fr: "Recherche : "
246  },
247  to: {
248    en: "to",
249    fr: "au"
250  },
251  /* Suggestions */
252  suggestionsDocumentsLabel: {
253    en: "Documents",
254    fr: "Documents"
255  },
256  suggestionsProfessorsLabel: {
257    en: "Professors",
258    fr: "Professeurs"
259  },
260  suggestionsRecherchesLabel: {
261    en: "Search",
262    fr: "Recherche"
263  }
264};
265function q(e) {
266  const t = a("lng");
267  return v[e] && v[e][t] ? v[e][t] : e;
268}
269const l = document.createElement("ul");
270l.classList.add("suggestions-list");
271function Y(e, t, n, s, r, i) {
272  let o, c, u;
273  if (e && ({ suggestions: o, documents: c, terms: u } = e), r.innerHTML = "", l.innerHTML = "", !(!o && !c && !t && !n))
274    if (V(c, s), P(n, s), Z(o, s), X(t, s), P(u, s), l.children.length > 0) {
275      const p = document.createElement("div");
276      p.classList.add("suggestions-list-outer"), p.appendChild(l), r.appendChild(p), g("Suggestions displayed", i, a("debug"));
277    } else
278      g("No suggestions to display", i, a("debug"));
279}
280function X(e, t) {
281  if (a("suggestionsTypes").includes("facet"))
282    for (const [s, r] of Object.entries(e.facets)) {
283      if (!r.includeInSuggestion)
284        continue;
285      let i = r.options.filter(
286        (o) => h(o.label.toLowerCase()).includes(h(t.toLowerCase())) && o.documentCount > 0
287      );
288      i = i.slice(0, a("suggestionsFacetLimit")), i.length >
288 0 && (F(r.label), i.forEach((o) => {
289        const c = document.createElement("li");
290        c.classList.add("suggestion", "facet"), c.dataset.facet = o.class, c.dataset.url = a("resultListingUrl") + "?" + s + "=" + o.value, c.ariaLabel = o.label, c.tabIndex = 0, c.innerHTML = `<span>${E(t, o.label)}</span>`, l.appendChild(c);
291      }));
292    }
293}
294function Z(e, t) {
295  !a("suggestionsTypes").includes("search") || !e || Object.keys(e).length === 0 || (F(q("suggestionsRecherchesLabel")), Object.entries(e).forEach(([s]) => {
296    const r = document.createElement("li");
297    r.classList.add("suggestion", "recherche"), r.ariaLabel = "Suggestion de recherche", r.dataset.url = a("resultListingUrl") + "?q=" + option.value, r.tabIndex = 0, r.innerHTML = `<span>${E(t, s)}</span>`, l.appendChild(r);
298  }));
299}
300function V(e, t) {
301  const n = a("suggestionsTypes").includes("document");
302  if (e = ee(t, e), !n || !e || e.length === 0)
303    return;
304  const s = G("suggestionsDocumentsLabel");
305  s && F(s), e.forEach(({ link: r, title: i }) => {
306    const o = document.createElement("li");
307    o.classList.add("suggestion", "document"), o.dataset.url = r, o.ariaLabel = i, o.tabIndex = 0, o.innerHTML = `<span>${E(t, i)}</span>`, l.appendChild(o);
308  });
309}
310function P(e, t) {
311  const n = a("suggestionsTypes") || [];
312  if (!(!(n.includes("terms") || n.includes("profs")) || !e || e.length === 0))
313    for (const [r, i] of Object.entries(e))
314      i.length > 0 && (F(r), i.forEach((o) => {
315        const c = document.createElement("li");
316        c.classList.add("suggestion", "term"), c.dataset.facetOptionClass = o.class, c.dataset.optionValue = o.optionValue, c.dataset.solrFacetName = o.solrFacetName, c.dataset.idFacet = o.idFacet, c.dataset.url = a("resultListingUrl") + "?" + o.solrFacetName + "=" + o.value, c.ariaLabel = o.label, c.tabIndex = 0, c.innerHTML = `<span>${E(t, o.label)} ${o.extraLabel ?? ""}</span>`, l.appendChild(c);
317      }));
318}
319function F(e) {
320  const t = document.createElement("li");
321  t.classList.add("separator"), t.innerHTML = e, l.appendChild(t);
322}
323function E(e, t) {
324  const n = h(t.toLowerCase()), s = h(e.toLowerCase()), r = n.indexOf(s);
325  if (r === -1)
326    return t;
327  const i = `<span class="highlight">${t.substring(r, r + e.length)}</span>`;
328  return t.substring(0, r) + i + t.substring(r + e.length);
329}
330function ee(e, t) {
331  return t ? a("suggestionsJsFilterEnabled") ? t.filter(({ title: n }) => h(n.toLowerCase()).includes(h(e.toLowerCase()))) : t : [];
332}
333function te(e) {
334  return e.length > a("maxSearchLength") ? e.substring(0, a("maxSearchLength")) : e;
335}
336function I(e, t, n = !1) {
337  return t + a("solrGetParameter") + "[filter][" + e + "]=" + ne(n);
338}
339function ne(e) {
340  const t = a("futureOnlyFilterField");
341  return e ? t + ":1900/01/01-2100/01/01" : t + ":" + se() + "-2100/01/01";
342}
343function $(e, t) {
344  return t + a("solrGetParameter") + "[page]=" + e;
345}
346function se() {
347  const e = /* @__PURE__ */ new Date(), t = e.getFullYear(), n = (e.getMonth() + 1).toString().padStart(2, "0"), s = e.getDate().toString().padStart(2, "0");
348  return `${t}/${n}/${s}`;
349}
350function xe(e) {
351  const t = e.querySelectorAll('input[type="radio"], input[type="checkbox"]');
352  return Array.prototype.some.call(t, function(n) {
353    return n.checked;
354  });
355}
356const b = [];
357let S = [];
358async function re(e, t) {
359  const n = e.replace(/&?tx_solr\[page\]=\d+/, ""), s = j(n), r = b[s] || null;
360  if (S.includes(s))
361    return new Promise((i, o) => {
362      const c = setInterval(() => {
363        S.includes(s) || (clearInterval(c), i(b[s]));
364      }, 100);
365    });
366  if (r)
367    return r;
368  try {
369    S.push(s);
370    const i = a("ajaxFacetsUrl") + n, o = await x(i, t), c = JSON.parse(o);
371    return b[s] = c, oe(), A(s), c;
372  } catch (i) {
373    i.name !== "AbortError" && console.error(i), A(s);
374  }
375}
376function A(e) {
377  S = S.filter((t) => t !== e);
378}
379function oe() {
380  const t = Object.keys(b);
381  if (t.length > 100) {
382    const n = t[0];
383    delete b[n];
384  }
385}
386async function ae(e = !0) {
387  const t = new AbortController().signal, s = a("defaultFilter") === "futureOnly" ? I(0, "?", e) + $(1, "&") : $(1, "?");
388  return re(s, t);
389}
390let y = null, U = [], k = !1;
391function ie() {
392  y && !y.signal.aborted && (g("Aborting the previous request", m(), a("debug")), y.abort());
393}
394async function ce() {
395  ie(), g("Fetching profs", m(), a("debug")), y = new AbortController();
396  const e = y.signal, t = a("ajaxSuggestionsUrl") + "?tx_udem_suggestions_profs=count";
397  try {
398    const n = await x(t, e);
399    if (!e.aborted) {
400      let s;
401      try {
402        s = JSON.parse(n), (typeof s != "object" || s === null) && (s = {});
403      } catch {
404        s = {};
405      }
406      return k = !0, s;
407    }
408  } catch (n) {
409    n.name !== "AbortError" && console.error(n);
410  }
411  return {};
412}
413async function le(e) {
414  const t = q("suggestionsProfessorsLabel");
415  if (!a("suggestionsTypes").includes("profs") || !e || e.trim().length === 0)
416    return { [t]: [] };
417  const n = 15, s = await ge(), r = C(e.trim()), i = [], o = [], c = [], u = [];
418  for (const f of s) {
419    const N = C(f.lastName || ""), R = C(f.firstName || ""), O = `${R} ${N}`, D = `${N} ${R}`;
420    if (D.startsWith(r) ? i.push(f) : O.startsWith(r) ? o.push(f) : D.includes(r) ? u.push(f) : O.includes(r) && c.push(f), i.length >= n || i.length + o.length >= n)
421      break;
422  }
423  const p = [
424    ...i,
425    ...o,
426    ...u,
427    ...c
428  ];
429  return {
430    [t]: p.slice(0, n).map(ue)
431  };
432}
433function ue(e) {
434  const { lastName: t, firstName: n, slug: s, unite: r } = e, i = "profs";
435  return {
436    extraLabel: r ? ` (${r})` : "",
437    label: `${t}, ${n}`,
438    value: s,
439    optionValue: `${i}:${s}`,
440    solrFacetName: i,
441    idFacet: `facet${i}`,
442    class: `facet-option-${i}-${s}`
443  };
444}
445async function ge() {
446  return a("suggestionsTypes").includes("profs") ? (k || (U = await ce()), U) : [];
447}
448function C(e) {
449  return e.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").trim();
450}
451let L = null, z = !1;
452const w = [];
453function fe() {
454  L && !L.signal.aborted && (g("Aborting the previous request", m(), a("debug")), L.abort());
455}
456async function de(e, t, n = !0) {
457  const s = j(e + n);
458  H() && Y(await he(e, t, n, s), await ae(n), await le(e), e, t, m());
459}
460async function he(e, t, n, s) {
461  const r = w[s] || null;
462  if (r)
463    return r;
464  fe(), g("Fetching suggestions", m(), a("debug")), L = new AbortController();
465  const i = L.signal;
466  let o = a("ajaxSuggestionsUrl") + "?" + pe() + "=" + encodeURIComponent(e) + me(1, n);
467  a("useSuggestionsMiddleware") && (o += "&rootline=" + a("suggestionsRootline"));
468  try {
469    const c = await x(o, i);
470    if (!i.aborted && (t.innerHTML = "", H())) {
471      const u = JSON.parse(c);
472      return w[s] = u, be(), u;
473    }
474  } catch (c) {
475    c.name !== "AbortError" && console.error(c);
476  }
477}
478function me(e, t) {
479  const n = a("suggestionsDefaultFilter"), s = a("defaultFilter");
480  return n === "futureOnly" || n === "matchDefaultFilter" && s === "futureOnly" ? I(e, "&", t) : "";
481}
482function H() {
483  return z;
484}
485function T(e) {
486  z = e;
487}
488function pe() {
489  return a("useSuggestionsMiddleware") ? "tx_udem_suggestions" : a("solrGetParameter") + "[queryString]";
490}
491function be() {
492  const t = Object.keys(w);
493  if (t.length > 100) {
494    const n = t[0];
495    delete w[n];
496  }
497}
498function Ne(e, t) {
499  e.form.addEventListener("submit", (s) => {
500    s.preventDefault(), typeof t == "function" ? t(e.value) : (e.form.submit(), T(!1));
501  }), e.addEventListener("keypress", (s) => {
502    s.key === "Enter" && e.form.dispatchEvent(new Event("submit", { cancelable: !0 }));
503  });
504  let n = e.form.querySelector(".submit");
505  n || (n = document.getElementById("menu-search-input-button")), n.addEventListener("click", (s) => {
506    s.preventDefault(), e.form.dispatchEvent(new Event("submit", { cancelable: !0 }));
507  }), e.addEventListener("input", () => {
508    e.value === "" && Se();
509  });
510}
511function Se() {
512  document.querySelectorAll(".facet-search-input").forEach((e) => {
513    e.value = "";
514  });
515}
516function Re(e) {
517  document.querySelectorAll(".facet-search-input").forEach((t) => {
518    t.value = e;
519  });
520}
521function Oe(e, t) {
522  e.value = J(te(t));
523}
524const ye = 100, Le = (e, t) => {
525  let n;
526  return (...s) => {
527    clearTimeout(n), n = setTimeout(() => {
528      e(...s);
529    }, t);
530  };
531}, De = Le((e, t, n, s = !0) => {
532  const r = e.target.value.trim();
533  r.length >= n && de(r, t, s);
534}, ye);
535function Pe(e, t) {
536  W(Date.now()), g("User is typing in the search input #" + e.id, m(), a("debug"));
537  const n = e.value.trim();
538  n.length >= a("minSearchLength") && n.length < a("maxSearchLength") ? T(!0) : (t.innerHTML = "", T(!1));
539}
540export {
541  Ee as a,
542  ae as b,
543  ge as c,
544  De as d,
545  xe as e,
546  Te as f,
547  a as g,
548  I as h,
549  we as i,
550  $ as j,
551  te as k,
552  Ce as l,
553  q as m,
554  Fe as n,
555  Ne as o,
556  x as p,
557  re as q,
558  Se as r,
559  Oe as s,
560  T as t,
561  ve as u,
562  Re as v,
563  Pe as w
564};

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.