PageSourceSearch

https://www.supertennis.tv/Areas/Supertennis2025/js/home_widget_v3.js

js supertennis.tv collected 2026-09-24 09:39:47 UTC 56,681 bytes, 1,722 lines download raw bytes

1/*let utente_discipline = {
2	"loyalty_program": true,
3	"discipline": [
4		{
5			"id_disciplina": 4332,
6			"disciplina": "tennis",
7			"membershipCard": "NzgzMjU5OTY0OQ==",
8			"ago": true,
9			"nonAgo": false,
10			"socio": false,
11			"ex_ago": false,
12			"ex_nonAgo": false,
13			"ex_socio": false
14		},
15		{
16			"id_disciplina": 174,
17			"disciplina": "pickleball",
18			"membershipCard": "NTU3OTg3MDgwMQ==",
19			"ago": true,
20			"nonAgo": false,
21			"socio": false,
22			"ex_ago": false,
23			"ex_nonAgo": false,
24			"ex_socio": false
25		}
26	]
27};*/
28
29// Ex Tesserato con tessera scaduta NON AGO o SOCIO
30/*let utente_discipline = {
31	"loyalty_program": false,
32	"discipline": [
33		{
34			"id_disciplina": 4332,
35			"disciplina": "tennis",
36			"membershipCard": "",
37			"ago": false,
38			"nonAgo": false,
39			"socio": false,
40			"ex_ago": false,
41			"ex_nonAgo": true,
42			"ex_socio": false
43		}
44	]
45}*/
46
47//Tesserato Non Agonista (senza attività sportiva)
48/*let utente_discipline = {
49	"loyalty_program": false,
50	"discipline": [
51		{
52			"id_disciplina": 4332,
53			"disciplina": "tennis",
54			"membershipCard": "NzgzMjU5OTY0OQ==",
55			"ago": false,
56			"nonAgo": true,
57			"socio": false,
58			"ex_ago": false,
59			"ex_nonAgo": false,
60			"ex_socio": false,
61			"id_persona_tpra": 174417
62		}
63	]
64}*/
65
66// non tesserato
67/*let utente_discipline = {
68	"loyalty_program": false,
69	"discipline": null
70};*/
71
72let utente_discipline = null;
73let hasLoyaltyProgram = false;
74let nonTesserato = false;
75
76let id_disciplina = null;
77let nome_disciplina = null;
78let membershipCard = null;
79let ago = null;
80let nonAgo = null;
81let socio = null;
82let ex_ago = null;
83let ex_nonAgo = null;
84let ex_socio = null;
85let id_persona_tpra = null
86
87const basePathSite = "Supertennis2025";
88
89function getHtmlFromTemplateDDDD(id) {
90	return document.getElementById(id).innerHTML;
91}
92
93function getHtmlFromTemplateRichiediTessera(id, disciplina) {
94	const template = document.getElementById(id);
95	if (!template) return "";
96
97	// Clona il contenuto del template
98	const clone = template.content.cloneNode(true);
99
100	// Aggiorna il testo dentro <span class="disciplina-label">
101	const spanDisciplina = clone.querySelector(".disciplina-label");
102	if (spanDisciplina) {
103		spanDisciplina.textContent = disciplina;
104	}
105
106	// Inserisce il contenuto in un div temporaneo e restituisce l'HTML come stringa
107	const temp = document.createElement("div");
108	temp.appendChild(clone);
109	return temp.innerHTML;
110}
111
112function generaLinkTorneo(tipo, id_torneo, id_disciplina, id_settore, guid) {
113	if (tipo === 'TORNEI FIT' || tipo === 'TORNEI FITP') {
114		return 'https://www.fitp.it/Tornei/Dettaglio-Competizione.html?competitionId=' + guid;
115	}
116	if (tipo === 'TORNEI TPRA') {
117		let disciplina = '';
118		if (id_disciplina === 4332) disciplina = 'tennis';
119		else if (id_disciplina === 172) disciplina = 'padel';
120		else if (id_disciplina === 171) disciplina = 'beach';
121		return 'https://tpra2.fitp.it/tornei/dettaglio-torneo/' + id_torneo + '?disciplina=' + disciplina + '&settore=' + id_settore;
122	}
123	if (tipo === 'CAMPIONATI A SQUADRE') {
124		return '/Tornei/Campionato-a-squadre-dettagli?competitionId=' + guid;
125	}
126	return '#'; // fallback
127}
128
129function getIconaDisciplina(id_disciplina) {
130	switch (id_disciplina) {
131		case 4332: return "tennis_fitp.svg";
132		case 172: return "padel_fitp.svg";
133		case 171: return "beach_fitp.svg";
134		case 174: return "pickleball_fitp.svg";
135		default: return "tennis_fitp.svg"; // fallback
136	}
137}
138
139function getNomeDisciplina(id_disciplina) {
140	switch (id_disciplina) {
141		case 4332: return "tennis";
142		case 172: return "padel";
143		case 171: return "beach tennis";
144		case 174: return "pickleball";
145		default: return "tennis"; // fallback
146	}
147}
148
149async function generaHtmlBoxTornei(idDisciplina, idProvincia = "258") {
150  const apiUrl = "https://dp-myfit-test-function-v2.azurewebsites.net/api/v1/tornei/raccomandati/list"; // Modificato endpoint
151  const token = localStorage.getItem("tokenAdb2c");
152  const guid = localStorage.getItem("Guid");
153  const member = localStorage.getItem("Member") || "1";
154  const teacher = localStorage.getItem("Teacher") || "0";
155  const udg = localStorage.getItem("Udg") || "1";
156  const acceptProfilation = localStorage.getItem("AcceptProfilation") || "1";
157  const hashKey = localStorage.getItem("Hashkey");  
158  
159
160  if (!token || !guid || !hashKey) {
161    console.error("Token, GUID o hashKey mancanti");
162    return null;
163  }
164
165  // Funzione di conversione idDisciplina in stringa
166  const convertIdDisciplina = (id) => {
167    switch (id) {
168      case 4332:
169        return "tennis";
170      case 172:
171        return "padel";
172      case 171:
173        return "beach";
174      case 174:
175        return "pickleball";
176      default:
177        return null; // Ritorna null se l'id non è valido
178    }
179  };
180
181  // Converti l'idDisciplina in stringa
182  const disciplinaString = convertIdDisciplina(idDisciplina);
183  if (!disciplinaString) {
184    console.error("ID disciplina non valido.");
185    return `<div class="text-white ps-3 pt-3">ID disciplina non valido</div>`;
186  }
187
188  try {
189    const response = await fetch(apiUrl, {
190      method: "POST",
191      headers: {
192        "Content-Type": "application/json",
193        Authorization: `Bearer ${token}`,
194      },
195      body: JSON.stringify({
196        guid,
197        member,
198        teacher,
199        udg,
200        acceptProfilation,
201        hashKey,
202        from_Tpra: false,
203      }),      
204    });
205
206    if (!response.ok) throw new Error("Risposta non valida");
207
208    const data = await response.json();
209
210    // Controllo per i tornei disponibili
211    const tornei = data[disciplinaString] || []; // Usa la stringa per accedere ai tornei
212
213    if (tornei.length === 0) {
214      return `
215        <div class="tournament-list rounded-4 rounded-bottom-0 border-bottom-0 pb-4 pt-3 pe-0 h-100 me-xl-5 ms-xl-1">
216          <div class="d-flex justify-content-between align-items-center pb-3">
217            <div class="text-fitp fw-bold ps-3">TORNEI VICINO A TE</div>
218            <button class="btn p-0 d-flex align-items-center gap-1">
219              <img src="Areas/${basePathSite}/images/widget/open-in-new.svg" alt="show more">
220              <a href="/Tornei/Ricerca-tornei" target="_blank" class="text-fitp fw-bold pe-3">CERCA TORNEO</a>
221            </button>
222          </div>
223          <div class="py-5 text-fitp text-center fw-bold">Nessun torneo disponibile</div>
224        </div>`;
225    }
226
227    // Funzione di parsing per data in formato dd-MM-yyyy
228    const parseDate = (str) => {
229      const [day, month, year] = str.split("-").map(Number);
230      return new Date(year, month - 1, day);
231    };
232
233    // Ordina tornei per data
234    const sortedTornei = tornei
235      .sort((a, b) => parseDate(a.data_inizio) - parseDate(b.data_inizio))
236      .slice(0, 3); // Limita ai primi 3 tornei
237	  
238    function capitalize(text) {
239      return text.toLowerCase().replace(/\b\w/g, (char) => char.toUpperCase());
240    }
241
242	const formatDate = (str) => {
243	  if (!str) return "";
244	  return str.replace(/-/g, "/");
245	};	
246
247    const htmlTornei = sortedTornei
248      .map((t) => {
249        const link = t.competitionLink; // Usa competitionLink direttamente
250        const icona = getIconaDisciplina(disciplinaString); // Usa la stringa per ottenere l'icona
251        return `
252        <a href="${link}" target="_blank">
253          <div class="tournament-item row align-items-stretch g-0">
254            <div class="col-md-4 bg-grey py-0 d-flex align-items-center mb-2">
255              <img src="/Areas/${basePathSite}/images/svg/${icona}" alt="tennis" class="px-2" style="height: 30px">
256              <div class="py-3">
257                <div class="d-flex text-fitp">
258                  <p class="mb-0 pe-1 text-fitp">dal</p>
259                  <p class="mb-0 text-fitp">${formatDate(t.data_inizio)}</p>
260                </div>
261                <div class="d-flex text-fitp">
262                  <p class="pe-1 mb-0 text-fitp">al</p>
263                  <p class="mb-0 text-fitp">${formatDate(t.data_fine)}</p>
264                </div>
265              </div>
266            </div>
267            <div class="col-md-8 py-2 ps-3 mb-2 align-content-center">
268              <div class="text-fitp fw-bold item-text">${capitalize(t.tennisclub)}</div>
269              <div class="text-fitp item-text">${capitalize(t.citta)} (${t.sigla_provincia})</div>
270              <div class="text-fitp item-text">${capitalize(t.nome_torneo)}</div>
271            </div>
272          </div>
273        </a>`;
274      })
275      .join("");
276
277    return `
278      <div class="tournament-list rounded-4 rounded-bottom-0 border-bottom-0 pb-4 pt-3 pe-0 h-100 me-xl-5 ms-xl-1" style="line-height: 1.3">
279        <div class="d-flex justify-content-between align-items-center pb-3">
280          <div class="text-fitp fw-bold ps-3">TORNEI VICINO A TE</div>
281          <button class="btn p-0 d-flex align-items-center gap-1">
282            <img src="Areas/${basePathSite}/images/widget/open-in-new.svg" alt="show more">
283            <a href="/Tornei/Ricerca-tornei" target="_blank" class="text-fitp fw-bold pe-3">CERCA TORNEO</a>
284          </button>
285        </div>
286        <div class="tornei-list">
287          ${htmlTornei}
288        </div>
289      </div>`;
290  } catch (error) {
291    console.error("Errore nel caricamento tornei:", error);
292    return `<div class="text-white ps-3 pt-3 text-center">Nessun torneo presente</div>`;
293  }
294}
295
296async function caricaDatiClassifica(disciplina) {
297	const apiUrl = "https://dp-myfit-test-function-v2.azurewebsites.net/api/v1/ranking/list";
298	const token = localStorage.getItem("tokenAdb2c");
299	const guid = localStorage.getItem("Guid");
300	const member = localStorage.getItem("Member") || "1";
301	const teacher = localStorage.getItem("Teacher") || "0";
302	const udg = localStorage.getItem("Udg") || "1";
303	const acceptProfilation = localStorage.getItem("AcceptProfilation") || "1";
304	const hashKey = localStorage.getItem("Hashkey");
305
306	if (!token || !guid || !hashKey) {
307		console.error("Token, GUID o hashKey mancanti");
308		return null;
309	}
310
311	try {
312		const response = await fetch(apiUrl, {
313			method: "POST",
314			headers: {
315				"Content-Type": "application/json",
316				"Authorization": `Bearer ${token}`
317			},
318			body: JSON.stringify({
319				guid,
320				member,
321				teacher,
322				udg,
323				acceptProfilation,
324				hashKey,
325				from_Tpra: false
326			})
327		});
328
329		if (!response.ok) throw new Error("Errore nella risposta");
330
331		const json = await response.json();
332		const dati = (json.ranking || []).find(r => r.discipline === disciplina);
333
334		return dati || null;
335	} catch (err) {
336		console.error("Errore recupero classifica:", err);
337		return null;
338	}
339}
340
341/*function generaHtmlClassifica(dati, disciplina) {
342	if (!dati) {
343		return `<div class="text-white ps-5 pt-3">Nessuna classifica trovata per ${disciplina}</div>`;
344	}
345
346	// const categoria = dati.category?.substring(0, 2) || "N/D";
347	const classifica = dati.rank?.substring(0, 2)|| "N/D";
348	const bestRank = dati.maxRank?.substring(0, 2)|| "N/D";
349	const annoBest = dati.maxRankSeason || "N/D";
350	const rankProjection = dati.rankProjection || "N/D";
351	const coefficient = dati.coefficient || "N/D";
352
353	return `
354		<div class="ranking-list pb-4 ps-1">
355			<div class="h-100 pb-4">
356				<h5 class="text-white py-3">LE MIE STATISTICHE</h5>
357				<div class="row pb-3 d-flex">
358					<div class="${disciplina == 'padel' ? 'col-12 pb-3' : 'col-6 pe-1 pb-1'} flex-column flex-lg-row  ">
359						<div class="border border-white rounded p-2 text-center">
360							<div class="text-white">${disciplina == 'padel' ? 'Fascia' : 'Classifica'}</div>
361							<div class="numero-categoria">${classifica}</div>
362						</div>
363					</div>
364					<div class="${disciplina == 'padel' ? 'col-12' : 'col-6 ps-1'} flex-column flex-lg-row ">
365						<div class="border border-white rounded p-2 text-center">
366							<div>${disciplina == 'padel' ? 'Coeff.' : 'Proiezione'}</div>
367							<div class="numero-categoria">${disciplina == 'padel' ? coefficient : rankProjection}</div>
368						</div>
369					</div>
370				</div>
371				<div class="border border-white rounded p-3 d-flex justify-content-between flex-column flex-lg-row mb
371-0">
372					<div>
373						<p class="mb-1 text-white">Best Rank</p>
374						<div class="pt-3">${annoBest}</div>
375					</div>
376					<div class="d-flex flex-column align-items-center">
377						<img src="Areas/${basePathSite}/images/widget/0oz79ka0sb8bm9bgracd.svg" alt="rank" style="width: 24px; height: 24px">
378						<div class="numero-categoria">${bestRank}</div>
379					</div>
380				</div>
381			</div>
382		</div>
383	`;
384}*/
385
386function generaHtmlClassifica(dati, disciplina) {
387	if (!dati) {
388		return `<div class="h-100 d-flex flex-column justify-content-center align-items-center text-white">Nessuna classifica trovata per ${disciplina}</div>`;
389	}
390
391	const categoria = dati.category?.substring(0, 2) || "N/D";
392	const classifica = dati.rank || "N/D";
393	const bestRank = dati.maxRank || "N/D";
394	const annoBest = dati.maxRankSeason || "N/D";
395	const rankProjection = dati.rankProjection || "N/D";
396	const coefficient = dati.coefficient || "N/D";
397
398	const mostraClassifica = disciplina !== 'padel';
399	const etichettaCategoria = disciplina === 'padel' ? 'Fascia' : 'Categoria';
400
401	return `
402	
403			<div class="h-100 pb-5 pb-xl-4 ms-1">
404				<h5 class="text-white py-3">LE MIE STATISTICHE</h5>
405				<div class="row pb-3 d-flex flex-wrap">
406				
407					<!-- Categoria o Fascia -->
408					<div class="col-6 pe-1 pb-2">
409						<div class="border border-white rounded p-2 text-center">
410							<div class="text-white">${etichettaCategoria}</div>
411							<div class="numero-categoria">${categoria}</div>
412						</div>
413					</div>
414					
415					<!-- Classifica (se non padel) -->
416					${mostraClassifica ? `
417					<div class="col-6 ps-1 pb-2">
418						<div class="border border-white rounded p-2 text-center">
419							<div class="text-white">Classifica</div>
420							<div class="numero-categoria">${classifica}</div>
421						</div>
422					</div>` : ''}
423
424					<!-- Proiezione o Coefficiente -->
425					<div class="${disciplina === 'padel' ? 'col-6 pe-1' : 'col-12'}  pb-2">
426						<div class="border border-white rounded p-2 text-center">
427							<div class="text-white">${disciplina === 'padel' ? 'Coeff.' : 'Proiezione'}</div>
428							<div class="numero-categoria">${disciplina === 'padel' ? coefficient : rankProjection}</div>
429						</div>
430					</div>
431
432					<!-- Best Rank -->
433					<div class="col-12 pb-2">
434						<div class="border border-white rounded p-3 justify-content-between align-items-center h-100 ${disciplina === 'padel' ? '' : 'd-flex'}">
435							<p class="mb-1 text-white">Best Rank</p>
436							<div class="pt-1">${annoBest}</div>
437							<div class="d-flex flex-column align-items-center pt-2">
438								<img src="Areas/${basePathSite}/images/widget/0oz79ka0sb8bm9bgracd.svg" alt="rank" style="width: 24px; height: 24px">
439								<div class="numero-categoria">${bestRank}</div>
440							</div>
441						</div>
442					</div>
443
444				</div>
445			</div>
446	
447	`;
448}
449
450async function generaHtmlMovimentiPuntiFedelta() {
451	const apiUrl = "https://dp-myfit-test-function-v2.azurewebsites.net/api/v1/loyalty-program/movimenti-punti/list";
452	const token = localStorage.getItem("tokenAdb2c");
453	const guid = localStorage.getItem("Guid");
454	const member = localStorage.getItem("Member") || "1";
455	const teacher = localStorage.getItem("Teacher") || "0";
456	const udg = localStorage.getItem("Udg") || "1";
457	const acceptProfilation = localStorage.getItem("AcceptProfilation") || "1";
458	const hashKey = localStorage.getItem("Hashkey");
459
460	if (!token || !guid || !hashKey) {
461		console.error("Token, GUID o hashKey mancanti");
462		return `<div class="text-white ps-3 pt-3">Errore autenticazione</div>`;
463	}
464
465	try {
466		const response = await fetch(apiUrl, {
467			method: "POST",
468			headers: {
469				"Content-Type": "application/json",
470				"Authorization": `Bearer ${token}`
471			},
472			body: JSON.stringify({
473				guid,
474				member,
475				teacher,
476				udg,
477				acceptProfilation,
478				hashKey,
479				from_Tpra: false,
480				rowsToSkip: 0,
481				fetchRows: 3,
482				sortColumn: "data",
483				sortOrder: "desc",
484				data_da: "",
485				data_a: ""
486			})
487		});
488
489		if (!response.ok) throw new Error("Errore nella risposta");
490
491		const json = await response.json();
492		const movimenti = json.results || [];
493
494		if (movimenti.length === 0) {
495			//return `<div class="h-100 d-flex flex-column justify-content-center align-items-center text-white">Nessun movimento punti trovato</div>`;
496		}
497
498		let itemsHtml = movimenti.map(item => {
499			const descrizione = item.testo || "N/D";
500			const punti = item.punti != null ? `${item.punti} pt` : "0 pt";
501			const sigla = item.sigla || "N/D";
502			const data = item && item.data ? new Date(item.data).toLocaleDateString("it-IT") : "N/D";
503
504			return `
505				<div class="point-item row mb-3">
506					<div class="col-3 text-white mb-0" style="font-size: 14.5px">${data}</div>
507					<div class="col-6 text-white mb-0" style="font-size: 14.5px">${descrizione}</div>
508					<div class="col-3 text-white fw-bold mb-0">${punti}</div>
509				</div>
510			`;
511		}).join("");
512		/* <div class=" text-white mb-0" style="font-size: 14.5px">${sigla}</div> */
513		
514		if (movimenti.length === 0) {
515			itemsHtml = `<div class="d-flex flex-column justify-content-center align-items-center text-white" style="min-height: 165px;">Nessun movimento punti trovato<br />Inizia ora ad accumulare i tuoi punti</div>`;
516		}		
517
518		return `
519           <div class="pb-4 h-100">
520                <div class="px-5 h-100">
521                    <h5 class="text-white text-center mb-2 pt-3">Punti fedeltà</h5>
522                    <p class="text-white text-center mb-4">Ultimi Movimenti</p>
523                    ${itemsHtml}
524					<button class="btn p-0 d-flex align-items-center gap-2 pt-1 text-white justify-content-center w-100">
525						<img src="Areas/${basePathSite}/images/widget/open-in-new-white.svg" alt="show more">
526						<a href="https://my.fitp.it/loyalty-gestione"  target="_blank" class="text-white fw-bold">
526VAI AL PROGRAMMA</a>
527					</button>
528                </div>
529           </div>
530        `;
531	} catch (err) {
532		console.error("Errore recupero movimenti fedeltà:", err);
533		return `<div class="text-white ps-3 pt-3">Errore nel recupero dei movimenti</div>`;
534	}
535}
536
537function attachSelectHandler(cardNumber) {
538	const select = document.querySelector(`#box-stats-ago select`);
539	if (select) {
540		select.addEventListener('change', async (e) => {
541			const nuovoAnno = parseInt(e.target.value);
542			const container = document.getElementById(`box-stats-ago`);
543			if (container) {
544				container.innerHTML = await generaBoxStatisticheIstogramma(cardNumber, 4332, nuovoAnno);
545				attachSelectHandler(cardNumber); // Riattacca perché il DOM è stato sovrascritto
546			}
547		});
548	}
549}
550
551async function generaBoxStatisticheIstogramma(cardNumber, disciplineId = 4332, annoSelezionato = null) {
552	const token = localStorage.getItem("tokenAdb2c");
553	const guid = localStorage.getItem("Guid");
554	const member = localStorage.getItem("Member") || "1";
555	const teacher = localStorage.getItem("Teacher") || "0";
556	const udg = localStorage.getItem("Udg") || "1";
557	const acceptProfilation = localStorage.getItem("AcceptProfilation") || "1";
558	const hashKey = localStorage.getItem("Hashkey");	
559	
560	const payloadBase = {
561		guid,
562		member,
563		teacher,
564		udg,
565		acceptProfilation,
566		hashKey
567	};
568
569	const headers = {
570		'Authorization': `Bearer ${token}`,
571		'Content-Type': 'application/json'
572	};
573
574	const resAgenda = await fetch('https://dp-myfit-test-function-v2.azurewebsites.net/api/v1/tesserati/dettaglio/semplice', {
575		method: 'POST',
576		headers,
577		body: JSON.stringify({
578			cardNumber,
579			fromYear: new Date().getFullYear(),
580			toYear: new Date().getFullYear()
581		})
582	});
583
584	const agendaData = await resAgenda.json();
585	const anniDisponibili = agendaData.activeYears.map(y => y.year).sort((a, b) => b - a);
586	//const cardNumber = agendaData.discipline.find(d => d.idDiscipline === disciplineId)?.membershipCard;
587	
588	if (anniDisponibili.length == 0) {
589		return `
590		<div class="d-flex flex-column h-100 justify-content-center align-items-center">
591			Nessun dato disponibile </div>
592			`;
593	}
594
595	if (!cardNumber) return '<div>Card non trovata</div>';
596	if (!annoSelezionato) annoSelezionato = anniDisponibili[0];
597
598	const resStats = await fetch('https://dp-fit-prod-function.azurewebsites.net/api/v6/player/stats', {
599		method: 'POST',
600		headers,
601		body: JSON.stringify({
602			...payloadBase,
603			cardNumber,
604			year: annoSelezionato,
605			tipology: '_T_'
606		})
607	});
608
609	const stats = await resStats.json();
610	
611	const vPartite = stats.Totale_Partite_VINTE || 0;
612	const pPartite = stats.Totale_Partite_PERSE || 0;
613	const vSet = stats.Set_Vinti_Totale || 0;
614	const pSet = stats.Set_Persi_Totale || 0;
615	const vGame = stats.Game_Vinti_Totale || 0;
616	const pGame = stats.Game_Persi_Totale || 0;
617
618	// Totale di partite, set e game
619	const totalePartite = stats.Totale_Partite || 0;
620	const totaleSet = stats.Set_Giocati_Totale || 0;
621	const totaleGame = stats.Game_Totali || 0;
622
623	// Calcoliamo il massimo dei totali per determinare la scala
624	const maxValue = Math.max(totalePartite, totaleSet, totaleGame);
625
626	// Calcolo la scala in base al massimo totale (per non avere barre troppo basse)
627	const scale = maxValue > 0 ? 100 / maxValue : 1;
628
629	// Funzione per generare le barre in base alla percentuale rispetto al totale
630	function generaBarra(v, p, totale) {
631		const percentV = (v / totale) * 100;  // Percentuale di vittorie
632		const percentP = (p / totale) * 100;  // Percentuale di sconfitte
633
634		const maxHeight = 120;  // Altezza massima per la barra (ad esempio 200px)
635
636		// Calcoliamo l'altezza in px in base alla percentuale
637		const hV = Math.round((percentV * maxHeight) / 100);
638		const hP = Math.round((percentP * maxHeight) / 100);
639
640		return `
641			<div class="bar-pair">
642				<div class="d-flex flex-column align-items-center mx-1">
643					<div class="bar bg-success" style="height: ${hV}px"></div>
644					<div class="bar-label pt-2">V</div>
645					<div class="pt-2 text-center" style="
646    min-width: 1.5rem;
647">${v}</div>
648				</div>
649				<div class="d-flex flex-column align-items-center mx-1">
650					<div class="bar bg-danger" style="height: ${hP}px"></div>
651					<div class="bar-label pt-2">P</div>
652					<div class="pt-2 text-center" style="
653    min-width: 1.5rem;
654">${p}</div>
655				</div>
656			</div>`;
657	}
658
659	/* const bottoni = anniDisponibili.map(anno =>
660		`<button class="stats-button${anno === annoSelezionato ? ' active' : ''}">${anno}</button>`).join(''); */
661
662	const selectOptions = anniDisponibili.map(anno =>
663		`<option value="${anno}"${anno === annoSelezionato ? ' selected' : ''}>${anno}</option>`
664	).join('');
665
666	return `
667		<div class="border-item-stats h-100 px-5 pe-xl-0">
668			<div class="d-flex justify-content-center py-3">
669			<select class="form-select">
670			${selectOptions}
671			</select>
672			</div>
673			<div class="d-flex justify-content-around mb-3">
674				<div>Partite</div>
675				<div>Set</div>
676				<div>Game</div>
677			</div>
678			<div class="bar-chart">
679				${generaBarra(vPartite, pPartite, totalePartite)}
680				${generaBarra(vSet, pSet, totaleSet)}
681				${generaBarra(vGame, pGame, totaleGame)}
682			</div>
683			 </div>
684        `;
685}
686
687async function caricaDatiStatisticheIstogrammaNonAgo(id_persona_tpra, id_disciplina = 4332) {
688	const token = localStorage.getItem("tokenAdb2c");
689	const guid = localStorage.getItem("Guid");
690	const member = localStorage.getItem("Member") || "1";
691	const teacher = localStorage.getItem("Teacher") || "0";
692	const udg = localStorage.getItem("Udg") || "1";
693	const acceptProfilation = localStorage.getItem("AcceptProfilation") || "1";
694	const hashKey = localStorage.getItem("Hashkey");
695	
696	const payloadBase = {
697		guid,
698		member,
699		teacher,
700		udg,
701		acceptProfilation,
702		hashKey
703	};
704
705	const headers = {
706		'Authorization': `Bearer ${token}`,
707		'Content-Type': 'application/json'
708	};
709
710	const response = await fetch('https://dp-myfit-test-function-v2.azurewebsites.net/api/v1/widget/utente/tpra-statistiche/list', {
711		method: 'POST',
712		headers,
713		body: JSON.stringify({
714			...payloadBase,
715			id_persona_tpra,
716			id_disciplina
717		})
718	});
719	
720	if (response.status === 204) {
721		return null; // Nessun contenuto restituito dal server
722	}	
723
724	const json = await response.json();
725
726	//const json = [{"anno":2025,"partite_giocate_totale":5,"partite_vinte_totale":2,"partite_perse_totale":3,"set_giocati_totale":11,"set_vinti_totale":5,"set_persi_totale":6,"game_giocati_totale":83,"game_vinti_totale":43,"game_persi_totale":40},{"anno":2024,"partite_giocate_totale":2,"partite_vinte_totale":1,"partite_perse_totale":1,"set_giocati_totale":4,"set_vinti_totale":2,"set_persi_totale":2,"game_giocati_totale":29,"game_vinti_totale":16,"game_persi_totale":13},{"anno":2022,"partite_giocate_totale":1,"partite_vinte_totale":0,"partite_perse_totale":1,"set_giocati_totale":2,"set_vinti_totale":0,"set_persi_totale":2,"game_giocati_totale":14,"game_vinti_totale":0,"game_persi_totale":14}];
727
728	if (json && Array.isArray(json) && json.length > 0) {
729		return json; // restituisce solo il flusso dati
730	} else {
731		return null; // nessun dato valido
732	}
733}
734
735function generaBoxStatisticheIstogrammaNonAgo(data, id_persona_tpra, id_disciplina = 4332, annoSelezionato = null) {
736	const anniDisponibili = data.map(d => d.anno).sort((a, b) => b - a);
737	if (!annoSelezionato) annoSelezionato = anniDisponibili[0];
738
739	function generaBarrePerAnno(anno) {
740		const stats = data.find(d => d.anno === parseInt(anno));
741		if (!stats) return '<div class="text-center">Dati non disponibili</div>';
742
743		const {
744			partite_giocate_totale: totalePartite = 0,
745			partite_vinte_totale: vPartite = 0,
746			partite_perse_totale: pPartite = 0,
747			set_giocati_totale: totaleSet = 0,
748			set_vinti_totale: vSet = 0,
749			set_persi_totale: pSet = 0,
750			game_giocati_totale: totaleGame = 0,
751			game_vinti_totale: vGame = 0,
752			game_persi_totale: pGame = 0
753		} = stats;
754
755		function generaBarra(v, p, totale) {
756			const percentV = (v / totale) * 100;
757			const percentP = (p / totale) * 100;
758			const maxHeight = 120;
759			const hV = Math.round((percentV * maxHeight) / 100);
760			const hP = Math.round((percentP * maxHeight) / 100);
761
762			return `
763				<div class="bar-pair">
764					<div class="d-flex flex-column align-items-center mx-1">
765						<div class="bar bg-success" style="height: ${hV}px"></div>
766						<div class="bar-label pt-2">V</div>
767						<div class="pt-2 text-center" style="min-width: 1.5rem;">${v}</div>
768					</div>
769					<div class="d-flex flex-column align-items-center mx-1">
770						<div class="bar bg-danger" style="height: ${hP}px"></div>
771						<div class="bar-label pt-2">P</div>
772						<div class="pt-2 text-center" style="min-width: 1.5rem;">${p}</div>
773					</div>
774				</div>`;
775		}
776
777		return `
778			${generaBarra(vPartite, pPartite, totalePartite)}
779			${generaBarra(vSet, pSet, totaleSet)}
780			${generaBarra(vGame, pGame, totaleGame)}
781		`;
782	}
783
784	const selectOptions = anniDisponibili.map(anno =>
785		`<option value="${anno}"${anno === annoSelezionato ? ' selected' : ''}>${anno}</option>`
786	).join('');
787
788	const containerId = `stat-box-${id_persona_tpra}-${id_disciplina}`;
789
790	return {
791		containerId,
792		html: `
793			<div id="${containerId}" class="border-item-stats h-100 px-5 pe-xl-0">
794				<div class="d-flex justify-content-center py-3">
795					<select class="form-select">
796						${selectOptions}
797					</select>
798				</div>
799				<div class="d-flex justify-content-around mb-3">
800					<div>Partite</div>
801					<div>Set</div>
802					<div>Game</div>
803				</div>
804				<div class="bar-chart">
805					${generaBarrePerAnno(annoSelezionato)}
806				</div>
807			</div>
808		`,
809		updateChart: (anno, chartEl) => {
810			chartEl.innerHTML = generaBarrePerAnno(anno);
811		}
812	};
813}
814
815async function generaHtmlBoxTorneiIscrizioni(idDisciplina, idProvincia = "258") {
816	const apiUrl = "https://dp-myfit-test-function-v2.azurewebsites.net/api/v2/tornei/iscrizioni/list";
817	const token = localStorage.getItem("tokenAdb2c");
818	const guid = localStorage.getItem("Guid");
819	const member = localStorage.getItem("Member") || "1";
820	const teacher = localStorage.getItem("Teacher") || "0";
821	const udg = localStorage.getItem("Udg") || "1";
822	const acceptProfilation = localStorage.getItem("AcceptProfilation") || "1";
823	const hashKey = localStorage.getItem("Hashkey");
824	
825	const payloadBase = {
826		guid,
827		member,
828		teacher,
829		udg,
830		acceptProfilation,
831		hashKey
832	};
833
834	if (!token || !guid) {
835		console.error("Token o GUID non presenti nel localStorage.");
836		return `<div class="text-white ps-3 pt-3">Errore autenticazione</div>`;
837	}
838
839	//try {
840		const response = await fetch(apiUrl, {
841			method: "POST",
842			headers: {
843				"Content-Type": "application/json",
844				"Authorization": `Bearer ${token}`
845			},
846			body: JSON.stringify({
847				...payloadBase,
848				onlyOpen: true,
849				from_Tpra: false
850			})
851		});
852
853		if (!response.ok) throw new Error("Risposta non valida");
854		
855		let data = {"tornei": []};
856
857		if (response.status === 200) {
858			data = await response.json();
859		}
860		
861		/*const data = {
862    "tornei": [
863        {
864            "idTorneo": 140687,
865            "descrizioneTorneo": "Singolare Maschile Open LIM. 4.NC - 3.1",
866            "consentitaDisiscrizione": true,
867            "torneoDal": "02/07/2025",
868            "torneoAl": "13/07/2025",
869            "dataIscrizione": "15/05/2025",
870            "disciplina": "Tennis",
871            "idTessera": "7832599649",
872            "uidTorneo": "53c182a9-a373-4617-b5c4-18f421c5dca4",
873            "nomeTorneo": "TROFEO MATCHBALL CLUB ESTATE 2025",
874            "competitionUid": "d641925d-ad7d-4dbc-b654-7f2589251181",
875            "competitonId": 51142,
876            "termineIscrizione": "29/06/2025",
877            "motivazione": "",
878            "onLineSubscription": true,
879            "uidDoubleTeam": null,
880            "isOnlyOnline": true,
881            "isFirstPlayer": true,
882            "competitionLink": "https://www.fitp.it/Tornei/Dettaglio-Competizione.html?competitionId=d641925d-ad7d-4dbc-b654-7f2589251181",
883            "tournamentYear": 2025,
884            "tournamentMonth": 7,
885            "idDisciplina": 1,
886            "gaMsgNumber": 0,
887            "consentitePreferenze": true,
888            "consentitiMessaggiGa": true,
889            "isDouble": null,
890            "tipologia": "FIT",
891            "utilizzatoSpecialEntry": false
892        }
893    ],
894    "tournamentNumber": 1,
895    "tournamentSubscriptions": "53c182a9-a373-4617-b5c4-18f421c5dca4",
896    "tournamentSubscriptionsTPRA": "",
897    "tournamentSubscriptionESports": ""
898};*/
899		
900		
901		const tornei = (data.tornei || [])
902			.slice(0, 3);
903
904		if (tornei.length === 0) {
905			return `
906		<div class="tournament-list-transparent rounded-4 rounded-bottom-0 border-bottom-0 border pb-4 pt-3 pe-3 h-100 ms-1 ms-xl-5" style="line-height: 1.3">
907			<div class="d-flex justify-content-between align-items-center pb-3">
908				<div class="text-white fw-bold ps-3">I MIEI TORNEI</div>
909					<button class="btn p-0 d-flex align-items-center gap-1">
910						<img src="Areas/${basePathSite}/images/widget/open-in-new-white.svg" alt="show more">
911						<a href="https://my.fitp.it/tornei" target="_blank" class="text-white fw-bold">VISUALIZZA TUTTI</a>
912					</button>
913				</div>
914				<div class="py-5 text-white text-center fw-bold">Nessun torneo disponibile</div>
915			</div>
916		</div>`;
917		}
918
919		function capitalize(text) {
920			return text
921				.toLowerCase()
922				.replace(/\b\w/g, char => char.toUpperCase());
923		}
924
925		const htmlTornei = tornei.map(t => {
926	return `
927		<a href="${t.competitionLink}" target="_blank">
928			<div class="tournament-item row align-items-stretch g-0">
929				<div class="col-md-4 bg-grey py-0 d-flex align-items-center mb-2">
930					<img src="Areas/${basePathSite}/images/widget/p6dn3evhyvjm9bgqy4w.svg" alt="tennis" class="px-3" style="height: 30px">
931					<div class="py-3">
932						<div class="d-flex text-fitp">
933							<p class="mb-0 pe-1 text-fitp">dal</p>
934							<p class="mb-0 text-fitp">${t.torneoDal}</p>
935						</div>
936						<div class="d-flex text-fitp">
937							<p class="pe-1 mb-0 text-fitp">al</p>
938							<p class="mb-0 text-fitp">${t.torneoAl}</p>
939						</div>
940					</div>
941				</div>
942				<div class="col-md-8 py-2 ps-3 mb-2 align-content-center ">
943					<div class="text-white fw-bold item-text">${capitalize(t.nomeTorneo)}</div>
944					<div class="text-white item-text">${capitalize(t.descrizioneTorneo)}</div>
945				</div>
946			</div>
947		</a>`;
948}).join('');
949
950
951		return `
952		
953				<div class="tournament-list-transparent rounded-4 rounded-bottom-0 border-bottom-0 border pb-4 pt-3 pe-3 h-100 ms-1 ms-xl-5" style="line-height: 1.3">
954				<div class="d-flex justify-content-between align-items-center pb-3">
955					<div class="text-white fw-bold ps-3">I MIEI TORNEI</div>
956					<button class="btn p-0 d-flex align-items-center gap-1">
957						<img src="Areas/${basePathSite}/images/widget/open-in-new-white.svg" alt="show more">
958						<a href="https://my.fitp.it/tornei" target="_blank" class="text-white fw-bold">VISUALIZZA TUTTI</a>
959					</button>
960				</div>
961				<div class="tornei-list">
962				${htmlTornei}
963				</div>
964			</div>
965		`;
966	//} catch (error) {
967	//	console.error("Errore nel caricamento tornei:", error);
968	//	return `<div class="text-white ps-3 pt-3">Errore nel caricamento tornei</div>`;
969	//}
970}
971
972window.checkAndInitCarousel = function () {
973	window.getHomeSkinBanner().then(() => {
974		if (jQueryV2("#skin-content").hasClass("skinned-content") && window.innerWidth > 767) {
975			var numItems = 3;
976			FITPCarousel.init('.myFitpCarousel-dev', {
977				items: numItems
978			});
979		}
980	});
981};
982
983async function caricaDatiAgenda() {
984	const token = localStorage.getItem("tokenAdb2c");
985	const guid = localStorage.getItem("Guid");
986	const member = localStorage.getItem("Member") || "1";
987	const teacher = localStorage.getItem("Teacher") || "0";
988	const udg = localStorage.getItem("Udg") || "1";
989	const acceptProfilation = localStorage.getItem("AcceptProfilation") || "1";
990	const hashKey = localStorage.getItem("Hashkey");
991	
992	const payloadBase = {
993		guid,
994		member,
995		teacher,
996		udg,
997		acceptProfilation,
998		hashKey
999	};
1000
1001	const headers = {
1002		'Authorization': `Bearer ${token}`,
1003		'Content-Type': 'application/json'
1004	};
1005
1006	const response = await fetch('https://dp-myfit-test-function-v2.azurewebsites.net/api/v2/agenda/list', {
1007		method: 'POST',
1008		headers,
1009		body: JSON.stringify({
1010			...payloadBase
1011		})
1012	});
1013
1014	//const json = await response.json();
1015	
1016	const json = {
1017    "eventi": [
1018        {
1019            "id": 2133,
1020            "data_inizio": "24/05/2025",
1021            "data_fine": "25/05/2025",
1022            "ora_inizio": "08:00",
1023            "oraFine": "10:00",
1024            "tipologia": "Evento",
1025            "link": null,
1026            "descrizione": null,
1027            "titolo": "Altro",
1028            "note": "prova",
1029            "competition_id": "csdbb",
1030            "id_torneo": null
1031        }
1032    ],
1033    "numero_eventi": 1
1034};
1035
1036	return json; // restituisce solo il flusso dati
1037
1038}
1039
1040function creaCalendarioConEventi(jsonEventi) {
1041    const eventiFiltrati = jsonEventi.eventi.filter(e => e.competition_id);
1042
1043    // Mappa eventi per data
1044    const eventiPerData = {};
1045    eventiFiltrati.forEach(e => {
1046      const [gg, mm, aa] = e.data_inizio.split('/');
1047      const isoDate = `${aa}-${mm.padStart(2, '0')}-${gg.padStart(2, '0')}`;
1048      if (!eventiPerData[isoDate]) eventiPerData[isoDate] = [];
1049      eventiPerData[isoDate].push(e);
1050    });
1051
1052    // Evidenzia date con eventi (popup solo visivo, ma click gestito sotto)
1053    const popups = {};
1054    Object.keys(eventiPerData).forEach(data => {
1055      const titoli = eventiPerData[data].map(e => e.titolo || `Torneo ${e.competition_id}`).join('<br>');
1056      popups[data] = {
1057        modifier: 'bg-red color-white',
1058        html: titoli
1059      };
1060    });
1061	
1062	
1063	/*// ✅ Filtra solo eventi con competition_id valorizzato
1064    const eventiFiltrati = jsonEventi.eventi.filter(e => e.competition_id);
1065
1066    // ✅ Mappa eventi per data_inizio
1067    const eventiPerData = {};
1068    eventiFiltrati.forEach(e => {
1069        const [gg, mm, aa] = e.data_inizio.split('/');
1070        const isoDate = `${aa}-${mm.padStart(2, '0')}-${gg.padStart(2, '0')}`;
1071        if (!eventiPerData[isoDate]) eventiPerData[isoDate] = [];
1072        eventiPerData[isoDate].push(e);
1073    });
1074
1075    // ✅ Evidenzia date con eventi (popup visivo)
1076    const popups = {};
1077    Object.keys(eventiPerData).forEach(data => {
1078        const titoli = eventiPerData[data].map(e => e.titolo || `Evento ${e.competition_id}`).join('<br>');
1079        popups[data] = {
1080            modifier: 'bg-red color-white',
1081            html: titoli
1082        };
1083    });*/
1084	
1085	
1086
1087
1088    const { Calendar } = window.VanillaCalendarPro;
1089
1090   const calendar = new Calendar('#calendar', {
1091    popups: popups,
1092    selectedDates: Object.keys(eventiPerData), // Evidenzia le date con eventi
1093    selectionDatesMode: 'multiple',
1094    settings: {
1095      selection: {
1096        day: true // Permetti di selezionare il giorno
1097      }
1098    },
1099    onClickDate(self) {
1100      // Ottieni la data selezionata
1101      const dataSelezionata = self.context.selectedDates[0]; // Ottieni la prima data selezionata
1102	  alert(dataSelezionata);
1103
1104      // Verifica se esistono eventi per la data selezionata
1105      const eventi = eventiPerData[dataSelezionata] || [];
1106      const contenitore = document.getElementById('dettagli-eventi');
1107
1108      if (eventi.length > 0) {
1109        const html = eventi.map(e => {
1110          const titolo = e.titolo || `Evento ${e.competition_id}`;
1111          const link = e.link || `https://tua-url.it/evento/${e.competition_id}`;
1112          return `<li><a href="${link}" target="_blank">${titolo}</a></li>`;
1113        }).join('');
1114        contenitore.innerHTML = `<ul>${html}</ul>`;
1115      } else {
1116        contenitore.innerHTML = 'Nessun evento per questa data.';
1117      }
1118    }
1119  });
1120
1121  calendar.init();
1122  
1123  
1124  
1125
1126  }
1127
1128window.initUtente = function (idDisciplinaFiltro = null) {
1129	hasLoyaltyProgram = utente_discipline.loyalty_program;
1130	nonTesserato = false;
1131
1132	// Reset valori
1133	id_disciplina = null;
1134	nome_disciplina = null;
1135	membershipCard = null;
1136	ago = null;
1137	nonAgo = null;
1138	socio = null;
1139	ex_ago = null;
1140	ex_nonAgo = null;
1141	ex_socio = null;
1142	id_persona_tpra = null;
1143
1144	const discipline = utente_discipline.discipline;
1145
1146	if (!Array.isArray(discipline) || discipline.length === 0) {
1147		nonTesserato = true;
1148
1149	} else {
1150
1151		let disciplina = null;
1152
1153		if (idDisciplinaFiltro !== null) {
1154			disciplina = discipline.find(d => d.id_disciplina === idDisciplinaFiltro);
1155		} else {
1156			disciplina = discipline[0];
1157		}
1158
1159		if (disciplina) {
1160			id_disciplina = disciplina.id_disciplina;
1161			nome_disciplina = disciplina.disciplina;
1162			membershipCard = disciplina.membershipCard;
1163			ago = disciplina.ago;
1164			nonAgo = disciplina.nonAgo;
1165			socio = disciplina.socio;
1166			ex_ago = disciplina.ex_ago;
1167			ex_nonAgo = disciplina.ex_nonAgo;
1168			ex_socio = disciplina.ex_socio;
1169			id_persona_tpra = disciplina.id_persona_tpra;
1170
1171			document.querySelectorAll('.sport-button').forEach(btn => btn.classList.remove('sportactive'));
1172			const buttonSportSelezionati = document.querySelectorAll(`button[data-disciplina="${id_disciplina}"]`);
1173			buttonSportSelezionati.forEach(btn => btn.classList.add('sportactive'));
1174			
1175		} else {
1176			nonTesserato = true;
1177		}
1178	}
1179
1180	//const carouselItems = document.querySelectorAll(".myFitpCarousel-dev .item");
1181	const carousel = document.querySelector(".myFitpCarousel-dev");
1182
1183	/*carouselItems[0].innerHTML = "<div class=\"row\"><div class=\"col-md-6\"></div><div class=\"col-md-6\"></div></div>";
1184	carouselItems[1].innerHTML = "";
1185	carouselItems[2].innerHTML = "";*/
1186
1187	var numItems = 3;
1188	
1189	jQueryV2("#div-widget-loading ").show();
1190
1191	if (nonTesserato) {
1192		
1193		jQueryV2("#div-widget-loading ").hide();
1194
1195		let templateCarousel = getHtmlFromTemplateDDDD("carousel-tpl-default");
1196		carousel.innerHTML = templateCarousel;
1197		const carouselItems = document.querySelectorAll(".myFitpCarousel-dev .item");
1198
1199		carouselItems[0].innerHTML = getHtmlFromTemplateRichiediTessera("widget-tpl-richiedi-tessera", getNomeDisciplina(idDisciplinaFiltro));
1200
1201		if (hasLoyaltyProgram == false) {
1202			carouselItems[1].innerHTML = getHtmlFromTemplateDDDD("widget-tpl-richiedi-loyalty");
1203		}
1204		else {
1205			generaHtmlMovimentiPuntiFedelta().then(html => {
1206				carouselItems[1].innerHTML = html;
1207			});
1208		}
1209		carouselItems[2].innerHTML = getHtmlFromTemplateDDDD("widget-tpl-richiedi-live-supertennis");
1210
1211		checkAndInitCarousel();
1212
1213	}
1214	else {
1215
1216		/*generaHtmlBoxTornei(id_disciplina).then(html => {
1217			const row = carouselItems[0].querySelector(".row");
1218			const columns = row.querySelectorAll(".col-md-6");
1219			const col1 = columns[0];
1220			const col2 = columns[1];			
1221			col1.innerHTML = html;
1222				
1223		});*/
1224
1225		/*generaBoxStatisticheIstogramma(membershipCard, 4332, 2019).then(html => {
1226			const row = carouselItems[0].querySelector(".row");
1227			const columns = row.querySelectorAll(".col-md-6");
1228			const col2 = columns[1];			
1229			col2.innerHTML = html;			
1230		});
1231		
1232		generaHtmlClassifica(nome_disciplina).then(html => {
1233			carouselItems[1].innerHTML = html;
1234		});
1235		
1236		generaHtmlMovimentiPuntiFedelta().then(html => {
1237			carouselItems[2].innerHTML = html;
1238		});*/
1239
1240		/*Promise.all([
1241			generaHtmlBoxTornei(id_disciplina),
1242			generaBoxStatisticheIstogramma(membershipCard, 4332, 2019),
1243			generaHtmlClassifica(nome_disciplina),
1244			generaHtmlMovimentiPuntiFedelta()
1245		]).then(([htmlTornei, htmlStats, htmlClassifica, htmlPunti]) => {
1246			carouselItems[0].innerHTML = htmlTornei;
1247			carouselItems[1].innerHTML = "<div class=\"row h-100\"><div class=\"col-md-6 pe-5\">" + htmlClassifica + "</div><div class=\"col-md-6 pb-4\">" + htmlStats + "</div></div>";
1248			carouselItems[2].innerHTML = htmlPunti;
1249
1250		});*/
1251
1252		if (socio == true || ex_ago == true || ex_nonAgo == true || ex_socio == true) {
1253			
1254			jQueryV2("#div-widget-loading ").hide();
1255
1256			let templateCarousel = getHtmlFromTemplateDDDD("carousel-tpl-default");
1257			carousel.innerHTML = templateCarousel;
1258
1259			const carouselItems = document.querySelectorAll(".myFitpCarousel-dev .item");
1260
1261			generaHtmlBoxTornei(id_disciplina).then(html => {
1262				carouselItems[0].innerHTML = html;
1263			});
1264
1265			carouselItems[1].innerHTML = getHtmlFromTemplateDDDD("widget-tpl-benefit-tesseramento");
1266
1267			if (hasLoyaltyProgram == false) {
1268				carouselItems[2].innerHTML = getHtmlFromTemplateDDDD("widget-tpl-richiedi-loyalty");
1269			}
1270			else {
1271				generaHtmlMovimentiPuntiFedelta().then(html => {
1272					carouselItems[2].innerHTML = html;
1273				});
1274			}
1275
1276			checkAndInitCarousel();
1277
1278		}
1279
1280		if (nonAgo == true) {
1281
1282			//const flussoStatistiche = [{"anno":2025,"partite_giocate_totale":5,"partite_vinte_totale":2,"partite_perse_totale":3,"set_giocati_totale":11,"set_vinti_totale":5,"set_persi_totale":6,"game_giocati_totale":83,"game_vinti_totale":43,"game_persi_totale":40},{"anno":2024,"partite_giocate_totale":2,"partite_vinte_totale":1,"partite_perse_totale":1,"set_giocati_totale":4,"set_vinti_totale":2,"set_persi_totale":2,"game_giocati_totale":29,"game_vinti_totale":16,"game_persi_totale":13},{"anno":2022,"partite_giocate_totale":1,"partite_vinte_totale":0,"partite_perse_totale":1,"set_giocati_totale":2,"set_vinti_totale":0,"set_persi_totale":2,"game_giocati_totale":14,"game_vinti_totale":0,"game_persi_totale":14}]; //await caricaDatiStatisticheIstogrammaNonAgo(id_persona_tpra, 4332);
1283
1284			//caricaDatiStatisticheIstogrammaNonAgo(174417, 172).then(flussoStatistiche => {
1285			Promise.all([
1286				caricaDatiStatisticheIstogrammaNonAgo(id_persona_tpra, id_disciplina),
1287				caricaDatiClassifica(nome_disciplina)
1288			]).then(([flussoStatistiche, dati_classifica]) => {
1289
1290				if (flussoStatistiche !== null) {
1291
1292					let templateCarousel = getHtmlFromTemplateDDDD("carousel-tpl-statistiche");
1293					carousel.innerHTML = templateCarousel;
1294
1295					const carouselItems = document.querySelectorAll(".myFitpCarousel-dev .item");
1296
1297					/*let dati_classifica = {
1298						"discipline": "tennis",
1299						"category": "4° Categoria",
1300						"rank": "4.NC",
1301						"rankProjection": "4.NC",
1302						"coefficient": "0",
1303						"maxRank": "4.NC",
1304						"maxRankSeason": "-",
1305						"maxRankCoefficient": "0"
1306					};*/
1307
1308					let htmlClassifica = generaHtmlClassifica(dati_classifica, nome_disciplina);
1309					//let htmlStats = generaBoxStatisticheIstogrammaNonAgo(flussoStatistiche, id_persona_tpra, 172);
1310
1311					const { containerId, html, updateChart } = generaBoxStatisticheIstogrammaNonAgo(flussoStatistiche, id_persona_tpra, id_disciplina);
1312					let htmlStats = html;
1313
1314					if (hasLoyaltyProgram == false) {
1315
1316						Promise.all([
1317							generaHtmlBoxTornei(id_disciplina)
1318						]).then(([htmlTornei]) => {
1319							
1320							jQueryV2("#div-widget-loading ").hide();
1321							
1322							carouselItems[0].innerHTML = htmlTornei;
1323
1324							const row = carouselItems[1].querySelector(".row");
1325							const columns = row.querySelectorAll(".col-md-6");
1326							const col1 = columns[0];
1327							const col2 = columns[1];
1328							col1.innerHTML = htmlClassifica;
1329							col2.innerHTML = htmlStats;
1330
1331							carouselItems[2].innerHTML = getHtmlFromTemplateDDDD("widget-tpl-richiedi-loyalty");
1332
1333							// solo dopo che l'HTML è stato inserito nel DOM
1334							const select = document.querySelector(`#${containerId} select`);
1335							const chart = document.querySelector(`#${containerId} .bar-chart`);
1336
1337							if (select && chart) {
1338								select.addEventListener('change', (e) => {
1339									const nuovoAnno = e.target.value;
1340									updateChart(nuovoAnno, chart);
1341								});
1342							}
1343
1344							checkAndInitCarousel();
1345
1346						});
1347
1348					}
1349					else {
1350
1351						Promise.all([
1352							generaHtmlBoxTornei(id_disciplina),
1353							generaHtmlMovimentiPuntiFedelta()
1354						]).then(([htmlTornei, htmlPunti]) => {
1355							
1356							jQueryV2("#div-widget-loading ").hide();
1357							
1358							carouselItems[0].innerHTML = htmlTornei;
1359
1360							const row = carouselItems[1].querySelector(".row");
1361							const columns = row.querySelectorAll(".col-md-6");
1362							const col1 = columns[0];
1363							const col2 = columns[1];
1364							col1.innerHTML = htmlClassifica;
1365							col2.innerHTML = htmlStats;
1366
1367							carouselItems[2].innerHTML = htmlPunti;
1368
1369							// solo dopo che l'HTML è stato inserito nel DOM
1370							const select = document.querySelector(`#${containerId} select`);
1371							const chart = document.querySelector(`#${containerId} .bar-chart`);
1372
1373							if (select && chart) {
1374								select.addEventListener('change', (e) => {
1375									const nuovoAnno = e.target.value;
1376									updateChart(nuovoAnno, chart);
1377								});
1378							}
1379
1380							checkAndInitCarousel();
1381
1382						});
1383
1384					}
1385
1386				} else {
1387
1388					let templateCarousel = getHtmlFromTemplateDDDD("carousel-tpl-default");
1389					carousel.innerHTML = templateCarousel;
1390
1391					const carouselItems = document.querySelectorAll(".myFitpCarousel-dev .item");
1392
1393					if (hasLoyaltyProgram == false) {						
1394
1395						Promise.all([
1396							generaHtmlBoxTornei(id_disciplina)
1397						]).then(([htmlTornei]) => {
1398							
1399							jQueryV2("#div-widget-loading ").hide();
1400							
1401							carouselItems[0].innerHTML = htmlTornei;
1402							carouselItems[1].innerHTML = getHtmlFromTemplateDDDD("widget-tpl-benefit-tesseramento");
1403							carouselItems[2].innerHTML = getHtmlFromTemplateDDDD("widget-tpl-richiedi-loyalty");
1404
1405							checkAndInitCarousel();
1406
1407						});
1408
1409					}
1410					else {
1411						
1412						Promise.all([
1413							generaHtmlBoxTornei(id_disciplina),
1414							generaHtmlMovimentiPuntiFedelta()
1415						]).then(([htmlTornei, htmlPunti]) => {
1416							
1417							jQueryV2("#div-widget-loading ").hide();
1418							
1419							carouselItems[0].innerHTML = htmlTornei;
1420							carouselItems[1].innerHTML = getHtmlFromTemplateDDDD("widget-tpl-benefit-tesseramento");
1421							carouselItems[2].innerHTML = htmlPunti;
1422
1423							checkAndInitCarousel();
1424
1425						});
1426
1427					}
1428
1429				}
1430
1431			});
1432
1433		}
1434
1435		if (ago == true) {
1436
1437			let templateCarousel = getHtmlFromTemplateDDDD("carousel-tpl-statistiche");
1438			
1439
1440
1441			//if (window.innerWidth < 768) {
1442			if (window.innerWidth < 1200) {
1443				templateCarousel = getHtmlFromTemplateDDDD("carousel-tpl-statistiche-mobile");
1444				numItems = 4;
1445			}		
1446
1447			carousel.innerHTML = templateCarousel;
1448
1449			const carouselItems = document.querySelectorAll(".myFitpCarousel-dev .item");
1450
1451				Promise.all([
1452					generaHtmlBoxTornei(id_disciplina),
1453					generaBoxStatisticheIstogramma(membershipCard, id_disciplina, null),
1454					caricaDatiClassifica(nome_disciplina),
1455					generaHtmlBoxTorneiIscrizioni(id_disciplina, null)
1456					//caricaDatiAgenda()
1457				//]).then(([htmlTornei, htmlStats, dati_classifica, dati_agenda]) => {
1458				]).then(([htmlTornei, htmlStats, dati_classifica, htmlTorneiIscrizioni]) => {
1459					
1460					jQueryV2("#div-widget-loading ").hide();					
1461					
1462					carouselItems[0].innerHTML = htmlTornei;
1463
1464					if (numItems == 3) {
1465
1466						const row = carouselItems[1].querySelector(".row");
1467						const columns = row.querySelectorAll(".col-md-6");
1468						const col1 = columns[0];
1469						const col2 = columns[1];
1470
1471						col1.innerHTML = generaHtmlClassifica(dati_classifica, nome_disciplina);
1472
1473						col2.innerHTML = "<div id=\"box-stats-ago\" class=\"h-100\"=>" + htmlStats + "</div>";
1474
1475						//alert(membershipCard);
1476
1477						attachSelectHandler(membershipCard);
1478						
1479						
1480						
1481						
1482						
1483						
1484
1485						carouselItems[2].innerHTML = htmlTorneiIscrizioni;
1486						
1487						/*carouselItems[2].innerHTML = getHtmlFromTemplateDDDD("widget-tpl-calendar");
1488						
1489						  dati_agenda = {
1490    eventi: [
1491      { id: 14, data_inizio: "12/05/2025", competition_id: "avc", titolo: "Torneo AVC" },
1492      { id: 35, data_inizio: "22/05/2025", competition_id: "cccccc", titolo: "Torneo CCCCCC" },
1493      { id: 36, data_inizio: "22/05/2025", competition_id: "dddddd", titolo: "Torneo DDDDDD" }
1494    ]
1495  };
1496  
1497  dati_agenda =
1498  {
1499    "eventi": [
1500        {
1501            "id": 2133,
1502            "data_inizio": "24/05/2025",
1503            "data_fine": "25/05/2025",
1504            "ora_inizio": "08:00",
1505            "oraFine": "10:00",
1506            "tipologia": "Evento",
1507            "link": null,
1508            "descrizione": null,
1509            "titolo": "Altro 1",
1510            "note": "prova",
1511            "competition_id": "csdbb",
1512            "id_torneo": null
1513        },
1514		{
1515            "id": 2134,
1516            "data_inizio": "29/05/2025",
1517            "data_fine": "29/05/2025",
1518            "ora_inizio": "08:00",
1519            "oraFine": "10:00",
1520            "tipologia": "Evento",
1521            "link": null,
1522            "descrizione": null,
1523            "titolo": "Altro 2",
1524            "note": "prova",
1525            "competition_id": "csdbb",
1526            "id_torneo": null
1527        }
1528    ],
1529    "numero_eventi": 1
1530};
1531
1532						
1533						creaCalendarioConEventi(dati_agenda);
1534						
1535						
1536						/* const options = {
1537						  locale: 'it', // Tedesco (Austria)
1538						  popups: {
1539							'2025-05-28': {
1540							  modifier: 'bg-red color-pink',
1541							   html: '2025 - CAM - Coppa d\'Inverno Misto - lim 3.5 - 2025/2026 ',
1542							},
1543							'2025-05-16': {
1544							  modifier: 'bg-red color-pink',
1545							   html: '2025 - CAM - Coppa d\'Inverno Misto - lim 3.5 - 2025/2026 ',
1546							},
1547							'2025-05-12': {
1548							  modifier: 'bg-red color-pink',
1549							   html: '2025 - CAM - Coppa d\'Inverno Misto - lim 3.5 - 2025/2026 ',
1550							},
1551							'2025-05-12': {
1552							  modifier: 'bg-red color-pink',
1553							   html: 'fdfdfdfd fd f df sdfds'
1554							},							
1555							'2025-05-25': {
1556							  modifier: 'bg-red color-pink',
1557							   html: '2025 - CAM - Coppa d\'Inverno Misto - lim 3.5 - 2025/2026 ',
1558							},
1559							'2025-05-29': {
1560							  modifier: 'bg-red color-pink',
1561							   html: '2025 - CAM - Coppa d\'Inverno Misto - lim 3.5 - 2025/2026 ',
1562							},
1563						  },
1564						};
1565						window.fitpWidgetInitialized = true;
1566						const { Calendar } = window.VanillaCalendarPro;
1567					 
1568						const calendar1 = new Calendar('#calendar', options);
1569						calendar1.init();*/
1570						
1571						
1572
1573					}
1574
1575					if (numItems == 4) {
1576
1577						carouselItems[1].innerHTML = generaHtmlClassifica(dati_classifica, nome_disciplina);
1578
1579						carouselItems[2].innerHTML = htmlStats;
1580
1581						attachSelectHandler(membershipCard);
1582
1583						carouselItems[3].innerHTML = htmlTorneiIscrizioni; //getHtmlFromTemplateDDDD("widget-tpl-richiedi-loyalty");
1584
1585					}
1586
1587					checkAndInitCarousel();
1588
1589				});
1590
1591			
1592
1593		}
1594
1595
1596	}
1597	
1598	jQueryV2("#div-widget").show();
1599
1600	if ((idDisciplinaFiltro !== null && jQueryV2("#skin-content").hasClass("skinned-content")) || window.innerWidth < 1600) {
1601
1602		FITPCarousel.init('.myFitpCarousel-dev', {
1603			items: numItems
1604		});
1605
1606	}
1607
1608}
1609
1610const breakpoints = [
1611  { name: 'mobile', max: 767 },
1612  { name: 'tablet', min: 768, max: 1023 },
1613  { name: 'desktop', min: 1024, max: 1199 },
1614  { name: 'desktop-1', min: 1200, max: 1599 },
1615  { name: 'desktop-2', min: 1600 }
1616];
1617
1618let currentBreakpoint = null;
1619
1620function getBreakpointName(width) {
1621  for (const bp of breakpoints) {
1622    if (
1623      (bp.min === undefined || width >= bp.min) &&
1624      (bp.max === undefined || width <= bp.max)
1625    ) {
1626      return bp.name;
1627    }
1628  }
1629  return null;
1630}
1631
1632function handleResize() {
1633  const width = window.innerWidth;
1634  const newBreakpoint = getBreakpointName(width);
1635
1636  if (newBreakpoint !== currentBreakpoint) {
1637    currentBreakpoint = newBreakpoint;
1638
1639    // Esegui qui le azioni una tantum per ogni breakpoint
1640    switch (currentBreakpoint) {
1641      case 'mobile':
1642        console.log('Entrato in mobile');
1643		initUtente();
1644        // azione per mobile
1645        break;
1646      case 'tablet':
1647        console.log('Entrato in tablet');
1648		initUtente();
1649        // azione per tablet
1650        break;
1651      case 'desktop':
1652        console.log('Entrato in desktop');
1653		initUtente();
1654        // azione per desktop
1655        break;
1656      case 'desktop-1':
1657        console.log('Entrato in desktop');
1658		initUtente();
1659        // azione per desktop
1660        break;		
1661    }
1662  }
1663}
1664
1665// inizializza
1666//handleResize();
1667
1668// ascolta i cambi di dimensione
1669window.addEventListener('resize', handleResize);
1670
1671window.initHomeWidgetUtente = function (dati_utente = null) {
1672	
1673	const elemento = document.getElementById("div-widget");
1674	if (dati_utente == null) {		
1675		elemento.style.display = "none";
1676	}
1677	else {
1678		utente_discipline = dati_utente;
1679		elemento.style.display = "block";
1680		initUtente();
1681	}
1682	
1683}
1684
1685document.addEventListener("DOMContentLoaded", () => {
1686	//initUtente(); // Caricamento iniziale
1687	//initHomeWidgetUtente();
1688
1689	document.querySelectorAll('.div-widget-dev [data-disciplina]').forEach(btn => {
1690		btn.addEventListener("click", () => {
1691
1692			document.querySelectorAll('.sport-button').forEach(btn => btn.classList.remove('sportactive'));
1693			// Aggiunge 'active' solo a quello cliccato
1694			btn.classList.add('sportactive');
1695
1696
1697			const idDisciplina = parseInt(btn.getAttribute("data-disciplina"));
1698			initUtente(idDisciplina);
1699		});
1700	});
1701
1702	jQueryV2('#myFitpCarousel').on('show.bs.collapse', function () {
1703		jQueryV2('[data-target="#myFitpCarousel"]').removeClass('collapsed');
1704		jQueryV2('#div-widget .sport-button').show();
1705	});
1706
1707	jQueryV2('#myFitpCarousel').on('hide.bs.collapse', function () {
1708		jQueryV2('[data-target="#myFitpCarousel"]').addClass('collapsed');
1709		jQueryV2('#div-widget .sport-button').hide();
1710	});
1711
1712	/*document.querySelectorAll('.sport-button').forEach(button => {
1713		button.addEventListener('click', () => {
1714			// Rimuove 'active' da tutti i bottoni
1715			document.querySelectorAll('.sport-button').forEach(btn => btn.classList.remove('sportactive'));
1716			// Aggiunge 'active' solo a quello cliccato
1717			button.classList.add('sportactive');
1718			console.log('Disciplina selezionata:', disciplina);
1719		});
1720	});*/
1721
1722});

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.