| 1 |
// --------------------------------------------------------------------------------------------------------------------- |
| 2 |
// == File /includes/page-form-builder/field-packs/calendar/_out/calendar.js |
| 3 |
// == Pack Calendar (WP-template–driven) — minimal, modern, and Builder-focused renderer |
| 4 |
// == Compatible with PHP pack: ../calendar.php (version 1.2.2) |
| 5 |
// --------------------------------------------------------------------------------------------------------------------- |
| 6 |
(function (w) { |
| 7 |
'use strict'; |
| 8 |
|
| 9 |
// Direct dev logger alias (snake format); no local wrappers. |
| 10 |
const dev = (w._wpbc && w._wpbc.dev) ? w._wpbc.dev : { log(){}, error(){} }; |
| 11 |
|
| 12 |
// Core singletons from the Builder. |
| 13 |
var Core = w.WPBC_BFB_Core || {}; |
| 14 |
var Registry = Core.WPBC_BFB_Field_Renderer_Registry; |
| 15 |
var Field_Base = Core.WPBC_BFB_Field_Base || null; |
| 16 |
|
| 17 |
if ( !Registry || !Registry.register ) { |
| 18 |
dev.error( 'WPBC_BFB_Field_Calendar', 'Registry missing — load bfb-core first.' ); |
| 19 |
return; |
| 20 |
} |
| 21 |
|
| 22 |
// Localized boot payload from PHP (calendar.php::enqueue_js()). |
| 23 |
var Boot = w.WPBC_BFB_CalendarBoot || {}; |
| 24 |
|
| 25 |
// Remember which resource IDs already have their data loaded this session. |
| 26 |
var rid_loaded_cache = Object.create( null ); |
| 27 |
|
| 28 |
// ----------------------------------------------------------------------------------------------------------------- |
| 29 |
// Resource ID Helpers |
| 30 |
// ----------------------------------------------------------------------------------------------------------------- |
| 31 |
/** |
| 32 |
* Get configured preview booking resource ID from localized boot data. |
| 33 |
* |
| 34 |
* @returns {number} |
| 35 |
*/ |
| 36 |
function get_configured_preview_resource_id() { |
| 37 |
var rid = Number( Boot.default_preview_resource_id || 1 ); |
| 38 |
rid = isFinite( rid ) ? Math.max( 1, Math.floor( rid ) ) : 1; |
| 39 |
return rid; |
| 40 |
} |
| 41 |
|
| 42 |
/** |
| 43 |
* Get first existing booking resource ID from localized boot data. |
| 44 |
* |
| 45 |
* @returns {number} |
| 46 |
*/ |
| 47 |
function get_first_existing_resource_id() { |
| 48 |
|
| 49 |
var resources = Array.isArray( Boot.booking_resources ) ? Boot.booking_resources : []; |
| 50 |
|
| 51 |
for ( var i = 0; i < resources.length; i++ ) { |
| 52 |
var id = Number( resources[i] && resources[i].booking_type_id ); |
| 53 |
if ( isFinite( id ) && id > 0 ) { |
| 54 |
return Math.floor( id ); |
| 55 |
} |
| 56 |
} |
| 57 |
|
| 58 |
return 1; |
| 59 |
} |
| 60 |
|
| 61 |
/** |
| 62 |
* Resolve effective preview booking resource ID. |
| 63 |
* Priority: |
| 64 |
* 1. Configured default preview resource, if it exists |
| 65 |
* 2. Provided rid, if it exists |
| 66 |
* 3. First existing booking resource |
| 67 |
* 4. Fallback to 1 |
| 68 |
* |
| 69 |
* @param {number} rid_candidate |
| 70 |
* @returns {number} |
| 71 |
*/ |
| 72 |
function resolve_effective_resource_id(rid_candidate) { |
| 73 |
|
| 74 |
var resources = Array.isArray( Boot.booking_resources ) ? Boot.booking_resources : []; |
| 75 |
var configured_rid = get_configured_preview_resource_id(); |
| 76 |
var candidate = Number( rid_candidate || 1 ); |
| 77 |
|
| 78 |
function resource_exists(id) { |
| 79 |
id = Number( id || 1 ); |
| 80 |
if ( ! isFinite( id ) || id <= 0 ) { |
| 81 |
return false; |
| 82 |
} |
| 83 |
|
| 84 |
for ( var i = 0; i < resources.length; i++ ) { |
| 85 |
if ( Number( resources[i] && resources[i].booking_type_id ) === id ) { |
| 86 |
return true; |
| 87 |
} |
| 88 |
} |
| 89 |
return false; |
| 90 |
} |
| 91 |
|
| 92 |
if ( resource_exists( configured_rid ) ) { |
| 93 |
return configured_rid; |
| 94 |
} |
| 95 |
|
| 96 |
if ( resource_exists( candidate ) ) { |
| 97 |
return candidate; |
| 98 |
} |
| 99 |
|
| 100 |
return get_first_existing_resource_id(); |
| 101 |
} |
| 102 |
|
| 103 |
// ----------------------------------------------------------------------------------------------------------------- |
| 104 |
// Small utilities |
| 105 |
// ----------------------------------------------------------------------------------------------------------------- |
| 106 |
|
| 107 |
/** |
| 108 |
* Debounce wrapper. |
| 109 |
* |
| 110 |
* @param {Function} fn |
| 111 |
* @param {number} ms |
| 112 |
* @returns {Function} |
| 113 |
*/ |
| 114 |
function debounce(fn, ms) { |
| 115 |
var t; |
| 116 |
return function () { |
| 117 |
var a = arguments; |
| 118 |
clearTimeout( t ); |
| 119 |
t = setTimeout( function () { |
| 120 |
fn.apply( null, a ); |
| 121 |
}, ms ); |
| 122 |
}; |
| 123 |
} |
| 124 |
|
| 125 |
/** |
| 126 |
* Wait until calendar API is present in the window (e.g., wpbc_calendar_show). |
| 127 |
* |
| 128 |
* @param {Function} cb Callback when ready. |
| 129 |
* @param {number} max_tries Maximum retry attempts. |
| 130 |
* @param {number} delay_ms Delay between attempts. |
| 131 |
*/ |
| 132 |
function wait_until_api_ready(cb, max_tries, delay_ms) { |
| 133 |
var tries = 0; |
| 134 |
(function tick() { |
| 135 |
if ( typeof w.wpbc_calendar_show === 'function' ) { |
| 136 |
try { |
| 137 |
cb(); |
| 138 |
} catch ( e ) { |
| 139 |
dev.error( 'api_ready_cb', e ); |
| 140 |
} |
| 141 |
return; |
| 142 |
} |
| 143 |
if ( tries++ >= (max_tries || 40) ) { |
| 144 |
dev.log( 'calendar_api_not_ready_after_retries' ); |
| 145 |
return; |
| 146 |
} |
| 147 |
w.setTimeout( tick, delay_ms || 100 ); |
| 148 |
})(); |
| 149 |
} |
| 150 |
|
| 151 |
/** |
| 152 |
* Apply "months in row" class to container element. |
| 153 |
* |
| 154 |
* @param {Element} field_el Field root element (wrap). |
| 155 |
* @param {number} months 1..12 |
| 156 |
*/ |
| 157 |
function apply_months_class(field_el, months) { |
| 158 |
var cont = field_el ? field_el.querySelector( '.wpbc_cal_container' ) : null; |
| 159 |
if ( ! cont ) { |
| 160 |
return; |
| 161 |
} |
| 162 |
// Remove existing cal_month_num_* safely via classList. |
| 163 |
Array.from( cont.classList ).forEach( function (c) { |
| 164 |
if ( /^cal_month_num_\d+$/.test( c ) ) { |
| 165 |
cont.classList.remove( c ); |
| 166 |
} |
| 167 |
} ); |
| 168 |
cont.classList.add( 'cal_month_num_' + months ); |
| 169 |
} |
| 170 |
|
| 171 |
/** |
| 172 |
* Set secure parameters (nonce, user_id, locale) before any AJAX calls. |
| 173 |
*/ |
| 174 |
function set_secure_params() { |
| 175 |
try { |
| 176 |
if ( !(w._wpbc && typeof w._wpbc.set_secure_param === 'function') ) { |
| 177 |
return; |
| 178 |
} |
| 179 |
if ( Boot.nonce ) { |
| 180 |
w._wpbc.set_secure_param( 'nonce', String( Boot.nonce ) ); |
| 181 |
} |
| 182 |
if ( Boot.user_id != null ) { |
| 183 |
w._wpbc.set_secure_param( 'user_id', String( Boot.user_id ) ); |
| 184 |
} |
| 185 |
if ( Boot.locale ) { |
| 186 |
w._wpbc.set_secure_param( 'locale', String( Boot.locale ) ); |
| 187 |
} |
| 188 |
} catch ( e ) { |
| 189 |
dev.log( 'secure_params_skip', e ); |
| 190 |
} |
| 191 |
} |
| 192 |
|
| 193 |
/** |
| 194 |
* Builder canvas preview starts at next month so it does not mostly show past/unavailable dates near month end. |
| 195 |
* |
| 196 |
* @returns {number[]} [year, month] |
| 197 |
*/ |
| 198 |
function get_canvas_next_month_scroll_to() { |
| 199 |
var today_arr = null; |
| 200 |
|
| 201 |
try { |
| 202 |
if ( w._wpbc && typeof w._wpbc.get_other_param === 'function' ) { |
| 203 |
today_arr = w._wpbc.get_other_param( 'today_arr' ); |
| 204 |
} |
| 205 |
} catch ( e ) { |
| 206 |
} |
| 207 |
|
| 208 |
var base_date = new Date(); |
| 209 |
if ( today_arr && today_arr.length >= 2 ) { |
| 210 |
base_date = new Date( Number( today_arr[0] ), Number( today_arr[1] ) - 1, 1 ); |
| 211 |
} else { |
| 212 |
base_date.setDate( 1 ); |
| 213 |
} |
| 214 |
|
| 215 |
base_date.setMonth( base_date.getMonth() + 1 ); |
| 216 |
|
| 217 |
return [ base_date.getFullYear(), base_date.getMonth() + 1 ]; |
| 218 |
} |
| 219 |
|
| 220 |
/** |
| 221 |
* Push calendar environment parameters for a specific resource and months count. |
| 222 |
* We *directly* set parameters (no global polyfill) to keep this file self-contained. |
| 223 |
* |
| 224 |
* @param {number} rid |
| 225 |
* @param {number} months |
| 226 |
*/ |
| 227 |
function set_calendar_params(rid, months) { |
| 228 |
var L = Boot || {}; |
| 229 |
try { |
| 230 |
if ( w._wpbc && typeof w._wpbc.balancer__set_max_threads === 'function' ) { |
| 231 |
w._wpbc.balancer__set_max_threads( Number( L.balancer_max_threads || 1 ) ); |
| 232 |
} |
| 233 |
} catch ( e ) { |
| 234 |
} |
| 235 |
|
| 236 |
function set_param(k, v) { |
| 237 |
try { |
| 238 |
if ( w._wpbc && typeof w._wpbc.calendar__set_param_value === 'function' ) { |
| 239 |
w._wpbc.calendar__set_param_value( rid, k, v ); |
| 240 |
} |
| 241 |
} catch ( e ) { |
| 242 |
} |
| 243 |
} |
| 244 |
|
| 245 |
if ( L.booking_max_monthes_in_calendar != null ) { |
| 246 |
set_param( 'booking_max_monthes_in_calendar', String( L.booking_max_monthes_in_calendar ) ); |
| 247 |
} |
| 248 |
if ( L.booking_start_day_weeek != null ) { |
| 249 |
set_param( 'booking_start_day_weeek', String( L.booking_start_day_weeek ) ); |
| 250 |
} |
| 251 |
set_param( 'calendar_number_of_months', String( months ) ); |
| 252 |
set_param( 'calendar_scroll_to', get_canvas_next_month_scroll_to() ); |
| 253 |
|
| 254 |
if ( L.booking_date_format ) { |
| 255 |
set_param( 'booking_date_format', String( L.booking_date_format ) ); |
| 256 |
} |
| 257 |
if ( L.booking_time_format ) { |
| 258 |
set_param( 'booking_time_format', String( L.booking_time_format ) ); |
| 259 |
} |
| 260 |
|
| 261 |
var ds = L.days_selection || {}; |
| 262 |
set_param( 'days_select_mode', String( ds.days_select_mode || 'multiple' ) ); |
| 263 |
set_param( 'fixed__days_num', Number( ds.fixed__days_num || 0 ) ); |
| 264 |
if ( ds.fixed__week_days__start != null ) { |
| 265 |
set_param( 'fixed__week_days__start', [ String( ds.fixed__week_days__start ) ] ); |
| 266 |
} |
| 267 |
set_param( 'dynamic__days_min', Number( ds.dynamic__days_min || 0 ) ); |
| 268 |
set_param( 'dynamic__days_max', Number( ds.dynamic__days_max || 0 ) ); |
| 269 |
if ( ds.dynamic__days_specific != null ) { |
| 270 |
var arr = String( ds.dynamic__days_specific || '' ).split( /\s*,\s*/ ).filter( Boolean ).map( Number ); |
| 271 |
set_param( 'dynamic__days_specific', arr ); |
| 272 |
} |
| 273 |
if ( ds.dynamic__week_days__start != null ) { |
| 274 |
set_param( 'dynamic__week_days__start', [ String( ds.dynamic__week_days__start ) ] ); |
| 275 |
} |
| 276 |
|
| 277 |
try { |
| 278 |
if ( typeof w.wpbc__conditions__SAVE_INITIAL__days_selection_params__bm === 'function' ) { |
| 279 |
w.wpbc__conditions__SAVE_INITIAL__days_selection_params__bm( rid ); |
| 280 |
} |
| 281 |
} catch ( e ) { |
| 282 |
} |
| 283 |
} |
| 284 |
|
| 285 |
/** |
| 286 |
* Extract sanitized rid and months from data or DOM. |
| 287 |
* |
| 288 |
* @param {Element} field_el |
| 289 |
* @param {object} data |
| 290 |
* @returns {{rid:number, months:number}} |
| 291 |
*/ |
| 292 |
function get_rid_and_months(field_el, data) { |
| 293 |
var rid = 1; |
| 294 |
var months = 1; |
| 295 |
|
| 296 |
// NEW: prefer dataset on the wrap (Inspector edits land here first) |
| 297 |
var wrap = field_el ? (field_el.closest && field_el.closest('.wpbc_calendar_wraper')) || field_el : null; |
| 298 |
if (wrap && wrap.dataset) { |
| 299 |
if (wrap.dataset.resource_id != null && wrap.dataset.resource_id !== '') { |
| 300 |
rid = Number(wrap.dataset.resource_id); |
| 301 |
} |
| 302 |
if (wrap.dataset.months != null && wrap.dataset.months !== '') { |
| 303 |
months = Number(wrap.dataset.months); |
| 304 |
} |
| 305 |
} |
| 306 |
|
| 307 |
if ( data && data.resource_id != null ) { |
| 308 |
rid = Number( data.resource_id ); |
| 309 |
} |
| 310 |
if ( data && data.months != null ) { |
| 311 |
months = Number( data.months ); |
| 312 |
} |
| 313 |
|
| 314 |
if ( !data ) { |
| 315 |
// Fallbacks from DOM when data object is not provided. |
| 316 |
var n = field_el ? field_el.querySelector( '[id^="calendar_booking"]' ) : null; |
| 317 |
if ( n && n.id ) { |
| 318 |
var m1 = n.id.match( /calendar_booking(\d+)/ ); |
| 319 |
if ( m1 ) { |
| 320 |
rid = Number( m1[1] ); |
| 321 |
} |
| 322 |
} |
| 323 |
var cont = field_el ? field_el.querySelector( '.wpbc_cal_container' ) : null; |
| 324 |
if ( cont && cont.className ) { |
| 325 |
var m2 = cont.className.match( /cal_month_num_(\d+)/ ); |
| 326 |
if ( m2 ) { |
| 327 |
months = Number( m2[1] ); |
| 328 |
} |
| 329 |
} |
| 330 |
} |
| 331 |
|
| 332 |
// FixIn: 2026-03-07 Override to always get the default booking resource, for specific user in MU or for existed resourecs ! |
| 333 |
rid = resolve_effective_resource_id( rid ); |
| 334 |
|
| 335 |
months = isFinite( months ) ? Math.max( 1, Math.min( 12, Math.floor( months ) ) ) : 1; |
| 336 |
|
| 337 |
return { rid: rid, months: months }; |
| 338 |
} |
| 339 |
|
| 340 |
// ----------------------------------------------------------------------------------------------------------------- |
| 341 |
// Minimal preview bootstrap — load/refresh a calendar for a field |
| 342 |
// ----------------------------------------------------------------------------------------------------------------- |
| 343 |
|
| 344 |
|
| 345 |
/** |
| 346 |
* Initialize (or update) a calendar preview for a given field element. |
| 347 |
* |
| 348 |
* @param {Element} field_el - calendar field element (wrap) |
| 349 |
* @param {object} data - builder field data (optional) |
| 350 |
* @param {boolean} should_reload_data - force AJAX data reload |
| 351 |
*/ |
| 352 |
function init_field(field_el, data, should_reload_data = true) { |
| 353 |
if ( !field_el ) { |
| 354 |
return; |
| 355 |
} |
| 356 |
|
| 357 |
var pair = get_rid_and_months( field_el, data ); |
| 358 |
var rid = pair.rid; |
| 359 |
|
| 360 |
|
| 361 |
wait_until_api_ready( function () { |
| 362 |
|
| 363 |
// 1) Always (re)apply local UI — needed after DOM moves. |
| 364 |
apply_months_class( field_el, pair.months ); |
| 365 |
set_calendar_params( rid, pair.months ); |
| 366 |
|
| 367 |
try { |
| 368 |
w.wpbc_calendar_show( String( rid ) ); |
| 369 |
} catch ( e1 ) { |
| 370 |
dev.error( 'wpbc_calendar_show', e1 ); |
| 371 |
} |
| 372 |
set_secure_params(); |
| 373 |
|
| 374 |
// 2) Decide on AJAX strictly by RID state + explicit request. |
| 375 |
var first_time_for_rid = !rid_loaded_cache[rid]; |
| 376 |
// Respect caller intent. Reload only if explicitly asked OR first time for this RID. |
| 377 |
var do_reload = !!should_reload_data || first_time_for_rid; |
| 378 |
|
| 379 |
try { |
| 380 |
if ( typeof w.wpbc_calendar__load_data__ajx === 'function' ) { |
| 381 |
if ( do_reload ) { |
| 382 |
w.wpbc_calendar__load_data__ajx( { |
| 383 |
'resource_id' : rid, |
| 384 |
'booking_hash' : '', |
| 385 |
'request_uri' : Boot.request_uri || (w.location ? String( w.location.pathname + w.location.search ) : ''), |
| 386 |
'custom_form' : 'standard', |
| 387 |
'aggregate_resource_id_str': '', |
| 388 |
'aggregate_type' : 'all' |
| 389 |
} ); |
| 390 |
// Mark this rid as loaded so subsequent DOM churn uses look-only refresh. |
| 391 |
rid_loaded_cache[rid] = true; |
| 392 |
} else if ( typeof w.wpbc_calendar__update_look === 'function' ) { |
| 393 |
w.wpbc_calendar__update_look( rid ); |
| 394 |
} |
| 395 |
} |
| 396 |
} catch ( e2 ) { |
| 397 |
dev.log( 'calendar_data_load_skip', e2 ); |
| 398 |
} |
| 399 |
|
| 400 |
// Track last config for soft dedupe (UI churn); keep per-element. |
| 401 |
try { field_el.setAttribute('data-wpbc-cal-init', '1'); field_el.setAttribute('data-wpbc-cal-loaded-rid', String(rid)); } catch (e3) {} |
| 402 |
try { apply_calendar_legend_preview_from_controls(); } catch (e4) {} |
| 403 |
|
| 404 |
}, 40, 100 ); |
| 405 |
} |
| 406 |
|
| 407 |
|
| 408 |
// ----------------------------------------------------------------------------------------------------------------- |
| 409 |
// FOCS: tiny bus & DOM helpers (snake format) |
| 410 |
// ----------------------------------------------------------------------------------------------------------------- |
| 411 |
const defer_fn = (fn) => (typeof w.requestAnimationFrame === 'function' ? w.requestAnimationFrame( fn ) : setTimeout( fn, 0 )); |
| 412 |
|
| 413 |
function find_calendar_wrap(el) { |
| 414 |
if ( ! el ) { |
| 415 |
return null; |
| 416 |
} |
| 417 |
return (el.closest && el.closest( '.wpbc_calendar_wraper' )) || el; |
| 418 |
} |
| 419 |
|
| 420 |
function is_calendar_wrap(el) { |
| 421 |
var wrap = find_calendar_wrap( el ); |
| 422 |
return !!(wrap && ( |
| 423 |
(wrap.dataset && wrap.dataset.type === 'calendar') || |
| 424 |
(wrap.querySelector && wrap.querySelector( '[id^="calendar_booking"]' )) |
| 425 |
)); |
| 426 |
} |
| 427 |
|
| 428 |
function look_refresh(target, opts) { |
| 429 |
var o = opts || {}; |
| 430 |
var wrap = find_calendar_wrap( target ); |
| 431 |
if ( ! wrap || ! document.contains( wrap ) ) { |
| 432 |
return; |
| 433 |
} |
| 434 |
defer_fn( function () { |
| 435 |
init_field( wrap, null, !!o.reload ); |
| 436 |
} ); |
| 437 |
} |
| 438 |
|
| 439 |
function on_event(type, handler) { |
| 440 |
document.addEventListener( type, handler ); |
| 441 |
} |
| 442 |
|
| 443 |
function get_calendar_skin_url_from_select(select_el) { |
| 444 |
if ( ! select_el || ! select_el.options ) { |
| 445 |
return ''; |
| 446 |
} |
| 447 |
|
| 448 |
var selected_option = select_el.options[ select_el.selectedIndex ]; |
| 449 |
if ( selected_option ) { |
| 450 |
return String( selected_option.getAttribute( 'data-wpbc-calendar-skin-url' ) || select_el.value || '' ); |
| 451 |
} |
| 452 |
|
| 453 |
return String( select_el.value || '' ); |
| 454 |
} |
| 455 |
|
| 456 |
function apply_calendar_skin_from_select(select_el) { |
| 457 |
var skin_url = get_calendar_skin_url_from_select( select_el ); |
| 458 |
if ( ! skin_url || typeof w.wpbc__calendar__change_skin !== 'function' ) { |
| 459 |
return; |
| 460 |
} |
| 461 |
|
| 462 |
try { |
| 463 |
w.wpbc__calendar__change_skin( skin_url ); |
| 464 |
} catch ( e ) { |
| 465 |
dev.error( 'calendar_skin_preview', e ); |
| 466 |
} |
| 467 |
} |
| 468 |
|
| 469 |
function bind_calendar_skin_preview_events() { |
| 470 |
document.addEventListener( 'change', function (e) { |
| 471 |
var target = e && e.target; |
| 472 |
if ( target && target.matches && target.matches( '.js-wpbc-bfb-calendar-skin' ) ) { |
| 473 |
apply_calendar_skin_from_select( target ); |
| 474 |
} |
| 475 |
}, true ); |
| 476 |
} |
| 477 |
|
| 478 |
function escape_html(s) { |
| 479 |
return String( s == null ? '' : s ) |
| 480 |
.replace( /&/g, '&' ) |
| 481 |
.replace( /</g, '<' ) |
| 482 |
.replace( />/g, '>' ) |
| 483 |
.replace( /"/g, '"' ) |
| 484 |
.replace( /'/g, ''' ); |
| 485 |
} |
| 486 |
|
| 487 |
var legend_item_option_map = { |
| 488 |
available : { show: 'booking_legend_is_show_item_available', text: 'booking_legend_text_for_item_available' }, |
| 489 |
pending : { show: 'booking_legend_is_show_item_pending', text: 'booking_legend_text_for_item_pending' }, |
| 490 |
approved : { show: 'booking_legend_is_show_item_approved', text: 'booking_legend_text_for_item_approved' }, |
| 491 |
partially : { show: 'booking_legend_is_show_item_partially', text: 'booking_legend_text_for_item_partially' }, |
| 492 |
unavailable: { show: 'booking_legend_is_show_item_unavailable', text: 'booking_legend_text_for_item_unavailable' } |
| 493 |
}; |
| 494 |
|
| 495 |
function get_checkbox_value_from_id(id, fallback) { |
| 496 |
var el = document.getElementById( id ); |
| 497 |
if ( el ) { |
| 498 |
return el.checked ? 'On' : 'Off'; |
| 499 |
} |
| 500 |
return fallback === 'On' ? 'On' : 'Off'; |
| 501 |
} |
| 502 |
|
| 503 |
function get_text_value_from_id(id, fallback) { |
| 504 |
var el = document.getElementById( id ); |
| 505 |
if ( el ) { |
| 506 |
return String( el.value || '' ); |
| 507 |
} |
| 508 |
return String( fallback || '' ); |
| 509 |
} |
| 510 |
|
| 511 |
function get_calendar_legend_values_from_controls() { |
| 512 |
var boot_legend = Boot.calendar_legend || {}; |
| 513 |
var boot_items = boot_legend.items || {}; |
| 514 |
var values = { |
| 515 |
show_legend : get_checkbox_value_from_id( 'booking_is_show_legend', boot_legend.show_legend ), |
| 516 |
show_numbers: get_checkbox_value_from_id( 'booking_legend_is_show_numbers', boot_legend.show_numbers ), |
| 517 |
is_vertical : get_checkbox_value_from_id( 'booking_legend_is_vertical', boot_legend.is_vertical ), |
| 518 |
items : {} |
| 519 |
}; |
| 520 |
|
| 521 |
Object.keys( legend_item_option_map ).forEach( function (item_key) { |
| 522 |
var map = legend_item_option_map[item_key]; |
| 523 |
var boot_item = boot_items[item_key] || {}; |
| 524 |
values.items[item_key] = { |
| 525 |
show : get_checkbox_value_from_id( map.show, boot_item.show ), |
| 526 |
title: get_text_value_from_id( map.text, boot_item.title || boot_item.placeholder || '' ) |
| 527 |
}; |
| 528 |
} ); |
| 529 |
|
| 530 |
return values; |
| 531 |
} |
| 532 |
|
| 533 |
function extract_legend_item_html(template_html) { |
| 534 |
var tmp = document.createElement( 'div' ); |
| 535 |
tmp.innerHTML = String( template_html || '' ); |
| 536 |
var item = tmp.querySelector( '.wpdev_hint_with_text' ); |
| 537 |
return item ? item.outerHTML : ''; |
| 538 |
} |
| 539 |
|
| 540 |
function render_calendar_legend_html(values) { |
| 541 |
var boot_legend = Boot.calendar_legend || {}; |
| 542 |
var boot_items = boot_legend.items || {}; |
| 543 |
var order = Array.isArray( boot_legend.items_order ) ? boot_legend.items_order : Object.keys( legend_item_option_map ); |
| 544 |
var html = ''; |
| 545 |
|
| 546 |
if ( ! values || values.show_legend !== 'On' ) { |
| 547 |
return ''; |
| 548 |
} |
| 549 |
|
| 550 |
order.forEach( function (item_key) { |
| 551 |
var item_values = values.items[item_key] || {}; |
| 552 |
var boot_item = boot_items[item_key] || {}; |
| 553 |
var template = (values.show_numbers === 'On') ? boot_item.template_number : boot_item.template_blank; |
| 554 |
var item_html = extract_legend_item_html( template ); |
| 555 |
|
| 556 |
if ( item_values.show !== 'On' || ! item_html ) { |
| 557 |
return; |
| 558 |
} |
| 559 |
|
| 560 |
item_html = item_html.replace( /__WPBC_LEGEND_TITLE__/g, escape_html( item_values.title || boot_item.placeholder || '' ) ); |
| 561 |
html += item_html; |
| 562 |
} ); |
| 563 |
|
| 564 |
if ( ! html ) { |
| 565 |
return ''; |
| 566 |
} |
| 567 |
|
| 568 |
return '<div class="block_hints datepick ' + (values.is_vertical === 'On' ? ' block_hints_vertical ' : '') + '">' + html + '</div>'; |
| 569 |
} |
| 570 |
|
| 571 |
function store_calendar_legend_values_in_boot(values) { |
| 572 |
Boot.calendar_legend = Boot.calendar_legend || {}; |
| 573 |
Boot.calendar_legend.items = Boot.calendar_legend.items || {}; |
| 574 |
Boot.calendar_legend.show_legend = values.show_legend; |
| 575 |
Boot.calendar_legend.show_numbers = values.show_numbers; |
| 576 |
Boot.calendar_legend.is_vertical = values.is_vertical; |
| 577 |
|
| 578 |
Object.keys( values.items || {} ).forEach( function (item_key) { |
| 579 |
Boot.calendar_legend.items[item_key] = Boot.calendar_legend.items[item_key] || {}; |
| 580 |
Boot.calendar_legend.items[item_key].show = values.items[item_key].show; |
| 581 |
Boot.calendar_legend.items[item_key].title = values.items[item_key].title; |
| 582 |
} ); |
| 583 |
} |
| 584 |
|
| 585 |
function apply_calendar_legend_preview_from_controls() { |
| 586 |
var values = get_calendar_legend_values_from_controls(); |
| 587 |
var html = render_calendar_legend_html( values ); |
| 588 |
var nodes = document.querySelectorAll( '.js-wpbc-bfb-calendar-legend-preview' ); |
| 589 |
|
| 590 |
store_calendar_legend_values_in_boot( values ); |
| 591 |
|
| 592 |
for ( var i = 0; i < nodes.length; i++ ) { |
| 593 |
nodes[i].innerHTML = html; |
| 594 |
} |
| 595 |
} |
| 596 |
|
| 597 |
function sync_calendar_legend_inspector_visibility() { |
| 598 |
var main = document.getElementById( 'booking_is_show_legend' ); |
| 599 |
var options = document.querySelector( '.js-wpbc-bfb-calendar-legend-options' ); |
| 600 |
if ( ! main || ! options ) { |
| 601 |
return; |
| 602 |
} |
| 603 |
options.style.display = main.checked ? '' : 'none'; |
| 604 |
} |
| 605 |
|
| 606 |
function bind_calendar_legend_preview_events() { |
| 607 |
function on_inspector_render() { |
| 608 |
sync_calendar_legend_inspector_visibility(); |
| 609 |
} |
| 610 |
|
| 611 |
document.addEventListener( 'change', function (e) { |
| 612 |
var target = e && e.target; |
| 613 |
if ( target && target.matches && target.matches( '.js-wpbc-bfb-calendar-legend-control' ) ) { |
| 614 |
sync_calendar_legend_inspector_visibility(); |
| 615 |
apply_calendar_legend_preview_from_controls(); |
| 616 |
} |
| 617 |
}, true ); |
| 618 |
|
| 619 |
document.addEventListener( 'input', debounce( function (e) { |
| 620 |
var target = e && e.target; |
| 621 |
if ( target && target.matches && target.matches( '.js-wpbc-bfb-calendar-legend-control' ) ) { |
| 622 |
apply_calendar_legend_preview_from_controls(); |
| 623 |
} |
| 624 |
}, 150 ), true ); |
| 625 |
|
| 626 |
document.addEventListener( 'wpbc_bfb_inspector_ready', on_inspector_render ); |
| 627 |
document.addEventListener( 'wpbc_bfb_inspector_render', on_inspector_render ); |
| 628 |
} |
| 629 |
|
| 630 |
/** |
| 631 |
* Initialize all calendars present in the current Builder preview panel. |
| 632 |
*/ |
| 633 |
function init_all_on_page(should_reload_data = true) { |
| 634 |
var scope = w.document.querySelector( '#wpbc_bfb__pages_panel' ) || w.document; |
| 635 |
var nodes = scope.querySelectorAll( '[id^="calendar_booking"]' ); |
| 636 |
for ( var i = 0; i < nodes.length; i++ ) { |
| 637 |
var node = nodes[i]; |
| 638 |
var field_el = node.closest( '.wpbc_calendar_wraper' ) || node.parentElement || node; |
| 639 |
init_field( field_el, null, should_reload_data ); |
| 640 |
} |
| 641 |
} |
| 642 |
|
| 643 |
|
| 644 |
/** |
| 645 |
* Listen to builder’s bus (events bubble on document via Core.WPBC_BFB_EventBus) |
| 646 |
*/ |
| 647 |
function bind_builder_bus_events() { |
| 648 |
|
| 649 |
// Alias events once. |
| 650 |
var EV = Core.WPBC_BFB_Events || {}; |
| 651 |
|
| 652 |
// 1) First structure ready -> look-only render. |
| 653 |
on_event( EV.STRUCTURE_LOADED, function () { |
| 654 |
init_all_on_page( false ); |
| 655 |
} ); |
| 656 |
|
| 657 |
// 2) Field/section added -> initialize that node if it’s a calendar (force reload). |
| 658 |
on_event( EV.FIELD_ADD, function (e) { |
| 659 |
var el = e && e.detail && e.detail.el; |
| 660 |
if ( ! el ) { |
| 661 |
// Defensive fallback: if emitter didn’t provide the element, refresh all calendars (look-only). |
| 662 |
return defer_fn(function () { init_all_on_page(false); }); |
| 663 |
} |
| 664 |
if ( is_calendar_wrap( el ) ) { |
| 665 |
look_refresh( el, { reload: true } ); // builder just inserted; So do Ajax fetch data. |
| 666 |
} |
| 667 |
} ); |
| 668 |
|
| 669 |
|
| 670 |
// 3) Generic structure changes. |
| 671 |
on_event( EV.STRUCTURE_CHANGE, function (e) { |
| 672 |
var d = (e && e.detail) || {}; |
| 673 |
var reason = d.reason || ''; |
| 674 |
|
| 675 |
// Heavy operations -> cheap look refresh for all calendars, no data reload. |
| 676 |
if ( reason === 'sort-update' || reason === 'section-move' || reason === 'delete' ) { |
| 677 |
return defer_fn( function () { |
| 678 |
init_all_on_page( false ); |
| 679 |
} ); |
| 680 |
} |
| 681 |
|
| 682 |
// Only care about calendar targets. |
| 683 |
if ( ! is_calendar_wrap( d.el ) ) return; |
| 684 |
|
| 685 |
// Only reload data on committed resource_id changes. |
| 686 |
var k = d.key || ''; |
| 687 |
var phase = (d.phase || '').toLowerCase(); // set by the emitter above. |
| 688 |
if ( k === 'resource_id' && phase !== 'change' ) { |
| 689 |
return; // Skip on second input handler: ins.addEventListener( 'input', handler, true ); in ../includes/page-form-builder/__js/core/bfb-ui.js //. |
| 690 |
} |
| 691 |
// Replace to TRUE, if needs to FORCE ajax reload of calendar data of resource ID change. |
| 692 |
var must_reload = (k === 'resource_id' && phase === 'change') |
| 693 |
? false |
| 694 |
: false; |
| 695 |
look_refresh( d.el, { reload: must_reload } ); |
| 696 |
} ); |
| 697 |
} |
| 698 |
|
| 699 |
// ----------------------------------------------------------------------------------------------------------------- |
| 700 |
// Field Renderer (class-like, extendable) |
| 701 |
// ----------------------------------------------------------------------------------------------------------------- |
| 702 |
class WPBC_BFB_Field_Calendar extends (Field_Base || class {}) { |
| 703 |
|
| 704 |
static template_id = 'wpbc-bfb-field-calendar'; // Underscore template id from PHP printer. |
| 705 |
static kind = 'calendar'; |
| 706 |
|
| 707 |
/** |
| 708 |
* Default props — keep in sync with PHP schema defaults. |
| 709 |
*/ |
| 710 |
static get_defaults() { |
| 711 |
return { |
| 712 |
type : 'calendar', |
| 713 |
label : 'Select Date', |
| 714 |
resource_id: resolve_effective_resource_id( 1 ), |
| 715 |
months : 1, |
| 716 |
name : '', |
| 717 |
html_id : '', |
| 718 |
cssclass : '', |
| 719 |
help : '', |
| 720 |
min_width : '250px' |
| 721 |
}; |
| 722 |
} |
| 723 |
|
| 724 |
/** |
| 725 |
* Called by the Builder after the field is dropped/loaded/previewed. |
| 726 |
* We (re)initialize the preview for the specific element. |
| 727 |
* |
| 728 |
* @param {object} data |
| 729 |
* @param {Element} field_el |
| 730 |
* @param {{context:string}} ctx |
| 731 |
*/ |
| 732 |
static on_field_drop(data, field_el, ctx) { |
| 733 |
try { |
| 734 |
init_field( field_el, data, false ); |
| 735 |
} catch ( e ) { |
| 736 |
dev.error( 'WPBC_BFB_Field_Calendar.on_field_drop', e ); |
| 737 |
} |
| 738 |
} |
| 739 |
|
| 740 |
/** |
| 741 |
* Hydrate after preview render (no rebuild). Called by builder.render_preview(). |
| 742 |
*/ |
| 743 |
static hydrate(field_el, data, ctx) { |
| 744 |
try { |
| 745 |
init_field( field_el, data, false ); |
| 746 |
} catch ( e ) { |
| 747 |
dev.error( 'WPBC_BFB_Field_Calendar.hydrate', e ); |
| 748 |
} |
| 749 |
} |
| 750 |
|
| 751 |
} |
| 752 |
|
| 753 |
// Register pack renderer with the central registry. |
| 754 |
try { |
| 755 |
Registry.register( 'calendar', WPBC_BFB_Field_Calendar ); |
| 756 |
} catch ( e ) { |
| 757 |
dev.error( 'WPBC_BFB_Field_Calendar.register', e ); |
| 758 |
} |
| 759 |
|
| 760 |
// Bootstrap: on DOM ready, run a first scan and wire light reactivity. |
| 761 |
function on_ready(fn) { |
| 762 |
if ( w.document.readyState === 'interactive' || w.document.readyState === 'complete' ) { |
| 763 |
try { |
| 764 |
fn(); |
| 765 |
} catch ( e ) { |
| 766 |
} |
| 767 |
} else { |
| 768 |
w.document.addEventListener( 'DOMContentLoaded', function () { |
| 769 |
try { |
| 770 |
fn(); |
| 771 |
} catch ( e ) { |
| 772 |
} |
| 773 |
} ); |
| 774 |
} |
| 775 |
} |
| 776 |
|
| 777 |
on_ready( function () { |
| 778 |
setTimeout( function () { |
| 779 |
init_all_on_page( false ); |
| 780 |
bind_builder_bus_events(); |
| 781 |
bind_calendar_skin_preview_events(); |
| 782 |
bind_calendar_legend_preview_events(); |
| 783 |
sync_calendar_legend_inspector_visibility(); |
| 784 |
}, 0 ); |
| 785 |
} ); |
| 786 |
|
| 787 |
// Optional export (handy for debugging). |
| 788 |
w.WPBC_BFB_Field_Calendar = WPBC_BFB_Field_Calendar; |
| 789 |
|
| 790 |
|
| 791 |
// -- Export for "Booking Form" ------------------------------------------------------------------------------------ |
| 792 |
|
| 793 |
/** |
| 794 |
* Register the "calendar" exporter (lazy: tries now, or waits for exporter-ready). |
| 795 |
* Output: |
| 796 |
* • [calendar] only (no rid/months/class/id tokens inside) |
| 797 |
* • If html_id / cssclass set → wrap shortcode in <span ... style="flex:1;">…</span> |
| 798 |
* • Label above (when addLabels !== false). |
| 799 |
* Help text is appended by WPBC_BFB_Exporter.render_field_node(). |
| 800 |
* |
| 801 |
* Booking Form exporter callback (Advanced Form shortcode). |
| 802 |
* |
| 803 |
* This callback is registered per field type via: |
| 804 |
* WPBC_BFB_Exporter.register( 'shortcode_name', callback ) |
| 805 |
* |
| 806 |
* Core call site (builder-exporter.js): |
| 807 |
* WPBC_BFB_Exporter.run_registered_exporter( field, io, cfg, once, ctx ) |
| 808 |
* → callback( field, emit, { io, cfg, once, ctx, core } ); |
| 809 |
* |
| 810 |
* @callback WPBC_BFB_ExporterCallback |
| 811 |
* @param {Object} field |
| 812 |
* Normalized field data coming from the Builder structure. |
| 813 |
* - field.type {string} Field type, e.g. "text". |
| 814 |
* - field.name {string} Name as stored on the canvas (already validated). |
| 815 |
* - field.id / html_id {string} Optional HTML id / user-visible id. |
| 816 |
* - field.label {string} Visible label in the form (may be empty). |
| 817 |
* - field.placeholder {string} Placeholder text (may be empty). |
| 818 |
* - field.required {boolean|number|string} "truthy" if required. |
| 819 |
* - field.cssclass {string} Extra CSS classes entered in Inspector. |
| 820 |
* - field.default_value {string} Default text value. |
| 821 |
* - field.options {Array} Only for option-based fields (select, checkbox, etc.). |
| 822 |
* - ... (Any other pack-specific props are also present.) |
| 823 |
* |
| 824 |
* @param {function(string):void} emit |
| 825 |
* Emits one line/fragment into the export buffer. |
| 826 |
* - Each call corresponds to one `push()` in the core exporter. |
| 827 |
* - For multi-line output (e.g. label + shortcode), call `emit()` multiple times: |
| 828 |
* emit('<l>Label</l>'); |
| 829 |
* emit('<br>[text* name ...]'); |
| 830 |
* |
| 831 |
* @param {Object} [extras] |
| 832 |
* Extra context passed by the core exporter. |
| 833 |
* |
| 834 |
* @param {Object} [extras.io] |
| 835 |
* Low-level writer used internally by the core. |
| 836 |
* Normally you do NOT need it in packs — prefer `emit()`. |
| 837 |
* - extras.io.open(str) → open a nested block (increments indentation). |
| 838 |
* - extras.io.close(str) → close a block (decrements indentation). |
| 839 |
* - extras.io.push(str) → push raw line (used by `emit()`). |
| 840 |
* - extras.io.blank() → push an empty line. |
| 841 |
* |
| 842 |
* @param {Object} [extras.cfg] |
| 843 |
* Export configuration (same object passed to WPBC_BFB_Exporter.export_form()). |
| 844 |
* Useful flags for field packs: |
| 845 |
* - extras.cfg.addLabels {boolean} Default: true. |
| 846 |
* If false, packs should NOT emit <l>Label</l> lines. |
| 847 |
* - extras.cfg.newline {string} Newline separator (usually "\n"). |
| 848 |
* - extras.cfg.gapPercent{number} Layout gap (used only by section/column logic). |
| 849 |
* |
| 850 |
* @param {Object} [extras.once] |
| 851 |
* Shared "once-per-form" guards across all fields. |
| 852 |
* Counters are incremented by some field types (captcha, coupon, etc.). |
| 853 |
* Typical shape: |
| 854 |
* - extras.once.captcha {number} |
| 855 |
* - extras.once.country {number} |
| 856 |
* - extras.once.coupon {number} |
| 857 |
* - extras.once.cost_corrections {number} |
| 858 |
* - extras.once.submit {number} |
| 859 |
* |
| 860 |
* Text field usually does not touch this object, but other packs can use it |
| 861 |
* to skip duplicates (e.g. only the first [coupon] per form is exported). |
| 862 |
* |
| 863 |
* @param {Object} [extras.ctx] |
| 864 |
* Shared export context for the entire form. |
| 865 |
* Currently: |
| 866 |
* - extras.ctx.usedIds {Set<string>} |
| 867 |
* Set of HTML/shortcode IDs already used in this export. |
| 868 |
* Helpers like Exp.id_option(field, ctx) use it to ensure uniqueness. |
| 869 |
* |
| 870 |
* Packs normally just pass `ctx` into helpers (id_option, etc.) without |
| 871 |
* mutating it directly. |
| 872 |
* |
| 873 |
* @param {Object} [extras.core] |
| 874 |
* Reference to WPBC_BFB_Core passed from builder-exporter.js. |
| 875 |
* Primarily used to access sanitizers: |
| 876 |
* - extras.core.WPBC_BFB_Sanitize.escape_html(...) |
| 877 |
* - extras.core.WPBC_BFB_Sanitize.escape_for_shortcode(...) |
| 878 |
* - extras.core.WPBC_BFB_Sanitize.sanitize_html_name(...) |
| 879 |
* - etc. |
| 880 |
*/ |
| 881 |
function export_shortcode_in_booking_form() { |
| 882 |
|
| 883 |
const Exp = w.WPBC_BFB_Exporter; |
| 884 |
if ( ! Exp || typeof Exp.register !== 'function' ) { return false; } |
| 885 |
if ( typeof Exp.has_exporter === 'function' && Exp.has_exporter( 'calendar' ) ) { return true; } |
| 886 |
|
| 887 |
// Use sanitize helpers from core (already loaded). |
| 888 |
const S = Core.WPBC_BFB_Sanitize || (w.WPBC_BFB_Core && w.WPBC_BFB_Core.WPBC_BFB_Sanitize) || {}; |
| 889 |
const esc = S.escape_html || (v => String( v )); |
| 890 |
const sid = S.sanitize_html_id || (v => String( v )); |
| 891 |
const scls = S.sanitize_css_classlist || (v => String( v )); |
| 892 |
|
| 893 |
/** |
| 894 |
* Per-field exporter for "calendar" in Advanced Form. |
| 895 |
* @type {WPBC_BFB_ExporterCallback} |
| 896 |
*/ |
| 897 |
Exp.register( 'calendar', (field, emit, extras = {}) => { |
| 898 |
|
| 899 |
const cfg = extras.cfg || {}; |
| 900 |
const ctx = extras.ctx; |
| 901 |
const usedIds = (ctx && ctx.usedIds instanceof Set) ? ctx.usedIds : null; |
| 902 |
const addLabels = cfg.addLabels !== false; |
| 903 |
|
| 904 |
// Optional wrapper attrs (id/class on outer span, not inside [calendar]). |
| 905 |
let html_id = field && field.html_id ? sid( String( field.html_id ) ) : ''; |
| 906 |
if ( html_id && usedIds ) { |
| 907 |
let u = html_id, i = 2; |
| 908 |
while ( usedIds.has( u ) ) { |
| 909 |
u = `${html_id}_${i++}`; |
| 910 |
} |
| 911 |
usedIds.add( u ); |
| 912 |
html_id = u; |
| 913 |
} |
| 914 |
|
| 915 |
const cls_raw = field && (field.cssclass_extra || field.cssclass || field.class) || ''; |
| 916 |
const cls = scls( String( cls_raw ) ); |
| 917 |
|
| 918 |
const hasWrap = !!( html_id || cls ); |
| 919 |
const wrapOpen = hasWrap |
| 920 |
? `<span${html_id ? ` id="${esc( html_id )}"` : ''}${cls ? ` class="${esc( cls )}"` : ''} style="flex:1;">` |
| 921 |
: ''; |
| 922 |
const wrapClose = hasWrap ? '</span>' : ''; |
| 923 |
|
| 924 |
// Calendar body is intentionally minimal; no rid/months/id/class tokens inside shortcode. |
| 925 |
const body = '[calendar]'; |
| 926 |
const label = (typeof field?.label === 'string') ? field.label.trim() : ''; |
| 927 |
|
| 928 |
if ( label && addLabels ) { |
| 929 |
emit( `<l>${esc( label )}</l>` ); |
| 930 |
emit( `<br>${wrapOpen}${body}${wrapClose}` ); |
| 931 |
} else { |
| 932 |
emit( `${wrapOpen}${body}${wrapClose}` ); |
| 933 |
} |
| 934 |
} ); |
| 935 |
|
| 936 |
return true; |
| 937 |
} |
| 938 |
|
| 939 |
// Try now; if exporter isn't ready yet, wait for one-shot event from builder-exporter. |
| 940 |
if ( ! export_shortcode_in_booking_form() ) { |
| 941 |
document.addEventListener( 'wpbc:bfb:exporter-ready', export_shortcode_in_booking_form, { once: true } ); |
| 942 |
} |
| 943 |
|
| 944 |
// -- Export for "Booking Data" ------------------------------------------------------------------------------------ |
| 945 |
|
| 946 |
/** |
| 947 |
* Register the "calendar" exporter for "Content of booking fields data". Produces e.g.: "<b>Dates</b>: |
| 948 |
* <f>[dates]</f><br>" |
| 949 |
* |
| 950 |
* Booking Data exporter callback ("Content of booking fields data"). Default output: <b>Label</b>: |
| 951 |
* <f>[field_name]</f><br> |
| 952 |
* |
| 953 |
* Registered per field type via: |
| 954 |
* WPBC_BFB_ContentExporter.register( 'shortcode_name', callback ) |
| 955 |
* |
| 956 |
* Core call site (builder-exporter.js): |
| 957 |
* WPBC_BFB_ContentExporter.run_registered_exporter( field, emit, { cfg, core } ); |
| 958 |
* |
| 959 |
* @callback WPBC_BFB_ContentExporterCallback |
| 960 |
* @param {Object} field |
| 961 |
* Normalized field data (same shape as in the main exporter). |
| 962 |
* Important properties for content templates: |
| 963 |
* - field.type {string} Field type, e.g. "text". |
| 964 |
* - field.name {string} Field name used as placeholder token. |
| 965 |
* - field.label {string} Human-readable label (may be empty). |
| 966 |
* - field.options {Array} For option-based fields (select, checkbox, radio, etc.). |
| 967 |
* - Other pack-specific props if needed. |
| 968 |
* |
| 969 |
* @param {function(string):void} emit |
| 970 |
* Emits a raw HTML fragment into the "Content of booking fields data" template. |
| 971 |
* Core will wrap everything once into: |
| 972 |
* <div class="standard-content-form"> |
| 973 |
* ... emitted fragments ... |
| 974 |
* </div> |
| 975 |
* |
| 976 |
* Typical usage pattern: |
| 977 |
* emit('<b>Label</b>: <f>[field_name]</f><br>'); |
| 978 |
* |
| 979 |
* In most cases, packs call the shared helper: |
| 980 |
* WPBC_BFB_ContentExporter.emit_line_bold_field(emit, label, token, cfg); |
| 981 |
* |
| 982 |
* @param {Object} [extras] |
| 983 |
* Additional context passed from run_registered_exporter(). |
| 984 |
* |
| 985 |
* @param {Object} [extras.cfg] |
| 986 |
* Content exporter configuration: |
| 987 |
* - extras.cfg.addLabels {boolean} Default: true. |
| 988 |
* If false, helper may omit the bold label part. |
| 989 |
* - extras.cfg.sep {string} Label separator, default ": ". |
| 990 |
* Example: "<b>Label</b>: " vs "<b>Label</b> – ". |
| 991 |
* - extras.cfg.newline {string} Newline separator when joining lines (usually "\n"). |
| 992 |
* |
| 993 |
* @param {Object} [extras.core] |
| 994 |
* Reference to WPBC_BFB_Core (same as in main exporter). |
| 995 |
* Usually not needed here, because: |
| 996 |
* - Sanitization and consistent rendering are already done via |
| 997 |
* WPBC_BFB_ContentExporter.emit_line_bold_field( ... ). |
| 998 |
*/ |
| 999 |
function export_shortcode_in_booking_data() { |
| 1000 |
|
| 1001 |
var C = w.WPBC_BFB_ContentExporter; |
| 1002 |
if ( ! C || typeof C.register !== 'function' ) { return false; } |
| 1003 |
if ( typeof C.has_exporter === 'function' && C.has_exporter( 'calendar' ) ) { return true; } |
| 1004 |
|
| 1005 |
C.register( 'calendar', function ( field, emit, extras ) { |
| 1006 |
|
| 1007 |
extras = extras || {}; |
| 1008 |
var cfg = extras.cfg || {}; |
| 1009 |
var label = (typeof field.label === 'string' && field.label.trim()) ? field.label.trim() : 'Dates'; |
| 1010 |
|
| 1011 |
// Reuse shared formatter from builder-exporter - e.g.: emit_line_bold_field(emit, label, token, cfg) -> emit(`<b>${S.escape_html(label)}</b>${sep}<f>[${token}]</f><br>`); . |
| 1012 |
// C.emit_line_bold_field( emit, label, 'dates', cfg ); |
| 1013 |
|
| 1014 |
if(0) { |
| 1015 |
// Defensive fallback: keep a simple, backward-compatible output. Just for help in using in other field packs. |
| 1016 |
var core_local = extras.core || Core || {}; |
| 1017 |
var S_local = core_local.WPBC_BFB_Sanitize || {}; |
| 1018 |
var esc = S_local.escape_html || function (s) { return String( s ); }; |
| 1019 |
|
| 1020 |
var sep = (cfg && typeof cfg.sep === 'string') ? cfg.sep : ': '; |
| 1021 |
var title = label ? '<b>' + esc( label ) + '</b>' + sep : ''; |
| 1022 |
emit( title + '<f>[dates]</f><br>' ); |
| 1023 |
} |
| 1024 |
} ); |
| 1025 |
|
| 1026 |
return true; |
| 1027 |
} |
| 1028 |
|
| 1029 |
if ( ! export_shortcode_in_booking_data() ) { |
| 1030 |
document.addEventListener( 'wpbc:bfb:content-exporter-ready', export_shortcode_in_booking_data, { once: true } ); |
| 1031 |
} |
| 1032 |
|
| 1033 |
})( window ); |
| 1034 |
|