PluginProbe
Booking Calendar / 11.9
Booking Calendar v11.9
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 / preview / _src / bfb-inline-preview.js

bfb-inline-preview.js in Booking Calendar 11.9, at includes/page-form-builder/preview/_src/bfb-inline-preview.js

312 lines 10.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * Replace request-local Form Builder previews using a data-only WPBC bootstrap.
3 *
4 * The helper owns only domain-neutral DOM and Booking Calendar initialization
5 * mechanics. It never evaluates returned JavaScript. Callers remain responsible
6 * for authorization, request validation, stale-response ownership, and visible
7 * user interface state.
8 *
9 * @package Booking Calendar
10 */
11 ( function ( window, document, $ ) {
12 'use strict';
13
14 /**
15 * Return a rejected jQuery promise with one controlled message.
16 *
17 * @param {string} message Error message.
18 * @return {Object} Rejected promise.
19 */
20 function rejected_preview( message ) {
21 var deferred = $.Deferred();
22
23 deferred.reject( new Error( message || '' ) );
24 return deferred.promise();
25 }
26
27 /**
28 * Destroy Datepick instances before their preview DOM is removed.
29 *
30 * @param {HTMLElement} container Inline preview content container.
31 * @return {void}
32 */
33 function cleanup_calendars( container ) {
34 if ( ! container || ! $.datepick ) {
35 return;
36 }
37
38 $( container ).find( '[id^="calendar_booking"]' ).each( function () {
39 var $calendar = $( this );
40
41 if ( typeof $calendar.datepick !== 'function' ) {
42 return;
43 }
44
45 try {
46 if ( 'function' === typeof $.datepick._getInst && $.datepick._getInst( this ) ) {
47 $calendar.datepick( 'destroy' );
48 }
49 } catch ( error ) {
50 $calendar.removeClass( 'hasDatepick' );
51 }
52 } );
53 }
54
55 /**
56 * Convert one value to a finite integer with a bounded fallback.
57 *
58 * @param {*} value Candidate value.
59 * @param {number} fallback Fallback value.
60 * @return {number} Normalized integer.
61 */
62 function to_integer( value, fallback ) {
63 var parsed = parseInt( value, 10 );
64
65 return isFinite( parsed ) ? parsed : fallback;
66 }
67
68 /**
69 * Copy only scalar request values from the server-owned bootstrap contract.
70 *
71 * @param {Object} request_values Candidate request values.
72 * @return {Object} Allow-listed calendar AJAX request.
73 */
74 function get_calendar_request( request_values ) {
75 var source = request_values && 'object' === typeof request_values ? request_values : {};
76 var request = {
77 resource_id: Math.max( 1, to_integer( source.resource_id, 1 ) ),
78 booking_hash: String( source.booking_hash || '' ),
79 request_uri: String( source.request_uri || '' ),
80 custom_form: String( source.custom_form || 'standard' ),
81 aggregate_resource_id_str: String( source.aggregate_resource_id_str || '' ),
82 aggregate_type: String( source.aggregate_type || 'all' ),
83 skip_general_availability: source.skip_general_availability ? 1 : 0,
84 classic_booking_context_token: String( source.classic_booking_context_token || '' )
85 };
86 var marker_keys = [
87 'wpbc_settings_calendar_preview',
88 'wpbc_setup_wizard_date_selection_preview'
89 ];
90 var toggle_keys = [
91 'wpbc_settings_calendar_preview_changeover',
92 'wpbc_settings_calendar_preview_triangles',
93 'wpbc_settings_calendar_preview_recurrent_time',
94 'wpbc_settings_calendar_preview_last_checkout',
95 'wpbc_settings_calendar_preview_show_legend',
96 'wpbc_settings_calendar_preview_legend_show_numbers',
97 'wpbc_settings_calendar_preview_legend_vertical',
98 'wpbc_settings_calendar_preview_legend_show_available',
99 'wpbc_settings_calendar_preview_legend_show_pending',
100 'wpbc_settings_calendar_preview_legend_show_approved',
101 'wpbc_settings_calendar_preview_legend_show_partially',
102 'wpbc_settings_calendar_preview_legend_show_unavailable'
103 ];
104 var text_keys = [
105 'wpbc_setup_wizard_date_selection_preview_nonce',
106 'wpbc_settings_calendar_preview_legend_text_available',
107 'wpbc_settings_calendar_preview_legend_text_pending',
108 'wpbc_settings_calendar_preview_legend_text_approved',
109 'wpbc_settings_calendar_preview_legend_text_partially',
110 'wpbc_settings_calendar_preview_legend_text_unavailable'
111 ];
112
113 $.each( marker_keys, function ( index, key ) {
114 if ( source[ key ] ) {
115 request[ key ] = 1;
116 }
117 } );
118 $.each( toggle_keys, function ( index, key ) {
119 if ( 'On' === source[ key ] || 'Off' === source[ key ] ) {
120 request[ key ] = source[ key ];
121 }
122 } );
123 $.each( text_keys, function ( index, key ) {
124 if ( Object.prototype.hasOwnProperty.call( source, key ) ) {
125 request[ key ] = String( source[ key ] || '' );
126 }
127 } );
128
129 return request;
130 }
131
132 /**
133 * Set one calendar parameter through the established WPBC state API.
134 *
135 * @param {number} resource_id Booking resource ID.
136 * @param {string} key Allow-listed parameter key.
137 * @param {*} value JSON-safe parameter value.
138 * @return {void}
139 */
140 function set_calendar_parameter( resource_id, key, value ) {
141 if ( window._wpbc && 'function' === typeof window._wpbc.calendar__set_param_value ) {
142 window._wpbc.calendar__set_param_value( resource_id, key, value );
143 }
144 }
145
146 /**
147 * Apply the fixed calendar-parameter contract returned by the server.
148 *
149 * @param {number} resource_id Booking resource ID.
150 * @param {Object} calendar_parameters Validated server parameter map.
151 * @return {void}
152 */
153 function apply_calendar_parameters( resource_id, calendar_parameters ) {
154 var parameters = calendar_parameters && 'object' === typeof calendar_parameters ? calendar_parameters : {};
155 var allowed_keys = [
156 'is_enabled_change_over',
157 'calendar_scroll_to',
158 'calendar_dates_start',
159 'calendar_dates_end',
160 'booking_max_monthes_in_calendar',
161 'booking_start_day_weeek',
162 'calendar_number_of_months',
163 'days_select_mode',
164 'fixed__days_num',
165 'fixed__week_days__start',
166 'dynamic__days_min',
167 'dynamic__days_max',
168 'dynamic__days_specific',
169 'dynamic__week_days__start',
170 'range_selection_guidance_is_enabled',
171 'booking_date_format',
172 'booking_time_format',
173 'is_parent_resource',
174 'booking_capacity_field',
175 'booking_is_dissbale_booking_for_different_sub_resources',
176 'booking_recurrent_time'
177 ];
178
179 $.each( allowed_keys, function ( index, key ) {
180 if ( Object.prototype.hasOwnProperty.call( parameters, key ) ) {
181 set_calendar_parameter( resource_id, key, parameters[ key ] );
182 }
183 } );
184 }
185
186 /**
187 * Initialize a rendered form through known Booking Calendar functions only.
188 *
189 * @param {Object} bootstrap JSON-safe bootstrap data returned by WPBC.
190 * @return {Object} Resolved or rejected jQuery promise.
191 */
192 function initialize_booking_form( bootstrap ) {
193 var config = bootstrap && 'object' === typeof bootstrap ? bootstrap : {};
194 var calendar_request = get_calendar_request( config.calendar_request );
195 var resource_id = calendar_request.resource_id;
196 var secure_parameters = config.secure_parameters && 'object' === typeof config.secure_parameters ? config.secure_parameters : {};
197
198 if (
199 ! window._wpbc ||
200 'function' !== typeof window.wpbc_calendar_show ||
201 'function' !== typeof window.wpbc_calendar__load_data__ajx ||
202 'function' !== typeof window._wpbc.set_secure_param
203 ) {
204 return rejected_preview( '' );
205 }
206
207 if ( 'function' === typeof window._wpbc.balancer__set_max_threads ) {
208 window._wpbc.balancer__set_max_threads( Math.max( 1, to_integer( config.balancer_max_threads, 1 ) ) );
209 }
210
211 apply_calendar_parameters( resource_id, config.calendar_parameters );
212
213 if ( window._wpbc.booking__set_param_value && 'function' === typeof window._wpbc.booking__set_param_value ) {
214 window._wpbc.booking__set_param_value(
215 resource_id,
216 'classic_booking_context_token',
217 String( config.classic_booking_context_token || '' )
218 );
219 }
220
221 if ( 'function' === typeof window._wpbc.set_other_param && Object.prototype.hasOwnProperty.call( config, 'is_enabled_change_over' ) ) {
222 window._wpbc.set_other_param( 'is_enabled_change_over', Boolean( config.is_enabled_change_over ) );
223 }
224
225 if ( 'function' === typeof window.wpbc__conditions__SAVE_INITIAL__days_selection_params__bm ) {
226 window.wpbc__conditions__SAVE_INITIAL__days_selection_params__bm( resource_id );
227 }
228
229 window.wpbc_calendar_show( String( resource_id ) );
230 window._wpbc.set_secure_param( 'nonce', String( secure_parameters.nonce || '' ) );
231 window._wpbc.set_secure_param( 'user_id', String( secure_parameters.user_id || '' ) );
232 window._wpbc.set_secure_param( 'locale', String( secure_parameters.locale || '' ) );
233 window.wpbc_calendar__load_data__ajx( calendar_request );
234
235 if ( 'function' === typeof window.wpbc_hook__init_booking_form_wizard_buttons ) {
236 window.wpbc_hook__init_booking_form_wizard_buttons();
237 }
238
239 return $.Deferred().resolve().promise();
240 }
241
242 /**
243 * Create a reusable inline Form Builder preview renderer.
244 *
245 * Only server-rendered HTML and structured bootstrap data from an authorized
246 * WPBC endpoint may be passed to `replace()`. Script elements are always
247 * discarded; this component never evaluates response code.
248 *
249 * @param {HTMLElement} container Inline preview content container.
250 * @return {Object|null} Preview renderer API, or null for invalid input.
251 */
252 function create_inline_preview( container ) {
253 var replacement_sequence = 0;
254
255 if ( ! container || 1 !== container.nodeType ) {
256 return null;
257 }
258
259 container.setAttribute( 'data-wpbc-inline-booking-preview', '1' );
260
261 /**
262 * Replace preview markup and initialize its known WPBC components.
263 *
264 * @param {string} html Trusted server-rendered preview HTML.
265 * @param {Object} bootstrap JSON-safe WPBC bootstrap contract.
266 * @param {Function} owns_request Callback returning whether the request remains current.
267 * @return {Object} jQuery promise.
268 */
269 function replace( html, bootstrap, owns_request ) {
270 var replacement_id = ++replacement_sequence;
271 var parsed = $.parseHTML( String( html || '' ), document, false ) || [];
272 var $parsed_container = $( '<div>' ).append( parsed );
273 var owns_preview = function () {
274 return replacement_id === replacement_sequence && ( ! owns_request || owns_request() );
275 };
276
277 if ( ! owns_preview() ) {
278 return rejected_preview( '' );
279 }
280
281 $parsed_container.find( 'script' ).addBack( 'script' ).remove();
282 cleanup_calendars( container );
283 $( container ).empty().append( $parsed_container.contents() );
284
285 if ( ! owns_preview() ) {
286 return rejected_preview( '' );
287 }
288
289 return initialize_booking_form( bootstrap );
290 }
291
292 /**
293 * Invalidate pending replacements and release calendar instances.
294 *
295 * @return {void}
296 */
297 function destroy() {
298 replacement_sequence += 1;
299 cleanup_calendars( container );
300 }
301
302 return {
303 replace: replace,
304 destroy: destroy
305 };
306 }
307
308 window.wpbc_bfb_inline_preview = {
309 create: create_inline_preview
310 };
311 }( window, document, window.jQuery ) );
312