PluginProbe
Booking Calendar / 11.0
Booking Calendar v11.0
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
booking / includes / page-form-builder / field-packs / time-start / _src / field-starttime-wptpl.js

field-starttime-wptpl.js in Booking Calendar 11.0, at includes/page-form-builder/field-packs/time-start/_src/field-starttime-wptpl.js

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