121 lines
18 KiB
JavaScript
121 lines
18 KiB
JavaScript
// Layout and player controls retained from the HTML reference. Business data is loaded by the Next.js application.
|
||
|
||
/* ---- unified-empty-states-script ---- */
|
||
|
||
|
||
const emptyStateImages={"write":"/assets/ce2f84f155d32b0b.png","planet":"/assets/a6ba4b9749786513.png","records":"/assets/51e809aac4f1e40a.png","error":"/assets/7c46ae4811eea7e9.png","purchased":"/assets/b57b58bd55aeee91.png","profile":"/assets/1f0d06c643b6660a.png","content":"/assets/2a5a7edaf95ff943.png","data":"/assets/77dd91d1ecd3d16c.png","search":"/assets/f2ef01f956fdcd80.png","network":"/assets/47632de987649cda.png","messages":"/assets/9bb14f2dd28ab426.png","calendar":"/assets/eb4bee364040817e.png"};
|
||
function emptyStateMarkup(kind,options={}){
|
||
const defaults={search:['搜索无结果','换个关键词试试,也许下一部好剧就在这里。','查看全部短剧'],data:['暂无数据','这里还没有记录,去发现一些喜欢的短剧吧。','去发现好剧'],error:['加载失败','内容暂时未能加载,请稍后重试。','重新加载'],content:['暂无内容','精彩内容正在准备中,先去看看其他好剧吧。','随便逛逛']};
|
||
const config=defaults[kind]||defaults.content,escape=value=>String(value).replace(/[&<>"']/g,c=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]));
|
||
const title=options.title||config[0],description=options.description||config[1],label=options.action===false?'':(options.label||config[2]);
|
||
const action=label?(kind==='error'?'<button type="button" class="ui-empty-action" data-media="retry">'+escape(label)+'</button>':'<a class="ui-empty-action" href="/shorts/">'+escape(label)+'</a>'):'';
|
||
return '<div class="ui-empty-state" role="status"><img src="'+(emptyStateImages[options.image||kind]||emptyStateImages.content)+'" alt="'+escape(title)+'" aria-hidden="true"><h3>'+escape(title)+'</h3><p>'+escape(description)+'</p>'+action+'</div>';
|
||
}
|
||
|
||
;
|
||
|
||
|
||
/* ---- sidebar-initial-state ---- */
|
||
|
||
try{if(localStorage.getItem('jgdj-sidebar-collapsed')==='true')document.documentElement.classList.add('sidebar-collapsed')}catch{}
|
||
;
|
||
|
||
|
||
/* ---- collapsible-sidebar-script ---- */
|
||
|
||
|
||
(() => {
|
||
const root=document.documentElement;
|
||
const toggle=document.getElementById('sidebar-toggle');
|
||
const key='jgdj-sidebar-collapsed';
|
||
function update(){const collapsed=root.classList.contains('sidebar-collapsed');toggle.setAttribute('aria-expanded',String(!collapsed));toggle.setAttribute('aria-label',collapsed?'展开侧边栏':'收起侧边栏');toggle.title=collapsed?'展开侧边栏':'收起侧边栏'}
|
||
const header=toggle.closest('.sidebar-header');
|
||
toggle.addEventListener('click',()=>{const collapsed=root.classList.toggle('sidebar-collapsed');header.classList.toggle('just-collapsed',collapsed);try{localStorage.setItem(key,String(collapsed))}catch{}update()});
|
||
header.addEventListener('mouseleave',()=>header.classList.remove('just-collapsed'));
|
||
header.addEventListener('focusout',()=>header.classList.remove('just-collapsed'));
|
||
toggle.addEventListener('keydown',()=>header.classList.remove('just-collapsed'));
|
||
update();
|
||
})();
|
||
|
||
;
|
||
|
||
|
||
/* ---- custom-player-controls-script ---- */
|
||
|
||
|
||
function installPlayerControls(screen,currentEpisode=1,actions={}){
|
||
const video=screen.querySelector('video'),bar=screen.querySelector('.play-screen-controls'),overlay=screen.querySelector('.play-screen-overlay'),status=screen.querySelector('.play-source-status');
|
||
const paths={play:'<path d="m8 5 11 7-11 7Z" fill="currentColor" stroke="none"/>',pause:'<path d="M8 5v14M16 5v14" stroke-width="4"/>',next:'<path d="m5 5 10 7-10 7Z" fill="currentColor" stroke="none"/><path d="M18 5v14"/>',volume:'<path d="M4 9h4l5-4v14l-5-4H4Z"/><path d="M16 8a6 6 0 0 1 0 8m3-11a10 10 0 0 1 0 14"/>',mute:'<path d="M4 9h4l5-4v14l-5-4H4Z"/><path d="m17 9 5 6m0-6-5 6"/>',full:'<path d="M8 3H3v5m13-5h5v5M3 16v5h5m8 0h5v-5"/>',exit:'<path d="M3 8h5V3m8 0v5h5M8 21v-5H3m18 0h-5v5"/>'};
|
||
Object.assign(paths,{"play": "<path d=\"M4 18.7591L4 6.62263C4 4.13699 6.70123 2.57713 8.85714 3.81995L14.1201 6.8509L19.3831 9.89455C21.539 11.1374 21.539 14.2444 19.3831 15.4872L14.1201 18.5309L8.85714 21.5618C6.70123 22.8047 4 21.2575 4 18.7591Z\" fill=\"currentColor\" stroke=\"none\"/>", "centerPlay": "<path d=\"M12 3C16.9706 3 21 7.02944 21 12C21 16.9706 16.9706 21 12 21C7.02944 21 3 16.9706 3 12C3 7.02944 7.02944 3 12 3ZM10.7646 8.53613C10.1184 8.15144 9.31065 8.63262 9.31055 9.40234V14.5996C9.31079 15.3692 10.1185 15.8504 10.7646 15.4658L15.1279 12.8672C15.774 12.4822 15.7739 11.5198 15.1279 11.1348L10.7646 8.53613Z\" fill=\"currentColor\" stroke=\"none\"/>", "previous": "<path d=\"M22.0001 6.81803V17.3711C22.0001 19.5325 19.6513 20.8888 17.7766 19.8082L13.2003 17.1726L8.624 14.5261C6.74936 13.4454 6.74936 10.7437 8.624 9.66307L13.2003 7.01652L17.7766 4.381C19.6513 3.30033 22.0001 4.64566 22.0001 6.81803Z\" fill=\"currentColor\" stroke=\"none\"/>\n<path d=\"M3.82705 19.7311C3.37493 19.7311 3 19.3561 3 18.904V5.27431C3 4.82219 3.37493 4.44727 3.82705 4.44727C4.27916 4.44727 4.65409 4.82219 4.65409 5.27431V18.904C4.65409 19.3561 4.27916 19.7311 3.82705 19.7311Z\" fill=\"currentColor\" stroke=\"none\"/>", "next": "<path d=\"M3 6.81803V17.3711C3 19.5325 5.34881 20.8888 7.22345 19.8082L11.7998 17.1726L16.3761 14.5261C18.2507 13.4454 18.2507 10.7437 16.3761 9.66307L11.7998 7.01652L7.22345 4.381C5.34881 3.30033 3 4.64566 3 6.81803Z\" fill=\"currentColor\" stroke=\"none\"/>\n<path d=\"M21.1729 19.7311C20.7208 19.7311 20.3459 19.3561 20.3459 18.904V5.27431C20.3459 4.82219 20.7208 4.44727 21.1729 4.44727C21.6251 4.44727 22 4.82219 22 5.27431V18.904C22 19.3561 21.6361 19.7311 21.1729 19.7311Z\" fill=\"currentColor\" stroke=\"none\"/>", "pause": "<rect x=\"4.79962\" y=\"3.59985\" width=\"4.8\" height=\"16.8\" rx=\"2.4\" fill=\"currentColor\" stroke=\"none\"/>\n<rect x=\"14.3996\" y=\"3.59985\" width=\"4.8\" height=\"16.8\" rx=\"2.4\" fill=\"currentColor\" stroke=\"none\"/>"});
|
||
const icon=name=>'<svg viewBox="0 0 24 24" aria-hidden="true">'+paths[name]+'</svg>';
|
||
const last=bar.querySelector('[data-play-action="next"]')?.disabled;
|
||
bar.innerHTML=`<input class="player-range player-seek" type="range" min="0" max="100" step="0.1" value="0" aria-label="播放进度" disabled><div class="player-control-row"><button type="button" data-play-action="previous" aria-label="上一集" title="上一集" ${currentEpisode<=1?'disabled':''}>${icon('previous')}</button><button type="button" data-media="toggle" aria-label="播放" title="播放 / 暂停(空格)">${icon('play')}</button><button type="button" data-play-action="next" aria-label="下一集" title="下一集" ${last?'disabled':''}>${icon('next')}</button><span class="player-time">00:00 / 00:00</span><div class="player-control-spacer"></div><div class="player-menu-wrap"><button type="button" data-menu="quality" aria-expanded="false">清晰度</button><div class="player-options" data-options="quality" hidden><button type="button" role="menuitemradio" aria-checked="true">自动</button><button type="button" disabled>1080P 超清</button><button type="button" disabled>720P 高清</button><button type="button" disabled>480P 标清</button><small>等待接入多清晰度片源</small></div></div><div class="player-menu-wrap"><button type="button" data-menu="speed" aria-expanded="false">倍速</button><div class="player-options" data-options="speed" hidden>${[.5,.75,1,1.25,1.5,2].map(n=>`<button type="button" data-speed="${n}" role="menuitemradio" aria-checked="${n===1}">${n}×</button>`).join('')}</div></div><div class="player-volume"><button type="button" data-media="mute" aria-label="静音" title="静音(M)">${icon('volume')}</button><input class="player-range" type="range" min="0" max="1" step="0.01" value="1" aria-label="音量"></div><button type="button" data-media="fullscreen" aria-label="进入全屏" title="全屏(F)">${icon('full')}</button></div>`;
|
||
screen.tabIndex=0;screen.setAttribute('aria-label','视频播放器');
|
||
const seek=bar.querySelector('.player-seek'),volume=bar.querySelector('.player-volume input'),toggle=bar.querySelector('[data-media="toggle"]'),mute=bar.querySelector('[data-media="mute"]'),full=bar.querySelector('[data-media="fullscreen"]');
|
||
const center=overlay.querySelector('button');center.innerHTML=icon('centerPlay');center.removeAttribute('data-play-action');center.dataset.media='toggle';
|
||
const time=t=>{t=Number.isFinite(t)?Math.max(0,Math.floor(t)):0;return (t>=3600?Math.floor(t/3600)+':':'')+String(Math.floor(t/60)%60).padStart(2,'0')+':'+String(t%60).padStart(2,'0')};
|
||
function sync(){const duration=Number.isFinite(video.duration)?video.duration:0;seek.disabled=!duration;seek.max=duration||100;seek.value=video.currentTime||0;seek.style.setProperty('--fill',(duration?video.currentTime/duration*100:0)+'%');seek.setAttribute('aria-valuetext',time(video.currentTime)+' / '+time(duration));bar.querySelector('.player-time').textContent=time(video.currentTime)+' / '+time(duration);toggle.innerHTML=icon(video.paused?'play':'pause');toggle.setAttribute('aria-label',video.paused?'播放':'暂停');overlay.hidden=!video.paused;volume.value=video.muted?0:video.volume;volume.style.setProperty('--fill',Number(volume.value)*100+'%');mute.innerHTML=icon(video.muted||!video.volume?'mute':'volume');mute.setAttribute('aria-label',video.muted||!video.volume?'取消静音':'静音')}
|
||
async function play(){if(actions.toggle){try{await actions.toggle()}catch{status.textContent='播放失败,请重试';overlay.hidden=false}return}if(!video.currentSrc&&!video.getAttribute('src')&&!video.querySelector('source[src]')){status.textContent='本剧尚未提供视频源,接入视频后即可播放';return}if(video.paused){try{await video.play()}catch{status.textContent='暂时无法播放,请检查视频源后重试';overlay.hidden=false}}else video.pause()}
|
||
async function retry(){overlay.querySelector('.ui-empty-state')?.remove();center.hidden=false;status.hidden=false;status.textContent='正在重新加载…';try{if(actions.retry)await actions.retry();else{video.load();await play()}}catch{status.textContent='视频加载失败,请重试';overlay.hidden=false}}
|
||
async function fullscreen(){try{if(document.fullscreenElement)await document.exitFullscreen();else if(screen.requestFullscreen)await screen.requestFullscreen();else if(video.webkitEnterFullscreen)video.webkitEnterFullscreen();else throw Error()}catch{status.textContent='当前浏览器暂不支持全屏';overlay.hidden=false}}
|
||
function closeMenus(){bar.querySelectorAll('[data-options]').forEach(e=>e.hidden=true);bar.querySelectorAll('[data-menu]').forEach(e=>e.setAttribute('aria-expanded','false'))}
|
||
screen.addEventListener('click',e=>{const b=e.target.closest('button');if(!b){if(e.target===video){closeMenus();play()}return}if(['previous','next'].includes(b.dataset.playAction)||b.hasAttribute('data-show-unlock'))return;e.stopPropagation();if(b.dataset.media==='retry')void retry();if(b.dataset.media==='toggle')play();if(b.dataset.media==='mute'){if(video.muted||!video.volume){video.muted=false;if(!video.volume)video.volume=.5}else video.muted=true;sync()}if(b.dataset.media==='fullscreen')fullscreen();if(b.dataset.menu){const menu=bar.querySelector('[data-options="'+b.dataset.menu+'"]'),open=menu.hidden;closeMenus();menu.hidden=!open;b.setAttribute('aria-expanded',String(open))}else if(b.dataset.speed){video.playbackRate=Number(b.dataset.speed);bar.querySelector('[data-menu="speed"]').textContent=b.dataset.speed==='1'?'倍速':b.dataset.speed+'×';bar.querySelectorAll('[data-speed]').forEach(e=>e.setAttribute('aria-checked',String(e===b)));closeMenus()}else closeMenus()});
|
||
seek.addEventListener('input',()=>{if(Number.isFinite(video.duration)&&video.duration>0){video.currentTime=Math.min(video.duration,Math.max(0,Number(seek.value)));sync()}});
|
||
volume.addEventListener('input',()=>{video.volume=Number(volume.value);video.muted=video.volume===0;sync()});
|
||
screen.addEventListener('keydown',e=>{if(e.target.matches('input,button'))return;if(e.code==='Space'){e.preventDefault();play()}else if(e.key.toLowerCase()==='m'){mute.click()}else if(e.key.toLowerCase()==='f')fullscreen();else if(['ArrowLeft','ArrowRight'].includes(e.key)&&Number.isFinite(video.duration)){e.preventDefault();video.currentTime=Math.min(video.duration,Math.max(0,video.currentTime+(e.key==='ArrowRight'?5:-5)));sync()}else if(e.key==='Escape')closeMenus()});
|
||
screen.addEventListener('focusout',e=>{if(!screen.contains(e.relatedTarget))closeMenus()});
|
||
screen.addEventListener('fullscreenchange',()=>{const active=document.fullscreenElement===screen;full.innerHTML=icon(active?'exit':'full');full.setAttribute('aria-label',active?'退出全屏':'进入全屏')});
|
||
['loadedmetadata','durationchange','timeupdate','play','pause','ended','volumechange','emptied'].forEach(event=>video.addEventListener(event,sync));function failed(event){if(event.type==='error')status.textContent='视频加载失败,请重试';overlay.querySelector('.ui-empty-state')?.remove();center.hidden=true;status.hidden=true;overlay.insertAdjacentHTML('beforeend',emptyStateMarkup('error',{description:status.textContent}));overlay.hidden=false}video.addEventListener('error',failed);screen.addEventListener('playback:error',failed);
|
||
screen._disposePlayer=()=>video.pause();sync();
|
||
}
|
||
|
||
;
|
||
|
||
|
||
/* ---- sticky-search-header-script ---- */
|
||
|
||
|
||
(()=>{
|
||
const siteHeader=document.getElementById('global-site-header');if(!siteHeader)return;
|
||
let scheduled=false;
|
||
function update(){siteHeader.classList.toggle('is-scrolled',window.scrollY>8);scheduled=false}
|
||
function measure(){document.documentElement.style.setProperty('--global-header-height',siteHeader.getBoundingClientRect().height+'px')}
|
||
new ResizeObserver(measure).observe(siteHeader);measure();
|
||
window.addEventListener('scroll',()=>{if(!scheduled){scheduled=true;requestAnimationFrame(update)}},{passive:true});
|
||
window.addEventListener('site:route',update);update();
|
||
})();
|
||
|
||
;
|
||
|
||
|
||
/* ---- mobile-slide-navigation-script ---- */
|
||
|
||
|
||
(()=>{
|
||
const header=document.querySelector('#global-site-header .top'),sidebar=document.getElementById('site-sidebar');if(!header||!sidebar)return;
|
||
const mobile=matchMedia('(max-width:760px)'),trigger=document.createElement('button');trigger.type='button';trigger.className='mobile-menu-trigger';trigger.setAttribute('aria-label','打开导航菜单');trigger.setAttribute('aria-controls','mobile-navigation-drawer');trigger.setAttribute('aria-expanded','false');trigger.innerHTML='<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 6h16M4 12h16M4 18h16"/></svg>';header.prepend(trigger);
|
||
const drawer=document.createElement('dialog');drawer.id='mobile-navigation-drawer';drawer.setAttribute('aria-label','网站导航');
|
||
drawer.innerHTML='<button type="button" class="mobile-drawer-close" aria-label="关闭导航菜单" autofocus><svg viewBox="0 0 24 24" aria-hidden="true"><path d="m5 5 14 14M19 5 5 19"/></svg></button><a class="mobile-drawer-brand" href="/" aria-label="黄果短剧首页"></a><nav class="mobile-drawer-links" aria-label="手机端主导航"></nav><div class="mobile-drawer-info"></div><div class="mobile-drawer-social" aria-label="官方社区"></div>';
|
||
const brand=sidebar.querySelector('.sidebar-brand').cloneNode(true);brand.removeAttribute('class');drawer.querySelector('.mobile-drawer-brand').append(brand);
|
||
const nav=drawer.querySelector('.mobile-drawer-links');
|
||
const sourceNavigation=sidebar.querySelector('[data-site-navigation="sidebar"]');
|
||
if(sourceNavigation)nav.innerHTML=sourceNavigation.innerHTML;
|
||
nav.querySelectorAll('a[data-page]').forEach(link=>{link.dataset.mobileRoute=link.dataset.page;link.dataset.siteNav='sidebar';});
|
||
const pcInfo=sidebar.querySelector('.side-info');if(pcInfo)drawer.querySelector('.mobile-drawer-info').innerHTML=pcInfo.innerHTML;
|
||
const footerLogo=drawer.querySelector('.mobile-drawer-info .side-info-wordmark');if(footerLogo){const brandText=document.createElement('span');brandText.className='mobile-drawer-brand-text';brandText.textContent='黄果短剧';footerLogo.replaceWith(brandText);}
|
||
sidebar.querySelectorAll('.sidebar-social a').forEach(link=>drawer.querySelector('.mobile-drawer-social').append(link.cloneNode(true)));
|
||
document.body.append(drawer);let timer=null,start=null;
|
||
function finishClose(){clearTimeout(timer);timer=null;drawer.classList.remove('is-closing');if(drawer.open)drawer.close();document.body.classList.remove('mobile-drawer-open');trigger.setAttribute('aria-expanded','false')}
|
||
function close(animated=true){if(!drawer.open)return;if(!animated||matchMedia('(prefers-reduced-motion:reduce)').matches){finishClose();return}if(timer)return;drawer.classList.add('is-closing');timer=setTimeout(finishClose,180)}
|
||
trigger.addEventListener('click',()=>{if(!mobile.matches)return;clearTimeout(timer);timer=null;drawer.classList.remove('is-closing');drawer.showModal();document.body.classList.add('mobile-drawer-open');trigger.setAttribute('aria-expanded','true')});
|
||
drawer.querySelector('.mobile-drawer-close').addEventListener('click',()=>close());
|
||
drawer.addEventListener('cancel',e=>{e.preventDefault();close()});drawer.addEventListener('close',finishClose);
|
||
drawer.addEventListener('click',e=>{if(e.target.closest('a')){close();return}if(e.target!==drawer)return;const r=drawer.getBoundingClientRect();if(e.clientX<r.left||e.clientX>r.right||e.clientY<r.top||e.clientY>r.bottom)close()});
|
||
drawer.addEventListener('touchstart',e=>{start=e.touches.length===1?{x:e.touches[0].clientX,y:e.touches[0].clientY}:null},{passive:true});
|
||
drawer.addEventListener('touchend',e=>{if(!start)return;const t=e.changedTouches[0],dx=t.clientX-start.x,dy=t.clientY-start.y;start=null;if(dx < -60&&Math.abs(dx)>Math.abs(dy)*1.5)close()},{passive:true});
|
||
drawer.addEventListener('touchcancel',()=>{start=null},{passive:true});
|
||
window.addEventListener('site:route',()=>close());mobile.addEventListener('change',()=>{if(!mobile.matches)close(false)});
|
||
})();
|
||
|
||
;
|