PluginProbe
Booking Calendar / 11.5
Booking Calendar v11.5
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 10.11 All 204 releases
booking / includes / page-form-builder / admin-page-tpl / _src / bfb-templates.js

bfb-templates.js in Booking Calendar 11.5, at includes/page-form-builder/admin-page-tpl/_src/bfb-templates.js

339 lines 9.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * Booking Calendar — WP template helpers (BFB Admin)
3 *
4 * Provides a tiny wrapper around wp.template() and a safe "ensure in DOM" helper
5 * for templates that produce modal markup or other admin UI.
6 *
7 * Exposes under:
8 * WPBC_BFB_Core.UI.Templates:
9 * - render_wp_template( template_id, data )
10 * - ensure_dom_from_wp_template( template_id, dom_id?, data? )
11 * - ensure_dom_ref_from_wp_template( template_id, dom_id?, data? )
12 *
13 * Back-compat aliases (optional):
14 * - render()
15 * - ensure_dom()
16 * - ensure_dom_ref()
17 * @file ../includes/page-form-builder/admin-page-tpl/_out/bfb-templates.js
18 */
19 (function (w, d) {
20 'use strict';
21
22 const Core = (w.WPBC_BFB_Core = w.WPBC_BFB_Core || {});
23 const UI = (Core.UI = Core.UI || {});
24
25 /**
26 * Namespace for template helpers.
27 */
28 const Templates = (UI.Templates = UI.Templates || {});
29
30 /**
31 * Escape an id for querySelector usage.
32 *
33 * @param {string} s
34 * @returns {string}
35 */
36 function css_escape(s) {
37 s = String( s || '' );
38 if ( w.CSS && typeof w.CSS.escape === 'function' ) {
39 return w.CSS.escape( s );
40 }
41 return s.replace( /([^\w-])/g, '\\$1' );
42 }
43
44 /**
45 * Try to obtain wp.template render function.
46 *
47 * @param {string} template_id
48 * @returns {Function|null}
49 */
50 function get_wp_template_fn(template_id) {
51 try {
52 if ( ! w.wp || typeof w.wp.template !== 'function' ) {
53 return null;
54 }
55 const fn = w.wp.template( String( template_id || '' ) );
56 return (typeof fn === 'function') ? fn : null;
57 } catch ( _e ) {
58 return null;
59 }
60 }
61
62 /**
63 * Render wp.template() safely.
64 *
65 * Note: template_id is the id WITHOUT the "tmpl-" prefix.
66 *
67 * @param {string} template_id
68 * @param {Object} [data]
69 * @returns {string} HTML string or empty string on failure
70 */
71 function render_wp_template(template_id, data) {
72 const fn = get_wp_template_fn( template_id );
73 if ( ! fn ) {
74 return '';
75 }
76 try {
77 return String( fn( data || {} ) );
78 } catch ( _e ) {
79 return '';
80 }
81 }
82
83 /**
84 * Parse arguments for ensure_dom_from_wp_template() supporting both signatures:
85 * - (template_id, dom_id, data)
86 * - (template_id, data) // dom_id omitted
87 *
88 * @param {string|Object} dom_id_or_data
89 * @param {Object} data
90 * @returns {{dom_id:string, tpl_data:Object}}
91 */
92 function normalize_dom_args(dom_id_or_data, data) {
93 // If 2nd arg is plain object => treat as data, no explicit dom_id.
94 if ( dom_id_or_data && typeof dom_id_or_data === 'object' && ! Array.isArray( dom_id_or_data ) ) {
95 return { dom_id: '', tpl_data: dom_id_or_data || {} };
96 }
97 return { dom_id: String( dom_id_or_data || '' ), tpl_data: data || {} };
98 }
99
100 /**
101 * Find an id on the root element or inside its subtree.
102 * Used to avoid inserting duplicates when template HTML already has an id.
103 *
104 * @param {HTMLElement} root
105 * @returns {string}
106 */
107 function detect_first_id(root) {
108 try {
109 const direct = String( root.id || root.getAttribute( 'id' ) || '' );
110 if ( direct ) {
111 return direct;
112 }
113
114 const first_with_id = root.querySelector ? root.querySelector( '[id]' ) : null;
115 return first_with_id ? String( first_with_id.id || '' ) : '';
116 } catch ( _e ) {
117 return '';
118 }
119 }
120
121 /**
122 * Ensure the returned node has a stable id (only if it doesn't have one).
123 *
124 * @param {HTMLElement} el
125 * @param {string} preferred_id
126 * @param {string} fallback_id
127 * @returns {void}
128 */
129 function ensure_element_id(el, preferred_id, fallback_id) {
130 if ( ! el || el.id ) {
131 return;
132 }
133
134 const base = String( preferred_id || fallback_id || '' );
135 if ( ! base ) {
136 return;
137 }
138
139 let candidate = base;
140 let i = 2;
141
142 while ( d.getElementById( candidate ) && d.getElementById( candidate ) !== el ) {
143 candidate = base + '-' + (i++);
144 }
145
146 try {
147 el.id = candidate;
148 } catch ( _e ) {
149 }
150 }
151
152 /**
153 * Ensure a rendered template element exists in DOM (lazy insert).
154 *
155 * Behavior:
156 * - If dom_id is provided and exists => returns that existing node.
157 * - Otherwise renders the template and inserts its first root node into DOM.
158 * - If the rendered root (or its subtree) already has an id that exists in DOM,
159 * return the existing element instead of inserting a duplicate.
160 * - Returns the requested dom_id element if possible, else the inserted root.
161 *
162 * @param {string} template_id
163 * @param {string|Object} [dom_id_or_data]
164 * @param {Object} [data]
165 * @returns {HTMLElement|null}
166 */
167 function ensure_dom_from_wp_template(template_id, dom_id_or_data, data) {
168 const args = normalize_dom_args( dom_id_or_data, data );
169 const dom_id = args.dom_id;
170 const tpl_data = args.tpl_data;
171
172 // 1) If caller asked for a specific dom_id and it's already in DOM, return it.
173 if ( dom_id ) {
174 const existing = d.getElementById( dom_id );
175 if ( existing ) {
176 return existing;
177 }
178 }
179
180 // 2) Render template HTML.
181 const html = render_wp_template( template_id, tpl_data );
182 if ( ! html ) {
183 return null;
184 }
185
186 // 3) Convert HTML -> element (first root node).
187 const wrap = d.createElement( 'div' );
188 wrap.innerHTML = String( html ).trim();
189
190 const root = wrap.firstElementChild;
191 if ( ! root ) {
192 return null;
193 }
194
195 // 4) Avoid duplicate IDs:
196 // If template contains an id (root or subtree) and that id already exists in DOM,
197 // return the existing element instead of inserting a duplicate.
198 const detected_id = detect_first_id( root );
199 if ( !dom_id && detected_id ) {
200 const existing_by_detected = d.getElementById( detected_id );
201 if ( existing_by_detected ) return existing_by_detected;
202 }
203
204 // 5) Insert into DOM.
205 (d.body || d.documentElement).appendChild( root );
206
207 // 6) Return requested target:
208 // - If dom_id provided: try getElementById(dom_id), else try querySelector inside root, else root.
209 let ret = root;
210
211 if ( dom_id ) {
212 ret = d.getElementById( dom_id ) || root;
213 if ( ret === root ) {
214 try {
215 const inside = root.querySelector( '#' + css_escape( dom_id ) );
216 if ( inside ) ret = inside;
217 } catch ( _e ) {
218 }
219 }
220 }
221
222 // 7) Make sure returned node has an id (useful for modal show/hide).
223 ensure_element_id( ret, dom_id, detected_id || ('wpbc_tpl_' + String( template_id || 'tpl' )) );
224
225 // 8) Post-render normalizers (safe no-op if not defined yet).
226 try {
227 if ( UI.apply_post_render ) {
228 UI.apply_post_render( ret );
229 }
230 } catch ( _e ) {
231 }
232
233 return ret;
234 }
235
236 /**
237 * Same as ensure_dom_from_wp_template(), but returns both element and resolved id.
238 *
239 * @param {string} template_id
240 * @param {string|Object} [dom_id_or_data]
241 * @param {Object} [data]
242 * @returns {{id:string, el:HTMLElement}|null}
243 */
244 function ensure_dom_ref_from_wp_template(template_id, dom_id_or_data, data) {
245 const el = ensure_dom_from_wp_template( template_id, dom_id_or_data, data );
246 if ( !el ) return null;
247 return { id: String( el.id || '' ), el };
248 }
249
250 // -------------------------------------------------------------------------
251 // Public API (clear names)
252 // -------------------------------------------------------------------------
253
254 Templates.render_wp_template = render_wp_template;
255 Templates.ensure_dom_from_wp_template = ensure_dom_from_wp_template;
256 Templates.ensure_dom_ref_from_wp_template = ensure_dom_ref_from_wp_template;
257
258 // Keep escape available (useful for callers that need selector-safe ids).
259 Templates.css_escape = css_escape;
260
261 // Also keep your existing UI.* aliases if other files already call these:.
262 UI.render_wp_template = Templates.render_wp_template;
263 UI.ensure_dom_from_wp_template = Templates.ensure_dom_from_wp_template;
264 UI.ensure_dom_ref_from_wp_template = Templates.ensure_dom_ref_from_wp_template;
265
266
267 // -------------------------------------------------------------------------
268 // UI.Modals (admin helper)
269 // - Optional dependency: jQuery.fn.wpbc_my_modal
270 // - Safe fallback: toggles display style
271 // -------------------------------------------------------------------------
272
273 const Modals = (UI.Modals = UI.Modals || {});
274
275 // Reuse the same escape helper everywhere.
276 Modals.css_escape = Modals.css_escape || Templates.css_escape || css_escape;
277
278 Modals.show = Modals.show || function (modal_el_or_id) {
279
280 const modal_id = (typeof modal_el_or_id === 'string')
281 ? String( modal_el_or_id || '' )
282 : String( (modal_el_or_id && modal_el_or_id.id) ? modal_el_or_id.id : '' );
283
284 const modal_el = (typeof modal_el_or_id === 'string')
285 ? d.getElementById( modal_id )
286 : modal_el_or_id;
287
288 if ( !modal_el ) return;
289
290 try {
291 if ( w.jQuery && w.jQuery.fn && w.jQuery.fn.wpbc_my_modal ) {
292
293 const $m = modal_id
294 ? w.jQuery( '#' + Modals.css_escape( modal_id ) )
295 : w.jQuery( modal_el );
296
297 if ( $m && $m.wpbc_my_modal ) {
298 $m.wpbc_my_modal( 'show' );
299 return;
300 }
301 }
302 } catch ( _e ) {
303 }
304
305 modal_el.style.display = 'block';
306 };
307
308 Modals.hide = Modals.hide || function (modal_el_or_id) {
309
310 const modal_id = (typeof modal_el_or_id === 'string')
311 ? String( modal_el_or_id || '' )
312 : String( (modal_el_or_id && modal_el_or_id.id) ? modal_el_or_id.id : '' );
313
314 const modal_el = (typeof modal_el_or_id === 'string')
315 ? d.getElementById( modal_id )
316 : modal_el_or_id;
317
318 if ( !modal_el ) return;
319
320 try {
321 if ( w.jQuery && w.jQuery.fn && w.jQuery.fn.wpbc_my_modal ) {
322
323 const $m = modal_id
324 ? w.jQuery( '#' + Modals.css_escape( modal_id ) )
325 : w.jQuery( modal_el );
326
327 if ( $m && $m.wpbc_my_modal ) {
328 $m.wpbc_my_modal( 'hide' );
329 return;
330 }
331 }
332 } catch ( _e ) {
333 }
334
335 modal_el.style.display = 'none';
336 };
337
338 }( window, document ));
339