/* Offline design prototype. No account, camera, upload, or production API calls. */ const $=s=>document.querySelector(s), $$=s=>[...document.querySelectorAll(s)]; const paths={back:'m15 18-6-6 6-6',next:'m9 18 6-6-6-6',close:'m6 6 12 12M6 18 18 6',plus:'M12 5v14M5 12h14',more:'M5 12h.01M12 12h.01M19 12h.01',album:'M3 7h7l2-3h9v16H3ZM3 9h18',photo:'M3 4h18v16H3ZM4 16l5-5 4 4 3-3 5 5M15 8h.01',upload:'M7 18H6a4 4 0 0 1-1-8 7 7 0 0 1 13-2 5 5 0 0 1 0 10h-1M12 19V9m-4 4 4-4 4 4',settings:'m9 3-.5 3-2 1-3-.8L2 10l2.5 2L4 15l2 3 3-.5 2 3 4-1 .5-3 3-1 2-4-2-2V6l-4-.5L13 3ZM9 12a3 3 0 1 0 6 0 3 3 0 1 0-6 0',user:'M8 7a4 4 0 1 0 8 0 4 4 0 1 0-8 0M4 21v-3a8 8 0 0 1 16 0v3',users:'M4 8a3 3 0 1 0 6 0 3 3 0 1 0-6 0M14 8a3 3 0 1 0 6 0 3 3 0 1 0-6 0M1 21v-3a6 6 0 0 1 12 0v3M14 14a6 6 0 0 1 9 4v3',lock:'M5 10h14v11H5ZM8 10V7a4 4 0 0 1 8 0v3M12 14v3',eye:'M2 12s3-7 10-7 10 7 10 7-3 7-10 7S2 12 2 12ZM9 12a3 3 0 1 0 6 0 3 3 0 1 0-6 0',globe:'M2 12h20M12 2a10 10 0 1 0 0 20 10 10 0 1 0 0-20ZM12 2c-6 6-6 14 0 20 6-6 6-14 0-20',help:'M9 9a3 3 0 1 1 5 2c-2 1-2 2-2 3M12 17h.01M12 2a10 10 0 1 0 0 20 10 10 0 1 0 0-20',logout:'M9 3H3v18h6M9 12h13m-5-5 5 5-5 5',calendar:'M4 5h16v16H4ZM4 10h16M8 3v4M16 3v4M8 14h2M14 14h2',pin:'M12 22S4 13 4 9a8 8 0 0 1 16 0c0 4-8 13-8 13ZM9 9a3 3 0 1 0 6 0 3 3 0 1 0-6 0',tag:'M3 3h8l10 10-8 8L3 11ZM7 7h.01',share:'M9 8l6-3M9 15l6 4M3 12a3 3 0 1 0 6 0 3 3 0 1 0-6 0M15 4a3 3 0 1 0 6 0 3 3 0 1 0-6 0M15 20a3 3 0 1 0 6 0 3 3 0 1 0-6 0',edit:'m4 16 12-12 4 4L8 20H4ZM13 7l4 4',camera:'M3 7h4l2-3h6l2 3h4v14H3ZM8 14a4 4 0 1 0 8 0 4 4 0 1 0-8 0',flip:'M3 11a9 9 0 0 1 16-6M19 1v5h-5M21 13a9 9 0 0 1-16 6M5 23v-5h5',clock:'M12 2a10 10 0 1 0 0 20 10 10 0 1 0 0-20ZM12 6v6l4 2',check:'m5 12 4 4L19 6',trash:'M3 6h18M9 6V3h6v3M5 6l1 15h12l1-15M10 10v7M14 10v7',link:'m9 15 6-6M8 16l-2 2a4 4 0 0 1-6-6l5-5a4 4 0 0 1 6 0M16 8l2-2a4 4 0 0 1 6 6l-5 5a4 4 0 0 1-6 0',logo:'M12 2 21 7v10l-9 5-9-5V7ZM12 2l4 7-4 7-8 1M21 7h-9l-4 7 4 8M21 17l-5-8H8L3 7'}; function icon(n){return ``} let lang='en',page=1,returnPage=2,albumTitle='Canton Fair · Autumn',editTab='access',uploadFilter='all',photoFilter='all',currentPhoto=1,categoryNames=['Exhibition','People','Highlights'],members=['Emma Chen','Daniel Lee'],extraTasks=0,hiddenPhotos=new Set([6]),removedPhotos=new Set(),retried=false,publicAccess=true,uploadAllowed=true,downloads=true,original=false,draftLanguage='en',toastTimer; const T=(en,zh)=>lang==='zh'?zh:en, esc=s=>String(s).replace(/[&<>"']/g,c=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c])); const pics=[[262,530,230,105],[845,248,358,435],[264,661,224,122],[506,656,185,142],[265,824,224,126],[504,823,181,129],[260,100,477,193],[848,300,345,378]]; function photo(i,cls='',height=''){const r=pics[((i%pics.length)+pics.length)%pics.length],[x,y,w,h]=r;return `
`} function button(label,action,cls='primary',ic=''){return ``} function row(ic,label,sub,action='',trailing=''){return `<${action?'button':'div'} class="row" ${action?`data-action="${action}"`:''}>${icon(ic)}${label}${sub?`

${sub}

`:''}
${trailing?`${trailing}`:''}${action?`${icon('next')}`:''}`} function bar(title,back=5,right='',root=false){return `
${root?`

${title}

`:`${title}`}${right}
`} function nav(selected){return ``} function field(label,id,type='text',value='',placeholder=''){const pw=type==='password';return ``} function seg(items,selected,action){return `
${items.map(([id,label])=>``).join('')}
`} function status(){return `
${icon('globe')}${publicAccess?T('Public','公开'):T('Private','未公开')}${icon('upload')}${uploadAllowed?T('Uploads on','可上传'):T('Uploads off','上传关闭')}
`} function grid(count=9,offset=0){return `
${Array.from({length:count},(_,k)=>k+offset).filter(i=>!removedPhotos.has(i)&&(photoFilter!=='hidden'||hiddenPhotos.has(i))&&(photoFilter!=='published'||!hiddenPhotos.has(i))).map(i=>``).join('')}
`} function login(){return `
${[2,4,0,3,6,1].map(i=>photo(i)).join('')}
${icon('logo')}PhotoLive

${T('Your event. In focus.','让每个现场,都被看见。')}

${T('Sign in to your company workspace','登录您的企业工作空间')}

${T('Demo mode · do not enter real credentials','演示模式 · 请勿输入真实账号密码')}

`} function settings(){return bar(T('Settings','设置'),5,'',true)+`
AM

Alex Morgan

alex.morgan · ${T('Staff','工作人员')}

${row('globe',T('Language','语言'),'', 'language',lang==='en'?'English':'中文')}${row('lock',T('Change password','修改密码'),'', 'go:3')}${row('help',T('Upload help','上传帮助'),'', 'help')}${row('help',T('App version','应用版本'),'', '', 'Design preview')}
${row('logout',T('Sign out','退出登录'),'', 'logout')}

${T('Sample staff profile · offline prototype','示例工作人员 · 离线交互原型')}

`+nav(2)} function password(){return bar(T('Change password','修改密码'),2)+`
${icon('lock')}
${T('Keep your account secure','保障您的账号安全')}

${T('Update the password for your company account.','修改用于企业账号登录的密码。')}

${field(T('Current password','当前密码'),'oldPassword','password','',T('Enter current password','输入当前密码'))}${field(T('New password','新密码'),'newPassword','password','',T('Enter new password','输入新密码'))}${field(T('Confirm password','确认密码'),'confirmPassword','password','',T('Enter new password again','再次输入新密码'))}
${icon('help')}

${T('Preview only. No password is saved or sent. Production rules are enforced by the account service.','仅演示校验,不保存或发送密码。正式密码规则由账号服务执行。')}

`} function albums(){return bar('PhotoLive',5,``,true)+`

${T('Albums','相册')}

${T('Organize and manage event photos','组织活动相册,管理现场影像')}

${[[0,albumTitle,'Oct 15–19 · Guangzhou',6],[5,T('Product Launch','新品发布会'),'Sep 3 · Shenzhen',6],[2,T('Team Stories','团队故事'),'Aug 12–15 · Shanghai',8]].map(([i,title,date,link])=>``).join('')}
`+nav(5)} function detail(empty=false){const title=empty?T('Team Stories','团队故事'):albumTitle;return bar('',5,``)+`
${photo(empty?2:6,'album-hero')}

${esc(title)}

${status()}
${empty?`
${icon('photo')}

${T('Ready for your first photo','准备好记录第一张照片')}

${T('Capture a moment or choose photos to begin.','拍摄精彩瞬间,或从手机相册选择照片。')}

`:`
${[[17,'photo',T('Photos','照片')],[11,'users',T('Members','成员')],[12,'tag',T('Categories','分类')],[19,'share',T('Share','分享')]].map(([id,ic,label])=>``).join('')}

${T('Recent photos','最近照片')}

${grid(9)}
`}
${button(T('Take photos','拍摄照片'),uploadAllowed?'go:13':'uploadOff','primary','camera')}${button(T('Choose photos','选择照片'),uploadAllowed?'choose':'uploadOff','secondary','photo')}
`} function albumForm(edit=false){const details=`
${field(T('Album title','相册名称'),'albumTitle','text',edit?albumTitle:T('Canton Fair · Autumn','广交会 · 秋季'))}
${field(T('Starts','开始日期'),'startDate','date','2026-10-15')}${field(T('Ends','结束日期'),'endDate','date','2026-10-19')}${field(T('Venue','地点'),'venue','text','Guangzhou')}${field(T('Address','详细地址'),'address','text',T('China Import and Export Fair Complex','中国进出口商品交易会展馆'))}

${T('Description & languages','说明与语言')}

`;const access=`
${[['uploads',T('Allow uploads','允许上传'),T('Team members can upload photos to this album.','团队成员可以向本相册上传照片。'),uploadAllowed],['public',T('Public access','公开访问'),T('Anyone with the link can view this album.','获得链接的观众可以浏览相册。'),publicAccess],['downloads',T('Allow downloads','允许下载'),T('Viewers can download photos from this album.','观众可以下载相册照片。'),downloads],['original',T('Original downloads','原图下载'),T('Allow downloading full-resolution files.','允许观众下载完整分辨率的原图。'),original]].map(([id,title,desc,on])=>``).join('')}
${row('globe',T('Content language','内容语言'),T('Default language for album content','相册内容的默认语言'),'contentLanguage','English')}
`;return bar(edit?T('Edit album','编辑相册'):T('New album','新建相册'),6)+`
${edit?seg([['details',T('Details','详情')],['access',T('Access','权限')]],editTab,'editTab'):''}
${details}
${edit?`
${access}
`:''}
`} function memberPage(){return bar(T('Members','成员'),6,``)+`

${T('Owner','创建者')}

AM
Alex Morgan

alex.morgan

${T('Owner','创建者')}

${T('Collaborators','协作者')}

${members.map((name,i)=>`
${name.split(' ').map(n=>n[0]).join('')}
${name}

${name.toLowerCase().replace(' ','.')}

${T('Can manage their own photos','可管理自己上传的照片')}

`).join('')}
${icon('help')}${T('Album owners manage all photos and members.','相册创建者管理全部照片和成员。')}
${button(T('Invite colleague','邀请同事'),'invite','secondary','users')}
`} function categories(){return bar(T('Categories','分类'),6,``)+`
${categoryNames.map((name,i)=>`
${icon('tag')}
${esc(name)}
`).join('')}

${T('Categories help your team organize event photos.','通过分类整理团队的活动照片。')}

`} function camera(){return bar(`${esc(albumTitle)}
`,6,``)+`
${photo(1)}
${T('Camera preview · sample photo','相机效果演示 · 示例照片')}

${T('No device camera or OSS connection','未调用设备相机,未连接 OSS')}

`} function uploadItem(i,type='upload'){let name='IMG_'+String(85+i).padStart(3,'0')+'.jpg';const attention=type==='attention',history=type==='history';let state=attention?(i===0?T('Connection interrupted','连接中断'):T('Source file unavailable','源文件不可用')):history?[T('Uploaded','已上传'),T('Processing','处理中'),T('Ready','已就绪')][i%3]:[T('Uploading 68%','上传中 68%'),T('Waiting for network','等待网络'),T('Confirming upload','确认上传中'),T('Uploaded','已上传')][i%4];return `
${photo(i+2,'upload-thumb')}
${name}

${history?'Today 14:'+String(32-i*3):esc(albumTitle)}

${!history?`

${T('Exhibition','展会现场')}

`:''}
${!attention&&i===0&&!history?'
':icon(attention?'help':i%3===0?'check':'clock')}${state}
${attention?`

${i===0?T('The network was lost during upload. You can retry this file.','上传时网络断开,可以重试此任务。'):T('The original file cannot be found on this device. Please select it again.','本机找不到源文件,请重新选择。')}

${button(i===0?T('Retry','重试'):T('Select again','重新选择'),i===0?'retry':'reselect','secondary')}`:history?`

${i%2?T('Other device','其他设备'):T('This device','本机')}

`:''}
`} function uploads(attention=false){const active=attention?'attention':uploadFilter;return bar(T('Uploads','上传'),5,``,true)+`

${T('Today · This device','今天 · 本机')}

82 ${T('uploaded','已上传')}

${3+extraTasks} ${T('uploading','上传中')} · 13 ${T('waiting','等待中')} · ${retried?1:2} ${T('need attention','需处理')}

${seg([['all',T('All','全部')],['active',T('Active','进行中')],['attention',T('Needs attention','需处理')]],active,'uploadFilter')}${active==='attention'?`${retried?'':uploadItem(0,'attention')}${uploadItem(1,'attention')}${button(T('Retry eligible tasks','重试可恢复任务'),'retry','primary full')}`:Array.from({length:active==='active'?3:4},(_,i)=>uploadItem(i)).join('')}${extraTasks?`
${icon('camera')}${T('Simulated photos queued: ','模拟拍摄入队:')}${extraTasks}
`:''}
`+nav(14)} function historyPage(){return bar(T('Upload history','上传历史'),14)+`

${T('Server records · All devices','服务端记录 · 所有设备')}

${Array.from({length:5},(_,i)=>uploadItem(i,'history')).join('')}

${T('Sample server records · not combined with the local queue','示例服务端记录 · 不与本机队列合并计数')}

`} function managePhotos(){return bar(T('Photos','照片'),6)+`

${esc(albumTitle)}

${seg([['all',T('All','全部')],['published',T('Published','已公开')],['hidden',T('Hidden','已隐藏')]],photoFilter,'photoFilter')}${grid(18)}
`} function photoDetails(){const hidden=hiddenPhotos.has(currentPhoto);return bar('',17,``)+`
${photo(currentPhoto,'large-photo')}
Emma Chen${hidden?T('Hidden','已隐藏'):T('Published','已公开')}

Oct 15, 2026 · 10:42

2400 × 1600 · 2.8 MB

${button(hidden?T('Publish again','重新公开'):T('Hide photo','隐藏照片'),hidden?'publish':'go:20','secondary','eye')}${button(T('Delete','删除'),'delete','danger','trash')}
`} function sharePage(){return bar(T('Share album','分享相册'),6)+`
${photo(6,'cover')}
Demo QR code

${T('Illustrative QR · opens example.com','演示二维码 · 指向 example.com')}

${button(T('Copy preview link','复制预览链接'),'copy','primary full','link')}
`} function languageSheet(){return `

${T('App language','应用语言')}

${[['auto','Follow device','跟随系统'],['en','English','English'],['zh','中文','中文']].map(([value,en,zh])=>``).join('')}${button(T('Done','完成'),'saveLanguage')}`} function categorySheet(index='new'){const edit=index!=='new';return `

${edit?T('Edit category','编辑分类'):T('New category','新建分类')}

${seg([['en','English'],['zh','中文']],'en','categoryLang')}
${field(T('Category name','分类名称'),'categoryName','text',edit?categoryNames[Number(index)]:T('Behind the scenes','幕后花絮'))}
`} function hideSheet(){return `

${T('Hide this photo?','隐藏这张照片?')}

${photo(currentPhoto)}

${T('Viewers will no longer see this photo. Authorized staff can publish it again.','观众将无法继续看到这张照片,有权限的工作人员可以重新公开。')}

${button(T('Hide photo','隐藏照片'),'confirmHide')}${button(T('Cancel','取消'),'closeSheet','ghost cancel')}`} function overlay(content,close='closeSheet'){const layer=document.createElement('div');layer.className='modal-layer';layer.innerHTML=``;layer.onclick=e=>{if(e.target===layer)action(close)};$('#app').append(layer);[...$('#app').children].filter(el=>el!==layer).forEach(el=>el.inert=true);layer.querySelector('input,button')?.focus({preventScroll:true});bindForms();return layer} function closeSheet(){const layer=$('.modal-layer');if(layer)layer.remove();[...$('#app').children].forEach(el=>el.inert=false);if([4,7,20].includes(page))go(page===4?returnPage:page===7?6:18);else $('.bar button')?.focus({preventScroll:true})} function render(){page=Number(location.hash.slice(1))||Number(document.body.dataset.page)||1;page=Math.max(1,Math.min(page,20));$('#app').className='app'+([13,18,20].includes(page)?' dark':'');document.documentElement.lang=lang==='zh'?'zh-CN':'en';let html='';switch(page){case 1:html=login();break;case 2:case 4:html=settings();break;case 3:html=password();break;case 5:html=albums();break;case 6:case 7:html=detail();break;case 8:html=detail(true);break;case 9:html=albumForm();break;case 10:html=albumForm(true);break;case 11:html=memberPage();break;case 12:html=categories();break;case 13:html=camera();break;case 14:html=uploads();break;case 15:html=uploads(true);break;case 16:html=historyPage();break;case 17:html=managePhotos();break;case 18:case 20:html=photoDetails();break;case 19:html=sharePage();break}$('#app').innerHTML=html;bindForms();if(page===4){draftLanguage=lang;overlay(languageSheet())}if(page===7)overlay(`

${T('Album actions','相册操作')}

${row('edit',T('Edit album','编辑相册'),'','go:10')}${row('users',T('Manage members','管理成员'),'','go:11')}${row('tag',T('Categories','分类'),'','go:12')}${row('share',T('Share album','分享相册'),'','go:19')}${button(T('Cancel','取消'),'closeSheet','ghost')}`);if(page===12)overlay(categorySheet());if(page===20)overlay(hideSheet());fitPhoto();document.title=`PhotoLive · ${String(page).padStart(2,'0')} · App preview`;if(window.parent!==window)window.parent.postMessage({type:'photolive-preview-page',page},'*')} function go(id){if(Number(id)===page){render();return}location.hash=String(id).padStart(2,'0')} function toast(text){$('.toast')?.remove();const el=document.createElement('div');el.className='toast';el.setAttribute('role','status');el.textContent=text;$('#app').append(el);clearTimeout(toastTimer);toastTimer=setTimeout(()=>el.remove(),2800)} function fitPhoto(){const stage=$('#photoStage');if(!stage)return;const r=pics[currentPhoto%pics.length],w=Math.min(stage.clientWidth,stage.clientHeight*r[2]/r[3]);$('.large-photo').style.width=w+'px';$('.large-photo').style.height=w*r[3]/r[2]+'px'} function invite(){overlay(`

${T('Invite colleague','邀请同事')}

${field(T('Search company accounts','搜索企业账号'),'searchMember','search','','Search')}
${['Olivia Wang','James Wilson','Sophia Li'].map(name=>``).join('')}

${T('Demo internal directory · no invitation is sent','示例企业通讯录 · 不发送真实邀请')}

`);$('#searchMember').oninput=e=>$$('.candidate').forEach(el=>el.hidden=!el.textContent.toLowerCase().includes(e.target.value.toLowerCase()))} function action(raw){const [name,...rest]=raw.split(':'),value=rest.join(':');switch(name){case 'go':go(value);break;case 'language':returnPage=page;draftLanguage=lang;go(4);break;case 'saveLanguage':{const selected=$('input[name=language]:checked').value;lang=selected==='auto'?(navigator.language.startsWith('zh')?'zh':'en'):selected;go(returnPage);break}case 'closeSheet':closeSheet();break;case 'logout':overlay(`

${T('Sign out of the preview?','退出预览账号?')}

${T('Production uploads remain tied to your account. This prototype contains sample tasks only.','正式版任务按账号保留,此原型中仅有示例任务。')}

${button(T('Sign out','退出'),'go:1','danger')}${button(T('Cancel','取消'),'closeSheet','ghost')}`);break;case 'help':overlay(`

${T('Upload help','上传帮助')}

${T('Keep the app connected to the network. Interrupted uploads can be retried from Uploads.','请保持网络连接。中断的上传可从上传中心重试。')}

${T('Background and lock-screen uploads depend on the operating system, permissions and power settings. Reopen the app after a force quit to resume pending work.','后台和锁屏上传受到操作系统、权限和省电设置影响。强制退出后请重新打开应用恢复任务。')}

${T('Uploaded does not mean publicly available; server processing and album access still apply.','已上传不代表已公开,仍需完成服务端处理并遵守相册权限。')}

${button(T('Got it','知道了'),'closeSheet')}`);break;case 'uploadOff':toast(T('Uploads are disabled for this album.','该相册已关闭上传。'));break;case 'choose':case 'reselect':overlay(`

${T('Choose sample photos','选择示例照片')}

${T('This demonstrates the photo selection flow without accessing your device library.','这里演示选图流程,不访问您的设备相册。')}

${[0,1,2].map(i=>``).join('')}
${button(T('Cancel','取消'),'closeSheet','ghost')}`);break;case 'selectSample':extraTasks++;closeSheet();toast(T('Sample photo added to the demo upload queue.','示例照片已加入演示上传队列。'));break;case 'editTab':{editTab=value;$('#detailsFields').hidden=value!=='details';$('#accessFields').hidden=value!=='access';$$('[data-action^="editTab:"]').forEach(el=>el.classList.toggle('active',el.dataset.action.endsWith(value)));break}case 'contentLanguage':overlay(`

${T('Content language','内容语言')}

${button('English','setContent:English','ghost')}${button('中文','setContent:中文','ghost')}`);break;case 'setContent':$('#contentLanguage').value=value;closeSheet();toast(T('Content language draft updated.','内容语言草稿已更新。'));break;case 'invite':invite();break;case 'inviteName':if(!members.includes(value))members.push(value);closeSheet();render();toast(T('Added to the sample team.','已加入示例协作团队。'));break;case 'removeMember':overlay(`

${T('Remove collaborator?','移除协作者?')}

${esc(members[Number(value)])}

${button(T('Remove from sample team','从示例团队移除'),'confirmRemove:'+value,'danger')}${button(T('Cancel','取消'),'closeSheet','ghost')}`);break;case 'confirmRemove':members.splice(Number(value),1);render();break;case 'category':overlay(categorySheet(value));break;case 'categoryLang':{const field=$('#categoryName');field.dataset[$('.tabs button.active')?.dataset.action.endsWith('zh')?'zh':'en']=field.value;field.value=field.dataset[value]||'';$$('[data-action^="categoryLang:"]').forEach(el=>el.classList.toggle('active',el.dataset.action.endsWith(value)));break}case 'cameraCategory':overlay(`

${T('Select category','选择分类')}

${categoryNames.map(n=>button(esc(n),'chooseCategory:'+n,'ghost')).join('')}`);break;case 'chooseCategory':closeSheet();toast(T('Selected: ','已选择:')+value);break;case 'shutter':extraTasks++;$('#cameraNote').textContent=T('Added to demo upload queue · ','已加入演示上传队列 · ')+extraTasks;break;case 'flip':$('.camera-view>.art').style.transform=$('.camera-view>.art').style.transform?'':'scaleX(-1)';toast(T('Simulated camera direction switch','已模拟切换相机方向'));break;case 'uploadFilter':uploadFilter=value;if(value==='attention')go(15);else if(page===15)go(14);else render();break;case 'retry':retried=true;render();toast(T('Eligible sample task queued again.','可恢复的示例任务已重新排队。'));break;case 'photoFilter':photoFilter=value;render();break;case 'view':currentPhoto=Number(value);go(18);break;case 'photoPrev':case 'photoNext':{let n=currentPhoto;do{n=(n+(name==='photoNext'?1:17))%18}while(removedPhotos.has(n)&&removedPhotos.size<18);currentPhoto=n;render();break}case 'photoMore':overlay(`

${T('Photo actions','照片操作')}

${button(T('View metadata','查看照片信息'),'metadata','ghost')}${button(T('Delete photo','删除照片'),'delete','danger')}${button(T('Cancel','取消'),'closeSheet','ghost')}`);break;case 'metadata':closeSheet();toast(T('Sample · Emma Chen · 2400 × 1600 · 2.8 MB','示例 · Emma Chen · 2400 × 1600 · 2.8 MB'));break;case 'confirmHide':hiddenPhotos.add(currentPhoto);go(18);break;case 'publish':hiddenPhotos.delete(currentPhoto);render();toast(T('Sample photo is visible again.','示例照片已重新公开。'));break;case 'delete':$('.modal-layer')?.remove();overlay(`

${T('Delete this photo?','删除这张照片?')}

${T('This removes it from this preview only. Production deletion follows server retention rules; it does not promise immediate physical deletion from OSS.','此处仅从预览移除。正式删除遵循服务端保留规则,不代表立即从 OSS 物理删除。')}

${button(T('Delete from preview','从预览删除'),'confirmDelete','danger')}${button(T('Cancel','取消'),'closeSheet','ghost')}`);break;case 'confirmDelete':removedPhotos.add(currentPhoto);go(17);break;case 'copy':{const url=new URL('../viewer-preview-2026-09-22.html',location.href).href;if(navigator.clipboard)navigator.clipboard.writeText(url).then(()=>toast(T('Local preview link copied','本地预览链接已复制'))).catch(()=>prompt(T('Copy preview link','复制预览链接'),url));else prompt(T('Copy preview link','复制预览链接'),url);break}case 'openViewer':window.open('../viewer-preview-2026-09-22.html','_blank','noopener,noreferrer');break}} function bindForms(){const login=$('#loginForm');if(login)login.onsubmit=e=>{e.preventDefault();if(!$('#account').value.trim()){$('#formError').textContent=T('Enter a demo account.','请输入演示账号。');return}go(5)};const password=$('#passwordForm');if(password)password.onsubmit=e=>{e.preventDefault();const a=$('#newPassword').value,b=$('#confirmPassword').value;if(!$('#oldPassword').value||!a||a!==b){$('#formError').textContent=T('Complete all fields and make sure the new passwords match.','请填写全部字段,并确保两次新密码一致。');return}go(2);setTimeout(()=>toast(T('Validation passed · no password was saved','演示校验通过 · 未保存密码')),60)};const form=$('#albumForm');if(form)form.onsubmit=e=>{e.preventDefault();if(!$('#albumTitle').value.trim()||!$('#startDate').value||!$('#endDate').value||$('#endDate').value<$('#startDate').value){$('#formError').textContent=T('Enter a title and valid start/end dates.','请填写相册名称及有效起止日期。');return}albumTitle=$('#albumTitle').value.trim();if(page===10){publicAccess=$('#access-public').checked;uploadAllowed=$('#access-uploads').checked;downloads=$('#access-downloads').checked;original=downloads&&$('#access-original').checked}go(6)};const cat=$('#categoryForm');if(cat)cat.onsubmit=e=>{e.preventDefault();const val=$('#categoryName').value.trim();if(!val){$('#categoryError').textContent=T('A name is required.','请填写分类名称。');return}if(cat.dataset.index==='new')categoryNames.push(val);else categoryNames[Number(cat.dataset.index)]=val;closeSheet();$('#app').innerHTML=categories();toast(T('Sample category saved.','示例分类已保存。'))};const download=$('#access-downloads');if(download)download.onchange=()=>{const original=$('#access-original');original.disabled=!download.checked;if(!download.checked)original.checked=false};['historyAlbum','historyDate'].forEach(id=>{const el=$('#'+id);if(el)el.onchange=()=>{$('#historyRows').innerHTML=Array.from({length:el.selectedIndex?2:5},(_,i)=>uploadItem(i+el.selectedIndex,'history')).join('');toast(T('Sample history filter updated','示例历史筛选已更新'))}})} document.addEventListener('click',e=>{const eye=e.target.closest('[data-eye]');if(eye){const input=$('#'+eye.dataset.eye);input.type=input.type==='password'?'text':'password';return}const target=e.target.closest('[data-go],[data-action]');if(target){if(target.dataset.go)go(target.dataset.go);else action(target.dataset.action)}});document.addEventListener('dblclick',e=>{if(e.target.closest('.large-photo'))$('.large-photo').classList.toggle('zoom')});document.addEventListener('keydown',e=>{const layer=$('.modal-layer');if(layer){if(e.key==='Escape')closeSheet();if(e.key==='Tab'){const els=[...layer.querySelectorAll('button,input,select,textarea')].filter(x=>!x.disabled);if(els.length){if(e.shiftKey&&document.activeElement===els[0]){e.preventDefault();els.at(-1).focus()}else if(!e.shiftKey&&document.activeElement===els.at(-1)){e.preventDefault();els[0].focus()}}}}else if(page===18&&!e.target.matches('input,textarea')){if(e.key==='ArrowLeft')action('photoPrev');if(e.key==='ArrowRight')action('photoNext');if(e.key==='Escape')go(17)}});let touchStart=null;document.addEventListener('pointerdown',e=>{if(e.target.closest('.photo-stage')&&!e.target.closest('button'))touchStart=[e.clientX,e.clientY]});document.addEventListener('pointerup',e=>{if(touchStart&&page===18){const dx=e.clientX-touchStart[0],dy=e.clientY-touchStart[1];if(Math.abs(dx)>60&&Math.abs(dx)>Math.abs(dy))action(dx<0?'photoNext':'photoPrev')}touchStart=null});window.addEventListener('resize',fitPhoto);window.addEventListener('hashchange',render);render();