PageSourceSearch

https://line-harness-komu.tokai-5532.workers.dev/assets/index-D1AIGlIw.js

js line-harness-komu.tokai-5532.workers.dev collected 2026-10-03 08:08:19 UTC 46,703 bytes, 425 lines download raw bytes

1const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/main-BnNTFj-a.js","assets/client-auj285-g.js","assets/main-BueFtPQk.css","assets/main-yX3ezR3y.js","assets/main-DIWYgIef.css"])))=>i.map(i=>d[i]);
2(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const i of document.querySelectorAll('link[rel="modulepreload"]'))o(i);new MutationObserver(i=>{for(const r of i)if(r.type==="childList")for(const s of r.addedNodes)s.tagName==="LINK"&&s.rel==="modulepreload"&&o(s)}).observe(document,{childList:!0,subtree:!0});function n(i){const r={};return i.integrity&&(r.integrity=i.integrity),i.referrerPolicy&&(r.referrerPolicy=i.referrerPolicy),i.crossOrigin==="use-credentials"?r.credentials="include":i.crossOrigin==="anonymous"?r.credentials="omit":r.credentials="same-origin",r}function o(i){if(i.ep)return;i.ep=!0;const r=n(i);fetch(i.href,r)}})();const se="modulepreload",ae=function(e){return"/"+e},Q={},oe=function(t,n,o){let i=Promise.resolve();if(n&&n.length>0){let s=function(f){return Promise.all(f.map(d=>Promise.resolve(d).then(h=>({status:"fulfilled",value:h}),h=>({status:"rejected",reason:h}))))};document.getElementsByTagName("link");const a=document.querySelector("meta[property=csp-nonce]"),c=(a==null?void 0:a.nonce)||(a==null?void 0:a.getAttribute("nonce"));i=s(n.map(f=>{if(f=ae(f),f in Q)return;Q[f]=!0;const d=f.endsWith(".css"),h=d?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${f}"]${h}`))return;const p=document.createElement("link");if(p.rel=d?"stylesheet":se,d||(p.as="script"),p.crossOrigin="",p.href=f,c&&p.setAttribute("nonce",c),document.head.appendChild(p),d)return new Promise((v,k)=>{p.addEventListener("load",v),p.addEventListener("error",()=>k(new Error(`Unable to preload CSS for ${f}`)))})}))}function r(s){const a=new Event("vite:preloadError",{cancelable:!0});if(a.payload=s,window.dispatchEvent(a),!a.defaultPrevented)throw s}return i.then(s=>{for(const a of s||[])a.status==="rejected"&&r(a.reason);return t().catch(r)})},j={},q=(j==null?void 0:j.VITE_CALENDAR_CONNECTION_ID)||"",b={currentYear:new Date().getFullYear(),currentMonth:new Date().getMonth(),selectedDate:null,slots:[],selectedSlot:null,profile:null,friendId:null,loading:!1,submitting:!1};function ie(e){const t=document.createElement("div");return t.textContent=e,t.innerHTML}function Y(e,t){return fetch(e,{...t,headers:{"Content-Type":"application/json",...t==null?void 0:t.headers}})}function N(e){const t=new Date(e);return`${String(t.getHours()).padStart(2,"0")}:${String(t.getMinutes()).padStart(2,"0")}`}function M(e){const t=new Date(e+"T00:00:00"),n=["日","月","火","水","木","金","土"];return`${t.getFullYear()}年${t.getMonth()+1}月${t.getDate()}日(${n[t.getDay()]})`}function H(){return document.getElementById("app")}function ce(e,t){return new Date(e,t+1,0).getDate()}function le(e,t){return new Date(e,t,1).getDay()}function de(e,t,n){const o=new Date;return o.getFullYear()===e&&o.getMonth()===t&&o.getDate()===n}function fe(e,t,n){const o=new Date,i=new Date(e,t,n);return o.setHours(0,0,0,0),i<o}function ue(e,t,n){return`${e}-${String(t+1).padStart(2,"0")}-${String(n).padStart(2,"0")}`}function me(){const{currentYear:e,currentMonth:t,selectedDate:n}=b,o=ce(e,t),i=le(e,t),r=["日","月","火","水","木","金","土"];let s=`
3    <div class="booking-calendar">
4      <div class="calendar-header">
5        <button class="cal-nav" data-action="prev-month">&lt;</button>
6        <span class="cal-title">${e}年${t+1}月</span>
7        <button class="cal-nav" data-action="next-month">&gt;</button>
8      </div>
9      <div class="cal-weekdays">
10        ${r.map((a,c)=>`<span class="${c===0?"sun":c===6?"sat":""}">${a}</span>`).join("")}
11      </div>
12      <div class="cal-days">
13  `;for(let a=0;a<i;a++)s+='<span class="cal-day empty"></span>';for(let a=1;a<=o;a++){const c=ue(e,t,a),f=fe(e,t,a),d=de(e,t,a),p=["cal-day",f?"past":"active",d?"today":"",n===c?"selected":"",new Date(e,t,a).getDay()===0?"sun":"",new Date(e,t,a).getDay()===6?"sat":""].filter(Boolean).join(" ");s+=`<span class="${p}" ${f?"":`data-date="${c}"`}>${a}</span>`}return s+="</div></div>",s}function pe(){const{slots:e,selectedDate:t,selectedSlot:n,loading:o}=b;if(!t)return"";if(o)return`
14      <div class="slots-section">
15        <h3>${M(t)}</h3>
16        <div class="slots-loading">
17          <div class="loading-spinner"></div>
18          <p>空き状況を確認中...</p>
19        </div>
20      </div>
21    `;if(e.length===0)return`
22      <div class="slots-section">
23        <h3>${M(t)}</h3>
24        <p class="no-slots">この日は予約枠がありません</p>
25      </div>
26    `;const i=e.map(r=>{const s=(n==null?void 0:n.startAt)===r.startAt;return`<button class="${r.available?s?"slot-btn selected":"slot-btn available":"slot-btn full"}" ${r.available?`data-start="${r.startAt}" data-end="${r.endAt}"`:"disabled"}>${N(r.startAt)} - ${N(r.endAt)}</button>`}).join("");return`
27    <div class="slots-section">
28      <h3>${M(t)}</h3>
29      <div class="slots-grid">${i}</div>
30    </div>
31  `}function ge(){const{selectedSlot:e,selectedDate:t,profile:n}=b;return!e||!t?"":`
32    <div class="confirm-section">
33      <div class="confirm-card">
34        <h3>予約内容の確認</h3>
35        <div class="confirm-details">
36          <div class="confirm-row">
37            <span class="confirm-label">日付</span>
38            <span class="confirm-value">${M(t)}</span>
39          </div>
40          <div class="confirm-row">
41            <span class="confirm-label">時間</span>
42            <span class="confirm-value">${N(e.startAt)} - ${N(e.endAt)}</span>
43          </div>
44          <div class="confirm-row">
45            <span class="confirm-label">お名前</span>
46            <span class="confirm-value">${n?ie(n.displayName):"---"}</span>
47          </div>
48        </div>
49        <button class="book-btn" data-action="confirm-booking">予約を確定する</button>
50      </div>
51    </div>
52  `}function C(){const e=H();e.innerHTML=`
53    <div class="booking-page">
54      <div class="booking-header">
55        <h1>予約</h1>
56        <p>ご希望の日時をお選びください</p>
57      </div>
58      ${me()}
59      ${pe()}
60      ${ge()}
61    </div>
62  `,xe()}function he(e,t){const n=H();n.innerHTML=`
63    <div class="booking-page">
64      <div class="success-card">
65        <div class="success-icon">✓</div>
66        <h2>予約が完了しました</h2>
67        <div class="confirm-details">
68          <div class="confirm-row">
69            <span class="confirm-label">日付</span>
70            <span class="confirm-value">${M(e)}</span>
71          </div>
72          <div class="confirm-row">
73            <span class="confirm-label">時間</span>
74            <span class="confirm-value">${N(t.startAt)} - ${N(t.endAt)}</span>
75          </div>
76        </div>
77        <p class="success-message">ご予約ありがとうございます。<br>当日のお越しをお待ちしております。</p>
78        <button class="close-btn" data-action="close">閉じる</button>
79      </div>
80    </div>
81  `;const o=n.querySelector('[data-action="close"]');o==null||o.addEventListener("click",()=>{liff.isInClient()?liff.closeWindow():window.close()})}function be(e){var n;const t=H();t.innerHTML=`
82    <div class="booking-page">
83      <div class="card">
84        <h2 style="color: #e53e3e;">エラー</h2>
85        <p class="error">${ie(e)}</p>
86        <button class="close-btn" data-action="retry" style="margin-top:16px;">やり直す</button>
87      </div>
88    </div>
89  `,(n=t.querySelector('[data-action="retry"]'))==null||n.addEventListener("click",()=>{b.selectedDate=null,b.selectedSlot=null,b.slots=[],C()})}function xe(){const e=H();e.querySelectorAll(".cal-nav").forEach(n=>{n.addEventListener("click",()=>{n.dataset.action==="prev-month"?(b.currentMonth--,b.currentMonth<0&&(b.currentMonth=11,b.currentYear--)):(b.currentMonth++,b.currentMonth>11&&(b.currentMonth=0,b.currentYear++)),b.selectedDate=null,b.selectedSlot=null,b.slots=[],C()})}),e.querySelectorAll(".cal-day.active").forEach(n=>{n.addEventListener("click",()=>{const o=n.dataset.date;o&&(b.selectedDate=o,b.selectedSlot=null,b.slots=[],b.loading=!0,C(),ye(o))})}),e.querySelectorAll(".slot-btn.available").forEach(n=>{n.addEventListener("click",()=>{const o=n.dataset.start,i=n.dataset.end;b.selectedSlot={startAt:o,endAt:i,available:!0},C(),setTimeout(()=>{const r=H().querySelector(".confirm-section");r==null||r.scrollIntoView({behavior:"smooth"})},50)})});const t=e.querySelector('[data-action="confirm-booking"]');t==null||t.addEventListener("click",()=>ve())}async function ye(e){try{const t=new URLSearchParams({date:e});q&&t.set("connectionId",q);const n=await Y(`/api/integrations/google-calendar/slots?${t}`);if(!n.ok)throw new Error("スロット取得に失敗しました");const o=await n.json();if(!o.success)throw new Error("スロット取得に失敗しました");b.slots=o.data}catch(t){b.slots=[],console.error("fetchSlots error:",t)}finally{b.loading=!1,C()}}async function ve(){const{selectedSlot:e,selectedDate:t,profile:n,friendId:o}=b;if(!e||!t||!n||b.submitting)return;b.submitting=!0;const i=H().querySelector('[data-action="confirm-booking"]');i&&(i.disabled=!0,i.textContent="送信中...");try{const r={title:`${n.displayName}様 予約`,startAt:e.startAt,endAt:e.endAt};q&&(r.connectionId=q),o&&(r.friendId=o);const s=await Y("/api/integrations/google-calendar/book",{method:"POST",body:JSON.stringify(r)});if(!s.ok){const a=await s.json().catch(()=>null);throw new Error((a==null?void 0:a.error)||"予約に失敗しました")}he(t,e)}catch(r){b.submitting=!1,be(r instanceof Error?r.message:"予約に失敗しました")}}async function we(){const e=await liff.getProfile();b.profile=e;const t="lh_uuid";try{b.friendId=localStorage.getItem(t)}catch{}const n=liff.getIDToken();if(n){const o=b.friendId;Y("/api/liff/link",{method:"POST",body:JSON.stringify({idToken:n,displayName:e.displayName,existingUuid:o})}).then(async i=>{var r;if(i.ok){const s=await i.json();if((r=s==null?void 0:s.data)!=null&&r.userId)try{localStorage.setItem(t,s.data.userId),b.friendId=s.data.userId}catch{}}}).catch(()=>{})}C()}const Z="lh_uuid",m={formDef:null,xHarnessBaseUrl:null,profile:null,friendId:null,
89submitting:!1,verifiedXUsername:"",refTrackedLinkId:null};let F=!1;function g(e){const t=document.createElement("div");return t.textContent=e,t.innerHTML}function B(e,t){return fetch(e,{...t,headers:{"Content-Type":"application/json",...t==null?void 0:t.headers}})}function S(){return document.getElementById("app")}function z(e){const t=e.required?" required":"",n=e.placeholder?` placeholder="${g(e.placeholder)}"`:"",o=e.required?'<span class="required-mark">*</span>':"";if(e.name==="x_username")return`
90      <div class="form-field">
91        <label class="form-label" for="field-${g(e.name)}">
92          ${g(e.label)}${o}
93        </label>
94        <div class="x-autocomplete-wrap">
95          <input
96            type="text"
97            name="${g(e.name)}"
98            id="field-${g(e.name)}"
99            class="form-input x-autocomplete-input"
100            placeholder="${e.placeholder?g(e.placeholder):"X ID or 名前で検索(3文字以上)"}"
101            autocomplete="off"
102            ${t} />
103          <ul class="x-suggest-list" id="x-suggest-list" hidden></ul>
104          <p class="x-suggest-hint" id="x-suggest-hint" hidden>3文字以上入力してください</p>
105        </div>
106        <div class="x-conditions" id="x-conditions" hidden></div>
107        <p class="x-conditions-result" id="x-conditions-result" hidden></p>
108      </div>
109    `;let i="";switch(e.type){case"textarea":i=`<textarea
110        name="${g(e.name)}"
111        id="field-${g(e.name)}"
112        class="form-textarea"
113        rows="4"
114        ${n}${t}></textarea>`;break;case"select":{const a=(e.options??[]).map(c=>`<option value="${g(c)}">${g(c)}</option>`).join("");i=`<select
115        name="${g(e.name)}"
116        id="field-${g(e.name)}"
117        class="form-select"${t}>
118        <option value="">選択してください</option>
119        ${a}
120      </select>`;break}case"radio":{const a=(e.options??[]).map(c=>`<label class="radio-label">
121              <input type="radio" name="${g(e.name)}" value="${g(c)}"${t} />
122              ${g(c)}
123            </label>`).join("");i=`<div class="radio-group${e.columns===2?" two-col":""}">${a}</div>`;break}case"checkbox":{const a=(e.options??[]).map(c=>`<label class="checkbox-label">
124              <input type="checkbox" name="${g(e.name)}" value="${g(c)}" />
125              ${g(c)}
126            </label>`).join("");i=`<div class="checkbox-group${e.columns===2?" two-col":""}">${a}</div>`;break}default:i=`<input
127        type="${g(e.type)}"
128        name="${g(e.name)}"
129        id="field-${g(e.name)}"
130        class="form-input"
131        ${n}${t} />`;break}const r=e.note?`<p class="form-note">${g(e.note)}</p>`:"";return`
132    ${e.section?`<div class="form-section">
133         <h2 class="form-section__title">${g(e.section)}</h2>
134         ${e.sectionNote?`<p class="form-section__note">${g(e.sectionNote)}</p>`:""}
135       </div>`:""}
136    <div class="form-field">
137      <label class="form-label" for="field-${g(e.name)}">
138        ${g(e.label)}${o}
139      </label>
140      ${r}
141      ${i}
142    </div>
143  `}function ke(){if(document.getElementById("form-styles"))return;const e=document.createElement("style");e.id="form-styles",e.textContent=`
144    .form-page { max-width: 480px; margin: 0 auto; padding: 16px; }
145    .form-header { text-align: center; margin-bottom: 24px; }
146    .form-header h1 { font-size: 20px; color: #333; margin-bottom: 8px; }
147    .form-description { font-size: 14px; color: #999; }
148    .form-profile { display: flex; align-items: center; justify-content: center; gap: 8px; margin-top: 12px; }
149    .form-profile img { width: 36px; height: 36px; border-radius: 50%; }
150    .form-profile span { font-size: 14px; font-weight: 600; }
151    .form-body { background: #fff; border-radius: 12px; padding: 20px; box-shadow: 0 1px 3px rgba(0,0,0,0.08); }
152    .form-field { margin-bottom: 20px; }
153    .form-label { display: block; font-size: 14px; font-weight: 600; color: #333; margin-bottom: 6px; }
154    .required-mark { color: #e53e3e; margin-left: 2px; }
155    /* 誤入力が致命的な項目の注意書き。目に留まるよう赤の枠+背景で強調する。 */
156    .form-note {
157      margin: 4px 0 8px; padding: 8px 10px; border-radius: 8px;
158      background: #fff5f5; border: 1px solid #feb2b2;
158 color: #c53030;
159      font-size: 13px; line-height: 1.6; white-space: pre-line; font-weight: 600;
160    }
161    .form-input, .form-textarea, .form-select {
162      width: 100%; padding: 12px; border: 1.5px solid #e0e0e0; border-radius: 8px;
163      font-size: 16px; font-family: inherit; background: #fafafa;
164      transition: border-color 0.15s; box-sizing: border-box;
165      -webkit-appearance: none;
166    }
167    .form-input:focus, .form-textarea:focus, .form-select:focus {
168      outline: none; border-color: #06C755; background: #fff;
169    }
170    .form-textarea { resize: vertical; min-height: 80px; }
171    .form-select { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23666' d='M6 8L1 3h10z'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: right 12px center; }
172    .form-section { margin: 26px 0 12px; padding-top: 18px; border-top: 2px solid #ececec; }
173    .form-field:first-of-type + .form-section, .form-section:first-child { margin-top: 4px; border-top: none; padding-top: 0; }
174    .form-section__title { margin: 0; font-size: 16px; font-weight: 700; color: #1a1a1a; letter-spacing: .01em; }
175    .form-section__note { margin: 4px 0 0; font-size: 13px; line-height: 1.6; color: #777; }
176    .radio-group, .checkbox-group { display: flex; flex-direction: column; gap: 10px; }
177    .radio-group.two-col, .checkbox-group.two-col { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
178    .radio-label, .checkbox-label {
179      display: flex; align-items: center; gap: 8px; font-size: 15px; color: #333;
180      padding: 10px 12px; background: #fafafa; border-radius: 8px; border: 1.5px solid #e0e0e0;
181      cursor: pointer; transition: border-color 0.15s;
182    }
183    .radio-label:has(input:checked), .checkbox-label:has(input:checked) {
184      border-color: #06C755; background: #e8faf0;
185    }
186    .radio-label input, .checkbox-label input { accent-color: #06C755; width: 18px; height: 18px; }
187    .radio-label input[type="radio"] { appearance: none; -webkit-appearance: none; width: 18px; height: 18px; border: 2px solid #ccc; border-radius: 50%; background: #fff; cursor: pointer; }
188    .radio-label input[type="radio"]:checked { background: #fff; border-color: #06C755; border-width: 5px; }
189    .submit-btn {
190      width: 100%; padding: 14px; border: none; border-radius: 8px;
191      background: #06C755; color: #fff; font-size: 16px; font-weight: 700;
192      cursor: pointer; font-family: inherit; margin-top: 8px; transition: opacity 0.15s;
193    }
194    .submit-btn:active { opacity: 0.85; }
195    .submit-btn:disabled { background: #bbb; cursor: not-allowed; }
196    .form-error { color: #e53e3e; font-size: 12px; margin-top: 4px; }
197    .x-loading-spinner {
198      width: 28px; height: 28px; border: 3px solid #333; border-top-color: #1D9BF0;
199      border-radius: 50%; animation: x-spin 0.8s linear infinite;
200    }
201    @keyframes x-spin { to { transform: rotate(360deg); } }
202    .x-autocomplete-wrap { position: relative; }
203    .x-suggest-list {
204      position: absolute; top: calc(100% + 4px); left: 0; right: 0; z-index: 100;
205      background: #1a1a1a; border: 1.5px solid #333; border-radius: 8px;
206      list-style: none; margin: 0; padding: 4px 0; box-shadow: 0 4px 12px rgba(0,0,0,0.4);
207      max-height: 240px; overflow-y: auto;
208    }
209    .x-suggest-item {
210      display: flex; align-items: center; gap: 10px;
211      padding: 10px 12px; cursor: pointer; transition: background 0.1s;
212    }
213    .x-suggest-item:hover, .x-suggest-item.focused { background: #2a2a2a; }
214    .x-suggest-avatar {
215      width: 32px; height: 32px; border-radius: 50%; object-fit: cover; flex-shrink: 0;
216      background: #333;
217    }
218    .x-suggest-avatar-placeholder {
219      width: 32px; height: 32px; border-radius: 50%; background: #444;
220      display: flex; align-items: center; justify-content: center;
221      font-size: 14px; color: #aaa; flex-shrink: 0;
222    }
223    .x-suggest-names { display: flex; flex-direction: column; overflow: hidden; }
224    .x-suggest-display { font-size: 14px; font-weight: 600; color: #f0f0f0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
225    .x-suggest-username { font-size: 12px; color: #888; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
226    .x-suggest-hint { font-size: 12px; color: #888; margin-top: 4px; }
227    .x-conditions-card {
228      margin-top: 12px;
229      padding: 16px;
230      background: #111;
231      border-radius: 12px;
232      border: 1px solid #222;
233    }
234    .x-condition-row {
235      display: flex;
236      align-items: center;
237      gap: 10px;
238      padding: 8px 0;
239      border-bottom: 1px solid #1a1a1a;
240      font-size: 14px;
241      color: #ccc;
242    }
243    .x-condition-row:last-child { border-bottom: none; }
244    .x-condition-check {
245      width: 28px;
246      height: 28px;
247      border-radius: 50%;
248      display: flex;
249      align-items: center;
250      justify-content: center;
251      font-size: 14px;
252      font-weight: bold;
253      flex-shrink: 0;
254    }
255    .x-condition-check.pass {
256      background: rgba(6, 199, 85, 0.15);
257      color: #06C755;
258      border: 2px solid #06C755;
259    }
260    .x-condition-check.fail {
261      background: rgba(229, 62, 62, 0.15);
262      color: #e53e3e;
263      border: 2px solid #e53e3e;
264    }
265    .x-condition-check.na {
266      background: rgba(255,255,255,0.05);
267      color: #555;
268      border: 2px solid #333;
269    }
270    .x-condition-check.checking {
271      background: rgba(255,255,255,0.05);
272      color: #888;
273      border: 2px solid #444;
274    }
275    .x-condition-check.checking .spin {
276      display: inline-block;
277      animation: spin 1s linear infinite;
278      font-size: 12px;
279    }
280    @keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
281    .x-conditions-summary {
282      margin-top: 12px;
283      padding: 10px 16px;
284      border-radius: 8px;
285      font-size: 14px;
286      font-weight: 600;
287      text-align: center;
288    }
289    .x-conditions-summary.pass {
290      background: rgba(6, 199, 85, 0.1);
291      color: #06C755;
292      border: 1px solid rgba(6, 199, 85, 0.3);
293    }
294    .x-conditions-summary.fail {
295      background: rgba(229, 62, 62, 0.1);
296      color: #e53e3e;
297      border: 1px solid rgba(229, 62, 62, 0.3);
298    }
299    .form-success { text-align: center; padding: 40px 20px; }
300    .form-success .check { width: 64px; height: 64px; border-radius: 50%; background: #06C755; color: #fff; font-size: 32px; line-height: 64px; margin: 0 auto 16px; }
301    .form-success h2 { font-size: 20px; color: #06C755; margin-bottom: 12px; }
302    .form-success p { font-size: 14px; color: #666; line-height: 1.6; }
303  `,document.head.appendChild(e)}function $e(){var f;const{formDef:e,profile:t}=m;if(!e)return;ke();const n=S(),o=e.hideProfile||!(t!=null&&t.pictureUrl)?"":`<div class="form-profile">
304        <img src="${t.pictureUrl}" alt="" />
305        <span>${g(t.displayName)} さん</span>
306      </div>`,i=e.fields.filter(d=>d.name!=="x_username"),r=e.fields.find(d=>d.name==="x_username"),s=!!r&&!!e.onSubmitWebhookUrl,a=i.map(z).join(""),c=r?z(r):"";if(s)n.innerHTML=`
307      <div class="form-page">
308        <div class="form-header">
309          <h1>${g(e.name).replace(/\\n|\n/g,"<br>")}</h1>
310          ${e.description&&!e.onSubmitWebhookUrl?`<p class="form-description">${g(e.description).replace(/\\n|\n/g,"<br>")}</p>`:""}
311          ${o}
312        </div>
313        <!-- Page 1: Survey -->
314        <div id="form-page-1">
315          <form id="survey-form" class="form-body" novalidate>
316            ${a}
317            <button type="submit" class="submit-btn" id="nextBtn">次へ →</button>
318          </form>
319        </div>
320        <!-- Page 2: X-Link -->
321        <div id="form-page-2" hidden>
322          <div class="form-header" style="padding-top:0">
323            <h1>X-Link で受け取り</h1>
324          </div>
325          <form id="liff-form" class="form-body" novalidate>
326            ${c}
327            <button type="submit" class="submit-btn" id="submitBtn">X Harness を受け取る</button>
328          </form>
329        </div>
330      </div>
331    `,(f=document.getElementById("survey-form"))==null||f.addEventListener("submit",async d=>{var v,k,P,I;d.preventDefault();for(const x of i)if(x.required)if(x.type==="checkbox"){if(document.querySelectorAll(`input[name="${x.name}"]:checked`).length===0){W(`${x.label} は必須項目です`);return}}else if(x.type==="radio"){if(!document.querySelector(`input[name="${x.name}"]:checked`)){W(`${x.label} は必須項目です`);return}}else{const l=document.querySelector(`[name="${x.name}"]`);if(!l||!l.value.trim()){W(`${x.label} は必須項目です`);return}}const h=document.getElementById("nextBtn");h.disabled=!0,h.textContent="保存中...";const p={};for(const x of i)x.type==="checkbox"?p[x.name]=Array.from(document.querySelectorAll(`input[name="${x.name}"]:checked`)).map(l=>l.value):x.type==="radio"?p[x.name]=((v=document.querySelector(`input[name="${x.name}"]:checked`))==null?void 0:v.value)??"":p[x.name]=(((k=document.querySelector(`[name="${x.name}"]`))==null?void 0:k.value)??"").trim();try{await B(`/api/forms/${e.id}/partial`,{method:"POST",body:JSON.stringify({lineUserId:(P=m.profile)==null?void 0:P.userId,friendId:m.friendId,data:p})})}catch{}if(document.getElementById("form-page-1").hidden=!0,document.getElementById("form-page-2").hidden=!1,window.scrollTo(0,0),!F){const x=document.querySelector(".x-autocomplete-input");x&&(x.disabled=!0);const l=document.querySelector(".x-autocomplete-wrap");if(l){const u=document.createElement("div");u.id="x-loading-overlay",u.innerHTML='<div class="x-loading-spinner"></div><p>X連携データを読み込み中...</p>',u.style.cssText="display:flex;flex-direction:column;align-items:center;gap:12px;padding:24px 0;color:#888;font-size:14px;",(I=l.parentElement)==null||I.insertBefore(u,l)}}}),ee();else{const d=e.fields.map(z).join("");n.innerHTML=`
332      <div class="form-page">
333        <div class="form-header">
334          <h1>${g(e.name).replace(/\\n|\n/g,"<br>")}</h1>
335          ${e.description&&!e.onSubmitWebhookUrl?`<p class="form-description">${g(e.description).replace(/\\n|\n/g,"<br>")}</p>`:""}
336          ${o}
337        </div>
338        <form id="liff-form" class="form-body" novalidate>
339          ${d}
340          <button type="submit" class="submit-btn" id="submitBtn">送信する</button>
341        </form>
342      </div>
343    `,ee()}}async function Ie(e,t){var c;const n=document.getElementById("submit-conditions");n&&n.remove();const o=[["reply","リプライ"],["like","いいね"],["repost","リポスト"],["follow","フォロー"]],i=document.createElement("div");i.id="submit-conditions",i.innerHTML='<div class="x-conditions-card" id="condition-card"></div>';const r=document.getElementById("submitBtn");(c=r==null?void 0:r.parentElement)==null||c.insertBefore(i,r);const s=document.getElementById("condition-card");for(const[f,d]of o){const h=e[f];if(h==null)continue;const p=document.createElement("div");p.className="x-condition-row",p.innerHTML=`<div class="x-condition-check checking"><span class="spin">⏳</span></div><span>${d}を確認中...</span>`,s.appendChild(p),await new Promise(v=>setTimeout(v,600)),h?p.innerHTML=`<div class="x-condition-check pass">✓</div><span>${d}</span>`:p.innerHTML=`<div class="x-condition-check fail">✗</div><span>${d}</span>`}await new Promise(f=>setTimeout(f,400));const a=document.createElement("div");a.className="x-conditions-summary pass",a.textContent="🎉 条件クリア!",i.appendChild(a)}function Se(e){var o;const t=S(),n=e.split(`
344`).map(i=>`<p>${g(i)}</p>`).join("");t.innerHTML=`
345    <div class="form-page">
346      <div class="success-card">
347        <div class="success-icon">🎉</div>
348        <h2>おめでとうございます!</h2>
349        <div class="success-message">${n}</div>
350        <button class="close-btn" id="closeBtn">閉じる</button>
351      </div>
352    </div>
353  `,(o=document.getElementById("closeBtn"))==null||o.addEventListener("click",()=>{liff.isInClient()?liff.closeWindow():window.close()})}function Ee(){var t;const e=S();e.innerHTML=`
354    <div class="form-page">
355      <div class="success-card">
356        <div class="success-icon">✓</div>
357        <h2>送信完了!</h2>
358        <p class="success-message">ご回答ありがとうございました。</p>
359        <button class="close-btn" id="closeBtn">閉じる</button>
360      </div>
361    </div>
362  `,(t=document.getElementById("closeBtn"))==null||t.addEventListener("click",()=>{liff.isInClient()?liff.closeWindow():window.close()}),liff.isInClient()&&setTimeout(()=>{try{liff.closeWindow()}catch{}},3e3)}function U(e){const t=S();t.innerHTML=`
363    <div class="form-page">
364      <div class="card">
365        <h2 style="color: #e53e3e;">エラー</h2>
366        <p class="error">${g(e)}</p>
367      </div>
368    </div>
369  `}function W(e){var i;const t=S().querySelector(".form-error-msg");t&&t.remove();const n=document.createElement("p");n.className="form-error-msg",n.style.cssText="color:#e53e3e;font-size:14px;margin:8px 0;text-align:center;",n.textContent=e;const o=document.getElementById("nextBtn")||document.getElementById("submitBtn");(i=o==null?void 0:o.parentElement)==null||i.insertBefore(n,o)}function Te(){const e=S();e.innerHTML=`
370    <div class="form-page">
371      <div class="card" style="text-align:center;padding:40px 20px;">
372        <div class="loading-spinner"></div>
373        <p style="margin-top:12px;color:#718096;">読み込み中...</p>
374      </div>
375    </div>
376  `}function Le(){const{formDef:e}=m;if(!e)return{};const t={};for(const n of e.fields)if(n.type==="checkbox"){const o=Array.from(document.querySelectorAll(`input[name="${n.name}"]:checked`)).map(i=>i.value);t[n.name]=o}else if(n.type==="radio"){const o=document.querySelector(`input[name="${n.name}"]:checked`);t[n.name]=(o==null?void 0:o.value)??""}else{const o=document.querySelector(`[name="${n.name}"]`);t[n.name]=(o==null?void 0:o.value)??""}return t}function De(){const{formDef:e}=m;if(!e)return null;for(const t of e.fields)if(t.required)if(t.type==="checkbox"){if(document.querySelectorAll(`input[name="${t.name}"]:checked`).length===0)return`${t.label} は必須項目です`}else if(t.type==="radio"){if(!document.querySelector(`input[name="${t.name}"]:checked`))return`${t.label} は必須項目です`}else{const n=document.querySelector(`[name="${t.name}"]`);if(!n||!n.value.trim())return`${t.label} は必須項目です`}return null}async function Pe(){var n,o,i,r,s,a;if(m.submitting||!m.formDef)return;const e=De();if(e){const c=S().querySelector(".form-error-msg");c&&c.remove();const f=document.createElement("p");f.className="form-error-msg",f.style.cssText="color:#e53e3e;font-size:14px;margin:8px 0;text-align:center;",f.textContent=e;const d=document.getElementById("submitBtn");(n=d==null?void 0:d.parentElement)==null||n.insertBefore(f,d);return}m.submitting=!0;const t=document.getElementById("submitBtn");t&&(t.disabled=!0,t.textContent="送信中...");try{const c=Le();console.log("Form data collected:",JSON.stringify(c));const f=m.formDef.fields.some(p=>p.name==="x_username");if(m.formDef.onSubmitWebhookUrl&&f){const p=(c.x_username??"").trim().replace(/^@/,"");if(!p||p!==m.verifiedXUsername){m.submitting=!1,t&&(t.disabled=!1,t.textContent="送信する");const u=S().querySelector(".form-error-msg");u&&u.remove();const y=document.createElement("p");y.className="form-error-msg",y.style.cssText="color:#e53e3e;font-size:14px;margin:8px 0;text-align:center;",y.textContent=p?"X IDを入力後、入力欄の外をタップして確認してください":"X IDを入力してください",(o=t==null?void 0:t.parentElement)==null||o.insertBefore(y,t);return}const v={reply:!0,like:!0,repost:!0,follow:!0};t&&(t.textContent="判定中..."),await Ie(v,!0),await new Promise(u=>setTimeout(u,500));const k=m.formDef.onSubmitMessageContent||"条件をクリアしました!",P=k.trimStart().startsWith("{")?"特典をLINEでお送りしました!":k,I={data:{...c},_skipWebhook:!0};(i=m.profile)!=null&&i.userId&&(I.lineUserId=m.profile.userId),m.refTrackedLinkId&&(I.trackedLinkId=m.refTrackedLinkId);const x=await B(`/api/forms/${m.formDef.id}/submit`,{method:"POST",body:JSON.stringify(I)});if(!x.ok){const u=await x.text().catch(()=>"");let y="送信に失敗しました";try{y=JSON.parse(u).error||y}catch{y=u||y}throw new Error(`${x.status}: ${y}`)}const l=await x.clone().json().catch(()=>null);if(((r=l==null?void 0:l.data)==null?void 0:r.webhookPassed)===!1)throw new Error(m.formDef.onSubmitWebhookFailMessage||"条件を満たしていません");Se(P);return}const d={data:c};(s=m.profile)!=null&&s.userId&&(d.lineUserId=m.profile.userId),m.refTrackedLinkId&&(d.trackedLinkId=m.refTrackedLinkId),console.log("Submitting to:",`/api/forms/${m.formDef.id}/submit`);const h=await B(`/api/forms/${m.formDef.id}/submit`,{method:"POST",body:JSON.stringify(d)});if(console.log("Response status:",h.status),!h.ok){const p=await h.text().catch(()=>"");let v="送信に失敗しました";try{v=JSON.parse(p).error||v}catch{v=p||v}throw new Error(`${h.status}: ${v}`)}Ee()}catch(c){m.submitting=!1,t&&(t.disabled=!1,t.textContent="送信する");const f=S().querySelector(".form-error-msg");f&&f.remove();const d=document.createElement("p");d.className="form-error-msg",d.style.cssText="color:#e53e3e;font-size:14px;margin:8px 0;text-align:center;",d.textContent=c instanceof Error?c.message:"送信に失敗しました";const h=document.getElementById("submitBtn");(a=h==null?void 0:h.parentElement)==null||a.insertBefore(d,h)}}function Ue(){const e=document.querySelector(".x-autocomplete-input");if(!e)return;const t=document.getElementById("x-suggest-list"),n=document.getElementById("x-suggest-hint"),o=document.getElementById("x-conditions"),i=document.getElementById("x-conditions-result");if(!t)return;let r=null,s=-1,a=[];function c(){var $;const u=new URLSearchParams(window.location.search).get("gate");if(u)return u;const w=((($=m.formDef)==null?void 0:$.onSubmitWebhookUrl)??"").match(/engagement-gates\/([^/]+)\/verify/);return w?w[1]:null}function f(){var u;const l={};if((u=m.formDef)!=null&&u.onSubmitWebhookHeaders)try{Object.assign(l,JSON.parse(m.formDef.onSubmitWebhookHeaders))}catch{}return l}const d=c();m.xHarnessBaseUrl&&d?fetch(`${m.xHarnessBaseUrl}/api/engagement-gates/${encodeURIComponent(d)}/repliers`,{headers:f()}).then(l=>l.json()).then(l=>{a=l.data??[],F=!0;const u=document.getElementById("x-loading-overlay");u&&(u.remove(),e.disabled=!1,e.focus())}).catch(()=>{F=!0;const l=document.getElementById("x-loading-overlay");l&&(l.remove(),e.disabled=!1)}):F=!0;function h(){o&&(o.hidden=!0),i&&(i.hidden=!0)}function p(){t.hidden=!0,t.innerHTML="",s=-1,n&&(n.hidden=!0)}function v(l){e.value=l,m.verifiedXUsername=l,p(),e.focus(),I(l)}function k(l){l.forEach((y,w)=>{y.classList.toggle("focused",w===s)});const u=l[s];u==null||u.scrollIntoView({block:"nearest"})}function P(l){if(!o||!i)return;if(l.userNotFound){o.hidden=!0,i.innerHTML='❌ Xアカウントが見つかりません<br><span style="font-size:11px;font-weight:normal">IDを確認してもう一度お試しください</span>',i.className="x-conditions-summary fail",i.hidden=!1;return}const u=[{key:"reply",label:"リプライ"},{key:"like",label:"いいね"},{key:"repost",label:"リポスト"},{key:"follow",label:"フォロー"}],y=l.conditions??[],w={};for(const E of y)w[E.type]=E;let $="";for(const E of u){const T=w[E.key];T&&(T.required?T.passed?$+=`
377          <div class="x-condition-row">
378            <div class="x-condition-check pass">✓</div>
379            <span>${g(E.label)}</span>
380          </div>`:$+=`
381          <div class="x-condition-row">
382            <div class="x-condition-check fail">✗</div>
383            <span>${g(E.label)}</span>
384          </div>`:$+=`
385          <div class="x-condition-row">
386            <div class="x-condition-check na">—</div>
387            <span>${g(E.label)}</span>
388          </div>`)}$?(o.innerHTML=`<div class="x-conditions-card">${$}</div>`,o.hidden=!1):o.hidden=!0,l.eligible?(i.textContent="🎉 条件クリア!特典を受け取れます",i.className="x-conditions-summary pass"):(i.textContent="条件を満たしていません",i.className="x-conditions-summary fail"),i.hidden=!1}async function I(l){const u=l.trim().replace(/^@/,"");if(!m.xHarnessBaseUrl||!u)return;const y=c();if(y)try{const w=`${m.xHarnessBaseUrl}/api/engagement-gates/${encodeURIComponent(y)}/verify?username=${encodeURIComponent(u)}`,$=await fetch(w,{headers:f()});if(!$.ok)throw new Error("verify failed");const T=(await $.json()).data;T&&(P(T),T.userNotFound||(m.verifiedXUsername=u,e&&(e.value=u)))}catch{h(),i&&(i.innerHTML='⚠️ 確認中にエラーが発生しました<br><span style="font-size:11px;font-weight:normal">しばらく待ってからもう一度お試しください</span>',i.className="x-conditions-summary fail",i.hidden=!1)}}function x(l){if(t.innerHTML="",s=-1,l.length===0){t.hidden=!0,n&&(n.innerHTML=a.length===0?'<span style="color:#e53e3e">⏳ まだリアクションがありません</span><br><span style="font-size:11px;color:#888">ポストにリポスト&フォローしてから再度お試しください</span>':'<span style="color:#888">候補に表示されなくても、そのままIDを入力して送信できます</span>',n.hidden=!1);return}n&&(n.hidden=!0);for(const u of l){const y=document.createElement("li");y.className="x-suggest-item",y.dataset.username=u.username;const w=u.profileImageUrl?`<img class="x-suggest-avatar" src="${g(u.profileImageUrl)}" alt="" />`:'<div class="x-suggest-avatar-placeholder">@</div>';y.innerHTML=`
389        ${w}
390        <div class="x-suggest-names">
391          <span class="x-suggest-display">${g(u.displayName)}</span>
392          <span class="x-suggest-username">@${g(u.username)}</span>
393        </div>
394      `,y.addEventListener("mousedown",$=>{$.preventDefault(),v(u.username)}),t.appendChild(y)}t.hidden=!1}e.addEventListener("input",()=>{const l=e.value.trim().replace(/^@/,"");if(r!==null&&clearTimeout(r),l!==m.verifiedXUsername&&(m.verifiedXUsername=""),!l){p(),h();return}if(l.length<3){p(),h(),n&&(n.textContent="3文字以上入力してください",n.hidden=!1);return}n&&(n.hidden=!0);const u=l.toLowerCase(),y=a.filter(w=>w.username.toLowerCase().includes(u)||w.displayName.toLowerCase().includes(u)).slice(0,5);x(y)}),e.addEventListener("keydown",l=>{const u=t.querySelectorAll(".x-suggest-item");if(u.length)if(l.key==="ArrowDown")l.preventDefault(),s=Math.min(s+1,u.length-1),k(u);else if(l.key==="ArrowUp")l.preventDefault(),s=Math.max(s-1,0),k(u);else if(l.key==="Enter"&&s>=0){l.preventDefault();const w=u[s].dataset.username;w&&v(w)}else l.key==="Escape"&&p()}),e.addEventListener("blur",()=>{setTimeout(()=>{p();const l=e.value.trim().replace(/^@/,"");l&&c()&&(r!==null&&clearTimeout(r),r=setTimeout(()=>{I(l)},300))},150)})}function ee(){const e=document.getElementById("liff-form");e==null||e.addEventListener("submit",t=>{t.preventDefault(),Pe()}),Ue()}async function Ce(e){var t;if(!e){U("フォームIDが指定されていません");return}Te();try{const[n,o]=await Promise.all([liff.getProfile(),B(`/api/forms/${e}`)]);m.profile=n;try{m.friendId=localStorage.getItem(Z)}catch{}const i=liff.getIDToken();if(i&&B("/api/liff/link",{method:"POST",body:JSON.stringify({idToken:i,displayName:n.displayName,existingUuid:m.friendId})}).then(async f=>{var d;if(f.ok){const h=await f.json();if((d=h==null?void 0:h.data)!=null&&d.userId)try{localStorage.setItem(Z,h.data.userId),m.friendId=h.data.userId}catch{}}}).catch(()=>{}),!o.ok){o.status===404?U("フォームが見つかりません"):U("フォームの読み込みに失敗しました");return}const r=await o.json();if(!r.success||!r.data){U("フォームの読み込みに失敗しました");return}if(!r.data.isActive){U("このフォームは現在受付を停止しています");return}m.formDef=r.data;const s=new URLSearchParams(window.location.search),a=s.get("xh");if(a)m.xHarnessBaseUrl=a.replace(/\/$/,"");else if(r.data.onSubmitWebhookUrl){const f=r.data.onSubmitWebhookUrl.match(/^(https?:\/\/[^/]+)/);f&&(m.xHarnessBaseUrl=f[1])}const c=s.get("ref");c&&(m.refTrackedLinkId=c),$e(),B(`/api/forms/${m.formDef.id}/opened`,{method:"POST",body:JSON.stringify({lineUserId:(t=m.profile)==null?void 0:t.userId,friendId:m.friendId})}).catch(()=>{})}catch(n){U(n instanceof Error?n.message:"エラーが発生しました")}}const Be=new Set(["javascript:","data:","vbscript:","file:"]);function Ne(e){if(!e)return null;const t=e.trim();if(!t)return null;for(let o=0;o<t.length;o++){const i=t.charCodeAt(o);if(i<32||i===127)return null}if(/^[/\\][/\\]/.test(t))return null;if(t.startsWith("/"))return t;let n;try{n=new URL(t)}catch{return null}return Be.has(n.protocol.toLowerCase())?null:t}const J={};function He(){const e=new URLSearchParams(window.location.search).get("liffId");return e||(J==null?void 0:J.VITE_LIFF_ID)||""}const O=He();if(!O)throw new Error("LIFF ID not found. Set ?liffId= in LIFF endpoint URL or VITE_LIFF_ID env.");const re="lh_uuid";let A="";function L(e,t){return fetch(e,{...t,headers:{"Content-Type":"application/json",...t==null?void 0:t.headers}})}function Me(){return window.location.pathname.replace(/^\/+/,"")==="book"?"book":new URLSearchParams(window.location.search).get("page")}function Oe(){const e=new URLSearchParams(window.location.search);return Ne(e.get("redirect"))}function R(){return new URLSearchParams(window.location.search).get("ref")}function V(){try{return localStorage.getItem(re)}catch{return null}}function G(e){try{localStorage.setItem(re,e)}catch{}}function _(e){const t=document.createElement("div");return t.textContent=e,t.innerHTML}function K(e){const t=document.getElementById("app"),n=A?`https://line.me/R/ti/p/${A}`:"#";t.innerHTML=`
395    <div class="card">
396      <div class="profile">
397        ${e.pictureUrl?`<img src="${e.pictureUrl}" alt="" />`:""}
398        <p class="name">
398${_(e.displayName)} さん</p>
399      </div>
400      <p class="message">まずは友だち追加をお願いします</p>
401      <a href="${n}" class="add-friend-btn" id="addFriendBtn">
402        友だち追加して始める
403      </a>
404      <p class="sub-message">追加後、この画面に戻ってきてください</p>
405    </div>
406  `;let o=!1;const i=async()=>{if(document.visibilityState==="visible")try{const{friendFlag:r}=await liff.getFriendship();if(!r)return;const s=new URLSearchParams(window.location.search).get("form");if(s&&!o){o=!0;try{const a=await liff.getProfile(),c=liff.getIDToken(),f=new URLSearchParams(window.location.search);await L("/api/liff/send-form-link",{method:"POST",body:JSON.stringify({lineUserId:a.userId,formId:s,idToken:c||"",ref:f.get("ref")||"",gate:f.get("gate")||"",xh:f.get("xh")||"",ig:f.get("ig")||""})})}catch{}}document.removeEventListener("visibilitychange",i),X(e,!1)}catch{}};document.addEventListener("visibilitychange",i)}function X(e,t){const n=document.getElementById("app"),o=R();n.innerHTML=`
407    <div class="card">
408      <div class="check-icon">${t?"🔄":"✓"}</div>
409      <h2>${t?"おかえりなさい!":"登録完了!"}</h2>
410      <div class="profile">
411        ${e.pictureUrl?`<img src="${e.pictureUrl}" alt="" />`:""}
412        <p class="name">${_(e.displayName)} さん</p>
413      </div>
414      <p class="message">
415        ${t?"以前のアカウント情報を引き継ぎました。":"ありがとうございます!これからお役立ち情報をお届けします。"}
416        <br>
416このページは閉じて大丈夫です。
417      </p>
418      ${o?`<p class="ref-badge">${_(o)}</p>`:""}
419    </div>
420  `,A&&setTimeout(()=>{window.location.href=`https://line.me/R/oaMessage/${A}/`},2e3)}function D(e){const t=document.getElementById("app");t.innerHTML=`
421    <div class="card">
422      <h2>エラー</h2>
423      <p class="error">${_(e)}</p>
424    </div>
425  `}async function te(){const e=Oe(),t=R();try{const n=V(),[o,i,r]=await Promise.all([liff.getProfile(),Promise.resolve(liff.getIDToken()),liff.getFriendship()]),s=new URLSearchParams(window.location.search),a=L("/api/liff/link",{method:"POST",body:JSON.stringify({idToken:i,displayName:o.displayName,existingUuid:n,ref:t,ig:s.get("ig")||"",gclid:s.get("gclid")||"",fbclid:s.get("fbclid")||"",twclid:s.get("twclid")||"",ttclid:s.get("ttclid")||"",utm_source:s.get("utm_source")||"",utm_medium:s.get("utm_medium")||"",utm_campaign:s.get("utm_campaign")||""})}).then(async c=>{var f;if(c.ok){const d=await c.json();(f=d==null?void 0:d.data)!=null&&f.userId&&G(d.data.userId)}return c}).catch(()=>{});if(t&&L("/api/affiliates/click",{method:"POST",body:JSON.stringify({code:t,url:window.location.href})}).catch(()=>{}),e){if(await Promise.race([a,new Promise(c=>setTimeout(c,500))]),e.includes("/t/")){const c=e.includes("?")?"&":"?";window.location.href=`${e}${c}lu=${encodeURIComponent(o.userId)}`}else window.location.href=e;return}
425if(!r.friendFlag){K(o);return}await a;{const c=new URLSearchParams(window.location.search).get("form");if(c){try{const f=liff.getIDToken(),d=new URLSearchParams(window.location.search);await L("/api/liff/send-form-link",{method:"POST",body:JSON.stringify({lineUserId:o.userId,formId:c,idToken:f||"",ref:t||"",gate:d.get("gate")||"",xh:d.get("xh")||"",ig:d.get("ig")||""})})}catch{}X(o,!!n)}else X(o,!!n)}}catch(n){e?window.location.href=e:D(n instanceof Error?n.message:"エラーが発生しました")}}async function Ae(){const[e,t,n]=await Promise.all([liff.getProfile(),Promise.resolve(liff.getIDToken()),liff.getFriendship()]);if(!t){D("LINE 認証情報の取得に失敗しました。LINE アプリ内で再度開いてください。");return}const o=V(),i=R(),r=new URLSearchParams(window.location.search).get("ig");if(L("/api/liff/link",{method:"POST",body:JSON.stringify({idToken:t,displayName:e.displayName,existingUuid:o,ref:i||void 0,ig:r||void 0})}).then(async c=>{var f;if(c.ok){const d=await c.json();(f=d==null?void 0:d.data)!=null&&f.userId&&G(d.data.userId)}}).catch(()=>{}),i&&L("/api/affiliates/click",{method:"POST",body:JSON.stringify({code:i,url:window.location.href})}).catch(()=>{}
425),!n.friendFlag){K(e);return}const s=document.getElementById("app");if(!s){D("mount target #app が見つかりません");return}const{mountSalonBooking:a}=await oe(async()=>{const{mountSalonBooking:c}=await import("./main-BnNTFj-a.js");return{mountSalonBooking:c}},__vite__mapDeps([0,1,2]));a(s,{liffId:O,lineUserId:e.userId,idToken:t})}async function ne(e){const[t,n,o]=await Promise.all([liff.getProfile(),Promise.resolve(liff.getIDToken()),liff.getFriendship()]);if(!n){D("LINE 認証情報の取得に失敗しました。LINE アプリ内で再度開いてください。");return}const i=V(),r=R();if(L("/api/liff/link",{method:"POST",body:JSON.stringify({idToken:n,displayName:t.displayName,existingUuid:i,ref:r||void 0})}).then(async p=>{var v;if(p.ok){const k=await p.json();(v=k==null?void 0:k.data)!=null&&v.userId&&G(k.data.userId)}}).catch(()=>{}),!o.friendFlag){K(t);return}const s=document.getElementById("app");if(!s){D("mount target #app が見つかりません");return}const c=new URLSearchParams(window.location.search).get("id")??"";if(e==="detail"&&!c){D("id クエリパラメータが必要です(?page=event&id=<eventId>)");return}const{mountEventBooking:f}=await oe(async()=>{const{mountEventBooking:p}=await import("./main-yX3ezR3y.js");return{mountEventBooking:p}},__vite__mapDeps([3,1,4])),d={liffId:O,lineUserId:t.userId,idToken:n};f(s,d,e==="detail"?{kind:"detail",eventId:c}:{kind:"history"})}async function Fe(){try{const e=fetch(`/api/liff/config?liffId=${encodeURIComponent(O)}`).then(n=>n.json()).then(n=>{var o;n.success&&((o=n.data)!=null&&o.botBasicId)&&(A=n.data.botBasicId)}).catch(()=>{});if(await liff.init({liffId:O}),!liff.isLoggedIn()){liff.login({redirectUri:window.location.href});return}await e;const t=Me();if(t==="book")await we();else if(t==="salon-book")await Ae();else if(t==="event")await ne("detail");else if(t==="event-me")await ne("history");else if(t==="form"){const o=new URLSearchParams(window.location.search).get("id");await Ce(o)}else t?await te():await te()}catch(e){D(e instanceof Error?e.message:"LIFF初期化エラー")}}Fe();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.