Streamlabs Privacy Policy
https://cdn.streamlabs.com/static/imgs/uploads/privacy-policy-aug20-2025_r3g0iofvsq6qvD4V.md
Last updated August 20, 2025
Sorry, something went wrong.
'); return; } setTimeout(tick, 30); }; tick(); }; function toKebab(str) { return (str || '') .toLowerCase() .trim() .replace(/[^\w\s-]/g, '') .replace(/\s+/g, '-') .replace(/-+/g, '-'); } function extractTitleFromLi(li) { const raw = (li.textContent || '').trim().replace(/\s+/g, ' '); // Accept: "1. Title", "1) Title", "1 - Title", "1: Title", "Section 1 - Title" const m = raw.match(/^(?:section\s*)?\d+(?:\.\d+)*\s*(?:[.)]|[:\-–])?\s*(.+)$/i); if (m && m[1]) return m[1].trim(); const strong = li.querySelector('strong,b'); if (strong && strong.textContent.trim()) return strong.textContent.trim(); const candidate = raw.split(/(?<=\.)\s+| {2,}/)[0].trim(); if (/\d/.test(candidate) && !/[A-Za-z]/.test(candidate)) return ''; return /[A-Za-z]/.test(candidate) ? candidate : ''; } function postProcessLinksAndHeadings(container, baseHref) { container.querySelectorAll('h1,h2,h3,h4,h5,h6').forEach(h => { if (!h.id) h.id = toKebab(h.textContent); }); container.querySelectorAll('a[href]').forEach(a => { try { const u = new URL(a.getAttribute('href'), baseHref); if (u.origin !== location.origin) a.setAttribute('rel', 'noopener'); } catch (_) {} }); } function addAnchorsAndBuildMenu(container) { const menuData = []; let olCount = 0; container.querySelectorAll('ol').forEach((ol) => { if (!ol.querySelector('h3')) return; olCount++; const olId = `section-${olCount}`; ol.id = olId; let liCount = 0; ol.querySelectorAll(':scope > li').forEach(li => { liCount++; const liId = `${olId}-${liCount}`; li.id = liId; const title = extractTitleFromLi(li); if (title) { const label = title.length > 120 ? (title.slice(0, 117) + '…') : title; menuData.push({ text: label, id: liId }); } }); }); return menuData; } function renderMenu(menuData) { const menuHolder = $('tos_menu'); if (!menuHolder) return; menuHolder.innerHTML = ''; const ul = document.createElement('ul'); menuData.forEach(item => { const li = document.createElement('li'); const a = document.createElement('a'); a.href = `#${item.id}`; a.textContent = item.text; li.appendChild(a); ul.appendChild(li); }); menuHolder.appendChild(ul); } function normalizeHeadings(root) { root.querySelectorAll('h1,h2,h3,h4,h5,h6').forEach(h => { const rawText = (h.textContent || '').trim(); const m = /\s*\{#([A-Za-z][\w\-:/\.]*)\}\s*$/i.exec(rawText); if (m) { const clean = rawText.slice(0, m.index).trim(); h.textContent = clean; h.id = m[1]; } if (!h.id) h.id = toKebab(h.textContent || ''); const currentLevel = parseInt(h.tagName.substring(1), 10); const newLevel = Math.min(currentLevel + 1, 6); if (newLevel !== currentLevel) { const newH = document.createElement(`h${newLevel}`); newH.id = h.id; while (h.firstChild) newH.appendChild(h.firstChild); h.replaceWith(newH); } }); } // ---- renderer ---- function renderFromUrl(MD_URL) { setStatus('Loading…
'); waitForLibs(() => { const md = new marked.Marked({ headerIds: true, mangle: false, gfm: true, breaks: false }); fetch(MD_URL, { credentials: 'omit', cache: 'no-store' }) .then(r => { if (!r.ok) throw new Error('Failed to fetch TOS markdown: ' + r.status); return r.text(); }) .then(mdText => { const rawHtml = md.parse(String(mdText)); const temp = document.createElement('div'); temp.innerHTML = rawHtml; normalizeHeadings(temp); const firstHeading = Array.from(temp.children).find( (el) => /^H[1-6]$/.test(el.tagName) ); if (firstHeading) { const titleEl = $('sl_tos_title'); if (titleEl) titleEl.textContent = firstHeading.textContent.trim(); firstHeading.remove(); } const dateRegex = /(Date of last update|Last updated|Updated)\s*:\s*.+/i; let dateCaptured = null; temp.querySelectorAll('p, h2, h3, h4, h5, h6').forEach(el => { if (dateCaptured) return; const m = (el.textContent || '').match(dateRegex); if (m) { dateCaptured = m[0].trim(); const dateEl = $('sl_tos_last_updated'); if (dateEl) dateEl.textContent = dateCaptured; } }); const safeHtml = DOMPurify.sanitize(temp.innerHTML, { USE_PROFILES: { html: true } }); const holder = $('sl_tos'); if (!holder) throw new Error('Missing #sl_tos container'); holder.innerHTML = safeHtml; // Defer heavier work so HTML paints ASAP defer(() => { try { postProcessLinksAndHeadings(holder, MD_URL); const menuData = addAnchorsAndBuildMenu(holder); renderMenu(menuData); } catch (e) { console.error(e); } }); }) .catch(err => { console.error(err); const fallback = $('tos') || $('sl_tos'); if (fallback) fallback.innerHTML = 'Sorry, something went wrong.
'; }); }); } // ---- Webflow-friendly boot (handles swapped/restored pages) ---- let started = false; let obs = null; let timeoutId = null; function stopWatching() { if (obs) obs.disconnect(); obs = null; if (timeoutId) clearTimeout(timeoutId); timeoutId = null; } function startIfReady() { if (started) return; const url = getMdUrl(); if (!url) return; started = true; stopWatching(); renderFromUrl(url); } function boot() { // If Webflow re-renders, allow re-run started = false; stopWatching(); // Try immediately startIfReady(); if (started) return; // Watch for Webflow DOM swaps obs = new MutationObserver(() => { startIfReady(); }); obs.observe(document.documentElement, { childList: true, subtree: true }); // Never hang forever on Loading… timeoutId = setTimeout(() => { if (started) return; stopWatching(); setStatus('Sorry, content did not load.
'); }, 8000); } // Multiple lifecycle hooks because Webflow can be “weird” here if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', boot, { once: true }); } else { boot(); } window.addEventListener('load', boot); window.addEventListener('pageshow', boot); })();
