/* VYNDR embeddable widget — single self-contained file, ~3.5 KB minified. * * * * Mounts a 300px card at the script tag's location, refreshes every 30 * minutes, links each grade to https://vyndr.app (new tab). Two themes: * dark (default), light. */ (function () { 'use strict'; var SCRIPT = document.currentScript; if (!SCRIPT) { var all = document.getElementsByTagName('script'); for (var i = all.length - 1; i >= 0; i--) { if (/widget\.js(\?|$)/.test(all[i].src)) { SCRIPT = all[i]; break; } } } if (!SCRIPT) return; var SPORT = (SCRIPT.getAttribute('data-sport') || 'nba').toLowerCase(); var THEME = (SCRIPT.getAttribute('data-theme') || 'dark').toLowerCase(); if (['nba', 'wnba', 'mlb'].indexOf(SPORT) < 0) SPORT = 'nba'; if (['dark', 'light'].indexOf(THEME) < 0) THEME = 'dark'; var API = (SCRIPT.getAttribute('data-api') || 'https://api.vyndr.app') + '/api/widget'; var REFRESH_MS = 30 * 60 * 1000; // ── styles ───────────────────────────────────────────────────────────── var PALETTE = THEME === 'light' ? { bg: '#FFFFFF', border: '#E5E5EC', text: '#0E0E16', textDim: '#4A4A5E', accent: '#0F3D2E', } : { bg: '#06060B', border: '#1E1E2E', text: '#E8E8F0', textDim: '#7A7A8E', accent: '#00D4A0', }; var GRADE_COLORS = { 'A+': '#00FFB8', 'A': '#00D4A0', 'A-': '#00D4A0', 'B+': '#4A9EFF', 'B': '#4A9EFF', 'B-': '#4A9EFF', 'C+': '#FFB347', 'C': '#FFB347', 'C-': '#FFB347', 'D': '#FF5252', 'F': '#FF5252', }; function el(tag, css, text) { var n = document.createElement(tag); if (css) n.style.cssText = css; if (text != null) n.textContent = text; return n; } function gradeColor(g) { return GRADE_COLORS[g] || PALETTE.textDim; } // ── mount ───────────────────────────────────────────────────────────── var host = el('div', [ 'box-sizing:border-box', 'width:300px', 'background:' + PALETTE.bg, 'color:' + PALETTE.text, 'border:1px solid ' + PALETTE.border, 'border-radius:14px', 'padding:16px', 'font-family:-apple-system,system-ui,BlinkMacSystemFont,"Segoe UI",sans-serif', 'font-size:14px', 'line-height:1.4', ].join(';') + ';'); host.setAttribute('data-vyndr-widget', SPORT); var header = el('div', 'display:flex;justify-content:space-between;align-items:baseline;margin-bottom:10px;'); var brand = el('span', 'font-family:"IBM Plex Mono","JetBrains Mono","SF Mono",ui-monospace,monospace;font-weight:800;letter-spacing:2px;color:' + PALETTE.text + ';'); brand.appendChild(document.createTextNode('VYND')); var r = el('span', 'color:' + PALETTE.accent + ';text-shadow:0 0 6px rgba(0,212,160,.45);', 'R'); brand.appendChild(r); header.appendChild(brand); header.appendChild(el('span', 'font-family:"IBM Plex Mono",monospace;font-size:10px;letter-spacing:2px;color:' + PALETTE.textDim + ';', 'TOP ' + SPORT.toUpperCase())); host.appendChild(header); var list = el('div', ''); host.appendChild(list); var footer = el('div', 'margin-top:12px;font-family:"IBM Plex Mono",monospace;font-size:10px;letter-spacing:2px;color:' + PALETTE.textDim + ';text-align:center;'); var foot = el('a', 'color:inherit;text-decoration:none;'); foot.href = 'https://vyndr.app'; foot.target = '_blank'; foot.rel = 'noopener noreferrer'; foot.textContent = 'Powered by VYNDR · vyndr.app'; footer.appendChild(foot); host.appendChild(footer); if (SCRIPT.parentNode) SCRIPT.parentNode.insertBefore(host, SCRIPT); // ── render ──────────────────────────────────────────────────────────── function row(g) { var a = el('a', [ 'display:flex', 'justify-content:space-between', 'align-items:center', 'padding:10px 12px', 'margin-bottom:6px', 'border-radius:8px', 'text-decoration:none', 'color:' + PALETTE.text, 'border:1px solid ' + PALETTE.border, 'background:' + (THEME === 'light' ? '#F8F8FC' : '#0E0E16'), ].join(';') + ';'); a.href = 'https://vyndr.app'; a.target = '_blank'; a.rel = 'noopener noreferrer'; var left = el('div', 'min-width:0;'); var name = el('div', 'font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;', String(g.player || '')); var sub = el('div', 'font-family:"IBM Plex Mono",monospace;font-size:11px;color:' + PALETTE.textDim + ';', [g.stat || '', String(g.direction || '').toUpperCase(), g.line ?? ''].join(' ')); left.appendChild(name); left.appendChild(sub); var grade = el('div', [ 'font-family:"IBM Plex Mono",monospace', 'font-weight:800', 'font-size:22px', 'margin-left:8px', 'color:' + gradeColor(g.grade), 'text-shadow:0 0 6px ' + gradeColor(g.grade) + '55', ].join(';') + ';', String(g.grade || '')); a.appendChild(left); a.appendChild(grade); return a; } function setError(msg) { list.innerHTML = ''; var p = el('div', 'padding:10px 0;color:' + PALETTE.textDim + ';font-size:12px;', msg); list.appendChild(p); } function fetchAndRender() { var url = API + '?sport=' + encodeURIComponent(SPORT) + '&_=' + Date.now(); var xhr = new XMLHttpRequest(); xhr.open('GET', url, true); xhr.timeout = 8000; xhr.onreadystatechange = function () { if (xhr.readyState !== 4) return; if (xhr.status < 200 || xhr.status >= 300) return setError('Signal unavailable. Try later.'); try { var data = JSON.parse(xhr.responseText); list.innerHTML = ''; var props = (data && data.props) || []; if (!props.length) return setError('No grades on the slate yet.'); for (var i = 0; i < props.length; i++) list.appendChild(row(props[i])); } catch (e) { setError('Could not parse response.'); } }; xhr.ontimeout = function () { setError('Signal timed out.'); }; xhr.send(); } fetchAndRender(); setInterval(fetchAndRender, REFRESH_MS); })();