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.