| 1 |
// WPBC BFB Pack: Range Time (time-slots) |
| 2 |
// Uses shared helpers from WPBC_BFB_Core.Time |
| 3 |
// Version 1.7.2 — add Booking Form + Booking Data exporters for "rangetime" |
| 4 |
// File: ../includes/page-form-builder/field-packs/time-range/_out/field-rangetime-wptpl.js |
| 5 |
// 31.10.2025 13:54 |
| 6 |
|
| 7 |
(function (w, d) { |
| 8 |
'use strict'; |
| 9 |
|
| 10 |
var Core = w.WPBC_BFB_Core || {}; |
| 11 |
var Registry = Core.WPBC_BFB_Field_Renderer_Registry; |
| 12 |
var Base = Core.WPBC_BFB_Field_Base || null; |
| 13 |
var Time = (Core && Core.Time) ? Core.Time : null; |
| 14 |
|
| 15 |
if ( !Registry || typeof Registry.register !== 'function' || !Base || !Time ) { |
| 16 |
if (w._wpbc && w._wpbc.dev && w._wpbc.dev.error) { |
| 17 |
w._wpbc.dev.error('wpbc_bfb_field_rangetime', 'Missing Core registry/base/time-utils'); |
| 18 |
} |
| 19 |
return; |
| 20 |
} |
| 21 |
|
| 22 |
// --------------------------------------------------------------------- |
| 23 |
// Small helpers local to this pack |
| 24 |
// --------------------------------------------------------------------- |
| 25 |
function emit_inspector_change(el){ |
| 26 |
if (!el) return; |
| 27 |
if (el.__wpbc_emitting) return; |
| 28 |
el.__wpbc_emitting = true; |
| 29 |
try { |
| 30 |
if (w.jQuery) { w.jQuery(el).trigger('input').trigger('change'); } |
| 31 |
el.dispatchEvent(new Event('input', {bubbles:true})); |
| 32 |
el.dispatchEvent(new Event('change', {bubbles:true})); |
| 33 |
} finally { |
| 34 |
el.__wpbc_emitting = false; |
| 35 |
} |
| 36 |
Time.schedule_init_timeselector(); |
| 37 |
} |
| 38 |
|
| 39 |
function get_current_format(panel){ |
| 40 |
var fmt = 'ampm'; |
| 41 |
panel.querySelectorAll('.js-rt-label-fmt').forEach(function (r) { |
| 42 |
if (r.checked) fmt = (r.value === '24h') ? '24h' : 'ampm'; |
| 43 |
}); |
| 44 |
return fmt; |
| 45 |
} |
| 46 |
|
| 47 |
function update_gen_visibility(panel){ |
| 48 |
var fmt = get_current_format(panel); |
| 49 |
var g24 = panel.querySelector('.js-rt-24h'); |
| 50 |
var gam = panel.querySelector('.js-rt-ampm'); |
| 51 |
if (g24) { g24.style.display = (fmt === '24h') ? '' : 'none'; g24.hidden = (fmt !== '24h'); } |
| 52 |
if (gam) { gam.style.display = (fmt === 'ampm') ? '' : 'none'; gam.hidden = (fmt !== 'ampm'); } |
| 53 |
} |
| 54 |
|
| 55 |
function enforce_locked_name(panel){ |
| 56 |
var hidden = panel && panel.querySelector('.js-locked-name[data-inspector-key="name"]'); |
| 57 |
if (!hidden) return; |
| 58 |
hidden.value = (hidden.value && hidden.value.trim() !== '') ? hidden.value : 'rangetime'; |
| 59 |
emit_inspector_change(hidden); |
| 60 |
} |
| 61 |
|
| 62 |
// --------------------------------------------------------------------- |
| 63 |
// Canvas observer (to reinvoke external timeselector and sync inspectors) |
| 64 |
// --------------------------------------------------------------------- |
| 65 |
(function attachCanvasObserver(){ |
| 66 |
var root = d.body, mo; |
| 67 |
|
| 68 |
function runSync(){ |
| 69 |
try { w.__wpbc_rt_mo_pause && w.__wpbc_rt_mo_pause(); } catch(e){} |
| 70 |
d.querySelectorAll('.wpbc_bfb__inspector_rangetime').forEach(function(panel){ |
| 71 |
try { wpbc_bfb_field_rangetime.sync_state_from_rows(panel); } catch(e){} |
| 72 |
}); |
| 73 |
try { w.__wpbc_rt_mo_resume && w.__wpbc_rt_mo_resume(); } catch(e){} |
| 74 |
} |
| 75 |
|
| 76 |
function handle(muts){ |
| 77 |
var found = false; |
| 78 |
for (var i=0;i<muts.length && !found;i++){ |
| 79 |
var m = muts[i]; |
| 80 |
// select itself modified |
| 81 |
if (m.type === 'childList' && m.target && m.target.matches && m.target.matches('.wpbc_bfb__preview-rangetime')) found = true; |
| 82 |
// added nodes? |
| 83 |
for (var j=0;j<m.addedNodes.length && !found;j++){ |
| 84 |
var n = m.addedNodes[j]; |
| 85 |
if (n.nodeType !== 1) continue; |
| 86 |
if ( n.matches && (n.matches('.wpbc_bfb__preview-rangetime') || n.matches('.wpbc_bfb__preview-timepicker')) ) { found = true; } |
| 87 |
else if ( n.querySelector && (n.querySelector('.wpbc_bfb__preview-rangetime') || n.querySelector('.wpbc_bfb__preview-timepicker')) ) { found = true; } |
| 88 |
} |
| 89 |
// attribute changes on select |
| 90 |
if (!found && m.type === 'attributes' && m.target && m.target.matches && m.target.matches('.wpbc_bfb__preview-rangetime')) found = true; |
| 91 |
} |
| 92 |
if (found) Time.schedule_init_timeselector(); |
| 93 |
if (found) runSync(); |
| 94 |
} |
| 95 |
|
| 96 |
try { |
| 97 |
mo = new MutationObserver(handle); |
| 98 |
mo.observe(root, { |
| 99 |
childList:true, subtree:true, attributes:true, attributeFilter:['value','class'] |
| 100 |
}); |
| 101 |
} catch(e) {} |
| 102 |
|
| 103 |
w.__wpbc_rt_mo_pause = function(){ try { mo && mo.disconnect(); } catch(e){} }; |
| 104 |
w.__wpbc_rt_mo_resume = function(){ try { |
| 105 |
mo && mo.observe(root, { childList:true, subtree:true, attributes:true, attributeFilter:['value','class'] }); |
| 106 |
} catch(e){} }; |
| 107 |
})(); |
| 108 |
|
| 109 |
// --------------------------------------------------------------------- |
| 110 |
// Renderer |
| 111 |
// --------------------------------------------------------------------- |
| 112 |
const wpbc_bfb_field_rangetime = class extends Base { |
| 113 |
static template_id = 'wpbc-bfb-field-rangetime'; |
| 114 |
static kind = 'rangetime'; |
| 115 |
|
| 116 |
static get_defaults(){ |
| 117 |
return { |
| 118 |
kind : 'field', |
| 119 |
type : 'rangetime', |
| 120 |
label : 'Time', |
| 121 |
name : 'rangetime', |
| 122 |
html_id : '', |
| 123 |
required : true, |
| 124 |
multiple : false, |
| 125 |
size : null, |
| 126 |
cssclass : '', |
| 127 |
help : '', |
| 128 |
default_value : '', |
| 129 |
placeholder : '--- Select time ---', |
| 130 |
value_differs : true, |
| 131 |
min_width : '240px', |
| 132 |
options : [ |
| 133 |
{ label: '10:00 AM - 12:00 PM', value: '10:00 - 12:00', selected: false }, |
| 134 |
{ label: '12:00 PM - 02:00 PM', value: '12:00 - 14:00', selected: false }, |
| 135 |
{ label: '02:00 PM - 04:00 PM', value: '14:00 - 16:00', selected: false }, |
| 136 |
{ label: '04:00 PM - 06:00 PM', value: '16:00 - 18:00', selected: false }, |
| 137 |
{ label: '06:00 PM - 08:00 PM', value: '18:00 - 20:00', selected: false } |
| 138 |
], |
| 139 |
gen_label_fmt : 'ampm', |
| 140 |
gen_start_24h : '09:00', |
| 141 |
gen_end_24h : '18:00', |
| 142 |
gen_start_ampm_t: '09:00', |
| 143 |
gen_end_ampm_t : '18:00', |
| 144 |
gen_step_h : 0, |
| 145 |
gen_step_m : 30 |
| 146 |
}; |
| 147 |
} |
| 148 |
|
| 149 |
/** |
| 150 |
* Executed after the field is dropped from the palette. |
| 151 |
* Locks canonical name and disables future auto-naming. |
| 152 |
* |
| 153 |
* @param {Object} data |
| 154 |
* @param {HTMLElement} el |
| 155 |
* @param {Object} ctx |
| 156 |
*/ |
| 157 |
static on_field_drop(data, el, ctx){ |
| 158 |
if (super.on_field_drop) super.on_field_drop(data, el, ctx); |
| 159 |
try { |
| 160 |
// Keep data model canonical |
| 161 |
if (data && typeof data === 'object') { |
| 162 |
data.name = 'rangetime'; |
| 163 |
data.required = true; |
| 164 |
} |
| 165 |
// Lock dataset flags on the dropped element |
| 166 |
if (el && el.dataset) { |
| 167 |
el.dataset.name = 'rangetime'; |
| 168 |
el.dataset.autoname = '0'; |
| 169 |
el.dataset.fresh = '0'; |
| 170 |
el.dataset.name_user_touched = '1'; |
| 171 |
el.setAttribute('data-required','true'); |
| 172 |
} |
| 173 |
// Sync the preview control (<select>) immediately |
| 174 |
var sel = el && el.querySelector('select.wpbc_bfb__preview-rangetime'); |
| 175 |
if (sel) sel.setAttribute('name','rangetime'); |
| 176 |
// If Inspector is present, update the hidden "name" proxy and notify |
| 177 |
var hidden = d.querySelector('.wpbc_bfb__inspector_rangetime .js-locked-name[data-inspector-key="name"]'); |
| 178 |
if (hidden) { hidden.value = 'rangetime'; hidden.setAttribute('data-locked','1'); if (typeof emit_inspector_change==='function') emit_inspector_change(hidden); } |
| 179 |
} catch(e){} |
| 180 |
} |
| 181 |
|
| 182 |
// Bind once for all inspector panels (events). |
| 183 |
static bind_inspector_events_once(){ |
| 184 |
if (this._bound_once) return; |
| 185 |
this._bound_once = true; |
| 186 |
|
| 187 |
// Persist placeholder when toggling global picker |
| 188 |
d.addEventListener( 'change', function (ev) { |
| 189 |
var tgl = ev.target && ev.target.closest( '.wpbc_bfb__inspector_rangetime .js-toggle-timeslot-picker' ); |
| 190 |
if ( !tgl ) return; |
| 191 |
var panel = tgl.closest( '.wpbc_bfb__inspector_rangetime' ); |
| 192 |
var ph = panel && panel.querySelector( '.js-placeholder' ); |
| 193 |
if ( ph ) emit_inspector_change( ph ); |
| 194 |
} ); |
| 195 |
|
| 196 |
// AM/PM <-> 24h switch |
| 197 |
d.addEventListener('change', function(ev){ |
| 198 |
var radio = ev.target && ev.target.closest('.wpbc_bfb__inspector_rangetime .js-rt-label-fmt'); |
| 199 |
if (!radio) return; |
| 200 |
|
| 201 |
var panel = radio.closest('.wpbc_bfb__inspector_rangetime'); |
| 202 |
if (!panel) return; |
| 203 |
|
| 204 |
var fmt = get_current_format(panel); // 'ampm' | '24h' |
| 205 |
var proxy = panel.querySelector('.js-rt-label-fmt-value'); |
| 206 |
if (proxy){ proxy.value = fmt; emit_inspector_change(proxy); } |
| 207 |
|
| 208 |
update_gen_visibility(panel); |
| 209 |
|
| 210 |
// Sync generator values across groups. |
| 211 |
if (fmt === '24h'){ |
| 212 |
var s_t = (panel.querySelector('.js-gen-start-ampm-time')||{}).value || ''; |
| 213 |
var e_t = (panel.querySelector('.js-gen-end-ampm-time')||{}).value || ''; |
| 214 |
var s_m = Time.parse_hhmm_24h(s_t), e_m = Time.parse_hhmm_24h(e_t); |
| 215 |
var s24 = isNaN(s_m) ? '' : Time.format_minutes_24h(s_m); |
| 216 |
var e24 = isNaN(e_m) ? '' : Time.format_minutes_24h(e_m); |
| 217 |
var s24el = panel.querySelector('.js-gen-start-24h'); |
| 218 |
var e24el = panel.querySelector('.js-gen-end-24h'); |
| 219 |
if (s24el){ s24el.value = s24; emit_inspector_change(s24el); } |
| 220 |
if (e24el){ e24el.value = e24; emit_inspector_change(e24el); } |
| 221 |
} else { |
| 222 |
var s24t = (panel.querySelector('.js-gen-start-24h')||{}).value || ''; |
| 223 |
var e24t = (panel.querySelector('.js-gen-end-24h')||{}).value || ''; |
| 224 |
var s_m2 = Time.parse_hhmm_24h(s24t), e_m2 = Time.parse_hhmm_24h(e24t); |
| 225 |
var sam = isNaN(s_m2) ? '' : Time.format_minutes_24h(s_m2); |
| 226 |
var eam = isNaN(e_m2) ? '' : Time.format_minutes_24h(e_m2); |
| 227 |
var st_el = panel.querySelector('.js-gen-start-ampm-time'); |
| 228 |
var et_el = panel.querySelector('.js-gen-end-ampm-time'); |
| 229 |
if (st_el){ st_el.value = sam; emit_inspector_change(st_el); } |
| 230 |
if (et_el){ et_el.value = eam; emit_inspector_change(et_el); } |
| 231 |
} |
| 232 |
|
| 233 |
Time.rebuild_all_rows_to_format(panel, fmt); |
| 234 |
if (fmt === '24h') Time.apply_imask_in_container_24h(panel); |
| 235 |
wpbc_bfb_field_rangetime.sync_state_from_rows(panel); |
| 236 |
Core.UI.pulse_query_debounced( panel, '.js-rt-generate' ); // avoid reflow spam while typing |
| 237 |
}); |
| 238 |
|
| 239 |
// Mask on focus (24h) |
| 240 |
d.addEventListener('focusin', function(ev){ |
| 241 |
var el = ev.target && ev.target.closest('.js-rt-mask[data-mask-kind="24h"]'); |
| 242 |
if (el && !el._imask) Time.apply_imask_to_input(el); |
| 243 |
}); |
| 244 |
|
| 245 |
// Duration range <-> number sync |
| 246 |
d.addEventListener('input', function(ev){ |
| 247 |
var range = ev.target && ev.target.closest('.wpbc_bfb__inspector_rangetime [data-len-group] [data-len-range]'); |
| 248 |
if (!range) return; |
| 249 |
var group = range.closest('[data-len-group]'); |
| 250 |
var num = group && group.querySelector('[data-len-value]'); |
| 251 |
if (num){ |
| 252 |
num.value = range.value; |
| 253 |
if (num.hasAttribute('data-inspector-key')) emit_inspector_change(num); |
| 254 |
} |
| 255 |
const panel = (range || num).closest('.wpbc_bfb__inspector_rangetime'); |
| 256 |
Core.UI.pulse_query_debounced( panel, '.js-rt-generate' ); // avoid reflow spam while typing |
| 257 |
}); |
| 258 |
d.addEventListener('input', function(ev){ |
| 259 |
var num = ev.target && ev.target.closest('.wpbc_bfb__inspector_rangetime [data-len-group] [data-len-value]'); |
| 260 |
if (!num) return; |
| 261 |
var group = num.closest('[data-len-group]'); |
| 262 |
var range = group && group.querySelector('[data-len-range]'); |
| 263 |
if (range) range.value = num.value; |
| 264 |
const panel = (range || num).closest('.wpbc_bfb__inspector_rangetime'); |
| 265 |
Core.UI.pulse_query_debounced( panel, '.js-rt-generate' ); // avoid reflow spam while typing |
| 266 |
}); |
| 267 |
|
| 268 |
// Pulse when any generator input changes (both formats + step) |
| 269 |
d.addEventListener('input', function(ev){ |
| 270 |
const genInput = ev.target && ev.target.closest( |
| 271 |
'.wpbc_bfb__inspector_rangetime .js-gen-start-24h,' + |
| 272 |
'.wpbc_bfb__inspector_rangetime .js-gen-end-24h,' + |
| 273 |
'.wpbc_bfb__inspector_rangetime .js-gen-start-ampm-time,' + |
| 274 |
'.wpbc_bfb__inspector_rangetime .js-gen-end-ampm-time,' + |
| 275 |
'.wpbc_bfb__inspector_rangetime .js-gen-step-h,' + |
| 276 |
'.wpbc_bfb__inspector_rangetime .js-gen-step-m' |
| 277 |
); |
| 278 |
if (!genInput) return; |
| 279 |
const panel = genInput.closest('.wpbc_bfb__inspector_rangetime'); |
| 280 |
Core.UI.pulse_query_debounced( panel, '.js-rt-generate' ); // avoid reflow spam while typing |
| 281 |
}); |
| 282 |
|
| 283 |
// Generate slots |
| 284 |
d.addEventListener('click', function(ev){ |
| 285 |
var btn = ev.target && ev.target.closest('.wpbc_bfb__inspector_rangetime .js-rt-generate'); |
| 286 |
if (!btn) return; |
| 287 |
ev.preventDefault(); |
| 288 |
|
| 289 |
var panel = btn.closest('.wpbc_bfb__inspector_rangetime'); |
| 290 |
if (!panel) return; |
| 291 |
|
| 292 |
var fmt = get_current_format(panel); |
| 293 |
var start_m, end_m; |
| 294 |
if (fmt === '24h'){ |
| 295 |
var s24 = (panel.querySelector('.js-gen-start-24h')||{}).value || ''; |
| 296 |
var e24 = (panel.querySelector('.js-gen-end-24h')||{}).value || ''; |
| 297 |
start_m = Time.parse_hhmm_24h(s24); |
| 298 |
end_m = Time.parse_hhmm_24h(e24); |
| 299 |
} else { |
| 300 |
var s_am = (panel.querySelector('.js-gen-start-ampm-time')||{}).value || ''; |
| 301 |
var e_am = (panel.querySelector('.js-gen-end-ampm-time')||{}).value || ''; |
| 302 |
start_m = Time.parse_hhmm_24h(s_am); // input[type=time] => "HH:MM" |
| 303 |
end_m = Time.parse_hhmm_24h(e_am); |
| 304 |
} |
| 305 |
var step_h = parseInt((panel.querySelector('.js-gen-step-h')||{}).value, 10) || 0; |
| 306 |
var step_m = parseInt((panel.querySelector('.js-gen-step-m')||{}).value, 10) || 0; |
| 307 |
var step = Math.max(1, step_h * 60 + step_m); |
| 308 |
|
| 309 |
var slots = Time.build_time_slots(start_m, end_m, step, fmt); |
| 310 |
wpbc_bfb_field_rangetime.replace_options_in_panel(panel, slots); |
| 311 |
Time.rebuild_all_rows_to_format(panel, fmt); |
| 312 |
if (fmt === '24h') Time.apply_imask_in_container_24h(panel); |
| 313 |
wpbc_bfb_field_rangetime.sync_state_from_rows(panel); |
| 314 |
}); |
| 315 |
|
| 316 |
// Clear |
| 317 |
d.addEventListener('click', function(ev){ |
| 318 |
var btn = ev.target && ev.target.closest('.wpbc_bfb__inspector_rangetime .js-rt-clear'); |
| 319 |
if (!btn) return; |
| 320 |
ev.preventDefault(); |
| 321 |
var panel = btn.closest('.wpbc_bfb__inspector_rangetime'); |
| 322 |
if (!panel) return; |
| 323 |
wpbc_bfb_field_rangetime.replace_options_in_panel(panel, []); |
| 324 |
wpbc_bfb_field_rangetime.sync_state_from_rows(panel); |
| 325 |
Core.UI.pulse_query_debounced(panel, '.js-rt-generate' ); // avoid reflow spam while typing |
| 326 |
}); |
| 327 |
|
| 328 |
// Add option |
| 329 |
d.addEventListener('click', function(ev){ |
| 330 |
var btn = ev.target && ev.target.closest('.wpbc_bfb__inspector_rangetime .js-rt-add-option'); |
| 331 |
if (!btn) return; |
| 332 |
ev.preventDefault(); |
| 333 |
var panel = btn.closest('.wpbc_bfb__inspector_rangetime'); |
| 334 |
if (!panel) return; |
| 335 |
|
| 336 |
wpbc_bfb_field_rangetime.add_option_row(panel); |
| 337 |
var fmt = get_current_format(panel); |
| 338 |
if (fmt === '24h') Time.apply_imask_in_container_24h(panel); |
| 339 |
wpbc_bfb_field_rangetime.sync_state_from_rows(panel); |
| 340 |
}); |
| 341 |
|
| 342 |
// Row dropdown actions |
| 343 |
d.addEventListener('click', function(ev){ |
| 344 |
var a = ev.target && ev.target.closest('.wpbc_bfb__inspector_rangetime .rt_dropdown_action'); |
| 345 |
if (!a) return; |
| 346 |
ev.preventDefault(); |
| 347 |
|
| 348 |
var action = a.getAttribute('data-rt-action') || ''; |
| 349 |
var row = a.closest('.wpbc_bfb__options_row'); |
| 350 |
var panel = a.closest('.wpbc_bfb__inspector_rangetime'); |
| 351 |
if (!row || !panel) return; |
| 352 |
|
| 353 |
if (action === 'add_after') { |
| 354 |
wpbc_bfb_field_rangetime.insert_row_after(panel, row, null); |
| 355 |
} else if (action === 'duplicate') { |
| 356 |
var fmt = get_current_format(panel); |
| 357 |
var data = wpbc_bfb_field_rangetime.read_row(row, fmt); |
| 358 |
wpbc_bfb_field_rangetime.insert_row_after(panel, row, data); |
| 359 |
} else if (action === 'remove') { |
| 360 |
var listEl = row.parentNode; |
| 361 |
listEl.removeChild(row); |
| 362 |
wpbc_bfb_field_rangetime.reindex_rows(listEl); |
| 363 |
} |
| 364 |
var fmt2 = get_current_format(panel); |
| 365 |
Time.rebuild_all_rows_to_format(panel, fmt2); |
| 366 |
if (fmt2 === '24h') Time.apply_imask_in_container_24h(panel); |
| 367 |
wpbc_bfb_field_rangetime.sync_state_from_rows(panel); |
| 368 |
}); |
| 369 |
|
| 370 |
// Row edits -> sync |
| 371 |
d.addEventListener('input', function(ev){ |
| 372 |
var row = ev.target && ev.target.closest('.wpbc_bfb__inspector_rangetime .wpbc_bfb__options_row'); |
| 373 |
if (!row) return; |
| 374 |
var panel = row.closest('.wpbc_bfb__inspector_rangetime'); |
| 375 |
wpbc_bfb_field_rangetime.sync_state_from_rows(panel); |
| 376 |
}); |
| 377 |
d.addEventListener('change', function(ev){ |
| 378 |
var row = ev.target && ev.target.closest('.wpbc_bfb__inspector_rangetime .wpbc_bfb__options_row'); |
| 379 |
if (!row) return; |
| 380 |
|
| 381 |
if (ev.target.classList.contains('wpbc_bfb__opt-selected-chk')){ |
| 382 |
var panel = row.closest('.wpbc_bfb__inspector_rangetime'); |
| 383 |
if (panel && !panel.querySelector('.js-opt-multiple:checked')) { |
| 384 |
panel.querySelectorAll('.wpbc_bfb__opt-selected-chk').forEach(function(chk){ |
| 385 |
if (chk !== ev.target) { |
| 386 |
chk.checked = false; |
| 387 |
chk.setAttribute('aria-checked', 'false'); |
| 388 |
} |
| 389 |
}); |
| 390 |
ev.target.setAttribute('aria-checked', 'true'); |
| 391 |
} |
| 392 |
} |
| 393 |
var panel2 = row.closest('.wpbc_bfb__inspector_rangetime'); |
| 394 |
wpbc_bfb_field_rangetime.sync_state_from_rows(panel2); |
| 395 |
}); |
| 396 |
|
| 397 |
// Init on load for existing panels |
| 398 |
var init = function(){ |
| 399 |
d.querySelectorAll('.wpbc_bfb__inspector_rangetime').forEach(function(panel){ |
| 400 |
update_gen_visibility(panel); |
| 401 |
var fmt = get_current_format(panel); |
| 402 |
if (fmt === '24h') Time.apply_imask_in_container_24h(panel); |
| 403 |
|
| 404 |
// placeholder disabled state mirror (if multiple is shown in future) |
| 405 |
var mul = panel.querySelector('.js-opt-multiple'); |
| 406 |
var ph = panel.querySelector('.js-placeholder'); |
| 407 |
var phNote = panel.querySelector('.js-placeholder-note'); |
| 408 |
if (mul && ph) ph.disabled = !!mul.checked; |
| 409 |
if (mul && phNote) phNote.style.display = mul.checked ? 'none' : ''; |
| 410 |
|
| 411 |
enforce_locked_name(panel); |
| 412 |
var ph_init = panel.querySelector('.js-placeholder'); |
| 413 |
if (ph_init) emit_inspector_change(ph_init); |
| 414 |
}); |
| 415 |
}; |
| 416 |
(d.readyState === 'loading') ? d.addEventListener('DOMContentLoaded', init) : init(); |
| 417 |
|
| 418 |
// Re-apply when Inspector re-renders a panel |
| 419 |
d.addEventListener('wpbc_bfb_inspector_ready', function(ev){ |
| 420 |
var panel = ev && ev.detail && ev.detail.panel; |
| 421 |
if (!panel) return; |
| 422 |
var rtPanel = panel.closest |
| 423 |
? (panel.matches('.wpbc_bfb__inspector_rangetime') ? panel : panel.closest('.wpbc_bfb__inspector_rangetime')) |
| 424 |
: null; |
| 425 |
if (!rtPanel) return; |
| 426 |
|
| 427 |
update_gen_visibility(rtPanel); |
| 428 |
var fmt = get_current_format(rtPanel); |
| 429 |
if (fmt === '24h') Time.apply_imask_in_container_24h(rtPanel); |
| 430 |
|
| 431 |
enforce_locked_name(rtPanel); |
| 432 |
var ph_init = rtPanel.querySelector('.js-placeholder'); |
| 433 |
if (ph_init) emit_inspector_change(ph_init); |
| 434 |
}); |
| 435 |
} |
| 436 |
|
| 437 |
// ------- Rows & state -------- |
| 438 |
static build_row_html(data, fmt, panel){ |
| 439 |
var label = (data && data.label) || ''; |
| 440 |
var value = (data && data.value) || ''; |
| 441 |
var parts = String(value).split(/\s*-\s*/); |
| 442 |
var start = parts[0] || ''; |
| 443 |
var end = parts[1] || ''; |
| 444 |
var uid = 'wpbc_ins_rt_' + Math.random().toString(36).slice(2,10); |
| 445 |
var sel = !!(data && (true === data.selected || 'true' === data.selected || 1 === data.selected || '1' === data.selected)); |
| 446 |
var i18n = { |
| 447 |
add : (panel && panel.dataset.i18nAdd) || 'Add New', |
| 448 |
duplicate: (panel && panel.dataset.i18nDuplicate) || 'Duplicate', |
| 449 |
remove : (panel && panel.dataset.i18nRemove) || 'Remove', |
| 450 |
def : (panel && panel.dataset.i18nDefault) || (w.wpbc_i18n_default || 'Default'), |
| 451 |
reorder : (panel && panel.dataset.i18nReorder) || 'Drag to reorder', |
| 452 |
rowlabel : (panel && panel.dataset.i18nRowlabel) || 'Label (e.g. 10:00 AM - 10:30 AM)' |
| 453 |
}; |
| 454 |
var time_inputs_html; |
| 455 |
if (fmt === '24h') { |
| 456 |
time_inputs_html = |
| 457 |
'<input type="text" class="wpbc_bfb__opt-start js-rt-mask" data-mask-kind="24h" placeholder="HH:MM" value="' + Time.esc_attr(start) + '">' + |
| 458 |
'<span class="wpbc_bfb__opt-sep" aria-hidden="true">-</span>' + |
| 459 |
'<input type="text" class="wpbc_bfb__opt-end js-rt-mask" data-mask-kind="24h" placeholder="HH:MM" value="' + Time.esc_attr(end) + '">'; |
| 460 |
} else { |
| 461 |
time_inputs_html = |
| 462 |
'<input type="time" class="wpbc_bfb__opt-start js-rt-start-time" step="300" value="' + Time.esc_attr(start) + '">' + |
| 463 |
'<span class="wpbc_bfb__opt-sep" aria-hidden="true">-</span>' + |
| 464 |
'<input type="time" class="wpbc_bfb__opt-end js-rt-end-time" step="300" value="' + Time.esc_attr(end) + '">'; |
| 465 |
} |
| 466 |
|
| 467 |
return '' + |
| 468 |
'<div class="wpbc_bfb__options_row" data-index="0">' + |
| 469 |
'<span class="wpbc_bfb__drag-handle" title="' + i18n.reorder + '"><span class="wpbc_icn_drag_indicator"></span></span>' + |
| 470 |
'<input type="text" class="wpbc_bfb__opt-label" placeholder="' + i18n.rowlabel + '" value="' + Time.esc_attr(label) + '">' + |
| 471 |
time_inputs_html + |
| 472 |
'<input type="text" class="wpbc_bfb__opt-value" value="' + Time.esc_attr(value || (start && end ? (start + ' - ' + end) : '')) + '" hidden>' + |
| 473 |
'<div class="wpbc_bfb__opt-selected">' + |
| 474 |
'<div class="inspector__control wpbc_ui__toggle">' + |
| 475 |
'<input type="checkbox" class="wpbc_bfb__opt-selected-chk inspector__input" id="' + uid + '" role="switch" ' + (sel ? 'checked aria-checked="true"' : 'aria-checked="false"') + '>' + |
| 476 |
'<label class="wpbc_ui__toggle_icon_radio" for="' + uid + '"></label>' + |
| 477 |
'<label class="wpbc_ui__toggle_label" for="' + uid + '">' + i18n.def + '</label>' + |
| 478 |
'</div>' + |
| 479 |
'</div>' + |
| 480 |
'<div class="wpbc_ui_el wpbc_ui_el_container wpbc_ui_el__dropdown">' + |
| 481 |
'<a href="javascript:void(0)" data-toggle="wpbc_dropdown" aria-expanded="false" class="ul_dropdown_menu_toggle">' + |
| 482 |
'<i class="menu_icon icon-1x wpbc_icn_more_vert"></i>' + |
| 483 |
'</a>' + |
| 484 |
'<ul class="ul_dropdown_menu" role="menu" style="right:0px; left:auto;">' + |
| 485 |
'<li><a class="ul_dropdown_menu_li_action rt_dropdown_action" data-rt-action="add_after" href="javascript:void(0)">' + i18n.add + '<i class="menu_icon icon-1x wpbc_icn_add_circle"></i></a></li>' + |
| 486 |
'<li><a class="ul_dropdown_menu_li_action rt_dropdown_action" data-rt-action="duplicate" href="javascript:void(0)">' + i18n.duplicate + '<i class="menu_icon icon-1x wpbc_icn_content_copy"></i></a></li>' + |
| 487 |
'<li class="divider"></li>' + |
| 488 |
'<li><a class="ul_dropdown_menu_li_action rt_dropdown_action" data-rt-action="remove" href="javascript:void(0)">' + i18n.remove + '<i class="menu_icon icon-1x wpbc_icn_delete_outline"></i></a></li>' + |
| 489 |
'</ul>' + |
| 490 |
'</div>' + |
| 491 |
'</div>'; |
| 492 |
} |
| 493 |
|
| 494 |
static add_option_row(panel){ |
| 495 |
var list = panel.querySelector('.wpbc_bfb__options_list'); |
| 496 |
if (!list) return; |
| 497 |
var fmt = get_current_format(panel); |
| 498 |
var s = 9 * 60, e = s + 30; |
| 499 |
var start = Time.format_minutes_24h(s); |
| 500 |
var end = Time.format_minutes_24h(e); |
| 501 |
var label = (fmt === '24h') ? (start + ' - ' + end) : (Time.format_minutes_ampm(s) + ' - ' + Time.format_minutes_ampm(e)); |
| 502 |
var html = wpbc_bfb_field_rangetime.build_row_html({ label: label, value: start + ' - ' + end, selected:false }, fmt, panel); |
| 503 |
|
| 504 |
var tmp = d.createElement('div'); tmp.innerHTML = html; |
| 505 |
var row = tmp.firstElementChild; |
| 506 |
list.appendChild(row); |
| 507 |
wpbc_bfb_field_rangetime.reindex_rows(list); |
| 508 |
if (fmt === '24h') { |
| 509 |
Time.apply_imask_to_input(row.querySelector('.wpbc_bfb__opt-start')); |
| 510 |
Time.apply_imask_to_input(row.querySelector('.wpbc_bfb__opt-end')); |
| 511 |
} |
| 512 |
} |
| 513 |
|
| 514 |
static insert_row_after(panel, after_row, data){ |
| 515 |
var list = panel.querySelector('.wpbc_bfb__options_list'); |
| 516 |
if (!list) return; |
| 517 |
var fmt = get_current_format(panel); |
| 518 |
if (!data){ |
| 519 |
var s = 9 * 60, e = s + 30; |
| 520 |
var start = Time.format_minutes_24h(s), end = Time.format_minutes_24h(e); |
| 521 |
data = { |
| 522 |
label : (fmt === '24h') ? (start + ' - ' + end) : (Time.format_minutes_ampm(s) + ' - ' + Time.format_minutes_ampm(e)), |
| 523 |
value : start + ' - ' + end, |
| 524 |
selected: false |
| 525 |
}; |
| 526 |
} |
| 527 |
var html = wpbc_bfb_field_rangetime.build_row_html(data, fmt, panel); |
| 528 |
var tmp = d.createElement('div'); tmp.innerHTML = html; |
| 529 |
var row = tmp.firstElementChild; |
| 530 |
after_row.parentNode.insertBefore(row, after_row.nextSibling); |
| 531 |
wpbc_bfb_field_rangetime.reindex_rows(list); |
| 532 |
if (fmt === '24h') { |
| 533 |
Time.apply_imask_to_input(row.querySelector('.wpbc_bfb__opt-start')); |
| 534 |
Time.apply_imask_to_input(row.querySelector('.wpbc_bfb__opt-end')); |
| 535 |
} |
| 536 |
} |
| 537 |
|
| 538 |
static read_row(row, fmt){ |
| 539 |
var label = (row.querySelector('.wpbc_bfb__opt-label')||{}).value || ''; |
| 540 |
var s_el = row.querySelector('.wpbc_bfb__opt-start'); |
| 541 |
var e_el = row.querySelector('.wpbc_bfb__opt-end'); |
| 542 |
var s_m = Time.parse_hhmm_24h(s_el ? s_el.value : ''); |
| 543 |
var e_m = Time.parse_hhmm_24h(e_el ? e_el.value : ''); |
| 544 |
if (isNaN(s_m)) s_m = Time.parse_minutes(s_el ? s_el.value : ''); |
| 545 |
if (isNaN(e_m)) e_m = Time.parse_minutes(e_el ? e_el.value : ''); |
| 546 |
|
| 547 |
// Allow export even when end <= start (overnight ranges) |
| 548 |
var value = (!isNaN(s_m) && !isNaN(e_m)) ? (Time.format_minutes_24h(s_m) + ' - ' + Time.format_minutes_24h(e_m)) : ''; |
| 549 |
var sel = !!((row.querySelector('.wpbc_bfb__opt-selected-chk')||{}).checked); |
| 550 |
return { label: label, value: value, selected: sel }; |
| 551 |
} |
| 552 |
|
| 553 |
static sync_state_from_rows(panel){ |
| 554 |
if (!panel) return; |
| 555 |
var list = panel.querySelector('.wpbc_bfb__options_list'); |
| 556 |
var state = panel.querySelector('.wpbc_bfb__options_state'); |
| 557 |
if (!list || !state) return; |
| 558 |
|
| 559 |
var isMultiple = !!panel.querySelector('.js-opt-multiple:checked'); |
| 560 |
var seenSelected = false; |
| 561 |
var fmt = get_current_format(panel); |
| 562 |
var out = []; |
| 563 |
|
| 564 |
list.querySelectorAll('.wpbc_bfb__options_row').forEach(function (row) { |
| 565 |
var obj = wpbc_bfb_field_rangetime.read_row(row, fmt); |
| 566 |
if (!isMultiple && obj.selected) { |
| 567 |
if (!seenSelected) { |
| 568 |
seenSelected = true; |
| 569 |
} else { |
| 570 |
obj.selected = false; |
| 571 |
var chk = row.querySelector('.wpbc_bfb__opt-selected-chk'); |
| 572 |
if (chk){ chk.checked = false; chk.setAttribute('aria-checked', 'false'); } |
| 573 |
} |
| 574 |
} |
| 575 |
out.push(obj); |
| 576 |
var hv = row.querySelector('.wpbc_bfb__opt-value'); |
| 577 |
if (hv) hv.value = obj.value || ''; |
| 578 |
}); |
| 579 |
|
| 580 |
try { state.value = JSON.stringify(out); } catch(e){ state.value = '[]'; } |
| 581 |
emit_inspector_change(state); |
| 582 |
} |
| 583 |
|
| 584 |
static replace_options_in_panel(panel, slots){ |
| 585 |
var list = panel.querySelector('.wpbc_bfb__options_list'); |
| 586 |
var state = panel.querySelector('.wpbc_bfb__options_state'); |
| 587 |
if (!list || !state) return; |
| 588 |
|
| 589 |
var fmt = get_current_format(panel); |
| 590 |
list.innerHTML = ''; |
| 591 |
(slots || []).forEach(function(opt){ |
| 592 |
var html = wpbc_bfb_field_rangetime.build_row_html(opt, fmt, panel); |
| 593 |
var tmp = d.createElement('div'); tmp.innerHTML = html; |
| 594 |
list.appendChild(tmp.firstElementChild); |
| 595 |
}); |
| 596 |
wpbc_bfb_field_rangetime.reindex_rows(list); |
| 597 |
if (fmt === '24h') Time.apply_imask_in_container_24h(panel); |
| 598 |
|
| 599 |
try { state.value = JSON.stringify(slots || []); } catch(e){ state.value = '[]'; } |
| 600 |
emit_inspector_change(state); |
| 601 |
} |
| 602 |
|
| 603 |
static reindex_rows(list){ |
| 604 |
if (!list) return; |
| 605 |
var i = 0; |
| 606 |
list.querySelectorAll('.wpbc_bfb__options_row').forEach(function(row){ |
| 607 |
row.setAttribute('data-index', String(i++)); |
| 608 |
}); |
| 609 |
} |
| 610 |
}; |
| 611 |
|
| 612 |
try { Registry.register('rangetime', wpbc_bfb_field_rangetime); } |
| 613 |
catch (e) { if (w._wpbc && w._wpbc.dev && w._wpbc.dev.error) w._wpbc.dev.error('wpbc_bfb_field_rangetime.register', e); } |
| 614 |
|
| 615 |
wpbc_bfb_field_rangetime.bind_inspector_events_once(); |
| 616 |
w.WPBC_BFB_Field_RangeTime = wpbc_bfb_field_rangetime; |
| 617 |
|
| 618 |
|
| 619 |
|
| 620 |
// ----------------------------------------------------------------------------------------------------------------- |
| 621 |
// Export for "Booking Form" (Advanced Form shortcode) |
| 622 |
// ----------------------------------------------------------------------------------------------------------------- |
| 623 |
/** |
| 624 |
* Booking Form exporter callback for "rangetime". |
| 625 |
* |
| 626 |
* Mirrors the legacy behavior: |
| 627 |
* WPBC_BFB_Exporter.emit_time_select( name, field, req_mark, id_opt, cls_opts, emit_label_then ); |
| 628 |
* |
| 629 |
* So the final shortcode body and label handling are identical to the old |
| 630 |
* switch/case path in builder-exporter.js, just moved into this pack. |
| 631 |
*/ |
| 632 |
function register_rangetime_booking_form_exporter() { |
| 633 |
|
| 634 |
var Exp = w.WPBC_BFB_Exporter; |
| 635 |
if ( ! Exp || typeof Exp.register !== 'function' ) { return; } |
| 636 |
if ( typeof Exp.has_exporter === 'function' && Exp.has_exporter( 'rangetime' ) ) { return; } |
| 637 |
|
| 638 |
var S = Core.WPBC_BFB_Sanitize || {}; |
| 639 |
var esc_html = S.escape_html || function( v ){ return String( v ); }; |
| 640 |
|
| 641 |
/** |
| 642 |
* @type {WPBC_BFB_ExporterCallback} |
| 643 |
*/ |
| 644 |
var exporter_callback = function( field, emit, extras ) { |
| 645 |
extras = extras || {}; |
| 646 |
|
| 647 |
var cfg = extras.cfg || {}; |
| 648 |
var ctx = extras.ctx; |
| 649 |
|
| 650 |
// Shared label wrapper: prefer global helper; fall back to local behavior. |
| 651 |
var emit_label_then = function( body ) { |
| 652 |
// Preferred path: centralized helper in builder-exporter.js |
| 653 |
if ( Exp && typeof Exp.emit_label_then === 'function' ) { |
| 654 |
Exp.emit_label_then( field, emit, body, cfg ); |
| 655 |
return; |
| 656 |
} |
| 657 |
}; |
| 658 |
|
| 659 |
// Required marker (same semantics as other text-like fields). |
| 660 |
var is_req = Exp.is_required( field ); |
| 661 |
var req_mark = is_req ? '*' : ''; |
| 662 |
|
| 663 |
// Name / id / classes from shared helpers so they stay in sync. |
| 664 |
var name = Exp.compute_name( 'rangetime', field ); |
| 665 |
var id_opt = Exp.id_option( field, ctx ); |
| 666 |
var cls_opts = Exp.class_options( field ); |
| 667 |
|
| 668 |
// Prefer the dedicated time helper to keep exact legacy shortcode shape. |
| 669 |
if ( typeof Exp.emit_time_select === 'function' ) { |
| 670 |
Exp.emit_time_select( name, field, req_mark, id_opt, cls_opts, emit_label_then ); |
| 671 |
return; |
| 672 |
} |
| 673 |
}; |
| 674 |
|
| 675 |
Exp.register( 'rangetime', exporter_callback ); |
| 676 |
} |
| 677 |
|
| 678 |
if ( w.WPBC_BFB_Exporter && typeof w.WPBC_BFB_Exporter.register === 'function' ) { |
| 679 |
register_rangetime_booking_form_exporter(); |
| 680 |
} else if ( typeof document !== 'undefined' ) { |
| 681 |
document.addEventListener( 'wpbc:bfb:exporter-ready', register_rangetime_booking_form_exporter, { once: true } ); |
| 682 |
} |
| 683 |
|
| 684 |
|
| 685 |
// ----------------------------------------------------------------------------------------------------------------- |
| 686 |
// Export for "Booking Data" (Content of booking fields data) |
| 687 |
// ----------------------------------------------------------------------------------------------------------------- |
| 688 |
/** |
| 689 |
* Booking Data exporter callback for "rangetime". |
| 690 |
* |
| 691 |
* Default behavior: |
| 692 |
* <b>Label</b>: <f>[rangetime]</f><br> |
| 693 |
* |
| 694 |
* The exported token name is kept fully in sync with the Booking Form exporter |
| 695 |
* via Exp.compute_name('rangetime', field). |
| 696 |
*/ |
| 697 |
function register_rangetime_booking_data_exporter() { |
| 698 |
|
| 699 |
var C = w.WPBC_BFB_ContentExporter; |
| 700 |
if ( ! C || typeof C.register !== 'function' ) { return; } |
| 701 |
if ( typeof C.has_exporter === 'function' && C.has_exporter( 'rangetime' ) ) { return; } |
| 702 |
|
| 703 |
C.register( 'rangetime', function( field, emit, extras ) { |
| 704 |
extras = extras || {}; |
| 705 |
var cfg = extras.cfg || {}; |
| 706 |
|
| 707 |
var Exp = w.WPBC_BFB_Exporter; |
| 708 |
if ( ! Exp || typeof Exp.compute_name !== 'function' ) { return; } |
| 709 |
|
| 710 |
// Keep exported name identical to the Booking Form exporter. |
| 711 |
var name = Exp.compute_name( 'rangetime', field ); |
| 712 |
if ( ! name ) { return; } |
| 713 |
|
| 714 |
var raw_label = ( typeof field.label === 'string' ) ? field.label : ''; |
| 715 |
var label = raw_label.trim() || name; |
| 716 |
|
| 717 |
// Shared helper: <b>Label</b>: <f>[name]</f><br> |
| 718 |
C.emit_line_bold_field( emit, label, name, cfg ); |
| 719 |
} ); |
| 720 |
} |
| 721 |
|
| 722 |
if ( w.WPBC_BFB_ContentExporter && typeof w.WPBC_BFB_ContentExporter.register === 'function' ) { |
| 723 |
register_rangetime_booking_data_exporter(); |
| 724 |
} else if ( typeof document !== 'undefined' ) { |
| 725 |
document.addEventListener( 'wpbc:bfb:content-exporter-ready', register_rangetime_booking_data_exporter, { once: true } ); |
| 726 |
} |
| 727 |
|
| 728 |
})(window, document); |
| 729 |
|