Emberveil sourcemobile/public/mobile.js
import {bindChat} from './chat.js?v=20261018-chat1';
import {createSocketBridge} from './network.js?v=20261018-touch2';
import {bindTouch,viewportSize} from './touch.js?v=20261018-touch2';
import {validateLogin,loginMessage} from './login.js?v=20261018-login1';
const $=id=>document.getElementById(id);
const bridge=createSocketBridge(location.protocol==='https:'?`wss://${location.host}/mobile/socket`:`ws://${location.hostname}:8801`);
let client=null,ready=false,nextOptions=false,attempted=false,loggedIn=false,submitting=false,formError='';
let stableGameHeight=globalThis.visualViewport?.height||innerHeight;
let chat=null,stableCompact=false;
let installPrompt=null;
addEventListener('beforeinstallprompt',event=>{event.preventDefault();installPrompt=event;});
addEventListener('appinstalled',()=>{$('home-shortcut').hidden=true;$('home-help').hidden=true;installPrompt=null;});
if(matchMedia('(display-mode:standalone)').matches||navigator.standalone)$('home-shortcut').hidden=true;
$('home-shortcut').onclick=async()=>{
if(installPrompt){const prompt=installPrompt;installPrompt=null;await prompt.prompt();return;}
const show=$('home-help').hidden;$('home-help').hidden=!show;$('home-shortcut').setAttribute('aria-expanded',String(show));
$('home-help').textContent=/iPad|iPhone|iPod/.test(navigator.userAgent)?'In Safari, tap Share, then Add to Home Screen. The shortcut opens Emberveil; an internet connection is required.':'Open your browser menu and choose Add to Home screen or Install app. The shortcut opens Emberveil; an internet connection is required.';
};
function fit(){
const typingOpen=chat?.isOpen()&&loggedIn;
if(!typingOpen)stableCompact=loggedIn&&matchMedia('(orientation:landscape) and (max-height:600px)').matches;
const compact=stableCompact;
document.body.classList.toggle('compact-game',compact);
if(!typingOpen)stableGameHeight=globalThis.visualViewport?.height||innerHeight;
const size=viewportSize(document.documentElement.clientWidth,typingOpen?stableGameHeight:(globalThis.visualViewport?.height||innerHeight),compact,0);
$('viewport').style.width=`${size.width}px`;$('viewport').style.height=`${size.height}px`;chat?.position();
}
addEventListener('resize',fit);globalThis.visualViewport?.addEventListener('resize',fit);addEventListener('fullscreenchange',fit);fit();
function failed(error){$('status').textContent='Could not load Emberveil. Check your connection, then tap Reload game.';console.error('Mobile startup:',error);$('start').textContent='Reload game';$('start').hidden=false;$('start').disabled=false;}
async function poll(){
try{
const previous=loggedIn;ready=await client.ready();loggedIn=await client.loggedIn();const busy=await client.busy(),description=await client.status();
if(loggedIn||busy)formError='';
$('status').textContent=formError||(!loggedIn&&description.trim()==='Connecting to server...'?'Ready to sign in':loginMessage(description));
$('login').hidden=!ready||loggedIn||busy;$('touch').hidden=!ready;$('controls').hidden=!ready;
document.body.classList.toggle('playing',loggedIn);
if(previous!==loggedIn){if(!loggedIn)chat.hide();fit();}
}catch(error){console.error('Mobile status:',error);$('status').textContent='Checking your game connection...';}
setTimeout(poll,750);
}
$('start').onclick=async()=>{
if(attempted){location.reload();return;}attempted=true;$('start').disabled=true;$('status').textContent='Loading the browser game. The first visit takes longer...';
try{
if(typeof cheerpjInit!=='function')throw new Error('Runtime script has not loaded');
await cheerpjInit({version:8,status:'splash',natives:bridge.natives});
cheerpjCreateDisplay(-1,-1,$('display'));
const library=await cheerpjRunLibrary('/app'+new URL('./client-mobile-20261018-blackgold.jar',location.href).pathname);client=await library.MobileMain;
$('status').textContent='Preparing the game files...';await client.start('/app'+new URL('./cache',location.href).pathname);
$('start').hidden=true;$('controls').append($('fullscreen'));poll();
}catch(error){failed(error);}
};
$('login').onsubmit=async event=>{
event.preventDefault();if(submitting)return;
if(!ready){formError='The game is still loading. Wait for Ready to sign in, then try again.';$('status').textContent=formError;return;}
const password=$('password').value,result=validateLogin($('username').value,password);
if(result.error){formError=result.error;$('status').textContent=formError;return;}
submitting=true;formError='';$('status').textContent='Connecting to Emberveil...';$('username').value=result.name;$('password').value='';
$('username').blur();$('password').blur();$('login-button').disabled=true;
try{await client.signIn(result.name,password);}
catch(error){formError='Could not start login. Check your connection and try again. On Galaxy, open this link in Chrome or Samsung Internet.';$('status').textContent=formError;console.error('Mobile sign-in failed:',error?.name||'Error');}
finally{submitting=false;$('login-button').disabled=false;}
};
$('username').addEventListener('input',()=>{formError='';});$('password').addEventListener('input',()=>{formError='';});
$('fullscreen').hidden=!document.body.requestFullscreen;
$('fullscreen').onclick=async()=>{try{if(!document.fullscreenElement)await document.body.requestFullscreen();else await document.exitFullscreen();fit();}catch{$('status').textContent='Fullscreen is unavailable here. Landscape mode still works.';}};
$('left').onclick=()=>client?.turn(-64);$('right').onclick=()=>client?.turn(64);$('closer').onclick=()=>client?.zoom(-1);$('farther').onclick=()=>client?.zoom(1);
chat=bindChat({form:$('typing'),input:$('text'),send:$('chat-send'),close:$('chat-close'),toggle:$('keyboard'),viewport:$('viewport'),connected:()=>ready&&loggedIn,write:text=>client.type(text),layout:fit,status:text=>{$('chat-status').textContent=text;}});
$('options').onclick=()=>{nextOptions=!nextOptions;$('options').setAttribute('aria-pressed',String(nextOptions));};
const cancelTouch=bindTouch($('touch'),{ready:()=>ready,options:()=>nextOptions,clearOptions:()=>{nextOptions=false;$('options').setAttribute('aria-pressed','false');},point:(x,y,button)=>{if(button===1&&loggedIn&&x>=17&&x<510&&y>=435&&y<=460)chat.open();else client.point(x,y,button);},zoom:delta=>client.zoom(delta),gesture:(...args)=>client.gesture(...args),wheel:(...args)=>client.wheel(...args)});
addEventListener('pagehide',()=>{cancelTouch();bridge.closeAll();});
addEventListener('pageshow',()=>bridge.resume());
globalThis.visualViewport?.addEventListener('scroll',()=>chat.position());
addEventListener('orientationchange',()=>{if(chat.isOpen())chat.hide();});
document.addEventListener('visibilitychange',()=>{if(document.hidden)cancelTouch();});