| 1 |
// --------------------------------------------------------------------------------------------------------------------- |
| 2 |
// == File /includes/page-form-builder/preview/_out/bfb-preview.js |
| 3 |
// == BFB Preview Client — sends current structure via AJAX and loads preview URL into iframe |
| 4 |
// --------------------------------------------------------------------------------------------------------------------- |
| 5 |
(function (w, d) { |
| 6 |
'use strict'; |
| 7 |
|
| 8 |
/** |
| 9 |
* Simple logger alias (optional). |
| 10 |
* |
| 11 |
* @type {{log:Function, error:Function}} |
| 12 |
*/ |
| 13 |
var dev = (w._wpbc && w._wpbc.dev) ? w._wpbc.dev : { |
| 14 |
log: function () {}, |
| 15 |
error: function () {} |
| 16 |
}; |
| 17 |
|
| 18 |
/** |
| 19 |
* Preview client class. |
| 20 |
* Binds to a preview panel root that contains button + iframe. |
| 21 |
*/ |
| 22 |
class wpbc_bfb_preview_client { |
| 23 |
|
| 24 |
/** |
| 25 |
* @param {HTMLElement} root_el Root element of the preview panel. |
| 26 |
*/ |
| 27 |
constructor(root_el) { |
| 28 |
this.root_el = root_el; |
| 29 |
this.iframe = root_el.querySelector( '[data-wpbc-bfb-preview-iframe="1"]' ); |
| 30 |
this.loader = root_el.querySelector( '[data-wpbc-bfb-preview-loader="1"]' ); // NEW |
| 31 |
this.button = null; // No local "Update Preview" button in panel anymore. |
| 32 |
|
| 33 |
this.nonce = root_el.getAttribute( 'data-preview-nonce' ) || ''; |
| 34 |
this.is_busy = false; |
| 35 |
|
| 36 |
if ( !this.iframe ) { |
| 37 |
dev.error( 'wpbc_bfb_preview_client', 'Missing iframe element' ); |
| 38 |
return; |
| 39 |
} |
| 40 |
|
| 41 |
this.bind_events(); |
| 42 |
} |
| 43 |
|
| 44 |
|
| 45 |
/** |
| 46 |
* Show/hide the overlay loader over the iframe. |
| 47 |
* |
| 48 |
* @param {boolean} is_visible |
| 49 |
*/ |
| 50 |
set_loader_visible(is_visible) { |
| 51 |
if ( !this.loader ) { |
| 52 |
return; |
| 53 |
} |
| 54 |
if ( is_visible ) { |
| 55 |
this.loader.classList.add( 'is-visible' ); |
| 56 |
} else { |
| 57 |
this.loader.classList.remove( 'is-visible' ); |
| 58 |
} |
| 59 |
} |
| 60 |
|
| 61 |
/** |
| 62 |
* Bind UI events (panel-local button, if present). |
| 63 |
*/ |
| 64 |
bind_events() { |
| 65 |
var self = this; |
| 66 |
|
| 67 |
if ( ! this.button ) { |
| 68 |
return; |
| 69 |
} |
| 70 |
|
| 71 |
this.button.addEventListener( 'click', function () { |
| 72 |
self.update_preview(); |
| 73 |
} ); |
| 74 |
} |
| 75 |
|
| 76 |
/** |
| 77 |
* Get current BFB structure from global builder. |
| 78 |
* |
| 79 |
* @returns {Object|null} |
| 80 |
*/ |
| 81 |
get_current_structure() { |
| 82 |
if ( ! w.wpbc_bfb || typeof w.wpbc_bfb.get_structure !== 'function' ) { |
| 83 |
dev.error( 'wpbc_bfb_preview_client', 'wpbc_bfb.get_structure() is not available' ); |
| 84 |
return null; |
| 85 |
} |
| 86 |
|
| 87 |
try { |
| 88 |
return w.wpbc_bfb.get_structure(); |
| 89 |
} catch (e) { |
| 90 |
dev.error( 'wpbc_bfb_preview_client.get_current_structure', e ); |
| 91 |
return null; |
| 92 |
} |
| 93 |
} |
| 94 |
|
| 95 |
/** |
| 96 |
* Parse combined length value like "100%", "320px", "12.5rem". |
| 97 |
* |
| 98 |
* @param {string} value |
| 99 |
* @return {{num:string, unit:string}} |
| 100 |
*/ |
| 101 |
parse_length_value(value) { |
| 102 |
var raw = String(value == null ? '' : value).trim(); |
| 103 |
var m = raw.match(/^\s*(-?\d+(?:\.\d+)?)\s*([a-z%]*)\s*$/i); |
| 104 |
if (!m) { |
| 105 |
return { num: raw, unit: '' }; |
| 106 |
} |
| 107 |
return { num: (m[1] || ''), unit: (m[2] || '') }; |
| 108 |
} |
| 109 |
|
| 110 |
/** |
| 111 |
* Build current form settings payload (same structure as real Save): |
| 112 |
* { |
| 113 |
* options: { ...source-of-truth... }, |
| 114 |
* css_vars: { ...compiled... } |
| 115 |
* } |
| 116 |
* |
| 117 |
* @param {string} form_name |
| 118 |
* @returns {{options:Object, css_vars:Object}} |
| 119 |
*/ |
| 120 |
get_current_form_settings(form_name) { |
| 121 |
|
| 122 |
var form_settings = { |
| 123 |
options : {}, |
| 124 |
css_vars: {} |
| 125 |
}; |
| 126 |
|
| 127 |
// --- same event contract as ajax/_out/bfb-ajax.js ---------------------------------------------- |
| 128 |
wpbc_bfb__dispatch_event_safe( 'wpbc:bfb:form_settings:collect', { |
| 129 |
settings : form_settings, |
| 130 |
form_name: form_name || 'standard' |
| 131 |
} ); |
| 132 |
|
| 133 |
// Strict: require correct shape. |
| 134 |
if ( !form_settings || typeof form_settings !== 'object' ) { |
| 135 |
form_settings = { options: {}, css_vars: {} }; |
| 136 |
} |
| 137 |
if ( !form_settings.options || typeof form_settings.options !== 'object' ) { |
| 138 |
form_settings.options = {}; |
| 139 |
} |
| 140 |
if ( !form_settings.css_vars || typeof form_settings.css_vars !== 'object' ) { |
| 141 |
form_settings.css_vars = {}; |
| 142 |
} |
| 143 |
|
| 144 |
return form_settings; |
| 145 |
} |
| 146 |
|
| 147 |
/** |
| 148 |
* Read a Form Style control directly from DOM. |
| 149 |
* |
| 150 |
* @param {string} key |
| 151 |
* @returns {string|null} |
| 152 |
*/ |
| 153 |
read_form_style_control_value(key) { |
| 154 |
var control = null; |
| 155 |
var checked = null; |
| 156 |
|
| 157 |
if ( key === 'booking_form_style' ) { |
| 158 |
checked = d.querySelector( 'input[type="radio"][name="booking_form_style"]:checked' ); |
| 159 |
return checked ? String( checked.value || '' ) : null; |
| 160 |
} |
| 161 |
|
| 162 |
control = d.querySelector( '[data-wpbc-bfb-fs-key="' + key + '"]' ); |
| 163 |
if ( ! control ) { |
| 164 |
control = d.getElementById( key ); |
| 165 |
} |
| 166 |
if ( ! control ) { |
| 167 |
return null; |
| 168 |
} |
| 169 |
|
| 170 |
if ( control.matches && control.matches( 'input[type="radio"]' ) ) { |
| 171 |
checked = d.querySelector( 'input[type="radio"][name="' + control.name + '"]:checked' ); |
| 172 |
return checked ? String( checked.value || '' ) : null; |
| 173 |
} |
| 174 |
|
| 175 |
if ( typeof control.value !== 'undefined' ) { |
| 176 |
return String( control.value == null ? '' : control.value ); |
| 177 |
} |
| 178 |
|
| 179 |
return null; |
| 180 |
} |
| 181 |
|
| 182 |
/** |
| 183 |
* Build the current global Form Style override for preview only. |
| 184 |
* |
| 185 |
* @returns {Object} |
| 186 |
*/ |
| 187 |
get_current_form_style_settings() { |
| 188 |
var style_keys = [ |
| 189 |
'booking_form_style', |
| 190 |
'booking_form_custom_background_color', |
| 191 |
'booking_form_custom_border_color', |
| 192 |
'booking_form_custom_border_width', |
| 193 |
'booking_form_custom_border_radius', |
| 194 |
'booking_form_custom_padding_vertical', |
| 195 |
'booking_form_custom_padding_horizontal', |
| 196 |
'booking_form_custom_text_color', |
| 197 |
'booking_form_custom_field_background_color', |
| 198 |
'booking_form_custom_field_text_color', |
| 199 |
'booking_form_custom_field_border_color', |
| 200 |
'booking_form_custom_button_background_color', |
| 201 |
'booking_form_custom_button_text_color', |
| 202 |
'booking_form_custom_button_border_color', |
| 203 |
'booking_form_custom_button_hover_background_color', |
| 204 |
'booking_form_custom_button_hover_text_color', |
| 205 |
'booking_form_custom_button_hover_border_color', |
| 206 |
'booking_form_custom_secondary_button_background_color', |
| 207 |
'booking_form_custom_secondary_button_text_color', |
| 208 |
'booking_form_custom_secondary_button_border_color', |
| 209 |
'booking_form_custom_secondary_button_hover_background_color', |
| 210 |
'booking_form_custom_secondary_button_hover_text_color', |
| 211 |
'booking_form_custom_secondary_button_hover_border_color' |
| 212 |
]; |
| 213 |
var out = {}; |
| 214 |
var collected = {}; |
| 215 |
var self = this; |
| 216 |
var localized = w.wpbc_bfb_settings_vars && w.wpbc_bfb_settings_vars.global_form_style |
| 217 |
? w.wpbc_bfb_settings_vars.global_form_style |
| 218 |
: {}; |
| 219 |
|
| 220 |
if ( w.WPBC_BFB_FormSettings && typeof w.WPBC_BFB_FormSettings.collect === 'function' ) { |
| 221 |
collected = w.WPBC_BFB_FormSettings.collect( 'global' ) || {}; |
| 222 |
} |
| 223 |
|
| 224 |
style_keys.forEach( function (key) { |
| 225 |
var dom_value = self.read_form_style_control_value( key ); |
| 226 |
if ( null !== dom_value ) { |
| 227 |
out[key] = dom_value; |
| 228 |
} else if ( Object.prototype.hasOwnProperty.call( collected, key ) ) { |
| 229 |
out[key] = collected[key]; |
| 230 |
} else if ( Object.prototype.hasOwnProperty.call( localized, key ) ) { |
| 231 |
out[key] = localized[key]; |
| 232 |
} |
| 233 |
} ); |
| 234 |
|
| 235 |
return out; |
| 236 |
} |
| 237 |
|
| 238 |
/** |
| 239 |
* Get the currently selected calendar skin URL from the Builder page. |
| 240 |
* |
| 241 |
* @returns {string} |
| 242 |
*/ |
| 243 |
get_selected_calendar_skin_url() { |
| 244 |
var select_el = d.querySelector( '.js-wpbc-bfb-calendar-skin' ); |
| 245 |
if ( select_el && select_el.options ) { |
| 246 |
var selected_option = select_el.options[ select_el.selectedIndex ]; |
| 247 |
if ( selected_option ) { |
| 248 |
var selected_url = selected_option.getAttribute( 'data-wpbc-calendar-skin-url' ); |
| 249 |
if ( selected_url ) { |
| 250 |
return String( selected_url ); |
| 251 |
} |
| 252 |
} |
| 253 |
} |
| 254 |
|
| 255 |
var stylesheet = d.getElementById( 'wpbc-calendar-skin-css' ); |
| 256 |
if ( stylesheet && stylesheet.href ) { |
| 257 |
return String( stylesheet.href ); |
| 258 |
} |
| 259 |
|
| 260 |
return ''; |
| 261 |
} |
| 262 |
|
| 263 |
/** |
| 264 |
* Apply the unsaved Builder calendar skin inside the loaded Preview iframe. |
| 265 |
* |
| 266 |
* @param {string} skin_url |
| 267 |
* @param {Function} done |
| 268 |
* @param {number} tries |
| 269 |
*/ |
| 270 |
apply_calendar_skin_to_iframe(skin_url, done, tries) { |
| 271 |
var self = this; |
| 272 |
var attempt_num = Number( tries || 0 ); |
| 273 |
|
| 274 |
if ( ! skin_url || ! this.iframe || ! this.iframe.contentWindow ) { |
| 275 |
if ( typeof done === 'function' ) { |
| 276 |
done(); |
| 277 |
} |
| 278 |
return; |
| 279 |
} |
| 280 |
|
| 281 |
try { |
| 282 |
if ( typeof this.iframe.contentWindow.wpbc__calendar__change_skin === 'function' ) { |
| 283 |
this.iframe.contentWindow.wpbc__calendar__change_skin( skin_url ); |
| 284 |
if ( typeof done === 'function' ) { |
| 285 |
done(); |
| 286 |
} |
| 287 |
return; |
| 288 |
} |
| 289 |
} catch ( e ) { |
| 290 |
dev.error( 'wpbc_bfb_preview_client.apply_calendar_skin_to_iframe', e ); |
| 291 |
} |
| 292 |
|
| 293 |
if ( attempt_num >= 20 ) { |
| 294 |
if ( typeof done === 'function' ) { |
| 295 |
done(); |
| 296 |
} |
| 297 |
return; |
| 298 |
} |
| 299 |
|
| 300 |
w.setTimeout( function () { |
| 301 |
self.apply_calendar_skin_to_iframe( skin_url, done, attempt_num + 1 ); |
| 302 |
}, 100 ); |
| 303 |
} |
| 304 |
|
| 305 |
/** |
| 306 |
* Send snapshot to server and update iframe src with returned preview URL. |
| 307 |
* |
| 308 |
* @param {{source_button?:HTMLElement}} [opts] |
| 309 |
*/ |
| 310 |
update_preview( opts ) { |
| 311 |
if ( this.is_busy ) { |
| 312 |
return; |
| 313 |
} |
| 314 |
|
| 315 |
var structure = this.get_current_structure(); |
| 316 |
if ( ! structure ) { |
| 317 |
return; |
| 318 |
} |
| 319 |
|
| 320 |
this.set_loader_visible( true ); |
| 321 |
|
| 322 |
var options = opts || {}; |
| 323 |
var source_btn = options.source_button || null; |
| 324 |
|
| 325 |
var $source_btn = null; |
| 326 |
if ( source_btn && w.jQuery && typeof w.wpbc_bfb__button_busy_start === 'function' ) { |
| 327 |
$source_btn = w.jQuery( source_btn ); |
| 328 |
if ( $source_btn && $source_btn.length ) { |
| 329 |
w.wpbc_bfb__button_busy_start( $source_btn ); |
| 330 |
} |
| 331 |
} |
| 332 |
|
| 333 |
this.is_busy = true; |
| 334 |
this.set_button_busy( true ); // local "Update Preview" button in the panel |
| 335 |
|
| 336 |
var payload = new w.FormData(); |
| 337 |
var cfg = w.WPBC_BFB_Ajax || {}; |
| 338 |
|
| 339 |
// Build settings payload (same as real saving). |
| 340 |
var form_settings = this.get_current_form_settings( cfg.form_name || 'standard' ); |
| 341 |
var preview_form_style = this.get_current_form_style_settings(); |
| 342 |
|
| 343 |
payload.append( 'action', 'WPBC_AJX_BFB_SAVE_FORM_CONFIG' ); |
| 344 |
payload.append( 'nonce', ( cfg.nonce_save || this.nonce || '' ) ); |
| 345 |
|
| 346 |
// IMPORTANT: |
| 347 |
// - form_name is the logical form key (usually 'standard' or selected form) |
| 348 |
payload.append( 'form_name', ( cfg.form_name || 'standard' ) ); |
| 349 |
payload.append( 'status', 'preview' ); |
| 350 |
payload.append( 'return_preview_url', '1' ); |
| 351 |
|
| 352 |
payload.append( 'engine', ( cfg.engine || 'bfb' ) ); |
| 353 |
payload.append( 'engine_version', ( cfg.engine_version || '1.0' ) ); |
| 354 |
|
| 355 |
payload.append( 'structure', JSON.stringify( structure ) ); |
| 356 |
|
| 357 |
// Send real settings (options + compiled css_vars). |
| 358 |
payload.append( 'settings', JSON.stringify( form_settings ) ); |
| 359 |
payload.append( 'preview_form_style', JSON.stringify( preview_form_style ) ); |
| 360 |
|
| 361 |
// ---------------------------------------------------------------------------- |
| 362 |
// Choose where advanced_form + content_form are taken from (auto|builder|advanced) |
| 363 |
// ---------------------------------------------------------------------------- |
| 364 |
var preview_source = wpbc_bfb_preview__get_source( cfg, source_btn ); |
| 365 |
payload.append( 'preview_source', preview_source ); |
| 366 |
|
| 367 |
var adv = null; |
| 368 |
|
| 369 |
// 1) Try Advanced Mode text (if selected / auto+dirty) |
| 370 |
if ( preview_source === 'advanced' || preview_source === 'auto' ) { |
| 371 |
|
| 372 |
adv = wpbc_bfb_preview__read_advanced_mode_payload( d, w ); |
| 373 |
|
| 374 |
var can_use_advanced = |
| 375 |
(preview_source === 'advanced') || |
| 376 |
(preview_source === 'auto' && adv && adv.is_dirty); |
| 377 |
|
| 378 |
if ( can_use_advanced ) { |
| 379 |
|
| 380 |
// Forced "advanced" but empty -> fallback to builder export. |
| 381 |
if ( ! wpbc_bfb_preview__has_text( adv.advanced_form ) && ! wpbc_bfb_preview__has_text( adv.content_form ) ) { |
| 382 |
|
| 383 |
if ( typeof w.wpbc_admin_show_message === 'function' ) { |
| 384 |
w.wpbc_admin_show_message( |
| 385 |
'Advanced Mode is selected, but editors are empty. Using Builder export.', |
| 386 |
'warning', |
| 387 |
6000 |
| 388 |
); |
| 389 |
} |
| 390 |
|
| 391 |
} else { |
| 392 |
|
| 393 |
if ( wpbc_bfb_preview__has_text( adv.advanced_form ) ) { |
| 394 |
payload.append( 'advanced_form', adv.advanced_form ); |
| 395 |
} |
| 396 |
if ( wpbc_bfb_preview__has_text( adv.content_form ) ) { |
| 397 |
payload.append( 'content_form', adv.content_form ); |
| 398 |
} |
| 399 |
|
| 400 |
} |
| 401 |
} |
| 402 |
} |
| 403 |
|
| 404 |
// 2) If not taken from Advanced Mode -> export from Builder structure (current behavior) |
| 405 |
var already_has_adv = |
| 406 |
payload.has && (payload.has( 'advanced_form' ) || payload.has( 'content_form' )); // some browsers |
| 407 |
// support |
| 408 |
// FormData.has |
| 409 |
|
| 410 |
// Fallback for old browsers (no FormData.has) |
| 411 |
if ( typeof payload.has !== 'function' ) { |
| 412 |
already_has_adv = false; // just try exporter if needed |
| 413 |
} |
| 414 |
|
| 415 |
if ( ! already_has_adv ) { |
| 416 |
|
| 417 |
if ( w.WPBC_BFB_Exporter && typeof w.WPBC_BFB_Exporter.export_all === 'function' ) { |
| 418 |
try { |
| 419 |
|
| 420 |
var width_combined = form_settings.options.booking_form_layout_width || ''; |
| 421 |
var parsed_width = this.parse_length_value( width_combined ); |
| 422 |
var width_unit = parsed_width.unit ? parsed_width.unit : '%'; |
| 423 |
|
| 424 |
var export_options = { |
| 425 |
gapPercent : 3, |
| 426 |
form_slug : (cfg.form_name || 'standard'), |
| 427 |
form_width_value: parsed_width.num, |
| 428 |
form_width_unit : width_unit |
| 429 |
}; |
| 430 |
|
| 431 |
var export_result = w.WPBC_BFB_Exporter.export_all( structure || [], export_options ); |
| 432 |
|
| 433 |
if ( export_result ) { |
| 434 |
if ( export_result.advanced_form ) { |
| 435 |
payload.append( 'advanced_form', export_result.advanced_form ); |
| 436 |
} |
| 437 |
if ( export_result.fields_data ) { |
| 438 |
payload.append( 'content_form', export_result.fields_data ); |
| 439 |
} |
| 440 |
} |
| 441 |
|
| 442 |
} catch ( e ) { |
| 443 |
console.error( 'WPBC BFB: export_all error', e ); |
| 444 |
} |
| 445 |
} |
| 446 |
} |
| 447 |
|
| 448 |
|
| 449 |
// Use shared helper if available; fallback to ajaxurl. |
| 450 |
var ajax_url = ''; |
| 451 |
if ( typeof w.wpbc_bfb__get_ajax_url === 'function' ) { |
| 452 |
ajax_url = w.wpbc_bfb__get_ajax_url(); |
| 453 |
} else if ( typeof w.ajaxurl !== 'undefined' && w.ajaxurl ) { |
| 454 |
ajax_url = w.ajaxurl; |
| 455 |
} |
| 456 |
|
| 457 |
var self = this; |
| 458 |
var preview_calendar_skin_url = this.get_selected_calendar_skin_url(); |
| 459 |
|
| 460 |
function end_busy_now() { |
| 461 |
self._end_busy( $source_btn ); |
| 462 |
} |
| 463 |
|
| 464 |
if ( ! ajax_url ) { |
| 465 |
dev.error( 'wpbc_bfb_preview_client', 'ajax URL is not defined' ); |
| 466 |
end_busy_now(); |
| 467 |
return; |
| 468 |
} |
| 469 |
|
| 470 |
w.fetch( ajax_url, { |
| 471 |
method: 'POST', |
| 472 |
body: payload |
| 473 |
} ) |
| 474 |
.then( function (response) { |
| 475 |
return response.json(); |
| 476 |
} ) |
| 477 |
.then( function (data) { |
| 478 |
if ( ! data || ! data.success || ! data.data || ! data.data.preview_url ) { |
| 479 |
dev.error( 'wpbc_bfb_preview_client', 'Preview AJAX error', data ); |
| 480 |
end_busy_now(); |
| 481 |
return; |
| 482 |
} |
| 483 |
|
| 484 |
if ( ! self.iframe ) { |
| 485 |
dev.error( 'wpbc_bfb_preview_client', 'Missing iframe element' ); |
| 486 |
end_busy_now(); |
| 487 |
return; |
| 488 |
} |
| 489 |
|
| 490 |
// Wait until iframe has really loaded the preview URL. |
| 491 |
var on_load = function () { |
| 492 |
self.iframe.removeEventListener( 'load', on_load ); |
| 493 |
self.apply_calendar_skin_to_iframe( preview_calendar_skin_url, end_busy_now ); |
| 494 |
}; |
| 495 |
|
| 496 |
self.iframe.addEventListener( 'load', on_load ); |
| 497 |
self.iframe.setAttribute( 'src', data.data.preview_url ); |
| 498 |
} ) |
| 499 |
.catch( function (err) { |
| 500 |
dev.error( 'wpbc_bfb_preview_client', 'Preview AJAX failed', err ); |
| 501 |
end_busy_now(); |
| 502 |
} ); |
| 503 |
} |
| 504 |
|
| 505 |
/** |
| 506 |
* Internal helper: stop busy state on panel button + optional top toolbar button. |
| 507 |
* |
| 508 |
* @param {jQuery|null} $source_btn |
| 509 |
* @private |
| 510 |
*/ |
| 511 |
_end_busy($source_btn) { |
| 512 |
this.is_busy = false; |
| 513 |
this.set_button_busy( false ); |
| 514 |
this.set_loader_visible( false ); // NEW |
| 515 |
|
| 516 |
if ( $source_btn && typeof w.wpbc_bfb__button_busy_end === 'function' ) { |
| 517 |
w.wpbc_bfb__button_busy_end( $source_btn ); |
| 518 |
} |
| 519 |
} |
| 520 |
|
| 521 |
/** |
| 522 |
* Force-reset any busy states related to preview. |
| 523 |
* This is called when switching back to Builder mode (soft-cancel UX). |
| 524 |
*/ |
| 525 |
reset_busy_state() { |
| 526 |
this.is_busy = false; |
| 527 |
this.set_button_busy( false ); // panel button, if any |
| 528 |
this.set_loader_visible( false ); // NEW |
| 529 |
|
| 530 |
if ( w.jQuery && typeof w.wpbc_bfb__button_busy_end === 'function' ) { |
| 531 |
// Top toolbar buttons that may show "Loading preview..." |
| 532 |
var $btns = w.jQuery( |
| 533 |
'[data-wpbc-bfb-top-preview-btn="1"],' + |
| 534 |
'[data-wpbc-bfb-top-refresh-btn="1"]' |
| 535 |
); |
| 536 |
|
| 537 |
$btns.each( function () { |
| 538 |
var $b = w.jQuery( this ); |
| 539 |
if ( $b.hasClass( 'wpbc-is-busy' ) ) { |
| 540 |
w.wpbc_bfb__button_busy_end( $b ); |
| 541 |
} |
| 542 |
} ); |
| 543 |
} |
| 544 |
} |
| 545 |
|
| 546 |
/** |
| 547 |
* Simple busy indicator for the panel "Update Preview" button. |
| 548 |
* Reuses global WPBC busy helpers if available, so the spinner matches Save/Load buttons. |
| 549 |
* |
| 550 |
* @param {boolean} is_busy |
| 551 |
*/ |
| 552 |
set_button_busy( is_busy ) { |
| 553 |
if ( ! this.button ) { |
| 554 |
return; |
| 555 |
} |
| 556 |
|
| 557 |
// If jQuery + global helpers exist, use same spinner as other toolbar buttons. |
| 558 |
if ( w.jQuery && typeof w.wpbc_bfb__button_busy_start === 'function' && typeof w.wpbc_bfb__button_busy_end === 'function' ) { |
| 559 |
var $btn = w.jQuery( this.button ); |
| 560 |
|
| 561 |
if ( is_busy ) { |
| 562 |
w.wpbc_bfb__button_busy_start( $btn ); |
| 563 |
} else { |
| 564 |
w.wpbc_bfb__button_busy_end( $btn ); |
| 565 |
} |
| 566 |
|
| 567 |
return; |
| 568 |
} |
| 569 |
|
| 570 |
// Fallback: simple disabled state without spinner. |
| 571 |
if ( is_busy ) { |
| 572 |
this.button.setAttribute( 'disabled', 'disabled' ); |
| 573 |
this.button.classList.add( 'wpbc_bfb__preview_btn_busy' ); |
| 574 |
} else { |
| 575 |
this.button.removeAttribute( 'disabled' ); |
| 576 |
this.button.classList.remove( 'wpbc_bfb__preview_btn_busy' ); |
| 577 |
} |
| 578 |
} |
| 579 |
|
| 580 |
/** |
| 581 |
* Set current view mode: "builder" or "preview". |
| 582 |
* Applies/removes CSS class on <body>. |
| 583 |
* |
| 584 |
* @param {('builder'|'preview')} mode |
| 585 |
*/ |
| 586 |
set_mode( mode ) { |
| 587 |
var body = d.body; |
| 588 |
if ( ! body ) { |
| 589 |
return; |
| 590 |
} |
| 591 |
|
| 592 |
if ( mode === 'preview' ) { |
| 593 |
body.classList.add( 'wpbc_bfb__mode_preview' ); |
| 594 |
} else { |
| 595 |
body.classList.remove( 'wpbc_bfb__mode_preview' ); |
| 596 |
} |
| 597 |
} |
| 598 |
|
| 599 |
} |
| 600 |
|
| 601 |
// -- Helpers ------------------------------------------------------------------------------------------------------ |
| 602 |
/** |
| 603 |
* Is Preview mode currently active? |
| 604 |
* We prefer body class (set by client.set_mode), fallback to active tab id. |
| 605 |
* |
| 606 |
* @return {boolean} |
| 607 |
*/ |
| 608 |
function wpbc_bfb_preview__is_preview_mode_active() { |
| 609 |
var body = d.body; |
| 610 |
if ( body && body.classList && body.classList.contains( 'wpbc_bfb__mode_preview' ) ) { |
| 611 |
return true; |
| 612 |
} |
| 613 |
return (wpbc_bfb_preview__get_active_top_tab_id() === 'preview_tab'); |
| 614 |
} |
| 615 |
|
| 616 |
/** |
| 617 |
* When a form is loaded via AJAX while Preview tab is active, |
| 618 |
* refresh the iframe so it shows the newly loaded form. |
| 619 |
* |
| 620 |
* @param {wpbc_bfb_preview_client} client |
| 621 |
*/ |
| 622 |
function wpbc_bfb_preview__bind_form_ajax_loaded_events(client) { |
| 623 |
|
| 624 |
if ( ! client ) { |
| 625 |
return; |
| 626 |
} |
| 627 |
|
| 628 |
// Prevent double binding if this script is injected twice. |
| 629 |
if ( w.__wpbc_bfb_preview__form_ajax_loaded_bound === '1' ) { |
| 630 |
return; |
| 631 |
} |
| 632 |
w.__wpbc_bfb_preview__form_ajax_loaded_bound = '1'; |
| 633 |
|
| 634 |
var debounce_id = null; |
| 635 |
|
| 636 |
d.addEventListener( |
| 637 |
'wpbc:bfb:form:ajax_loaded', |
| 638 |
function (ev) { |
| 639 |
|
| 640 |
// Only do anything if Preview is currently active. |
| 641 |
if ( ! wpbc_bfb_preview__is_preview_mode_active() ) { |
| 642 |
return; |
| 643 |
} |
| 644 |
|
| 645 |
var det = (ev && ev.detail) ? ev.detail : {}; |
| 646 |
var fn = det.form_name ? String( det.form_name ) : ''; |
| 647 |
|
| 648 |
// Best-effort: keep cfg.form_name in sync for payload building. |
| 649 |
if ( fn ) { |
| 650 |
w.WPBC_BFB_Ajax = w.WPBC_BFB_Ajax || {}; |
| 651 |
w.WPBC_BFB_Ajax.form_name = fn; |
| 652 |
} |
| 653 |
|
| 654 |
// Debounce multiple rapid loads (or secondary events). |
| 655 |
if ( debounce_id ) { |
| 656 |
clearTimeout( debounce_id ); |
| 657 |
} |
| 658 |
|
| 659 |
debounce_id = setTimeout( function () { |
| 660 |
debounce_id = null; |
| 661 |
|
| 662 |
// Preview might have been left while waiting. |
| 663 |
if ( ! wpbc_bfb_preview__is_preview_mode_active() ) { |
| 664 |
return; |
| 665 |
} |
| 666 |
|
| 667 |
// If we are already sending preview, don't stack another request. |
| 668 |
if ( client.is_busy ) { |
| 669 |
// Try once more shortly (safe). |
| 670 |
setTimeout( function () { |
| 671 |
if ( wpbc_bfb_preview__is_preview_mode_active() && ! client.is_busy ) { |
| 672 |
client.update_preview( { source_button: null } ); |
| 673 |
} |
| 674 |
}, 250 ); |
| 675 |
return; |
| 676 |
} |
| 677 |
|
| 678 |
// Wait until builder API is present (form load can be async). |
| 679 |
var tries = 0; |
| 680 |
(function wait_for_builder() { |
| 681 |
|
| 682 |
if ( ! wpbc_bfb_preview__is_preview_mode_active() ) { |
| 683 |
return; |
| 684 |
} |
| 685 |
|
| 686 |
if ( w.wpbc_bfb && typeof w.wpbc_bfb.get_structure === 'function' ) { |
| 687 |
client.update_preview( { source_button: null } ); |
| 688 |
return; |
| 689 |
} |
| 690 |
|
| 691 |
tries++; |
| 692 |
if ( tries < 15 ) { |
| 693 |
setTimeout( wait_for_builder, 200 ); |
| 694 |
} |
| 695 |
})(); |
| 696 |
}, 180 ); |
| 697 |
|
| 698 |
}, |
| 699 |
{ passive: true } |
| 700 |
); |
| 701 |
} |
| 702 |
|
| 703 |
function wpbc_bfb_preview__get_source(cfg, btn) { |
| 704 |
|
| 705 |
// priority: button attr -> global cfg -> default |
| 706 |
var v = ''; |
| 707 |
try { |
| 708 |
if ( btn && btn.getAttribute ) { |
| 709 |
v = btn.getAttribute( 'data-wpbc-bfb-preview-source' ) || btn.getAttribute( 'data-wpbc-bfb-save-source' ) || ''; |
| 710 |
} |
| 711 |
} catch ( e ) {} |
| 712 |
|
| 713 |
if ( ! v && cfg && (cfg.preview_source || cfg.save_source) ) { |
| 714 |
v = cfg.preview_source || cfg.save_source; |
| 715 |
} |
| 716 |
|
| 717 |
v = String( v || 'auto' ).toLowerCase(); |
| 718 |
if ( [ 'builder', 'advanced', 'auto' ].indexOf( v ) === -1 ) { |
| 719 |
v = 'builder'; |
| 720 |
} |
| 721 |
return v; |
| 722 |
} |
| 723 |
|
| 724 |
function wpbc_bfb_preview__has_text(v) { |
| 725 |
return !! (v && String( v ).trim()); |
| 726 |
} |
| 727 |
|
| 728 |
function wpbc_bfb_preview__read_advanced_mode_payload(d, w) { |
| 729 |
|
| 730 |
// best: API (syncs CodeMirror -> textarea) |
| 731 |
if ( w.wpbc_bfb_advanced_editor_api && typeof w.wpbc_bfb_advanced_editor_api.get_values === 'function' ) { |
| 732 |
try { |
| 733 |
return w.wpbc_bfb_advanced_editor_api.get_values(); |
| 734 |
} catch ( e ) {} |
| 735 |
} |
| 736 |
|
| 737 |
// fallback: read textareas directly |
| 738 |
var ta_form = d.getElementById( 'wpbc_bfb__advanced_form_editor' ); |
| 739 |
var ta_content = d.getElementById( 'wpbc_bfb__content_form_editor' ); |
| 740 |
|
| 741 |
return { |
| 742 |
advanced_form: ta_form ? String( ta_form.value || '' ) : '', |
| 743 |
content_form : ta_content ? String( ta_content.value || '' ) : '', |
| 744 |
is_dirty : false |
| 745 |
}; |
| 746 |
} |
| 747 |
|
| 748 |
/** |
| 749 |
* Is this element the Preview TAB link in top tabs nav? |
| 750 |
* |
| 751 |
* @param {HTMLElement|null} el |
| 752 |
* @return {boolean} |
| 753 |
*/ |
| 754 |
function wpbc_bfb_preview__is_preview_tab_link(el) { |
| 755 |
if ( !el || !el.getAttribute ) { |
| 756 |
return false; |
| 757 |
} |
| 758 |
return ( |
| 759 |
el.getAttribute( 'data-wpbc-bfb-action' ) === 'panel' && |
| 760 |
el.getAttribute( 'data-wpbc-bfb-tab' ) === 'preview_tab' |
| 761 |
); |
| 762 |
} |
| 763 |
|
| 764 |
/** |
| 765 |
* Is this element the Builder TAB link in top tabs nav? |
| 766 |
* |
| 767 |
* @param {HTMLElement|null} el |
| 768 |
* @return {boolean} |
| 769 |
*/ |
| 770 |
function wpbc_bfb_preview__is_builder_tab_link(el) { |
| 771 |
if ( !el || !el.getAttribute ) { |
| 772 |
return false; |
| 773 |
} |
| 774 |
return ( |
| 775 |
el.getAttribute( 'data-wpbc-bfb-action' ) === 'panel' && |
| 776 |
el.getAttribute( 'data-wpbc-bfb-tab' ) === 'builder_tab' |
| 777 |
); |
| 778 |
} |
| 779 |
|
| 780 |
/** |
| 781 |
* Activate a "panel" tab and show its panel section (scoped). |
| 782 |
* |
| 783 |
* Strategy: |
| 784 |
* 1) Try to click the real top-tab link (best). |
| 785 |
* 2) If link does not exist (e.g. Preview tab removed from nav), do a scoped manual switch: |
| 786 |
* - use nav's data-wpbc-bfb-panels-root + data-wpbc-bfb-panel-class |
| 787 |
* - hide/show ONLY those outer panels |
| 788 |
* - DO NOT touch inner panels |
| 789 |
* |
| 790 |
* @param {string} tab_id |
| 791 |
* @return {boolean} |
| 792 |
*/ |
| 793 |
function wpbc_bfb__activate_panel_tab(tab_id) { |
| 794 |
|
| 795 |
tab_id = String( tab_id || '' ).trim(); |
| 796 |
if ( ! tab_id ) { |
| 797 |
return false; |
| 798 |
} |
| 799 |
|
| 800 |
// 1) Best: trigger existing top-tabs switching by clicking the tab link (if it exists). |
| 801 |
var tab_link = d.querySelector( '[data-wpbc-bfb-action="panel"][data-wpbc-bfb-tab="' + tab_id + '"]' ); |
| 802 |
if ( tab_link && typeof tab_link.click === 'function' ) { |
| 803 |
try { |
| 804 |
tab_link.click(); |
| 805 |
return true; |
| 806 |
} catch ( _e ) {} |
| 807 |
} |
| 808 |
|
| 809 |
// 2) Fallback: scoped manual toggle (outer panels only). |
| 810 |
var nav = d.getElementById( 'wpbc_bfb__top_horisontal_nav' ); |
| 811 |
|
| 812 |
// Determine outer panels root. |
| 813 |
var panels_root = d; |
| 814 |
var panels_root_selector = ''; |
| 815 |
|
| 816 |
if ( nav ) { |
| 817 |
panels_root_selector = nav.getAttribute( 'data-wpbc-bfb-panels-root' ) || ''; |
| 818 |
panels_root_selector = String( panels_root_selector || '' ).trim(); |
| 819 |
} |
| 820 |
|
| 821 |
// Fallback if attribute missing: prefer #wpbc_bfb__top_panels if present. |
| 822 |
if ( ! panels_root_selector ) { |
| 823 |
panels_root_selector = '#wpbc_bfb__top_panels'; |
| 824 |
} |
| 825 |
|
| 826 |
if ( panels_root_selector ) { |
| 827 |
try { |
| 828 |
var root_el = d.querySelector( panels_root_selector ); |
| 829 |
if ( root_el ) { |
| 830 |
panels_root = root_el; |
| 831 |
} |
| 832 |
} catch ( _e2 ) {} |
| 833 |
} |
| 834 |
|
| 835 |
// Determine outer base class (IMPORTANT: must be the OUTER base, not wpbc_bfb__tab_section). |
| 836 |
var panel_base_class = 'wpbc_bfb__top_tab_section'; |
| 837 |
if ( nav ) { |
| 838 |
var from_nav = nav.getAttribute( 'data-wpbc-bfb-panel-class' ) || ''; |
| 839 |
from_nav = String( from_nav || '' ).trim(); |
| 840 |
if ( from_nav ) { |
| 841 |
panel_base_class = from_nav; |
| 842 |
} |
| 843 |
} |
| 844 |
|
| 845 |
// Update active tab on nav (source of truth). |
| 846 |
if ( nav ) { |
| 847 |
nav.setAttribute( 'data-active-tab', tab_id ); |
| 848 |
} |
| 849 |
|
| 850 |
// Hide ONLY outer panels (scoped by base class). |
| 851 |
var selector_all = '.' + panel_base_class; |
| 852 |
var panel_nodes = panels_root.querySelectorAll( selector_all ); |
| 853 |
|
| 854 |
for ( var i = 0; i < panel_nodes.length; i++ ) { |
| 855 |
panel_nodes[i].style.display = 'none'; |
| 856 |
} |
| 857 |
|
| 858 |
// Show requested outer panel. |
| 859 |
var selector_active = '.' + panel_base_class + '__' + tab_id; |
| 860 |
var active_panel = panels_root.querySelector( selector_active ); |
| 861 |
|
| 862 |
if ( ! active_panel ) { |
| 863 |
// Backward-compatible fallback (if someone forgot to add outer classes). |
| 864 |
active_panel = panels_root.querySelector( '.wpbc_bfb__tab_section__' + tab_id ); |
| 865 |
} |
| 866 |
|
| 867 |
if ( active_panel ) { |
| 868 |
active_panel.style.display = ''; |
| 869 |
} |
| 870 |
|
| 871 |
// Mark active item in outer nav if it exists (Preview can be missing). |
| 872 |
if ( nav ) { |
| 873 |
var items = nav.querySelectorAll( '.wpbc_ui_el__horis_nav_item' ); |
| 874 |
for ( var j = 0; j < items.length; j++ ) { |
| 875 |
items[j].classList.remove( 'active' ); |
| 876 |
} |
| 877 |
|
| 878 |
var active_item = nav.querySelector( '.wpbc_ui_el__horis_nav_item__' + tab_id ); |
| 879 |
if ( active_item ) { |
| 880 |
active_item.classList.add( 'active' ); |
| 881 |
} |
| 882 |
} |
| 883 |
|
| 884 |
// Emit same event as bfb-top-tabs.js so other modules stay in sync. |
| 885 |
try { |
| 886 |
d.dispatchEvent( |
| 887 |
new CustomEvent( 'wpbc:bfb:top-tab', { |
| 888 |
detail: { |
| 889 |
tab : tab_id, |
| 890 |
nav_id: (nav && nav.id) ? String( nav.id ) : '' |
| 891 |
} |
| 892 |
} ) |
| 893 |
); |
| 894 |
} catch ( _e3 ) {} |
| 895 |
|
| 896 |
return true; |
| 897 |
} |
| 898 |
|
| 899 |
|
| 900 |
/** |
| 901 |
* Get currently active top tab id from the nav container. |
| 902 |
* |
| 903 |
* @return {string} |
| 904 |
*/ |
| 905 |
function wpbc_bfb_preview__get_active_top_tab_id() { |
| 906 |
var nav = d.getElementById( 'wpbc_bfb__top_horisontal_nav' ); |
| 907 |
if ( !nav ) { |
| 908 |
return ''; |
| 909 |
} |
| 910 |
return nav.getAttribute( 'data-active-tab' ) || ''; |
| 911 |
} |
| 912 |
|
| 913 |
/** |
| 914 |
* Sync preview "mode" with currently active top tab. |
| 915 |
* |
| 916 |
* Rules: |
| 917 |
* - preview_tab => enable preview mode (buttons make sense) |
| 918 |
* - any other tab => disable preview mode + reset busy states |
| 919 |
* |
| 920 |
* IMPORTANT: |
| 921 |
* - We do NOT change the active top tab here (no panel switching). |
| 922 |
* We only sync preview UI mode. |
| 923 |
* |
| 924 |
* @param {string} tab_id |
| 925 |
* @param {wpbc_bfb_preview_client} client |
| 926 |
*/ |
| 927 |
function wpbc_bfb_preview__sync_mode_to_tab(tab_id, client) { |
| 928 |
|
| 929 |
tab_id = String( tab_id || '' ); |
| 930 |
|
| 931 |
if ( !client ) { |
| 932 |
return; |
| 933 |
} |
| 934 |
|
| 935 |
if ( 'preview_tab' === tab_id ) { |
| 936 |
client.set_mode( 'preview' ); |
| 937 |
return; |
| 938 |
} |
| 939 |
|
| 940 |
// Leaving preview -> soft-cancel busy state and hide preview-specific toolbar. |
| 941 |
if ( typeof client.reset_busy_state === 'function' ) { |
| 942 |
client.reset_busy_state(); |
| 943 |
} |
| 944 |
client.set_mode( 'builder' ); |
| 945 |
} |
| 946 |
|
| 947 |
/** |
| 948 |
* Bind to top-tab switch event emitted by bfb-top-tabs.js. |
| 949 |
* |
| 950 |
* @param {wpbc_bfb_preview_client} client |
| 951 |
*/ |
| 952 |
function wpbc_bfb_preview__bind_top_tab_events(client) { |
| 953 |
|
| 954 |
// Sync immediately (in case event already fired before this script init). |
| 955 |
wpbc_bfb_preview__sync_mode_to_tab( wpbc_bfb_preview__get_active_top_tab_id(), client ); |
| 956 |
|
| 957 |
// React to future tab changes. |
| 958 |
d.addEventListener( 'wpbc:bfb:top-tab', function (ev) { |
| 959 |
var det = (ev && ev.detail) ? ev.detail : {}; |
| 960 |
var tab_id = det.tab ? String( det.tab ) : ''; |
| 961 |
var nav_id = det.nav_id ? String( det.nav_id ) : ''; |
| 962 |
var prev_id = det.prev_tab ? String( det.prev_tab ) : ''; |
| 963 |
|
| 964 |
// If user switched to preview_tab in the MAIN top nav, |
| 965 |
// remember what tab they came from. |
| 966 |
if ( nav_id === TOP_NAV_ID && tab_id === 'preview_tab' && prev_id && prev_id !== 'preview_tab' ) { |
| 967 |
return_tab_id = prev_id; |
| 968 |
} |
| 969 |
|
| 970 |
wpbc_bfb_preview__sync_mode_to_tab( tab_id, client ); |
| 971 |
} ); |
| 972 |
|
| 973 |
} |
| 974 |
|
| 975 |
|
| 976 |
// -- Bind | Init on Load ------------------------------------------------------------------------------------------ |
| 977 |
var TOP_NAV_ID = 'wpbc_bfb__top_horisontal_nav'; |
| 978 |
var return_tab_id = 'builder_tab'; |
| 979 |
|
| 980 |
function remember_return_tab_from_dom() { |
| 981 |
var cur = wpbc_bfb_preview__get_active_top_tab_id(); |
| 982 |
if (cur && cur !== 'preview_tab') { |
| 983 |
return_tab_id = cur; |
| 984 |
} |
| 985 |
} |
| 986 |
|
| 987 |
function get_return_tab_id() { |
| 988 |
var t = String(return_tab_id || '').trim(); |
| 989 |
if (!t || t === 'preview_tab') t = 'builder_tab'; |
| 990 |
return t; |
| 991 |
} |
| 992 |
|
| 993 |
/** |
| 994 |
* Auto-init preview client on Builder page and wire top toolbar buttons. |
| 995 |
*/ |
| 996 |
function wpbc_bfb_init_preview_client() { |
| 997 |
var root = d.querySelector( '[data-wpbc-bfb-preview-root="1"]' ); |
| 998 |
|
| 999 |
if ( !root ) { |
| 1000 |
return; |
| 1001 |
} |
| 1002 |
|
| 1003 |
var client = new wpbc_bfb_preview_client( root ); |
| 1004 |
|
| 1005 |
if ( !w.WPBC_BFB_Preview ) { |
| 1006 |
w.WPBC_BFB_Preview = { |
| 1007 |
client: client, |
| 1008 |
|
| 1009 |
show_preview: function (opts) { |
| 1010 |
var opt = opts || {}; |
| 1011 |
var src = opt.source_button || null; |
| 1012 |
|
| 1013 |
// Remember where we were BEFORE switching to preview. |
| 1014 |
remember_return_tab_from_dom(); |
| 1015 |
|
| 1016 |
if ( ! wpbc_bfb_preview__is_preview_tab_link( src ) ) { |
| 1017 |
wpbc_bfb__activate_panel_tab( 'preview_tab' ); |
| 1018 |
} |
| 1019 |
|
| 1020 |
// Enable preview mode UI (shows refresh/back buttons etc). |
| 1021 |
client.set_mode( 'preview' ); |
| 1022 |
|
| 1023 |
// IMPORTANT: Always regenerate when show_preview() is called (Preview tab click included). |
| 1024 |
client.update_preview( { source_button: src } ); |
| 1025 |
}, |
| 1026 |
|
| 1027 |
|
| 1028 |
show_builder: function (opts) { |
| 1029 |
var opt = opts || {}; |
| 1030 |
var src = opt.source_button || null; |
| 1031 |
|
| 1032 |
if ( client && typeof client.reset_busy_state === 'function' ) { |
| 1033 |
client.reset_busy_state(); |
| 1034 |
} |
| 1035 |
|
| 1036 |
var back_to = get_return_tab_id(); |
| 1037 |
|
| 1038 |
// If Back button itself is not a real tab link -> just activate remembered tab. |
| 1039 |
// If activation fails (tab removed), fallback to builder_tab. |
| 1040 |
if ( ! wpbc_bfb_preview__is_builder_tab_link( src ) ) { |
| 1041 |
if ( ! wpbc_bfb__activate_panel_tab( back_to ) ) { |
| 1042 |
wpbc_bfb__activate_panel_tab( 'builder_tab' ); |
| 1043 |
} |
| 1044 |
} |
| 1045 |
|
| 1046 |
client.set_mode( 'builder' ); |
| 1047 |
}, |
| 1048 |
|
| 1049 |
show_advanced_tab: function (opts) { |
| 1050 |
var opt = opts || {}; |
| 1051 |
|
| 1052 |
if ( client && typeof client.reset_busy_state === 'function' ) { |
| 1053 |
client.reset_busy_state(); |
| 1054 |
} |
| 1055 |
wpbc_bfb__activate_panel_tab( 'advanced_tab' ); |
| 1056 |
client.set_mode( 'builder' ); |
| 1057 |
} |
| 1058 |
|
| 1059 |
|
| 1060 |
}; |
| 1061 |
} |
| 1062 |
|
| 1063 |
// Listen for top-tab changes and sync preview mode automatically. |
| 1064 |
wpbc_bfb_preview__bind_top_tab_events( client ); |
| 1065 |
|
| 1066 |
// When a form is loaded via AJAX, refresh preview (only if preview mode is active). |
| 1067 |
wpbc_bfb_preview__bind_form_ajax_loaded_events( client ); |
| 1068 |
|
| 1069 |
wpbc_bfb_bind_top_toolbar_buttons( client ); |
| 1070 |
} |
| 1071 |
|
| 1072 |
|
| 1073 |
/** |
| 1074 |
* Bind top toolbar Builder / Preview / Refresh buttons. |
| 1075 |
* Supports multiple elements for each action (toolbar buttons, top tabs, etc.) |
| 1076 |
* via data-wpbc-bfb-top-*-btn="1". |
| 1077 |
*/ |
| 1078 |
function wpbc_bfb_bind_top_toolbar_buttons(client) { |
| 1079 |
|
| 1080 |
var btn_preview_list = d.querySelectorAll( '[data-wpbc-bfb-top-preview-btn="1"]' ); |
| 1081 |
var btn_builder_list = d.querySelectorAll( '[data-wpbc-bfb-top-builder-btn="1"]' ); |
| 1082 |
var btn_refresh_list = d.querySelectorAll( '[data-wpbc-bfb-top-refresh-btn="1"]' ); |
| 1083 |
|
| 1084 |
function for_each_node(list, cb) { |
| 1085 |
if ( !list || !list.length ) { |
| 1086 |
return; |
| 1087 |
} |
| 1088 |
Array.prototype.forEach.call( list, function (el) { |
| 1089 |
if ( el && typeof cb === 'function' ) { |
| 1090 |
cb( el ); |
| 1091 |
} |
| 1092 |
} ); |
| 1093 |
} |
| 1094 |
|
| 1095 |
for_each_node( btn_preview_list, function (btn_preview) { |
| 1096 |
|
| 1097 |
// Prevent double binding if this script runs twice. |
| 1098 |
if ( btn_preview.getAttribute( 'data-wpbc-bfb-bound' ) === '1' ) { |
| 1099 |
return; |
| 1100 |
} |
| 1101 |
btn_preview.setAttribute( 'data-wpbc-bfb-bound', '1' ); |
| 1102 |
|
| 1103 |
btn_preview.addEventListener( 'click', function (e) { |
| 1104 |
e.preventDefault(); |
| 1105 |
|
| 1106 |
if ( w.WPBC_BFB_Preview && typeof w.WPBC_BFB_Preview.show_preview === 'function' ) { |
| 1107 |
w.WPBC_BFB_Preview.show_preview( { |
| 1108 |
source_button: btn_preview |
| 1109 |
} ); |
| 1110 |
} |
| 1111 |
} ); |
| 1112 |
} ); |
| 1113 |
|
| 1114 |
for_each_node( btn_refresh_list, function (btn_refresh) { |
| 1115 |
|
| 1116 |
if ( btn_refresh.getAttribute( 'data-wpbc-bfb-bound' ) === '1' ) { |
| 1117 |
return; |
| 1118 |
} |
| 1119 |
btn_refresh.setAttribute( 'data-wpbc-bfb-bound', '1' ); |
| 1120 |
|
| 1121 |
btn_refresh.addEventListener( 'click', function (e) { |
| 1122 |
e.preventDefault(); |
| 1123 |
|
| 1124 |
if ( w.WPBC_BFB_Preview && typeof w.WPBC_BFB_Preview.show_preview === 'function' ) { |
| 1125 |
w.WPBC_BFB_Preview.show_preview( { |
| 1126 |
update : true, |
| 1127 |
source_button: btn_refresh |
| 1128 |
} ); |
| 1129 |
} |
| 1130 |
} ); |
| 1131 |
} ); |
| 1132 |
|
| 1133 |
for_each_node( btn_builder_list, function (btn_builder) { |
| 1134 |
|
| 1135 |
if ( btn_builder.getAttribute( 'data-wpbc-bfb-bound' ) === '1' ) { |
| 1136 |
return; |
| 1137 |
} |
| 1138 |
btn_builder.setAttribute( 'data-wpbc-bfb-bound', '1' ); |
| 1139 |
|
| 1140 |
btn_builder.addEventListener( 'click', function (e) { |
| 1141 |
e.preventDefault(); |
| 1142 |
|
| 1143 |
if ( w.WPBC_BFB_Preview && typeof w.WPBC_BFB_Preview.show_builder === 'function' ) { |
| 1144 |
w.WPBC_BFB_Preview.show_builder( { source_button: btn_builder } ); |
| 1145 |
} |
| 1146 |
} ); |
| 1147 |
} ); |
| 1148 |
} |
| 1149 |
|
| 1150 |
|
| 1151 |
if ( d.readyState === 'complete' || d.readyState === 'interactive' ) { |
| 1152 |
setTimeout( wpbc_bfb_init_preview_client, 0 ); |
| 1153 |
} else { |
| 1154 |
d.addEventListener( 'DOMContentLoaded', wpbc_bfb_init_preview_client ); |
| 1155 |
} |
| 1156 |
|
| 1157 |
})( window, document ); |
| 1158 |
|