/* Senobar Studio Analytics */ /* Script: Time Spend */ (function () { // --- Get token from script src --- var currentScript = document.currentScript; var token = null; if (currentScript && currentScript.src) { try { var url = new URL(currentScript.src); token = url.searchParams.get("token"); } catch (e) { var m = currentScript.src.match(/[?&]token=([^&#]+)/); if (m) token = decodeURIComponent(m[1]); } } if (!token) return; const API_URL = "https://analytics-api.senobarstudio.ir/api/Metric/traffic/" + token; // --- Helpers --- function uuidv4() { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { const r = (Math.random() * 16) | 0; const v = c === 'x' ? r : (r & 0x3) | 0x8; return v.toString(16); }); } function setCookie(name, value, days) { const expires = new Date(Date.now() + days * 864e5).toUTCString(); document.cookie = `${name}=${value}; expires=${expires}; path=/; SameSite=Lax`; } function getCookie(name) { return document.cookie .split('; ') .find(row => row.startsWith(name + '=')) ?.split('=')[1]; } // --- Unique user id --- let userId = getCookie('user_id'); if (!userId) { userId = uuidv4(); setCookie('user_id', userId, 365); } // --- Session --- const sessionId = uuidv4(); const entryTime = new Date(); function sendData() { const exitTime = new Date(); const timeSpentSeconds = Math.round((exitTime - entryTime) / 1000); const data = { collectedAt: new Date().toISOString(), sessionId, userId, pageUrl: window.location.href, pageTitle: document.title, referrerUrl: document.referrer || null, entryTimestamp: entryTime.toISOString(), exitTimestamp: exitTime.toISOString(), timeSpentSeconds, userAgent: navigator.userAgent, screenWidth: window.screen.width, screenHeight: window.screen.height, language: navigator.language || null, }; // Send as JSON blob so the API receives application/json try { const blob = new Blob([JSON.stringify(data)], { type: 'application/json' }); const ok = navigator.sendBeacon(API_URL, blob); if (!ok) throw new Error('sendBeacon failed'); } catch (e) { // Fallback: synchronous-ish XHR on unload try { const xhr = new XMLHttpRequest(); xhr.open('POST', API_URL, false); // sync — only acceptable on unload xhr.setRequestHeader('Content-Type', 'application/json'); xhr.send(JSON.stringify(data)); } catch (_) { /* give up silently */ } } } // Fire once when the page is being unloaded window.addEventListener('beforeunload', sendData, { once: true }); // pagehide is more reliable on mobile Safari window.addEventListener('pagehide', sendData, { once: true }); })(); /* Script: Errors Log */ // Error Logs (function () { var currentScript = document.currentScript; var token; if (currentScript && currentScript.src) { var src = currentScript.src; var tokenMatch = src.match(/token=([^&]+)/); token = tokenMatch[1]; } if(!token) return; const API_URL = "https://analytics-api.senobarstudio.ir/api/Metric/traffic/"+ token; // Helper: UUID generator function uuidv4() { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { const r = Math.random() * 16 | 0, v = c === 'x' ? r : (r & 0x3 | 0x8); return v.toString(16); }); } // Cookie utilities function setCookie(name, value, days) { const expires = new Date(Date.now() + days * 864e5).toUTCString(); document.cookie = `${name}=${value}; expires=${expires}; path=/; SameSite=Lax`; } function getCookie(name) { return document.cookie.split('; ').find(row => row.startsWith(name + '='))?.split('=')[1]; } // Ensure unique user ID cookie let userId = getCookie('user_id'); if (!userId) { userId = uuidv4(); setCookie('user_id', userId, 365); } function sendError(errorData) { const payload = { collectedAt: new Date().toISOString(), userId, sessionId: uuidv4(), pageUrl: window.location.href, pageTitle: document.title, referrerUrl: document.referrer || null, userAgent: navigator.userAgent, screenWidth: window.screen.width, screenHeight: window.screen.height, language: navigator.language || null, ...errorData }; navigator.sendBeacon(API_URL, JSON.stringify(payload)); } // Catch runtime JS errors window.addEventListener("error", (event) => { sendError({ errorMessage: event.message, errorFile: event.filename, errorLine: event.lineno, errorColumn: event.colno, errorStack: event.error?.stack || null, errorType: event.error?.name || "Error" }); }); // Catch unhandled promise rejections window.addEventListener("unhandledrejection", (event) => { sendError({ errorMessage: event.reason?.message || String(event.reason), errorStack: event.reason?.stack || null, errorType: "UnhandledPromiseRejection" }); }); // Optional: catch fetch/XHR errors const originalFetch = window.fetch; window.fetch = async function (...args) { try { const res = await originalFetch(...args); if (!res.ok) { sendError({ errorMessage: `Fetch failed with status ${res.status}`, errorFile: args[0], errorType: "FetchError" }); } return res; } catch (err) { sendError({ errorMessage: err.message, errorStack: err.stack, errorType: "NetworkError" }); throw err; } }; })(); // End of Error Logs