/* global ajaxurl, WZTomSelectSettings, jQuery, TomSelect */ (function ($) { 'use strict'; function normalizeOption(item) { if (!item || typeof item !== 'object') { return null; } const value = item.id || item.value || ''; const text = item.name || item.text || value; if (!value && !text) { return null; } return { value: String(value), text: String(text) }; } function getEndpointOptions(settings, endpoint) { if (!settings || !endpoint) { return []; } const endpointOptions = settings[endpoint]; if (!Array.isArray(endpointOptions)) { return []; } return endpointOptions .map(normalizeOption) .filter(Boolean); } function isTaxonomyEndpoint(endpoint) { const key = typeof endpoint === 'string' ? endpoint : ''; return key === 'category' || key === 'post_tag' || key.includes('tax'); } function initTomSelect(root) { const scope = (root && typeof root.querySelectorAll === 'function') ? root : document; const elements = scope.querySelectorAll('.ts_autocomplete'); elements.forEach(function (element) { if (element.tomselect) { return; } const tagName = (element.tagName || '').toUpperCase(); if (tagName !== 'INPUT' && tagName !== 'SELECT' && tagName !== 'TEXTAREA') { return; } const prefix = element.getAttribute('data-wp-prefix') || 'WZ'; const settingsKey = `${prefix}TomSelectSettings`; const settings = window[settingsKey] || window.WZTomSelectSettings || window.freemkitTomSelectSettings || {}; if (!settings || typeof settings !== 'object') { return; } const action = element.getAttribute('data-wp-action') || settings.action; const nonce = element.getAttribute('data-wp-nonce') || settings.nonce; const endpoint = element.getAttribute('data-wp-endpoint') || settings.endpoint; const strings = settings.strings || {}; const formattedOptions = getEndpointOptions(settings, endpoint); let rawValue = ''; if (tagName === 'SELECT' && element.multiple && element.selectedOptions) { rawValue = Array.from(element.selectedOptions) .map(option => String(option.value || '').trim()) .filter(Boolean) .join(','); } else if (typeof element.value === 'string') { rawValue = element.value; } else { const valueAttribute = element.getAttribute('value'); rawValue = typeof valueAttribute === 'string' ? valueAttribute : ''; } const savedIds = rawValue.split(',').map(id => id.trim()).filter(Boolean); const taxonomyEndpoint = isTaxonomyEndpoint(endpoint); // For taxonomy endpoints, add saved values as options so Tom Select can display them if (taxonomyEndpoint && savedIds.length > 0) { const savedOptions = savedIds.map(savedValue => { // Extract term name from formatted string "Name (taxonomy:id)" const match = savedValue.match(/^(.*)\s+\(.*:\d+\)$/); const termName = match ? match[1] : savedValue; return { value: savedValue, text: termName }; }); // Merge saved options with existing options, avoiding duplicates const allOptions = [...formattedOptions]; savedOptions.forEach(savedOption => { if (!allOptions.some(opt => opt.value === savedOption.value)) { allOptions.push(savedOption); } }); // Replace formattedOptions with merged options formattedOptions.length = 0; formattedOptions.push(...allOptions); } // For non-taxonomy endpoints, add saved values as options so Tom Select can display them. if (!taxonomyEndpoint && savedIds.length > 0) { savedIds.forEach(savedValue => { if (!formattedOptions.some(opt => opt.value === savedValue)) { formattedOptions.push({ value: savedValue, text: savedValue }); } }); } // Get any custom config from data attributes let customConfig = {}; const configAttr = element.getAttribute('data-ts-config'); if (configAttr) { try { customConfig = JSON.parse(configAttr); } catch (e) { console.error('Error parsing custom config:', configAttr, e); } } // Default config const defaultConfig = { plugins: ['dropdown_input', 'clear_button', 'remove_button'], valueField: 'value', labelField: 'text', searchField: ['text', 'value'], options: formattedOptions, items: savedIds, persist: true, createOnBlur: false, create: false, render: { no_results: (data, escape) => { const template = strings.no_results || 'No results for "%s"'; return `
${template.replace('%s', escape(data.input))}
`; }, option: (data, escape) => { // For taxonomy endpoints, display only the formatted value to avoid duplication if (taxonomyEndpoint) { return `
${escape(data.value)}
`; } // Avoid showing "value (value)" when value and text are identical. if (data.text === data.value) { return `
${escape(data.value)}
`; } return `
${escape(data.text)} (${escape(data.value)})
`; }, item: (data, escape) => { // For taxonomy endpoints, display only the formatted value to avoid duplication if (taxonomyEndpoint) { return `
${escape(data.value)}
`; } // Avoid showing "value (value)" when value and text are identical. if (data.text === data.value) { return `
${escape(data.value)}
`; } return `
${escape(data.text)} (${escape(data.value)})
`; } }, load: function (query, callback) { if (!query.length) { callback(); return; } // Build base payload. const payload = { action: action, nonce: nonce, q: query, endpoint: endpoint }; // Optional: harvest sibling fields from the closest repeater // row and merge them into the payload. Declared on the input // as data-wp-extra-fields='{"row_pat":"pat","row_id":"row_id"}' // — meaning send the closest row's `[pat]` input as `row_pat` // and `[row_id]` as `row_id`. const extraAttr = element.getAttribute('data-wp-extra-fields'); if (extraAttr) { try { const map = JSON.parse(extraAttr); const row = element.closest('.wz-repeater-item'); if (row && map && typeof map === 'object') { Object.keys(map).forEach(function (key) { const suffix = map[key]; if (!suffix) { return; } const sibling = row.querySelector('[name$="[' + suffix + ']"]'); if (sibling && typeof sibling.value === 'string') { payload[key] = sibling.value; } }); } } catch (e) { console.error('Error parsing data-wp-extra-fields:', extraAttr, e); } } $.ajax({ url: ajaxurl, type: 'POST', dataType: 'json', data: payload, error: function () { callback(); }, success: function (res) { if (res.success && res.data && Array.isArray(res.data.items)) { callback( res.data.items .map(normalizeOption) .filter(Boolean) ); } else if (res.success && Array.isArray(res.data)) { callback( res.data .map(normalizeOption) .filter(Boolean) ); } else { callback(); } } }); } }; // Merge default config with custom config const finalConfig = { ...defaultConfig, ...customConfig }; // Initialize Tom Select with merged config try { new TomSelect(element, finalConfig); } catch (error) { console.error('Tom Select initialization error:', error); } }); } window.WZInitTomSelect = initTomSelect; document.addEventListener('wz:repeater-item-added', function (event) { const detail = event && event.detail ? event.detail : {}; initTomSelect(detail.container || document); }); $(document).ready(function () { initTomSelect(document); }); })(jQuery);