PluginProbe
Booking Calendar / 11.9
Booking Calendar v11.9
11.9 11.8.4 11.8.3 11.8.2 11.8.1 11.8 11.7 11.6.1 11.6 11.5 11.4.3 11.4.2 11.4.1 11.4 11.3 11.2.1 11.2 11.1 11.0 10.15.7 10.15.6 10.1.3 10.10 10.10.1 10.10.2 All 205 releases
← All changes | includes/save-load-option/_src/save-load-option.js +187 -28 10.15.6 → 11.9 View file →
@@ -1,4 +1,4 @@
1 1 /**
2 2 * General Option Loader/Saver (client)
3 3 *
4 4 * - Provides:
@@ -157,19 +157,128 @@
157 157 $el.removeData('wpbc-uix-busy')
158 158 .removeData('wpbc-uix-original-html');
159 159 }
160 160
161 + var wpbc_uix_autosave_registry = {};
162 +
163 + function wpbc_uix_get_option_save_config($el) {
164 + return {
165 + data_name : $el.data('wpbc-u-save-name'),
166 + fields_raw : $el.data('wpbc-u-save-fields') || '',
167 + inline_value : wpbc_uix_read_attr_or_data($el, 'data-wpbc-u-save-value', 'wpbc-u-save-value'),
168 + json : wpbc_uix_read_attr_or_data($el, 'data-wpbc-u-save-value-json', 'wpbc-u-save-value-json'),
169 + value_from_selector: $el.data('wpbc-u-save-value-from') || $el.attr('data-wpbc-u-save-value-from')
170 + };
171 + }
172 +
173 + function wpbc_uix_build_option_save_payload($el, cfg) {
174 + cfg = cfg || wpbc_uix_get_option_save_config($el);
175 +
176 + if (typeof cfg.json === 'string' && cfg.json.trim() !== '') {
177 + return cfg.json.trim();
178 + }
179 +
180 + if (cfg.value_from_selector) {
181 + var $src = $(cfg.value_from_selector);
182 + var $control = $src.is('input,select,textarea') ? $src : $src.find('input,select,textarea').first();
183 + return wpbc_uix_get_control_value($control);
184 + }
185 +
186 + if (typeof cfg.inline_value !== 'undefined' && cfg.inline_value !== null) {
187 + return String(cfg.inline_value);
188 + }
189 +
190 + if (cfg.fields_raw) {
191 + var fields = String(cfg.fields_raw).split(',')
192 + .map(function (s) { return String(s || '').trim(); })
193 + .filter(Boolean);
194 +
195 + var data = {};
196 +
197 + fields.forEach(function (sel) {
198 + var $f = $(sel);
199 + if (!$f.length) { return; }
200 +
201 + var $control = $f.is('input,select,textarea') ? $f : $f.find('input,select,textarea').first();
202 + if (!$control.length) { return; }
203 +
204 + var key = $control.attr('name') || $control.attr('id');
205 + if (!key) { return; }
206 +
207 + data[key] = wpbc_uix_get_control_value($control);
208 + });
209 +
210 + return $.param(data);
211 + }
212 +
213 + return null;
214 + }
215 +
216 + function wpbc_uix_update_autosave_registry_from_element(el, is_dirty) {
217 + var $el = $(el);
218 + var cfg = wpbc_uix_get_option_save_config($el);
219 +
220 + if (!cfg.data_name) {
221 + return;
222 + }
223 +
224 + wpbc_uix_autosave_registry[cfg.data_name] = {
225 + data_name : cfg.data_name,
226 + payload : wpbc_uix_build_option_save_payload($el, cfg),
227 + dirty : !!is_dirty,
228 + el : el
229 + };
230 + }
231 +
232 + function wpbc_uix_save_autosave_registry_entry(entry) {
233 + if (!entry || !entry.dirty || !entry.data_name || entry.payload === null || !w.wpbc_option_saver_loader_config || !w.wpbc_option_saver_loader_config.save_nonce) {
234 + return;
235 + }
236 +
237 + $(document).trigger('wpbc:option:beforeSave', [ $(), entry.payload ]);
238 +
239 + $.ajax({
240 + url: w.wpbc_option_saver_loader_config.ajax_url,
241 + type: 'POST',
242 + data: {
243 + action: w.wpbc_option_saver_loader_config.action_save,
244 + nonce: w.wpbc_option_saver_loader_config.save_nonce,
245 + data_name: entry.data_name,
246 + data_value: entry.payload
247 + }
248 + })
249 + .done(function (resp) {
250 + if (resp && resp.success) {
251 + entry.dirty = false;
252 + if (entry.el) {
253 + $(entry.el).attr('data-wpbc-u-autosave-dirty', '0');
254 + }
255 + if (typeof w.wpbc_admin_show_message === 'function') {
256 + w.wpbc_admin_show_message((resp.data && resp.data.message) ? resp.data.message : 'Saved', 'success', 1000, false);
257 + }
258 + } else if (typeof w.wpbc_admin_show_message === 'function') {
259 + w.wpbc_admin_show_message((resp && resp.data && resp.data.message) ? resp.data.message : 'Save error', 'error', 30000);
260 + }
261 +
262 + $(document).trigger('wpbc:option:afterSave', [ resp ]);
263 + })
264 + .fail(function (xhr) {
265 + if (typeof w.wpbc_admin_show_message === 'function') {
266 + w.wpbc_admin_show_message('WPBC | AJAX ' + xhr.status + ' ' + xhr.statusText, 'error', 30000);
267 + }
268 + $(document).trigger('wpbc:option:afterSave', [ { success: false, data: { message: xhr.statusText } } ]);
269 + });
270 + }
271 +
161 272 /**
162 273 * Save Option - send ajax request to save data.
163 274 *
164 275 * Data attributes:
165 276 * data-wpbc-u-save-name — option key (required)
166 - * data-wpbc-u-save-nonce — nonce value (required)
167 - * data-wpbc-u-save-action — nonce action (required)
277 + * The fixed save nonce is supplied by wpbc_option_saver_loader_config.
168 278 * data-wpbc-u-save-value — RAW scalar to save (optional) (dynamic: read via attr first)
169 279 * data-wpbc-u-save-value-json — JSON string to save (optional) (dynamic: read via attr first)
170 - * data-wpbc-u-save-fields — CSV selectors; values serialized with jQuery.param (optional). Optional allowlist of keys for split mode (CSV).
171 - * data-wpbc-u-save-mode - Optional.: 'split' | '' -- Optional: split JSON object into separate options server-side.
280 + * data-wpbc-u-save-fields — CSV selectors serialized with jQuery.param (optional). Server policy owns the writable-key allowlist.
172 281 * data-wpbc-u-save-value-from — OPTIONAL selector to read scalar from (checkbox => On/Off)
173 282 * data-wpbc-u-busy-text — custom text during AJAX (optional)
174 283 * data-wpbc-u-save-callback — window function name to call on success (optional)
175 284 *
@@ -184,18 +293,15 @@
184 293 }
185 294
186 295 var $el = $(el);
187 296
188 - // Static values can be read from .data().
189 - var nonce = $el.data('wpbc-u-save-nonce');
190 - var nonce_action = $el.data('wpbc-u-save-action');
191 - var data_name = $el.data('wpbc-u-save-name');
297 + var nonce = w.wpbc_option_saver_loader_config.save_nonce;
298 + var data_name = $el.data('wpbc-u-save-name');
192 299
193 300 // Dynamic values MUST prefer attribute read (fresh), fallback to .data().
194 301 var fields_raw = $el.data('wpbc-u-save-fields') || '';
195 302 var inline_value = wpbc_uix_read_attr_or_data($el, 'data-wpbc-u-save-value', 'wpbc-u-save-value');
196 303 var json = wpbc_uix_read_attr_or_data($el, 'data-wpbc-u-save-value-json', 'wpbc-u-save-value-json');
197 - var save_mode = $el.data('wpbc-u-save-mode') || $el.attr('data-wpbc-u-save-mode') || '';
198 304
199 305 // Optional: compute scalar from another control selector at click time.
200 306 var value_from_selector = $el.data('wpbc-u-save-value-from') || $el.attr('data-wpbc-u-save-value-from');
201 307
@@ -201,10 +307,10 @@
201 307
202 308 var cb_id = $el.data('wpbc-u-save-callback');
203 309 var cb_fn = (cb_id && typeof w[cb_id] === 'function') ? w[cb_id] : null;
204 310
205 - if (!nonce || !nonce_action || !data_name) {
206 - console.error('WPBC | missing nonce/action/name');
311 + if (!nonce || !data_name) {
312 + console.error('WPBC | missing nonce/name');
207 313 return;
208 314 }
209 315
210 316 var payload = '';
@@ -266,20 +372,13 @@
266 372
267 373 $.ajax({
268 374 url: w.wpbc_option_saver_loader_config.ajax_url,
269 375 type: 'POST',
270 - data: {
271 - action: w.wpbc_option_saver_loader_config.action_save,
272 - nonce: nonce,
273 - nonce_action: nonce_action,
274 - data_name: data_name,
275 - data_value: payload,
276 -
277 - // Optional: split JSON object into separate options server-side.
278 - data_mode: save_mode,
279 -
280 - // Optional allowlist of keys for split mode (CSV).
281 - data_fields: (save_mode === 'split' ? String(fields_raw || '') : '')
376 + data: {
377 + action: w.wpbc_option_saver_loader_config.action_save,
378 + nonce: nonce,
379 + data_name: data_name,
380 + data_value: payload
282 381 }
283 382 })
284 383 .done(function (resp) {
285 384
@@ -287,8 +386,13 @@
287 386 // Fixed: show success only when resp.success is true.
288 387
289 388 if (resp && resp.success) {
290 389
390 + $el.attr('data-wpbc-u-autosave-dirty', '0');
391 + if (data_name && wpbc_uix_autosave_registry[data_name]) {
392 + wpbc_uix_autosave_registry[data_name].dirty = false;
393 + }
394 +
291 395 if (cb_fn) {
292 396 try { cb_fn(resp); } catch (e) { console.error(e); }
293 397 }
294 398
@@ -324,8 +428,60 @@
324 428 });
325 429 };
326 430
327 431 /**
432 + * Wire opt-in autosave of global options after successful BFB form save.
433 + *
434 + * Add data-wpbc-u-autosave-on-form-save="1" to a save control to participate.
435 + * Dirty state is tracked from data-wpbc-u-save-value-from, or data-wpbc-u-autosave-watch when present.
436 + *
437 + * @returns {void}
438 + */
439 + function wpbc_uix_bind_autosave_on_form_save() {
440 + var autosave_selector = '[data-wpbc-u-autosave-on-form-save="1"]';
441 +
442 + $(document).on('change input', 'input,select,textarea', function () {
443 + var changed_el = this;
444 +
445 + $(autosave_selector).each(function () {
446 + var $btn = $(this);
447 + var watch_selector = $btn.attr('data-wpbc-u-autosave-watch') || $btn.attr('data-wpbc-u-save-value-from') || '';
448 +
449 + if (!watch_selector) {
450 + return;
451 + }
452 +
453 + var $watched = $(watch_selector);
454 + if ($watched.filter(changed_el).length || $watched.has(changed_el).length) {
455 + $btn.attr('data-wpbc-u-autosave-dirty', '1');
456 + wpbc_uix_update_autosave_registry_from_element(this, true);
457 + }
458 + });
459 + });
460 +
461 + document.addEventListener('wpbc:bfb:form:ajax_saved', function () {
462 + $(autosave_selector).each(function () {
463 + if (this.getAttribute('data-wpbc-u-autosave-dirty') === '1') {
464 + wpbc_uix_update_autosave_registry_from_element(this, true);
465 + }
466 + });
467 +
468 + Object.keys(wpbc_uix_autosave_registry).forEach(function (option_name) {
469 + var entry = wpbc_uix_autosave_registry[option_name];
470 + if (!entry || !entry.dirty) {
471 + return;
472 + }
473 +
474 + if (entry.el && document.documentElement.contains(entry.el)) {
475 + w.wpbc_save_option_from_element(entry.el);
476 + } else {
477 + wpbc_uix_save_autosave_registry_entry(entry);
478 + }
479 + });
480 + });
481 + }
482 +
483 + /**
328 484 * Load option value via AJAX.
329 485 *
330 486 * @param {HTMLElement} el element with data attributes.
331 487 * @returns {void}
@@ -342,10 +498,10 @@
342 498
343 499 var cb_id = $el.data('wpbc-u-load-callback');
344 500 var cb_fn = (cb_id && typeof w[cb_id] === 'function') ? w[cb_id] : null;
345 501
346 - if (!name) {
347 - console.error('WPBC | missing data-wpbc-u-load-name');
502 + if (!name || !w.wpbc_option_saver_loader_config.load_nonce) {
503 + console.error('WPBC | missing load nonce/name');
348 504 return;
349 505 }
350 506
351 507 $(document).trigger('wpbc:option:beforeLoad', [ $el, name ]);
@@ -353,11 +509,12 @@
353 509
354 510 $.ajax({
355 511 url: w.wpbc_option_saver_loader_config.ajax_url,
356 512 type: 'GET',
357 - data: {
358 - action: w.wpbc_option_saver_loader_config.action_load,
359 - data_name: name
513 + data: {
514 + action: w.wpbc_option_saver_loader_config.action_load,
515 + nonce: w.wpbc_option_saver_loader_config.load_nonce,
516 + data_name: name
360 517 }
361 518 })
362 519 .done(function (resp) {
363 520 if (resp && resp.success) {
@@ -376,6 +533,8 @@
376 533 .always(function () {
377 534 wpbc_uix_busy_off($el);
378 535 });
379 536 };
537 +
538 + wpbc_uix_bind_autosave_on_form_save();
380 539
381 540 }(window, jQuery));