お知らせ

直前に追加するため、パース中に実行すると // まだ存在せず【一覧が静的のまま・APIを一度も叩かない】状態になる // (実測 2026-08-18 kk-narumi: 連動したはずが /news/ へのリクエストが0件だった)。 function boot(){ var API="https://7s04ys3bm1.execute-api.ap-northeast-1.amazonaws.com", SITE="ccd979eb4e204bc8bbafca744b6572dc"; function esc(s){return String(s==null?"":s).replace(/&/g,"&").replace(//g,">").replace(/"/g,""");} // 元の一覧の日付表記(区切り・ゼロ埋め・年月日)に合わせて整形する function fmt(iso,sample){ var m=String(iso||"").match(/(\d{4})-(\d{2})-(\d{2})/); if(!m) return ""; var y=m[1],mo=m[2],da=m[3]; var hasY=/\d{4}/.test(sample); // 年を書かない一覧(「7月29日」「07/29」)にも合わせる if(/月/.test(sample)){ var s=sample.match(/(\d{1,2})月\s*(\d{1,2})?日?/)||[]; var out=(hasY?y+"年":"")+((s[1]&&s[1].length===2)?mo:String(+mo))+"月"; if(s[2]||/日/.test(sample)) out+=((s[2]&&s[2].length===2)?da:String(+da))+"日"; return out; } var sep=(sample.match(/\d+([^\d])/)||[,"/"])[1], p=sample.split(sep); if(hasY){ var out2=y+sep+((p[1]&&p[1].length===2)?mo:String(+mo)); if(p.length>2) out2+=sep+((p[2]&&p[2].length===2)?da:String(+da)); return out2; } var out3=((p[0]&&p[0].length===2)?mo:String(+mo)); if(p.length>1) out3+=sep+((p[1]&&p[1].length===2)?da:String(+da)); return out3; } var WRAP={tr:"tbody",li:"ul",dt:"dl",dd:"dl",article:"div"}; // 公開APIのパスは種別名と一致しない: お知らせ=/news/ だがブログは /columns/(複数形)。 // /column/ を叩くと404になり、一覧が静的のまま(=管理画面の更新が載らない)になる。 var EP={news:"news",column:"columns"}; [].forEach.call(document.querySelectorAll('script[data-sm-newstpl]'),function(sc){ var cfg; try{cfg=JSON.parse(sc.textContent);}catch(e){return;} var first=document.querySelector('[data-sm-row="'+cfg.type+'"]'); if(!first) return; var box=first.parentElement; if(!box) return; fetch(API+"/"+(EP[cfg.type]||cfg.type)+"/"+SITE,{cache:"no-cache"}).then(function(r){return r.ok?r.json():null}).then(function(d){ var items=Array.isArray(d)?d:((d&&d.items)||[]); if(!items.length) return; // 取れなければ元の一覧のまま var html=""; items.forEach(function(it){ var title=String(it.title||"").trim(); if(!title) return; var raw=it.published_at||it.date||""; var iso=(String(raw).match(/\d{4}-\d{2}-\d{2}/)||[""])[0]; // 元サイトからコピー済みの詳細ページがあれば復元(同名記事があるので日付+タイトルを優先) // 年なし一覧では日付キーが "-MM-DD|title" になるのでそちらも見る var dl=cfg.dlinks||{}; var link=dl[iso+"|"+title]||dl[iso.slice(4)+"|"+title]||cfg.links[title]||""; html+=cfg.tpl.split("{{date}}").join(esc(fmt(raw,cfg.dateSample))) .split("{{title}}").join(esc(title)) .split("{{image}}").join(esc(it.image_url||cfg.imageSample||"")) .split("{{link}}").join(link?esc(link):"#"); }); if(!html) return; var w=document.createElement(WRAP[cfg.rowTag]||"div"); w.innerHTML=html; var frag=document.createDocumentFragment(); while(w.firstChild) frag.appendChild(w.firstChild); var tags={}; tags[cfg.rowTag]=1; if(cfg.pairTag) tags[cfg.pairTag]=1; // div 行は同じコンテナに「もっと見る」等の別の div が混ざるので、行のクラスで絞る var old=[].filter.call(box.children,function(el){ if(!tags[el.tagName.toLowerCase()]) return false; if(cfg.rowClass) return String(el.className||"").split(/\s+/)[0]===cfg.rowClass; return true; }); box.insertBefore(frag,old[0]||null); old.forEach(function(el){el.parentNode.removeChild(el);}); // コピー内に詳細ページが無い記事(引き渡し後に追加された分)は遷移させない [].forEach.call(box.querySelectorAll('a[href="#"]'),function(a){ a.addEventListener("click",function(e){e.preventDefault();}); }); }).catch(function(){}); }); // ── 元のお問い合わせフォームを widget の受け口へ繋ぐ ── // デザイン・項目はコピーしたまま、送信だけ POST /contact/{siteId} に差し替える。 // 元サイトの action(WordPress/CF7)へ飛ばさないよう capture フェーズで奪う。 // 対応の付かない項目も「ラベル: 値」で message に必ず載せるので情報は落ちない。 var ctpl=document.querySelector('script[data-sm-contacttpl]'); var cform=document.querySelector('form[data-sm-contactform]'); if(ctpl&&cform){ var ccfg; try{ccfg=JSON.parse(ctpl.textContent);}catch(e){ccfg=null;} if(ccfg&&ccfg.fields){ var busy=false; cform.addEventListener("submit",function(ev){ ev.preventDefault(); ev.stopImmediatePropagation(); if(busy) return; busy=true; var slots={name:[],email:[],phone:[],company:[],message:[]}, detail=[]; ccfg.fields.forEach(function(f){ var els=cform.querySelectorAll('[name="'+String(f.name).replace(/"/g,'\\"')+'"]'); var vals=[]; [].forEach.call(els,function(el){ if((el.type==="checkbox"||el.type==="radio")&&!el.checked) return; var v=(el.value==null?"":String(el.value)).trim(); if(v) vals.push(v); }); if(!vals.length) return; var v=vals.join(f.slot==="phone"?"-":" "); if(f.slot&&slots[f.slot]) slots[f.slot].push(v); detail.push((f.label||f.name)+": "+v); }); var msg=slots.message.join("\n\n"); var body={ name:slots.name.join(" ")||"(未記入)", email:slots.email.join(" "), phone:slots.phone.join("-"), company:slots.company.join(" "), message:(msg?msg+"\n\n":"")+"---- 入力内容 ----\n"+detail.join("\n"), _hp:"" }; var note=document.createElement("p"); note.setAttribute("data-sm-contact-note",""); note.style.cssText="margin:1em 0;font-weight:600"; note.textContent="送信中…"; cform.parentNode.insertBefore(note,cform); fetch(API+"/contact/"+SITE,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(body)}) .then(function(r){return r.ok?r.json().catch(function(){return{ok:true}}):Promise.reject(r.status)}) .then(function(){ note.textContent="お問い合わせを送信しました。ありがとうございます。"; cform.style.display="none"; }) .catch(function(){ note.textContent="送信に失敗しました。時間をおいて再度お試しください。"; busy=false; }); },true); } } // ── widget の寸法/コントラスト補正 ── function fit(host){ if(host.__smFit||!host.shadowRoot) return; host.__smFit=1; var rf=parseFloat(getComputedStyle(document.documentElement).fontSize)||16; if(Math.abs(rf-16)>0.5) host.style.zoom=String(16/rf); var el=host,bg=null; while(el&&el!==document.documentElement){ var c=getComputedStyle(el).backgroundColor; if(c&&!/^rgba\(0, 0, 0, 0\)$|transparent/.test(c)){bg=c;break;} el=el.parentElement; } var m=bg&&bg.match(/(\d+),\s*(\d+),\s*(\d+)/); if(!m) return; if((0.2126*+m[1]+0.7152*+m[2]+0.0722*+m[3])/255>=0.45) return; // 明るい背景なら既定のままで良い var st=document.createElement("style"); st.textContent='h1,h2,h3,h4,h5,strong,b{color:#fff !important}' +'p,span,time,small,li,dd,dt,td,th,label{color:rgba(255,255,255,.78) !important}' +'a{color:#fff !important}article,li,tr,hr,section{border-color:rgba(255,255,255,.28) !important}' +'input,textarea,select{color:#111 !important;background:#fff !important}'; host.shadowRoot.appendChild(st); } function scan(){[].forEach.call(document.querySelectorAll("[data-sm-widget]"),fit);} scan(); var n=0,iv=setInterval(function(){scan();if(++n>40)clearInterval(iv);},250); // loader の描画待ち } if(document.readyState==="loading") document.addEventListener("DOMContentLoaded",boot); else boot(); })();