感想マンダラ ☰ ツール一覧
感想マンダラ:講演会・セミナー用入力はこの端末に自動保存されます
イベント未設定

🎤 講演会の感想を、マンダラチャートに

8つの視点(小見出し)に沿って感想を書くと、参加者全員の感想がその場でシェアされます。最後は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(); 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.kan){ conf=d.kan; saveConfCache(); } } }catch(e){} if(!conf){ $('startMsg').textContent='このコードのイベントが見つかりません。コードをご確認ください(オフラインの場合は電波の良いところで)。'; return; } try{ localStorage.setItem('kansou_last', JSON.stringify({ev:ev,name:me.name})); }catch(e){} $('evBadge').textContent='イベント:'+ev; $('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; } 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='KAN-'+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({kan:{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; } /* ---- 起動 ---- */ 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; } 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('kansou_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; } })();