お知らせページメーカー

第2版:障害・復旧の報告に対応。「発生→復旧作業中→復旧しました」を選ぶと、第何報・発表の日時・使えないもの・代わりの方法が入った告知とお詫び文になります。
臨時休業・メンテナンス中のお知らせを、入力するだけでHTMLファイル1枚・SNS用の画像・投稿文にします。PHPもデータベースも使わない静的なページなので、サーバーに置くだけで表示できます。

種類
色

HTMLファイルの使い方

1. 「HTMLファイルを保存」で index.html を保存します。
2. サーバーのいまの index.html を別名で残してから、保存したファイルを置きます。
3. 元に戻すときは、残しておいたファイルの名前を戻します。

このファイルは文字と色だけで、外部の読み込みはありません。検索結果に出ないように noindex を入れています。検索エンジンに「一時的な停止」と伝える 503 という応答は、HTMLだけでは出せません(サーバーの設定が必要です)。

入力した内容はこの端末の中にだけ保存され、どこにも送信しません。

`; } function postText(o){ const tags={'臨時休業':'#臨時休業 #お知らせ','メンテナンス':'#メンテナンス #お知らせ','営業時間変更':'#営業時間変更 #お知らせ','障害':'#障害情報 #お知らせ','その他':'#お知らせ'}[o.kind]; const t=o.kind==='障害'?o.title:`【${o.title}】`; return [t,...o.lines,...(o.period?o.period.split('\n').map(p=>`■ ${p}`):[]),...o.tail,'',tags].join('\n'); } function render(){ $('incident').hidden=f.kind.value!=='障害'; const o=build(); $('preview').innerHTML=pageHtml(o,false); try{localStorage.setItem('oshirase-v1',JSON.stringify(data()))}catch(e){} } function save(blob,name){ const a=document.createElement('a');a.href=URL.createObjectURL(blob);a.download=name;document.body.appendChild(a);a.click(); setTimeout(()=>{URL.revokeObjectURL(a.href);a.remove()},1000); } function wrap(ctx,text,max){ const out=[];let cur=''; for(const ch of text){ if(ctx.measureText(cur+ch).width>max&&cur){ if('、。)」』'.includes(ch)){cur+=ch;continue} out.push(cur);cur=ch; }else cur+=ch; } if(cur)out.push(cur);return out; } function drawPng(){ const o=build();const W=1080,H=1350,P=90; const cv=document.createElement('canvas');cv.width=W;cv.height=H;const x=cv.getContext('2d'); const font='"Hiragino Sans","Yu Gothic UI","Meiryo",sans-serif'; x.fillStyle=o.c[1];x.fillRect(0,0,W,H); x.fillStyle='#fff';x.fillRect(60,60,W-120,H-120); x.fillStyle=o.c[0];x.fillRect(60,60,W-120,16); let y=170;x.textBaseline='alphabetic'; if(o.name){x.fillStyle='#706b60';x.font=`36px ${font}`;x.fillText(o.name,P,y);y+=80;} x.fillStyle=o.c[0];x.font=`bold 72px ${font}`; for(const l of wrap(x,o.title,W-2*P)){x.fillText(l,P,y);y+=90} y+=20;x.fillStyle='#24231f';x.font=`38px ${font}`; const para=(t,lh)=>{for(const l of wrap(x,t,W-2*P)){x.fillText(l,P,y);y+=lh}y+=14}; o.lines.forEach(t=>para(t,58)); if(o.period){ x.font=`bold 44px ${font}`;const ls=o.period.split('\n').flatMap(p=>wrap(x,p,W-2*P-40)); const h=ls.length*62+36;x.strokeStyle=o.c[0];x.lineWidth=4;x.strokeRect(P,y-10,W-2*P,h); y+=48;ls.forEach(l=>{x.fillText(l,P+20,y);y+=62});y+=30; } x.font=`36px ${font}`;o.tail.forEach(t=>t.split('\n').forEach(s=>para(s,54))); if(y>H-80){$('msg').textContent='文字が多いため、画像の下が切れています。ひとことを短くしてください。'} cv.toBlob(b=>{save(b,'oshirase.png');if(y<=H-80)$('msg').textContent='画像を保存しました。'},'image/png'); } $('dlHtml').onclick=()=>{save(new Blob([pageHtml(build(),true)],{type:'text/html'}),'index.html');$('msg').textContent='index.html を保存しました。使い方は下の説明をご覧ください。'}; $('dlPng').onclick=drawPng; $('copy').onclick=async()=>{ const t=postText(build()); try{await navigator.clipboard.writeText(t);$('msg').textContent='投稿文をコピーしました。'} catch(e){prompt('この文をコピーしてください',t)} }; f.addEventListener('input',render);f.addEventListener('change',render); try{ const s=JSON.parse(localStorage.getItem('oshirase-v1')||'null'); if(s)for(const [k,v] of Object.entries(s)){const el=f.elements[k];if(!el)continue; if(el instanceof RadioNodeList)[...el].forEach(r=>r.checked=r.value===v); else if(el.type==='checkbox')el.checked=!!v;else el.value=v;} }catch(e){} {const d=new Date(),p=n=>String(n).padStart(2,'0'),day=`${d.getFullYear()}-${p(d.getMonth()+1)}-${p(d.getDate())}`; if(!f.sd.value)f.sd.value=day; if(!f.at.value)f.at.value=`${day}T${p(d.getHours())}:${p(d.getMinutes())}`;} render();