PageSourceSearch

https://www.cpm.it/wp-content/themes/hello-theme-child-master/assets/js/mobile-courses.js?ver=1.0.2

js cpm.it collected 2026-10-02 10:27:33 UTC 8,107 bytes, 245 lines download raw bytes

1(() => {
2  const cfg = window.CJ_MENU || {};
3  const DEBUG = true;
4  const log = (...a) => DEBUG && console.log("[CJ COURSES]", ...a);
5
6  function decodeHtmlEntities(str) {
7    if (str == null) return "";
8    const txt = document.createElement("textarea");
9    txt.innerHTML = String(str);
10    return txt.value;
11    }
12
13
14  const overlay = document.getElementById(cfg.overlayId || "headermenu_mobile");
15  if (!overlay) return log("STOP: overlay non trovato");
16
17  const menuBox   = document.getElementById(cfg.menuContainerId || "mobile_menu_");
18  const courseBox = document.getElementById(cfg.courseContainerId || "mobile_course_");
19  if (!menuBox || !courseBox) return log("STOP: menuBox/courseBox non trovati");
20
21  const chooseBtn = overlay.querySelector(cfg.chooseCoursesSelector || "#choose_course_btn a");
22  if (!chooseBtn) return log("STOP: chooseBtn non trovato");
23
24  const chooseBtnWrap = document.getElementById("choose_course_btn");
25  const cont_btn_header = document.getElementById("cont_btn_header");
26
27  const courseHeaderRow = courseBox.querySelector('[data-id="47b52158"]');
28  if (!courseHeaderRow) return log("STOP: courseHeaderRow non trovato");
29
30  if (courseBox.querySelector(".cjcourses__viewport")) return;
31
32  const courseTitleWrap = document.getElementById(cfg.courseTitleId || "mobile_course_title");
33  const courseTitleP = courseTitleWrap ? courseTitleWrap.querySelector("p") : null;
34
35  const courseBackWrap = document.getElementById(cfg.courseBackId || "mobile_course_back");
36  if (courseBackWrap) {
37    courseBackWrap.style.cursor = "pointer";
38    courseBackWrap.setAttribute("role", "button");
39    courseBackWrap.setAttribute("tabindex", "0");
40    courseBackWrap.setAttribute("aria-label", "Indietro");
41  }
42
43  const viewport = document.createElement("div");
44  viewport.className = "cjcourses__viewport";
45
46  const track = document.createElement("div");
47  track.className = "cjcourses__track";
48
49  viewport.appendChild(track);
50  courseHeaderRow.insertAdjacentElement("afterend", viewport);
51
52  let depth = 0;
53  let crumbs = [];
54  const rootTitle = cfg.coursesRootTitle || "Scegli il tuo corso";
55
56  function setCourseTitle() {
57    const txt = crumbs.length ? crumbs.join(" > ") : rootTitle;
58    if (courseTitleP) courseTitleP.textContent = txt;
59    else if (courseTitleWrap) courseTitleWrap.textContent = txt;
60  }
61
62  function translate() {
63    track.style.transform = `translateX(-${depth * 100}%)`;
64  }
65
66  async function api(url) {
67    const res = await fetch(url, { credentials: "same-origin" });
68    if (!res.ok) {
69      const err = new Error(`API error ${res.status}`);
70      err.status = res.status;
71      throw err;
72    }
73    return res.json();
74  }
75
76  // --- Anti-loop helpers ---
77  const idsOf = (arr) =>
78    (Array.isArray(arr) ? arr : [])
79      .map(x => String(x?.id ?? ""))
80      .filter(Boolean)
81      .sort()
82      .join(",");
83
84  function makePanel(items, type, panelMeta = {}) {
85    const panel = document.createElement("div");
86    panel.className = "cjcourses__panel";
87    panel.dataset.type = type;
88
89    // per anti-loop: salviamo gli ID di questo pannello
90    if (type === "category") {
91      panel.dataset.ids = idsOf(items);
92      // opzionale: salva anche slug parent (utile debug)
93      if (panelMeta.parentSlug) panel.dataset.parentSlug = panelMeta.parentSlug;
94    }
95
96    const ul = document.createElement("ul");
97    ul.className = "cjcourses__list";
98
99    items.forEach((it) => {
100      const li = document.createElement("li");
101      li.className = "cjcourses__item";
102
103      if (type === "category") {
104        const btn = document.createElement("button");
105        btn.type = "button";
106        btn.className = "cjcourses__btn";
107
108        const count = (typeof it.count === "number") ? it.count : null;
109
110        btn.innerHTML = `
111          <div>
112            <span class="cjcourses__label">${decodeHtmlEntities(it.name ?? "")}</span>
113            ${count !== null ? `<sup class="cjcourses__count">(${count})</sup>` : ``}
114          </div>
115          <span class="cjcourses__chev">
116            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512.01 512.01">
117              <path d="M498.8,244.43H11.64C5.21,244.43,0,249.64,0,256.06s5.21,11.64,11.64,11.64h487.17c6.43,0,11.64-5.21,11.64-11.64s-5.21-11.64-11.64-11.64Z"></path>
118              <path d="M243.58,12.48v487.17c0,6.43,5.21,11.64,11.64,11.64s11.64-5.21,11.64-11.64V12.48c0-6.43-5.21-11.64-11.64-11.64s-11.64,5.21-11.64,11.64Z"></path>
119            </svg>
120          </span>
121        `;
122
123        btn.addEventListener("click", async () => {
124          const currentPanel = track.querySelectorAll(".cjcourses__panel")[depth];
125          const currentIds = currentPanel?.dataset?.ids || "";
126
127          // 1) prova sottocategorie
128          let subcats = null;
129          try {
130            // endpoint consigliato
131            subcats = await api(`/wp-json/cj/v1/course-categories?parent_slug=${encodeURIComponent(it.slug || "")}`);
132          } catch (e) {
133            subcats = null;
134          }
135
136          const subIds = idsOf(subcats);
137
138          // ✅ Anti-loop:
139          // se la lista sottocategorie è vuota, oppure uguale al pannello corrente, NON è un vero livello.
140          const isRealSublevel =
141            Array.isArray(subcats) &&
142            subcats.length > 0 &&
143            subIds &&
144            subIds !== currentIds &&
145            !subIds.split(",").includes(String(it.id)); // evita caso in cui torna sé stessa
146
147          if (isRealSublevel) {
148            crumbs.push(decodeHtmlEntities(it.name ?? ""));
149            setCourseTitle();
150
151            track.appendChild(makePanel(subcats, "category", { parentSlug: it.slug || "" }));
152            depth += 1;
153            translate();
154            return;
155          }
156
157          // 2) fallback: carica corsi
158          try {
159            const courses = await api(`/wp-json/cj/v1/courses?cat=${encodeURIComponent(it.slug || "")}&include_children=0`);
160
161            crumbs.push(decodeHtmlEntities(it.name ?? ""));
162            setCourseTitle();
163
164            track.appendChild(makePanel(courses, "course"));
165            depth += 1;
166            translate();
167          } catch (e) {
168            log("Errore caricamento corsi:", e);
169          }
170        });
171
172        li.appendChild(btn);
173      } else {
174        const a = document.createElement("a");
175        a.className = "cjcourses__link";
176        a.href = it.url;
177        a.textContent = decodeHtmlEntities(it.title);
178        li.appendChild(a);
179      }
180
181      ul.appendChild(li);
182    });
183
184    panel.appendChild(ul);
185    return panel;
186  }
187
188  async function openCourses() {
189    if (chooseBtnWrap) chooseBtnWrap.classList.add("btn_disabled");
190    if (cont_btn_header) cont_btn_header.classList.add("all_disabled");
191
192    menuBox.style.display = "none";
193    courseBox.style.display = "block";
194
195    track.innerHTML = "";
196    depth = 0;
197    crumbs = [];
198    setCourseTitle();
199    translate();
200
201    // root categories
202    const cats = await api("/wp-json/cj/v1/course-categories?parent=0");
203    track.appendChild(makePanel(cats, "category", { parentSlug: "" }));
204  }
205
206  function backCourses() {
207    if (depth > 0) {
208      const panels = track.querySelectorAll(".cjcourses__panel");
209      if (panels.length) panels[panels.length - 1].remove();
210      depth -= 1;
211      crumbs.pop();
212      setCourseTitle();
213      translate();
214      return;
215    }
216
217    courseBox.style.display = "none";
218    menuBox.style.display = "block";
219    if (chooseBtnWrap) chooseBtnWrap.classList.remove("btn_disabled");
220    if (cont_btn_header) cont_btn_header.classList.remove("all_disabled");
221  }
222
223  courseBox.style.display = "none";
224  menuBox.style.display = "block";
225
226  chooseBtn.addEventListener("click", (e) => {
227    e.preventDefault();
228    e.stopPropagation();
229    openCourses().catch(console.error);
230  });
231
232  if (courseBackWrap) {
233    courseBackWrap.addEventListener("click", (e) => {
234      e.preventDefault();
235      e.stopPropagation();
236      backCourses();
237    });
238    courseBackWrap.addEventListener("keydown", (e) => {
239      if (e.key === "Enter" || e.key === " ") {
240        e.preventDefault();
241        backCourses();
242      }
243    });
244  }
245})();

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.