PluginProbe
Booking Calendar / 11.2
Booking Calendar v11.2
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-grid / _src / time-grid-wptpl.js

time-grid-wptpl.js in Booking Calendar 11.2, at includes/page-form-builder/field-packs/time-grid/_src/time-grid-wptpl.js

604 lines 23.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 // File: /includes/page-form-builder/field-packs/time-slots/_out/field-time-slots-wptpl.js
2 (function (w, d) {
3 'use strict';
4
5 /**
6 * WPBC BFB — Time Slots (robust)
7 * - Defers renderer registration until Core/Registry exists (prevents "No renderer found").
8 * - Initializes the Inspector via custom event, DOMContentLoaded, and MutationObserver fallbacks.
9 * - Implements toolbar buttons: rename, duplicate, clear, copy ← day, copy ← week, Fill row…, Fill column…
10 * - All helpers and identifiers use snake_case where possible.
11 */
12
13 /* ====================== Tiny utils ====================== */
14
15 function pad2( n ) { n = parseInt( n, 10 ); return ( n < 10 ? '0' + n : '' + n ); }
16
17 function time_to_min( t ) {
18 if ( ! t || 'string' !== typeof t ) { return null; }
19 var m = t.match( /^(\d{1,2}):(\d{2})$/ );
20 if ( ! m ) { return null; }
21 var hh = parseInt( m[1], 10 ), mm = parseInt( m[2], 10 );
22 if ( hh < 0 || hh > 23 || mm < 0 || mm > 59 ) { return null; }
23 return hh * 60 + mm;
24 }
25
26 function min_to_time( mins ) {
27 var m = parseInt( mins, 10 );
28 if ( ! isFinite( m ) ) { m = 0; }
29 if ( m < 0 ) { m = 0; }
30 m = m % ( 24 * 60 );
31 var hh = Math.floor( m / 60 ), mm = m % 60;
32 return pad2( hh ) + ':' + pad2( mm );
33 }
34
35 function normalize_step( step ) {
36 var s = parseInt( step, 10 );
37 if ( ! isFinite( s ) || s < 5 ) { s = 5; }
38 if ( s > 180 ) { s = 180; }
39 return s;
40 }
41
42 function build_row_minutes( from_min, to_min, step ) {
43 var arr = []; for ( var m = from_min; m < to_min; m += step ) { arr.push( m ); } return arr;
44 }
45
46 /**
47 * Merge minute indexes into continuous time ranges.
48 *
49 * @param {Array<number>} selected_minutes Sorted minute indexes (start of each slot)
50 * @param {number} step Step in minutes
51 * @return {Array<{from:string,to:string}>}
52 */
53 function merge_day_ranges( selected_minutes, step ) {
54 var out = [];
55 if ( ! Array.isArray( selected_minutes ) || ! selected_minutes.length ) { return out; }
56 selected_minutes.sort( function(a,b){ return a - b; } );
57 var run_start = selected_minutes[0], prev = run_start, i, m;
58 for ( i = 1; i < selected_minutes.length; i++ ) {
59 m = selected_minutes[i];
60 if ( m === prev + step ) { prev = m; continue; }
61 out.push( { from: min_to_time( run_start ), to: min_to_time( prev + step ) } );
62 run_start = m; prev = m;
63 }
64 out.push( { from: min_to_time( run_start ), to: min_to_time( prev + step ) } );
65 return out;
66 }
67
68 /**
69 * Expand ranges to a set of "on" minute cells for easy painting.
70 */
71 function expand_day_ranges( ranges, step, from_min, to_min ) {
72 var set = Object.create( null );
73 ( ranges || [] ).forEach( function( r ) {
74 var a = time_to_min( r.from ), b = time_to_min( r.to );
75 if ( a == null || b == null ) { return; }
76 for ( var m = a; m < b; m += step ) {
77 if ( m >= from_min && m < to_min ) { set[ m ] = true; }
78 }
79 } );
80 return set;
81 }
82
83 /**
84 * Keep profiles length in sync with week_cycle.
85 */
86 function reconcile_profiles( profiles, week_cycle ) {
87 var out = [], labels = ['Week A','Week B','Week C','Week D','Week E','Week F'], i;
88 for ( i = 0; i < week_cycle; i++ ) {
89 if ( profiles && profiles[i] ) {
90 out.push( profiles[i] );
91 } else {
92 out.push( { key: String.fromCharCode( 65 + i ), label: labels[i] || ( 'Week ' + ( i + 1 ) ), slots: { '1':[], '2':[], '3':[], '4':[], '5':[], '6':[], '7':[] } } );
93 }
94 }
95 return out;
96 }
97
98 /**
99 * Snap a minute value to a step grid.
100 *
101 * @param {number} m Minute value
102 * @param {number} step Step in minutes
103 * @param {'down'|'up'} dir Direction
104 */
105 function snap_to_step( m, step, dir ) {
106 var q = Math.floor( m / step ) * step;
107 if ( 'up' === dir && q < m ) { q += step; }
108 return q;
109 }
110
111 /* ====================== Templates ====================== */
112
113 var tmpl_fn = ( w.wp && w.wp.template ) ? w.wp.template : function(){ return function(){ return ''; }; };
114
115 function render_grid_rows( body_el, start_min, end_min, step ) {
116 body_el.innerHTML = '';
117 var row_t = tmpl_fn( 'wpbc-bfb-timegrid-row' );
118 build_row_minutes( start_min, end_min, step ).forEach( function( minute ) {
119 var html = row_t( { minute: minute, label: min_to_time( minute ) } );
120 var wrap = d.createElement( 'div' ); wrap.innerHTML = html;
121 var row = wrap.firstElementChild; if ( row ) { body_el.appendChild( row ); }
122 } );
123 }
124
125 function paint_selection( root, profile, start_min, end_min, step ) {
126 var body = root.querySelector( '.wpbc_bfb__timegrid_body' );
127 if ( ! body || ! profile || ! profile.slots ) { return; }
128 for ( var d_i = 1; d_i <= 7; d_i++ ) {
129 var ranges = profile.slots[ String( d_i ) ] || [];
130 var set = expand_day_ranges( ranges, step, start_min, end_min );
131 var cells = body.querySelectorAll( '.wpbc_bfb__timegrid_cell--slot[data-day="' + d_i + '"]' );
132 for ( var i = 0; i < cells.length; i++ ) {
133 var c = cells[ i ];
134 var mm = parseInt( c.getAttribute( 'data-minute' ), 10 );
135 if ( set[ mm ] ) { c.classList.add( 'is-on' ); } else { c.classList.remove( 'is-on' ); }
136 }
137 }
138 }
139
140 function read_selection_into_profile( root, profile, start_min, end_min, step ) {
141 var body = root.querySelector( '.wpbc_bfb__timegrid_body' );
142 if ( ! body || ! profile ) { return; }
143 for ( var d_i = 1; d_i <= 7; d_i++ ) {
144 var cells = body.querySelectorAll( '.wpbc_bfb__timegrid_cell--slot[data-day="' + d_i + '"].is-on' );
145 var minutes = [], i;
146 for ( i = 0; i < cells.length; i++ ) {
147 minutes.push( parseInt( cells[i].getAttribute( 'data-minute' ), 10 ) );
148 }
149 profile.slots[ String( d_i ) ] = merge_day_ranges( minutes, step );
150 }
151 }
152
153 function toggle_rect( root, day_from, day_to, min_from, min_to, mode ) {
154 var body = root.querySelector( '.wpbc_bfb__timegrid_body' );
155 if ( ! body ) { return; }
156 var df = Math.min( day_from, day_to ), dt = Math.max( day_from, day_to );
157 var mf = Math.min( min_from, min_to ), mt = Math.max( min_from, min_to );
158 for ( var d_i = df; d_i <= dt; d_i++ ) {
159 var cells = body.querySelectorAll( '.wpbc_bfb__timegrid_cell--slot[data-day="' + d_i + '"]' );
160 for ( var i = 0; i < cells.length; i++ ) {
161 var c = cells[i], mm = parseInt( c.getAttribute( 'data-minute' ), 10 );
162 if ( mm >= mf && mm <= mt ) {
163 if ( 'on' === mode ) { c.classList.add( 'is-on' ); }
164 else if ( 'off' === mode ) { c.classList.remove( 'is-on' ); }
165 else { c.classList.toggle( 'is-on' ); }
166 }
167 }
168 }
169 }
170
171 /* ====================== Inspector controller ====================== */
172
173 /**
174 * Parse day tokens like "Mon,Wed,Fri", "1-5", "Mon-Fri".
175 *
176 * @return {Array<number>} days 1..7
177 */
178 function parse_days_input( txt ) {
179 var map = { mon:1, tue:2, wed:3, thu:4, fri:5, sat:6, sun:7 };
180 if ( ! txt || 'string' !== typeof txt ) { return [1,2,3,4,5,6,7]; }
181 var t = txt.toLowerCase().replace(/\s+/g,'').trim();
182 if ( ! t ) { return [1,2,3,4,5,6,7]; }
183
184 function push_range( a, b, out ) {
185 var i, s = Math.min( a, b ), e = Math.max( a, b );
186 for ( i = s; i <= e; i++ ) { out.push( i ); }
187 }
188
189 var out = [];
190 // split by comma first, then support ranges with '-'
191 var parts = t.split(',');
192 for ( var i = 0; i < parts.length; i++ ) {
193 var p = parts[i];
194 if ( ! p ) { continue; }
195 var rng = p.split('-');
196 if ( rng.length === 2 ) {
197 var a = map[ rng[0] ] || parseInt( rng[0], 10 );
198 var b = map[ rng[1] ] || parseInt( rng[1], 10 );
199 if ( isFinite( a ) && isFinite( b ) ) { push_range( a, b, out ); }
200 } else {
201 var v = map[ p ] || parseInt( p, 10 );
202 if ( isFinite( v ) ) { out.push( v ); }
203 }
204 }
205 // sanitize to 1..7 unique
206 var seen = {}; out = out.filter( function(n){ if ( n < 1 || n > 7 || seen[n] ){ return false; } seen[n] = true; return true; } );
207 return out.length ? out : [1,2,3,4,5,6,7];
208 }
209
210 /**
211 * Initialize grid (rows, painting, events) and wire toolbar actions.
212 */
213 function init_grid_controller( inspector_root, data_obj, update_hidden_json_cb ) {
214 if ( inspector_root.__wpbc_timegrid_inited ) { return; }
215 inspector_root.__wpbc_timegrid_inited = true;
216
217 var grid_root = inspector_root.querySelector( '.wpbc_bfb__timegrid_root' );
218 var body_el = grid_root.querySelector( '.wpbc_bfb__timegrid_body' );
219 var start_time_in = inspector_root.querySelector( '[data-inspector-key="start_time"]' );
220 var end_time_in = inspector_root.querySelector( '[data-inspector-key="end_time"]' );
221 var step_in = inspector_root.querySelector( '[data-inspector-key="step_minutes"]' );
222 var week_cycle_in = inspector_root.querySelector( '.js-week-cycle' );
223 var profile_sel = inspector_root.querySelector( '.js-profile-select' );
224
225 var btn_rename = inspector_root.querySelector( '.js-profile-rename' );
226 var btn_duplicate = inspector_root.querySelector( '.js-profile-duplicate' );
227 var btn_clear = inspector_root.querySelector( '.js-profile-clear' );
228 var btn_row_fill = inspector_root.querySelector( '.js-row-fill' );
229 var btn_col_fill = inspector_root.querySelector( '.js-col-fill' );
230 var btn_copy_day = inspector_root.querySelector( '.js-copy-prev-day' );
231 var btn_copy_week = inspector_root.querySelector( '.js-copy-prev-week' );
232
233 function get_state() {
234 var start_min = time_to_min( ( start_time_in && start_time_in.value ) || '08:00' );
235 var end_min = time_to_min( ( end_time_in && end_time_in.value ) || '20:00' );
236 var step = normalize_step( ( step_in && step_in.value ) || 30 );
237 if ( start_min == null ) { start_min = 8 * 60; }
238 if ( end_min == null ) { end_min = 20 * 60; }
239 if ( end_min <= start_min ) { end_min = start_min + step; }
240 return { start_min: start_min, end_min: end_min, step: step };
241 }
242
243 function refresh_profile_selector() {
244 profile_sel.innerHTML = '';
245 ( data_obj.profiles || [] ).forEach( function( p, i ) {
246 var opt = d.createElement( 'option' );
247 opt.value = '' + i;
248 opt.textContent = ( p && p.label ) ? p.label : ( 'Week ' + ( i + 1 ) );
249 profile_sel.appendChild( opt );
250 } );
251 if ( data_obj.profiles.length ) { profile_sel.value = '0'; }
252 }
253
254 function current_profile_index() {
255 var idx = parseInt( profile_sel.value || '0', 10 );
256 if ( ! isFinite( idx ) || idx < 0 ) { idx = 0; }
257 if ( idx >= data_obj.profiles.length ) { idx = data_obj.profiles.length - 1; }
258 return idx;
259 }
260
261 function current_profile() {
262 return data_obj.profiles[ current_profile_index() ];
263 }
264
265 function rebuild_rows_and_paint() {
266 var st = get_state();
267 render_grid_rows( body_el, st.start_min, st.end_min, st.step );
268 paint_selection( grid_root, current_profile(), st.start_min, st.end_min, st.step );
269 }
270
271 function persist_profiles() {
272 var st = get_state();
273 read_selection_into_profile( grid_root, current_profile(), st.start_min, st.end_min, st.step );
274 update_hidden_json_cb( data_obj.profiles );
275 }
276
277 /* ---------- Toolbar actions (buttons) ---------- */
278
279 // Rename profile
280 if ( btn_rename ) {
281 btn_rename.addEventListener( 'click', function() {
282 var p = current_profile(); if ( ! p ) { return; }
283 var nv = w.prompt( 'Profile name:', p.label || '' );
284 if ( nv != null ) {
285 p.label = ( '' + nv ).trim() || p.label;
286 var keep = current_profile_index();
287 refresh_profile_selector();
288 profile_sel.value = '' + keep;
289 persist_profiles();
290 }
291 } );
292 }
293
294 // Duplicate profile (max 6, per UI)
295 if ( btn_duplicate ) {
296 btn_duplicate.addEventListener( 'click', function() {
297 if ( data_obj.profiles.length >= 6 ) {
298 w.alert( 'Maximum profiles reached (6).' );
299 return;
300 }
301 var idx = current_profile_index();
302 var p = current_profile();
303 var copy = JSON.parse( JSON.stringify( p ) );
304 copy.label = ( p.label || 'Week' ) + ' (copy)';
305 data_obj.profiles.splice( idx + 1, 0, copy );
306
307 // Adjust cycle to fit
308 var wc = parseInt( ( week_cycle_in && week_cycle_in.value ) || '1', 10 );
309 if ( ! isFinite( wc ) || wc < 1 ) { wc = 1; }
310 wc = Math.min( 6, Math.max( wc, data_obj.profiles.length ) );
311 if ( week_cycle_in ) { week_cycle_in.value = '' + wc; }
312 data_obj.week_cycle = wc;
313
314 refresh_profile_selector();
315 profile_sel.value = '' + ( idx + 1 );
316 rebuild_rows_and_paint();
317 persist_profiles();
318 } );
319 }
320
321 // Clear all slots in current profile
322 if ( btn_clear ) {
323 btn_clear.addEventListener( 'click', function() {
324 var p = current_profile(); if ( ! p ) { return; }
325 p.slots = { '1':[], '2':[], '3':[], '4':[], '5':[], '6':[], '7':[] };
326 rebuild_rows_and_paint();
327 persist_profiles();
328 } );
329 }
330
331 // Copy ← day : copy day-1 into day, for 2..7
332 if ( btn_copy_day ) {
333 btn_copy_day.addEventListener( 'click', function() {
334 var p = current_profile(); if ( ! p ) { return; }
335 var st = get_state();
336 read_selection_into_profile( grid_root, p, st.start_min, st.end_min, st.step );
337 for ( var d_i = 2; d_i <= 7; d_i++ ) {
338 p.slots[ String( d_i ) ] = JSON.parse( JSON.stringify( p.slots[ String( d_i - 1 ) ] || [] ) );
339 }
340 rebuild_rows_and_paint();
341 persist_profiles();
342 } );
343 }
344
345 // Copy ← week : copy previous profile slots into current
346 if ( btn_copy_week ) {
347 btn_copy_week.addEventListener( 'click', function() {
348 var idx = current_profile_index();
349 if ( idx <= 0 ) { return; }
350 var prev = data_obj.profiles[ idx - 1 ];
351 var cur = data_obj.profiles[ idx ];
352 cur.slots = JSON.parse( JSON.stringify( prev && prev.slots ? prev.slots : { '1':[], '2':[], '3':[], '4':[], '5':[], '6':[], '7':[] } ) );
353 rebuild_rows_and_paint();
354 persist_profiles();
355 } );
356 }
357
358 // Fill row… : prompt for time range; apply across ALL days
359 if ( btn_row_fill ) {
360 btn_row_fill.addEventListener( 'click', function() {
361 var st = get_state();
362 var rng = w.prompt( 'Enter time range to fill across ALL days (e.g. 09:00-12:00):', '' );
363 if ( ! rng ) { return; }
364 var mm = rng.replace(/\s+/g,'').split('-');
365 if ( mm.length !== 2 ) { return; }
366 var a = time_to_min( mm[0] ), b = time_to_min( mm[1] );
367 if ( a == null || b == null ) { return; }
368 a = snap_to_step( a, st.step, 'down' );
369 b = snap_to_step( b, st.step, 'up' );
370 if ( b <= a ) { return; }
371 // paint DOM rectangle then persist (automatic merge)
372 toggle_rect( grid_root, 1, 7, a, b - st.step, 'on' );
373 persist_profiles();
374 paint_selection( grid_root, current_profile(), st.start_min, st.end_min, st.step );
375 } );
376 }
377
378 // Fill column… : prompt for time range and days (e.g. "Mon-Fri" or "1,3,5")
379 if ( btn_col_fill ) {
380 btn_col_fill.addEventListener( 'click', function() {
381 var st = get_state();
382 var rng = w.prompt( 'Enter time range (e.g. 10:00-14:00):', '' );
383 if ( ! rng ) { return; }
384 var days = w.prompt( 'Enter days (Mon-Fri, 1-7, Mon,Wed,Fri). Leave empty for Mon-Fri:', 'Mon-Fri' );
385 var day_list = parse_days_input( days || 'Mon-Fri' );
386
387 var mm = rng.replace(/\s+/g,'').split('-');
388 if ( mm.length !== 2 ) { return; }
389 var a = time_to_min( mm[0] ), b = time_to_min( mm[1] );
390 if ( a == null || b == null ) { return; }
391 a = snap_to_step( a, st.step, 'down' );
392 b = snap_to_step( b, st.step, 'up' );
393 if ( b <= a ) { return; }
394
395 // apply to selected days only
396 for ( var i = 0; i < day_list.length; i++ ) {
397 var d_i = day_list[i];
398 toggle_rect( grid_root, d_i, d_i, a, b - st.step, 'on' );
399 }
400 persist_profiles();
401 paint_selection( grid_root, current_profile(), st.start_min, st.end_min, st.step );
402 } );
403 }
404
405 /* ---------- Core UI bindings ---------- */
406
407 // Week cycle
408 if ( week_cycle_in ) {
409 week_cycle_in.addEventListener( 'change', function() {
410 var wc = parseInt( week_cycle_in.value || '1', 10 );
411 if ( ! isFinite( wc ) || wc < 1 ) { wc = 1; }
412 if ( wc > 6 ) { wc = 6; }
413 data_obj.week_cycle = wc;
414 data_obj.profiles = reconcile_profiles( data_obj.profiles || [], wc );
415 var keep_idx = 0;
416 refresh_profile_selector();
417 profile_sel.value = '' + keep_idx;
418 rebuild_rows_and_paint();
419 persist_profiles();
420 } );
421 }
422
423 // Profile switch
424 if ( profile_sel ) {
425 profile_sel.addEventListener( 'change', function() {
426 rebuild_rows_and_paint();
427 } );
428 }
429
430 // Time range / step
431 [ start_time_in, end_time_in, step_in ].forEach( function( el ) {
432 if ( el ) {
433 el.addEventListener( 'change', function() {
434 rebuild_rows_and_paint();
435 persist_profiles();
436 } );
437 }
438 } );
439
440 // Drag selection
441 var drag_state = null;
442 body_el.addEventListener( 'mousedown', function( ev ) {
443 var cell = ev.target && ev.target.closest && ev.target.closest( '.wpbc_bfb__timegrid_cell--slot' );
444 if ( ! cell ) { return; }
445 var day = parseInt( cell.getAttribute( 'data-day' ), 10 );
446 var mm = parseInt( cell.getAttribute( 'data-minute' ), 10 );
447 var will_on = ! cell.classList.contains( 'is-on' );
448 drag_state = { day0: day, mm0: mm, mode: will_on ? 'on' : 'off' };
449 toggle_rect( grid_root, day, day, mm, mm, drag_state.mode );
450 ev.preventDefault();
451 } );
452 body_el.addEventListener( 'mouseover', function( ev ) {
453 if ( ! drag_state ) { return; }
454 var cell = ev.target && ev.target.closest && ev.target.closest( '.wpbc_bfb__timegrid_cell--slot' );
455 if ( ! cell ) { return; }
456 var day = parseInt( cell.getAttribute( 'data-day' ), 10 );
457 var mm = parseInt( cell.getAttribute( 'data-minute' ), 10 );
458 toggle_rect( grid_root, drag_state.day0, day, drag_state.mm0, mm, drag_state.mode );
459 } );
460 w.addEventListener( 'mouseup', function() {
461 if ( drag_state ) {
462 drag_state = null;
463 persist_profiles();
464 }
465 } );
466
467 // First render
468 data_obj.week_cycle = parseInt( data_obj.week_cycle || 1, 10 );
469 if ( ! isFinite( data_obj.week_cycle ) || data_obj.week_cycle < 1 ) { data_obj.week_cycle = 1; }
470 if ( data_obj.week_cycle > 6 ) { data_obj.week_cycle = 6; }
471 data_obj.profiles = reconcile_profiles( Array.isArray( data_obj.profiles ) ? data_obj.profiles : [], data_obj.week_cycle );
472
473 refresh_profile_selector();
474 rebuild_rows_and_paint();
475 persist_profiles();
476 }
477
478 /* ====================== Registration (deferred) ====================== */
479
480 function with_registry( cb ) {
481 var tries = 0, max_tries = 200;
482 (function loop() {
483 var Core = w.WPBC_BFB_Core || {};
484 var Registry = Core.WPBC_BFB_Field_Renderer_Registry;
485 var Field_Base = Core.WPBC_BFB_Field_Base || Core.WPBC_BFB_Select_Base;
486 if ( Registry && Registry.register && Field_Base ) {
487 cb( Registry, Field_Base );
488 } else if ( tries++ < max_tries ) {
489 setTimeout( loop, 50 );
490 } else if ( w.console ) {
491 w.console.error( '[WPBC][time_slots] Core/Registry not available.' );
492 }
493 })();
494 }
495
496 function register_time_slots() {
497 with_registry( function( Registry, Field_Base ) {
498 class wpbc_bfb_field_time_slots extends Field_Base {
499 static template_id = 'wpbc-bfb-field-time-slots';
500 static kind = 'time_slots';
501 static get_defaults() {
502 var d = ( super.get_defaults ? super.get_defaults() : {} );
503 return Object.assign( {}, d, {
504 type : 'time_slots',
505 label : 'Time Slots',
506 cssclass : '',
507 help : '',
508 start_time : '08:00',
509 end_time : '20:00',
510 step_minutes: 30,
511 week_cycle : 1,
512 profiles : [
513 { key: 'A', label: 'Week A', slots: { '1':[], '2':[], '3':[], '4':[], '5':[], '6':[], '7':[] } }
514 ],
515 min_width : '320px'
516 } );
517 }
518 }
519 try { Registry.register( 'time_slots', wpbc_bfb_field_time_slots ); }
520 catch ( e ) { if ( w.console ) { w.console.error( '[WPBC][time_slots] register failed:', e ); } }
521 w.WPBC_BFB_Field_Time_Slots = wpbc_bfb_field_time_slots; // for debugging
522 } );
523 }
524 register_time_slots();
525
526 /* ====================== Inspector init bindings ====================== */
527
528 function try_init_on_panel( panel ) {
529 if ( ! panel ) { return; }
530 var grid_root = panel.querySelector( '.wpbc_bfb__timegrid_root' );
531 var json_el = panel.querySelector( '.js-profiles-json' );
532 if ( ! grid_root || ! json_el ) { return; }
533 if ( panel.__wpbc_timegrid_inited ) { return; }
534
535 var profiles = [];
536 try { profiles = JSON.parse( json_el.value || '[]' ); } catch (e) { profiles = []; }
537
538 function update_hidden_json( profiles_arr ) {
539 try {
540 json_el.value = JSON.stringify( profiles_arr || [] );
541 var evt = d.createEvent( 'HTMLEvents' ); evt.initEvent( 'change', true, false ); json_el.dispatchEvent( evt );
542 } catch (e) {}
543 }
544
545 var data_obj = {
546 start_time : ( ( panel.querySelector( '[data-inspector-key="start_time"]' ) || {} ).value ) || '08:00',
547 end_time : ( ( panel.querySelector( '[data-inspector-key="end_time"]' ) || {} ).value ) || '20:00',
548 step_minutes: parseInt( ( ( panel.querySelector( '[data-inspector-key="step_minutes"]' ) || {} ).value ) || '30', 10 ),
549 week_cycle : parseInt( ( ( panel.querySelector( '.js-week-cycle' ) || {} ).value ) || '1', 10 ),
550 profiles : profiles
551 };
552
553 init_grid_controller( panel, data_obj, update_hidden_json );
554 }
555
556 d.addEventListener( 'wpbc_bfb_inspector_ready', function( ev ) {
557 try { try_init_on_panel( ev && ev.detail && ev.detail.panel ); } catch (e) {}
558 } );
559
560 d.addEventListener( 'DOMContentLoaded', function() {
561 var panels = d.querySelectorAll( '.wpbc_bfb__inspector__body' );
562 for ( var i = 0; i < panels.length; i++ ) { try_init_on_panel( panels[ i ] ); }
563 } );
564
565 try {
566 var mo = new MutationObserver( function( muts ) {
567 for ( var i = 0; i < muts.length; i++ ) {
568 var n = muts[ i ].target;
569 if ( ! n ) { continue; }
570 if ( n.classList && n.classList.contains( 'wpbc_bfb__inspector__body' ) ) {
571 try_init_on_panel( n );
572 } else if ( n.querySelector ) {
573 var p = n.querySelector( '.wpbc_bfb__inspector__body' );
574 if ( p ) { try_init_on_panel( p ); }
575 }
576 }
577 } );
578 mo.observe( d.documentElement, { childList: true, subtree: true } );
579 } catch (e) {}
580
581 /* ====================== Minimal CSS (scoped) ====================== */
582
583 var css = ''
584 + '.wpbc_bfb__time_preview__week{border:1px solid #e5e5e5;border-radius:6px;padding:8px;margin:8px 0;}'
585 + '.wpbc_bfb__time_preview__week_title{margin-bottom:6px;}'
586 + '.wpbc_bfb__time_preview__row{display:flex;align-items:center;margin:2px 0;}'
587 + '.wpbc_bfb__time_preview__day{width:42px;opacity:.8;font-size:12px;}'
588 + '.wpbc_bfb__time_badge{display:inline-block;border:1px solid #d0d0d0;border-radius:12px;padding:2px 8px;margin:0 4px 4px 0;font-size:11px;background:#f7f7f7;}'
589 + '.wpbc_bfb__time_badge--empty{opacity:.6;}'
590 + '.wpbc_bfb__timegrid_root{border:1px solid #e1e1e1;border-radius:6px;overflow:hidden;margin-top:6px;}'
591 + '.wpbc_bfb__timegrid_head,.wpbc_bfb__timegrid_row{display:grid;grid-template-columns:80px repeat(7,1fr);}'
592 + '.wpbc_bfb__timegrid_cell{border-bottom:1px solid #f0f0f0;border-right:1px solid #f5f5f5;padding:4px;min-height:24px;box-sizing:border-box;}'
593 + '.wpbc_bfb__timegrid_cell--corner{background:#fafafa;}'
594 + '.wpbc_bfb__timegrid_cell--day{background:#fafafa;text-align:center;font-weight:600;}'
595 + '.wpbc_bfb__timegrid_cell--time{background:#fafafa;font-variant-numeric:tabular-nums;}'
596 + '.wpbc_bfb__timegrid_cell--slot{cursor:crosshair;}'
597 + '.wpbc_bfb__timegrid_cell--slot.is-on{background:rgba(0,120,212,.12);outline:1px solid rgba(0,120,212,.35);}'
598 + '.wpbc_bfb__timegrid_toolbar{display:flex;justify-content:space-between;gap:8px;margin:8px 0;}'
599 + '.wpbc_bfb__timegrid_profiles select{min-width:140px;}';
600
601 try { var st = d.createElement( 'style' ); st.type = 'text/css'; st.appendChild( d.createTextNode( css ) ); d.head.appendChild( st ); } catch (e) {}
602
603 })( window, document );
604