import {createPreferences} from '../src/preferences.js'; import {installTranslations} from '../src/localization.js'; window.dmPreferences = createPreferences(window); document.addEventListener('DOMContentLoaded', () => { // This file is served from each project's own origin; no third-party scripts. const nav=document.querySelector('.dm-nav'); const menu=nav?.querySelector('.dm-menu'); // Native details provides disclosure semantics and keyboard activation. document.addEventListener('click',event=>{if(menu?.open&&!menu.contains(event.target))menu.open=false;}); document.addEventListener('keydown',event=>{if(event.key==='Escape'&&menu?.open){menu.open=false;menu.querySelector('summary').focus();}}); nav?.addEventListener('click',event=>{if(event.target.closest('a'))menu.open=false;}); // Keep secondary app navigation in the same disclosure. Delegate to the original // controls so product renderers retain their own state and event handlers. const project=document.body.dataset.dmProject; const appActions={ habits:[['[data-view="habits"]'],['[data-view="dashboard"]']], weights:[['#settings']], heartbeats:[], }[project]||[]; const links=menu?.querySelector('.dm-nav-links'); if(links){ const extras=document.createElement('div');extras.className='dm-app-actions';links.prepend(extras); if(project==='workout'){for(const [selector,label] of [['#sound','sound'],['#help','exercise guide']]){const target=document.querySelector(selector);if(target){const button=document.createElement('button');button.type='button';const sync=()=>{button.textContent=selector==='#sound'?target.title:label;if(target.hasAttribute('aria-pressed'))button.setAttribute('aria-pressed',target.getAttribute('aria-pressed'));};sync();new MutationObserver(sync).observe(target,{attributes:true,attributeFilter:['title','aria-pressed']});button.onclick=()=>{target.click();sync();menu.open=false;};extras.append(button);}}} if(project==='heartbeats'){ for(const [label,href] of [['dashboard','/app'],['heartbeat documentation','/docs']]){ const link=document.createElement('a');link.textContent=label;link.href=href;extras.append(link); } } const proxies=appActions.map(([selector,label])=>{ const button=document.createElement('button');button.type='button';button.hidden=true;extras.append(button); button.addEventListener('click',()=>{menu.open=false;document.querySelector(selector)?.click();}); return {selector,label,button}; }); const syncActions=()=>{for(const {selector,label,button} of proxies){ const target=document.querySelector(selector);const text=window.dmPreferences.translate(label||target?.textContent?.trim()||''); if(button.hidden===!!target)button.hidden=!target; if(button.textContent!==text)button.textContent=text; }}; syncActions(); if(proxies.length)new MutationObserver(syncActions).observe(document.body,{childList:true,subtree:true}); } const accountPage=document.body.dataset.dmProject==='account'; const fullscreen=nav?.querySelector('[data-dm-fullscreen]'); fullscreen?.addEventListener('click',async()=>{try{if(document.fullscreenElement)await document.exitFullscreen();else await document.documentElement.requestFullscreen();menu.open=false;}catch{fullscreen.textContent='fullscreen unavailable';}}); document.addEventListener('fullscreenchange',()=>{if(fullscreen)fullscreen.textContent=document.fullscreenElement?'exit fullscreen':'fullscreen';}); async function applyAccountPreferences(){try{const response=await fetch(accountPage?'/api/preferences':'/sso/preferences',{cache:'no-store'});if(!response.ok)return;const {preferences}=await response.json();window.dmAccountPreferences=preferences;if(preferences.language)window.dmPreferences.setLanguage(preferences.language);if(preferences.theme)window.dmPreferences.setTheme(preferences.theme);window.dispatchEvent(new CustomEvent('dm-account-preferences',{detail:preferences}));}catch{}} applyAccountPreferences(); window.addEventListener('focus',applyAccountPreferences); if(nav&&!accountPage){ fetch('/sso/me',{cache:'no-store'}).then(r=>{if(!r.ok)throw Error('account unavailable');return r.json();}).then(({user})=>{ if(!user)return; for(const link of nav.querySelectorAll('[data-dm-guest]'))link.hidden=true; const account=nav.querySelector('[data-dm-account]');if(account){account.hidden=false;account.title=user.email;} const form=nav.querySelector('[data-dm-logout]');if(form)form.hidden=false; }).catch(()=>{}); } if('serviceWorker' in navigator){ navigator.serviceWorker.register('/sw.js',{updateViaCache:'none'}).then(registration=>{ registration.waiting?.postMessage({type:'SKIP_WAITING'}); registration.addEventListener('updatefound',()=>{const worker=registration.installing;worker?.addEventListener('statechange',()=>{if(worker.state==='installed')registration.waiting?.postMessage({type:'SKIP_WAITING'});});}); }).catch(()=>{}); } let installPrompt; const install=document.querySelector('[data-dm-install]'); const standalone=matchMedia('(display-mode: standalone)').matches||navigator.standalone; let installed=!!standalone; if(install)install.hidden=installed; const instructions=document.createElement('dialog'); instructions.setAttribute('aria-labelledby','dm-install-title'); instructions.innerHTML='
on iphone or ipad, open this app in safari, tap share, then choose “add to home screen”.
on other devices, open your browser menu and choose “install app” or “add to home screen”. if neither option is available, use a browser that supports installation.
'; document.body.append(instructions); window.addEventListener('beforeinstallprompt',event=>{installPrompt=event;if(install&&!installed)install.hidden=false;}); install?.addEventListener('click',async()=>{ if(!installPrompt){instructions.showModal();return;} const prompt=installPrompt;installPrompt=null; try{await prompt.prompt();await prompt.userChoice;}catch{instructions.showModal();} }); window.addEventListener('appinstalled',()=>{installed=true;installPrompt=null;if(install)install.hidden=true;if(instructions.open)instructions.close();}); // App renderers may create their main element after this script runs. document.querySelector('.dm-skip')?.addEventListener('click',()=>{const main=document.querySelector('main');if(main){main.id='dm-content';main.tabIndex=-1;main.focus();}}); installTranslations(window); });