PageSourceSearch

https://www.ajpark.jp/migration-safety.js

js ajpark.jp collected 2026-10-02 16:19:07 UTC 9,056 bytes, 210 lines download raw bytes

1(() => {
2  "use strict";
3  const stagingPreview = window.location.hostname.endsWith(".workers.dev")
4    || ["localhost", "127.0.0.1"].includes(window.location.hostname);
5  if (stagingPreview) window["ga-disable-G-27P7B6LY38"] = true;
6
7  const formSecurity = new WeakMap();
8  let turnstileScriptPromise;
9
10  const showState = (form, ok, message) => {
11    const wrapper = form.closest(".w-form");
12    if (!wrapper) return;
13    const done = wrapper.querySelector(".w-form-done");
14    const fail = wrapper.querySelector(".w-form-fail");
15    form.style.display = ok ? "none" : "";
16    if (done) done.style.display = ok ? "block" : "none";
17    if (fail) {
18      fail.style.display = ok ? "none" : "block";
19      const text = fail.querySelector("div");
20      if (text && message) text.textContent = message;
21    }
22  };
23
24  const loadTurnstile = () => {
25    if (window.turnstile) return Promise.resolve();
26    if (turnstileScriptPromise) return turnstileScriptPromise;
27    turnstileScriptPromise = new Promise((resolve, reject) => {
28      const script = document.createElement("script");
29      script.src = "https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit";
30      script.async = true;
31      script.defer = true;
32      script.onload = () => window.turnstile ? resolve() : reject(new Error("セキュリティ確認を開始できませんでした。"));
33      script.onerror = () => reject(new Error("セキュリティ確認を読み込めませんでした。"));
34      document.head.append(script);
35    });
36    return turnstileScriptPromise;
37  };
38
39  const securityStatus = (form) => {
40    let status = form.querySelector(".form-security-status");
41    if (status) return status;
42    status = document.createElement("div");
43    status.className = "form-security-status";
44    status.setAttribute("role", "status");
45    status.setAttribute("aria-live", "polite");
46    status.hidden = true;
47    const submit = form.querySelector('[type="submit"]');
48    if (submit) submit.insertAdjacentElement("beforebegin", status);
49    else form.append(status);
50    return status;
51  };
52
53  const initializeTurnstile = async (form) => {
54    const query = new URLSearchParams({
55      path: window.location.pathname,
56      formName: form.dataset.name || "",
57      elementId: form.dataset.wfElementId || ""
58    });
59    const response = await fetch(`/api/forms/config?${query}`, { headers: { accept: "application/json" }, cache: "no-store" });
60    const result = await response.json().catch(() => ({}));
61    if (!response.ok || !result.turnstile?.configured || !result.turnstile.siteKey || !result.turnstile.action) {
62      throw new Error(result.error || "セキュリティ確認を利用できません。時間をおいて再度お試しください。");
63    }
64    await loadTurnstile();
65    const status = securityStatus(form);
66    const container = document.createElement("div");
67    container.className = "form-turnstile";
68    container.style.width = "100%";
69    const submit = form.querySelector('[type="submit"]');
70    if (submit) submit.insertAdjacentElement("beforebegin", container);
71    else form.append(container);
72    const state = { widgetId: null, resolve: null, reject: null };
73    state.widgetId = window.turnstile.render(container, {
74      sitekey: result.turnstile.siteKey,
75      action: result.turnstile.action,
76      execution: "execute",
77      appearance: "interaction-only",
78      size: "flexible",
79      callback: (token) => {
80        status.hidden = true;
81        status.textContent = "";
82        const resolve = state.resolve;
83        state.resolve = null;
84        state.reject = null;
85        if (resolve) resolve(token);
86      },
87      "expired-callback": () => {
88        status.hidden = false;
89        status.textContent = "セキュリティ確認の有効期限が切れました。もう一度確認してください。";
90        const reject = state.reject;
91        state.resolve = null;
92        state.reject = null;
93        if (reject) reject(new Error(status.textContent));
94      },
95      "error-callback": () => {
96        status.hidden = false;
97        status.textContent = "セキュリティ確認を完了できませんでした。もう一度お試しください。";
98        const reject = state.reject;
99        state.resolve = null;
100        state.reject = null;
101        if (reject) reject(new Error(status.textContent));
102      }
103    });
104    return state;
105  };
106
107  const turnstileToken = (state) => {
108    const ready = window.turnstile.getResponse(state.widgetId);
109    if (ready) return Promise.resolve(ready);
110    return new Promise((resolve, reject) => {
111      state.resolve = resolve;
112      state.reject = reject;
113      window.turnstile.execute(state.widgetId);
114    });
115  };
116
117  const prepare = (form) => {
118    if (form.dataset.cloudflareMigrated === "true") return;
119    form.dataset.cloudflareMigrated = "true";
120    const website = document.createElement("input");
121    website.type = "text";
122    website.name = "website";
123    website.tabIndex = -1;
124    website.autocomplete = "off";
125    website.setAttribute("aria-hidden", "true");
126    website.style.cssText = "position:absolute;left:-10000px;width:1px;height:1px;overflow:hidden";
127    form.append(website);
128    const started = document.createElement("input");
129    started.type = "hidden";
130    started.name = "_started_at";
131    started.value = String(Date.now());
132    form.append(started);
133    const formName = document.createElement("input");
134    formName.type = "hidden";
135    formName.name = "_form_name";
136    formName.value = form.dataset.name || "";
137    form.append(formName);
138    const elementId = document.createElement("input");
139    elementId.type = "hidden";
140    elementId.name = "_form_element_id";
141    elementId.value = form.dataset.wfElementId || "";
142    form.append(elementId);
143    securityStatus(form);
144    formSecurity.set(form, initializeTurnstile(form)
145      .then((state) => ({ state }))
146      .catch((error) => ({ error })));
147  };
148
149  const semanticFormData = (form) => {
150    const data = new FormData();
151    for (const field of form.elements) {
152      if (!(field instanceof HTMLInputElement || field instanceof HTMLTextAreaElement || field instanceof HTMLSelectElement)) continue;
153      if (!field.name || field.disabled || field.type === "submit" || field.type === "button") continue;
154      if ((field.type === "radio" || field.type === "checkbox") && !field.checked) continue;
155      const name = field.dataset.name || field.name;
156      if (field instanceof HTMLInputElement && field.type === "file") {
157        for (const file of field.files || []) data.append(name, file);
158      } else if (field instanceof HTMLSelectElement && field.multiple) {
159        for (const option of field.selectedOptions) data.append(name, option.value);
160      } else data.append(name, field.value);
161    }
162    return data;
163  };
164
165  document.addEventListener("DOMContentLoaded", () => {
166    document.querySelectorAll(".w-form form").forEach(prepare);
167  });
168
169  document.addEventListener("submit", async (event) => {
170    const form = event.target;
171    if (!(form instanceof HTMLFormElement) || !form.closest(".w-form")) return;
172    event.preventDefault();
173    event.stopImmediatePropagation();
174    prepare(form);
175    if (form.dataset.cloudflarePending === "true") return;
176    form.dataset.cloudflarePending = "true";
177    form.setAttribute("aria-busy", "true");
178    const submitControls = [...form.querySelectorAll('[type="submit"]')];
179    const disabledBefore = submitControls.map((control) => control.disabled);
180    submitControls.forEach((control) => { control.disabled = true; });
181    let state;
182    let succeeded = false;
183    try {
184      const security = await formSecurity.get(form);
185      if (security.error) throw security.error;
186      state = security.state;
187      const token = await turnstileToken(state);
188      const body = semanticFormData(form);
189      body.set("cf-turnstile-response", token);
190      const response = await fetch("/api/forms/submit", { method: "POST", body, headers: { accept: "application/json" } });
191      const result = await response.json().catch(() => ({}));
192      if (!response.ok) {
193        const retryAfter = Number(result.retryAfterSeconds || response.headers.get("retry-after") || 0);
194        const guidance = response.status === 429 && retryAfter > 0 ? `(約${Math.max(1, Math.ceil(retryAfter / 60))}分後に再度お試しください。)` : "";
195        throw new Error(`${result.error || "送信に失敗しました。時間をおいて再度お試しください。"}${guidance}`);
196      }
197      succeeded = true;
198      showState(form, true);
199    } catch (error) {
200      showState(form, false, error instanceof Error ? error.message : "送信に失敗しました。");
201      if (state?.widgetId != null && window.turnstile) window.turnstile.reset(state.widgetId);
202    } finally {
203      if (!succeeded) {
204        delete form.dataset.cloudflarePending;
205        form.removeAttribute("aria-busy");
206        submitControls.forEach((control, index) => { control.disabled = disabledBefore[index]; });
207      }
208    }
209  }, true);
210})();

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.