/** * Admin JavaScript. * * @package contact-form-7-mailchimp-extension * @author renzo.johnson@gmail.com * @copyright 2014-2026 https://renzojohnson.com * @license GPL-3.0+ */ /** * Fix CF7's beforeunload detection for dynamically populated selects. * * CF7 compares defaultValue vs value to detect changes. PHP-generated selects * with selected="selected" have undefined defaultValue causing false positives. * This syncs defaultValue to current value on page load. */ document.addEventListener('DOMContentLoaded', function() { const form = document.getElementById('wpcf7-admin-form-element'); if (!form) return; // Sync select defaultValue to prevent false "unsaved changes" warnings form.querySelectorAll('select').forEach(function(select) { if (select.value && select.defaultValue !== select.value) { // Set defaultValue for select (affects selectedIndex tracking) Array.from(select.options).forEach(function(opt) { opt.defaultSelected = opt.selected; }); } }); // Sync checkbox values (browsers set value="on" but defaultValue="") form.querySelectorAll('input[type="checkbox"]').forEach(function(cb) { if (cb.value && !cb.defaultValue) { cb.defaultValue = cb.value; } }); }); function getFormId() { const dataContainer = document.getElementById('cmatic_data'); if (dataContainer && dataContainer.dataset.formId) { return parseInt(dataContainer.dataset.formId, 10) || 0; } return 0; } function getApiValid() { const dataContainer = document.getElementById('cmatic_data'); return dataContainer?.dataset?.apiValid || '0'; } /** * Show inline status message near an element (non-invasive alternative to alert). * * @param {HTMLElement} targetElement Element to show message near. * @param {string} message Message text. * @param {string} type 'error', 'warning', or 'success'. * @param {number} duration Auto-hide after ms (0 = manual close). */ function showInlineMessage(targetElement, message, type = 'warning', duration = 5000) { // Remove any existing message const existingMsg = targetElement.parentNode.querySelector('.cmatic-inline-msg'); if (existingMsg) existingMsg.remove(); const msg = document.createElement('span'); msg.className = `cmatic-inline-msg cmatic-msg-${type}`; msg.textContent = message; msg.style.cssText = ` display: inline-block; margin-left: 10px; padding: 4px 10px; border-radius: 3px; font-size: 13px; animation: cmatic-fade-in 0.3s ease; `; if (type === 'error') { msg.style.background = '#f8d7da'; msg.style.color = '#721c24'; msg.style.border = '1px solid #f5c6cb'; } else if (type === 'warning') { msg.style.background = '#fff3cd'; msg.style.color = '#856404'; msg.style.border = '1px solid #ffeeba'; } else { msg.style.background = '#d4edda'; msg.style.color = '#155724'; msg.style.border = '1px solid #c3e6cb'; } targetElement.parentNode.insertBefore(msg, targetElement.nextSibling); if (duration > 0) { setTimeout(() => { msg.style.opacity = '0'; msg.style.transition = 'opacity 0.3s ease'; setTimeout(() => msg.remove(), 300); }, duration); } } /** * Securely get the API key - fetches from REST endpoint if masked. * CVE-2025-68989 fix: API key no longer stored in data-real-key attribute. * @returns {Promise} The API key. */ async function getApiKey() { const apiInput = document.getElementById('cmatic-api'); if (!apiInput) return ''; const isMasked = apiInput.dataset.isMasked === '1'; const hasKey = apiInput.dataset.hasKey === '1'; const inputValue = apiInput.value.trim(); // If not masked, the input contains the real key (user just typed/pasted it). if (!isMasked) { return inputValue; } // If masked but no key exists in DB, return empty. if (!hasKey) { return ''; } // Masked with existing key - fetch the real key from secure endpoint. const formId = getFormId(); if (!formId) return ''; try { // Use Lite endpoint (works for both Lite and PRO). const restUrl = typeof chimpmaticLite !== 'undefined' ? chimpmaticLite.restUrl : getRestUrl().replace('chimpmatic/v1/', 'chimpmatic-lite/v1/'); const nonce = typeof chimpmaticLite !== 'undefined' ? chimpmaticLite.restNonce : (typeof wpApiSettings !== 'undefined' ? wpApiSettings.nonce : ''); const response = await fetch( `${restUrl}api-key/${formId}`, { method: 'GET', headers: { 'X-WP-Nonce': nonce, 'Content-Type': 'application/json' } } ); if (!response.ok) { console.error('ChimpMatic: Failed to fetch API key'); return ''; } const data = await response.json(); return data.api_key || ''; } catch (err) { console.error('ChimpMatic: Error fetching API key', err); return ''; } } function setApiValid(value) { const dataContainer = document.getElementById('cmatic_data'); if (dataContainer) { dataContainer.dataset.apiValid = value ? '1' : '0'; } } function getRestUrl() { if (typeof wpApiSettings !== 'undefined' && wpApiSettings.root) { return wpApiSettings.root + 'chimpmatic/v1/'; } return '/wp-json/chimpmatic/v1/'; } const REST_ENDPOINTS = { SETTINGS_SAVE: 'settings/save', SETTINGS_CONFIG: 'settings/config', AUDIENCES: 'mailchimp/audiences', FIELDS: 'mailchimp/fields', GROUPS: 'mailchimp/groups', INTERESTS: 'mailchimp/interests', EXPORT_USERS: 'export/users', NOTICES_DISMISS: 'notices/dismiss', TELEMETRY_TOGGLE: 'telemetry/toggle' }; const actionToEndpoint = { 'wpcf7_chm_savetool': 'settings/save', 'wpcf7_chm_savetool_cfg': 'settings/config', 'wpcf7_chm_loadlistas': 'mailchimp/audiences', 'wpcf7_chm_loadcampos': 'mailchimp/fields', 'wpcf7_chm_loadgrupos': 'mailchimp/groups', 'wpcf7_chm_get_interest': 'mailchimp/interests', 'wpcf7_chm_exporuser': 'export/users' }; async function chmRequest(actionOrEndpoint, data = {}) { const endpoint = actionToEndpoint[actionOrEndpoint] || actionOrEndpoint; return await chmRestRequest(endpoint, data); } async function chmRestRequest(endpoint, data = {}) { const url = getRestUrl() + endpoint; try { const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': wpApiSettings?.nonce || '' }, body: JSON.stringify(data), credentials: 'same-origin' }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const result = await response.json(); return result.html || result.data || ''; } catch (error) { console.error('[Chimpmatic] Error:', error); throw error; } } // Save Tool Configuration document.addEventListener('click', function(event) { if (event.target && event.target.id === 'chm_submitme') { event.preventDefault(); const data = { tool_key: document.getElementById('wpcf7-mailchimp-tool_key')?.value || '', chm_idformxx: getFormId(), uemail: document.getElementById('wpcf7-mailchimp-uemail')?.value || '', prod_id: document.getElementById('wpcf7-mailchimp-prod-id')?.value || '' }; chmRequest('wpcf7_chm_savetool', data) .then(response => { const panel = document.getElementById('chm_panel_principal'); if (panel) panel.innerHTML = response; }) .catch(error => { alert('Error: ' + error.message); }); } }); // Save Tool Config document.addEventListener('click', function(event) { if (event.target && event.target.id === 'chm_submitme_cfg') { event.preventDefault(); const data = { tool_key: document.getElementById('wpcf7-mailchimp-tool_key')?.value || '', uemail: document.getElementById('wpcf7-mailchimp-uemail')?.value || '', prod_id: document.getElementById('wpcf7-mailchimp-prod-id')?.value || '' }; chmRequest('wpcf7_chm_savetool_cfg', data) .then(response => { const liveElements = document.querySelectorAll('.chimp-live'); liveElements.forEach(el => el.innerHTML = response); }) .catch(error => { alert('Error: ' + error.message); }); } }); // Connect and Fetch Your Audiences (Load Lists) document.addEventListener('click', function(event) { if (event.target && event.target.id === 'chm_activalist') { event.preventDefault(); const button = event.target; if (button.disabled) return; const panel = button.closest('.cmatic-field-group') || button.closest('.mce-custom-fields'); let apiKeyElement = panel ? panel.querySelector('#cmatic-api') : null; if (!apiKeyElement) { apiKeyElement = document.getElementById('cmatic-api'); } const apiKey = apiKeyElement?.value || ''; if (!apiKey || apiKey.trim() === '') { showInlineMessage(button, 'Enter API key first', 'warning'); return; } const formId = getFormId(); if (!formId || formId <= 0) { showInlineMessage(button, 'Save form first', 'warning'); return; } const data = { tool_key: document.getElementById('wpcf7-mailchimp-tool_key')?.value || '', chm_idformxx: formId, chimpapi: apiKey }; const originalText = button.value; button.value = 'Syncing...'; button.disabled = true; chmRequest('wpcf7_chm_loadlistas', data) .then(response => { const listPanel = document.getElementById('chm_panel_listamail'); if (listPanel) { listPanel.innerHTML = response; } const valor = getApiValid(); let attrclass = ''; let chm_valid = ''; if (valor === '1') { attrclass = 'spt-response-out spt-valid'; chm_valid = '

ChimpmaticAPI Connected

'; button.value = 'Synced'; button.disabled = false; setTimeout(() => { button.value = 'Connected'; const fieldsBtn = document.getElementById('chm_selgetcampos'); if (fieldsBtn && !fieldsBtn.disabled) { fieldsBtn.click(); } }, 800); } else { attrclass = 'spt-response-out'; chm_valid = '

ChimpmaticAPI Inactive

'; button.value = originalText; button.disabled = false; const configPanel = document.getElementById('chm_panel_configcampos'); if (configPanel) { configPanel.innerHTML = ' '; } } if (listPanel) { listPanel.className = attrclass; } const validPanel = document.getElementById('chm_apivalid'); if (validPanel) { validPanel.innerHTML = chm_valid; } }) .catch(error => { button.value = originalText; button.disabled = false; alert('Error loading audiences: ' + error.message); }); } }); // Auto-connect on paste document.addEventListener('paste', function(event) { const apiInput = document.getElementById('cmatic-api'); if (event.target !== apiInput) return; setTimeout(function() { const apiKey = apiInput.value.trim(); if (apiKey && apiKey.length >= 30 && apiKey.includes('-')) { const connectBtn = document.getElementById('chm_activalist'); if (connectBtn && !connectBtn.disabled) { connectBtn.click(); } } }, 100); }); // Auto-refresh fields when audience dropdown changes document.addEventListener('change', function(event) { if (event.target && event.target.id === 'wpcf7-mailchimp-list') { const fieldsBtn = document.getElementById('chm_selgetcampos') || document.getElementById('mce_fetch_fields'); if (fieldsBtn && !fieldsBtn.disabled) { fieldsBtn.click(); } } }); // Fetch Your Fields and Groups document.addEventListener('click', async function(event) { if (event.target && (event.target.id === 'chm_selgetcampos' || event.target.id === 'mce_fetch_fields')) { event.preventDefault(); const button = event.target; if (button.disabled) return; const data = { tool_key: document.getElementById('wpcf7-mailchimp-tool_key')?.value || '', chm_idformxx: getFormId(), chm_listid: document.getElementById('wpcf7-mailchimp-list')?.value || '', chimpapi: await getApiKey() }; const isInputButton = button.tagName === 'INPUT'; const originalText = isInputButton ? button.value : button.textContent; if (isInputButton) { button.value = 'Syncing...'; } else { button.textContent = 'Syncing...'; } button.disabled = true; chmRequest('wpcf7_chm_loadcampos', data) .then(response => { const genPanel = document.getElementById('chm_panel_gencamposygrupos'); if (genPanel) { genPanel.innerHTML = response; genPanel.className = 'spt-response-out'; const listPanel = document.getElementById('chm_panel_listamail'); const attrclass = listPanel ? listPanel.className : ''; if (attrclass === 'spt-response-out') { genPanel.className = 'spt-response-out'; } else { genPanel.className = 'spt-response-out spt-valid'; } setTimeout(function() { if (typeof applyFuzzyMatchingPro === 'function') { applyFuzzyMatchingPro(); } }, 100); } if (isInputButton) { button.value = 'Synced!'; } else { button.textContent = 'Synced!'; } setTimeout(() => { if (isInputButton) { button.value = originalText; } else { button.textContent = originalText; } button.disabled = false; }, 800); }) .catch(error => { if (isInputButton) { button.value = originalText; } else { button.textContent = originalText; } button.disabled = false; alert('Error: ' + error.message); }); } }); // Load Groups document.addEventListener('click', async function(event) { if (event.target && event.target.id === 'chm_activagroups') { event.preventDefault(); const data = { tool_key: document.getElementById('wpcf7-mailchimp-tool_key')?.value || '', chm_idformxx: getFormId(), chm_listid: document.getElementById('wpcf7-mailchimp-list')?.value || '', chimpapi: await getApiKey() }; chmRequest('wpcf7_chm_loadgrupos', data) .then(response => { const groupPanel = document.getElementById('chm_panel_listgroup'); if (groupPanel) { groupPanel.innerHTML = response; groupPanel.className = 'spt-response-out'; const listPanel = document.getElementById('chm_panel_listamail'); const attrclass = listPanel ? listPanel.className : ''; if (attrclass === 'spt-response-out') { groupPanel.className = 'spt-response-out'; } else { groupPanel.className = 'spt-response-out spt-valid'; } } }) .catch(error => { alert('Error: ' + error.message); }); } }); // Export Users document.addEventListener('click', async function(event) { if (event.target && event.target.id === 'chm_userexport') { event.preventDefault(); const valuesChecked = []; let icont = 1; document.querySelectorAll("input[type='checkbox'][name='usercheck']:checked").forEach(checkbox => { const idListInput = document.getElementById(`wpcf7-mailchimp-idlistexport${icont}`); valuesChecked.push([ checkbox.value, idListInput ? idListInput.value : '' ]); icont++; }); const data = { tool_key: document.getElementById('wpcf7-mailchimp-tool_key')?.value || '', chm_idformxx: getFormId(), chimpapi: await getApiKey(), cadseluser: valuesChecked }; chmRequest('wpcf7_chm_exporuser', data) .then(response => { const exportPanel = document.getElementById('chm_panel_exporuser'); if (exportPanel) { exportPanel.innerHTML = response; } }) .catch(error => { alert('Error: ' + error.message); }); } }); // Get Interest (Groups - Arbitrary) document.addEventListener('change', async function(event) { if (event.target && event.target.classList.contains('chimp-gg-arbirary')) { event.preventDefault(); const checkbox = event.target; const itag = checkbox.getAttribute('data-tag'); const xchk = checkbox.checked ? 1 : 0; const ggKeyInput = document.getElementById(`wpcf7-mailchimp-ggCustomKey${itag}`); const data = { valcheck: xchk, chm_idformxx: getFormId(), chm_listid: document.getElementById('wpcf7-mailchimp-list')?.value || '', chimpapi: await getApiKey(), indtag: itag, ggid: ggKeyInput ? ggKeyInput.value : '' }; chmRequest('wpcf7_chm_get_interest', data) .then(response => { // Find the select element and replace it with the response const selectElement = document.getElementById(`wpcf7-mailchimp-ggCustomValue${itag}`); if (selectElement) { selectElement.outerHTML = response; // Auto-select first interest option (backend already saved it) const newSelect = document.getElementById(`wpcf7-mailchimp-ggCustomValue${itag}`); if (newSelect) { if (xchk === 1 && newSelect.options.length > 1) { newSelect.selectedIndex = 1; } // Sync defaultSelected to prevent false "unsaved changes" warning Array.from(newSelect.options).forEach(opt => { opt.defaultSelected = opt.selected; }); } } // Sync checkbox defaultChecked after successful save checkbox.defaultChecked = checkbox.checked; }) .catch(error => { alert('Error loading interests: ' + error.message); }); } }); function togglePanel(panelSelector, buttonElement, showText, hideText) { const panel = typeof panelSelector === 'string' ? (panelSelector.startsWith('.') ? document.querySelector(panelSelector) : document.getElementById(panelSelector)) : panelSelector; if (panel) { const isHidden = panel.style.display === 'none' || !panel.style.display || window.getComputedStyle(panel).display === 'none'; panel.style.display = isHidden ? 'block' : 'none'; if (buttonElement && showText && hideText) { buttonElement.textContent = isHidden ? hideText : showText; } } } // On page load, set Connected button state document.addEventListener('DOMContentLoaded', function() { const connectBtn = document.getElementById('chm_activalist'); const isApiValid = getApiValid() === '1'; if (connectBtn && isApiValid && connectBtn.value !== 'Connected') { connectBtn.value = 'Connected'; } }); function findBestMatchPro(mergeTag, fieldName, cf7Tags) { if (!mergeTag || !cf7Tags || cf7Tags.length === 0) return null; const normalize = (str) => String(str).toLowerCase().replace(/[^a-z0-9]/g, ''); const normalizedTag = normalize(mergeTag); const normalizedName = normalize(fieldName); const keywordMappings = { email: ['email', 'mail', 'correo'], emailaddress: ['email', 'mail'], fname: ['name', 'firstname', 'first', 'nombre', 'your-name'], firstname: ['name', 'firstname', 'first', 'nombre'], lname: ['lastname', 'last', 'apellido', 'surname'], lastname: ['lastname', 'last', 'apellido'], name: ['name', 'nombre', 'your-name'], fullname: ['name', 'fullname', 'nombre'], phone: ['phone', 'tel', 'telefono', 'mobile', 'cell'], mobilephone: ['phone', 'tel', 'mobile', 'cell'], address: ['address', 'direccion', 'street'], address1: ['address', 'address1', 'street'], address2: ['address2', 'apt', 'suite'], city: ['city', 'ciudad'], state: ['state', 'province', 'region', 'estado'], zip: ['zip', 'postal', 'postcode'], country: ['country', 'pais'], company: ['company', 'organization', 'empresa', 'org'], website: ['website', 'url', 'web', 'sitio'], birthday: ['birthday', 'birth', 'dob', 'cumpleanos'], message: ['message', 'comments', 'mensaje', 'nota', 'your-message'] }; for (const [mcKeyword, cf7Keywords] of Object.entries(keywordMappings)) { if (normalizedTag.includes(mcKeyword) || normalizedName.includes(mcKeyword)) { for (const cf7Keyword of cf7Keywords) { const match = cf7Tags.find(tag => { const tagName = normalize(tag.name || tag); return tagName.includes(cf7Keyword); }); if (match) { return match.name || match; } } } } for (const tag of cf7Tags) { const tagName = normalize(tag.name || tag); if (normalizedTag.includes(tagName) || tagName.includes(normalizedTag)) { return tag.name || tag; } if (normalizedName.includes(tagName) || tagName.includes(normalizedName)) { return tag.name || tag; } } return null; } function applyFuzzyMatchingPro() { const genPanel = document.getElementById('chm_panel_gencamposygrupos'); const camposPanel = document.getElementById('chm_panel_camposforma'); let fieldRows = document.querySelectorAll('#chm_panel_camposforma .mcee-container'); if (fieldRows.length === 0) { fieldRows = document.querySelectorAll('#chm_panel_gencamposygrupos .mcee-container'); } if (fieldRows.length === 0) { fieldRows = document.querySelectorAll('.mcee-container'); } if (!fieldRows || fieldRows.length === 0) { return; } const cf7TagsSet = new Set(); const allDropdowns = document.querySelectorAll('[id^="wpcf7-mailchimp-CustomValue"]'); allDropdowns.forEach(dropdown => { Array.from(dropdown.options).forEach(option => { if (option.value && option.value.trim() !== '' && option.value !== ' ') { cf7TagsSet.add(option.value); } }); }); const cf7Tags = Array.from(cf7TagsSet).map(name => ({ name })); if (cf7Tags.length === 0) { return; } const changedFields = []; fieldRows.forEach((row, index) => { const keyInput = row.querySelector('[id^="wpcf7-mailchimp-CustomKey"]'); const dropdown = row.querySelector('[id^="wpcf7-mailchimp-CustomValue"]'); if (!keyInput || !dropdown) return; if (dropdown.value && dropdown.value.trim() !== '' && dropdown.value !== ' ') { return; } const dropdownOptions = []; Array.from(dropdown.options).forEach(option => { if (option.value && option.value.trim() !== '' && option.value !== ' ') { dropdownOptions.push({ name: option.value }); } }); if (dropdownOptions.length === 0) { return; } const mergeTag = keyInput.value; const label = row.querySelector('label'); const fieldName = label ? label.textContent.split('*|')[0].trim() : mergeTag; const bestMatch = findBestMatchPro(mergeTag, fieldName, dropdownOptions); if (bestMatch) { dropdown.value = bestMatch; const fieldMatch = dropdown.id.match(/wpcf7-mailchimp-CustomValue(\d+)/); if (fieldMatch) { changedFields.push({ field: 'CustomValue' + fieldMatch[1], value: bestMatch }); } Array.from(dropdown.options).forEach(opt => { opt.defaultSelected = (opt.value === bestMatch); }); } }); if (changedFields.length > 0) { saveFieldMappingsPro(changedFields); } } async function saveFieldMappingsPro(fields) { const formId = getFormId(); if (!formId || fields.length === 0) return; const restUrl = (typeof wpApiSettings !== 'undefined' && wpApiSettings.root) ? wpApiSettings.root + 'chimpmatic-lite/v1/form/field' : '/wp-json/chimpmatic-lite/v1/form/field'; for (const { field, value } of fields) { try { await fetch(restUrl, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': wpApiSettings?.nonce || '' }, body: JSON.stringify({ form_id: formId, field, value }) }); } catch (error) { console.error('Failed to save field mapping:', field, error); } } } // Tags Chip UI with auto-save (function() { document.addEventListener('change', function(e) { if (e.target.type !== 'checkbox') return; const chip = e.target.closest('.cmatic-tag-chip'); if (!chip) return; const checkbox = e.target; chip.classList.toggle('selected', checkbox.checked); // Extract tag name from checkbox name: wpcf7-mailchimp[labeltags][TAGNAME] const nameMatch = checkbox.name.match(/\[labeltags\]\[([^\]]+)\]/); if (!nameMatch) return; const tagName = nameMatch[1]; const formId = getFormId(); if (!formId) return; // Auto-save via unified REST API endpoint const restUrl = (typeof wpApiSettings !== 'undefined' && wpApiSettings.root) ? wpApiSettings.root + 'chimpmatic-lite/v1/form/field' : '/wp-json/chimpmatic-lite/v1/form/field'; fetch(restUrl, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': wpApiSettings?.nonce || '' }, body: JSON.stringify({ form_id: formId, field: 'labeltags.' + tagName, value: checkbox.checked }) }) .then(response => response.json()) .then(data => { if (data.success) { // Sync defaultChecked to prevent beforeunload warning checkbox.defaultChecked = checkbox.checked; } }) .catch(error => { console.error('Tag auto-save failed:', error); }); }); })(); // GDPR Dropdown Auto-Save (PRO feature) (function() { document.addEventListener('change', function(e) { if (e.target.tagName !== 'SELECT') return; // Match GDPR dropdown: wpcf7-mailchimp[GDPRCustomValue1] const nameMatch = e.target.name.match(/wpcf7-mailchimp\[GDPRCustomValue(\d+)\]/); if (!nameMatch) return; const fieldIndex = nameMatch[1]; const formId = getFormId(); if (!formId) return; const restUrl = (typeof wpApiSettings !== 'undefined' && wpApiSettings.root) ? wpApiSettings.root + 'chimpmatic-lite/v1/form/field' : '/wp-json/chimpmatic-lite/v1/form/field'; fetch(restUrl, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': wpApiSettings?.nonce || '' }, body: JSON.stringify({ form_id: formId, field: 'GDPRCustomValue' + fieldIndex, value: e.target.value.trim() }) }) .then(response => response.json()) .then(data => { if (data.success) { // Sync defaultValue to prevent beforeunload warning e.target.dataset.savedValue = e.target.value; } }) .catch(error => { console.error('GDPR auto-save failed:', error); }); }); })(); // Groups/Interests Dropdown Auto-Save (PRO feature) (function() { document.addEventListener('change', function(e) { if (e.target.tagName !== 'SELECT') return; // Match Groups dropdown: wpcf7-mailchimp[ggCustomValue1] const nameMatch = e.target.name.match(/wpcf7-mailchimp\[ggCustomValue(\d+)\]/); if (!nameMatch) return; const fieldIndex = nameMatch[1]; const formId = getFormId(); if (!formId) return; const restUrl = (typeof wpApiSettings !== 'undefined' && wpApiSettings.root) ? wpApiSettings.root + 'chimpmatic-lite/v1/form/field' : '/wp-json/chimpmatic-lite/v1/form/field'; fetch(restUrl, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': wpApiSettings?.nonce || '' }, body: JSON.stringify({ form_id: formId, field: 'ggCustomValue' + fieldIndex, value: e.target.value.trim() }) }) .then(response => response.json()) .then(data => { if (data.success) { // Sync defaultValue to prevent beforeunload warning e.target.dataset.savedValue = e.target.value; } }) .catch(error => { console.error('Groups auto-save failed:', error); }); }); })(); // Per-form Boolean Setting Toggle Auto-Save (sync_tags, checknotaddgroups, etc.) (function() { // Global fields are handled by chimpmatic-lite.js via /settings/toggle endpoint const globalFields = ['debug', 'backlink', 'auto_update', 'telemetry']; document.addEventListener('change', function(e) { if (e.target.type !== 'checkbox') return; // Only handle checkboxes with data-field attribute const fieldName = e.target.dataset.field; if (!fieldName) return; // Skip global fields - handled by chimpmatic-lite.js if (globalFields.includes(fieldName)) return; // Handle toggle-target visibility (use class, not inline style - CF7 strips inline styles) const toggleTarget = e.target.dataset.toggleTarget; if (toggleTarget) { const target = document.querySelector(toggleTarget); if (target) { target.classList.toggle('cmatic-hidden', !e.target.checked); } } const formId = getFormId(); if (!formId) return; const toggle = e.target.closest('.cmatic-toggle'); if (toggle) { toggle.classList.add('is-saving'); } const restUrl = (typeof wpApiSettings !== 'undefined' && wpApiSettings.root) ? wpApiSettings.root + 'cmatic/form/setting' : '/wp-json/cmatic/form/setting'; fetch(restUrl, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': wpApiSettings?.nonce || '' }, body: JSON.stringify({ form_id: formId, field: fieldName, value: e.target.checked }) }) .then(response => response.json()) .then(data => { if (toggle) { toggle.classList.remove('is-saving'); } if (data.success) { // Sync defaultChecked to prevent beforeunload warning e.target.defaultChecked = e.target.checked; } else { // Revert on failure e.target.checked = !e.target.checked; // Revert toggle-target visibility if (toggleTarget) { const target = document.querySelector(toggleTarget); if (target) { target.classList.toggle('cmatic-hidden', !e.target.checked); } } console.error('Setting save failed:', data.message); } }) .catch(error => { if (toggle) { toggle.classList.remove('is-saving'); } // Revert on error e.target.checked = !e.target.checked; // Revert toggle-target visibility if (toggleTarget) { const target = document.querySelector(toggleTarget); if (target) { target.classList.toggle('cmatic-hidden', !e.target.checked); } } console.error('Setting auto-save failed:', error); }); }); })(); // License Activation Handler document.addEventListener('DOMContentLoaded', function() { const activationForm = document.getElementById('chimpmatic-activation-form'); const deactivateBtn = document.getElementById('chimpmatic-deactivate-btn'); function showFeedback(message, type = 'info') { const feedbackDiv = document.getElementById('chimpmatic-license-feedback'); const messageP = document.getElementById('chimpmatic-license-feedback-message'); if (!feedbackDiv || !messageP) return; messageP.textContent = message; if (type === 'success') { feedbackDiv.style.backgroundColor = '#d4edda'; feedbackDiv.style.borderColor = '#c3e6cb'; feedbackDiv.style.color = '#155724'; feedbackDiv.style.border = '1px solid #c3e6cb'; } else if (type === 'error') { feedbackDiv.style.backgroundColor = '#f8d7da'; feedbackDiv.style.borderColor = '#f5c6cb'; feedbackDiv.style.color = '#721c24'; feedbackDiv.style.border = '1px solid #f5c6cb'; } else { feedbackDiv.style.backgroundColor = '#d1ecf1'; feedbackDiv.style.borderColor = '#bee5eb'; feedbackDiv.style.color = '#0c5460'; feedbackDiv.style.border = '1px solid #bee5eb'; } feedbackDiv.style.display = 'block'; } function hideFeedback() { const feedbackDiv = document.getElementById('chimpmatic-license-feedback'); if (feedbackDiv) { feedbackDiv.style.display = 'none'; } } if (activationForm) { activationForm.addEventListener('submit', async function(e) { e.preventDefault(); const licenseKey = document.getElementById('license_key').value.trim(); const productId = document.getElementById('product_id').value.trim(); const activateBtn = document.getElementById('chimpmatic-activate-btn'); if (!licenseKey || !productId) { showFeedback('Please enter both license key and product ID', 'error'); return; } activateBtn.disabled = true; activateBtn.textContent = 'Activating...'; hideFeedback(); try { showFeedback('Checking activation status...', 'info'); await new Promise(resolve => setTimeout(resolve, 500)); const restUrl = (typeof wpApiSettings !== 'undefined' && wpApiSettings.root) ? wpApiSettings.root + 'chimpmatic/v1/license/activate' : '/wp-json/chimpmatic/v1/license/activate'; const response = await fetch(restUrl, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': wpApiSettings?.nonce || '' }, body: JSON.stringify({ license_key: licenseKey, product_id: productId }), credentials: 'same-origin' }); const result = await response.json(); if (result.success) { if (result.was_deactivated) { showFeedback('Site already activated... deactivating previous activation...', 'info'); await new Promise(resolve => setTimeout(resolve, 800)); } showFeedback('Activating license...', 'info'); await new Promise(resolve => setTimeout(resolve, 500)); showFeedback('Success! License activated.', 'success'); await new Promise(resolve => setTimeout(resolve, 1000)); window.location.reload(); } else { showFeedback(result.message || 'Activation failed', 'error'); activateBtn.disabled = false; activateBtn.textContent = 'Activate License'; } } catch (error) { showFeedback('Error: ' + error.message, 'error'); activateBtn.disabled = false; activateBtn.textContent = 'Activate License'; } }); } if (deactivateBtn) { deactivateBtn.addEventListener('click', async function(e) { if (!e.target.onclick || e.target.onclick.call(e.target) === false) { return; } const btn = e.target; btn.disabled = true; btn.textContent = 'Deactivating...'; try { const restUrl = (typeof wpApiSettings !== 'undefined' && wpApiSettings.root) ? wpApiSettings.root + 'chimpmatic/v1/license/deactivate' : '/wp-json/chimpmatic/v1/license/deactivate'; const response = await fetch(restUrl, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': wpApiSettings?.nonce || '' }, credentials: 'same-origin' }); const result = await response.json(); if (result.success) { window.location.reload(); } else { alert('Deactivation failed: ' + (result.message || 'Unknown error')); btn.disabled = false; btn.textContent = 'Deactivate License For This Site'; } } catch (error) { alert('Error: ' + error.message); btn.disabled = false; btn.textContent = 'Deactivate License For This Site'; } }); } }); // CF7 Integration Page License Activation/Deactivation (function() { 'use strict'; const activateBtn = document.getElementById('cmatic-cf7-activate-btn'); const deactivateBtn = document.getElementById('cmatic-cf7-deactivate-btn'); const feedbackDiv = document.getElementById('cmatic-cf7-license-feedback'); let form = activateBtn ? activateBtn.closest('form') : (deactivateBtn ? deactivateBtn.closest('form') : null); if (!form && activateBtn) { let element = activateBtn.parentElement; while (element && element.tagName !== 'FORM') { element = element.parentElement; } form = element; } function showFeedback(message, type = 'info') { if (!feedbackDiv) return; feedbackDiv.textContent = message; feedbackDiv.style.display = 'block'; if (type === 'success') { feedbackDiv.style.backgroundColor = '#d4edda'; feedbackDiv.style.color = '#155724'; feedbackDiv.style.border = '1px solid #c3e6cb'; } else if (type === 'error') { feedbackDiv.style.backgroundColor = '#f8d7da'; feedbackDiv.style.color = '#721c24'; feedbackDiv.style.border = '1px solid #f5c6cb'; } else { feedbackDiv.style.backgroundColor = '#d1ecf1'; feedbackDiv.style.color = '#0c5460'; feedbackDiv.style.border = '1px solid #bee5eb'; } } function hideFeedback() { if (feedbackDiv) { feedbackDiv.style.display = 'none'; } } if (activateBtn) { activateBtn.addEventListener('click', async function(e) { e.preventDefault(); const licenseKey = document.getElementById('license_key').value.trim(); const productId = document.getElementById('product_id').value.trim(); if (!licenseKey || !productId) { showFeedback('Please enter both License Key and Product ID', 'error'); return; } activateBtn.disabled = true; activateBtn.textContent = 'Activating...'; hideFeedback(); try { const restUrl = (typeof wpApiSettings !== 'undefined' && wpApiSettings.root) ? wpApiSettings.root + 'chimpmatic/v1/license/activate' : '/wp-json/chimpmatic/v1/license/activate'; const response = await fetch(restUrl, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': wpApiSettings?.nonce || '' }, credentials: 'same-origin', body: JSON.stringify({ license_key: licenseKey, product_id: productId }) }); const result = await response.json(); if (result.success) { showFeedback('License activated successfully!', 'success'); setTimeout(function() { window.location.reload(); }, 1000); } else { showFeedback(result.message || 'Activation failed', 'error'); activateBtn.disabled = false; activateBtn.textContent = 'Activate License'; } } catch (error) { showFeedback('Network error: ' + error.message, 'error'); activateBtn.disabled = false; activateBtn.textContent = 'Activate License'; } }); } if (deactivateBtn) { deactivateBtn.addEventListener('click', async function(e) { e.preventDefault(); deactivateBtn.disabled = true; deactivateBtn.textContent = 'Deactivating...'; hideFeedback(); try { const restUrl = (typeof wpApiSettings !== 'undefined' && wpApiSettings.root) ? wpApiSettings.root + 'chimpmatic/v1/license/deactivate' : '/wp-json/chimpmatic/v1/license/deactivate'; const response = await fetch(restUrl, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': wpApiSettings?.nonce || '' }, credentials: 'same-origin' }); const result = await response.json(); if (result.success) { showFeedback('License deactivated successfully!', 'success'); setTimeout(function() { window.location.reload(); }, 1000); } else { showFeedback(result.message || 'Deactivation failed', 'error'); deactivateBtn.disabled = false; deactivateBtn.textContent = 'Deactivate License'; } } catch (error) { showFeedback('Network error: ' + error.message, 'error'); deactivateBtn.disabled = false; deactivateBtn.textContent = 'Deactivate License'; } }); } })(); // Dependency Update Handler (function() { 'use strict'; const updateBtn = document.querySelector('.cmatic-update-dependencies-btn'); if (updateBtn) { updateBtn.addEventListener('click', function(e) { updateBtn.disabled = true; updateBtn.textContent = 'Updating...'; updateBtn.style.opacity = '0.6'; const dismissBtn = document.querySelector('.cmatic-dismiss-notice-btn'); if (dismissBtn) { dismissBtn.disabled = true; dismissBtn.style.opacity = '0.6'; } const noticeDiv = updateBtn.closest('.notice'); if (noticeDiv) { const dismissX = noticeDiv.querySelector('.notice-dismiss'); if (dismissX) { dismissX.style.pointerEvents = 'none'; dismissX.style.opacity = '0.3'; } } }); } const dismissBtn = document.querySelector('.cmatic-dismiss-notice-btn'); if (dismissBtn) { dismissBtn.addEventListener('click', function(e) { e.preventDefault(); const nonce = dismissBtn.getAttribute('data-nonce'); if (!nonce) { return; } dismissBtn.disabled = true; dismissBtn.textContent = 'Dismissing...'; const restUrl = getRestUrl() + REST_ENDPOINTS.NOTICES_DISMISS; fetch(restUrl, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': wpApiSettings?.nonce || '' }, credentials: 'same-origin', body: JSON.stringify({}) }) .then(response => response.json()) .then(result => { if (result.success) { const noticeDiv = dismissBtn.closest('.notice'); if (noticeDiv) { noticeDiv.style.transition = 'opacity 0.3s'; noticeDiv.style.opacity = '0'; setTimeout(function() { noticeDiv.remove(); }, 300); } } else { dismissBtn.disabled = false; dismissBtn.textContent = 'Dismiss'; } }) .catch(error => { dismissBtn.disabled = false; dismissBtn.textContent = 'Dismiss'; }); }); } })();