PluginProbe
WebberZone Top 10 — Popular Posts / 4.3.0
WebberZone Top 10 — Popular Posts v4.3.0
4.5.2 4.5.1 4.5.0 4.4.3 4.4.2 4.4.1 4.4.0 4.3.4 4.3.3 4.3.2 4.3.1 4.3.0 trunk 1.0 1.0.1 1.1 1.2 1.3 1.4 1.4.1 1.5 1.5.1 1.5.2 1.5.3 1.6 All 118 releases
top-10 / includes / admin / settings / js / tom-select-init.js

tom-select-init.js in WebberZone Top 10 — Popular Posts 4.3.0, at includes/admin/settings/js/tom-select-init.js

236 lines 9.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /* global ajaxurl, WZTomSelectSettings, jQuery, TomSelect */
2
3 (function ($) {
4 'use strict';
5
6 function normalizeOption(item) {
7 if (!item || typeof item !== 'object') {
8 return null;
9 }
10
11 const value = item.id || item.value || '';
12 const text = item.name || item.text || value;
13
14 if (!value && !text) {
15 return null;
16 }
17
18 return { value: String(value), text: String(text) };
19 }
20
21 function getEndpointOptions(settings, endpoint) {
22 if (!settings || !endpoint) {
23 return [];
24 }
25
26 const endpointOptions = settings[endpoint];
27
28 if (!Array.isArray(endpointOptions)) {
29 return [];
30 }
31
32 return endpointOptions
33 .map(normalizeOption)
34 .filter(Boolean);
35 }
36
37 function isTaxonomyEndpoint(endpoint) {
38 const key = typeof endpoint === 'string' ? endpoint : '';
39 return key === 'category' || key === 'post_tag' || key.includes('tax');
40 }
41
42 function initTomSelect(root) {
43 const scope = (root && typeof root.querySelectorAll === 'function') ? root : document;
44 const elements = scope.querySelectorAll('.ts_autocomplete');
45
46 elements.forEach(function (element) {
47 if (element.tomselect) {
48 return;
49 }
50
51 const tagName = (element.tagName || '').toUpperCase();
52 if (tagName !== 'INPUT' && tagName !== 'SELECT' && tagName !== 'TEXTAREA') {
53 return;
54 }
55
56 const prefix = element.getAttribute('data-wp-prefix') || 'WZ';
57 const settingsKey = `${prefix}TomSelectSettings`;
58 const settings = window[settingsKey]
59 || window.WZTomSelectSettings
60 || window.freemkitTomSelectSettings
61 || {};
62
63 if (!settings || typeof settings !== 'object') {
64 return;
65 }
66
67 const action = element.getAttribute('data-wp-action') || settings.action;
68 const nonce = element.getAttribute('data-wp-nonce') || settings.nonce;
69 const endpoint = element.getAttribute('data-wp-endpoint') || settings.endpoint;
70 const strings = settings.strings || {};
71
72 const formattedOptions = getEndpointOptions(settings, endpoint);
73
74 let rawValue = '';
75
76 if (tagName === 'SELECT' && element.multiple && element.selectedOptions) {
77 rawValue = Array.from(element.selectedOptions)
78 .map(option => String(option.value || '').trim())
79 .filter(Boolean)
80 .join(',');
81 } else if (typeof element.value === 'string') {
82 rawValue = element.value;
83 } else {
84 const valueAttribute = element.getAttribute('value');
85 rawValue = typeof valueAttribute === 'string' ? valueAttribute : '';
86 }
87
88 const savedIds = rawValue.split(',').map(id => id.trim()).filter(Boolean);
89 const taxonomyEndpoint = isTaxonomyEndpoint(endpoint);
90
91 // For taxonomy endpoints, add saved values as options so Tom Select can display them
92 if (taxonomyEndpoint && savedIds.length > 0) {
93 const savedOptions = savedIds.map(savedValue => {
94 // Extract term name from formatted string "Name (taxonomy:id)"
95 const match = savedValue.match(/^(.*)\s+\(.*:\d+\)$/);
96 const termName = match ? match[1] : savedValue;
97 return { value: savedValue, text: termName };
98 });
99
100 // Merge saved options with existing options, avoiding duplicates
101 const allOptions = [...formattedOptions];
102 savedOptions.forEach(savedOption => {
103 if (!allOptions.some(opt => opt.value === savedOption.value)) {
104 allOptions.push(savedOption);
105 }
106 });
107
108 // Replace formattedOptions with merged options
109 formattedOptions.length = 0;
110 formattedOptions.push(...allOptions);
111 }
112
113 // For non-taxonomy endpoints, add saved values as options so Tom Select can display them.
114 if (!taxonomyEndpoint && savedIds.length > 0) {
115 savedIds.forEach(savedValue => {
116 if (!formattedOptions.some(opt => opt.value === savedValue)) {
117 formattedOptions.push({ value: savedValue, text: savedValue });
118 }
119 });
120 }
121
122 // Get any custom config from data attributes
123 let customConfig = {};
124 const configAttr = element.getAttribute('data-ts-config');
125
126 if (configAttr) {
127 try {
128 customConfig = JSON.parse(configAttr);
129 } catch (e) {
130 console.error('Error parsing custom config:', configAttr, e);
131 }
132 }
133
134 // Default config
135 const defaultConfig = {
136 plugins: ['dropdown_input', 'clear_button', 'remove_button'],
137 valueField: 'value',
138 labelField: 'text',
139 searchField: ['text', 'value'],
140 options: formattedOptions,
141 items: savedIds,
142 persist: true,
143 createOnBlur: false,
144 create: false,
145 render: {
146 no_results: (data, escape) => {
147 const template = strings.no_results || 'No results for "%s"';
148 return `<div class="no-results">${template.replace('%s', escape(data.input))}</div>`;
149 },
150 option: (data, escape) => {
151 // For taxonomy endpoints, display only the formatted value to avoid duplication
152 if (taxonomyEndpoint) {
153 return `<div>${escape(data.value)}</div>`;
154 }
155 // Avoid showing "value (value)" when value and text are identical.
156 if (data.text === data.value) {
157 return `<div>${escape(data.value)}</div>`;
158 }
159 return `<div>${escape(data.text)} (${escape(data.value)})</div>`;
160 },
161 item: (data, escape) => {
162 // For taxonomy endpoints, display only the formatted value to avoid duplication
163 if (taxonomyEndpoint) {
164 return `<div>${escape(data.value)}</div>`;
165 }
166 // Avoid showing "value (value)" when value and text are identical.
167 if (data.text === data.value) {
168 return `<div>${escape(data.value)}</div>`;
169 }
170 return `<div>${escape(data.text)} (${escape(data.value)})</div>`;
171 }
172 },
173 load: function (query, callback) {
174 if (!query.length) {
175 callback();
176 return;
177 }
178
179 $.ajax({
180 url: ajaxurl,
181 type: 'POST',
182 dataType: 'json',
183 data: {
184 action: action,
185 nonce: nonce,
186 q: query,
187 endpoint: endpoint
188 },
189 error: function () {
190 callback();
191 },
192 success: function (res) {
193 if (res.success && res.data && Array.isArray(res.data.items)) {
194 callback(
195 res.data.items
196 .map(normalizeOption)
197 .filter(Boolean)
198 );
199 } else if (res.success && Array.isArray(res.data)) {
200 callback(
201 res.data
202 .map(normalizeOption)
203 .filter(Boolean)
204 );
205 } else {
206 callback();
207 }
208 }
209 });
210 }
211 };
212
213 // Merge default config with custom config
214 const finalConfig = { ...defaultConfig, ...customConfig };
215
216 // Initialize Tom Select with merged config
217 try {
218 new TomSelect(element, finalConfig);
219 } catch (error) {
220 console.error('Tom Select initialization error:', error);
221 }
222 });
223 }
224
225 window.WZInitTomSelect = initTomSelect;
226
227 document.addEventListener('wz:repeater-item-added', function (event) {
228 const detail = event && event.detail ? event.detail : {};
229 initTomSelect(detail.container || document);
230 });
231
232 $(document).ready(function () {
233 initTomSelect(document);
234 });
235 })(jQuery);
236