// === VNEWS Frontend v2 - Full Functions === // Added: World Cup highlight tab + Friendly highlights on homepage + 🔥 HOT feed // Updated: HOT feed chỉ hiển thị hashtag chips, click vào hashtag thì hiện tin // Fix: safeJson parsing to handle non-JSON error responses gracefully // Fix: timeout cho rewrite calls, không treo vô hạn // === SAFE JSON PARSE === async function safeJson(resp){ try{ const text=await resp.text(); try{return JSON.parse(text);} catch(e){ // Server returned non-JSON (e.g., HTML error page) console.warn('Non-JSON response:', text.slice(0,200)); return {error: 'Server error', _status: resp.status, _raw: text.slice(0,200)}; } }catch(e){ return {error: 'Network error', _msg: e.message}; } } // === FETCH WITH TIMEOUT === function fetchWithTimeout(url, opts, timeoutMs){ return new Promise((resolve, reject)=>{ const ctrl = new AbortController(); const timer = setTimeout(() => { ctrl.abort(); reject(new Error('timeout')); }, timeoutMs); fetch(url, { ...opts, signal: ctrl.signal }).then(r => { clearTimeout(timer); resolve(r); }).catch(e => { clearTimeout(timer); reject(e); }); }); } // === LOAD HOME === async function loadHome(){ const[featured,sh,wall,hlLeagues,ai,wcData]=await Promise.all([ fetch('/api/livescore/featured').then(r=>safeJson(r)).catch(()=>null), fetch('/api/shorts').then(r=>safeJson(r)).catch(()=>[]), fetch('/api/ai_wall').then(r=>safeJson(r)).catch(()=>({posts:[]})), fetch('/api/highlights/leagues').then(r=>safeJson(r)).catch(()=>({})), fetch('/api/genk_ai').then(r=>safeJson(r)).catch(()=>[]), fetch('/api/wc2026').then(r=>safeJson(r)).catch(()=>null) ]); _hlLeagueData=hlLeagues; _wc2026Data=wcData; _shortsData=interleaveShorts(sh||[]); let h=''; if(featured&&featured.home){const sc=featured.status==='live'?'':'upcoming';const st=featured.status==='live'?`🔴 ${featured.minute||'LIVE'}`:`⏰ ${featured.time}`;h+=`