booking
/
includes
/
page-form-builder
/
field-packs
/
time-duration
/
_src
/
field-durationtime-wptpl.js
field-durationtime-wptpl.js in Booking Calendar 11.0, at includes/page-form-builder/field-packs/time-duration/_src/field-durationtime-wptpl.js
| 1 | // --------------------------------------------------------------------------------------------------------------------- |
| 2 | // == File /includes/page-form-builder/field-packs/time-durationtime/_out/field-durationtime-wptpl.js |
| 3 | // == Pack Duration Time (WP-template–driven) — reuses Start Time inspector logic via shared selectors |
| 4 | // == Depends on Core: WPBC_BFB_Field_Base, Field_Renderer_Registry, Core.Time utils, StartTime pack (events) |
| 5 | // == Version 1.0.3 (04.11.2025 13:22) |
| 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_durationtime', 'Missing Core registry/base/time-utils' ); |
| 18 | } |
| 19 | return; |
| 20 | } |
| 21 | |
| 22 | // --------------------------------------------------------------------- |
| 23 | // Helpers (label humanizer + row relabel) |
| 24 | // --------------------------------------------------------------------- |
| 25 | function humanize_minutes_label( totalMins ){ |
| 26 | if ( isNaN(totalMins) || totalMins < 0 ) { return ''; } |
| 27 | var h = Math.floor( totalMins / 60 ); |
| 28 | var m = totalMins % 60; |
| 29 | |
| 30 | // i18n fallbacks |
| 31 | var i18n_minutes = w.wpbc_i18n_minutes || 'minutes'; |
| 32 | var i18n_min = w.wpbc_i18n_min || 'min'; |
| 33 | var i18n_h = w.wpbc_i18n_h || 'h'; |
| 34 | |
| 35 | if ( h === 0 ) { |
| 36 | // "15 minutes" |
| 37 | return String( m ) + ' ' + i18n_minutes; |
| 38 | } |
| 39 | // "1h" or "1h 15m" |
| 40 | return ( h + i18n_h ) + ( m > 0 ? ( ' ' + m + (w.wpbc_i18n_m || 'm') ) : '' ); |
| 41 | } |
| 42 | |
| 43 | function relabel_rows_in_duration_panel( panel ){ |
| 44 | if ( ! panel ) { return; } |
| 45 | // Only touch our pack |
| 46 | var dtype = panel.getAttribute('data-type') || ''; |
| 47 | if ( dtype !== 'durationtime' ) { return; } |
| 48 | |
| 49 | var list = panel.querySelector('.wpbc_bfb__options_list'); |
| 50 | if ( ! list ) { return; } |
| 51 | |
| 52 | list.querySelectorAll('.wpbc_bfb__options_row').forEach(function(row){ |
| 53 | var valEl = row.querySelector('.wpbc_bfb__opt-value'); // authoritative 24h value holder |
| 54 | var timeEl = row.querySelector('.wpbc_bfb__opt-time'); // visible editor |
| 55 | var raw = (valEl && valEl.value) || (timeEl && timeEl.value) || ''; |
| 56 | var mins = Time.parse_hhmm_24h( String(raw) ); |
| 57 | if ( isNaN(mins) ) { return; } |
| 58 | |
| 59 | var labelEl = row.querySelector('.wpbc_bfb__opt-label'); |
| 60 | if ( ! labelEl ) { return; } |
| 61 | |
| 62 | // Set default humanized label (user can edit later; we do this only on generation) |
| 63 | labelEl.value = humanize_minutes_label( mins ); |
| 64 | }); |
| 65 | |
| 66 | // Re-sync the hidden JSON state after relabel |
| 67 | try { |
| 68 | var ST = w.WPBC_BFB_Field_StartTime; |
| 69 | if ( ST && typeof ST.sync_state_from_rows === 'function' ) { |
| 70 | ST.sync_state_from_rows( panel ); |
| 71 | } |
| 72 | } catch(e){} |
| 73 | } |
| 74 | |
| 75 | // --------------------------------------------------------------------- |
| 76 | // Event wiring (react to StartTime generator completion) |
| 77 | // --------------------------------------------------------------------- |
| 78 | (function bind_generation_listeners_once(){ |
| 79 | if ( w.__wpbc_dt_bound ) { return; } |
| 80 | w.__wpbc_dt_bound = true; |
| 81 | |
| 82 | // DOM CustomEvent path |
| 83 | d.addEventListener('wpbc_bfb_time_options_generated', function(ev){ |
| 84 | try { |
| 85 | var panel = ev && ev.target; |
| 86 | var detail= ev && ev.detail || {}; |
| 87 | // Only for Duration Time panels |
| 88 | var dtype = (detail && detail.type) || (panel && panel.getAttribute('data-type')) || ''; |
| 89 | if ( dtype !== 'durationtime' ) { return; } |
| 90 | relabel_rows_in_duration_panel( panel ); |
| 91 | } catch(e){} |
| 92 | }, true); |
| 93 | |
| 94 | // Core bus path (if available) |
| 95 | try { |
| 96 | var EVNAME = (Core && Core.WPBC_BFB_Events && Core.WPBC_BFB_Events.TIME_OPTIONS_GENERATED) || 'wpbc_bfb:time_options_generated'; |
| 97 | if ( Core && Core.bus && typeof Core.bus.on === 'function' ) { |
| 98 | Core.bus.on( EVNAME, function(payload){ |
| 99 | try { |
| 100 | if ( !payload || payload.type !== 'durationtime' ) { return; } |
| 101 | relabel_rows_in_duration_panel( payload.panel ); |
| 102 | } catch(e){} |
| 103 | }); |
| 104 | } |
| 105 | } catch(e){} |
| 106 | })(); |
| 107 | |
| 108 | /** |
| 109 | * WPBC_BFB: Duration Time field renderer (template-driven). |
| 110 | * Inspector is structurally identical to Start Time, so we intentionally reuse: |
| 111 | * - root class: .wpbc_bfb__inspector_starttime (Start Time JS binds here) |
| 112 | * - option rows markup / state syncing (delegated to Start Time JS) |
| 113 | */ |
| 114 | const wpbc_bfb_field_durationtime = class extends Base { |
| 115 | |
| 116 | static template_id = 'wpbc-bfb-field-durationtime'; |
| 117 | static kind = 'durationtime'; |
| 118 | |
| 119 | static get_defaults(){ |
| 120 | return { |
| 121 | kind : 'field', |
| 122 | type : 'durationtime', |
| 123 | label : 'Duration time', |
| 124 | name : 'durationtime', |
| 125 | html_id : '', |
| 126 | required : true, |
| 127 | multiple : false, |
| 128 | size : null, |
| 129 | cssclass : '', |
| 130 | help : '', |
| 131 | default_value : '', |
| 132 | placeholder : '--- Select duration time ---', |
| 133 | value_differs : true, |
| 134 | min_width : '180px', |
| 135 | |
| 136 | // 24h labels by default make sense for durations (values are 24h, labels will be humanized on generation). |
| 137 | options : [ |
| 138 | { label: '00:15', value: '00:15', selected: false }, |
| 139 | { label: '00:30', value: '00:30', selected: false }, |
| 140 | { label: '00:45', value: '00:45', selected: false }, |
| 141 | { label: '01:00', value: '01:00', selected: false }, |
| 142 | { label: '01:30', value: '01:30', selected: false }, |
| 143 | { label: '02:00', value: '02:00', selected: false } |
| 144 | ], |
| 145 | |
| 146 | // Generator defaults (min/max duration). |
| 147 | // NOTE: We keep '24h' internally but hide the UI in the template (no AM/PM vs 24h for durations). |
| 148 | gen_label_fmt : '24h', |
| 149 | gen_start_24h : '00:15', |
| 150 | gen_end_24h : '06:00', |
| 151 | gen_start_ampm_t: '00:15', |
| 152 | gen_end_ampm_t : '06:00', |
| 153 | gen_step_h : 0, |
| 154 | gen_step_m : 15 |
| 155 | }; |
| 156 | } |
| 157 | |
| 158 | /** |
| 159 | * After the field is dropped from the palette. |
| 160 | * Locks "name" to 'durationtime' and ensures required=true for consistency. |
| 161 | * |
| 162 | * @param {Object} data |
| 163 | * @param {HTMLElement} el |
| 164 | * @param {Object} ctx |
| 165 | */ |
| 166 | static on_field_drop( data, el, ctx ){ |
| 167 | if ( super.on_field_drop ) { super.on_field_drop( data, el, ctx ); } |
| 168 | try { |
| 169 | if ( data && typeof data === 'object' ) { |
| 170 | data.name = 'durationtime'; |
| 171 | data.required = true; |
| 172 | if ( ! data.placeholder ) { |
| 173 | data.placeholder = '--- ' + ( (w.wpbc_i18n_select_durationtime) || 'Select duration time' ) + ' ---'; |
| 174 | } |
| 175 | // Keep 24h format internally; the UI radios are hidden in the template. |
| 176 | data.gen_label_fmt = data.gen_label_fmt || '24h'; |
| 177 | } |
| 178 | if ( el && el.dataset ) { |
| 179 | el.dataset.name = 'durationtime'; |
| 180 | el.dataset.autoname = '0'; |
| 181 | el.dataset.fresh = '0'; |
| 182 | el.dataset.name_user_touched = '1'; |
| 183 | el.setAttribute( 'data-required', 'true' ); |
| 184 | } |
| 185 | var sel = el && el.querySelector( 'select.wpbc_bfb__preview-timepicker' ); |
| 186 | if ( sel ) { sel.setAttribute( 'name', 'durationtime' ); } |
| 187 | } catch(e){} |
| 188 | } |
| 189 | }; |
| 190 | |
| 191 | try { Registry.register( 'durationtime', wpbc_bfb_field_durationtime ); } |
| 192 | catch ( e ) { if ( w._wpbc && w._wpbc.dev && w._wpbc.dev.error ) { w._wpbc.dev.error( 'wpbc_bfb_field_durationtime.register', e ); } } |
| 193 | |
| 194 | // No extra bindings; Start Time pack listeners handle the inspector via shared classes. |
| 195 | // This file only post-processes generated options via events. |
| 196 | w.WPBC_BFB_Field_DurationTime = wpbc_bfb_field_durationtime; |
| 197 | |
| 198 | // ----------------------------------------------------------------------------------------------------------------- |
| 199 | // Export for "Booking Form" (Advanced Form shortcode) |
| 200 | // ----------------------------------------------------------------------------------------------------------------- |
| 201 | /** |
| 202 | * Booking Form exporter callback for "durationtime". |
| 203 | * |
| 204 | * Mirrors the legacy behavior: |
| 205 | * WPBC_BFB_Exporter.emit_time_select( name, field, req_mark, id_opt, cls_opts, emit_label_then ); |
| 206 | * |
| 207 | * So the final shortcode body and label handling are identical to the old |
| 208 | * switch/case path in builder-exporter.js, just moved into this pack. |
| 209 | */ |
| 210 | function register_durationtime_booking_form_exporter() { |
| 211 | |
| 212 | var Exp = w.WPBC_BFB_Exporter; |
| 213 | if ( ! Exp || typeof Exp.register !== 'function' ) { return; } |
| 214 | if ( typeof Exp.has_exporter === 'function' && Exp.has_exporter( 'durationtime' ) ) { return; } |
| 215 | |
| 216 | var S = Core.WPBC_BFB_Sanitize || {}; |
| 217 | var esc_html = S.escape_html || function( v ){ return String( v ); }; // kept for parity with Start/End Time |
| 218 | |
| 219 | /** |
| 220 | * @type {WPBC_BFB_ExporterCallback} |
| 221 | */ |
| 222 | var exporter_callback = function( field, emit, extras ) { |
| 223 | extras = extras || {}; |
| 224 | |
| 225 | var cfg = extras.cfg || {}; |
| 226 | var ctx = extras.ctx; |
| 227 | |
| 228 | // Shared label wrapper: same pattern as Start/End Time. |
| 229 | var emit_label_then = function( body ) { |
| 230 | // Preferred path: centralized helper in builder-exporter.js |
| 231 | if ( Exp && typeof Exp.emit_label_then === 'function' ) { |
| 232 | Exp.emit_label_then( field, emit, body, cfg ); |
| 233 | return; |
| 234 | } |
| 235 | // No fallback (aligned with Start/End Time packs). |
| 236 | }; |
| 237 | |
| 238 | // Required marker. |
| 239 | var is_req = Exp.is_required( field ); |
| 240 | var req_mark = is_req ? '*' : ''; |
| 241 | |
| 242 | // Name / id / classes from shared helpers so they stay in sync. |
| 243 | var name = Exp.compute_name( 'durationtime', field ); |
| 244 | var id_opt = Exp.id_option( field, ctx ); |
| 245 | var cls_opts = Exp.class_options( field ); |
| 246 | |
| 247 | // Dedicated time helper to keep exact legacy shortcode shape. |
| 248 | if ( typeof Exp.emit_time_select === 'function' ) { |
| 249 | Exp.emit_time_select( name, field, req_mark, id_opt, cls_opts, emit_label_then ); |
| 250 | return; |
| 251 | } |
| 252 | }; |
| 253 | |
| 254 | Exp.register( 'durationtime', exporter_callback ); |
| 255 | } |
| 256 | |
| 257 | if ( w.WPBC_BFB_Exporter && typeof w.WPBC_BFB_Exporter.register === 'function' ) { |
| 258 | register_durationtime_booking_form_exporter(); |
| 259 | } else if ( typeof document !== 'undefined' ) { |
| 260 | document.addEventListener( 'wpbc:bfb:exporter-ready', register_durationtime_booking_form_exporter, { once: true } ); |
| 261 | } |
| 262 | |
| 263 | |
| 264 | // ----------------------------------------------------------------------------------------------------------------- |
| 265 | // Export for "Booking Data" (Content of booking fields data) |
| 266 | // ----------------------------------------------------------------------------------------------------------------- |
| 267 | /** |
| 268 | * Booking Data exporter callback for "durationtime". |
| 269 | * |
| 270 | * Default behavior: |
| 271 | * <b>Label</b>: <f>[durationtime]</f><br> |
| 272 | * |
| 273 | * The exported token name is kept fully in sync with the Booking Form exporter |
| 274 | * via Exp.compute_name('durationtime', field). |
| 275 | */ |
| 276 | function register_durationtime_booking_data_exporter() { |
| 277 | |
| 278 | var C = w.WPBC_BFB_ContentExporter; |
| 279 | if ( ! C || typeof C.register !== 'function' ) { return; } |
| 280 | if ( typeof C.has_exporter === 'function' && C.has_exporter( 'durationtime' ) ) { return; } |
| 281 | |
| 282 | C.register( 'durationtime', function( field, emit, extras ) { |
| 283 | extras = extras || {}; |
| 284 | var cfg = extras.cfg || {}; |
| 285 | |
| 286 | var Exp = w.WPBC_BFB_Exporter; |
| 287 | if ( ! Exp || typeof Exp.compute_name !== 'function' ) { return; } |
| 288 | |
| 289 | // Base name, identical to Booking Form exporter. |
| 290 | var name = Exp.compute_name( 'durationtime', field ); |
| 291 | if ( ! name ) { return; } |
| 292 | |
| 293 | var raw_label = ( typeof field.label === 'string' ) ? field.label : ''; |
| 294 | var label = raw_label.trim() || name; |
| 295 | |
| 296 | // Build a combined token: [name] / [name_val] |
| 297 | // emit_line_bold_field will wrap it as: <f>[{token}]</f> |
| 298 | var token = name + '_val] / [' + name; |
| 299 | |
| 300 | // Result: <b>Label</b>: <f>[durationtime_val] / [durationtime]</f><br> |
| 301 | C.emit_line_bold_field( emit, label, token, cfg ); |
| 302 | } ); |
| 303 | } |
| 304 | |
| 305 | if ( w.WPBC_BFB_ContentExporter && typeof w.WPBC_BFB_ContentExporter.register === 'function' ) { |
| 306 | register_durationtime_booking_data_exporter(); |
| 307 | } else if ( typeof document !== 'undefined' ) { |
| 308 | document.addEventListener( 'wpbc:bfb:content-exporter-ready', register_durationtime_booking_data_exporter, { once: true } ); |
| 309 | } |
| 310 | |
| 311 | })( window, document ); |
| 312 |