講演会マンダラノート ☰ ツール一覧
講演会マンダラノート:資料&共有メモ入力はこの端末に自動保存されます
講演会未設定

🎤 講演会の資料とメモを、マンダラチャートに

A〜Hの各パートに資料(PDF・動画・リンク)がまとまっています。マスをタップして資料を見ながら、気づき・メモを書くと、参加者全員でその場でシェアされます。最後はPDF・画像にして、資料URLごとお持ち帰りできます。

' +'

講演会マンダラノート:'+esc(t)+'

' +(me.name?('
'+esc(me.name)+' さん
'):'') +'' +''); w.document.close(); }); } function exportPng(){ if(typeof html2canvas==='undefined'){ alert('準備中です'); return; } toast('🖼 画像を作成しています…'); html2canvas($('capWrap'),{backgroundColor:'#ffffff',scale:3}).then(cv=>{ const a=document.createElement('a'); a.download='講演会マンダラノート.png'; a.href=cv.toDataURL('image/png'); a.click(); toast('🖼 保存しました'); }); } async function copyLink(){ const u=location.origin+location.pathname+'?ev='+encodeURIComponent(ev); try{ await navigator.clipboard.writeText(u); toast('🔗 リンクをコピーしました'); } catch(e){ prompt('このリンクをコピーしてください', u); } } /* ---- 参加フロー ---- */ async function joinEvent(code, name){ ev=code; me.name=name; loadMe(); me.name=name; saveMe(); // 1) ファイル内プリセット(資料リンクつき)を最優先 if(PRESETS[code]){ conf=PRESETS[code]; saveConfCache(); } else{ // 2) サーバー設定(主催者フォームで作成したもの) conf=loadConfCache(); $('startMsg').textContent='講演会情報を読み込んでいます…'; try{ const r=await jsonp({action:'getProfile', classCode:classCode(), nickname:'__config'}, 20000); if(r&&r.ok&&r.data){ const d=JSON.parse(r.data); if(d&&d.kou){ conf=d.kou; saveConfCache(); } } }catch(e){} } if(!conf){ $('startMsg').textContent='このコードの講演会が見つかりません。コードをご確認ください(オフラインの場合は電波の良いところで)。'; return; } try{ localStorage.setItem('kouen_last', JSON.stringify({ev:ev,name:me.name})); }catch(e){} $('evBadge').textContent='講演会:'+ev; sendLead(); $('vStart').style.display='none'; $('vMain').style.display='block'; render(); fetchComments(true).then(render); } /* ---- 主催者フロー ---- */ function renderOrgForm(){ $('orgHeads').innerHTML=ORDER.map(k=>`
${k}
`).join(''); $('orgUrls').innerHTML=[0,1,2].map(i=>`
`).join(''); } async function orgSave(){ const code=($('orgEv').value||'').trim(); if(!/^[a-zA-Z0-9_-]{3,20}$/.test(code)){ $('orgMsg').textContent='講演会コードは半角英数字3〜20文字で入れてください。'; return; } if(PRESETS[code]){ $('orgMsg').textContent='このコードはファイル内プリセットで使われています。別のコードにしてください。'; return; } const title=($('orgTitle').value||'').trim(); if(!title){ $('orgMsg').textContent='講演タイトルを入れてください。'; return; } const hs={}; ORDER.forEach(k=>{ hs[k]=(document.querySelector('[data-oh="'+k+'"]').value||'').trim()||DEF_HEADS[k]; }); const urls=[0,1,2].map(i=>({ label:(document.querySelector('[data-ol="'+i+'"]').value||'').trim(), url:(document.querySelector('[data-ou="'+i+'"]').value||'').trim() })).filter(u=>u.url); const bad=urls.find(u=>!/^https?:\/\//.test(u.url)); if(bad){ $('orgMsg').textContent='URLは https:// から入れてください:'+bad.url; return; } $('orgMsg').textContent='保存しています…'; $('btnOrgSave').disabled=true; const cc='KOU-'+code; try{ // 既存講演会の上書き確認 try{ const r0=await jsonp({action:'getProfile', classCode:cc, nickname:'__config'}, 15000); if(r0&&r0.ok&&r0.data){ if(!confirm('このコードの講演会は既にあります。上書きしますか?')){ $('orgMsg').textContent=''; $('btnOrgSave').disabled=false; return; } } }catch(e){} await jsonp({action:'register', classCode:cc, nickname:'__config', pass:'okr'}).catch(()=>null); await postJSON({action:'saveProfile', classCode:cc, userId:'', nickname:'__config', dataJSON:JSON.stringify({kou:{title:title, sub:($('orgSub').value||'').trim(), heads:hs, urls:urls}})}); ev=code; conf={title:title, sub:($('orgSub').value||'').trim(), heads:hs, urls:urls}; saveConfCache(); const u=location.origin+location.pathname+'?ev='+encodeURIComponent(code); $('orgShare').style.display='block'; $('orgShareUrl').textContent=u; $('orgMsg').textContent=''; $('orgShareCopy').onclick=async()=>{ try{ await navigator.clipboard.writeText(u); toast('🔗 コピーしました'); }catch(e){ prompt('コピーしてください',u); } }; $('inEv').value=code; // そのまま自分も参加しやすく toast('✅ 講演会を保存しました'); }catch(e){ $('orgMsg').textContent='保存に失敗しました。もう一度お試しください。'; } $('btnOrgSave').disabled=false; } /* ---- 起動 ---- */ function renderPresetList(){ const keys=Object.keys(PRESETS); $('presetList').innerHTML = keys.length? keys.map(c=>``).join('') : '

現在開催中の講演会はありません。コードを直接入力してください。

'; $('presetList').querySelectorAll('[data-pv]').forEach(el=>el.onclick=()=>{ $('inEv').value=el.dataset.pv; $('inName').focus(); }); } renderPresetList(); renderOrgForm(); $('btnOrgToggle').onclick=()=>{ const c=$('orgCard'); c.style.display=c.style.display==='none'?'block':'none'; }; $('btnOrgSave').onclick=orgSave; $('btnJoin').onclick=()=>{ const code=($('inEv').value||'').trim(), name=($('inName').value||'').trim(); if(!code){ $('startMsg').textContent='講演会コードを入れてください。'; return; } if(!name){ $('startMsg').textContent='おなまえを入れてください。'; return; } const email=($('inMail').value||'').trim(); if(email && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)){ $('startMsg').textContent='メールアドレスの形式をご確認ください(空欄でも参加できます)。'; return; } joinEvent(code, name); }; [$('inEv'),$('inName')].forEach(el=>el.addEventListener('keydown',e=>{ if(e.isComposing||e.keyCode===229) return; // IME変換中のEnterは無視(Mac対策) if(e.key==='Enter'){ e.preventDefault(); $('btnJoin').click(); } })); let toastTimer=null; function toast(m){ const el=$('toast'); el.textContent=m; el.style.opacity='1'; clearTimeout(toastTimer); toastTimer=setTimeout(()=>el.style.opacity='0',2600); } // 定期的にメモを再取得(60秒ごと・開いている間) setInterval(()=>{ if(ev&&$('vMain').style.display!=='none'){ fetchComments(true).then(()=>{ if(!sel) renderGrid(); else render(); }); } }, 60000); // URLパラメータ ?ev=CODE → コード入力を省略 (function(){ const q=new URLSearchParams(location.search); const code=(q.get('ev')||'').trim(); const last=(function(){ try{ return JSON.parse(localStorage.getItem('kouen_last')); }catch(e){ return null; } })(); if(code){ $('inEv').value=code; if(last&&last.ev===code&&last.name){ $('inName').value=last.name; } $('inName').focus(); } else if(last&&last.ev){ $('inEv').value=last.ev; if(last.name) $('inName').value=last.name; } })();