PageSourceSearch

https://nanoda.com/wp-content/plugins/ngtn_central/assets/ngtn_central.js?v=1.1

js nanoda.com collected 2026-09-24 22:43:49 UTC 21,020 bytes, 538 lines download raw bytes

1let me = {};
2let keycloak = null;
3const formatter = new Intl.NumberFormat(undefined, {
4    minimumFractionDigits: 0,
5    maximumFractionDigits: 3,
6});
7
8document.addEventListener('DOMContentLoaded', async function () {
9    keycloak = new Keycloak({
10        url: ngtnCentralConfig.idpServer + '/auth',
11        realm: 'ngtn',
12        clientId: 'ngtn-app',
13    });
14
15    let countdownActive = false; // Controlla se il countdown è attivo
16    let wasRunning = false; // Memorizza se il timer stava andando prima della perdita di focus
17    let progress = 0;
18    let lastId = null; // Memorizza l'ID dell'ultimo timer
19    const totalDuration = 30000; // 30 secondi
20    const activeCounters = new Set(); // Memorizza gli ID già conteggiati
21    let lastTimestamp = null; // Per il calcolo del tempo con requestAnimationFrame
22    let lastUpdate = 0; // Ultimo aggiornamento del display
23
24    const ngtnDiv = document.getElementById('ngtn-central-div');
25
26    // Recupera i token salvati dal localStorage
27    const savedToken = localStorage.getItem('keycloak_token');
28    const savedRefreshToken = localStorage.getItem('keycloak_refresh_token');
29    if (savedToken && savedRefreshToken) {
30        keycloak.token = savedToken;
31        keycloak.refreshToken = savedRefreshToken;
32    }
33
34    try {
35        const authenticated = await keycloak.init({
36            onLoad: 'check-sso',
37            silentCheckSsoRedirectUri: `${window.location.origin}/silent-check-sso.html`,
38        });
39
40        if (authenticated) {
41            console.log("NGTN Central: utente autenticato")
42            // Salva i token nel localStorage
43            localStorage.setItem('keycloak_token', keycloak.token);
44            localStorage.setItem('keycloak_refresh_token', keycloak.refreshToken);
45            await loadMe();
46            console.log("NGTN Central: utente " + me.email)
47        }
48
49        checkView();
50
51        // Imposta il contenuto iniziale del div
52        setContent(ngtnDiv, false, keycloak.authenticated);
53
54        // Aggiorna periodicamente il token
55        setInterval(async () => {
56            try {
57                if (keycloak.token) {
58                    await keycloak.updateToken(30); // Aggiorna il token se necessario
59                    localStorage.setItem('keycloak_token', keycloak.token);
60                    localStorage.setItem('keycloak_refresh_token', keycloak.refreshToken);
61                }
62            } catch (error) {
63                console.error('Errore durante il refresh del token:', error);
64                keycloak.logout(); // Effettua il logout se il refresh fallisce
65            }
66        }, 600000); // Ogni 10 minuti (600000 millisecondi)
67
68    } catch (error) {
69        console.error('Errore durante l\'inizializzazione di Keycloak:', error);
70        ngtnDiv.innerHTML = '<p>Errore durante la verifica del login</p>';
71    }
72
73    // Animazioni per l'espansione e la contrazione del div
74    let timeout;
75    ngtnDiv.addEventListener('mouseenter', () => {
76        clearTimeout(timeout);
77        ngtnDiv.style.transition = 'width 500ms, height 500ms';
78        ngtnDiv.style.width = '280px';
79        ngtnDiv.style.height = '60px';
80        ngtnDiv.addEventListener('transitionend', function handleExpand() {
81            ngtnDiv.removeEventListener('transitionend', handleExpand);
82            setContent(ngtnDiv, true, keycloak.authenticated);
83        });
84    });
85
86    ngtnDiv.addEventListener('mouseleave', () => {
87        timeout = setTimeout(() => {
88            ngtnDiv.style.transition = 'width 500ms, height 500ms';
89            ngtnDiv.style.width = '60px';
90            ngtnDiv.style.height = '60px';
91            setContent(ngtnDiv, false, keycloak.authenticated);
92            ngtnDiv.addEventListener('transitionend', function handleShrink() {
93                ngtnDiv.removeEventListener('transitionend', handleShrink);
94                setContent(ngtnDiv, false, keycloak.authenticated);
95            });
96        }, 1500);
97    });
98
99    async function checkView() {
100        const url = new URL(window.location.href);
101        url.hash = ""; // Rimuove i frammenti (#)
102        url.search = ""; // Rimuove i parametri di query
103        const currentUrl = url.href.startsWith("https://www.") ? url.href : url.href.replace("https://", "https://www.");
104        console.log("NGTN Central: current url " + currentUrl)
105        console.dir("NGTN Central: me.email " + me.email)
106        const postData = {
107            url: currentUrl,
108            ip: ngtnCentralConfig.clientIp,
109            email: keycloak.authenticated ? me.email : "",
110            deviceType: ngtnCentralConfig.deviceType,
111            isBot: ngtnCentralConfig.isBot,
112            os: ngtnCentralConfig.os,
113            osVersion: ngtnCentralConfig.osVersion,
114            browser: ngtnCentralConfig.browser,
115            browserVersion: ngtnCentralConfig.browserVersion,
116        };
117        console.log("NGTN Central: postData")
118
119        try {
120            let currentSite = window.location.origin;
121            if (!currentSite.startsWith("https://")) {
122                currentSite = "https://" + currentSite;
123            }
124            if (!currentSite.includes("www.")) {
125                currentSite = currentSite.replace("https://", "https://www.");
126            }
127            const response = await fetch(ngtnCentralConfig.endPoint + "/api/v1/public/views/", {
128                method: "POST",
129                headers: {
130                    "Content-Type": "application/json",
131                    "NGTN-Key": ngtnCentralConfig.apiKey,
132                    "NGTN-Site": currentSite,
133                },
134                body: JSON.stringify(postData),
135            });
136
137            if (!response.ok) throw new Error("Errore nel POST /views/");
138
139            const data = await response.json();
140            const payload = data.payload[0];
141            if (payload.allowCounter) {
142                startCounter(payload.id);
143            }
144        } catch (error) {
145            console.error("Errore durante il POST /view:", error);
146        }
147    }
148
149    function startCounter(id) {
150        if (countdownActive || activeCounters.has(id)) return;
151        countdownActive = true;
152        wasRunning = true;
153        lastId = id;
154        activeCounters.add(id);
155
156        console.log(`⏳ Timer avviato per ID: ${id}`);
157
158        const imgDiv = document.getElementById("ngtn-img-div");
159        imgDiv.style.position = "relative";
160
161        const progressBar = document.createElement("div");
162        progressBar.style.position = "absolute";
163        progressBar.style.top = "0";
164        progressBar.style.left = "0";
165        progressBar.style.height = "2px";
166        progressBar.style.width = "0%";
167        progressBar.style.backgroundColor = "blue";
168        progressBar.style.zIndex = "1000";
169        imgDiv.appendChild(progressBar);
170
171        const timerDiv = document.createElement("div");
172        timerDiv.className = "timer-text";
173        imgDiv.appendChild(timerDiv);
174
175        lastTimestamp = performance.now(); // Inizializza il tempo di partenza
176
177        function updateTimer(timestamp) {
178            if (!countdownActive) return;
179
180            let elapsed = timestamp - lastTimestamp;
181            lastTimestamp = timestamp;
182            progress += elapsed;
183
184            // Aggiornamento solo ogni 5 secondi
185            if (progress - lastUpdate >= 5000 || progress >= totalDuration) {
186                lastUpdate = progress;
187                const remainingTime = Math.max(totalDuration - progress, 0);
188                const seconds = Math.floor(remainingTime / 1000);
189                console.log(`⏰ Timer aggiornato: ${seconds} secondi rimanenti`);
190
191                timerDiv.textContent = `00:${seconds.toString().padStart(2, '0')}`;
192            }
193
194            progressBar.style.width = `${(progress / totalDuration) * 100}%`;
195
196            if (progress < totalDuration) {
197                requestAnimationFrame(updateTimer);
198            } else {
199                console.log("✅ Timer completato!");
200                progressBar.remove();
201                timerDiv.remove();
202                confirmView(id);
203                countdownActive = false;
204                wasRunning = false;
205                lastId = null;
206                activeCounters.delete(id);
207            }
208        }
209
210        requestAnimationFrame(updateTimer);
211    }
212
213// ✅ Listener per il cambio di visibilità della pagina
214    document.addEventListener("visibilitychange", function () {
215        if (document.hidden) {
216            console.log("⏸️ Scheda non visibile, countdown in pausa.");
217            if (countdownActive) {
218                wasRunning = true;
219                countdownActive = false; // Pausa il timer
220            }
221        } else {
222            if (wasRunning && lastId !== null) {
223                console.log(`▶️ Scheda visibile, countdown riprende con ID: ${lastId}`);
224                countdownActive = true; // Riattiva il timer
225                requestAnimationFrame(startCounter.bind(null, lastId));
226            }
227        }
228    });
229
230// ✅ Listener per il cambio di pagina o caricamento
231    window.addEventListener("beforeunload", function () {
232        console.log("🚨 La pagina sta per essere ricaricata!");
233        console.log("🔄 Navigazione: Timer rimosso.");
234        countdownActive = false;
235        wasRunning = false;
236        lastId = null;
237    });
238
239// ✅ Memorizza il progresso del timer nella sessione del browser
240    window.addEventListener("load", function () {
241        const savedProgress = sessionStorage.getItem("timerProgress");
242        if (savedProgress) {
243            progress = parseInt(savedProgress, 10);
244            if (progress < totalDuration && lastId !== null) {
245                console.log(`♻️ Riprendo il timer dopo la navigazione: ${progress / 1000} secondi`);
246                startCounter(lastId);
247            }
248        }
249    });
250
251// ✅ Salva il progresso ogni 5 secondi
252    setInterval(() => {
253        if (countdownActive) {
254            sessionStorage.setItem("timerProgress", progress);
255        }
256    }, 5000);
257
258    async function confirmView(id) {
259        const currentUrl = window.location.href.startsWith("https://www.") ? window.location.href : window.location.href.replace("https://", "https://www.");
260        const postData = {
261            id: id,
262            url: currentUrl,
263            email: me.email,
264        };
265
266        try {
267            let currentSite = window.location.origin;
268            if (!currentSite.startsWith("https://")) {
269                currentSite = "https://" + currentSite;
270            }
271            if (!currentSite.includes("www.")) {
272                currentSite = currentSite.replace("https://", "https://www.");
273            }
274            const response = await fetch(ngtnCentralConfig.endPoint + "/api/v1/public/views/confirm", {
275                method: "POST",
276                headers: {
277                    "Content-Type": "application/json",
278                    "NGTN-Key": ngtnCentralConfig.apiKey,
279                    "NGTN-Site": currentSite,
280                },
281                body: JSON.stringify(postData),
282            });
283
284            if (!response.ok) throw new Error("Errore nel POST /views/confirm");
285
286            const data = await response.json();
287            const payload = data.payload[0];
288            if (payload.pointsAssigned > 0) {
289                showPointsAnimation(payload.pointsAssigned);
290            }
291        } catch (error) {
292            console.error("Errore durante il POST /views/confirm:", error);
293        }
294    }
295
296    function showPointsAnimation(pointsAssigned) {
297        const pointsDiv = document.createElement("div");
298        pointsDiv.textContent = `+${pointsAssigned}`;
299        pointsDiv.className = "font-with-border";
300
301        const imgDiv = document.getElementById("ngtn-img-div");
302        imgDiv.appendChild(pointsDiv);
303
304        pointsDiv.animate(
305            [
306                { fontSize: "10px" },
307                { fontSize: "56px" },
308            ],
309            { duration: 800, fill: "forwards" }
310        );
311
312        setTimeout(() => {
313            pointsDiv.style.animation = "blink 1s step-start infinite";
314            setTimeout(() => {
315                pointsDiv.remove();
316            }, 3500);
317        }, 800);
318    }
319
320    function getHeaders() {
321        let currentSite = window.location.origin;
322        if (!currentSite.startsWith("https://")) {
323            currentSite = "https://" + currentSite;
324        }
325        if (!currentSite.includes("www.")) {
326            currentSite = currentSite.replace("https://", "https://www.");
327        }
328
329        return {
330            "NGTN-Key": ngtnCentralConfig.apiKey,
331            "NGTN-Site": currentSite,
332            "Authorization": "Bearer " + keycloak.token
333        }
334    }
335
336    async function loadLevelImage() {
337        const levelImage = document.getElementById('ngtn-user-level');
338
339        const response = await fetch(me.levelUrl, {
340            method: 'GET',
341            headers: getHeaders()
342        });
343
344        if (!response.ok) {
345            console.error('Failed to fetch image');
346            return;
347        }
348
349        const blob = await response.blob();
350        const imageUrl = URL.createObjectURL(blob);
351
352        levelImage.src = imageUrl;
353        levelImage.style.display = "block";
354    }
355
356    async function loadAvatar() {
357        const avatar = document.getElementById('ngtn-user-avatar');
358
359        const response = await fetch(me.avatarUrl, {
360            method: 'GET',
361            headers: getHeaders()
362        });
363
364        if (!response.ok) {
365            console.error('Failed to fetch image');
366            return;
367        }
368
369        const blob = await response.blob();
370        const imageUrl = URL.createObjectURL(blob);
371
372        avatar.src = imageUrl;
373        avatar.style.display = "block";
374    }
375
376    function getContentExpandedLogged() {
377        const position = ngtnCentralConfig.position == 'up_dx' ? 'top: 106px; right: 6px;' : 'top: 106px; left: 38px;';
378        return `<div style="display: flex; align-items: flex-start;padding:4px!important">
379                            <div id="ngtn-img-div" style="width:52px; margin:2px; text-align:center">
380                                <img id="ngtn-user-avatar" alt="User Avatar" style="max-width: 52px; height: 52px;display:none">
381                            </div>
382                            <div style="margin-left: 10px;text-align:left">
383                                <p style="font-size: 12px; margin: 0;line-height:1.1em!important">Ciao, ${me.nickName != "" ? me.nickName : me.firstName}</p>
384                                ` + (me.profileComplete ? "" : `<p style="font-size: 10px; margin: 0; color:red; line-height:14px!important" class="blink-image">Il tuo profilo NON è completo, ottieni punti completandolo!</p>`) + `
385                            </div>
386                            <div style="width:68px; margin:0px!important; text-align:center;font-size: 10px!important;line-height:12px!important ">
387                                <img id="ngtn-user-level" alt="User Level" style="max-width: 46px; height: 46px;display:none">
388                            </div>
389                        </div>
390<div id="div-ngtn-buttons" style="text-align:center;z-index:10000; width: 280px; height: 60px; position: fixed;${position};display:none">
391    <button id="ngtn-open-central" class="ngtn-button-blue">APRI NGTN-Central</button>
392    <button id="ngtn-logout-button" class="ngtn-button-red">LOGOUT</button>
393</div>`;
394    }
395
396    function getContentExpandedNotLogged() {
397        return `<div style="display: flex; align-items: flex-start;">
398                            <div id="ngtn-img-div" style="width:52px; margin:5px">
399                                <img src="${ngtnCentralConfig.pluginUrl}assets/images/not_logged.webp" class="blink-image"  alt="Not Logged Image" style="max-width: 52px; max-height: 52px;">
400                            </div>
401                            <div style="margin-left: 10px; padding: 4px;line-height:16px!important">
402                                <button id="ngtn-login-button" class ="ngtn-button-blue">ACCEDI</button>
403                                <div style="text-align:left!important">
404                                    <span style="font-size:10px">Resitrati per iniziare a guadagnare punti leggendo gli articoli sui nostri magazine</span>
405                                </div>
406                            </div>
407                        </div>`;
408    }
409
410    function getContentShrinkedLogged() {
411        return `<div id="ngtn-img-div" style="display: flex; align-items: center; justify-content: center; width: 100%; height: 100%;">
412                    <img id="ngtn-user-avatar" alt="User Avatar" style="max-width: 52px; max-height: 52px;display:none">
413                </div>`;
414    }
415
416    function getContentShrinkedNotLogged() {
417        return `<div style="width:52px; margin:5px">
418                    <img src="${ngtnCentralConfig.pluginUrl}assets/images/not_logged.webp" class="blink-image"  alt="Not Logged Image" style="max-width: 52px; height: auto;">
419                </div>`;
420    }
421
422    async function loadMe() {
423        // Prepara i dati della richiesta
424        const url = ngtnCentralConfig.endPoint + "/api/v1/private/me/";
425
426        // Effettua una chiamata GET al server
427        await fetch(url, {
428            method: "GET",
429            headers: getHeaders()
430        })
431            .then(response => {
432                if (!response.ok) {
433                    throw new Error("Errore nel reperimento delle info");
434                }
435                return response.json();
436            })
437            .then(data => {
438                me = data.payload[0]
439            })
440            .catch(error => {
441                console.error(error);
442                ngtnDiv.innerHTML = `
443                    <div style="color: red; text-align: center;">
444                        Errore nel reperimento delle info
445                    </div>`;
446            });
447    }
448
449    async function setContent(ngtnDiv, expanded, isLogged) {
450        ngtnDiv.innerHTML = '<img src="' + ngtnCentralConfig.pluginUrl + '/assets/images/loader.gif" style="width:60px;height:60px">';
451        if (isLogged) {
452            if (expanded) {
453                ngtnDiv.innerHTML = getContentExpandedLogged();
454                await loadLevelImage();
455
456                document.getElementById('div-ngtn-buttons').style.display = 'block'
457
458                const loginButton = document.getElementById('ngtn-logout-button');
459                loginButton.addEventListener('click', () => keycloakLogout());
460
461                const centralButton = document.getElementById('ngtn-open-central');
462                centralButton.addEventListener('click', () => openCentral());
463            } else {
464                ngtnDiv.innerHTML = getContentShrinkedLogged();
465            }
466            await loadAvatar();
467        } else {
468            if (expanded) {
469                ngtnDiv.innerHTML = getContentExpandedNotLogged();
470
471                const loginButton = document.getElementById('ngtn-login-button');
472                loginButton.addEventListener('click', () => openKeycloakLogin());
473            } else {
474                ngtnDiv.innerHTML = getContentShrinkedNotLogged();
475            }
476        }
477    }
478
479    function keycloakLogout() {
480        keycloak.logout();
481    }
482
483    function openKeycloakLogin() {
484        keycloak.login();
485    }
486});
487
488function openCentral() {
489    window.open("https://central.ngtn.eu", "_blank");
490}
491
492/*function openCentral() {
493    const popup = document.getElementById('ngtn-central-popup');
494
495    // Imposta il contenuto del div
496    popup.innerHTML = `
497        <div id="ngtn-central-content" style="position: relative; padding: 20px;">
498            <button id="ngtn-close-button" style="
499                position: absolute;
500                top: -8px;
501                right: 0px;
502                width: 30px;
503                height: 30px;
504                border-radius: 50%;
505                border: none;
506                background-color: white;
507                color: red;
508                font-size: 16px;
509                cursor: pointer;
510                box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
511            ">X</button>
512            <p style="color: white; font-size: 18px; text-align: center;">Benvenuto in NGTN Central!</p>
513        </div>
514    `;
515
516    // Stile del div principale
517    Object.assign(popup.style, {
518        display: 'block',
519        position: 'fixed',
520        top: '50%',
521        left: '50%',
522        transform: 'translate(-50%, -50%)',
523        width: '950px',
524        height: '700px',
525        backgroundColor: 'rgba(27, 120, 202, 0.8)', // Colore di sfondo con opacità
526        borderRadius: '50% 40% 60% 30% / 30% 60% 40% 50%', // Forma irregolare
527        boxShadow: '0 4px 8px rgba(0, 0, 0, 0.2)',
528        zIndex: 10000,
529    });
530
531    // Aggiungi evento per chiudere il popup
532    document.getElementById('ngtn-close-button').addEventListener('click', closeCentral);
533}*/
534
535/*function closeCentral() {
536    const popup = document.getElementById('ngtn-central-popup');
537    popup.style.display = 'none';
538}*/

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.