let post_data = {}; let workatoProcessed = false; // window.locationの元の機能を保存 window.originalLocation = { href: window.location.href, origin: window.location.origin, host: window.location.host, protocol: window.location.protocol, replace: window.location.replace.bind(window.location), assign: window.location.assign.bind(window.location) }; // トークンを取得する window.tokenManager = { accessToken: null, fetchAccessToken: async function () { const tokenApiUrl = 'https://safie.jp/_hcms/api/workato-get-token'; try { const res = await fetch(tokenApiUrl, { method: 'GET', headers: { 'Content-Type': 'application/json', }, }); if (!res.ok) throw new Error('Token fetch failed with status ' + res.status); const data = await res.json(); if (!data.access_token) throw new Error('No access_token in response'); this.accessToken = data.access_token; console.log('取得したaccess_token:', this.accessToken); return this.accessToken; } catch (err) { console.error('access_token取得エラー:', err); throw err; } } }; // JWT トークンのデコード関数 const decodeJWT = (token) => { try { const parts = token.split('.'); if (parts.length !== 3) return null; const payload = JSON.parse(atob(parts[1].replace(/-/g, '+').replace(/_/g, '/'))); return payload; } catch (e) { console.error('JWT デコードエラー:', e); return null; } }; // トークンの詳細分析 const analyzeToken = (token) => { const decoded = decodeJWT(token); if (!decoded) return null; const now = Math.floor(Date.now() / 1000); const isExpired = decoded.exp && decoded.exp < now; const timeToExpiry = decoded.exp ? decoded.exp - now : null; return { decoded, isExpired, timeToExpiry, expiryDate: decoded.exp ? new Date(decoded.exp * 1000) : null }; }; // XMLHttpRequest fallback関数 const sendWithXHR = (endpoint, payload, token) => { return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); xhr.open('POST', endpoint, true); xhr.setRequestHeader('Content-Type', 'application/json'); xhr.setRequestHeader('Authorization', 'Bearer ' + token); xhr.onreadystatechange = function() { if (xhr.readyState === 4) { console.log('📡 XHR レスポンス:', { status: xhr.status, statusText: xhr.statusText, responseText: xhr.responseText.substring(0, 200) + '...' }); if (xhr.status >= 200 && xhr.status < 300) { resolve({ success: true, response: xhr.responseText, status: xhr.status }); } else { resolve({ success: false, response: xhr.responseText, status: xhr.status }); } } }; xhr.onerror = function() { console.error('❌ XHR ネットワークエラー'); reject(new Error('XHR network error')); }; xhr.ontimeout = function() { console.error('❌ XHR タイムアウト'); reject(new Error('XHR timeout')); }; xhr.timeout = 45000; // 45秒タイムアウト try { xhr.send(JSON.stringify(payload)); console.log('📤 XHR リクエスト送信完了'); } catch (e) { console.error('❌ XHR 送信エラー:', e); reject(e); } }); }; // エンドポイントの接続テスト関数 const testEndpointConnection = async (endpoint) => { try { console.log('🔍 エンドポイント接続テスト開始:', endpoint); // OPTIONSリクエストでCORSをテスト const optionsResponse = await fetch(endpoint, { method: 'OPTIONS', headers: { 'Origin': window.originalLocation.origin, 'Access-Control-Request-Method': 'POST', 'Access-Control-Request-Headers': 'Content-Type, Authorization' } }); console.log('✅ OPTIONS レスポンス:', { status: optionsResponse.status, headers: Object.fromEntries(optionsResponse.headers.entries()) }); return true; } catch (error) { console.error('❌ エンドポイント接続テスト失敗:', error); return false; } }; // タイムアウト付きAPI呼び出し関数 const sendWithTimeout = async (endpoint, payload, timeoutMs = 30000) => { const controller = new AbortController(); // AbortController の状態監視 controller.signal.addEventListener('abort', () => { console.log('AbortController によりリクエストが中断されました'); console.log('中断理由:', controller.signal.reason || 'タイムアウト'); }); const timeoutId = setTimeout(() => { console.log(' タイムアウトでリクエストをキャンセル'); controller.abort('timeout'); }, timeoutMs); try { console.log(`API呼び出し開始 (タイムアウト: ${timeoutMs}ms)`); const headers = { "Content-Type": "application/json", "Authorization": "Bearer " + window.tokenManager.accessToken, }; console.log('送信ヘッダー:', { 'Content-Type': headers['Content-Type'], 'Authorization': 'Bearer ' + window.tokenManager.accessToken.substring(0, 20) + '...', 'Origin': window.originalLocation.origin, 'Referer': window.originalLocation.href }); console.log('fetch() 実行直前...'); console.log('リクエスト詳細:', { url: endpoint, method: 'POST', bodyLength: JSON.stringify(payload).length, timestamp: new Date().toISOString() }); const res = await fetch(endpoint, { method: "POST", headers: headers, body: JSON.stringify(payload), signal: controller.signal, // AbortControllerを設定 mode: 'cors', // CORS設定を明示 credentials: 'omit' // 認証情報は送信しない }); console.log('fetch() 実行完了:', { status: res.status, statusText: res.statusText, timestamp: new Date().toISOString() }); clearTimeout(timeoutId); const responseText = await res.text(); console.log('API呼び出し完了:', { status: res.status, statusText: res.statusText, responseLength: responseText.length, response: responseText.substring(0, 200) + (responseText.length > 200 ? '...' : '') }); return { success: res.ok, response: responseText, status: res.status }; } catch (error) { clearTimeout(timeoutId); console.error('sendWithTimeout エラー詳細:', { name: error.name, message: error.message, stack: error.stack, toString: error.toString() }); if (error.name === 'AbortError') { console.error('リクエストがキャンセルされました (タイムアウトまたは中断)'); throw new Error('リクエストがタイムアウトしました'); } else if (error.name === 'TypeError' && error.message.includes('fetch')) { console.error('Fetch API エラー - 詳細調査'); console.error(' エラー発生時の環境情報:', { endpoint: endpoint, online: navigator.onLine, protocol: window.originalLocation.protocol, host: window.originalLocation.host, cors: 'fetch mode: cors, credentials: omit' }); // XHRでのfallback試行 console.log('XMLHttpRequest でリトライを試行...'); try { const xhrResult = await sendWithXHR(endpoint, payload, window.tokenManager.accessToken); console.log('XHR fallback 成功'); return xhrResult; } catch (xhrError) { console.error('XHR fallback も失敗:', xhrError); throw new Error('ネットワーク接続エラーが発生しました (Fetch & XHR 両方失敗)'); } } else { console.error('その他のネットワークエラー:', error); throw error; } } }; // エンドポイント別タイムアウト設定 const getTimeoutForEndpoint = (selected) => { const timeouts = { 'prospect': 60000, // 60秒 'case': 60000, // 60秒 'inquirymail': 60000 // 60秒 }; return timeouts[selected] || 30000; }; const buttonController = { disable: function($button) { const originalText = $button.val() || $button.text(); $button.data('original-text', originalText); $button.prop('disabled', true); $button.css({ 'background-color': '#cccccc', 'color': '#666666', 'cursor': 'not-allowed', 'opacity': '0.6' }); const sendingText = window.hsFormText && window.hsFormText.sending ? window.hsFormText.sending : '送信中...'; $button.val(sendingText).text(sendingText); }, enable: function($button) { let originalText = $button.data('original-text'); if (!originalText) { originalText = window.hsFormText && window.hsFormText.send ? window.hsFormText.send : '送信'; } $button.prop('disabled', false); $button.css({ 'background-color': '', 'color': '', 'cursor': '', 'opacity': '' }); $button.val(originalText).text(originalText); $button.removeData('original-text'); } }; // フォーム処理を別関数に分離 async function processFormImproved($form) { console.log("🚀 独立フォーム処理開始", $form); const $submitButton = $form.find('input[type="submit"], button[type="submit"]'); const gtmCheckVal = $form.find('input[name="gtm_check"]').val(); console.log("gtm_check の値 (val):", gtmCheckVal); if (gtmCheckVal && gtmCheckVal.toLowerCase() === "true") { post_data["gtm_inquirynumber"] = "null"; let inquirynumber = getCookie('inquiryNumber'); if (inquirynumber != null) { post_data["gtm_inquirynumber"] = inquirynumber; $('input[name="gtm_inquirynumber"]').val(inquirynumber); } console.log("postdata に設定された gtm_inquirynumber:", post_data["gtm_inquirynumber"]); } else { console.log("gtm_check は 'true' ではありません。現在の値1158:", gtmCheckVal); } try { const formData = new FormData($form.get(0)); const workatoCheck = formData.get("workato_check"); if (workatoCheck !== "true") { console.log("workato_check チェックなし → Workato連携スキップ"); buttonController.enable($submitButton); return; } console.log("workato_check チェックあり → Workato連携開始"); const selected = formData.get("test_endpoint"); const baseURL = "https://apim.jp.workato.com/safie/cc/v1/"; const endpointMap = { case: "case", prospect: "prospect", inquirymail: "inquirymail", }; const path = endpointMap[selected]; if (!path) { alert("送信先が選択されていません"); buttonController.enable($submitButton); // ⭐️追加 // showDebugStatus("送信先未選択", '#dc3545'); return; } const endpoint = baseURL + path; // showDebugStatus(`エンドポイント: ${selected}`, '#17a2b8'); // トークン確認 try { // showDebugStatus("トークン確認中...", '#ffc107'); if (!window.tokenManager.accessToken) { console.log("fetchAccessToken を呼び出します"); await window.tokenManager.fetchAccessToken(); console.log("fetchAccessToken 呼び出し完了"); } // トークンの詳細分析 const tokenAnalysis = analyzeToken(window.tokenManager.accessToken); console.log("🔐 トークン詳細分析:", { hasToken: !!window.tokenManager.accessToken, tokenLength: window.tokenManager.accessToken?.length, isExpired: tokenAnalysis?.isExpired, timeToExpiry: tokenAnalysis?.timeToExpiry + ' seconds', expiryDate: tokenAnalysis?.expiryDate?.toISOString(), decodedPayload: tokenAnalysis?.decoded }); if (tokenAnalysis?.isExpired) { console.warn("トークンが期限切れです。再取得します..."); // showDebugStatus("⚠️ トークン期限切れ - 再取得中", '#ffc107'); await window.tokenManager.fetchAccessToken(); console.log("トークン再取得完了"); } console.log("トークン確認完了"); // showDebugStatus("✅ トークン確認完了", '#28a745'); } catch (e) { console.error('トークン取得エラー:', e); buttonController.enable($submitButton); // ⭐️追加 // showDebugStatus("トークン取得エラー", '#dc3545'); // alert('認証エラーが発生しました。再度お試しください。'); return; } let rawPayload = {}; // 同じキーが複数ある場合は値をセミコロンで連結して保持 for (const [key, value] of formData.entries()) { if (Object.prototype.hasOwnProperty.call(rawPayload, key)) { rawPayload[key] = rawPayload[key] + ';' + value; } else { rawPayload[key] = value; } } const isCaseEndpoint = selected === "case"; delete rawPayload.test_endpoint; delete rawPayload.workato_check; delete rawPayload.gtm_check; const testPayload = isCaseEndpoint ? { "device_id": "", "model": { "model_id": "", "description": "" }, "payment_id": 0, "old_plan_info": { "item_id": 0, "description": "" }, "cvr_length": "", "new_plan_start_date": "", "remarks": "", "organization_name": "", "name": "", "phone_number": "", "group": { "group_id": "", "name": "" }, "category": "", "cancellation_payment": [ { "item_type": "", "item_id": "", "payment_id": "", "description": "" } ], "cancellation_date": "", "cancellation_reason": "", "mail_address": "", "device_name": "", "owner": "", "model_id": "", "firmware": "", "supportshare": "", "share_permission": "", "os_browser": "" } : {}; const payload = { ...(isCaseEndpoint ? testPayload : {}), ...rawPayload, ...post_data }; console.log("送信データ詳細:", { endpoint: endpoint, selected: selected, payloadSize: JSON.stringify(payload).length, payloadKeys: Object.keys(payload), tokenPresent: !!window.tokenManager.accessToken }); // エンドポイント別タイムアウト設定でAPI呼び出し const timeout = getTimeoutForEndpoint(selected); console.log(` ${selected}エンドポイント用タイムアウト: ${timeout}ms`); console.log('🔧 API呼び出し前の最終確認:', { endpoint: endpoint, method: 'POST', hasToken: !!window.tokenManager.accessToken, tokenLength: window.tokenManager.accessToken ? window.tokenManager.accessToken.length : 0, payloadSize: JSON.stringify(payload).length, timeout: timeout, origin: window.originalLocation.origin, userAgent: navigator.userAgent.substring(0, 50) + '...' }); console.log('API呼び出し開始'); const result = await sendWithTimeout(endpoint, payload, timeout); if (result.success) { console.log('Workato API 呼び出し成功'); const productInput = $form.find("input[name='product']"); if (productInput.length > 0) { let productValue = null; productInput.each(function () { if (this.checked) { productValue = $(this).val(); return false; } }); if (productValue) { const customRedirectUrl = `${form_redirect_url}?product=${encodeURIComponent(productValue)}`; console.log("カスタムリダイレクトURL準備:", customRedirectUrl); localStorage.setItem("hs_my_redirect_url", customRedirectUrl); // HubSpotのリダイレクト無効化(popstate/hashchangeイベントもブロック) window.hsRedirectBlocked = true; // History APIも無効化 const originalPushState = history.pushState; const originalReplaceState = history.replaceState; history.pushState = function() { if (window.hsRedirectBlocked) { console.log('history.pushState ブロック'); return; } return originalPushState.apply(this, arguments); }; history.replaceState = function() { if (window.hsRedirectBlocked) { console.log('history.replaceState ブロック'); return; } return originalReplaceState.apply(this, arguments); }; // location系メソッドの無効化 if (!window.location._safieOverridden) { const originalReplace = window.location.replace; const originalAssign = window.location.assign; window.location.replace = function(url) { if (window.hsRedirectBlocked) { console.log('location.replace ブロック:', url); return; } return originalReplace.call(this, url); }; window.location.assign = function(url) { if (window.hsRedirectBlocked) { console.log('location.assign ブロック:', url); return; } return originalAssign.call(this, url); }; window.location._safieOverridden = true; } // HubSpotのリダイレクト用DOMの変更も監視 const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { if (window.hsRedirectBlocked && mutation.type === 'childList') { mutation.addedNodes.forEach((node) => { if (node.tagName === 'META' && node.getAttribute('http-equiv') === 'refresh') { console.log('META refreshタグを削除'); node.remove(); } }); } }); }); observer.observe(document.head, { childList: true, subtree: true }); } } /// ★★★ fetchで確実にHubSpot登録してからリダイレクト ★★★ let customRedirectUrl = localStorage.getItem('hs_my_redirect_url'); const formElement = $form.get(0); const hubspotFormData = new FormData(formElement); const formAction = $form.attr('action'); console.log('📤 HubSpotにデータ送信開始(fetch使用)'); const hubspotTimeoutId = setTimeout(() => { console.log('⏱️ タイムアウト - 強制リダイレクト'); if (customRedirectUrl) { localStorage.removeItem('hs_my_redirect_url'); window.location.href = customRedirectUrl; } else if (typeof form_redirect_url !== 'undefined' && form_redirect_url) { window.location.href = form_redirect_url; } else { workatoProcessed = false; $form.get(0).submit(); } }, 10000); fetch(formAction, { method: 'POST', body: hubspotFormData, keepalive: true, }) .then((response) => { clearTimeout(hubspotTimeoutId); console.log('✅ HubSpot登録完了:', response.status); return response.text().then((text) => { console.log('📄 応答:', text.substring(0, 100) + '...'); return response; }); }) .then((response) => { if (customRedirectUrl) { localStorage.removeItem('hs_my_redirect_url'); console.log('🔄 カスタムリダイレクト実行:', customRedirectUrl); window.location.href = customRedirectUrl; } else if (typeof form_redirect_url !== 'undefined' && form_redirect_url) { console.log('🔄 デフォルトリダイレクト実行'); window.location.href = form_redirect_url; } else { workatoProcessed = false; $form.get(0).submit(); } }) .catch((error) => { clearTimeout(hubspotTimeoutId); console.error('❌ エラー:', error); if (customRedirectUrl) { localStorage.removeItem('hs_my_redirect_url'); window.location.href = customRedirectUrl; } else if (typeof form_redirect_url !== 'undefined' && form_redirect_url) { window.location.href = form_redirect_url; } else { workatoProcessed = false; $form.get(0).submit(); } }); /// ★★★ fetchで確実にHubSpot登録してからリダイレクト ★★★ } else { console.error('API呼び出し失敗:', { constructor: error.constructor.name, status: result.status, response: result.response, endpoint: endpoint, }); buttonController.enable($submitButton); if (result.status === 401) { console.error('🔐 認証エラー: トークンが無効または権限不足'); alert(`認証エラーが発生しました。\n${selected}エンドポイントへのアクセス権限がない可能性があります。\n管理者にお問い合わせください。`); } else if (result.status === 403) { console.error('🚫 権限エラー: アクセス禁止'); alert(`アクセス権限がありません。\n管理者にお問い合わせください。`); } else { alert(`送信に失敗しました (エラーコード: ${result.status})\n詳細はコンソールをご確認ください。`); } } } catch (error) { console.error('処理エラー詳細:', { constructor: error.constructor.name, name: error.name, message: error.message, stack: error.stack, }); buttonController.enable($submitButton); if (error.message && error.message.includes('タイムアウト')) { alert('送信処理がタイムアウトしました。ネットワーク環境をご確認の上、再度お試しください。'); } else if (error.message && error.message.includes('ネットワーク接続エラー')) { alert('ネットワーク接続エラーが発生しました。インターネット接続をご確認ください。'); } else { alert('送信中にエラーが発生しました。\nエラー詳細: ' + (error.message || error.toString())); } } } window.cmnFormOptions = { locale: document.getElementsByTagName('html')[0].getAttribute('lang') || 'ja', onFormReady: async function ($form) { console.log("=== onFormReady ==="); console.log('$form', $form) console.log('redirectUrl', this.redirectUrl) try { if (!window.tokenManager.accessToken) { console.log("fetchAccessToken を呼び出します"); await window.tokenManager.fetchAccessToken(); console.log("fetchAccessToken 呼び出し完了"); } console.log("トークン取得成功:", window.tokenManager.accessToken); // HubSpotのデフォルトバリデーション機能をすべて使用 const validateForm = (formElement) => { const requiredFields = formElement.querySelectorAll( 'input[required], select[required], textarea[required], .hs-form-field:has(.hs-form-required) input:not([type="hidden"]), .hs-form-required input:not([type="hidden"]), .hs-input[data-required="true"], .hs-form-required select, .hs-form-required input, .hs-form-required textarea' ); // ラジオボタンのname属性を記録(重複チェック用) const checkedRadioGroups = new Set(); for (let field of requiredFields) { let isEmpty = false; let targetField = field; // エラー表示対象のフィールド // ラジオボタンの場合 if (field.type === 'radio') { const radioName = field.name; // 既にチェック済みのグループはスキップ if (checkedRadioGroups.has(radioName)) { continue; } checkedRadioGroups.add(radioName); // 同じname属性のラジオボタンでcheckedがあるか確認 const radioGroup = formElement.querySelectorAll( `input[type="radio"][name="${radioName}"]` ); isEmpty = ![...radioGroup].some((radio) => radio.checked); radioGroup.forEach((radio) => { if (radio.checked) { radio.setAttribute('checked', 'checked'); } else { radio.removeAttribute('checked'); } }); // ラジオボタンの場合、最初の要素にフォーカス targetField = radioGroup[0]; if (isEmpty) { // 各ラジオボタンにフォーカス→ブラーを実行してエラーを表示 radioGroup.forEach((radio) => { radio.focus(); radio.blur(); }); } } // チェックボックスの場合 else if (field.type === 'checkbox') { isEmpty = !field.checked; if (field.checked) { field.setAttribute('checked', 'checked'); } else { field.removeAttribute('checked'); } } // セレクトボックスの場合 else if (field.tagName === 'SELECT') { isEmpty = !field.value || field.value === '' || field.selectedIndex === 0; } // テキスト入力の場合 else { isEmpty = !field.value || field.value.trim() === ''; } if (isEmpty) { console.log( '❌ 必須フィールドが未入力:', field.name || field.id, `(type: ${field.type || field.tagName})` ); // HubSpotのエラー表示をトリガー targetField.focus(); targetField.blur(); // ★ラジオボタンの場合は少し待ってから再度トリガー if (field.type === 'radio') { setTimeout(() => { targetField.focus(); targetField.blur(); }, 50); } // 親要素にエラークラスを追加(HubSpotの標準的な方法) const fieldParent = targetField.closest('.hs-form-field') || targetField.closest('.field'); if (fieldParent) { fieldParent.classList.add('error'); fieldParent.classList.add('invalid'); const errorMsg = fieldParent.querySelector('.hs-error-msg'); if (errorMsg) { errorMsg.style.display = 'block'; } } return false; } else { // エラーがない場合はエラー表示をクリア const fieldParent = targetField.closest('.hs-form-field') || targetField.closest('.field'); if (fieldParent) { fieldParent.classList.remove('error'); fieldParent.classList.remove('invalid'); } } } // チェックボックス、ラジオボタン、非表示項目以外のすべての入力項目にてfocusoutイベントをトリガーし、HubSpotのバリデーションを即座に実行 const inputFields = formElement.querySelectorAll( 'input:not([type="checkbox"]):not([type="radio"]):not([type="hidden"]), select, textarea' ); inputFields.forEach((input) => { const event = new Event('focusout', { bubbles: true }); input.dispatchEvent(event); }); // HubSpotのバリデーションエラーがある入力項目が存在する場合、最初のエラー項目にフォーカスして送信をブロック const errorFields = formElement.querySelectorAll( 'input.invalid, input.error, textarea.invalid, textarea.error, select.invalid, select.error' ); if (errorFields.length > 0) { errorFields[0].focus(); console.log('❌ HubSpotの入力チェックエラーを検出'); return false; } // メールアドレス項目の形式チェック完了まで遅延可能性があるため、ダブルチェックを実施 const emailFields = formElement.querySelectorAll( 'input[type="email"]' ); for (let emailField of emailFields) { if (emailField.value) { // Workatoと同じ正規表現でメールアドレス形式チェックを実施 const emailPattern = /^[a-zA-Z0-9!#$%&'*+\-/=?^_`{|}~.]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*\.[a-zA-Z]{2,}$/; // メールアドレス形式が合致しない場合はフォーム送信をブロック if (!emailPattern.test(emailField.value)) { console.log( '❌ メールアドレス形式エラー:', emailField.name || emailField.id, `(${emailField.value})` ); alert('メールアドレスの形式が正しくありません。'); return false; } // ドメインまたはサブドメインにACEプレフィックスが含まれる場合は国際化ドメインとみなして拒否 const acePrefixPattern = /[@\.]xn--/; const fqdn = emailField.value.substring(emailField.value.lastIndexOf('@')); if (acePrefixPattern.test(fqdn)) { console.log( '❌ 国際化ドメインエラー:', emailField.name || emailField.id, `(${emailField.value})` ); alert('メールアドレスの形式が正しくありません。'); return false; } // Chromeでは検証時に前後の空白が除去されてしまっているため、検証時に利用した値で上書き emailField.value = emailField.value; } } return true; }; const formElement = $form.get(0); const radioButtons = formElement.querySelectorAll('input[type="radio"]'); radioButtons.forEach((radio) => { radio.addEventListener('change', function () { const groupName = this.name; formElement.querySelectorAll(`input[type="radio"][name="${groupName}"]`).forEach((r) => { r.removeAttribute('checked'); }); if (this.checked) { this.setAttribute('checked', 'checked'); } }); }); // チェックボックス用も追加 const checkboxes = formElement.querySelectorAll('input[type="checkbox"]'); checkboxes.forEach((checkbox) => { checkbox.addEventListener('change', function () { if (this.checked) { this.setAttribute('checked', 'checked'); } else { this.removeAttribute('checked'); } }); }); if (formElement) { // すべてのsubmitイベントを強制的に防ぐ formElement.addEventListener( "submit", function (e) { console.log("フォーム送信をブロックしました"); }, true ); // submitボタンのclickイベントも防ぐ const submitButtons = formElement.querySelectorAll( 'input[type="submit"], button[type="submit"]' ); submitButtons.forEach((button) => { button.addEventListener( "click", function (e) { console.log('workatoProcessedの状態:', workatoProcessed); // カスタム処理を実行 if (!workatoProcessed) { formElement.querySelectorAll('input[type="radio"]').forEach(radio => { if (radio.checked) { radio.setAttribute('checked', 'checked'); console.log('ラジオボタンチェック確認:', radio.name, '=', radio.value); } else { radio.removeAttribute('checked'); } }); e.preventDefault(); e.stopPropagation(); e.stopImmediatePropagation(); // ★ まず最初にフォームバリデーションをチェック if (!validateForm(formElement)) { console.log("❌ フォームバリデーションエラー - 処理を中止"); return false; // バリデーションエラーの場合は何もしない } const $button = $(this); buttonController.disable($button); console.log("=== Workato処理を先に実行 ==="); // Workato処理を非同期実行 processFormImproved($form).then(() => { workatoProcessed = true; console.log("✅ Workato処理完了 - フォーム再送信"); // 処理完了後、フォームを再送信(今度はWorkato処理をスキップ) $form.find('input[type="submit"]').click(); }); console.log("=== 処理完了 ==="); return false; // 初回送信を阻止 } console.log("=== 2回目の実行 ==="); return true; // 2回目は実行する }, true ); }); } } catch (e) { console.error(' ❌ onFormReady トークン取得エラー:', e); const $submitButton = $form.find('input[type="submit"], button[type="submit"]'); buttonController.disable($submitButton); } }, onFormSubmitted: async function ($form) { const customRedirectUrl = localStorage.getItem("hs_my_redirect_url"); if (customRedirectUrl) { localStorage.removeItem("hs_my_redirect_url"); console.log('🔄 カスタムリダイレクト実行:', customRedirectUrl); // ブロックを解除してリダイレクト実行 window.hsRedirectBlocked = false; setTimeout(() => { window.originalLocation.assign(customRedirectUrl); }, 100); return false; } } };