PageSourceSearch

https://xoigac.top/dist/js/focus-player.js?v=zkkr8WfQNdK0bypb_iQ17EK_w_2LYoVzcJhqT7urmDM

js xoigac.top collected 2026-10-10 02:21:51 UTC 204,299 bytes, 1 lines download raw bytes

1window.switchFocusMatch=function(){console.warn("[FocusPlayer] 函数尚未初始化,请稍候...")},window.filterFocusMatches=function(){console.warn("[FocusPlayer] 筛选函数尚未初始化,请稍候...")},window.isFocusWorldCupMatch=function(leagueApiId,leagueName){if(1===leagueApiId||"1"===leagueApiId)return!0;const league=(leagueName||"").toLowerCase();return league.includes("world cup")||league.includes("fifa world cup")||league.includes("世界杯")},window.FOCUS_WORLD_CUP_BADGE_ICON='<svg class="focus-wc-badge-icon" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 9H4.5a2.5 2.5 0 0 1 0-5H6"/><path d="M18 9h1.5a2.5 2.5 0 0 0 0-5H18"/><path d="M4 22h16"/><path d="M10 14.66V17c0 .55-.47.98-.97 1.21C7.85 18.75 7 20.24 7 22"/><path d="M14 14.66V17c0 .55.47.98.97 1.21C16.15 18.75 17 20.24 17 22"/><path d="M18 2H6v7a6 6 0 0 0 12 0V2Z"/></svg>',window.buildFocusLiveBadgeLabel=function(isWorldCup,liveText){const text=liveText||"LIVE";return isWorldCup?`<span class="badge bg-danger focus-live-badge-wc">${window.FOCUS_WORLD_CUP_BADGE_ICON}${text}</span>`:`<span class="badge bg-danger">${text}</span>`},window.switchFocusBeauty=function(){console.warn("[FocusPlayer] switchFocusBeauty 尚未初始化,请稍候...")},function(){"use strict";const AVATAR_COLOR_PALETTE=["#FF6B6B","#4ECDC4","#45B7D1","#FFA07A","#98D8C8","#F7DC6F","#BB8FCE","#85C1E2","#F8B739","#52B788","#E63946","#06FFA5"];function generateColorFromNickname(nickname){if(!nickname)return AVATAR_COLOR_PALETTE[0];const hash=function hashString(str){if(!str)return 0;let hash=0;for(let i=0;i<str.length;i++)hash=(hash<<5)-hash+str.charCodeAt(i),hash&=hash;return Math.abs(hash)}(nickname);return AVATAR_COLOR_PALETTE[hash%AVATAR_COLOR_PALETTE.length]}function createLetterAvatar(nickname){const letter=function extractFirstLetter(nickname){return nickname&&0!==nickname.trim().length?nickname.trim()[0].toUpperCase():(console.warn("[Avatar] Invalid nickname provided, using default"),"?")}(nickname),backgroundColor=generateColorFromNickname(nickname),avatarDiv=document.createElement("div");return avatarDiv.className="focus-chat-msg-avatar focus-chat-avatar-letter",avatarDiv.style.borderColor=backgroundColor,avatarDiv.textContent=letter,avatarDiv}function renderAvatar(user){return user&&user.nickname?user.avatarUrl&&"/images/default-avatar.svg"!==user.avatarUrl&&"undefined"!==user.avatarUrl&&"null"!==user.avatarUrl?function createImageAvatar(avatarUrl,nickname){const avatarDiv=document.createElement("div");avatarDiv.className="focus-chat-msg-avatar";const img=document.createElement("img");return img.src=avatarUrl,img.alt="",img.onerror=function(){const letterAvatar=createLetterAvatar(nickname);avatarDiv.parentNode.replaceChild(letterAvatar,avatarDiv)},avatarDiv.appendChild(img),avatarDiv}(user.avatarUrl,user.nickname):createLetterAvatar(user.nickname):createLetterAvatar("?")}function getLogoPlaceholder(type="teams"){return"leagues"===type?"/images/placeholder-league.svg":"flags"===type?"/images/placeholder-flag.svg":"teams"===type?"/images/placeholder-team.svg":"/images/placeholder-image.svg"}function optimizeImageUrl(url,type="teams",size="medium",sportType="football"){return url&&"undefined"!==url&&"null"!==url?url:getLogoPlaceholder(type)}function setFocusLogo(img,url,type="teams"){img&&(!function bindLogoFallback(img,type="teams"){if(!img)return;const placeholder=getLogoPlaceholder(type);img.onerror=function(){this.onerror=null,this.src=placeholder}}(img,type),delete img.dataset.fallback,img.src=optimizeImageUrl(url,type))}const setUserInitiatedPlay=()=>{try{sessionStorage.setItem("focus_user_played","1")}catch(e){}};function isBeautyCardElement(card){return!!card&&card.classList.contains("beauty-card")}function persistFocusSelectedCard(card){const storageId=function getFocusCardStorageId(card){return card?isBeautyCardElement(card)?card.dataset.identifier||"":card.dataset.cardId||"":""}(card);if(storageId)try{localStorage.setItem("focus_selected_card_id",storageId)}catch(e){console.warn("[FocusPlayer] 无法保存选中卡片:",e)}}function findStoredFocusCard(storedId){if(!storedId)return null;const byCardId=document.querySelector(`.focus-card[data-card-id="${storedId}"], .focus-match-card[data-card-id="${storedId}"]`);if(byCardId)return byCardId;const byIdentifierFootball=document.querySelector(`.focus-card[data-identifier="${storedId}"], .focus-match-card[data-identifier="${storedId}"]`);return byIdentifierFootball||document.querySelector(`.beauty-card[data-identifier="${storedId}"]`)}function syncPlayerFollowFromCard(card){if(!card)return;const streamerId=parseInt(card.dataset.streamerId,10)||0;window.StreamerFollow&&"function"==typeof window.StreamerFollow.setPlayerStreamer?window.StreamerFollow.setPlayerStreamer(streamerId,void 0,card):window.__pendingPlayerStreamer={streamerId:streamerId,card:card}}function activateFocusCard(card,options){card&&(document.querySelectorAll(".focus-card, .focus-match-card, .beauty-card").forEach(c=>c.classList.remove("active")),card.classList.add("active"),options&&!0===options.skipCover||updateFocusPlayerCoverFromCard(card),syncPlayerFollowFromCard(card))}
1function extractBackgroundImageUrl(value){if(!value||"none"===value)return"";const match=String(value).match(/url\(["']?([^"')]+)["']?\)/i);return match?match[1]:""}let focusCoverLoadToken=0;function applyFocusPlayerCover(coverUrl){const container=document.getElementById("focusPlayerContainer");if(!container)return;const normalized=(coverUrl||"").trim();let cover=container.querySelector(".focus-player-cover");if(!normalized)return focusCoverLoadToken+=1,cover?.remove(),container.classList.remove("has-cover"),void container.classList.add("cover-ready");if(!cover){cover=document.createElement("div"),cover.className="focus-player-cover",cover.setAttribute("aria-hidden","true");const video=container.querySelector("video");video?container.insertBefore(cover,video):container.prepend(cover)}if(cover.dataset.coverUrl===normalized&&cover.classList.contains("is-ready"))return void container.classList.add("has-cover","cover-ready");const loadToken=++focusCoverLoadToken;cover.classList.remove("is-ready"),cover.dataset.coverUrl=normalized,container.classList.add("has-cover"),container.classList.remove("cover-ready");const img=new Image,revealCover=()=>{loadToken===focusCoverLoadToken&&(cover.style.backgroundImage=`url("${normalized.replace(/"/g,'\\"')}")`,requestAnimationFrame(()=>{loadToken===focusCoverLoadToken&&(cover.classList.add("is-ready"),container.classList.add("cover-ready"))}))};img.onload=async()=>{if("function"==typeof img.decode)try{await img.decode()}catch(e){}revealCover()},img.onerror=revealCover,img.src=normalized}function updateFocusPlayerCoverFromCard(card){applyFocusPlayerCover(function getFocusCardCoverUrl(card){if(!card)return"";const direct=(card.dataset.coverImage||card.dataset.streamerCardBackground||card.dataset.streamerAvatar||"").trim();if(direct)return direct;const bgEl=card.querySelector(".focus-card-bg.has-streamer-bg, .beauty-card-bg, .focus-card-bg.beauty-card-bg");if(bgEl){const inlineUrl=extractBackgroundImageUrl(bgEl.getAttribute("style")||"");if(inlineUrl)return inlineUrl;const computedUrl=extractBackgroundImageUrl(window.getComputedStyle(bgEl).backgroundImage);if(computedUrl)return computedUrl}return""}(card))}let liveKitSdkLoadPromise=null;async function ensureLiveKitSdkLoaded(){window.LivekitClient||window.LiveKitClient||window.livekit||(liveKitSdkLoadPromise||(liveKitSdkLoadPromise=function loadLiveKitSDK(){return new Promise((resolve,reject)=>{if(window.LivekitClient||window.LiveKitClient||window.livekit)return void resolve();const script=document.createElement("script");
1script.src="https://cdn.jsdelivr.net/npm/[email protected]/dist/livekit-client.umd.js",script.onload=resolve,script.onerror=()=>reject(new Error("LiveKit SDK 加载失败")),document.head.appendChild(script)})}()),await liveKitSdkLoadPromise)}async function getWebRtcTokenFast(matchId){const cache=(window.__webrtcTokenCache||(window.__webrtcTokenCache={}),window.__webrtcTokenCache),cached=cache[String(matchId)],now=Date.now();if(cached&&cached.token&&cached.wsUrl&&now-cached.fetchedAt<6e4)return cached;const tokenResponse=await fetch(`/api/webrtc/token/${matchId}`);if(!tokenResponse.ok)throw new Error("获取token失败");const tokenData=await tokenResponse.json();if(!tokenData.success||!tokenData.token)throw new Error(tokenData.message||"获取token失败");const value={token:tokenData.token,wsUrl:tokenData.wsUrl,fetchedAt:now};return cache[String(matchId)]=value,value}let focusCurrentMatchId=null,focusHlsInstance=null,focusHlsQualitySelector=null,focusLivekitRoom=null,focusIvsAdapter=null,focusChatConnection=null,focusCurrentChatRoomId=null,focusCanMuteUsers=!1,focusHardNoPublisher=!1,focusCf409Handled=!1,focusIsVipUser=!1,focusCurrentQuote=null,focusChatSettings={showAvatar:!0,nicknameMaskRule:"None",minIntervalSeconds:1,maxMessagesPerMinute:60,announcementEnabled:!1,announcementRotateSeconds:5,announcements:[],announcementContent:""};const focusChatAnnouncementDismiss=window.ChatAnnouncementUtil?.createDismissState?.()||{dismissed:!1,reset(){this.dismissed=!1},dismiss(){this.dismissed=!0}};let focusIsLoadingMore=!1,focusHasMore=!0,focusCurrentPage=1,focusFirstLoadDone=!1,focusUnreadCount=0,focusSuppressUnread=!0,focusScrollListenerBound=!1,focusLastMessageTime=0,focusMessageTimestamps=[],isStreamPlaying=!1,isFocusPlayingInProgress=!1,lastFocusStreamingType=null,lastFocusIdentifier=null,focusSwitchGeneration=0;function beginFocusSwitch(){return++focusSwitchGeneration}function isFocusSwitchStale(gen){return gen!==focusSwitchGeneration}let playbackTimeoutTimer=null,ivsLoadTimeoutTimer=null,cfLoadTimeoutTimer=null,hasDefinitiveError=!1;let focusRetryCount=0,focusRetryTimer=null;function clearFocusRetryTimer(){focusRetryTimer&&(clearTimeout(focusRetryTimer),focusRetryTimer=null)}function translateServerMessage(message){if(!message)return message;if(!window.L?.T)return message;if("string"==typeof message&&message.startsWith("player.")){const translated=window.L.T(`match.${message}`);return translated&&translated!==`match.${message}`?translated:message}return message}function isAllSourcesFailedResult(result){return!0===result?.allSourcesFailed||"player.allSourcesFailed"===result?.message||"string"==typeof result?.message&&result.message.includes("allSourcesFailed")}function handleAllSourcesFailed(){showFocusPlayerError(window.L?.T?window.L.T("match.player.allSourcesFailed"):"所有直播源已失败,请稍后重试",!1,!1,!0),clearFocusRetryTimer(),focusRetryCount=0,hideFocusStreamStartingOverlay(),stopFocusPublisherPolling()}function countFocusLiveCards(){const container=document.getElementById("liveMatchCards");if(!container)return 0;let count=0;return container.querySelectorAll(":scope > .focus-card-item").forEach(item=>{isFocusCardItemLive(item)&&count++}),count}function refreshFocusLiveCountFromDom(){const count=countFocusLiveCards(),el=document.getElementById("focusLiveCount");return el&&(el.textContent=count),"function"==typeof window.updateFocusLiveButtonState&&window.updateFocusLiveButtonState(count),count}function getFocusLiveCount(){const domCount=countFocusLiveCards(),container=document.getElementById("liveMatchCards");if(container&&container.querySelectorAll(":scope > .focus-card-item").length>0)return domCount;const el=document.getElementById("focusLiveCount");if(el){const n=parseInt(el.textContent,10);if(!isNaN(n))return n}return domCount}function markSessionUserPickedTab(filterType){if(filterType)try{sessionStorage.setItem("focus_user_picked_tab","streamer"===filterType?"football":filterType)}catch(e){}}function isFocusCardItemLive(item){return!!item&&("true"===item.dataset.isLive||null!==item.querySelector(".focus-card.is-live, .beauty-card.is-live"))}
1function isFocusMatchCardLive(card){if(!card)return!1;if(card.classList.contains("is-live"))return!0;const status=card.dataset.status;if("running"===status||"starting"===status)return!0;if("true"===card.dataset.isLive)return!0;const item=card.closest(".focus-card-item");return!(!item||"true"!==item.dataset.isLive)}function findFocusCardForPlayback(container){if(!container)return null;const identifier=container.dataset.identifier;if(identifier){const byIdentifier=document.querySelector(`.focus-card[data-identifier="${identifier}"], .focus-match-card[data-identifier="${identifier}"]`);if(byIdentifier)return byIdentifier;const byCardId=document.querySelector(`.focus-card[data-card-id="${identifier}"], .focus-match-card[data-card-id="${identifier}"]`);if(byCardId)return byCardId}const matchId=container.dataset.matchId;if(matchId){const active=document.querySelector(`.focus-card.active[data-match-id="${matchId}"], .focus-match-card.active[data-match-id="${matchId}"]`);if(active)return active}return document.querySelector(".focus-card.active, .focus-match-card.active")}function hasFocusWorldCupCards(){const section=document.getElementById("focusLiveSection");return"true"===section?.dataset.hasWorldCup||"false"!==section?.dataset.hasWorldCup&&function getFocusWorldCupCount(){const container=document.getElementById("liveMatchCards");return container?container.querySelectorAll(':scope > .focus-card-item[data-is-world-cup="true"]').length:0}()>0}function toHomeCategory(filter){return"beauty"===filter?"entertainment":"streamer"===filter?"all":"football"===filter||"basketball"===filter||"entertainment"===filter||"worldcup"===filter?filter:"all"}function toUiFilter(category){return"entertainment"===category?"beauty":category||"all"}function parseHomeFocusStateFromLocation(){const path=(location.pathname||"/").replace(/\/+$/,"")||"/",segment="/"===path?"":path.replace(/^\//,"").split("/")[0].toLowerCase();return{category:{football:1,basketball:1,entertainment:1,worldcup:1}[segment]?segment:"all",live:"live"===(new URLSearchParams(location.search).get("status")||"").toLowerCase()}}function syncHomeFocusRouting(state){window.homeFocusRouting=window.homeFocusRouting||{},window.homeFocusRouting.category=state.category,window.homeFocusRouting.uiFilter=toUiFilter(state.category),window.homeFocusRouting.live=!!state.live}function syncHomeFocusUrl(state,historyMode){if("none"===historyMode)return;const next=function buildHomeFocusUrl(category,live){const url=new URL("all"!==category&&category?"/"+category:"/",location.origin);new URLSearchParams(location.search).forEach((value,key)=>{"status"!==key&&url.searchParams.set(key,value)}),live?url.searchParams.set("status","live"):url.searchParams.delete("status");const search=url.searchParams.toString();return url.pathname+(search?"?"+search:"")+location.hash}(state.category,state.live),currentNoHash=location.pathname+location.search,nextUrl=new URL(next,location.origin);if(currentNoHash===nextUrl.pathname+nextUrl.search)return;const payload={homeFocus:state};"replace"===historyMode?history.replaceState(payload,"",next):history.pushState(payload,"",next)}document.addEventListener("DOMContentLoaded",function(){!function restoreStoredActivation(){try{const storedCardId=localStorage.getItem("focus_selected_card_id");if(!storedCardId)return;const storedCard=findStoredFocusCard(storedCardId);if(!storedCard)return;if(activateFocusCard(storedCard,{skipCover:!0}),!isBeautyCardElement(storedCard)){const cardsContainer=storedCard.closest("[data-tab-content]"),cardTab=cardsContainer?.dataset?.tabContent;cardTab&&"function"==typeof window.switchStreamTab&&window.switchStreamTab(cardTab)}}catch(e){console.error("[FocusPlayer] 恢复激活状态失败:",e)}}(),function initFocusFilterUserTracking(){document.querySelectorAll(".focus-filter-btn, .focus-live-filter-btn").forEach(btn=>
1{"1"!==btn.dataset.filterUserTrack&&(btn.dataset.filterUserTrack="1",btn.addEventListener("click",()=>{const filter=btn.dataset.filter;filter&&"live"!==filter&&markSessionUserPickedTab(filter)}))})}(),function initCloudflareWhepEventListeners(){window.addEventListener("cloudflareWhepConnected",event=>{event.detail.whepUrl,hideFocusLoadingMessage(),hideFocusPlayerError(),isStreamPlaying=!0;const video=document.getElementById("focusLivePlayer");video&&video.classList.add("loaded")}),window.addEventListener("cloudflareWhepDisconnected",event=>{event.detail.reason;const container=document.getElementById("focusPlayerContainer"),identifier=container?.dataset?.identifier;identifier&&(isStreamPlaying=!1,showFocusPlayerErrorSmart())}),window.addEventListener("cloudflareWhepError",event=>{event.detail.error;const container=document.getElementById("focusPlayerContainer"),identifier=container?.dataset?.identifier;if(!identifier)return;const is409Error=event.detail.error&&(event.detail.error.includes("409")||event.detail.error.includes("player.notLiveYet"));isStreamPlaying=!1,is409Error?(focusCf409Handled=!0,globalRetryManager.reset(),clearFocusRetryTimer(),focusRetryCount=0,hideFocusLoadingMessage(),showFocusNotLiveMessage()):showFocusPlayerErrorSmart(event.detail.error)}),window.addEventListener("cloudflareWhepClosed",event=>{event.detail.whepUrl,isStreamPlaying=!1})}();"undefined"!=typeof IVSPlayerAdapter||console.error("[FocusPlayer] 依赖未加载,请检查脚本加载顺序"),setTimeout(async()=>{window.sortFocusCardsByTime(),"function"==typeof window.updateFocusFilterCounts&&window.updateFocusFilterCounts(),initializeAllCardsLiveStatus(),await syncFocusStreamLiveStatusFromDb(),refreshFocusLiveCountFromDom(),restoreFilterState();(function initFocusPlayer(){const focusSection=document.getElementById("focusLiveSection");if(!focusSection)return!1;const hasLive="true"===focusSection.dataset.hasLive,hasBeauty="true"===focusSection.dataset.hasBeauty||null!==document.querySelector("#liveMatchCards .beauty-card"),hasCards="true"===focusSection.dataset.hasCards||null!==document.querySelector("#liveMatchCards .focus-card-item");if(!hasLive&&!hasBeauty&&!hasCards)return initFocusLiveUpdates(),!1;const container=document.getElementById("focusPlayerContainer");if(!container)return!1;const video=document.getElementById("focusLivePlayer");if(!video)return applyFocusDeepLink()||async function bootstrapFocusChatOnly(){const card=document.querySelector("#liveMatchCards .focus-card.active, #liveMatchCards .beauty-card.active")||findFirstFocusCardForChat();card&&(activateFocusCard(card),await async function initFocusChatFromCard(card){if(!card)return;if(card.classList.contains("beauty-card")){const identifier=card.dataset.identifier;return void(identifier&&await initFocusChat(null,null,identifier))}const matchId=parseInt(card.dataset.matchId,10),isManualFootball="true"===card.dataset.manualFootball||matchId<0,identifier=card.dataset.identifier||card.dataset.cardId,manualRoomId=isManualFootball?toStandaloneChatRoomId(identifier):null;if(manualRoomId)return void await initFocusChat(null,null,manualRoomId);const parts=identifier?String(identifier).split("_"):[],streamSessionId=parts.length>1?parts[1]:card.dataset.streamSessionId||null;await initFocusChat(matchId,streamSessionId)}(card));initFocusChatHeightSync(),initFocusLiveUpdates(),initializeAllCardsLiveStatus(),restoreFilterState()}(),!1;if(applyFocusDeepLink())return!0;try{const storedCardId=localStorage.getItem("focus_selected_card_id");if(storedCardId){const storedCard=findStoredFocusCard(storedCardId),storedItem=storedCard?.closest(".focus-card-item");if(storedCard&&"none"!==storedItem?.style.display){if(initFocusChatHeightSync(),initFocusLiveUpdates(),isBeautyCardElement(storedCard)){const switchGen=beginFocusSwitch();activateFocusCard(storedCard);const liveCards=document.getElementById("liveMatchCards");return liveCards&&setTimeout(()=>scrollActiveCardToLeft(storedCard,liveCards),50),switchFocusBeauty(storedCard,switchGen),!0}const matchId=parseInt(storedCard.dataset.matchId,10),streamSessionId=storedCard.dataset.streamSessionId||void 0;if(!isNaN(matchId))return switchFocusMatch(matchId,streamSessionId),!0}}}catch(e){console.error("[FocusPlayer] 恢复选中卡片失败:",e)}try{const storedCardId=localStorage.getItem("focus_selected_card_id"),storedCard=storedCardId?findStoredFocusCard(storedCardId):null,
1storedVisible=storedCard&&"none"!==storedCard.closest(".focus-card-item")?.style.display;if(!(storedCardId&&storedVisible)){const defaultCard=function findFirstDefaultFocusCard(){if(getFocusLiveCount()>0){const liveCard=findFirstLiveFocusCard();if(liveCard)return liveCard}return findFirstFocusCardForChat()}();if(defaultCard){const liveCardsEl=document.getElementById("liveMatchCards");if(liveCardsEl&&setTimeout(()=>scrollActiveCardToLeft(defaultCard,liveCardsEl),50),initFocusChatHeightSync(),initFocusLiveUpdates(),isBeautyCardElement(defaultCard))return switchFocusBeauty(defaultCard,beginFocusSwitch()),!0;const matchId=parseInt(defaultCard.dataset.matchId,10),streamSessionId=defaultCard.dataset.streamSessionId||void 0;if(!isNaN(matchId))return switchFocusMatch(matchId,streamSessionId),!0}}}catch(e){console.error("[FocusPlayer] 选中默认卡片失败:",e)}let initialMatchId=parseInt(container.dataset.matchId),initialStreamingType=container.dataset.streamingType,initialIdentifier=container.dataset.identifier;focusCurrentMatchId=initialMatchId;const streamingType=initialStreamingType;hideFocusLoadingMessage(),ensureFocusVideoMuted(),setUserInitiatedPlay();const playbackCard=findFocusCardForPlayback(container);playbackCard&&!isFocusMatchCardLive(playbackCard)?showFocusNotLiveMessage():startFocusPlayback(focusCurrentMatchId,streamingType,initialIdentifier);const initialStandaloneRoomId=toStandaloneChatRoomId(initialIdentifier);if(initialStandaloneRoomId)initFocusChat(null,null,initialStandaloneRoomId);else{const identifierParts=initialIdentifier?initialIdentifier.split("_"):[],streamSessionId=identifierParts.length>1?identifierParts[1]:null;initFocusChat(focusCurrentMatchId,streamSessionId)}try{const initManual="true"===document.getElementById("focusPlayerContainer")?.dataset?.manualFootball;window.MatchSidebarInfoApp?.setMatch?.(focusCurrentMatchId,!initManual&&Number(focusCurrentMatchId)>0)}catch(_){}initFocusChatHeightSync(),initFocusLiveUpdates();const liveCards=document.getElementById("liveMatchCards");liveCards&&liveCards.addEventListener("scroll",()=>{try{localStorage.setItem("focus_cards_scroll_live",String(liveCards.scrollLeft))}catch(e){}},{passive:!0});const upcomingCards=document.getElementById("upcomingMatchCards");upcomingCards&&upcomingCards.addEventListener("scroll",()=>{try{localStorage.setItem("focus_cards_scroll_upcoming",String(upcomingCards.scrollLeft))}catch(e){}},{passive:!0});checkFocusMutePermission(),video&&function initFocusPictureInPicture(video){if(!video||!document.pictureInPictureEnabled)return;if("undefined"!=typeof PersistentPlaybackManager)return;if("undefined"!=typeof PiPManager&&!focusPiPManager)return focusPiPManager=new PiPManager(video,{enableDesktopAutoPiP:!0,enableMobileAutoPiP:!0,onPiPEnter:()=>{isFocusPipActive=!0},onPiPExit:()=>{isFocusPipActive=!1}}),focusPiPManager.initialize(),void(window.focusPiPManager=focusPiPManager);const playerWrapper=document.querySelector(".focus-player-wrapper");if(!playerWrapper)return;const enableGesture=()=>{hasFocusUserGesture||(hasFocusUserGesture=!0)};["click","mousedown","mouseup","mousemove","touchstart","touchend","touchmove","keydown","keyup","keypress","wheel","scroll"].forEach(event=>{document.addEventListener(event,enableGesture,{once:!0,passive:!0})}),video.addEventListener("enterpictureinpicture",()=>{isFocusPipActive=!0}),video.addEventListener("leavepictureinpicture",()=>{isFocusPipActive=!1;playerWrapper.getBoundingClientRect().bottom<0&&(isFocusPipUserDisabled=!0)});let focusAutoPipActive=!1;const observer=new IntersectionObserver(entries=>{const entry=entries[0];if(video.paused||video.ended||!isStreamPlaying)return;if(!hasFocusUserGesture)return;if(video.readyState<1)return;isFocusPipActive&&entry.isIntersecting&&!focusAutoPipActive||(entry.isIntersecting?(isFocusPipActive&&focusAutoPipActive&&(document.exitPictureInPicture().catch(()=>{}),focusAutoPipActive=!1),isFocusPipUserDisabled=!1):isFocusPipActive||isFocusPipUserDisabled||(focusAutoPipActive=!0,video.requestPictureInPicture().catch(e=>{focusAutoPipActive=!1,e.message})))},{threshold:0,rootMargin:"0px"});observer.observe(playerWrapper)}(video);return function initFocusCardHoverPreload(){if("undefined"==typeof IVSPlayerAdapter)return;const cards=document.querySelectorAll(".focus-card, .focus-match-card");let sdkPreloaded=!1;cards.forEach(card=>{const streamingType=card.dataset.streamingType;"AWS_IVS"!==streamingType?.toUpperCase()&&"IVS"!==streamingType?.toUpperCase()||card.addEventListener("mouseenter",()=>{sdkPreloaded||"function"!=typeof loadIVSSDK||(sdkPreloaded=!0,loadIVSSDK().catch(()=>{}))})})}(),!0})()||await tryAutoPlayFirstLiveFocus(),function initVisibilityHandler(){if(focusVisibilityHandlerInitialized)return;focusVisibilityHandlerInitialized=!0;const video=document.getElementById("focusLivePlayer");if(!video)return;if("undefined"!=typeof PersistentPlaybackManager){const isFocusIvsActive=()=>!(!focusIvsAdapter||"destroyed"===focusIvsAdapter.getState())||!(!video.srcObject&&!video.src&&!focusLivekitRoom),resumeFocusPlayer=async targetVideo=>{const streamType=(lastFocusStreamingType||"").toUpperCase().replace(/_/g,"-");if(focusIvsAdapter&&("AWS-IVS"===streamType||"IVS"===streamType||"AWSIVS"===streamType)){const state=focusIvsAdapter.getState();if("buffering"===state){for(let i=0;i<15;
1i++)if(await new Promise(r=>setTimeout(r,200)),!targetVideo.paused&&targetVideo.readyState>=2)return!0;return!targetVideo.paused}if("ready"===state&&(await new Promise(r=>setTimeout(r,150)),!targetVideo.paused&&!targetVideo.ended))return!0;focusIvsAdapter.play();for(let i=0;i<12;i++)if(await new Promise(r=>setTimeout(r,200)),!targetVideo.paused&&targetVideo.readyState>=2)return!0;return!targetVideo.paused}try{return await targetVideo.play(),!0}catch(error){if("NotAllowedError"===error.name)return targetVideo.muted=!0,await targetVideo.play(),!0;throw error}};focusPersistentPlaybackManager=new PersistentPlaybackManager(video,{playerId:"focusLivePlayer",enableAutoPiP:!0,enableAutoResume:!0,enableMobileAutoPiP:!0,resumeDelay:500,showLoadingOnResume:!1,skipVisibilityHandling:void 0!==window.PcPlaybackVisibility&&window.PcPlaybackVisibility.isActive(),reconnectAttempts:2,reconnectDelay:800,isSourceActive:isFocusIvsActive,resumePlayer:resumeFocusPlayer,pausePlayer:async()=>{try{if(focusIvsAdapter&&"function"==typeof focusIvsAdapter.pause&&focusIvsAdapter.getState&&"destroyed"!==focusIvsAdapter.getState())return void focusIvsAdapter.pause()}catch(e){}video&&"function"==typeof video.pause&&video.pause()},getStreamInfo:()=>{const container=document.getElementById("focusPlayerContainer"),identifier=container?.dataset?.identifier||lastFocusIdentifier,isBeauty="true"===container?.dataset?.beauty||identifier&&String(identifier).startsWith("standalone-");return{streamType:lastFocusStreamingType||"WebRTC",matchId:isBeauty?identifier:focusCurrentMatchId}},reconnectStream:async(streamType,matchIdOrIdentifier)=>{isStreamPlaying=!1,setUserInitiatedPlay();const container=document.getElementById("focusPlayerContainer"),identifier=container?.dataset?.identifier||lastFocusIdentifier||matchIdOrIdentifier;return("true"===container?.dataset?.beauty||identifier&&String(identifier).startsWith("standalone-"))&&identifier?(destroyFocusPlayer(),lastFocusIdentifier=identifier,lastFocusStreamingType=streamType||"AWS-IVS",await tryLoadIvsStream(null,identifier)):(startFocusPlayback(matchIdOrIdentifier,streamType,identifier),!0)},onShowLoading:()=>{showFocusLoadingMessage()},onHideLoading:()=>{hideFocusLoadingMessage()},onError:error=>{console.error("[FocusPlayer] PersistentPlayback 错误:",error.message),showFocusPlayerError(error.message)}}),focusPersistentPlaybackManager.initialize(),window.focusPersistentPlaybackManager=focusPersistentPlaybackManager,window.focusPiPManager=focusPersistentPlaybackManager.pipManager||null,initPcFocusPlaybackVisibility(resumeFocusPlayer)}else void 0!==window.PcPlaybackVisibility&&window.PcPlaybackVisibility.isActive()?initPcFocusPlaybackVisibility(null):function initSimpleVisibilityHandler(){let mobileHidePauseTimer=null,wasPlayingBeforeHidden=!1;const isMobileUa=()=>/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)||"MacIntel"===navigator.platform&&navigator.maxTouchPoints>1;document.addEventListener("visibilitychange",function(){const video=document.getElementById("focusLivePlayer");video&&(document.hidden?(wasPlayingBeforeHidden=!video.paused&&!video.ended&&isStreamPlaying,mobileHidePauseTimer&&clearTimeout(mobileHidePauseTimer),isMobileUa()&&wasPlayingBeforeHidden&&(mobileHidePauseTimer=setTimeout(()=>{if(mobileHidePauseTimer=null,document.hidden)try{if(document.pictureInPictureElement===video)return;focusIvsAdapter&&"function"==typeof focusIvsAdapter.pause?focusIvsAdapter.pause():video.pause()}catch(e){}},2500))):(mobileHidePauseTimer&&(clearTimeout(mobileHidePauseTimer),mobileHidePauseTimer=null),setTimeout(()=>{const currentVideo=document.getElementById("focusLivePlayer");if(!currentVideo)return;const hasSource=currentVideo.srcObject||currentVideo.src||focusIvsAdapter&&focusIvsAdapter.getState&&"destroyed"!==focusIvsAdapter.getState(),isVideoBlack=!currentVideo.videoWidth||0===currentVideo.videoWidth,wasPlaying=wasPlayingBeforeHidden||isStreamPlaying&&focusCurrentMatchId;!wasPlaying||hasSource&&!isVideoBlack||focusLivekitRoom||focusIvsAdapter?wasPlaying&&currentVideo.paused&&(focusIvsAdapter&&"function"==typeof focusIvsAdapter.play?focusIvsAdapter.play():safeFocusPlay(currentVideo)):(isStreamPlaying=!1,lastFocusStreamingType&&reloadFocusStream()),wasPlayingBeforeHidden=!1},300)))})}()}(),function initPlayerMoreDropdown(){"function"==typeof window.initUnifiedPlayerMoreDropdown&&window.initUnifiedPlayerMoreDropdown()}
1(),document.querySelectorAll(".focus-cards-tabs .focus-tab[data-tab]").forEach(btn=>{btn.addEventListener("click",()=>{try{localStorage.setItem("focus_cards_active_tab",btn.dataset.tab)}catch(e){}})})},100)}),window.countFocusLiveCards=countFocusLiveCards,window.refreshFocusLiveCountFromDom=refreshFocusLiveCountFromDom,window.markFocusUserPickedTab=markSessionUserPickedTab,window.hasLiveWorldCupFocus=function hasLiveWorldCupFocus(){const section=document.getElementById("focusLiveSection");return"true"===section?.dataset.hasLiveWorldCup||"false"!==section?.dataset.hasLiveWorldCup&&function getFocusLiveWorldCupCount(){const container=document.getElementById("liveMatchCards");if(container){let count=0;return container.querySelectorAll(':scope > .focus-card-item[data-is-world-cup="true"]').forEach(item=>{isFocusCardItemLive(item)&&count++}),count}const section=document.getElementById("focusLiveSection");if(section?.dataset.liveWorldCupCount){const parsed=parseInt(section.dataset.liveWorldCupCount,10);if(!Number.isNaN(parsed))return parsed}return 0}()>0};let homeFocusState=parseHomeFocusStateFromLocation();function applyHomeFocusState(state,options){const opts="object"==typeof options&&null!==options?options:{};let category=toHomeCategory(state?.category),live=!!state?.live;"worldcup"!==category||hasFocusWorldCupCards()||(category="all"),live&&0===getFocusLiveCount()&&(live=!1),live&&(category="all"),homeFocusState={category:category,live:live},syncHomeFocusRouting(homeFocusState);const uiFilter=toUiFilter(category);document.querySelectorAll("#focusFilterGroup .focus-filter-btn").forEach(btn=>
1{btn.classList.toggle("active",!live&&btn.dataset.filter===uiFilter)});const liveBtn=document.getElementById("focusLiveBadge");liveBtn&&liveBtn.classList.toggle("active",live);const cardsContainer=document.getElementById("liveMatchCards");cardsContainer&&cardsContainer.querySelectorAll(".focus-card-item").forEach(item=>{item.style.display=function cardMatchesHomeFocus(item,state){if(!item)return!1;const isBeauty=null!==item.querySelector(".beauty-card"),isLive=isFocusCardItemLive(item);if(state.live&&!isLive)return!1;const uiFilter=toUiFilter(state.category);return"worldcup"===uiFilter?!isBeauty&&"true"===item.dataset.isWorldCup:"football"===uiFilter?!isBeauty&&"basketball"!==item.dataset.sportType:"basketball"===uiFilter?!isBeauty&&"basketball"===item.dataset.sportType:"beauty"!==uiFilter||isBeauty}(item,homeFocusState)?"":"none"});const upcomingWrapper=document.getElementById("upcomingCardsWrapper")||document.getElementById("upcomingMatchCards")?.closest(".focus-cards-wrapper");if(upcomingWrapper){const shouldShowUpcoming=null!==upcomingWrapper.querySelector(".focus-match-card")&&!live&&"beauty"!==uiFilter&&"worldcup"!==uiFilter&&"basketball"!==uiFilter;upcomingWrapper.classList.toggle("d-none",!shouldShowUpcoming)}try{localStorage.setItem("focus_filter_type",uiFilter),localStorage.setItem("focus_filter_live",live?"1":"0")}catch(e){console.warn("[FocusPlayer] 无法保存筛选状态:",e)}opts.userInitiated&&uiFilter&&markSessionUserPickedTab(uiFilter);const historyMode=opts.history||(opts.programmatic?"replace":"push");syncHomeFocusUrl(homeFocusState,historyMode),function applyHomeCategorySeo(category){const entry=window.homeFocusRouting?.seo?.[category]||window.homeFocusRouting?.seo?.all;entry&&(entry.title&&(document.title=entry.title,document.querySelector('meta[property="og:title"]')?.setAttribute("content",entry.title),document.querySelector('meta[name="twitter:title"]')?.setAttribute("content",entry.title)),entry.description&&(document.querySelector('meta[name="description"]')?.setAttribute("content",entry.description),document.querySelector('meta[property="og:description"]')?.setAttribute("content",entry.description),document.querySelector('meta[name="twitter:description"]')?.setAttribute("content",entry.description)),entry.canonical&&(document.getElementById("canonicalUrl")?.setAttribute("href",entry.canonical),document.getElementById("ogUrl")?.setAttribute("content",entry.canonical),document.getElementById("hreflangVi")?.setAttribute("href",entry.canonical)))}(category),"function"==typeof updateMatchSelectorLabelState&&updateMatchSelectorLabelState();const modal=document.getElementById("matchSelectorModal");modal?.classList.contains("active")&&"function"==typeof window.renderMatchSelectorCards&&window.renderMatchSelectorCards(),cardsContainer&&scheduleFocusCardsScrollUiRefresh(cardsContainer,!0);const upcomingContainer=document.getElementById("upcomingMatchCards"),upcomingWrapperEl=document.getElementById("upcomingCardsWrapper")||upcomingContainer?.closest(".focus-cards-wrapper");upcomingContainer&&upcomingWrapperEl&&!upcomingWrapperEl.classList.contains("d-none")&&scheduleFocusCardsScrollUiRefresh(upcomingContainer,!0)}function getFocusCardSortTier(item){const tier=item?.dataset?.sortTier;if(void 0!==tier&&""!==tier)return parseInt(tier,10);if(item.querySelector(".beauty-card"))return 1;const card=item.querySelector(".focus-card");return"true"===card?.dataset.hasStreamerTask?0:2}function isFocusCardPinned(item){return"true"===item?.dataset?.isFocusPinned}function isFocusCardLiveJumpPinned(item){return"true"===item?.dataset?.isLiveJumpPinned}function getFocusPinWeight(item){const weight=item?.dataset?.focusPinWeight;if(void 0===weight||""===weight)return 0;const parsed=parseInt(weight,10);return Number.isFinite(parsed)?parsed:0}function getFocusFollowRank(item){const rank=parseInt(item?.dataset?.followRank,10);if(Number.isFinite(rank))return rank;return"true"===item?.dataset?.isLive||!!item?.querySelector(".focus-card.is-live, .beauty-card.is-live")?1:3}function getFocusCardSortDate(item){const raw=item?.dataset?.sortTime||"";if(!raw)return"9999-12-31";const parsed=new Date(raw);if(!Number.isNaN(parsed.getTime()))try{return new Intl.DateTimeFormat("en-CA",{timeZone:"Asia/Ho_Chi_Minh",year:"numeric",month:"2-digit",day:"2-digit"}).format(parsed)}catch(e){return new Date(parsed.getTime()+252e5).toISOString().slice(0,10)}const matched=String(raw).match(/^(\d{4}-\d{2}-\d{2})/);return matched?matched[1]:"9999-12-31"}function getFocusPinSortZone(isFocusPinned,isLiveJumpPinned,isLive){return isLiveJumpPinned&&isLive?3:isFocusPinned?2:isLive?1:0}function compareFocusCardItems(a,b){const dateDiff=getFocusCardSortDate(a).localeCompare(getFocusCardSortDate(b));if(0!==dateDiff)return dateDiff;const followDiff=getFocusFollowRank(a)-getFocusFollowRank(b);if(0!==followDiff)return followDiff;const aLive="true"===a.dataset.isLive||a.querySelector(".focus-card.is-live, .beauty-card.is-live")?1:0,bLive="true"===b.dataset.isLive||b.querySelector(".focus-card.is-live, .beauty-card.is-live")?1:0,zoneDiff=getFocusPinSortZone(isFocusCardPinned(a),isFocusCardLiveJumpPinned(a),!!aLive)-getFocusPinSortZone(isFocusCardPinned(b),isFocusCardLiveJumpPinned(b),!!bLive);if(0!==zoneDiff)return-zoneDiff;const weightDiff=getFocusPinWeight(b)-getFocusPinWeight(a);if(0!==weightDiff)return weightDiff;const tierDiff=getFocusCardSortTier(a)-getFocusCardSortTier(b);return 0!==tierDiff?tierDiff:(a.dataset.sortTime||"9999").localeCompare(b.dataset.sortTime||"9999")}function findFirstLiveFocusCard(){const container=document.getElementById("liveMatchCards");if(!container)return null;const liveItems=Array.from(container.querySelectorAll(".focus-card-item")).filter(item=>"none"!==item.style.display&&isFocusCardItemLive(item));return 0===liveItems.length?null:(liveItems.sort(compareFocusCardItems),liveItems[0]?.querySelector(".focus-card, .beauty-card")||null)}function restoreFilterState(){try{applyHomeFocusState(parseHomeFocusStateFromLocation(),{programmatic:!0,history:"replace"})}catch(e){console.error("[FocusPlayer] 恢复筛选状态失败:",e)}}function initializeAllCardsLiveStatus(){const focusDataElement=document.getElementById("focusMatchesData");if(focusDataElement)try{const focusMatches=JSON.parse(focusDataElement.textContent);
1focusMatches.length,focusMatches.forEach(match=>{const cardId=match.streamSessionId?`${match.id}_${match.streamSessionId}`:String(match.id),shouldShowLive=match.isStreamerLiveNow;updateCardLiveStatus(match.id,shouldShowLive,match.hasStreamerTask,cardId)}),function optimizeAllExistingImages(){const allImages=document.querySelectorAll('img[src*="api-sports.io"]');allImages.forEach(img=>{const originalSrc=img.src,optimizedUrl=optimizeImageUrl(originalSrc,"teams");optimizedUrl!==originalSrc&&(img.dataset.originalUrl=originalSrc,img.src=optimizedUrl)}),allImages.length}()}catch(e){console.error("[FocusPlayer] 解析 focusMatchesData 失败:",e)}}function updateCardLiveStatus(matchId,isLive,hasStreamerTask,cardId=null){if(cardId){const card=document.querySelector(`.focus-card[data-card-id="${cardId}"], .focus-match-card[data-card-id="${cardId}"]`);if(!card)return void console.warn(`未找到卡片: ${cardId}`);updateSingleCardLiveStatus(card,matchId,isLive,hasStreamerTask,cardId)}else{const cards=document.querySelectorAll(`.focus-card[data-match-id="${matchId}"], .focus-match-card[data-match-id="${matchId}"]`);if(0===cards.length)return void console.warn(`未找到比赛 ${matchId} 的任何卡片`);cards.length,cards.forEach(card=>{const cardId=card.dataset.cardId;updateSingleCardLiveStatus(card,matchId,isLive,hasStreamerTask,cardId)})}}function updateSingleCardLiveStatus(card,matchId,isLive,hasStreamerTask,cardId){const containerId=cardId?`match_live_badge_${cardId}`:`match_live_badge_${matchId}`,container=document.getElementById(containerId);if(!container)return void console.warn(`未找到徽章容器: ${containerId}`);const focusSection=document.getElementById("focusLiveSection"),liveBadgeText=focusSection?.dataset?.liveBadgeText||"LIVE";if(isLive){const cardItem=card.closest(".focus-card-item"),isWorldCup="true"===cardItem?.dataset.isWorldCup||"function"==typeof window.isFocusWorldCupMatch&&window.isFocusWorldCupMatch(card.dataset.leagueApiId,card.dataset.league),liveBadgeLabel="function"==typeof window.buildFocusLiveBadgeLabel?window.buildFocusLiveBadgeLabel(isWorldCup,liveBadgeText):`<span class="badge bg-danger">${liveBadgeText}</span>`;container.innerHTML=`\n                <div class="focus-card-badge">\n                    ${liveBadgeLabel}\n                </div>\n                <div class="focus-card-badge focus-card-badge-right">\n                    <span class="badge focus-time-badge" data-match-time="${matchId}">--:--</span>\n                </div>\n            `}else container.innerHTML=`\n                <button type="button" class="focus-reminder-btn" aria-label="${window.L?.T?window.L.T("home.setReminder"):"Set Reminder"}" data-match-id="${matchId}" data-text-set="${window.L?.T?window.L.T("home.setReminder"):"Set Reminder"}" data-text-cancel="${window.L?.T?window.L.T("home.cancelReminder"):"Cancel Reminder"}" onclick="event.stopPropagation(); toggleMatchReminder(event, this);">\n                    <i class="fas fa-bell" aria-hidden="true"></i>\n                </button>\n                <div class="focus-card-badge focus-card-badge-right">\n                    <span class="badge focus-time-badge" data-match-time="${matchId}">--:--</span>\n                </div>\n            `;const timeElement=container.querySelector(".focus-time-badge");if(timeElement&&card.dataset.matchDate){const matchDate=new Date(card.dataset.matchDate);if(!isNaN(matchDate.getTime())){const options={day:"2-digit",month:"2-digit",hour:"2-digit",minute:"2-digit",hour12:!1,timeZone:"Asia/Ho_Chi_Minh"},timeStr=matchDate.toLocaleString("en-GB",options).replace(",","");timeElement.textContent=timeStr}}card.querySelectorAll(".focus-card-team img").forEach(img=>{const originalUrl=img.dataset.originalUrl||img.src;if(originalUrl&&originalUrl.includes("api-sports.io")){const optimizedUrl=optimizeImageUrl(originalUrl,"teams");optimizedUrl!==originalUrl&&(img.dataset.originalUrl=originalUrl,img.src=optimizedUrl)}}),card.classList.toggle("is-live",!!isLive);const cardItem=card.closest(".focus-card-item");if(cardItem){cardItem.dataset.isLive=isLive?"true":"false";const sid=card.dataset.streamerId;cardItem.dataset.followRank=String(window.StreamerFollow?.computeFollowRank?window.StreamerFollow.computeFollowRank(sid,!!isLive):isLive?1:3),cardItem.classList.toggle("is-followed",!(!window.StreamerFollow?.isFollowing||!window.StreamerFollow.isFollowing(sid)))}"function"==typeof window.sortFocusCardsByTime&&window.sortFocusCardsByTime(),"function"==typeof window.refreshFocusLiveCountFromDom&&window.refreshFocusLiveCountFromDom()}function findFirstFocusCardForChat(){const container=document.getElementById("liveMatchCards");if(!container)return null;const items=Array.from(container.querySelectorAll(".focus-card-item")).filter(item=>"none"!==item.style.display);return 0===items.length?null:(items.sort(compareFocusCardItems),items[0]?.querySelector(".focus-card, .beauty-card")||null)}function clearFocusDeepLinkParams(){try{const url=new URL(window.location.href);["focusMatch","focusSession","match","session"].forEach(key=>url.searchParams.delete(key));const next=url.pathname+url.search+url.hash;window.history.replaceState({},"",next||url.pathname)}catch(_){}}function applyFocusDeepLink(){const link=function getFocusDeepLinkParams(){const params=new URLSearchParams(window.location.search),matchIdRaw=params.get("focusMatch")||params.get("match");if(!matchIdRaw)return null;const matchId=parseInt(matchIdRaw,10);return isNaN(matchId)?null:{matchId:matchId,streamSessionId:params.get("focusSession")||params.get("session")||void 0}}();if(!link)return!1;const cardId=link.streamSessionId?`${link.matchId}_${link.streamSessionId}`:link.matchId.toString(),card=findStoredFocusCard(cardId);if(!card)return console.warn("[FocusPlayer] 深链卡片未找到:",cardId),clearFocusDeepLinkParams(),!1;initFocusChatHeightSync(),initFocusLiveUpdates(),applyHomeFocusState({category:"all",live:!1},{programmatic:!0,history:"replace"});const liveCards=document.getElementById("liveMatchCards");return liveCards&&setTimeout(()=>scrollActiveCardToLeft(card,liveCards),50),isBeautyCardElement(card)?switchFocusBeauty(card,beginFocusSwitch()):switchFocusMatch(link.matchId,link.streamSessionId),function scrollToFocusSection(){const focusSection=document.getElementById("focusLiveSection");focusSection&&setTimeout(()=>{focusSection.scrollIntoView({behavior:"smooth",block:"start"})},120)}(),clearFocusDeepLinkParams(),!0}function startFocusPlayback(matchId,streamingType,identifier){hideFocusPlayerError(),hideFocusNotLiveOverlay(),hideFocusPlayOverlay(),destroyFocusPlayer();const playbackCard=findFocusCardForPlayback(document.getElementById("focusPlayerContainer"));playbackCard&&updateFocusPlayerCoverFromCard(playbackCard),showFocusLoadingMessage(),ensureFocusVideoMuted();const video=document.getElementById("focusLivePlayer");if(video){const onPlaying=()=>{hideFocusPlayOverlay(),hideFocusPlayerError(),hideFocusLoadingMessage(),video.removeEventListener("playing",onPlaying)};video.addEventListener("playing",onPlaying)}loadFocusStreamWithRetry(matchId,streamingType,identifier)}function hideFocusPlayOverlay(){const overlay=document.getElementById("focusPlayOverlay");overlay&&(overlay.style.display="none")}syncHomeFocusRouting(homeFocusState),window.__homeFocusPopstateBound||(window.__homeFocusPopstateBound=!0,window.addEventListener("popstate",()=>{(document.getElementById("focusFilterGroup")||document.getElementById("focusLiveBadge"))&&applyHomeFocusState(parseHomeFocusStateFromLocation(),{programmatic:!0,history:"none"})})),window.updateFocusLiveButtonState=function(count){const normalizedCount=Math.max(0,0|count),btn=document.getElementById("focusLiveBadge");btn&&(normalizedCount<=0?(btn.classList.add("no-animation"),btn.disabled=!0,btn.setAttribute("aria-disabled","true"),homeFocusState.live&&applyHomeFocusState({category:homeFocusState.category,live:!1},{programmatic:!0})):(btn.classList.remove("no-animation"),btn.disabled=!1,btn.removeAttribute("aria-disabled"))),document.querySelectorAll('[data-filter-count="live"]').forEach(el=>{normalizedCount>0?(el.textContent=normalizedCount,el.hidden=!1):el.hidden=!0})},window.updateFocusFilterCounts=function(){const container=document.getElementById("liveMatchCards");if(!container)return;let football=0,basketball=0,beauty=0,worldcup=0,liveWorldcup=0;container.querySelectorAll(":scope > .focus-card-item").forEach(item=>{item.querySelector(".beauty-card")?beauty++:"basketball"===item.dataset.sportType?basketball++:(football++,"true"===item.dataset.isWorldCup&&(worldcup++,isFocusCardItemLive(item)&&liveWorldcup++))});const section=document.getElementById("focusLiveSection");section&&(section.dataset.hasLiveWorldCup=liveWorldcup>0?"true":"false",section.dataset.liveWorldCupCount=String(liveWorldcup)),document.querySelectorAll('[data-filter-count="football"]').forEach(el=>{football>0?(el.textContent=football,el.hidden=!1):el.hidden=!0}),document.querySelectorAll('[data-filter-count="basketball"]').forEach(el=>{basketball>0?(el.textContent=basketball,el.hidden=!1):el.hidden=!0}),document.querySelectorAll('[data-filter-count="beauty"]').forEach(el=>{beauty>0?(el.textContent=beauty,el.hidden=!1):el.hidden=!0}),document.querySelectorAll('[data-filter-count="worldcup"]').forEach(el=>{worldcup>0?(el.textContent=worldcup,el.hidden=!1):el.hidden=!0}),function updateFocusWorldCupTabVisibility(count){const normalized=Math.max(0,0|count);document.querySelectorAll('.focus-filter-btn[data-filter="worldcup"], .match-selector-label[data-filter="worldcup"]').forEach(el=>{el.classList.toggle("d-none",normalized<=0)});const section=document.getElementById("focusLiveSection");section&&(section.dataset.hasWorldCup=normalized>0?"true":"false")}(worldcup);document.querySelector('.focus-filter-btn.active[data-filter="worldcup"]')&&0===worldcup&&applyHomeFocusState({category:"all",live:homeFocusState.live},{programmatic:!0})},window.sortFocusCardsByTime=function(container){const root=container||document.getElementById("liveMatchCards");if(!root)return;const items=Array.from(root.querySelectorAll(":scope > .focus-card-item"));
1items.length<2||(items.sort(compareFocusCardItems),items.forEach(item=>root.appendChild(item)),"function"==typeof window.updateFocusFilterCounts&&window.updateFocusFilterCounts(),"function"==typeof window.updateFocusNavArrows?window.updateFocusNavArrows(root):"function"==typeof window.updateFocusCardsScrollbar&&window.updateFocusCardsScrollbar(root),"function"==typeof window.applyBlvToFocusCardStreamers&&window.applyBlvToFocusCardStreamers(root))},window.getFocusIvsAdapter=()=>focusIvsAdapter,window.getFocusHlsInstance=()=>focusHlsInstance;let isFocusPipActive=!1,isFocusPipUserDisabled=!1,hasFocusUserGesture=!1,focusPiPManager=null;new Map;let focusVisibilityHandlerInitialized=!1,focusPersistentPlaybackManager=null;function isFocusWatchingOrWaiting(){if(isFocusPlayerActuallyPlaying())return!0;const container=document.getElementById("focusPlayerContainer");return!!container&&(!!container.classList.contains("focus-no-stream")||(!!container.querySelector(".focus-not-live-overlay, .focus-error-overlay")||(!!document.querySelector(".flip-countdown")||(!!focusPublisherPollingTimer||!!container.querySelector(".focus-stream-starting-overlay")))))}function pcShortHideFocus(){stopFocusPublisherPolling(),focusPersistentPlaybackManager&&focusPersistentPlaybackManager.notifyPageHidden()}async function resumeFocusAfterShortHide(resumeFocusPlayerFn){if(focusPersistentPlaybackManager&&isFocusStreamStillConnected()){if(await focusPersistentPlaybackManager.resumeAfterShortHide(()=>focusIvsAdapter?.getState?.()))return isStreamPlaying=!0,hideFocusPlayOverlay(),void hideFocusLoadingMessage()}await async function softResumeFocusPlayer(resumeFocusPlayerFn){const video=document.getElementById("focusLivePlayer");if(!video)return;const streamType=(lastFocusStreamingType||"").toUpperCase().replace(/_/g,"-"),isIvs="AWS-IVS"===streamType||"IVS"===streamType||"AWSIVS"===streamType;focusIvsAdapter&&isIvs&&"function"==typeof focusIvsAdapter.play&&focusIvsAdapter.play();if("function"==typeof resumeFocusPlayerFn)return void await resumeFocusPlayerFn(v
1ideo);video.paused&&safeFocusPlay(video)}(resumeFocusPlayerFn);const streamType=(lastFocusStreamingType||"").toUpperCase();focusCurrentMatchId&&"WEBRTC"===streamType&&function startFocusPublisherPolling(matchId){if(stopFocusPublisherPolling(),window.PcPlaybackVisibility?.isPageHidden()&&window.PcPlaybackVisibility?.shouldManageCurrentStream())return;showFocusStreamStartingOverlay(),focusPublisherTimeoutTimer=setTimeout(()=>{stopFocusPublisherPolling(),hideFocusStreamStartingOverlay();showFocusPlayerError(window.L?.T?window.L.T("match.player.streamTimeout"):"直播启动超时,请稍后重试",!0)},FOCUS_PUBLISHER_TIMEOUT),focusPublisherPollingTimer=setInterval(async()=>{if(!window.PcPlaybackVisibility?.isPageHidden()||!window.PcPlaybackVisibility?.shouldManageCurrentStream())try{const response=await fetch(`/match/stream-check/${matchId}`);if(!response.ok)return void console.warn("[FocusPublisherPolling] 检查失败:",response.status);const result=await response.json();if(isAllSourcesFailedResult(result))return stopFocusPublisherPolling(),void handleAllSourcesFailed();const publishers=Number(result.publishers??0);publishers>0&&(stopFocusPublisherPolling(),hideFocusStreamStartingOverlay(),hideFocusPlayOverlay(),focusHardNoPublisher=!1,loadFocusWebRtcStream(matchId).then(()=>{isStreamPlaying=!0,hideFocusPlayerError()}).catch(e=>{console.error("[FocusPublisherPolling] 播放失败:",e)}))}catch(e){console.error("[FocusPublisherPolling] 轮询异常:",e)}},FOCUS_PUBLISHER_POLL_INTERVAL)}(focusCurrentMatchId)}function pcLongHideTeardownFocus(){stopFocusPublisherPolling(),isStreamPlaying=!1,destroyFocusPlayer();const video=document.getElementById("focusLivePlayer");if(video)try{video.pause(),video.removeAttribute("src"),video.src="",video.srcObject=null,video.load()}catch(e){console.warn("[FocusPlayer] 长隐藏 teardown 重置 video 失败:",e)}}async function resumeFocusAfterTeardown(){await new Promise(r=>setTimeout(r,500)),hideFocusPlayerError(),hideFocusNotLiveOverlay(),hideFocusPlayOverlay(),setUserInitiatedPlay(),focusRetryCount=0,clearFocusRetryTimer();const container=document.getElementById("focusPlayerContainer");if(!container)return;const identifier=container.dataset.identifier||lastFocusIdentifier,streamingType=container.dataset.streaming||container.dataset.streamingType||lastFocusStreamingType||"WebRTC",isBeauty="true"===container.dataset.beauty,video=document.getElementById("focusLivePlayer");if(video){const onPlaying=()=>{hideFocusPlayOverlay(),hideFocusPlayerError(),hideFocusLoadingMessage(),video.removeEventListener("playing",onPlaying)};video.addEventListener("playing",onPlaying)}const playbackCard=findFocusCardForPlayback(container);if(playbackCard&&updateFocusPlayerCoverFromCard(playbackCard),showFocusLoadingMessage(),ensureFocusVideoMuted(),isBeauty){if(!identifier)return void console.warn("[FocusPlayer] teardown resume: 美女直播无 identifier");const beautyStreamingType="WebRTC"===streamingType?"AWS-IVS":streamingType;return lastFocusIdentifier=identifier,lastFocusStreamingType=beautyStreamingType,void await tryLoadStream(null,beautyStreamingType,identifier)}const rawMatchId=container.dataset.matchId,parsed=rawMatchId?Number(rawMatchId):NaN,matchId=Number.isFinite(parsed)?parsed:focusCurrentMatchId;Number.isFinite(matchId)?await loadFocusStreamWithRetry(matchId,streamingType,identifier):console.warn("[FocusPlayer] teardown resume: 无效 matchId")}function isFocusStreamStillConnected(){const video=document.getElementById("focusLivePlayer");return!(!focusIvsAdapter||!focusIvsAdapter.getState||"destroyed"===focusIvsAdapter.getState())||(!(!focusLivekitRoom||"connected"!==focusLivekitRoom.state)||!!(video&&(video.srcObject||video.src)&&video.readyState>=1))}function initPcFocusPlaybackVisibility(resumeFocusPlayerFn){void 0!==window.PcPlaybackVisibility&&(window.PcPlaybackVisibility.registerFocusHandlers({getStreamingType:()=>lastFocusStreamingType,isWatchingOrWaiting:isFocusWatchingOrWaiting,isStreamStillConnected:isFocusStreamStillConnected,onShortHidePause:pcShortHideFocus,onLongHideTeardown:pcLongHideTeardownFocus,onResumeAfterShortHide:()=>resumeFocusAfterShortHide(resumeFocusPlayerFn),onResumeAfterTeardown:resumeFocusAfterTeardown,onResumePendingAutoPlay:async()=>{isFocusStreamStillConnected()?await resumeFocusAfterShortHide(resumeFocusPlayerFn):"function"==typeof window.tryAutoPlayFirstLiveFocus&&await window.tryAutoPlayFirstLiveFocus()}}),window.PcPlaybackVisibility.init())}function safeFocusPlay(video){video&&(isFocusPlayingInProgress&&(isFocusPlayingInProgress=!1),video.paused&&(isFocusPlayingInProgress=!0,video.play().then(()=>{isFocusPlayingInProgress=!1}).catch(e=>
1{e.message,isFocusPlayingInProgress=!1})))}function reloadFocusStream(){const matchId=focusCurrentMatchId;if(!matchId)return void console.warn("[FocusPlayer] 无法重试:没有当前比赛ID");const container=document.getElementById("focusPlayerContainer"),identifier=container?.dataset?.identifier;setUserInitiatedPlay(),startFocusPlayback(matchId,lastFocusStreamingType||"WebRTC",identifier)}function fallbackCopyFocusLink(url){const textarea=document.createElement("textarea");textarea.value=url,textarea.style.position="fixed",textarea.style.opacity="0",document.body.appendChild(textarea),textarea.select();try{document.execCommand("copy"),"function"==typeof showToast&&showToast(window.L?.T?window.L.T("common.linkCopied"):"链接已复制到剪贴板","success")}catch(e){console.error("复制失败:",e)}document.body.removeChild(textarea)}async function loadFocusStreamWithRetry(matchId,streamingType,identifier){focusCurrentMatchId=matchId,lastFocusStreamingType=streamingType,lastFocusIdentifier=identifier,isStreamPlaying=!1,focusHardNoPublisher=!1,focusCf409Handled=!1,focusRetryCount=0,clearFocusRetryTimer(),await attemptFocusPlayback(matchId,streamingType,identifier)}async function attemptFocusPlayback(matchId,streamingType,identifier){if(!matchId)return console.error("[FocusPlayer] matchId为空,无法播放"),void showFocusPlayerError("比赛ID无效",!0);if(focusRetryCount++,"WEBRTC"===streamingType?.toUpperCase()){!function prefetchWebRtcFast(matchId){matchId&&Promise.allSettled([ensureLiveKitSdkLoaded(),getWebRtcTokenFast(matchId)])}(matchId);const startResult=await async function startFocusStream(matchId){try{const response=await fetch(`/match/start-stream/${matchId}`,{method:"POST"}),result=await response.json();return result}catch(e){return console.error("[FocusPlayer] 启动流失败:",e),{success:!1,status:"error",message:"无法启动流"}}}(matchId);if(!startResult.success){const isNotLiveYet=!0===startResult.notInTimeWindow||"not_in_window"===startResult.status||"player.notLiveYet"===startResult.message||startResult.message?.includes("notLiveYet"),isStreamerNotStarted="failed"===startResult.status&&("player.streamerNotStarted"===startResult.message||startResult.message?.includes("streamerNotStarted")||startResult.message?.includes("主播未开始推流"));if(isNotLiveYet){return showFocusPlayerError(startResult.message?translateServerMessage(startResult.message):window.L?.T?window.L.T("match.player.notLiveYet"):"比赛暂未开始直播",!1,!1,!0),clearFocusRetryTimer(),void(focusRetryCount=0)}if(isStreamerNotStarted){return showFocusPlayerError(startResult.message?translateServerMessage(startResult.message):window.L?.T?window.L.T("match.player.streamerNotStarted"):"主播未开始推流",!1,!1,!0),clearFocusRetryTimer(),void(focusRetryCount=0)}if(isAllSourcesFailedResult(startResult))return void handleAllSourcesFailed();startResult.message?translateServerMessage(startResult.message):window.L?.T&&window.L.T("match.player.streamStartFailed");if(startResult.message,focusRetryCount<3)focusRetryTimer=setTimeout(()=>{attemptFocusPlayback(matchId,streamingType,identifier)},3e3);else{showFocusPlayerError(window.L?.T?window.L.T("match.player.streamTimeout"):"直播连接失败,请重试",!0)}return}const publishers=Number(startResult.publishers??0),status=startResult.status;if(isAllSourcesFailedResult(startResult))return void handleAllSourcesFailed();if(publishers>0||"ready"===status){startPlaybackTimeout(matchId);try{await loadFocusWebRtcStream(matchId),isStreamPlaying=!0,focusRetryCount=0,clearFocusRetryTimer(),clearPlaybackTimeout(),hideFocusPlayerError()}catch(e){if(focusRetryCount<3)focusRetryTimer=setTimeout(()=>{attemptFocusPlayback(matchId,streamingType,identifier)},3e3);else{showFocusPlayerError(window.L?.T?window.L.T("match.player.streamTimeout"):"直播连接失败,请重试",!0)}}return}if("starting"===status){showFocusStreamStartingOverlay();let pollCount=0;const maxPolls=13,pollInterval=setInterval(async()=>{pollCount++;try{const checkResult=await checkFocusStreamStatus(matchId);if(isAllSourcesFailedResult(checkResult))return clearInterval(pollInterval),void handleAllSourcesFailed();const currentPublishers=Number(checkResult.publishers??0);if(currentPublishers>0)return clearInterval(pollInterval),hideFocusStreamStartingOverlay(),hideFocusPlayOverlay(),focusHardNoPublisher=!1,void loadFocusWebRtcStream(matchId).then(()=>{isStreamPlaying=!0,hideFocusPlayerError()}).catch(e=>{console.error("[FocusPlayer] 播放失败:",e)})}catch(e){console.error("[FocusPlayer] 轮询异常:",e)}if(pollCount>=maxPolls){clearInterval(pollInterval),hideFocusStreamStartingOverlay();try{const finalCheck=await checkFocusStreamStatus(matchId);if(isAllSourcesFailedResult(finalCheck))return void handleAllSourcesFailed();const finalPublishers=Number(finalCheck.publishers??0);if(finalPublishers>0)hideFocusPlayOverlay(),focusHardNoPublisher=!1,loadFocusWebRtcStream(matchId).then(()=>{isStreamPlaying=!0,hideFocusPlayerError()}).catch(e=>{console.error("[FocusPlayer] 播放失败:",e)});else{showFocusPlayerError(window.L?.T?window.L.T("match.player.streamTimeout"):"直播启动超时,请稍后重试",!0)}}catch(e){console.error("[FocusPlayer] 最终状态检查异常:",e);showFocusPlayerError(window.L?.T?window.L.T("match.player.streamTimeout"):"直播启动超时,请稍后重试",!0)}}},2e3);return}startResult.message?translateServerMessage(startResult.message):window.L?.T&&window.L.T("match.player.streamError");return void startPlaybackTimeout(matchId)}clearPlaybackTimeout(),hasDefinitiveError=!1,startPlaybackTimeout(matchId);
1if(await tryLoadStream(matchId,streamingType,identifier))isStreamPlaying=!0,clearPlaybackTimeout();else{if(clearPlaybackTimeout(),focusCf409Handled)return void(focusCf409Handled=!1);if(focusHardNoPublisher)return;if(window.focusIsSilentRetry)return!1;showFocusPlayerErrorSmart()}}function startPlaybackTimeout(matchId){clearPlaybackTimeout(),playbackTimeoutTimer=setTimeout(()=>{const video=document.getElementById("focusLivePlayer");video&&!video.paused&&video.readyState>=2&&video.videoWidth>0||isStreamPlaying||(stopFocusPublisherPolling(),window.focusIsSilentRetry||showFocusPlayerError(window.L?.T?window.L.T("match.player.streamTimeout"):"直播启动超时,请稍后重试",!0))},2e4)}function clearPlaybackTimeout(){playbackTimeoutTimer&&(clearTimeout(playbackTimeoutTimer),playbackTimeoutTimer=null)}async function checkFocusStreamStatus(matchId){try{const response=await fetch(`/match/stream-check/${matchId}`);return await response.json()}catch(e){return{success:!1,status:"error",message:"无法检查流状态"}}}async function tryLoadStream(matchId,streamingType,identifier){try{const type=streamingType?.toUpperCase()?.replace("-","_")||"HLS";return"WEBRTC"===type?await async function tryLoadWebRtcStream(matchId){try{const statusResult=await checkFocusStreamStatus(matchId);if(statusResult&&statusResult.streamType&&"WEBRTC"===String(statusResult.streamType).toUpperCase()){if(0===Number(statusResult.publishers??0))return!1}const tokenResponse=await fetch(`/api/webrtc/token/${matchId}`);if(!tokenResponse.ok)return showFocusPlayerErrorSmart(),!1;const tokenData=await tokenResponse.json();if(!tokenData.success||!tokenData.token)return showFocusPlayerErrorSmart(),!1;return document.getElementById("focusLivePlayer")?(destroyFocusPlayer(),await loadFocusWebRtcStream(matchId),hideFocusPlayerError(),isStreamPlaying=!0,!0):(showFocusPlayerErrorSmart(),!1)}catch(e){return showFocusPlayerErrorSmart(),!1}}(matchId):"AWS_IVS"===type||"AWSIVS"===type||"IVS"===type?await tryLoadIvsStream(matchId,identifier):"MUX"===type?await tryLoadMuxStream(matchId,identifier):"CLOUDFLARE_STREAM"===type||"CLOUDFLARESTREAM"===type||"CLOUDFLARE"===type||"CF"===type?await async function tryLoadCloudflareStream(matchId,identifier){try{if("undefined"==typeof initCloudflareWhepPlayer)return console.error("[FocusPlayer] Cloudflare WHEP 播放器未加载"),showFocusPlayerError("播放器加载失败,请刷新页面重试",!1,!1,!0),!1;const effectiveIdentifier=identifier||matchId,qualityOptions=await async function getCfQualityOptions(identifier){try{const response=await fetch(`/api/fixtures/cf-quality-options/${identifier}`);if(!response.ok)return response.status,null;const data=await response.json();return data.success&&data.data&&data.data.length>0?(currentCfQualityOptions=data.data,currentCfQuality=data.data[0].quality,data.data,data.data):null}catch(e){return console.error("[FocusPlayer] 获取 CF 清晰度选项失败:",e),null}}(effectiveIdentifier);let whepUrl=null;if(qualityOptions&&qualityOptions.length>0?(whepUrl=qualityOptions[0].whepUrl,function showCfQualitySelector(options,matchId){if(void 0===window.QualitySelector)return void console.error("[FocusPlayer] QualitySelector 类未找到!");const existing=document.getElementById("cfQualitySelector");existing&&existing.remove();const selector=document.createElement("div");selector.id="cfQualitySelector",selector.className="cf-quality-selector-container";const actionsContainer=document.querySelector(".focus-player-actions"),playerContainer=document.querySelector(".focus-player-container")||document.getElementById("focusLivePlayer")?.parentElement;if(actionsContainer){const insertAnchor=actionsContainer.querySelector("#focusMatchDetailLink")||actionsContainer.querySelector(".player-more-dropdown")||actionsContainer.firstChild;actionsContainer.insertBefore(selector,insertAnchor)}else{if(!playerContainer)return void console.error("[FocusPlayer] 找不到合适的容器插入选择器!");"static"===getComputedStyle(playerContainer).position&&(playerContainer.style.position="relative"),playerContainer.appendChild(selector)}const qualities=options.map(opt=>({quality:opt.quality,label:opt.displayName,whepUrl:opt.whepUrl}));cfQualitySelectorInstance=new window.QualitySelector(selector,{qualities:qualities,currentQuality:currentCfQuality||qualities[0].quality,onChange:async(quality,whepUrl)=>{currentCfQuality=quality;const video=document.getElementById("focusLivePlayer");if(video&&whepUrl)try{await initCloudflareWhepPlayer(video,whepUrl),video.play().catch(e=>console.warn("[FocusPlayer] 自动播放失败:",e.message))}catch(e){console.error("[FocusPlayer] 切换清晰度失败:",e.message)}}}),options.length}(qualityOptions)):(whepUrl=await async function getCloudflareWhepUrl(identifier){try{identifier.toString().split("_")[0];if(!identifier.toString().includes("_")){const metaPlayUrl=document.querySelector('meta[name="match-play-url"]')?.content;if(metaPlayUrl&&metaPlayUrl.includes("cloudflarestream.com"))return metaPlayUrl}const response=await fetch(`/api/stream/info/${identifier}`);if(!response.ok)return console.warn("[FocusPlayer] 获取流信息失败:",response.status),null;const data=await response.json(),whepUrl=data.playUrl||data.whepUrl||null;return whepUrl}catch(e){return console.error("[FocusPlayer] 获取 Cloudflare WHEP 地址异常:",e.message),null}}(effectiveIdentifier),function hideCfQualitySelector(){const selector=document.getElementById("cfQualitySelector");selector&&selector.remove();cfQualitySelectorInstance=null,cfQualityMenuOpen=!1}()),!whepUrl)return console.warn("[FocusPlayer] 无法获取 Cloudflare WHEP 播放地址"),showFocusPlayerErrorSmart(),!1;const video=document.getElementById("focusLivePlayer");return video?(await initCloudflareWhepPlayer(video,whepUrl),new Promise(resolve=>{cfLoadTimeoutTimer=setTimeout(()=>{showFocusPlayerErrorSmart(),resolve(!1)},15e3);const onPlaying=()=>{cfLoadTimeoutTimer&&(clearTimeout(cfLoadTimeoutTimer),cfLoadTimeoutTimer=null),video.removeEventListener("playing",onPlaying),hideFocusPlayerError(),hideFocusLoadingMessage(),isStreamPlaying=!0,video.classList.add("loaded"),resolve(!0)};video.addEventListener("playing",onPlaying)})):(console.warn("[FocusPlayer] 视频元素不存在"),showFocusPlayerErrorSmart(),!1)}catch(e){console.error("[FocusPlayer] Cloudflare Stream 加载异常:",e.message);return 409===e.status||e.isWaitingForStream||e.message&&e.message.includes("409")||show
1FocusPlayerErrorSmart(),!1}}(matchId,identifier):await async function tryLoadHlsStream(matchId){const hlsUrl=`/hls/${matchId}/index.m3u8`;try{if(!(await fetch(hlsUrl,{method:"HEAD"})).ok)return showFocusPlayerErrorSmart(),!1;const video=document.getElementById("focusLivePlayer");return video?(destroyFocusPlayer(),loadFocusHlsStream(hlsUrl,matchId),new Promise(resolve=>{const timeout=setTimeout(()=>{showFocusPlayerErrorSmart(),resolve(!1)},3e3);focusHlsInstance?(focusHlsInstance.on(Hls.Events.MANIFEST_PARSED,()=>{clearTimeout(timeout),hideFocusPlayerError(),isStreamPlaying=!0,resolve(!0)}),focusHlsInstance.on(Hls.Events.ERROR,(event,data)=>{data.fatal&&(clearTimeout(timeout),showFocusPlayerErrorSmart(),resolve(!1))})):(video.addEventListener("loadeddata",()=>{clearTimeout(timeout),hideFocusPlayerError(),isStreamPlaying=!0,resolve(!0)},{once:!0}),video.addEventListener("error",()=>{clearTimeout(timeout),showFocusPlayerErrorSmart(),resolve(!1)},{once:!0}))})):(showFocusPlayerError(),!1)}catch(e){return showFocusPlayerErrorSmart(),!1}}(matchId)}catch(e){return e.message,showFocusPlayerErrorSmart(),!1}}window.reloadFocusStream=reloadFocusStream,window.resyncFocusLiveStream=async function resyncFocusLiveStream(){const matchId=focusCurrentMatchId,video=document.getElementById("focusLivePlayer");if(!matchId||!video)return!1;const streamingType=(lastFocusStreamingType||document.getElementById("focusPlayerContainer")?.dataset?.streamingType||"WebRTC").toUpperCase().replace("-","_");if("WEBRTC"===streamingType||"LIVEKIT"===streamingType){if(focusLivekitRoom){try{await focusLivekitRoom.disconnect()}catch(e){console.warn("[FocusPlayer] LIVE 同步断开旧连接失败:",e)}focusLivekitRoom=null}return await loadFocusWebRtcStream(matchId,{forceReconnect:!0}),!0}if("AWS_IVS"===streamingType||"AWSIVS"===streamingType||"IVS"===streamingType)return!!focusIvsAdapter?.seekToLive?.()&&(focusIvsAdapter.play?.(),!0);const hls=focusHlsInstance||("function"==typeof window.getFocusHlsInstance?window.getFocusHlsInstance():null);if(hls&&Number.isFinite(hls.liveSyncPosition)&&hls.liveSyncPosition>0)return video.currentTime=hls.liveSyncPosition,await video.play().catch(()=>{}),!0;if(video.seekable&&video.seekable.length>0){const liveEdge=video.seekable.end(video.seekable.length-1);return video.currentTime=Math.max(0,liveEdge-.5),await video.play().catch(()=>{}),!0}return!1},window.reportFocusVideoError=function(matchId){const resolvedId=matchId||focusCurrentMatchId||function(){const container=document.getElementById("focusPlayerContainer"),raw=container?.dataset?.matchId,n=raw?Number(raw):NaN;return Number.isFinite(n)?n:null}();resolvedId&&"function"==typeof window.reportVideoError&&window.reportVideoError(resolvedId)},window.shareFocusMatch=function(){const{url:url,title:title}=function getFocusShareTarget(){const container=document.getElementById("focusPlayerContainer"),isBeauty="true"===container?.dataset?.beauty,isManualFootball="true"===container?.dataset?.manualFootball||Number(container?.dataset?.matchId)<0;let url=window.location.href,title=document.title;if(isBeauty){const identifier=container?.dataset?.identifier||"",sessionId=identifier.startsWith("standalone-")?identifier.slice(11):"";if(sessionId){const slug=document.querySelector(".beauty-card.active")?.dataset?.streamerSlug?.trim();url=`${window.location.origin}/live/beauty/${sessionId}${slug?`/${slug}`:""}`}const activeCard=document.querySelector(".beauty-card.active"),cardTitle=activeCard?.dataset?.title?.trim(),streamer=activeCard?.dataset?.streamer?.trim();cardTitle&&streamer?title=`${streamer} · ${cardTitle}`:(cardTitle||streamer)&&(title=cardTitle||streamer)}else if(isManualFootball){const activeCard=document.querySelector(".focus-card.active");let sessionId=activeCard?.dataset?.streamSessionId||"";if(!sessionId){const matchId=Number(container?.dataset?.matchId);Number.isFinite(matchId)&&matchId<0&&(sessionId=String(-matchId))}if(!sessionId){const identifier=container?.dataset?.identifier||"";identifier.startsWith("standalone-")?sessionId=identifier.slice(11):identifier.startsWith("manual-")&&(sessionId=identifier.slice(7))}if(sessionId){const slug=activeCard?.dataset?.streamerSlug?.trim();url=`${window.location.origin}/live/football/${sessionId}${slug?`/${slug}`:""}`}const homeTeam=activeCard?.dataset?.homeTeam?.trim(),awayTeam=activeCard?.dataset?.awayTeam?.trim();homeTeam&&awayTeam&&(title=`${homeTeam} vs ${awayTeam}`)}
1else{const detailLink=document.getElementById("focusMatchDetailLink");detailLink?.href&&!detailLink.classList.contains("d-none")&&(url=detailLink.href)}return{url:url,title:title}}();"function"!=typeof window.shareUrlWithFallback?function copyFocusShareLink(url){navigator.clipboard?navigator.clipboard.writeText(url).then(()=>{"function"==typeof showToast&&showToast(window.L?.T?window.L.T("common.linkCopied"):"链接已复制到剪贴板","success")}).catch(()=>{fallbackCopyFocusLink(url)}):fallbackCopyFocusLink(url)}(url):window.shareUrlWithFallback(url,title)};let currentCfQualityOptions=null,currentCfQuality=null;let cfQualityMenuOpen=!1,cfQualitySelectorInstance=null;function buildCfQualityMenu(options,matchId){const selector=document.getElementById("cfQualitySelector");if(!selector)return;const menu=selector.querySelector(".ivs-quality-menu");menu&&(menu.innerHTML="",options.forEach(opt=>{const isActive=opt.quality===currentCfQuality,option=document.createElement("div");option.className="ivs-quality-option"+(isActive?" active":"");let label=opt.displayName;if(opt.bitrate){label+=` <span class="ivs-quality-bitrate">${(opt.bitrate/1e6).toFixed(1)}Mbps</span>`}option.innerHTML=`\n                <span class="ivs-quality-label">${label}</span>\n                ${isActive?'<span class="ivs-quality-check">✓</span>':""}\n            `,option.addEventListener("click",e=>{e.preventDefault(),e.stopPropagation(),selectCfQuality(opt.quality,matchId)}),menu.appendChild(option)}))}async function selectCfQuality(quality,matchId){if(!currentCfQualityOptions)return;const option=currentCfQualityOptions.find(o=>o.quality===quality);if(!option||!option.whepUrl)return void console.warn("[FocusPlayer] 无效的清晰度选项:",quality);const selector=document.getElementById("cfQualitySelector"),menu=selector?.querySelector(".ivs-quality-menu");menu&&menu.classList.remove("show"),cfQualityMenuOpen=!1;const textEl=selector?.querySelector(".ivs-quality-text");textEl&&(textEl.textContent="切换中..."),option.whepUrl,currentCfQuality=quality;const video=document.getElementById("focusLivePlayer");if(video)try{await initCloudflareWhepPlayer(video,option.whepUrl),video.play().catch(e=>console.warn("[FocusPlayer] 自动播放失败:",e.message)),textEl&&(textEl.textContent=option.displayName),buildCfQualityMenu(currentCfQualityOptions,matchId)}catch(e){console.error("[FocusPlayer] 切换清晰度失败:",e.message),textEl&&(textEl.textContent=option.displayName)}}async function tryLoadMuxStream(matchId,identifier){try{const effectiveIdentifier=identifier||matchId,response=await fetch(`/api/stream/info/${effectiveIdentifier}`);if(!response.ok)return showFocusPlayerErrorSmart(),!1;const data=await response.json(),hlsUrl=data.playbackUrl||data.playUrl;if(!hlsUrl)return showFocusPlayerErrorSmart(),!1;const video=document.getElementById("focusLivePlayer");return video?(destroyFocusPlayer(),loadFocusHlsStream(hlsUrl,matchId,{lowLatency:!0,enableQualitySelector:!0,hlsVariants:data.variants}),new Promise(resolve=>{const timeout=setTimeout(()=>{showFocusPlayerErrorSmart(),resolve(!1)},1e4);focusHlsInstance?(focusHlsInstance.on(Hls.Events.MANIFEST_PARSED,()=>{clearTimeout(timeout),hideFocusPlayerError(),hideFocusLoadingMessage(),hideFocusNotLiveOverlay(),isStreamPlaying=!0,video.classList.add("loaded"),resolve(!0)}),focusHlsInstance.on(Hls.Events.ERROR,(event,errData)=>{errData.fatal&&(clearTimeout(timeout),showFocusPlayerErrorSmart(),resolve(!1))})):(video.addEventListener("loadeddata",()=>{clearTimeout(timeout),hideFocusPlayerError(),isStreamPlaying=!0,video.classList.add("loaded"),resolve(!0)},{once:!0}),video.addEventListener("error",()=>{clearTimeout(timeout),showFocusPlayerErrorSmart(),resolve(!1)},{once:!0}))})):(showFocusPlayerError(),!1)}catch(e){return console.error("[FocusPlayer] Mux 加载异常:",e.message),showFocusPlayerErrorSmart(),!1}}async function tryLoadIvsStream(matchId,identifier){try{if("undefined"==typeof IVSPlayerAdapter)return console.error("[FocusPlayer] IVSPlayerAdapter 未加载,无法播放 IVS 流"),showFocusPlayerError(window.L?.T?window.L.T("match.player.playerLoadFailed"):"播放器加载失败,请刷新页面重试",!0),!1;const video=document.getElementById("focusLivePlayer");if(!video)return showFocusPlayerErrorSmart(),!1;const effectiveIdentifier=identifier||matchId,playbackUrl=await async function getFocusIvsPlaybackUrl(identifier){try{const response=await fetch(`/api/stream/info/${identifier}`);if(!response.ok)return console.warn("[FocusPlayer] API 响应失败:",response.status),null;const playbackUrl=(await response.json()).playbackUrl||null;return playbackUrl}catch(e){return console.error("[FocusPlayer] 获取 IVS 播放地址失败:",e.message),null}}(effectiveIdentifier);return playbackUrl?new Promise(resolve=>{ivsLoadTimeoutTimer=setTimeout(()=>{showFocusPlayerError("IVS 播放超时",!1,!1,!0),resolve(!1)},2e4);let hasResolved=!1;const resolveOnce=value=>{hasResolved||(hasResolved=!0,ivsLoadTimeoutTimer&&(clearTimeout(ivsLoadTimeoutTimer),ivsLoadTimeoutTimer=null),resolve(value))};focusIvsAdapter=new IVSPlayerAdapter(video,{autoplay:!0,muted:!0,lowLatency:!0,showQualitySelector:!0,logLevel:"info"}),focusIvsAdapter.on("playing",()=>{hideFocusPlayerError(),hideFocusLoadingMessage(),hideFocusNotLiveOverlay(),isStreamPlaying=!0,setUserInitiatedPlay(),video.classList.add("loaded"),resolveOnce(!0)}),focusIvsAdapter.on("ready",()=>{hideFocusLoadingMessage()}),focusIvsAdapter.on("error",error=>{"ErrorNoSource"===error.type&&!1!==error.recoverable||(console.error("[FocusPlayer] IVS 错误:",error),error.recoverable&&"ErrorNotAvailable"!==error.type||(console.error("[FocusPlayer] IVS 确定性错误,直接显示错误提示:",error.type),hasDefinitiveError=!0,showFocusPlayerError(error.message||"播放失败",!1,!1,!0),resolveOnce(!1)))}),focusIvsAdapter.on("ended",()=>{showFocusPlayerError("直播已结束,正在重新连接...",!1,!1,!1),destroyFocusPlayer(),setTimeout(()=>{focusCurrentMatchId&&lastFocusStreamingType&&loadFocusStreamWithRetry(focusCurrentMatchId,lastFocusStreamingType,lastFocusIdentifier)},2e3),resolveOnce(!1)}
1),focusIvsAdapter.initialize(playbackUrl).catch(error=>{console.error("[FocusPlayer] IVS 初始化失败:",error.message),showFocusPlayerError(error.message||"IVS 初始化失败",!1,!1,!0),resolveOnce(!1)})}):(console.error("[FocusPlayer] 无法获取 IVS 播放地址"),showFocusPlayerError("无法获取播放地址",!1,!1,!0),!1)}catch(e){return console.error("[FocusPlayer] IVS 加载异常:",e.message),showFocusPlayerError(e.message||"IVS 加载失败",!1,!1,!0),!1}}function initFocusChatHeightSync(){const playerWrapper=document.querySelector(".focus-player-wrapper"),chatWrapper=document.getElementById("focusChatWrapper");if(!playerWrapper||!chatWrapper)return;function syncHeight(){if(window.innerWidth<=992)return void(chatWrapper.style.height="");const playerHeight=playerWrapper.offsetHeight;playerHeight>0&&(chatWrapper.style.height=playerHeight+"px")}syncHeight(),window.addEventListener("resize",syncHeight);const video=document.getElementById("focusLivePlayer");video&&video.addEventListener("loadedmetadata",syncHeight),setTimeout(syncHeight,500)}function initFocusLiveUpdates(){window.MatchLiveHub&&(window.MatchLiveHub.on("onMatchUpdate",handleFocusMatchUpdate),window.MatchLiveHub.on("onStreamerStatusUpdate",handleFocusStreamerStatusUpdate)),window.MatchStore&&window.MatchStore.subscribe(handleFocusStoreUpdate),syncFocusStreamLiveStatusFromDb()}function ensureFocusVideoMuted(){const video=document.getElementById("focusLivePlayer");video&&(video.muted=!0,video.setAttribute("muted",""))}function isFocusPlayerActuallyPlaying(){const video=document.getElementById("focusLivePlayer");return!!video&&(!!isStreamPlaying||(!video.paused&&video.readyState>=2&&video.videoWidth>0||(!(!focusIvsAdapter||!focusIvsAdapter.getState||"playing"!==focusIvsAdapter.getState())||!(!focusLivekitRoom||"connected"!==focusLivekitRoom.state||video.paused))))}async function tryAutoPlayFirstLiveFocus(){if(window.PcPlaybackVisibility?.deferAutoPlay())return;if(countFocusLiveCards()<=0)return;const container=document.getElementById("focusPlayerContainer"),video=document.getElementById("focusLivePlayer");if(!container||!video)return;if(window.focusIsSwitching||focusIvsAdapter||focusLivekitRoom||focusPublisherPollingTimer||ivsLoadTimeoutTimer)return;const hasNotLiveOverlay=!!container.querySelector(".focus-not-live-overlay");if(isFocusPlayerActuallyPlaying()&&!hasNotLiveOverlay)return;let targetCard=document.querySelector("#liveMatchCards .focus-card.active, #liveMatchCards .beauty-card.active");if(targetCard&&isFocusCardItemLive(targetCard.closest(".focus-card-item"))||(targetCard=findFirstLiveFocusCard()),!targetCard)return;if(ensureFocusVideoMuted(),hideFocusNotLiveOverlay(),hideFocusPlayerError(),hideFocusPlayOverlay(),isBeautyCardElement(targetCard))return void switchFocusBeauty(targetCard,beginFocusSwitch());const matchId=parseInt(targetCard.dataset.matchId,10),streamSessionId=targetCard.dataset.streamSessionId||void 0;isNaN(matchId)||await window.switchFocusMatch(matchId,streamSessionId)}async function syncFocusStreamLiveStatusFromDb(){try{const response=await fetch("/api/focus-stream-live-status");if(!response.ok)return;const payload=await response.json();if(!payload?.success||!Array.isArray(payload.sessions))return;const liveMap=new Map(payload.sessions.map(s=>[`${s.matchId}_${s.sessionId}`,!!s.isLive]));let anyBecameLive=!1;document.querySelectorAll("#liveMatchCards .focus-card[data-stream-session-id]").forEach(card=>{const matchId=card.dataset.matchId,sessionId=card.dataset.streamSessionId;if(!matchId||!sessionId)return;const key=`${matchId}_${sessionId}`;if(!liveMap.has(key))return;const isLive=liveMap.get(key),wasLive=card.classList.contains("is-live");isLive&&!wasLive&&(anyBecameLive=!0);const hasStreamerTask="true"===card.dataset.hasStreamerTask;updateCardLiveStatus(parseInt(matchId,10),isLive,hasStreamerTask,key)}),refreshFocusLiveCountFromDom(),anyBecameLive&&await tryAutoPlayFirstLiveFocus()}catch(e){console.warn("[FocusPlayer] 同步 DB 直播状态失败:",e)}}function handleFocusStreamerStatusUpdate(data){if(data.matchId&&void 0!==data.isStreamerLiveNow){const cardSelector=data.cardId?`.focus-card[data-card-id="${data.cardId}"], .focus-match-card[data-card-id="${data.cardId}"]`:`.focus-card[data-match-id="${data.matchId}"], .focus-match-card[data-match-id="${data.matchId}"]`,cards=document.querySelectorAll(cardSelector);if(0===cards.length)return void console.warn(`[FocusPlayer] 未找到比赛 ${data.matchId} 的卡片`);data.matchId,cards.length,cards.forEach(card=>{const hasStreamerTask="true"===card?.dataset?.hasStreamerTask,cardId=data.cardId||card?.dataset?.cardId;updateCardLiveStatus(data.matchId,data.isStreamerLiveNow,hasStreamerTask,cardId)}),refreshFocusLiveCountFromDom(),data.isStreamerLiveNow&&tryAutoPlayFirstLiveFocus(),data.matchId===focusCurrentMatchId&&data.isStreamerLiveNow}}window.switchCfQuality=async function(quality,matchId){await selectCfQuality(quality,matchId)},window.tryAutoPlayFirstLiveFocus=tryAutoPlayFirstLiveFocus,window.syncFocusStreamLiveStatusFromDb=syncFocusStreamLiveStatusFromDb;let focusPublisherPollingTimer=null,focusPublisherTimeoutTimer=null;const FOCUS_PUBLISHER_POLL_INTERVAL=3e3,FOCUS_PUBLISHER_TIMEOUT=2e4;function stopFocusPublisherPolling(){focusPublisherPollingTimer&&(clearInterval(focusPublisherPollingTimer),focusPublisherPollingTimer=null),focusPublisherTimeoutTimer&&(clearTimeout(focusPublisherTimeoutTimer),focusPublisherTimeoutTimer=null)}function showFocusStreamStartingOverlay(){const container=document.getElementById("focusPlayerContainer");if(!container)return;hideFocusPlayOverlay(),hideFocusPlayerError(),hideFocusLoadingMessage();let overlay=document.getElementById("focusStreamStartingOverlay");overlay||(overlay=document.createElement("div"),overlay.id="focusStreamStartingOverlay",overlay.style.cssText="\n                position: absolute;\n                inset: 0;\n                display: flex;\n                flex-direction: column;\n                align-items: center;\n                justify-content: center;\n                background: rgba(0,0,0,0.95);\n                z-index: 30;\n                color: #fff;\n                font-size: 16px;\n                text-align: center;\n                padding: 20px;\n                pointer-events: none;\n            ",container.appendChild(overlay));const startingText=window.L?.T?window.L.T("match.player.streamStarting"):"首次启动直播,请稍等片刻",connectingText=window.L?.T?window.L.T("match.player.connectingStream"):"正在连接直播源...";overlay.innerHTML=`\n            <div style="margin-bottom: 15px;">\n                <svg width="48" height="48" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">\n                    <circle cx="12" cy="12" r="10" stroke="#3B82F6" stroke-width="2" opacity="0.3"/>\n                    <path d="M12 2a10 10 0 0 1 10 10" stroke="#3B82F6" stroke-width="2" stroke-linecap="round">\n                        <animateTransform attributeName="transform" type="rotate" from="0 12 12" to="360 12 12" dur="1s" repeatCount="indef
1inite"/>\n                    </path>\n                </svg>\n            </div>\n            <div style="font-weight: 500; margin-bottom: 8px;">${startingText}</div>\n            <div style="font-size: 13px; color: #9CA3AF;">${connectingText}</div>\n        `,overlay.style.display="flex"}function hideFocusStreamStartingOverlay(){const overlay=document.getElementById("focusStreamStartingOverlay");overlay&&(overlay.style.display="none")}function handleFocusMatchUpdate(data){const matchId=data.matchId,payload=data.payload||data;updateFocusHeaderFromPayload(matchId,payload),updateFocusMatchCard(matchId,payload)}function handleFocusStoreUpdate(updates){updates&&Array.isArray(updates)&&updates.forEach(update=>{const matchId=update.matchId,match=update.data;match&&(updateFocusHeaderFromPayload(matchId,match),updateFocusMatchCard(matchId,match))})}function updateFocusHeaderFromPayload(matchId,payload){if(matchId!==focusCurrentMatchId)return;const homeScoreEl=document.querySelector(".focus-score-display .home-score"),awayScoreEl=document.querySelector(".focus-score-display .away-score");homeScoreEl&&void 0!==payload.homeScore&&(homeScoreEl.textContent=payload.homeScore),awayScoreEl&&void 0!==payload.awayScore&&(awayScoreEl.textContent=payload.awayScore);const statusEl=document.querySelector(".focus-status");if(statusEl){const statusText=statusEl.querySelector(".status-text"),minuteText=statusEl.querySelector(".status-minute");statusText&&payload.status&&(statusText.textContent=getStatusText(payload.status)),minuteText&&void 0!==payload.minute&&(minuteText.textContent=payload.minute>0?`${payload.minute}'`:"",minuteText.dataset.minute=payload.minute)}}function updateFocusMatchCard(matchId,payload){const cards=document.querySelectorAll(`.focus-card[data-match-id="${matchId}"], .focus-match-card[data-match-id="${matchId}"]`);if(!cards.length)return;const notStartedStatuses=["NS","TBD","PST"];payload.status&&notStartedStatuses.includes(payload.status);cards.forEach(card=>{const currentStatus=payload.status||card.dataset.status,shouldShowScore=!notStartedStatuses.includes(currentStatus);let homeScoreEl=card.querySelector(".focus-card-score.home-score"),awayScoreEl=card.querySelector(".focus-card-score.away-score");if(shouldShowScore){const rows=card.querySelectorAll(".focus-card-team-row");!homeScoreEl&&rows.length>=1&&(homeScoreEl=document.createElement("span"),homeScoreEl.className="focus-card-score home-score",homeScoreEl.dataset.matchId=matchId,homeScoreEl.textContent=payload.homeScore??0,rows[0].appendChild(homeScoreEl)),!awayScoreEl&&rows.length>=2&&(awayScoreEl=document.createElement("span"),awayScoreEl.className="focus-card-score away-score",awayScoreEl.dataset.matchId=matchId,awayScoreEl.textContent=payload.awayScore??0,rows[1].appendChild(awayScoreEl)),homeScoreEl&&void 0!==payload.homeScore&&homeScoreEl.textContent!=payload.homeScore&&(homeScoreEl.textContent=payload.homeScore,homeScoreEl.classList.remove("score-changed"),homeScoreEl.offsetWidth,homeScoreEl.classList.add("score-changed"),setTimeout(()=>homeScoreEl.classList.remove("score-changed"),1200)),awayScoreEl&&void 0!==payload.awayScore&&awayScoreEl.textContent!=payload.awayScore&&(awayScoreEl.textContent=payload.awayScore,awayScoreEl.classList.remove("score-changed"),awayScoreEl.offsetWidth,awayScoreEl.classList.add("score-changed"),setTimeout(()=>awayScoreEl.classList.remove("score-changed"),1200))}else homeScoreEl&&homeScoreEl.remove(),awayScoreEl&&awayScoreEl.remove();const vsEl=card.querySelector(".focus-card-vs");vsEl&&void 0!==payload.homeScore&&void 0!==payload.awayScore&&(vsEl.textContent=shouldShowScore?`${payload.homeScore} - ${payload.awayScore}`:"VS");const statusTextEl=card.querySelector(".card-status .status-text");statusTextEl&&payload.status&&(statusTextEl.textContent=getStatusText(payload.status));const minuteEl=card.querySelector(".card-status .js-match-minute");if(void 0!==payload.minute&&payload.minute>0)if(minuteEl)minuteEl.textContent=payload.minute+"'",minuteEl.dataset.minute=payload.minute;else{const cardStatus=card.querySelector(".card-status");if(cardStatus){const newMinuteEl=document.createElement("span");newMinuteEl.className="status-minute js-match-minute",newMinuteEl.dataset.matchI
1d=card.dataset.matchId,newMinuteEl.dataset.minute=payload.minute,newMinuteEl.textContent=payload.minute+"'",cardStatus.appendChild(newMinuteEl)}}void 0!==payload.homeScore&&(card.dataset.homeScore=payload.homeScore),void 0!==payload.awayScore&&(card.dataset.awayScore=payload.awayScore),payload.status&&(card.dataset.status=payload.status),void 0!==payload.minute&&(card.dataset.minute=payload.minute)})}async function checkFocusMutePermission(){try{const response=await fetch("/api/chat/can-mute");if(response.ok){const data=await response.json();focusCanMuteUsers=data.canMute,focusIsVipUser=data.isVip}}catch(e){console.error("[FocusChat] 检查权限失败:",e)}}function loadFocusHlsStream(url,matchId,options={}){const{lowLatency:lowLatency=!1,enableQualitySelector:enableQualitySelector=!1,hlsVariants:hlsVariants=null}=options,video=document.getElementById("focusLivePlayer");if(video)if("function"==typeof hideHlsQualitySelector&&hideHlsQualitySelector("hlsQualitySelector"),focusHlsQualitySelector&&(focusHlsQualitySelector.destroy(),focusHlsQualitySelector=null),hideFocusPlayerError(),Hls&&Hls.isSupported()){const hlsConfig=lowLatency&&"function"==typeof getLowLatencyHlsConfig?getLowLatencyHlsConfig():{enableWorker:!0,lowLatencyMode:!0,backBufferLength:90};focusHlsInstance=new Hls(hlsConfig),focusHlsInstance.loadSource(url),focusHlsInstance.attachMedia(video),focusHlsInstance.on(Hls.Events.MANIFEST_PARSED,function(){hideFocusPlayerError(),hideFocusLoadingMessage(),clearPlaybackTimeout(),isStreamPlaying=!0,video.classList.add("loaded"),enableQualitySelector&&"function"==typeof setupMuxHlsQualitySelector&&setupMuxHlsQualitySelector({hls:focusHlsInstance,video:video,masterUrl:url,variants:hlsVariants,selectorId:"hlsQualitySelector",storageKey:"mux_focus_hls_quality_level"}).then(selector=>{focusHlsQualitySelector=selector}),video.play().then(()=>{hideFocusPlayerError(),hideFocusPlayOverlay()}).catch(e=>{})}),focusHlsInstance.on(Hls.Events.ERROR,function(event,data){data.fatal&&(console.error("[FocusPlayer] HLS error:",data),showFocusPlayerErrorSmart())})}else if(video.canPlayType("application/vnd.apple.mpegurl")){(async()=>{if(enableQualitySelector&&"function"==typeof setupMuxHlsQualitySelector){const selector=await setupMuxHlsQualitySelector({video:video,masterUrl:url,variants:hlsVariants,selectorId:"hlsQualitySelector",storageKey:"mux_focus_hls_quality_level"});focusHlsQualitySelector=selector,selector&&-1!==selector.currentLevel||(video.src=url)}else video.src=url})().then(()=>{video.play().then(()=>{hideFocusPlayerError(),hideFocusPlayOverlay()}).catch(e=>{})}).catch(e=>{console.warn("[FocusPlayer] iOS HLS 初始化失败:",e),video.src=url,video.play().catch(()=>{})}),video.addEventListener("loadeddata",function onLoaded(){hideFocusPlayerError(),hideFocusLoadingMessage(),clearPlaybackTimeout(),video.classList.add("loaded"),video.removeEventListener("loadeddata",onLoaded)}),video.addEventListener("playing",function onPlaying(){hideFocusPlayerError(),hideFocusPlayOverlay(),clearPlaybackTimeout(),isStreamPlaying=!0,video.removeEventListener("playing",onPlaying)}),video.addEventListener("pause",function onPause(){const container=document.getElementById("focusPlayerContainer");"true"!==container?.dataset?.userPausedLive&&isStreamPlaying&&(video.srcObject||video.src)&&!video.ended&&video.play().catch(()=>{})})}}async function loadFocusWebRtcStream(matchId,options={}){const{forceReconnect:forceReconnect=!1}="object"==typeof options&&null!==options?options:{},video=document.getElementById("focusLivePlayer");if(video)if(!forceReconnect&&focusLivekitRoom&&"connected"===focusLivekitRoom.state&&!video.paused&&video.readyState>=2)isStreamPlaying=!0;else{if(forceReconnect&&focusLivekitRoom){try{await focusLivekitRoom.disconnect()}catch(e){console.warn("[FocusPlayer] 强制重连前断开失败:",e)}focusLivekitRoom=null}try{await ensureLiveKitSdkLoaded();const tokenData=await getWebRtcTokenFast(matchId),{token:token,wsUrl:wsUrl}=tokenData;await async function connectFocusToLiveKit(token,wsUrl,video){const LiveKit=window.LivekitClient||window.LiveKitClient||window.livekit;if(!LiveKit)throw new Error("LiveKit SDK 未加载");const{Room:Room,RoomEvent:RoomEvent,VideoPresets:VideoPresets}=LiveKit;if(focusLivekitRoom)try{await focusLivekitRoom.disconnect()}catch(e){console.warn("断开旧连接失败:",e)}focusLivekitRoom=new Room({adaptiveStream:!0,dynacast:!0,videoCaptureDefaults:{resolution:VideoPresets.h720.resolution}});let lastFocusVideoTrackTime=0,lastFocusAudioTrackTime=0;focusLivekitRoom.on(RoomEvent.TrackSubscribed,(track,publication,participant)=>{const now=Date.now();if("video"===track.kind){if(now-lastFocusVideoTrackTime<2e3)return;lastFocusVideoTrackTime=now,track.kind,track.attach(video),isStreamPlaying=!0,clearPlaybackTimeout(),hideFocusLoadingMessage(),video.classList.add("loaded"),isFocusPlayingInProgress||(isFocusPlayingInProgress=!0,video.muted||(video.muted=!0),video.play().then(()=>{isFocusPlayingInProgress=!1,hideFocusPlayerError(),hideFocusPlayOverlay()}).catch(e=>{isFocusPlayingInProgress=!1,"NotAllowedError"!==e.name&&"AbortError"!==e.name&&e.message}))}else if("audio"===track.kind){if(now-lastFocusAudioTrackTime<2e3)return;lastFocusAudioTrackTime=now,track.kind,track.attach(video)}}),focusLivekitRoom.on(RoomEvent.Disconnected,()=>{}),await focusLivekitRoom.connect(wsUrl,token)}(token,wsUrl,video)}catch(error){console.error("[FocusPlayer] WebRTC error:",error),show
1FocusPlayerErrorSmart()}}}function destroyFocusPlayer(){const video=document.getElementById("focusLivePlayer");if(ivsLoadTimeoutTimer&&(clearTimeout(ivsLoadTimeoutTimer),ivsLoadTimeoutTimer=null),cfLoadTimeoutTimer&&(clearTimeout(cfLoadTimeoutTimer),cfLoadTimeoutTimer=null),focusIvsAdapter){try{focusIvsAdapter.destroy()}catch(e){console.warn("[FocusPlayer] 销毁 IVS 播放器失败:",e)}focusIvsAdapter=null}if("function"==typeof destroyCloudflareWhepPlayer)try{destroyCloudflareWhepPlayer()}catch(e){console.warn("[FocusPlayer] 销毁 Cloudflare WHEP 播放器失败:",e)}if(focusHlsInstance&&(focusHlsInstance.destroy(),focusHlsInstance=null),focusHlsQualitySelector&&(focusHlsQualitySelector.destroy(),focusHlsQualitySelector=null),"function"==typeof hideHlsQualitySelector&&hideHlsQualitySelector("hlsQualitySelector"),focusLivekitRoom){try{focusLivekitRoom.disconnect()}catch(e){console.warn("断开WebRTC失败:",e)}focusLivekitRoom=null}const ivsQualitySelector=document.querySelector(".ivs-quality-selector-container");ivsQualitySelector&&ivsQualitySelector.remove();const cfQualitySelector=document.getElementById("cfQualitySelector");if(cfQualitySelector&&cfQualitySelector.remove(),video){if(video.srcObject&&"function"==typeof video.srcObject.getTracks)try{video.srcObject.getTracks().forEach(track=>track.stop())}catch(e){console.warn("[FocusPlayer] 停止视频轨道失败:",e)}video.srcObject=null,video.src="",video.classList.remove("loaded")}}let focusLoadingTimeoutTimer=null;function showFocusLoadingMessage(){const container=document.getElementById("focusPlayerContainer");if(!container)return;const activeCard=document.querySelector(".focus-card.active, .beauty-card.active, .focus-match-card.active");activeCard&&updateFocusPlayerCoverFromCard(activeCard);if(container.querySelector(".focus-loading-overlay"))return;const loadingText=document.body.dataset.loadingText||"正在获取资源,请稍等几秒钟...",hintText=document.body.dataset.loadingHintText||"如果视频无法播放或卡顿请尝试刷新",hasCover=!!container.querySelector(".focus-player-cover"),overlay=document.createElement("div");overlay.className="focus-loading-overlay"+(hasCover?" has-cover":""),overlay.innerHTML=`\n            <div class="loading-content">\n                <div class="loading-spinner">\n                    <div class="loading-spinner-dot"></div>\n                </div>\n                <div class="loading-text">${loadingText}</div>\n                <div class="loading-hint">${hintText}</div>\n                <div class="loading-progress"></div>\n            </div>\n        `,container.appendChild(overlay),clearFocusLoadingTimeout(),focusLoadingTimeoutTimer=setTimeout(()=>{hideFocusLoadingMessage()},3e4)}function hideFocusLoadingMessage(){clearFocusLoadingTimeout();const overlay=document.querySelector(".focus-loading-overlay");overlay&&overlay.remove()}function clearFocusLoadingTimeout(){focusLoadingTimeoutTimer&&(clearTimeout(focusLoadingTimeoutTimer),focusLoadingTimeoutTimer=null)}async function checkLiveStatusFromRedis(matchId){try{const response=await fetch(`/api/live-status/${matchId}`);return(await response.json()).isLive||!1}catch(error){return console.error("[FocusLiveStatus] 检查直播状态失败:",error),!1}}let globalRetryManager={retryCount:0,maxRetries:10,baseDelay:1e3,maxDelay:3e4,retryTimer:null,totalRetries:0,maxTotalRetries:5,getRetryDelay(){return Math.min(this.baseDelay*Math.pow(2,this.retryCount),this.maxDelay)+1e3*Math.random()},reset(){this.retryCount=0,this.retryTimer&&(clearTimeout(this.retryTimer),this.retryTimer=null),this.totalRetries=0},scheduleRetry(callback){if(this.totalRetries>=this.maxTotalRetries)return console.error("[FocusRetryManager] ❌ 达到绝对最大重试次数,停止重试,显示错误"),this.showMaxRetriesError(),!1;if(this.retryCount>=this.maxRetries)return!1;const delay=this.getRetryDelay();return this.retryCount++,this.totalRetries++,window.focusIsSilentRetry||(this.retryCount,this.totalRetries,Math.round(delay)),this.retryTimer=setTimeout(()=>{callback()},delay),!0},showMaxRetriesError(){hideFocusLoadingMessage();if(!document.getElementById("focusLivePlayer"))return;let overlay=document.getElementById("focusPlayerMaxRetriesOverlay");if(!overlay){overlay=document.createElement("div"),overlay.id="focusPlayerMaxRetriesOverlay",overlay.className="focus-player-error-overlay";
1const container=document.querySelector(".focus-live-player-container");container&&container.appendChild(overlay)}const errorText=document.body.getAttribute("data-playback-error-text")||"播放失败",retryText=document.body.getAttribute("data-retry-text")||"重试";overlay.innerHTML=`\n                <div class="error-content-wrapper">\n                    <div class="error-card">\n                        <div class="error-icon-container">\n                            <svg class="error-icon-svg" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">\n                                <circle cx="12" cy="12" r="10" stroke="#EF4444" stroke-width="1.5" opacity="0.3"/>\n                                <path d="M12 8v4M12 16v1" stroke="#EF4444" stroke-width="2" stroke-linecap="round"/>\n                            </svg>\n                        </div>\n                        <h3 class="error-title">${errorText}</h3>\n                        <p class="error-message">多次尝试后仍无法播放,请稍后再试</p>\n                        <button class="error-retry-btn" onclick="manualRetryFocusPlayback()">\n                            <svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">\n                                <path d="M4 12a8 8 0 0 1 14.93-4M20 12a8 8 0 0 1-14.93 4M4 8v4h4M20 16v-4h-4" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>\n                            </svg>\n                            ${retryText}\n                        </button>\n                    </div>\n                </div>\n            `,overlay.classList.add("show")}};function showFocusPlayerErrorSmart(message,showRetry=!0){window.focusIsSilentRetry||showFocusPlayerError(message,showRetry,!1)}async function showFocusPlayerError(message,showRetry=!0,isSilentRetry=!1,isDefinitiveError=!1){if(window.focusIsSwitching)return;hideFocusLoadingMessage();const matchId=getCurrentFocusMatchId();if(!matchId)return void showFocusNotLiveMessage();if(isDefinitiveError)return void showFocusNotLiveMessage();if(!window.focusHasTriedPlayback)return window.focusHasTriedPlayback=!0,void showFocusLoadingWithRetry(matchId);await checkLiveStatusFromRedis(matchId)?showFocusLoadingWithRetry(matchId):showFocusNotLiveMessage()}function formatFocusPanelDate(raw){if(!raw)return"";const d=new Date(raw);if(Number.isNaN(d.getTime()))return"";const parts=new Intl.DateTimeFormat("en-GB",{day:"2-digit",month:"2-digit",hour:"2-digit",minute:"2-digit",hour12:!1,timeZone:"Asia/Ho_Chi_Minh"}).formatToParts(d),get=type=>parts.find(part=>part.type===type)?.value||"";return`${get("day")}/${get("month")} ${get("hour")}:${get("minute")}`}function pickFocusI18nText(key,fallback){try{if(window.L?.T){const translated=window.L.T(key),prefix=key.includes(".")?key.slice(0,key.indexOf(".")+1):"";if(translated&&(!prefix||!translated.startsWith(prefix)))return translated}}catch(e){}return fallback}function buildFocusNotLiveMetaHtml(meta){if(!meta)return"";const leagueHtml=meta.league?`<div class="focus-not-live-league">\n                    ${meta.leagueLogo?`<img src="${escapeHtml(meta.leagueLogo)}" alt="" class="focus-not-live-league-logo" loading="lazy" onerror="this.style.display='none'">`:""}\n                    <span>${escapeHtml(meta.league)}</span>\n               </div>`:"";let matchupHtml="";if(meta.isBeauty)meta.title&&(matchupHtml=`<div class="focus-not-live-beauty-title">${escapeHtml(meta.title)}</div>`);else if(meta.homeTeam&&meta.awayTeam){const homeLogoHtml=meta.homeLogo?`<img src="${escapeHtml(meta.homeLogo)}" alt="" class="focus-not-live-team-logo" loading="lazy" onerror="this.onerror=null; this.src='/images/placeholder-team.svg';">`:"",awayLogoHtml=meta.awayLogo?`<img src="${escapeHtml(meta.awayLogo)}" alt="" class="focus-not-live-team-logo" loading="lazy" onerror="this.onerror=null; this.src='/images/placeholder-team.svg';">`:"";matchupHtml=`\n                <div class="focus-not-live-matchup-grid">\n                    <div class="focus-not-live-team">\n                        ${homeLogoHtml}
1\n                        <span class="focus-not-live-team-name">${escapeHtml(meta.homeTeam)}</span>\n                    </div>\n                    <div class="focus-not-live-vs" aria-hidden="true">VS</div>\n                    <div class="focus-not-live-team">\n                        ${awayLogoHtml}\n                        <span class="focus-not-live-team-name">${escapeHtml(meta.awayTeam)}</span>\n                    </div>\n                </div>`}else{const fallback=meta.homeTeam||meta.awayTeam||meta.title;fallback&&(matchupHtml=`<div class="focus-not-live-matchup-single">${escapeHtml(fallback)}</div>`)}const kickoffLabel=function resolveFocusKickoffLabel(status,{isBeauty:isBeauty=!1,isManualFootball:isManualFootball=!1,matchDate:matchDate=""}={}){const code=(status||"").trim().toUpperCase();if(isBeauty)return"LIVE"===code||"RUNNING"===code||"STARTING"===code?pickFocusI18nText("home.beautyLiveNow","直播中"):pickFocusI18nText("home.beautyWaitingStart","等待开始");if(isManualFootball){if("LIVE"===code||"RUNNING"===code||"STARTING"===code)return pickFocusI18nText("match.status.live","进行中");if("FT"===code||"STOPPED"===code||"ENDED"===code)return pickFocusI18nText("match.finished","已结束");const scheduled=matchDate?new Date(matchDate):null;return scheduled&&!Number.isNaN(scheduled.getTime())&&scheduled.getTime()>Date.now()?pickFocusI18nText("home.startingSoon","即将开始"):pickFocusI18nText("match.waitingStart","等待开赛")}let key="match.startingSoon";switch(code){case"NS":key="match.streamNotStarted";break;case"TBD":key="match.notStarted";break;case"PST":key="match.postponed";break;case"SUSP":key="match.suspended";break;case"INT":key="match.interrupted";break;case"CANC":key="match.status.cancelled";break;case"ABD":key="match.status.abandoned";break;case"AWD":key="match.status.awarded";break;case"WO":key="match.status.walkover";break;case"1H":key="match.status.firstHalf";break;case"2H":key="match.status.secondHalf";break;case"HT":key="match.status.halftime";break;case"ET":key="match.status.extraTime";break;case"BT":key="match.status.breakTime";break;case"P":key="match.status.penalties";break;case"LIVE":key="match.status.live";break;case"FT":key="match.finished";break;case"AET":key="match.status.afterExtraTime";break;case"PEN":key="match.status.afterPenalties";break;case"Q1":key="match.status.quarter1";break;case"Q2":key="match.status.quarter2";break;case"Q3":key="match.status.quarter3";break;case"Q4":key="match.status.quarter4";break;case"OT":key="match.status.overtime";break;default:key="match.startingSoon"}return pickFocusI18nText(key,{"match.streamNotStarted":"直播未开始","match.notStarted":"未开始","match.startingSoon":"即将开赛","match.postponed":"已延期","match.suspended":"已暂停","match.interrupted":"已中断","match.finished":"已结束","match.status.live":"进行中","match.status.firstHalf":"上半场","match.status.secondHalf":"下半场","match.status.halftime":"中场休息","match.status.extraTime":"加时赛","match.status.penalties":"点球大战","match.status.breakTime":"节间休息","match.status.overtime":"加时赛","match.status.quarter1":"Q1","match.status.quarter2":"Q2","match.status.quarter3":"Q3","match.status.quarter4":"Q4","match.status.cancelled":"已取消","match.status.abandoned":"已中止","match.status.awarded":"判负","match.status.walkover":"弃权","match.status.afterExtraTime":"加时赛后结束","match.status.afterPenalties":"点球决胜后结束","match.waitingStart":"等待开赛"}[key]||"即将开赛")}(meta.matchStatus,{isBeauty:meta.isBeauty,isManualFootball:meta.isManualFootball,matchDate:meta.matchDate}),timeHtml=kickoffLabel||meta.time?`<div class="focus-not-live-kickoff">\n                    ${kickoffLabel?`<span class="focus-not-live-kickoff-label">${escapeHtml(kickoffLabel)}</span>`:""}\n                    ${kickoffLabel&&meta.time?'<span class="focus-not-live-kickoff-sep" aria-hidden="true">·</span>':""}\n                    ${meta.time?`<div class="focus-not-live-time">\n                        <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><polyline points="12 6 12 12 16 14"></polyline></svg>
1\n                        <span>${escapeHtml(meta.time)}</span>\n                    </div>`:""}\n               </div>`:"",streamerHtml=!meta.streamer||meta.isBeauty&&meta.streamer===meta.title?"":`<div class="focus-not-live-streamer">\n                    <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z"></path><path d="M19 10v2a7 7 0 0 1-14 0v-2"></path><line x1="12" y1="19" x2="12" y2="22"></line></svg>\n                    <span>${escapeHtml("function"==typeof formatBlvStreamerName?formatBlvStreamerName(meta.streamer):meta.streamer)}</span>\n               </div>`;return leagueHtml||matchupHtml||timeHtml||streamerHtml?`<div class="focus-not-live-meta">${leagueHtml}${matchupHtml}${timeHtml}${streamerHtml}</div>`:""}function showFocusNotLiveMessage(){hideFocusLoadingMessage();const container=document.getElementById("focusPlayerContainer");if(!container)return;const activeCard=document.querySelector(".focus-card.active, .beauty-card.active, .focus-match-card.active"),showRetry=function isFocusCardLiveForPanel(card){return!!card&&(isBeautyCardElement(card)?isBeautyCardLive(card):isFocusMatchCardLive(card))}(activeCard);container.classList.add("has-not-live-overlay"),updateFocusPlayerCoverFromCard(activeCard),container.querySelectorAll(".focus-error-overlay, .focus-waiting-overlay, .focus-not-live-overlay").forEach(overlay=>overlay.remove());const overlay=document.createElement("div"),hasCover=!!container.querySelector(".focus-player-cover");overlay.className="focus-not-live-overlay focus-error-overlay"+(hasCover?" has-cover":"");const meta=function getFocusNotLivePanelMeta(card){if(!card)return{league:"",leagueLogo:"",homeTeam:"",awayTeam:"",homeLogo:"",awayLogo:"",title:"",streamer:"",isBeauty:!1,time:"",matchStatus:"",isManualFootball:!1,matchDate:""};const item=card.closest(".focus-card-item"),matchDate=card.dataset.matchDate||item?.dataset.sortTime||"",matchStatus=(card.dataset.status||"").trim(),isManualFootball="true"===card.dataset.manualFootball||card.classList.contains("manual-football-card")||parseInt(card.dataset.matchId,10)<0,isBeauty=card.classList.contains("beauty-card")||!!card.dataset.beautyId,streamer=(card.dataset.streamer||"").trim()||(item?.querySelector(".focus-card-streamer")?.textContent||"").replace(/\s+/g," ").trim();if(isBeauty){const title=(card.dataset.title||"").trim();return{league:(window.L?.T?window.L.T("home.beautyLive"):"")||"娱乐直播",leagueLogo:card.dataset.streamerAvatar||"",homeTeam:"",awayTeam:"",homeLogo:"",awayLogo:"",title:title||streamer,streamer:streamer,isBeauty:!0,time:formatFocusPanelDate(matchDate),matchStatus:matchStatus,isManualFootball:!1,matchDate:matchDate}}return{league:(card.dataset.league||"").trim(),leagueLogo:card.dataset.leagueLogo||"",homeTeam:(card.dataset.homeTeam||"").trim(),awayTeam:(card.dataset.awayTeam||"").trim(),homeLogo:card.dataset.homeLogo||"",awayLogo:card.dataset.awayLogo||"",title:(card.dataset.title||"").trim(),streamer:streamer,isBeauty:!1,time:formatFocusPanelDate(matchDate),matchStatus:matchStatus,isManualFootball:isManualFootball,matchDate:matchDate}}(activeCard),metaHtml=buildFocusNotLiveMetaHtml(meta);let notLiveText=document.body.getAttribute("data-not-live-text")||"直播未开始";meta.isBeauty&&(notLiveText=pickFocusI18nText("home.beautyLivePending","直播尚未开始"));let retryBtnText="重试";try{if(window.L&&window.L.T){const translated=window.L.T("match.player.retry")||window.L.T("common.retry");!translated||translated.startsWith("match.")||translated.startsWith("common.")||(retryBtnText=translated)}}catch(e){console.warn("[FocusPlayer] 翻译加载失败,使用默认文本")}const retryHtml=showRetry?`<div class="focus-not-live-divider" aria-hidden="true"></div>\n                    <button class="error-retry-btn error-retry-btn-inline focus-not-live-retry" onclick="retryFocusPlayback()" type="button">
1\n                        <svg class="retry-icon" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">\n                            <path d="M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>\n                            <path d="M3 3v5h5" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>\n                            <path d="M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>\n                            <path d="M16 21v-5h5" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>\n                        </svg>\n                        <span>${retryBtnText}</span>\n                    </button>`:"";overlay.innerHTML=`\n            <div class="error-content-wrapper">\n                <div class="error-card error-card-inline focus-not-live-panel${showRetry?"":" focus-not-live-panel--no-retry"}">\n                    ${metaHtml}\n                    <div class="focus-not-live-status focus-not-live-status-subtle">\n                        <span class="focus-not-live-dot" aria-hidden="true"></span>\n                        <p class="focus-not-live-status-text" onmousedown="event.preventDefault()">${notLiveText}</p>\n                    </div>\n                    ${retryHtml}\n                </div>\n            </div>\n        `,container.appendChild(overlay)}function showFocusLoadingWithRetry(matchId){hideFocusPlayerError(),hideFocusNotLiveOverlay(),showFocusLoadingMessage();globalRetryManager.scheduleRetry(()=>{window.focusIsSilentRetry,async function retryFocusPlaybackSilently(matchId){try{if(!await checkLiveStatusFromRedis(matchId))return globalRetryManager.reset(),void showFocusNotLiveMessage();globalRetryManager.retryCount;const container=document.getElementById("focusPlayerContainer"),identifier=container?.dataset?.identifier,streamingType=container?.dataset?.streamingType||lastFocusStreamingType||"WebRTC";await async function attemptFocusPlaybackSilently(matchId,streamingType,identifier){window.focusIsSilentRetry=!0;try{return await tryLoadStream(matchId,streamingType,identifier)?(isStreamPlaying=!0,globalRetryManager.reset(),hideFocusLoadingMessage(),hideFocusPlayerError(),window.focusIsSilentRetry=!1,!0):(window.focusIsSilentRetry=!1,!1)}catch(error){return console.error("[FocusPlayer] 静默播放尝试异常:",error),window.focusIsSilentRetry=!1,!1}}(matchId,streamingType,identifier)}catch(error){console.error("[FocusRetryManager] 重试播放失败:",error)}}(matchId)})}function hideFocusNotLiveOverlay(){const container=document.getElementById("focusPlayerContainer");if(!container)return;const overlay=container.querySelector(".focus-not-live-overlay");overlay&&overlay.remove(),container.classList.remove("has-not-live-overlay")}function getCurrentFocusMatchId(){const activeCard=document.querySelector(".focus-card.active");return activeCard?.dataset?.matchId}function hideFocusPlayerError(){document.querySelectorAll(".focus-waiting-overlay, .focus-error-overlay, .focus-loading-overlay, .focus-not-live-overlay").forEach(overlay=>overlay.remove());const container=document.getElementById("focusPlayerContainer");container?.classList.remove("has-not-live-overlay"),function clearFocusPlayerCover(){applyFocusPlayerCover("")}(),globalRetryManager.reset(),window.focusIsSilentRetry=!1,globalRetryManager.reset()}async function initFocusChat(matchId,streamSessionId,explicitRoomId){const roomId=explicitRoomId||(streamSessionId?`${matchId}_${streamSessionId}`:matchId?.toString());if(!roomId)return void console.error("[FocusChat] 无效的聊天室 ID");if(focusChatConnection&&await leaveFocusChatRoom(),"undefined"==typeof signalR)return void console.error("[FocusChat] SignalR 未加载");focusCurrentChatRoomId=roomId,focusChatConnection=(new signalR.HubConnectionBuilder).withUrl("/chatHub").withAutomaticReconnect().build(),focusChatConnection.on("ReceiveMessage",function(message){!function appendFocusChatMessage(message){const container=document.getElementById("focusChatMessages");if(!container)return;const empty=container.querySelector(".focus-chat-empty, .chat-loading, .focus-chat-no-live");empty&&empty.remove();message.avatarUrl||message.avatar;const userName=message.userName||"Anonymous",content=(message.realUserName,message.id,message.content||""),{quoteUser:quoteUser,quoteText:quoteText,pureText:pureText}=extractFocusQuote(content),msgEl=buildFocusMessageElement(message);container.appendChild(msgEl);const wrapper=document.getElementById("focusChatMessagesWrapper");if(wrapper){const currentUser=(window.currentUserName||"Guest").trim().toLowerCase(),isSelfMessage=message.userName&&message.userName.trim().toLowerCase()===currentUser,shouldCountUnread="system"!==message.type&&!isSelfMessage;if(isFocusNearBottom()||!focusFirstLoadDone)scrollFocusChatWrapperToBottom(wrapper),focusUnreadCount=0,updateFocusUnreadBadge();else if(!focusSuppressUnread&&shouldCountUnread){focusUnreadCount++,updateFocusUnreadBadge();const scrollBottomBtn=document.getElementById("focusChatScrollBottom");scrollBottomBtn&&scrollBottomBtn.classList.remove("d-none")}}}(message);try{window.MatchSidebarInfoApp?.notifyNewChatMessage?.(message)}catch(_){}}),focusChatConnection.on("OnlineCount",function(count){const onlineNum=document.querySelector(".focus-chat-online .online-num");onlineNum&&(onlineNum.textContent=count)}),focusChatConnection.on("MessageDeleted",function(data){const msgEl=document.querySelector(`.focus-chat-msg[data-message-id="${data.messageId}"]`);msgEl&&(msgEl.style.transition="opacity 0.3s, transform 0.3s",msgEl.style.opacity="0",msgEl.style.transform="translateX(-20px)",setTimeout(()=>msgEl.remove(),300))}),focusChatConnection.on("ReactionUpdate",function(data){const msgEl=document.querySelector(`.focus-chat-msg[data-message-id="${data.messageId}"]`);if(msgEl){const reactionsContainer=msgEl.querySelector(".focus-chat-msg-reactions");reactionsContainer&&renderFocusReactions(reactionsContainer,data.reactions,data.messageId)}}),focusChatConnection.on("AnnouncementUpdated",function(data){!function applyFocusChatAnnouncementUpdate(data){if(!data)return;if(window.ChatAnnouncementUtil?.shouldApplyAnnouncementUpdate&&!window.ChatAnnouncementUtil.shouldApplyAnnouncementUpdate(data,focusCurrentChatRoomId,focusChatSettings))return;window.ChatAnnouncementUtil?.applyAnnouncementPayload?window.ChatAnnouncementUtil.applyAnnouncementPayload(focusChatSettings,data):(focusChatSettings.announcementEnabled=!0===data.announcementEnabled,focusChatSettings.announcementContent=data.announcementContent||"");renderFocusChatAnnouncement(data.action)}(data)}),console.warn("🔔 [首页] 注册 StreamRouteChanged 事件处理器"),focusChatConnection.on("StreamRouteChanged",function(data){console.warn("⚡ [首页] StreamRouteChanged 事件触发:",JSON.stringify(data));const currentMatchId=window.focusCurrentMatchId||focusCurrentChatRoomId,notifyMatchId=data.matchId||data.MatchId;if(console.warn("📍 [首页] 当前比赛ID:",currentMatchId,"通知比赛ID:",notifyMatchId),notifyMatchId&&String(notifyMatchId)===String(currentMatchId)){console.warn("✅ [首页] 比
1赛ID匹配,准备刷新页面");const tipText="zh"===(document.documentElement.lang||"vi")?"检测到更佳播放线路,马上为您切换...":"Phát hiện đường truyền tốt hơn, đang chuyển đổi cho bạn...",tip=document.createElement("div");tip.className="stream-route-tip",tip.innerHTML=`\n                    <div class="stream-route-tip-content">\n                        <i class="bi bi-lightning-charge-fill me-2"></i>\n                        <span>${tipText}</span>\n                    </div>\n                `,document.body.appendChild(tip),setTimeout(()=>{window.location.reload()},2e3)}else console.warn("❌ [首页] 比赛ID不匹配,忽略通知")}),updateFocusChatStatus("connecting"),focusChatConnection.onreconnecting(()=>{updateFocusChatStatus("connecting")}),focusChatConnection.onreconnected(async()=>{if(updateFocusChatStatus("connected"),focusCurrentChatRoomId)try{await joinFocusChatRoom(focusCurrentChatRoomId),await loadFocusHistory(!1,{maxAttempts:3})}catch(e){console.error("[FocusChat] 重连后加入房间失败:",e)}}),focusChatConnection.onclose(error=>{window.stopChatWatchHeartbeat?.(),updateFocusChatStatus("disconnected"),focusCurrentChatRoomId&&setTimeout(()=>{"Disconnected"===focusChatConnection?.state&&reconnectFocusChat()},5e3)});try{await focusChatConnection.start(),updateFocusChatStatus("connected"),await loadFocusChatSettings(),await checkFocusMutePermission(),await joinFocusChatRoom(roomId),await loadFocusHistory(!1,{maxAttempts:3}),function initFocusScrollListener(){const wrapper=document.getElementById("focusChatMessagesWrapper");if(!wrapper||focusScrollListenerBound)return;focusScrollListenerBound=!0;const scrollBottomBtn=document.getElementById("focusChatScrollBottom");scrollBottomBtn&&scrollBottomBtn.addEventListener("click",scrollFocusToBottom);let scrollThrottle=null;wrapper.addEventListener("scroll",()=>{isFocusNearBottom()?(scrollBottomBtn&&scrollBottomBtn.classList.add("d-none"),focusUnreadCount=0,updateFocusUnreadBadge()):focusFirstLoadDone&&scrollBottomBtn&&scrollBottomBtn.classList.remove("d-none"),focusHasMore&&!focusIsLoadingMore&&(scrollThrottle||(scrollThrottle=setTimeout(()=>{(function isFocusNearTop(){const wrapper=document.getElementById("focusChatMessagesWrapper");if(!wrapper)return!1;const maxScroll=wrapper.scrollHeight-wrapper.clientHeight;return Math.abs(wrapper.scrollTop)>=maxScroll-60})()&&loadFocusHistory(!0),scrollThrottle=null},200)))})}()}catch(error){console.error("[FocusChat] 连接失败:",error),updateFocusChatStatus("disconnected")}const sendBtn=document.getElementById("focusChatSendBtn"),input=document.getElementById("focusChatInput");sendBtn&&(sendBtn.onclick=()=>sendFocusChatMessage()),input&&(input.onclick=()=>{input.hasAttribute("readonly")&&window.AuthModal&&window.AuthModal.open("login")},input.onkeypress=e=>{"Enter"===e.key&&(e.preventDefault(),sendFocusChatMessage())}),function initFocusEmojiPicker(){const emojiBtn=document.getElementById("focusEmojiBtn"),emojiPicker=document.getElementById("focusEmojiPicker"),emojiPickerContent=document.getElementById("focusEmojiPickerContent"),chatInput=document.getElementById("focusChatInput");if(!emojiBtn||!emojiPicker||!emojiPickerContent)return;emojiBtn.addEventListener("click",e=>{if(chatInput&&chatInput.hasAttribute("readonly"))return e.stopPropagation(),void(window.AuthModal&&window.AuthModal.open("login"));emojiPicker.classList.toggle("d-none")}),emojiPickerContent.innerHTML="",focusEmojis.forEach(emoji=>{const btn=document.createElement("button");btn.type="button",btn.className="emoji-item",btn.textContent=emoji,btn.addEventListener("click",()=>{if(chatInput){const start=chatInput.selectionStart,end=chatInput.selectionEnd,value=chatInput.value;chatInput.value=value.substring(0,start)+emoji+value.substring(end),chatInput.selectionStart=chatInput.selectionEnd=start+emoji.length,chatInput.focus()}emojiPicker.classList.add("d-none")}),emojiPickerContent.appendChild(btn)}),document.addEventListener("click",e=>{emojiPicker.classList.contains("d-none")||emojiPicker.contains(e.target)||emojiBtn.contains(e.target)||emojiPicker.classList.add("d-none")})}()}window.retryFocusPlayback=async function(){const container=document.getElementById("focusPlayerContainer");if(!container)return;const identifier=container.dataset.identifier||lastFocusIdentifier,streamingType=container.dataset.streamingType||lastFocusStreamingType,isBeauty="true"===container.dataset.beauty,retryBtn=document.querySelector(".error-retry-btn");if(retryBtn&&(retryBtn.classList.add("retrying"),setTimeout(()=>retryBtn.classList.remove("retrying"),1e3)),hideFocusNotLiveOverlay(),hideFocusLoadingMessage(),showFocusLoadingMessage(),setUserInitiatedPlay(),focusRetryCount=0,clearFocusRetryTimer(),isBeauty){if(!identifier){console.error("[FocusPlayer] 无法获取美女直播 identifier");window.focusChatI18n?.beautyLivePending||window.L?.T&&window.L.T("home.beautyLivePending");return void showFocusPlayerError(0,!0,!1,!1)}const beautyStreamingType=streamingType||"AWS-IVS";destroyFocusPlayer(),clearPlaybackTimeout(),lastFocusIdentifier=identifier,lastFocusStreamingType=beautyStreamingType;
1if(!await tryLoadStream(null,beautyStreamingType,identifier)){window.focusChatI18n?.beautyLivePending||window.L?.T&&window.L.T("home.beautyLivePending");showFocusPlayerError(0,!0,!1,!1)}return}const matchId=parseInt(container.dataset.matchId);matchId&&streamingType?loadFocusStreamWithRetry(matchId,streamingType,identifier):console.error("[FocusPlayer] 无法获取比赛信息")},window.manualRetryFocusPlayback=function(){globalRetryManager.reset();const overlay=document.getElementById("focusPlayerMaxRetriesOverlay");overlay&&overlay.remove();const matchId=getCurrentFocusMatchId();matchId?(showFocusLoadingMessage(),switchFocusMatch(parseInt(matchId))):console.error("[FocusManualRetry] 无法获取比赛ID")};const focusEmojis=["😀","😁","😂","🤣","😃","😄","😅","😆","😉","😊","😋","😎","😍","🥰","😘","😗","😙","😚","🙂","🤗","🤔","😐","😑","😶","🙄","😏","😣","😥","😮","🤐","😯","😪","😫","😴","😌","😛","😜","😝","🤤","😒","👍","👎","👏","🙌","🤝","🙏","💪","❤️","🔥","⚽","🏆","🎉","🎊","💯","✨","💥","💢","💦","💨","🚀"];async function loadFocusChatSettings(){try{const response=await fetch(window.ChatAnnouncementUtil?.buildSettingsUrl?.(focusCurrentChatRoomId)||"/api/chat/settings");if(response.ok){const data=await response.json();focusChatSettings={showAvatar:data.showAvatar??!0,nicknameMaskRule:data.nicknameMaskRule||"None",minIntervalSeconds:data.minIntervalSeconds||1,maxMessagesPerMinute:data.maxMessagesPerMinute||60,announcementEnabled:!0===data.announcementEnabled,announcementRotateSeconds:data.announcementRotateSeconds||5,announcements:Array.isArray(data.announcements)?data.announcements:[],announcementContent:data.announcementContent||"",isRoomOverride:!0===data.isRoomOverride,isStreamerOverride:!0===data.isStreamerOverride,streamSessionId:data.streamSessionId||null,streamerId:data.streamerId||null,roomStreamerId:data.roomStreamerId||null},renderFocusChatAnnouncement()}}catch(e){console.error("[FocusChat] 加载聊天设置失败:",e)}}function renderFocusChatAnnouncement(action){const bar=document.getElementById("focusChatAnnouncement"),textEl=document.getElementById("focusChatAnnouncementText");if(window.ChatAnnouncementUtil?.renderAnnouncementBar)return void window.ChatAnnouncementUtil.renderAnnouncementBar({bar:bar,textEl:textEl,settings:focusChatSettings,dismissState:focusChatAnnouncementDismiss,action:action});if(!bar||!textEl)return;const content=(focusChatSettings.announcementContent||"").trim();focusChatSettings.announcementEnabled&&content&&!focusChatAnnouncementDismiss.dismissed?(textEl.textContent=content,bar.classList.remove("d-none")):(textEl.innerHTML="",bar.classList.add("d-none"))}async function checkFocusMutePermission(){try{const response=await fetch("/api/chat/can-mute");if(response.ok){const data=await response.json();focusCanMuteUsers=data.canMute,focusIsVipUser=data.isVip,window.focusCanMuteUsers=focusCanMuteUsers,window.focusIsVipUser=focusIsVipUser,data.isAdmin,data.isSubAccountModerator}}catch(e){console.error("[FocusChat] 检查禁言权限失败:",e)}}function applyFocusNicknameMask(nickname){if(!nickname||"None"===focusChatSettings.nicknameMaskRule)return nickname;const rule=focusChatSettings.nicknameMaskRule,len=nickname.length;if("Middle"===rule&&len>2){const start=nickname.charAt(0),end=nickname.charAt(len-1);return start+"*".repeat(Math.min(len-2,4))+end}if("End"===rule&&len>1){const visible=Math.ceil(len/2);return nickname.substring(0,visible)+"*".repeat(len-visible)}if("Start"===rule&&len>1){const visible=Math.ceil(len/2);return"*".repeat(len-visible)+nickname.substring(len-visible)}return nickname}function showFocusMuteDialog(targetUserName){if(!focusCanMuteUsers||!targetUserName||targetUserName===window.currentUserName)return;window.focusChatI18n;const dialog=document.createElement("div");dialog.className="focus-mute-dialog-overlay",dialog.innerHTML=`\n            <div class="focus-mute-dialog">\n                <div class="focus-mute-dialog-header">\n                    <i class="fas fa-volume-mute"></i>
1\n                    <span>${window.L?.T?window.L.T("match.chat.muteDialogTitle"):"禁言用户"}</span>\n                </div>\n                <div class="focus-mute-dialog-body">\n                    <p>${window.L?.T?window.L.T("match.chat.confirmMuteMessage",{userName:escapeHtml(targetUserName)}):`确定要禁言 <strong>${escapeHtml(targetUserName)}</strong> 吗?`}</p>\n                    <div class="focus-mute-duration-options">\n                        <label><input type="radio" name="focusMuteDuration" value="5" checked> ${window.L?.T?window.L.T("match.chat.muteDuration.5min"):"5分钟"}</label>\n                        <label><input type="radio" name="focusMuteDuration" value="15"> ${window.L?.T?window.L.T("match.chat.muteDuration.15min"):"15分钟"}</label>\n                        <label><input type="radio" name="focusMuteDuration" value="60"> ${window.L?.T?window.L.T("match.chat.muteDuration.1hour"):"1小时"}</label>\n                        <label><input type="radio" name="focusMuteDuration" value="1440"> ${window.L?.T?window.L.T("match.chat.muteDuration.24hours"):"24小时"}</label>\n                        <label><input type="radio" name="focusMuteDuration" value="0"> ${window.L?.T?window.L.T("match.chat.muteDuration.permanent"):"永久"}</label>\n                    </div>\n                    <input type="text" class="focus-mute-reason-input" placeholder="${window.L?.T?window.L.T("match.chat.muteReason"):"禁言原因(可选)"}" />\n                </div>\n                <div class="focus-mute-dialog-footer">\n                    <button class="focus-mute-cancel-btn">${window.L?.T?window.L.T("common.cancel"):"取消"}</button>\n                    <button class="focus-mute-confirm-btn">${window.L?.T?window.L.T("match.chat.confirmMute"):"确定禁言"}</button>\n                </div>\n            </div>\n        `,document.body.appendChild(dialog);const cancelBtn=dialog.querySelector(".focus-mute-cancel-btn");cancelBtn&&cancelBtn.addEventListener("click",()=>{dialog.remove()}),dialog.addEventListener("click",e=>{e.target===dialog&&dialog.remove()});const confirmBtn=dialog.querySelector(".focus-mute-confirm-btn");confirmBtn&&confirmBtn.addEventListener("click",async()=>{const duration=dialog.querySelector('input[name="focusMuteDuration"]:checked').value,reason=dialog.querySelector(".focus-mute-reason-input").value.trim();try{const response=await fetch("/api/chat/mute",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({userName:targetUserName,durationMinutes:parseInt(duration),reason:reason})});if(response.ok)showFocusChatError(window.L?.T?window.L.T("match.chat.muteSuccess"):"禁言成功");else{showFocusChatError((await response.json()).message||(window.L?.T?window.L.T("match.chat.muteFailed"):"禁言失败"))}}catch(e){console.error("[FocusChat] 禁言失败:",e),showFocusChatError(window.L?.T?window.L.T("match.chat.muteFailed"):"禁言失败")}dialog.remove()})}function focusChatHistoryDelay(ms){return new Promise(resolve=>setTimeout(resolve,ms))}async function reconnectFocusChat(){if(focusCurrentChatRoomId){updateFocusChatStatus("connecting");try{if("Disconnected"===focusChatConnection?.state)await focusChatConnection.start(),updateFocusChatStatus("connected"),await joinFocusChatRoom(focusCurrentChatRoomId),await loadFocusHistory(!1,{maxAttempts:3});else if(focusChatConnection)await joinFocusChatRoom(focusCurrentChatRoomId),await loadFocusHistory(!1,{maxAttempts:3});else{const parsed=function parseFocusChatRoomId(roomId){if(!roomId)return null;const id=String(roomId);if(id.startsWith("standalone-"))return{matchId:null,streamSessionId:null,explicitRoomId:id};const separatorIndex=id.lastIndexOf("_");if(separatorIndex>0&&separatorIndex<id.length-1){const sessionPart=id.slice(separatorIndex+1);if(/^\d+$/.test(sessionPart))return{matchId:id.slice(0,separatorIndex),streamSessionId:sessionPart,explicitRoomId:null}}return{matchId:id,streamSessionId:null,explicitRoomId:null}}(focusCurrentChatRoomId);parsed?.explicitRoomId?await initFocusChat(null,null,parsed.explicitRoomId):await initFocusChat(parsed?.matchI
1d,parsed?.streamSessionId)}}catch(error){console.error("[FocusChat] 重连失败:",error),updateFocusChatStatus("disconnected"),setTimeout(()=>{"Disconnected"===focusChatConnection?.state&&reconnectFocusChat()},1e4)}}}async function joinFocusChatRoom(roomId){if(focusChatConnection)try{const userName=window.currentUserName||"Guest";await focusChatConnection.invoke("JoinRoom",roomId.toString(),userName,window.getChatVisitorId()),window.startChatWatchHeartbeat?.(focusChatConnection,roomId.toString()),focusCurrentChatRoomId=roomId}catch(error){throw console.error("[FocusChat] 加入房间失败:",error),updateFocusChatStatus("disconnected"),error}}async function leaveFocusChatRoom(){if(focusChatConnection&&focusCurrentChatRoomId)try{window.stopChatWatchHeartbeat?.();const userName=window.currentUserName||"Guest";await focusChatConnection.invoke("LeaveRoom",focusCurrentChatRoomId.toString(),userName)}catch(error){console.error("[FocusChat] 离开房间失败:",error)}}async function sendFocusChatMessage(){const input=document.getElementById("focusChatInput");if(!input||!focusChatConnection)return;let content=input.value.trim();if(!content)return;const userName=window.currentUserName,i18n=window.focusChatI18n||{};if(!userName)return void(window.AuthModal?window.AuthModal.open("login"):showFocusChatError(i18n.loginRequired||"请先登录"));const throttleResult=function validateFocusMessageSend(){const settings=window.ChatEnhanced?.getSettings?.()||{minIntervalSeconds:2,maxMessagesPerMinute:20},now=Date.now(),i18n=window.focusChatI18n||{},elapsedSeconds=(now-focusLastMessageTime)/1e3;if(focusLastMessageTime>0&&elapsedSeconds<(settings.minIntervalSeconds||2)){const remaining=Math.ceil((settings.minIntervalSeconds||2)-elapsedSeconds);return{valid:!1,message:(window.L?.T?window.L.T("match.chat.sendTooFrequent",{remaining:remaining}):null)||(i18n.sendTooFrequent?i18n.sendTooFrequent.replace("{remaining}",remaining):null)||`发送过于频繁,请等待 ${remaining} 秒后再试`}}const oneMinuteAgo=now-6e4;focusMessageTimestamps=focusMessageTimestamps.filter(t=>t>oneMinuteAgo);const maxPerMinute=settings.maxMessagesPerMinute||20;if(focusMessageTimestamps.length>=maxPerMinute)return{valid:!1,message:i18n.maxMessagesPerMinute||`每分钟最多发送 ${maxPerMinute} 条消息,请稍后再试`};return{valid:!0}}();if(throttleResult.valid){focusCurrentQuote&&(content=window.ChatQuoteUtil?.formatQuoteMessage(focusCurrentQuote,content)||content);try{const result=await focusChatConnection.invoke("SendMessage",focusCurrentChatRoomId.toString(),userName,content);if(result&&!0!==result.success){if(result&&result.code){showFocusChatError({Muted:i18n.muted||"您已被禁言",Throttled:i18n.throttled||"发送太频繁,请稍后再试",TooLong:i18n.tooLong||"消息太长",Empty:i18n.empty||"消息不能为空"}[result.code]||i18n.sendFailed||"发送失败")}}else input.value="",clearFocusQuote(),function recordFocusMessageSent(){const now=Date.now();focusLastMessageTime=now,focusMessageTimestamps.push(now)}(),focusUnreadCount=0,updateFocusUnreadBadge(),scrollFocusChatWrapperToBottom(document.getElementById("focusChatMessagesWrapper"))}catch(error){console.error("[FocusChat] 发送失败:",error),showFocusChatError(i18n.sendFailed||"发送失败")}}else showFocusChatError(throttleResult.message)}async function loadFocusHistory(append=!1,options={}){if(focusIsLoadingMore)return;if(!focusHasMore&&append)return;const roomId=focusCurrentChatRoomId;if(!roomId)return;const maxAttempts=append?1:options.maxAttempts??3,retryDelayMs=options.retryDelayMs??1e3,page=append?focusCurrentPage:1;focusIsLoadingMore=!0,append&&function showFocusLoadMoreSkeleton(){const container=document.getElementById("focusChatMessages");if(!container)return;if(container.querySelector(".focus-load-more-area"))return;const skeleton=document.createElement("div");skeleton.className="focus-load-more-area",skeleton.innerHTML='\n            <div class="focus-load-more-skeleton">\n                <div class="focus-skeleton-avatar"></div>\n                <div class="focus-skeleton-content">\n                    <div class="focus-skeleton-line"></div>\n                    <div class="focus-skeleton-line"></div>\n                </div>\n            </div>\n        ',container.insertBefore(skeleton,container.firstChild)}();try{let messages=null,lastError=null;for(let attempt=1;attempt<=maxAttempts;attempt++)try{const url=`/Match/GetChatHistory?roomId=${encodeURIComponent(roomId)}&page=${page}&pageSize=50`,response=await fetch(url);if(!response.ok){if(lastError=new Error(`HTTP ${response.status}`),attempt<maxAttempts){await focusChatHistoryDelay(retryDelayMs*attempt);continue}break}messages=await response.json();break}catch(err){lastError=err,attempt<maxAttempts&&await focusChatHistoryDelay(retryDelayMs*attempt)}if(null===messages){console.error("[FocusChat] 加载历史消息失败:",lastError);const container=document.getElementById("focusChatMessages"),hasExisting=container?.querySelector(".focus-chat-msg");return void(append||hasExisting||setTimeout(()=>loadFocusHistory(!1,{maxAttempts:3}),3e3))}const container=document.getElementById("focusChatMessages");if(!container)return;if(append)focusHasMore=50===messages.length;else if(container.innerHTML="",focusCurrentPage=2,focusHasMore=50===messages.length,0===messages.length)return function showFocusChatEmpty(){const container=document.getElementById("focusChatMessages");if(!container)return;const i18n=window.focusChatI18n||{};container.innerHTML=`\n            <div class="focus-chat-empty">\n                <div class="focus-chat-empty-icon">\n                    <i class="fas fa-comments"></i>\n                </div>\n                <p class="focus-chat-empty-title">${i18n.emptyTitle||"暂无消息"}</p>\n                <p class="focus-chat-empty-subtitle">${i18n.emptySubtitle||"来发第一条消息吧!"}</p>\n            </div>\n        `}(),void(focusFirstLoadDone=!0);const fragment=document.createDocumentFragment();
1if(messages.forEach(msg=>{const msgEl=buildFocusMessageElement(msg);fragment.appendChild(msgEl)}),append){const firstVisibleMsg=container.querySelector(".focus-chat-msg, .system-message");let anchorOffset=0;const wrapper=document.getElementById("focusChatMessagesWrapper");if(firstVisibleMsg&&wrapper){const wrapperRect=wrapper.getBoundingClientRect();anchorOffset=firstVisibleMsg.getBoundingClientRect().top-wrapperRect.top}if(hideFocusLoadMoreSkeleton(),container.insertBefore(fragment,container.firstChild),firstVisibleMsg&&wrapper){const wrapperRect=wrapper.getBoundingClientRect(),drift=firstVisibleMsg.getBoundingClientRect().top-wrapperRect.top-anchorOffset;wrapper.scrollTop+=drift}}else{container.appendChild(fragment),focusFirstLoadDone=!0;const wrapper=document.getElementById("focusChatMessagesWrapper");scrollFocusChatWrapperToBottom(wrapper),requestAnimationFrame(()=>{scrollFocusChatWrapperToBottom(wrapper),focusUnreadCount=0,updateFocusUnreadBadge();const scrollBottomBtn=document.getElementById("focusChatScrollBottom");scrollBottomBtn&&scrollBottomBtn.classList.add("d-none"),setTimeout(()=>{focusSuppressUnread=!1},500)})}const empty=container.querySelector(".focus-chat-empty");empty&&empty.remove(),append&&focusCurrentPage++}catch(e){console.error("[FocusChat] 加载历史消息失败:",e)}finally{focusIsLoadingMore=!1,hideFocusLoadMoreSkeleton()}}function scrollFocusChatWrapperToBottom(wrapper){if(!wrapper)return;const snap=()=>{wrapper.scrollTop=0;const overflow=wrapper.scrollHeight-wrapper.clientHeight;overflow>0&&Math.abs(wrapper.scrollTop)>4&&(wrapper.scrollTop=overflow)};snap(),requestAnimationFrame(snap)}function updateFocusUnreadBadge(){const unreadBadge=document.getElementById("focusUnreadBadge");unreadBadge&&focusFirstLoadDone&&!focusSuppressUnread&&(focusUnreadCount>0?(unreadBadge.textContent=focusUnreadCount>
199?"99+":focusUnreadCount,unreadBadge.classList.remove("d-none")):unreadBadge.classList.add("d-none"))}function scrollFocusToBottom(){const wrapper=document.getElementById("focusChatMessagesWrapper"),scrollBottomBtn=document.getElementById("focusChatScrollBottom");scrollFocusChatWrapperToBottom(wrapper),focusUnreadCount=0,updateFocusUnreadBadge(),scrollBottomBtn&&scrollBottomBtn.classList.add("d-none")}function isFocusNearBottom(){const wrapper=document.getElementById("focusChatMessagesWrapper");return!!wrapper&&Math.abs(wrapper.scrollTop)<=100}function hideFocusLoadMoreSkeleton(){const container=document.getElementById("focusChatMessages");if(!container)return;const skeleton=container.querySelector(".focus-load-more-area");skeleton&&skeleton.remove()}function buildFocusMessageElement(message){const msgEl=document.createElement("div");msgEl.className="focus-chat-msg",msgEl.dataset.messageId=message.id||0;const realUserName=message.realUserName||message.userName||"Anonymous";msgEl.dataset.userName=realUserName,msgEl.dataset.content=message.content||"";const avatarUrl=message.avatarUrl||message.avatar||"/images/default-avatar.svg",rawUserName=message.userName||"Anonymous",displayUserName=applyFocusNicknameMask(rawUserName),content=message.content||"",{quoteUser:quoteUser,quoteText:quoteText,quoteMsgId:quoteMsgId,pureText:pureText}=extractFocusQuote(content);msgEl.dataset.pureContent=pureText;let quoteHtml="";quoteUser&&null!=quoteText&&""!==quoteText&&(quoteHtml=window.ChatQuoteUtil?.buildQuoteHtml(quoteUser,quoteText,{escapeHtml:escapeHtml,maskUser:applyFocusNicknameMask,maxLen:30,className:"focus-chat-msg-quote"})||"");const tChat=(key,fallback)=>window.L?.T?window.L.T(`match.chat.${key}`):fallback,deleteBtn=(focusIsVipUser||focusCanMuteUsers)&&message.id?`<button class="focus-msg-action-btn focus-msg-delete-btn" title="${tChat("deleteMessage","删除")}" data-msg-id="${message.id}"><i class="fas fa-trash-alt"></i></button>`:"";let avatarElement=null;focusChatSettings.showAvatar&&(avatarElement=renderAvatar({nickname:rawUserName,avatarUrl:avatarUrl}),avatarElement.dataset.username=escapeHtml(rawUserName),avatarElement.dataset.realUsername=escapeHtml(realUserName),avatarElement.style.cursor="pointer");const bodyDiv=document.createElement("div");if(bodyDiv.className="focus-chat-msg-body",quoteHtml){const quoteDiv=document.createElement("div");quoteDiv.innerHTML=quoteHtml,bodyDiv.appendChild(quoteDiv.firstElementChild)}const headerDiv=document.createElement("div");headerDiv.className="focus-chat-msg-header";const messageHtml=window.ChatAnnouncementUtil?.parseChatMessageContent?window.ChatAnnouncementUtil.parseChatMessageContent(pureText):escapeHtml(pureText);headerDiv.innerHTML=`<span class="focus-chat-msg-meta"><span class="focus-chat-msg-name ${message.isAdmin?"admin-name":message.isStreamer?"streamer-name":""}">${escapeHtml(displayUserName)}</span> ${window.VipLevel?window.VipLevel.renderBadgeHtml(message.vipLevel||1,message.vipLevelName,message.isAdmin,message.isStreamer):""} <span class="focus-chat-msg-time">${function formatChatTime(dateStr){if(!dateStr)return"";return new Date(dateStr).toLocaleTimeString("zh-CN",{hour:"2-digit",minute:"2-digit"})}(message.createdAt)}</span> </span><span class="focus-chat-msg-text">${messageHtml}</span>`,bodyDiv.appendChild(headerDiv);const reactionsDiv=document.createElement("div");reactionsDiv.className="focus-chat-msg-reactions",bodyDiv.appendChild(reactionsDiv);const actionsDiv=document.createElement("div");actionsDiv.className="focus-chat-msg-actions",actionsDiv.innerHTML=`\n            <button class="focus-msg-action-btn focus-msg-reply-btn" title="${tChat("quoteReply","引用回复")}"><i class="fas fa-reply"></i></button>\n            <button class="focus-msg-action-btn focus-msg-emoji-btn" title="${tChat("emoji","表情")}"><i class="far fa-smile"></i></button>\n            ${deleteBtn}\n        `,bodyDiv.appendChild(actionsDiv),avatarElement&&msgEl.appendChild(avatarElement),msgEl.appendChild(bodyDiv),message.reactions&&message.reactions.length>0&&renderFocusReactions(reactionsDiv,message.reactions,message.id||0);const avatarWrapper=msgEl.querySelector(".focus-chat-msg-avatar");if(avatarWrapper){const targetUser=avatarWrapper.dataset.realUsername||avatarWrapper.dataset.username,targetNickname=displayUserName,targetAvatarUrl=avatarUrl;if(avatarWrapper.addEventListener("click",e=>{e.stopPropagation(),function showChatUserPopup(event,username,nickname,avatarUrl,vipLevel,vipLevelName){document.querySelectorAll(".chat-user-popup").forEach(el=>el.remove());const isCurrentUser=username===window.currentUserName,displayName=nickname||username||"?",letter=displayName.trim()[0]?.toUpperCase()||"?",hasAvatar=avatarUrl&&"/images/default-avatar.svg"!==avatarUrl&&"undefined"!==avatarUrl&&"null"!==avatarUrl;let hash=0;for(let i=0;i<displayName.length;i++)hash=(hash<<5)-hash+displayName.charCodeAt(i),hash&=hash;const colors=["#FF6B6B","#4ECDC4","#45B7D1","#FFA07A","#98D8C8","#F7DC6F","#BB8FCE","#85C1E2","#F8B739","#52B788","#E63946","#06FFA5"],bgColor=colors[Math.abs(hash)%colors.length],avatarHtml=hasAvatar?`<img src="${avatarUrl}" alt="" style="width:100%;height:100%;object-fit:cover;border-radius:50%">`:`<span style="font-size:1.4rem;font-weight:600;color:#fff">${letter}</span>`,popup=document.createElement("div");
1popup.className="chat-user-popup",popup.innerHTML=`\n            <div class="chat-user-popup-avatar" style="background:${hasAvatar?"transparent":bgColor}">\n                ${avatarHtml}\n            </div>\n            <div class="chat-user-popup-name">${escapeHtml(displayName)}</div>\n            <div style="margin-bottom:4px">${window.VipLevel?window.VipLevel.renderBadgeHtml(vipLevel||1,vipLevelName):""}</div>\n            <div class="chat-user-popup-vip-loading" style="text-align:center;padding:8px 0;font-size:0.65rem;color:#64748b"><i class="fas fa-spinner fa-spin"></i></div>\n            <div class="chat-user-popup-actions">\n                ${isCurrentUser?`<button class="chat-user-popup-btn chat-user-popup-btn-primary" onclick="window.AvatarUpload?.open(); document.querySelectorAll('.chat-user-popup').forEach(e=>e.remove())">\n                    <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>\n                    ${window.L?.T?window.L.T("user.changeAvatar"):"Đổi ảnh đại diện"}\n                </button>`:""}\n                ${focusCanMuteUsers&&!isCurrentUser?`<button class="chat-user-popup-btn chat-user-popup-btn-danger" data-action="mute" data-username="${escapeHtml(username)}">\n                    <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>\n                    ${window.L?.T?window.L.T("match.chat.muteAction"):"Bị cấm ngôn"}\n                </button>`:""}\n            </div>\n        `,document.body.appendChild(popup),window.VipLevel&&window.VipLevel.fetchUserVipInfo&&window.VipLevel.fetchUserVipInfo(username).then(result=>{const loadingEl=popup.querySelector(".chat-user-popup-vip-loading");if(loadingEl&&result&&result.data){let html=window.VipLevel.renderPopupVipHtml(result.data,result.i18n);focusCanMuteUsers&&result.publicId&&(html+=window.VipLevel.renderPublicIdHtml(result.publicId)),loadingEl.outerHTML=html,window.VipLevel.bindPublicIdCopy(popup)}else loadingEl&&loadingEl.remove()});const muteBtn=popup.querySelector('[data-action="mute"]');if(muteBtn&&muteBtn.addEventListener("click",()=>{popup.remove(),showFocusMuteDialog(muteBtn.dataset.username)}),window.innerWidth<=768)popup.style.position="fixed",popup.style.bottom="0",popup.style.left="0",popup.style.right="0",popup.style.top="auto",popup.style.width="100%",popup.style.maxWidth="100%",popup.style.borderRadius="16px 16px 0 0",popup.style.maxHeight="70vh",popup.style.overflowY="auto";else{const rect=event.target.closest(".focus-chat-msg-avatar").getBoundingClientRect(),popupHeight=380;let top=rect.bottom+8,left=rect.left;top+popupHeight>window.innerHeight&&(top=Math.max(10,rect.top-popupHeight-8)),left+220>window.innerWidth&&(left=window.innerWidth-230),left<10&&(left=10),popup.style.top=top+"px",popup.style.left=left+"px"}setTimeout(()=>{const closeHandler=e=>{popup.contains(e.target)||(popup.remove(),document.removeEventListener("click",closeHandler))};document.addEventListener("click",closeHandler)},10)}(e,targetUser,targetNickname,targetAvatarUrl,message.vipLevel,message.vipLevelName)}),focusCanMuteUsers){let longPressTimer=null;avatarWrapper.addEventListener("touchstart",e=>{longPressTimer=setTimeout(()=>{e.preventDefault(),showFocusMuteDialog(targetUser)},500)}),avatarWrapper.addEventListener("touchend",()=>clearTimeout(longPressTimer)),avatarWrapper.addEventListener("touchmove",()=>clearTimeout(longPressTimer))}avatarWrapper.style.cursor="pointer"}return function bindFocusMessageActions(msgEl,message,pureText,quoteMeta){const userName=message.userName||"Anonymous",messageId=message.id||0,replyBtn=msgEl.querySelector(".focus-msg-reply-btn");replyBtn&&replyBtn.addEventListener("click",e=>{e.stopPropagation(),function setFocusQuote(userName,content,messageId){focusCurrentQuote={userName:userName,content:content,messageId:messageId||0};let quotePreview=document.getElementById("focusQuotePreview");if(!quotePreview){const inputArea=document.querySelector(".focus-chat-input-area");inputArea&&(quotePreview=document.createElement("div"),quotePreview.id="focusQuotePreview",quotePreview.className="focus-quote-preview",inputArea.insertBefore(quotePreview,inputArea.firstChild))}quotePreview&&(quotePreview.innerHTML=`\n                <div class="quote-preview-content">\n                    <i class="fas fa-reply"></i>
1\n                    <span class="quote-preview-user">${escapeHtml(userName)}</span>\n                    <span class="quote-preview-text">${escapeHtml(content.substring(0,40))}${content.length>40?"...":""}</span>\n                </div>\n                <button class="quote-preview-close" onclick="clearFocusQuote()"><i class="fas fa-times"></i></button>\n            `,quotePreview.classList.remove("d-none"));scrollFocusChatWrapperToBottom(document.getElementById("focusChatMessagesWrapper"));const input=document.getElementById("focusChatInput");input&&input.focus()}(userName,pureText,messageId)});const quoteBlock=msgEl.querySelector(".focus-chat-msg-quote, .message-quote");quoteBlock&&quoteMeta&&window.ChatQuoteUtil?.bindQuoteJump(quoteBlock,{quoteMsgId:quoteMeta.quoteMsgId,quoteUser:quoteMeta.quoteUser,quoteText:quoteMeta.quoteText},{messagesContainer:document.getElementById("focusChatMessages"),scrollWrapper:document.getElementById("focusChatMessagesWrapper"),options:{roomId:focusCurrentChatRoomId,buildElement:msg=>buildFocusMessageElement(msg),loadMore:async()=>{for(;focusIsLoadingMore;)await new Promise(r=>setTimeout(r,50));await loadFocusHistory(!0)},hasMore:()=>focusHasMore}});const emojiBtn=msgEl.querySelector(".focus-msg-emoji-btn");emojiBtn&&emojiBtn.addEventListener("click",e=>{e.stopPropagation(),function showFocusQuickEmoji(btn,messageId){document.querySelectorAll(".focus-quick-emoji").forEach(el=>el.remove());const quickEmojis=["👍","❤️","😂","😮","😢","🔥"],panel=document.createElement("div");panel.className="focus-quick-emoji",panel.innerHTML=quickEmojis.map(emoji=>`<button class="quick-emoji-btn" data-emoji="${emoji}">${emoji}</button>`).join("");const rect=btn.getBoundingClientRect();panel.style.position="fixed",panel.style.top=rect.top-40+"px";const panelWidth=240;let leftPos=rect.right+8;leftPos+panelWidth>window.innerWidth&&(leftPos=rect.left-panelWidth-8);leftPos<8&&(leftPos=8);panel.style.left=leftPos+"px",document.body.appendChild(panel);const autoCloseTimer=setTimeout(()=>{panel.parentNode&&panel.remove()},5e3);panel.querySelectorAll(".quick-emoji-btn").forEach(emojiBtn=>{emojiBtn.addEventListener("click",async()=>{const emoji=emojiBtn.dataset.emoji;clearTimeout(autoCloseTimer),panel.remove(),await toggleFocusReaction(messageId,emoji)})}),setTimeout(()=>{const closeHandler=e=>{panel.contains(e.target)||e.target===btn||(clearTimeout(autoCloseTimer),panel.remove(),document.removeEventListener("click",closeHandler))};document.addEventListener("click",closeHandler)},10)}(emojiBtn,messageId)});const deleteBtn=msgEl.querySelector(".focus-msg-delete-btn");deleteBtn&&deleteBtn.addEventListener("click",e=>{e.stopPropagation(),function showFocusDeleteConfirm(messageId){const existing=document.querySelector(".focus-delete-dialog");existing&&existing.remove();const i18n=window.focusChatI18n||{},dialog=document.createElement("div");dialog.className="focus-delete-dialog",dialog.innerHTML=`\n            <div class="focus-delete-content">\n                <div class="focus-delete-icon"><i class="fas fa-trash-alt"></i></div>\n                <div class="focus-delete-title">${i18n.deleteTitle||"确认删除消息"}</div>\n                <div class="focus-delete-text">${i18n.deleteText||"确定要删除这条消息吗?此操作无法撤销。"}</div>\n                <div class="focus-delete-actions">\n                    <button class="focus-delete-cancel">${i18n.cancel||"取消"}</button>\n                    <button class="focus-delete-confirm">${i18n.confirmDelete||"确认删除"}</button>\n                </div>\n            </div>\n        `,document.body.appendChild(dialog);const cancelBtn=dialog.querySelector(".focus-delete-cancel");cancelBtn&&cancelBtn.addEventListener("click",()=>{dialog.remove()});const confirmBtn=dialog.querySelector(".focus-delete-confirm");confirmBtn&&confirmBtn.addEventListener("click",async()=>{dialog.remove(),await async function deleteFocusMessage(messageId){if(!focusChatConnection||!focusIsVipUser&&!focusCanMuteUsers)return;try{const result=await focusChatConnection.invoke("DeleteMessage",messageId,focusCurrentChatRoomId.toString());result.success||showFocusChatError(result.message||"删除失败")}catch(e){console.error("[FocusChat] 删除消息失败:",e),show
1FocusChatError("删除消息失败,请稍后重试")}}(messageId)});dialog.addEventListener("click",e=>{e.target===dialog&&dialog.remove()})}(messageId)});msgEl.addEventListener("click",e=>{document.querySelectorAll(".focus-chat-msg.actions-visible").forEach(el=>{el!==msgEl&&el.classList.remove("actions-visible")}),msgEl.classList.toggle("actions-visible")})}(msgEl,message,pureText,{quoteUser:quoteUser,quoteText:quoteText,quoteMsgId:quoteMsgId}),msgEl}function extractFocusQuote(content){return window.ChatQuoteUtil?.extractQuote(content)||{quoteUser:null,quoteText:null,quoteMsgId:0,pureText:content||""}}async function toggleFocusReaction(messageId,emoji){if(!focusChatConnection)return;const userName=window.currentUserName,i18n=window.focusChatI18n||{};if(userName)try{await focusChatConnection.invoke("ToggleReaction",messageId,userName,emoji)}catch(error){console.error("[FocusChat] 表情反应失败:",error)}else window.AuthModal?window.AuthModal.open("login"):showFocusChatError(i18n.loginRequired||"请先登录")}function renderFocusReactions(container,reactions,messageId){container&&(container.innerHTML="",reactions&&0!==reactions.length&&reactions.forEach(r=>{const item=document.createElement("span");item.className="focus-reaction-item",item.title=r.users?r.users.join(", "):"",item.innerHTML=`${r.emoji}<span class="reaction-count">${r.count>1?r.count:""}</span>`,item.addEventListener("click",()=>{toggleFocusReaction(messageId,r.emoji)}),container.appendChild(item)}))}function updateFocusChatStatus(status){const badge=document.getElementById("focusChatStatusBadge");if(badge)switch(badge.className="focus-chat-status",status){case"connecting":badge.className="focus-chat-status connecting";break;case"connected":badge.className="focus-chat-status connected";break;default:badge.className="focus-chat-status disconnected"}}function showFocusChatError(message){const errorEl=document.getElementById("focusChatError");if(!errorEl)return;const decodedMessage=decodeHtmlEntities(message);errorEl.textContent=decodedMessage,errorEl.classList.remove("d-none"),setTimeout(()=>{errorEl.classList.add("d-none")},3e3)}function decodeHtmlEntities(text){if(!text)return text;const textarea=document.createElement("textarea");return textarea.innerHTML=text,textarea.value}function escapeHtml(text){const div=document.createElement("div");return div.textContent=text,div.innerHTML}function scrollActiveCardToLeft(card,container){if(!card||!container)return;const cardItem=card.closest(".focus-card-item")||card,containerRect=container.getBoundingClientRect(),scrollOffset=cardItem.getBoundingClientRect().left-containerRect.left+container.scrollLeft;container.scrollTo({left:scrollOffset,behavior:"smooth"})}window.addEventListener("userLoggedIn",()=>{checkFocusMutePermission()}),window.clearFocusQuote=function(){focusCurrentQuote=null;const quotePreview=document.getElementById("focusQuotePreview");quotePreview&&quotePreview.classList.add("d-none")},window.switchStreamTab=function(tabName){document.querySelectorAll(".focus-tab").forEach(tab=>{tab.dataset.tab===tabName?tab.classList.add("active"):tab.classList.remove("active")}),document.querySelectorAll("[data-tab-content]").forEach(content=>{content.dataset.tabContent===tabName?content.classList.remove("d-none"):content.classList.add("d-none")})},window.scrollActiveCardToLeft=scrollActiveCardToLeft;let focusCardsExpanded=!1;function decodeHtmlEntities(str){if(!str||!str.includes("&"))return str;const el=document.createElement("textarea");return el.innerHTML=str,el.value}function isBeautyCardLive(cardEl){return!!cardEl&&("running"===cardEl.dataset.status||cardEl.classList.contains("is-live"))}function getStatusText(status){return{"1H":"H1","2H":"H2"}[status]||status||""}function toStandaloneChatRoomId(identifier){if(!identifier)return null;const id=String(identifier);return id.startsWith("standalone-")?id:id.startsWith("manual-")?"standalone-"+id.slice(7):null}function getFocusNativeFullscreenElement(){return document.fullscreenElement||document.webkitFullscreenElement||document.mozFullScreenElement||document.msFullscreenElement||null}
1function exitFocusNativeFullscreen(){const exit=document.exitFullscreen||document.webkitExitFullscreen||document.mozCancelFullScreen||document.msExitFullscreen;if(!exit||!getFocusNativeFullscreenElement())return Promise.resolve();try{return Promise.resolve(exit.call(document)).catch(()=>{})}catch(_){return Promise.resolve()}}function enterFocusChatFullscreen(){const section=document.getElementById("focusLiveSection");if(!section||section.classList.contains("fullscreen"))return;const body=document.body,html=document.documentElement,scrollY=window.scrollY;section.classList.add("fullscreen"),body.classList.add("focus-fullscreen-open"),html.classList.add("focus-fullscreen-open"),body.style.overflow="hidden",body.style.position="fixed",body.style.top=`-${scrollY}px`,body.style.left="0",body.style.right="0",body.style.width="100%",section.dataset.scrollY=scrollY,function setupFocusFullscreenCloseBtn(section){if(section.querySelector(".focus-fullscreen-exit-btn"))return;const exitBtn=document.createElement("button");exitBtn.className="focus-fullscreen-exit-btn",exitBtn.innerHTML='<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>',exitBtn.title="关闭",exitBtn.setAttribute("aria-label","关闭全屏");let hideTimeout=null;function showBtn(){clearTimeout(hideTimeout),exitBtn.classList.add("show"),hideTimeout=setTimeout(()=>exitBtn.classList.remove("show"),3e3)}const playerContainer=section.querySelector(".focus-player-container");playerContainer&&(playerContainer.addEventListener("click",function(e){e.target===exitBtn||exitBtn.contains(e.target)||showBtn()}),playerContainer.addEventListener("touchstart",function(e){e.target===exitBtn||exitBtn.contains(e.target)||showBtn()}));exitBtn.onclick=function(e){e.stopPropagation(),toggleFocusPlayerFullscreen()},section.appendChild(exitBtn),showBtn()}(section)}function leaveFocusChatFullscreen(){const section=document.getElementById("focusLiveSection");if(!section||!section.classList.contains("fullscreen"))return;const body=document.body,html=document.documentElement;section.classList.remove("fullscreen"),body.classList.remove("focus-fullscreen-open"),html.classList.remove("focus-fullscreen-open"),body.style.overflow="",body.style.position="",body.style.top="",body.style.left="",body.style.right="",body.style.width="";const savedScrollY=section.dataset.scrollY;savedScrollY&&(window.scrollTo(0,parseInt(savedScrollY)),delete section.dataset.scrollY);const exitBtn=section.querySelector(".focus-fullscreen-exit-btn");exitBtn&&exitBtn.remove(),"function"==typeof window.setMobileFsMode&&window.setMobileFsMode("normal")}function updateNavArrows(container){const wrapper=container.closest(".focus-cards-wrapper");if(!wrapper)return;const leftBtn=wrapper.querySelector(".focus-cards-nav-left"),rightBtn=wrapper.querySelector(".focus-cards-nav-right");if(!leftBtn||!rightBtn)return;if(!(container.scrollWidth>container.offsetWidth))return wrapper.classList.add("no-scroll"),void updateFocusCardsScrollbar(container);wrapper.classList.remove("no-scroll");const atStart=container.scrollLeft<=0,atEnd=container.scrollLeft+container.offsetWidth>=container.scrollWidth-1;atStart?leftBtn.classList.add("disabled"):leftBtn.classList.remove("disabled"),atEnd?rightBtn.classList.add("disabled"):rightBtn.classList.remove("disabled"),updateFocusCardsScrollbar(container)}function ensureFocusCardsScrollbar(container){const wrapper=container.closest(".focus-cards-wrapper");if(!wrapper||wrapper.querySelector(".focus-cards-scrollbar-track"))return wrapper?.querySelector(".focus-cards-scrollbar-track")||null;const track=document.createElement("div");track.className="focus-cards-scrollbar-track is-hidden",track.setAttribute("aria-hidden","true");const thumb=document.createElement("div");return thumb.className="focus-cards-scrollbar-thumb",track.appendChild(thumb),wrapper.appendChild(track),function bindFocusCardsScrollbar(container,track,thumb){let dragging=!1,dragStartX=0,dragStartScroll=0,dragThumbWidth=0,dragThumbMaxLeft=0,dragMaxScroll=0,suppressScrollbarSync=!1;const setFreeScrolling=enabled=>{container.classList.toggle("is-free-scrolling",enabled)},refreshDragMetrics=()=>{const trackWidth=track.clientWidth;dragThumbWidth=Math.max(container.clientWidth/container.scrollWidth*trackWidth,32),dragThumbMaxLeft=Math.max(trackWidth-dragThumbWidth,0),dragMaxScroll=Math.max(container.scrollWidth-container.clientWidth,0)},applyThumbPosition=scrollLeft=>{const thumbLeft=dragMaxScroll>0?scrollLeft/dragMaxScroll*dragThumbMaxLeft:0;thumb.style.width=`${dragThumbWidth}px`,thumb.style.transform=`translate3d(${thumbLeft}px, 0, 0)`},onPointerMove=event=>{if(!dragging)return;const deltaX=event.clientX-dragStartX,scrollDelta=dragThumbMaxLeft>0?deltaX/dragThumbMaxLeft*dragMaxScroll:0,nextScroll=Math.max(0,Math.min(dragStartScroll+scrollDelta,dragMaxScroll));suppressScrollbarSync=!0,container.scrollLeft=nextScroll,applyThumbPosition(nextScroll),suppressScrollbarSync=!1},stopDragging=()=>{dragging&&(dragging=!1,thumb.classList.remove("is-dragging"),setFreeScrolling(!1),document.body.style.userSelect="",document.removeEventListener("pointermove",onPointerMove),document.removeEventListener("pointerup",stopDragging),document.removeEventListener("pointercancel",stopDragging),updateFocusCardsScrollbar(container))};if(container.addEventListener("scroll",()=>{suppressScrollbarSync||dragging||updateFocusCardsScrollbar(container)},{passive:!0}),"undefined"!=typeof ResizeObserver){const resizeObserver=new ResizeObserver(()=>updateFocusCardsScrollbar(container));resizeObserver.observe(container),resizeObserver.observe(track)}track.addEventListener("click",event=>{if(dragging||event.target===thumb)return;refreshDragMetrics();const rect=track.getBoundingClientRect(),clickX=event.clientX-rect.left,targetLeft=Math.max(0,Math.min(clickX-dragThumbWidth/2,rect.width-dragThumbWidth)),targetScroll=dragThumbMaxLeft>0?targetLeft/dragThumbMaxLeft*dragMaxScroll:0;
1container.scrollTo({left:targetScroll,behavior:"smooth"})}),thumb.addEventListener("pointerdown",event=>{event.preventDefault(),dragging=!0,refreshDragMetrics(),dragStartX=event.clientX,dragStartScroll=container.scrollLeft,thumb.classList.add("is-dragging"),setFreeScrolling(!0),document.body.style.userSelect="none",thumb.setPointerCapture&&thumb.setPointerCapture(event.pointerId),document.addEventListener("pointermove",onPointerMove),document.addEventListener("pointerup",stopDragging),document.addEventListener("pointercancel",stopDragging)})}(container,track,thumb),track}function updateFocusCardsScrollbar(container){if(!container||window.matchMedia("(max-width: 768px)").matches)return;const track=ensureFocusCardsScrollbar(container);if(!track)return;const thumb=track.querySelector(".focus-cards-scrollbar-thumb");if(!thumb||thumb.classList.contains("is-dragging"))return;const maxScroll=container.scrollWidth-container.clientWidth;if(maxScroll<=1)return void track.classList.add("is-hidden");track.classList.remove("is-hidden");const trackWidth=track.clientWidth,thumbWidth=Math.max(container.clientWidth/container.scrollWidth*trackWidth,32),thumbMaxLeft=Math.max(trackWidth-thumbWidth,0),thumbLeft=container.scrollLeft/maxScroll*thumbMaxLeft;thumb.style.width=`${thumbWidth}px`,thumb.style.transform=`translate3d(${thumbLeft}px, 0, 0)`}function scheduleFocusCardsScrollUiRefresh(container,resetScroll=!1){container&&requestAnimationFrame(()=>{requestAnimationFrame(()=>{const wrapper=container.closest(".focus-cards-wrapper");if(!container.isConnected||container.classList.contains("d-none"))return;if(wrapper?.classList.contains("d-none"))return;const maxScroll=Math.max(container.scrollWidth-container.clientWidth,0);resetScroll?container.scrollLeft=0:container.scrollLeft>maxScroll&&(container.scrollLeft=maxScroll),updateNavArrows(container),updateFocusCardsScrollbar(container)})})}function initFocusCardsScrollbars(){document.querySelectorAll(".focus-match-cards").forEach(container=>{ensureFocusCardsScrollbar(container),updateFocusCardsScrollbar(container)})}window.toggleFocusCardsExpand=function(){const liveCards=document.getElementById("liveMatchCards"),upcomingCards=document.getElementById("upcomingMatchCards"),toggleBtn=document.getElementById("focusCardsToggle");focusCardsExpanded=!focusCardsExpanded;const expandText=window.L?.T?window.L.T("common.expand"):"展开",collapseText=window.L?.T?window.L.T("common.collapse"):"收起";if(focusCardsExpanded)liveCards&&liveCards.classList.add("focus-cards-expanded"),upcomingCards&&upcomingCards.classList.add("focus-cards-expanded"),toggleBtn&&(toggleBtn.innerHTML='<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M4 14h16M4 10h16"/></svg><span>'+collapseText+"</span>",toggleBtn.classList.add("expanded"));else{liveCards&&liveCards.classList.remove("focus-cards-expanded"),upcomingCards&&upcomingCards.classList.remove("focus-cards-expanded"),toggleBtn&&(toggleBtn.innerHTML='<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/></svg><span>'+expandText+"</span>",toggleBtn.classList.remove("expanded")),"function"==typeof window.scheduleFocusCardsScrollUiRefresh&&(window.scheduleFocusCardsScrollUiRefresh(liveCards,!1),window.scheduleFocusCardsScrollUiRefresh(upcomingCards,!1));const activeCard=document.querySelector(".focus-card.active, .focus-match-card.active");if(activeCard){const container=activeCard.closest("[data-tab-content]");container&&setTimeout(()=>scrollActiveCardToLeft(activeCard,container),100)}}},window.switchFocusMatch=async function(matchId,streamSessionId){const switchGen=beginFocusSwitch(),cardId=streamSessionId?`${matchId}_${streamSessionId}`:matchId.toString(),card=document.querySelector(`.focus-card[data-card-id="${cardId}"], .focus-match-card[data-card-id="${cardId}"]`);if(!card)return void console.warn("[FocusPlayer] 未找到卡片:",cardId);const identifier=card.dataset.identifier||card.dataset.cardId,isManualFootball="true"===card.dataset.manualFootball||parseInt(matchId,10)<0,streamingType=card.dataset.streamingType,homeTeam=card.dataset.homeTeam,awayTeam=card.dataset.awayTeam,homeLogo=card.dataset.homeLogo,awayLogo=card.dataset.awayLogo,homeScore=card.dataset.homeScore,awayScore=card.dataset.awayScore,status=card.dataset.status,minute=card.dataset.minute,leagueLogo=(card.dataset.matchDate,card.dataset.leagueLogo),matchUrl=card.dataset.matchUrl,cardLive=isFocusMatchCardLive
1(card);focusCurrentMatchId=matchId,activateFocusCard(card,{skipCover:!cardLive});const container=document.getElementById("focusPlayerContainer");container&&(container.dataset.matchId=matchId,container.dataset.streamingType=streamingType,container.dataset.identifier=identifier,container.dataset.manualFootball=isManualFootball?"true":"false",delete container.dataset.beauty),syncPlayerFollowFromCard(card);const teamsEl=document.querySelector(".focus-teams");teamsEl&&(teamsEl.style.display="");const statusContainer=document.querySelector(".focus-match-info .focus-status")||document.querySelector(".focus-status");statusContainer&&statusContainer.classList.remove("focus-status-beauty");const chatInput=document.getElementById("focusChatInput");chatInput&&(chatInput.disabled=!1);const chatSendBtn=document.getElementById("focusChatSendBtn");chatSendBtn&&(chatSendBtn.disabled=!1),isManualFootball?function updateFocusManualMatchInfo(homeLogo,awayLogo,homeTeam,awayTeam,status){const matchInfoEl=document.getElementById("focusMatchInfo");if(!matchInfoEl)return;const isLive="LIVE"===status||"running"===status,homeName=escapeHtml(homeTeam||""),awayName=escapeHtml(awayTeam||""),statusHtml=isLive?'<div class="focus-status"><span class="status-text">LIVE</span></div>':"";matchInfoEl.innerHTML=`\n            <div class="focus-teams focus-teams-manual">\n                <div class="focus-team-block">\n                    <img src="${escapeHtml(optimizeImageUrl(homeLogo,"teams"))}" alt="${homeName}" class="focus-team-logo"\n                         onerror="this.onerror=null; this.src='/images/placeholder-team.svg';">\n                    <span class="focus-team-label">${homeName}</span>\n                </div>\n                <span class="focus-manual-vs">VS</span>\n                <div class="focus-team-block">\n                    <img src="${escapeHtml(optimizeImageUrl(awayLogo,"teams"))}" alt="${awayName}" class="focus-team-logo"\n                         onerror="this.onerror=null; this.src='/images/placeholder-team.svg';">\n                    <span class="focus-team-label">${awayName}</span>\n                </div>\n            </div>\n            ${statusHtml}\n        `}(homeLogo,awayLogo,homeTeam,awayTeam,status):function updateFocusMatchInfo(homeLogo,awayLogo,homeTeam,awayTeam,homeScore,awayScore,status,minute){function normalizeLogoUrl(url,type="teams"){return optimizeImageUrl(url,type)}const matchInfoEl=document.getElementById("focusMatchInfo");matchInfoEl?.querySelector(".focus-teams-manual")&&(matchInfoEl.innerHTML=`\n                <div class="focus-teams">\n                    <img src="${normalizeLogoUrl(homeLogo,"teams")}" alt="${homeTeam||""}" class="focus-team-logo"\n                         onerror="this.onerror=null; this.src='/images/placeholder-team.svg';">\n                    <span class="focus-score-display">\n                        <span class="home-score">${homeScore??0}</span>\n                        <span class="sep">-</span>\n                        <span class="away-score">${awayScore??0}</span>\n                    </span>\n                    <img src="${normalizeLogoUrl(awayLogo,"teams")}" alt="${awayTeam||""}" class="focus-team-logo"\n                         onerror="this.onerror=null; this.src='/images/placeholder-team.svg';">\n                </div>\n                <div class="focus-status">\n                    <span class="status-text">${getStatusText(status)}</span>\n                </div>\n            `);const teamsEl=document.querySelector(".focus-teams");if(teamsEl){const homeLogoImg=teamsEl.querySelector("img:first-child"),awayLogoImg=teamsEl.querySelector("img:last-child"),homeScoreEl=teamsEl.querySelector(".home-score"),awayScoreEl=teamsEl.querySelector(".away-score");homeLogoImg&&(homeLogoImg.dataset.originalUrl=homeLogo,setFocusLogo(homeLogoImg,homeLogo,"teams"),homeTeam&&(homeLogoImg.alt=homeTeam)),awayLogoImg&&(awayLogoImg.dataset.originalUrl=awayLogo,setFocusLogo(awayLogoImg,awayLogo,"teams"),awayTeam&&(awayLogoImg.alt=awayTeam)),homeScoreEl&&(homeScoreEl.textContent=homeScore),awayScoreEl&&(awayScoreEl.textContent=awayScore)}const statusEl=document.querySelector(".focus-status");if(statusEl){statusEl.classList.remove("focus-status-beauty"),statusEl.style.display="";const statusText=statusEl.querySelector(".status-text"),minuteText=statusEl.querySelector(".status-minute"),parsedMinute=parseInt(minute,10)||0;statusText&&(statusText.textContent=getStatusText(status)),minuteText&&(minuteText.textContent=parsedMinute>0?`${parsedMinute}'`:"",minuteText.style.display=parsedMinute>
10?"":"none",minuteText.dataset.matchId=focusCurrentMatchId,minuteText.dataset.minute=parsedMinute)}const footerTeamsEl=document.getElementById("focusFooterTeams");if(footerTeamsEl){const homeTeamEl=footerTeamsEl.querySelector(".home-team"),awayTeamEl=footerTeamsEl.querySelector(".away-team");homeTeamEl&&homeTeam&&(homeTeamEl.textContent=homeTeam),awayTeamEl&&awayTeam&&(awayTeamEl.textContent=awayTeam)}}(homeLogo,awayLogo,homeTeam,awayTeam,homeScore,awayScore,status,minute);const detailLink=document.getElementById("focusMatchDetailLink");if(detailLink)if(isManualFootball){const sessionId=card.dataset.streamSessionId||streamSessionId;if(matchUrl)detailLink.classList.remove("d-none"),detailLink.href=matchUrl;else if(sessionId){const slug=card.dataset.streamerSlug;detailLink.classList.remove("d-none"),detailLink.href=`/live/football/${sessionId}${slug?`/${slug}`:""}`}else detailLink.classList.add("d-none")}else detailLink.classList.remove("d-none"),detailLink.href=matchUrl;const chatMatchName=document.getElementById("focusChatMatchName");chatMatchName&&(chatMatchName.textContent=`${homeTeam} vs ${awayTeam}`);const chatLeagueLogo=document.getElementById("focusChatLeagueLogo");if(chatLeagueLogo&&(chatLeagueLogo.dataset.originalUrl=leagueLogo,setFocusLogo(chatLeagueLogo,leagueLogo,"leagues")),hideFocusPlayerError(),cardLive&&hideFocusNotLiveOverlay(),window.focusIsSwitching=!0,destroyFocusPlayer(),clearFocusRetryTimer(),clearPlaybackTimeout(),hideFocusPlayOverlay(),ensureFocusVideoMuted(),setUserInitiatedPlay(),await new Promise(resolve=>setTimeout(resolve,100)),isFocusSwitchStale(switchGen))return;if(window.focusIsSwitching=!1,cardLive?(showFocusLoadingMessage(),await loadFocusStreamWithRetry(matchId,streamingType,identifier)):showFocusNotLiveMessage(),isFocusSwitchStale(switchGen))return;const manualChatRoomId=isManualFootball?toStandaloneChatRoomId(identifier):null;await async function switchFocusChatRoom(matchId,streamSessionId,explicitRoomId){focusChatAnnouncementDismiss.reset();const announcementBar=document.getElementById("focusChatAnnouncement");announcementBar&&announcementBar.classList.add("d-none");const roomId=explicitRoomId||(streamSessionId?`${matchId}_${streamSessionId}`:matchId?.toString());if(!focusChatConnection)return void await initFocusChat(matchId,streamSessionId,explicitRoomId);if(roomId&&String(roomId)===String(focusCurrentChatRoomId)){try{"Disconnected"===focusChatConnection.state&&(await focusChatConnection.start(),updateFocusChatStatus("connected")),"Connected"===focusChatConnection.state&&await joinFocusChatRoom(roomId),await loadFocusChatSettings(),await loadFocusHistory(!1,{maxAttempts:3})}catch(e){console.error("[FocusChat] 同房间刷新失败:",e)}return}await leaveFocusChatRoom(),focusCurrentPage=1,focusHasMore=!0,focusFirstLoadDone=!1,focusUnreadCount=0,focusSuppressUnread=!0;const container=document.getElementById("focusChatMessages");container&&(container.innerHTML=`\n                <div class="chat-loading">\n                    <div class="loading-spinner">\n                        <div class="spinner-ring"></div>\n                        <div class="spinner-ring"></div>\n                        <div class="spinner-ring"></div>\n                    </div>\n                    <p class="loading-text">${window.L?.T("match.chat.loadingHistory")||"加载历史消息..."}</p>\n                </div>\n            `);await joinFocusChatRoom(roomId),await loadFocusChatSettings(),await loadFocusHistory(!1,{maxAttempts:3})}(matchId,streamSessionId,manualChatRoomId);try{window.MatchSidebarInfoApp?.setMatch?.(matchId,!isManualFootball&&Number(matchId)>0)}catch(_){}if(isFocusSwitchStale(switchGen))return;persistFocusSelectedCard(card),void 0!==window.streamerCacheManager&&streamSessionId&&window.streamerCacheManager.saveStreamerSelection(matchId,streamSessionId,streamingType);const cardsContainer=card.closest("[data-tab-content]");if(cardsContainer){const currentTab=cardsContainer.dataset.tabContent;currentTab&&localStorage.setItem("focus_cards_active_tab",currentTab)}},window.switchFocusBeauty=async function(cardEl,switchGen){if(!cardEl)return;const activeGen=switchGen??beginFocusSwitch(),identifier=cardEl.dataset.identifier,streamingType=cardEl.dataset.streamingType||"AWS-IVS",title=cardEl.dataset.title||"",streamer=cardEl.dataset.streamer||"";if(!identifier)return void console.warn("[FocusPlayer] 美女直播卡片缺少 identifier");
1const beautyLive=isBeautyCardLive(cardEl);if(!document.getElementById("focusLivePlayer"))return void console.warn("[FocusPlayer] 焦点播放器不存在,无法在焦点区播放美女直播");activateFocusCard(cardEl,{skipCover:!beautyLive});const sessionId=cardEl.dataset.beautyId,detailLink=document.getElementById("focusMatchDetailLink");if(detailLink)if(sessionId){detailLink.classList.remove("d-none");const slug=cardEl.dataset.streamerSlug?.trim();detailLink.href=`/live/beauty/${sessionId}${slug?`/${slug}`:""}`}else detailLink.classList.add("d-none");!function updateFocusBeautyInfo(title,streamer){const teamsEl=document.querySelector(".focus-teams");teamsEl&&(teamsEl.style.display="none");const statusContainer=document.querySelector(".focus-match-info .focus-status")||document.querySelector(".focus-status");if(!statusContainer)return;const statusText=statusContainer.querySelector(".status-text"),minuteText=statusContainer.querySelector(".status-minute"),trimmedTitle=(title||"").trim(),trimmedStreamer="function"==typeof formatBlvStreamerName?formatBlvStreamerName(streamer||""):(streamer||"").trim(),label=trimmedTitle||trimmedStreamer;statusContainer.classList.add("focus-status-beauty"),label?(statusContainer.style.display="",statusText&&(statusText.textContent=label),minuteText&&(minuteText.textContent="",minuteText.style.display="none")):(statusContainer.style.display="none",statusText&&(statusText.textContent=""),minuteText&&(minuteText.textContent="",minuteText.style.display="none"));const footerTeamsEl=document.getElementById("focusFooterTeams");if(footerTeamsEl){const homeTeamEl=footerTeamsEl.querySelector(".home-team"),awayTeamEl=footerTeamsEl.querySelector(".away-team");homeTeamEl&&(homeTeamEl.textContent=label),awayTeamEl&&(awayTeamEl.textContent="")}}(title,streamer);const container=document.getElementById("focusPlayerContainer");container&&(container.dataset.identifier=identifier,container.dataset.streamingType=streamingType,container.dataset.beauty="true",container.dataset.manualFootball="false",delete container.dataset.matchId),syncPlayerFollowFromCard(cardEl);!function updateFocusBeautyChatHeader(title,streamer,avatarUrl){const chatLeagueLogo=document.getElementById("focusChatLeagueLogo");chatLeagueLogo&&avatarUrl&&(chatLeagueLogo.dataset.originalUrl=avatarUrl,setFocusLogo(chatLeagueLogo,avatarUrl,"teams"))}(0,0,cardEl.dataset.streamerAvatar||""),function restoreFocusChatControls(){const input=document.getElementById("focusChatInput"),sendBtn=document.getElementById("focusChatSendBtn");input&&(input.disabled=!1,input.dataset.prevPlaceholder&&(input.placeholder=input.dataset.prevPlaceholder));sendBtn&&(sendBtn.disabled=!0===input?.hasAttribute("readonly"))}(),focusCurrentMatchId=null;try{window.MatchSidebarInfoApp?.setMatch?.(0,!1)}catch(_){}if(identifier)try{await initFocusChat(null,null,identifier)}catch(e){console.error("[FocusPlayer] 美女直播聊天室初始化失败:",e)}if(!isFocusSwitchStale(activeGen)&&(hideFocusPlayerError(),beautyLive&&hideFocusNotLiveOverlay(),window.focusIsSwitching=!0,destroyFocusPlayer(),clearFocusRetryTimer(),clearPlaybackTimeout(),function hideFocusPlayOverlaySafe(){try{hideFocusPlayOverlay()}catch(e){}}(),setUserInitiatedPlay(),await new Promise(resolve=>setTimeout(resolve,100)),!isFocusSwitchStale(activeGen))){if(window.focusIsSwitching=!1,lastFocusIdentifier=identifier,lastFocusStreamingType=streamingType,beautyLive){showFocusLoadingMessage();
1const played=await tryLoadStream(null,streamingType,identifier);if(isFocusSwitchStale(activeGen))return;if(!played){window.focusChatI18n?.beautyLivePending||window.L?.T&&window.L.T("home.beautyLivePending");showFocusPlayerError(0,!0,!1,!1)}}else showFocusNotLiveMessage();persistFocusSelectedCard(cardEl)}},window.filterFocusMatches=function(filterType,options){const opts="object"==typeof options&&null!==options?options:{};if(null!=opts.category||null==filterType&&null!=opts.live)applyHomeFocusState({category:opts.category??homeFocusState.category,live:opts.live??homeFocusState.live},opts);else{if("live"===filterType){const liveBtn=document.getElementById("focusLiveBadge");if(!homeFocusState.live&&(liveBtn?.disabled||0===getFocusLiveCount()))return;return void applyHomeFocusState({category:"all",live:!homeFocusState.live},opts)}applyHomeFocusState({category:toHomeCategory(filterType),live:!1},opts)}},window.switchFocusStreamSource=function(sourceData,streamingType,btn){document.querySelectorAll("#focusStreamSourceButtons .source-btn").forEach(b=>b.classList.remove("active")),btn&&btn.classList.add("active"),"WEBRTC"===streamingType?.toUpperCase()?loadFocusWebRtcStream(parseInt(sourceData)):loadFocusHlsStream(sourceData)},window.toggleFocusPlayerFullscreen=function(){const section=document.getElementById("focusLiveSection"),container=document.getElementById("focusPlayerContainer");if(!section)return;if(section.classList.contains("fullscreen"))return void leaveFocusChatFullscreen();"function"==typeof window.setMobileFsMode&&window.setMobileFsMode("chat");const fsEl=getFocusNativeFullscreenElement();fsEl&&(fsEl===container||container&&container.contains(fsEl))?exitFocusNativeFullscreen().then(enterFocusChatFullscreen):enterFocusChatFullscreen()},window.toggleFocusPlayerVideoFullscreen=function(){const section=document.getElementById("focusLiveSection"),container=document.getElementById("focusPlayerContainer"),video=document.getElementById("focusLivePlayer");if(!container)return;const enterVideoFullscreen=()=>
1{if("function"==typeof window.toggleMobileVideoFullscreen&&"function"==typeof window.isMobilePlaybackDevice&&window.isMobilePlaybackDevice())return void window.toggleMobileVideoFullscreen(container,video);if("function"==typeof window.setMobileFsMode){const fsEl=getFocusNativeFullscreenElement();fsEl===container||fsEl&&container.contains(fsEl)?window.setMobileFsMode("normal"):window.setMobileFsMode("video")}const fsEl=getFocusNativeFullscreenElement();if(fsEl===container||fsEl&&container.contains(fsEl))return void exitFocusNativeFullscreen();const request=container.requestFullscreen||container.webkitRequestFullscreen||container.mozRequestFullScreen||container.msRequestFullscreen;if(request)try{Promise.resolve(request.call(container)).catch(()=>{})}catch(_){}};if(section?.classList.contains("fullscreen"))return leaveFocusChatFullscreen(),void exitFocusNativeFullscreen().then(enterVideoFullscreen);enterVideoFullscreen()},document.addEventListener("keydown",function(e){if("Escape"===e.key){const section=document.getElementById("focusLiveSection");section&&section.classList.contains("fullscreen")&&toggleFocusPlayerFullscreen()}}),window.updateFocusNavArrows=updateNavArrows,window.updateFocusCardsScrollbar=updateFocusCardsScrollbar,window.scheduleFocusCardsScrollUiRefresh=scheduleFocusCardsScrollUiRefresh,window.initFocusCardsScrollbars=initFocusCardsScrollbars,document.addEventListener("DOMContentLoaded",()=>{let resizeTimer;initFocusCardsScrollbars(),document.querySelectorAll(".focus-match-cards").forEach(container=>{updateNavArrows(container),container.addEventListener("scroll",()=>updateNavArrows(container))}),window.addEventListener("resize",()=>{clearTimeout(resizeTimer),resizeTimer=setTimeout(()=>{document.querySelectorAll(".focus-match-cards").forEach(container=>{updateNavArrows(container),updateFocusCardsScrollbar(container)})},150)})})}(),window.scrollFocusCards=function(direction,containerId){const container=document.getElementById(containerId);if(!container)return void console.error("[ScrollCards] 容器未找到:",containerId);if(container.classList.contains("d-none"))return void console.warn("[ScrollCards] 容器被隐藏,跳过滚动");const card=container.querySelector(".focus-card, .focus-match-card");if(!card)return void console.warn("[ScrollCards] 容器内没有卡片");const cardWidth=card.offsetWidth,distance=2*(cardWidth+12);container.scrollLeft,container.scrollWidth,container.offsetWidth,"left"===direction?container.scrollBy({left:-distance,behavior:"smooth"}):container.scrollBy({left:distance,behavior:"smooth"}),setTimeout(()=>{"function"==typeof window.updateFocusNavArrows?window.updateFocusNavArrows(container):"function"==typeof window.updateFocusCardsScrollbar&&window.updateFocusCardsScrollbar(container)},300)};let matchSelectorMatches=[],matchSelectorCurrentTab="all",matchSelectorSearchQuery="",matchSelectorFilterType="all";function getHomeFocusUiState(){const routing=window.homeFocusRouting||{},uiFilter=routing.uiFilter||("entertainment"===routing.category?"beauty":routing.category||"all");return{category:routing.category||"all",uiFilter:uiFilter,live:!!routing.live}}function getModalLiveCount(){const countEl=document.getElementById("focusLiveCount");if(countEl){const n=parseInt(countEl.textContent,10);if(!isNaN(n))return n}return matchSelectorMatches.filter(m=>m.isStreamerLiveNow).length+document.querySelectorAll("#liveMatchCards .beauty-card.is-live").length}function getMainFocusFilter(){return getHomeFocusUiState().uiFilter}function hasLiveWorldCupFocus(){if("function"==typeof window.hasLiveWorldCupFocus&&window.hasLiveWorldCupFocus!==hasLiveWorldCupFocus)return window.hasLiveWorldCupFocus();const section=document.getElementById("focusLiveSection");if("true"===section?.dataset.hasLiveWorldCup)return!0;if("false"===section?.dataset.hasLiveWorldCup)return!1;const container=document.getElementById("liveMatchCards");return!!container&&Array.from(container.querySelectorAll(':scope > .focus-card-item[data-is-world-cup="true"]')).some(item=>"true"===item.dataset.isLive||item.querySelector(".focus-card.is-live, .beauty-card.is-live"))}function hasFocusWorldCupCards(){const section=document.getElementById("focusLiveSection");if("true"===section?.dataset.hasWorldCup)return!0;
1if("false"===section?.dataset.hasWorldCup)return!1;const container=document.getElementById("liveMatchCards");return!!container&&container.querySelectorAll(':scope > .focus-card-item[data-is-world-cup="true"]').length>0}function updateMatchSelectorLabelState(){const liveCount=getModalLiveCount(),state=getHomeFocusUiState();matchSelectorFilterType=state.uiFilter,document.querySelectorAll(".match-selector-label").forEach(label=>{const filterType=label.dataset.filter;filterType&&("live"===filterType?(label.classList.toggle("active",state.live),label.classList.toggle("disabled",0===liveCount)):label.classList.toggle("active",!state.live&&filterType===state.uiFilter))})}function handleMatchSelectorTouchMove(e){const modalBody=document.querySelector("#matchSelectorModal .match-selector-body");modalBody&&modalBody.contains(e.target)||e.preventDefault()}function handleMatchSelectorEsc(e){"Escape"===e.key&&closeMatchSelectorModal()}function loadMatchSelectorData(){const dataEl=document.getElementById("focusMatchesData");if(dataEl)try{matchSelectorMatches=JSON.parse(dataEl.textContent),matchSelectorMatches.length}catch(e){console.error("[MatchSelector] Failed to parse match data:",e)}else console.error("[MatchSelector] focusMatchesData not found")}function getBeautyCardsForSelector(){return Array.from(document.querySelectorAll("#liveMatchCards .focus-card-item")).map(item=>{const card=item.querySelector(".beauty-card");if(!card)return null;const bg=card.querySelector(".beauty-card-bg"),avatarMatch=bg?.style.backgroundImage?.match(/url\(["']?(.+?)["']?\)/),timeBadge=card.querySelector(".focus-time-badge");return{sessionId:card.dataset.beautyId,identifier:card.dataset.identifier,title:card.dataset.title||"",streamer:card.dataset.streamer||"",streamerSlug:card.dataset.streamerSlug||"",streamerId:card.dataset.streamerId||"",status:card.dataset.status||"",isLive:card.classList.contains("is-live")||"running"===card.dataset.status,avatar:avatarMatch?avatarMatch[1]:"",sortTime:item.dataset.sortTime||timeBadge?.textContent||"",isFocusPinned:"true"===item.dataset.isFocusPinned,isLiveJumpPinned:"true"===item.dataset.isLiveJumpPinned,focusPinWeight:parseInt(item.dataset.focusPinWeight||"0",10)||0}}).filter(Boolean)}function renderBeautyCardForModal(b,isActive,liveText,beautyLabel){const bgStyle=b.avatar?`background-image:url(${b.avatar});`:"";let timeStr="";if(b.sortTime&&b.sortTime.includes("T")){const d=new Date(b.sortTime);isNaN(d.getTime())||(timeStr=new Intl.DateTimeFormat("zh-CN",{day:"2-digit",month:"2-digit",hour:"2-digit",minute:"2-digit",timeZone:"Asia/Ho_Chi_Minh"}).format(d))}const badgeHtml=b.isLive?`<div class="focus-card-badge"><span class="badge bg-danger beauty-live-badge"><span class="beauty-live-dot"></span>${liveText}</span></div>`:timeStr?`<div class="focus-card-badge focus-card-badge-right"><span class="badge focus-time-badge">${timeStr}</span></div>`:"";return`\n        <div class="focus-card-item">\n            <div class="focus-card beauty-card ${b.isLive?"is-live":""} ${isActive?"active":""}"\n                 data-beauty-id="${b.sessionId}"\n                 data-identifier="${b.identifier}"\n                 data-title="${b.title}"\n                 data-streamer="${b.streamer}"\n                 data-streamer-slug="${b.streamerSlug||""}"\n                 data-streamer-id="${b.streamerId||""}"\n                 data-status="${b.status}"\n                 onclick="selectBeautyFromModal('${b.sessionId}')">\n                <div class="focus-card-bg beauty-card-bg" style="${bgStyle}">\n                    <div class="beauty-card-tag">\n                        <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z"></path><path d="M19 10v2a7 7 0 0 1-14 0v-2"></path><line x1="12" y1="19" x2="12" y2="22"></line></svg>\n                        <span>${beautyLabel}</span>\n                    </div>\n                    <div class="match-live-badge-container">\n                        ${badgeHtml}\n                    </div>\n                    <div class="beauty-card-play">\n                        <svg width="40" height="40" viewBox="0 0 24 24" fill="rgba(255,255,255,0.95)" stroke="none"><circle cx="12" cy="12" r="11" fill="rgba(0,0,0,0.35)"></circle><polygon points="10 8 16 12 10 16" fill="#fff"></polygon></svg>\n                    </div>\n                    <div class="beauty-card-info">\n                        <div class="beauty-card-title">${b.title}</div>\n                    </div>\n                </div>\n            </div>\n            ${b.streamer?`\n                ${b.streamerId?`<a class="focus-card-streamer" href="/streamer/${b.streamerId}" onclick="event.stopPropagation();">`:'<div class="focus-card-streamer">'}\n                    <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="me-1"><path d="M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z"></path><path d="M19 10v2a7 7 0 0 1-14 0v-2"></path><line x1="12" y1="19" x2="12" y2="22"></line></svg>${"function"==typeof formatBlvStreamerName?formatBlvStreamerName(b.streamer):b.streamer}\n                ${b.streamerId?"</a>":"</div>"}\n            `:""}\n        </div>\n    `}window.openMatchSelectorModal=function(){const modal=document.getElementById("matchSelectorModal");modal&&(loadMatchSelectorData(),matchSelectorFilterType=getHomeFocusUiState().uiFilter,updateMatchSelectorLabelState(),"function"==typeof window.updateFocusFilterCounts&&window.updateFocusFilterCounts(),renderMatchSelectorCards(),modal.classList.add("active"),document.body.style.overflow="hidden",document.documentElement.classList.add("modal-open"),document.body.classList.add("modal-open"),document.addEventListener("touchmove",handleMatchSelectorTouchMove,{passive:!1}),setTimeout(()=>{const searchInput=document.getElementById("matchSelectorSearch");searchInput&&searchInput.focus()},100),document.addEventListener("keydown",handleMatchSelectorEsc))},window.closeMatchSelectorModal=function(){const modal=document.getElementById("matchSelectorModal");modal&&(modal.classList.remove("active"),document.body.style.overflow="",document.documentElement.classList.remove("modal-open"),document.body.classList.remove("modal-open"),document.removeEventListener("touchmove",handleMatchSelectorTouchMove,{passive:!1}),document.removeEventListener("keydown",handleMatchSelectorEsc))}
1,window.switchMatchSelectorTab=function(tab){renderMatchSelectorCards()},window.filterMatchesByType=function(type){"function"==typeof window.filterFocusMatches&&window.filterFocusMatches(type,{userInitiated:!0}),updateMatchSelectorLabelState(),renderMatchSelectorCards()},window.filterMatchSelectorCards=function(query){matchSelectorSearchQuery=query.toLowerCase().trim(),renderMatchSelectorCards()},window.renderMatchSelectorCards=function renderMatchSelectorCards(){const grid=document.getElementById("matchSelectorGrid"),emptyEl=document.getElementById("matchSelectorEmpty");if(!grid||!emptyEl)return;const activeCard=document.querySelector(".focus-card.active, .focus-match-card.active"),activeMatchId=activeCard?.dataset.matchId?parseInt(activeCard.dataset.matchId):null,activeBeautyId=activeCard?.dataset.beautyId||null,activeCardId=activeCard?.dataset.cardId||null,state=getHomeFocusUiState(),filter=state.uiFilter;matchSelectorFilterType=filter;const includeFootball="all"===filter||"football"===filter||"basketball"===filter||"worldcup"===filter,includeBeauty="all"===filter||"beauty"===filter,basketballOnly="basketball"===filter,footballOnly="football"===filter,liveOnly=state.live,worldCupOnly="worldcup"===filter;let entries=[];if(includeFootball&&matchSelectorMatches.forEach(m=>{if(matchSelectorSearchQuery){if(!`${m.homeTeam} ${m.awayTeam} ${m.league}`.toLowerCase().includes(matchSelectorSearchQuery))return}if(worldCupOnly&&!window.isFocusWorldCupMatch(m.leagueApiId,m.league))return;if(basketballOnly&&"basketball"!==m.sportType)return;if(footballOnly&&"basketball"===m.sportType)return;const isLive=!!m.isStreamerLiveNow;liveOnly&&!isLive||entries.push({kind:"football",isLive:isLive,sortTime:m.matchDate||"",data:m})}),includeBeauty&&getBeautyCardsForSelector().forEach(b=>{if(matchSelectorSearchQuery){if(!`${b.title} ${b.streamer}`.toLowerCase().includes(matchSelectorSearchQuery))return}liveOnly&&!b.isLive||entries.push({kind:"beauty",isLive:b.isLive,sortTime:b.sortTime||"",data:b})}),entries.sort((a,b)=>{const zoneA=a.data.isLiveJumpPinned&&a.isLive?3:a.data.isFocusPinned?2:a.isLive?1:0,zoneB=b.data.isLiveJumpPinned&&b.isLive?3:b.data.isFocusPinned?2:b.isLive?1:0;if(zoneA!==zoneB)return zoneB-zoneA;const weightDiff=(b.data.focusPinWeight||0)-(a.data.focusPinWeight||0);if(0!==weightDiff)return weightDiff;const tierA="beauty"===a.kind?1:a.data.hasStreamerTask?0:2,tierB="beauty"===b.kind?1:b.data.hasStreamerTask?0:2;return tierA!==tierB?tierA-tierB:String(a.sortTime).localeCompare(String(b.sortTime))}),0===entries.length)return grid.innerHTML="",void(emptyEl.style.display="flex");emptyEl.style.display="none";const liveText=document.querySelector("[data-live-badge-text]")?.dataset.liveBadgeText||"LIVE",beautyLabel=document.querySelector(".beauty-card-tag span")?.textContent?.trim()||(window.L?.T?window.L.T("home.beautyLive"):"")||"美女直播";grid.innerHTML=entries.map(entry=>{if("beauty"===entry.kind){const b=entry.data;return renderBeautyCardForModal(b,b.sessionId===activeBeautyId,liveText,beautyLabel)}const m=entry.data,cardId=m.streamSessionId?`${m.id}_${m.streamSessionId}`:m.id.toString(),isActive=m.id===activeMatchId&&(activeCardId===cardId||!m.streamSessionId),isLive=m.isStreamerLiveNow,hasStreamerTask=m.hasStreamerTask,matchDate=new Date(m.matchDate),timeStr=new Intl.DateTimeFormat("zh-CN",{month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",timeZone:"Asia/Ho_Chi_Minh"}).format(matchDate),hasStreamerBg=hasStreamerTask&&m.streamerCardBackground,bgStyle=hasStreamerBg?`background-image: url(${m.streamerCardBackground});`:"",isSatelliteLive=!hasStreamerTask,isWorldCup="function"==typeof window.isFocusWorldCupMatch&&window.isFocusWorldCupMatch(m.leagueApiId,m.league),liveBadgeLabel=isLive?"function"==typeof window.buildFocusLiveBadgeLabel?window.buildFocusLiveBadgeLabel(isWorldCup,liveText):`<span class="badge bg-danger">${liveText}</span>`:"";return`\n            <div class="focus-card-item">\n                <div class="focus-card ${isLive?"is-live":""} ${isActive?"active":""}" \n                     data-card-id="${cardId}"\n                     data-match-id="${m.id}"\n                     data-stream-session-id="${m.streamSessionId||""}"\n                     data-streaming-type="${m.streamingType||"AWS-IVS"}"\n                     data-home-team="${m.homeTeam}"\n                     data-away-team="${m.awayTeam}
1"\n                     data-home-logo="${m.homeTeamLogo}"\n                     data-away-logo="${m.awayTeamLogo}"\n                     data-home-score="${m.homeScore||0}"\n                     data-away-score="${m.awayScore||0}"\n                     data-status="${m.status||"NS"}"\n                     data-minute="${m.minute||0}"\n                     data-league-logo="${m.leagueLogo||""}"\n                     data-match-url="${m.matchUrl||""}"\n                     data-streamer-id="${m.streamerId||""}"\n                     data-streamer="${m.streamerNickname||""}"\n                     onclick="selectMatchFromModal(${m.id}, ${m.streamSessionId?`'${m.streamSessionId}'`:"null"})">\n                    <div class="focus-card-bg ${hasStreamerBg?"has-streamer-bg":""} ${isSatelliteLive?"satellite-live-bg":""}" style="${bgStyle}">\n                        ${isSatelliteLive?'\n                            <div class="satellite-animation">\n                                <div class="satellite-icon">\n                                    <svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">\n                                        <rect x="2" y="7" width="20" height="13" rx="2" ry="2"></rect>\n                                        <line x1="8" y1="21" x2="16" y2="21"></line>\n                                        <line x1="12" y1="17" x2="12" y2="21"></line>\n                                        <polyline points="17 2 12 7 7 2"></polyline>\n                                    </svg>\n                                </div>\n                                <div class="signal-waves">\n                                    <div class="wave wave-1"></div>\n                                    <div class="wave wave-2"></div>\n                                    <div class="wave wave-3"></div>\n                                </div>\n                            </div>\n                        ':""}\n                        <div id="match_live_badge_${cardId}" class="match-live-badge-container">\n                            ${isLive?`\n                                <div class="focus-card-badge">\n                                    ${liveBadgeLabel}\n                                </div>\n                                <div class="focus-card-badge focus-card-badge-right">\n                                    <span class="badge focus-time-badge">${timeStr}</span>\n                                </div>\n                            `:`\n                                <div class="focus-card-badge focus-card-badge-right">\n                                    <span class="badge focus-time-badge">${timeStr}</span>\n                                </div>\n                            `}\n                        </div>\n                    </div>\n                    <div class="focus-card-teams">\n                        <div class="focus-card-team-row">\n                            <img src="${m.homeTeamLogo}" alt="${m.homeTeam}" loading="lazy" onerror="this.onerror=null; this.src='/images/placeholder-team.svg';">\n                            <span class="focus-card-team-name">${m.homeTeam}</span>\n                            ${isLive?`<span class="focus-card-score home-score" data-match-id="${m.id}">${m.homeScore??0}</span>`:""}\n                        </div>\n                        <div class="focus-card-team-row">\n                            <img src="${m.awayTeamLogo}" alt="${m.awayTeam}" loading="lazy" onerror="this.onerror=null; this.src='/images/placeholder-team.svg';">\n                            <span class="focus-card-team-name">${m.awayTeam}</span>\n                            ${isLive?`<span class="focus-card-score away-score" data-match-id="${m.id}">${m.awayScore??0}</span>`:""}\n                        </div>\n                    </div>\n                </div>\n                ${hasStreamerTask&&m.streamerNickname?`\n                    ${m.streamerId?`<a class="focus-card-streamer" href="/streamer/${m.streamerId}" onclick="event.stopPropagation();">`:'<div class="focus-card-streamer">'}\n                        <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="me-1"><path d="M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z"></path><path d="M19 10v2a7 7 0 0 1-14 0v-2"></path><line x1="12" y1="19" x2="12" y2="22"></line></svg>${"function"==typeof formatBlvStreamerName?formatBlvStreamerName(m.streamerNickname):m.streamerNickname}\n                    ${m.streamerId?"</a>":"</div>"}\n                `:""}\n            </div>\n        `}).join("")},window.selectBeautyFromModal=function(sessionId){closeMatchSelectorModal();const card=document.querySelector(`#liveMatchCards .beauty-card[data-beauty-id="${sessionId}"]`);card&&"function"==typeof window.switchFocusBeauty&&window.switchFocusBeauty(card)},window.selectMatchFromModal=function(matchId,streamSessionId){null!=streamSessionId&&""!==streamSessionId||
1(streamSessionId=null),closeMatchSelectorModal(),"function"==typeof switchFocusMatch?switchFocusMatch(matchId,streamSessionId):"function"==typeof window.switchFocusMatch&&window.switchFocusMatch(matchId,streamSessionId)};

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.