| 1 |
<?php |
| 2 |
|
| 3 |
// This file is included via Render::view() using extract(); all variables below are in the calling method's local scope, not global namespace. |
| 4 |
|
| 5 |
use BitCode\BitForm\Core\Util\EscapingHelper; |
| 6 |
|
| 7 |
if (!defined('ABSPATH')) { |
| 8 |
exit; |
| 9 |
} |
| 10 |
if (!defined('BITFORMS_ASSET_URI')) { |
| 11 |
exit; |
| 12 |
} |
| 13 |
|
| 14 |
$formUpdateVersion = get_option('bitform_form_update_version'); |
| 15 |
$formIdSafe = sanitize_key((string) $formID); |
| 16 |
|
| 17 |
$baseCSSPath = "/form-styles/bitform-{$formID}.css"; |
| 18 |
$baseConversationalCSSPath = "/form-styles/bitform-conversational-{$formID}.css"; |
| 19 |
$customCSSPath = "/form-styles/bitform-custom-{$formID}.css"; |
| 20 |
$standaloneCSSPath = "/form-styles/bitform-standalone-{$formID}.css"; |
| 21 |
|
| 22 |
wp_register_style('bitform-conversational-base-' . $formIdSafe, BITFORMS_UPLOAD_BASE_URL . $baseCSSPath, [], $formUpdateVersion); |
| 23 |
wp_register_style('bitform-conversational-' . $formIdSafe, BITFORMS_UPLOAD_BASE_URL . $baseConversationalCSSPath, [], $formUpdateVersion); |
| 24 |
wp_enqueue_style('bitform-conversational-base-' . $formIdSafe); |
| 25 |
wp_enqueue_style('bitform-conversational-' . $formIdSafe); |
| 26 |
|
| 27 |
if (file_exists(BITFORMS_CONTENT_DIR . $customCSSPath)) { |
| 28 |
wp_register_style('bitform-conversational-custom-' . $formIdSafe, BITFORMS_UPLOAD_BASE_URL . $customCSSPath, [], $formUpdateVersion); |
| 29 |
wp_enqueue_style('bitform-conversational-custom-' . $formIdSafe); |
| 30 |
} |
| 31 |
|
| 32 |
if (file_exists(BITFORMS_CONTENT_DIR . $standaloneCSSPath)) { |
| 33 |
wp_register_style('bitform-conversational-standalone-' . $formIdSafe, BITFORMS_UPLOAD_BASE_URL . $standaloneCSSPath, [], $formUpdateVersion); |
| 34 |
wp_enqueue_style('bitform-conversational-standalone-' . $formIdSafe); |
| 35 |
} |
| 36 |
|
| 37 |
if (isset($font) && '' !== $font) { |
| 38 |
wp_register_style('bitform-conversational-font-' . $formIdSafe, $font, [], $formUpdateVersion); |
| 39 |
wp_enqueue_style('bitform-conversational-font-' . $formIdSafe); |
| 40 |
} |
| 41 |
|
| 42 |
$conversationalJsPath = BITFORMS_UPLOAD_BASE_URL . '/form-scripts/bitform-conversational-' . $formID . '.js'; |
| 43 |
wp_register_script('bitform-conversational-' . $formIdSafe, $conversationalJsPath, [], $formUpdateVersion, true); |
| 44 |
wp_enqueue_script('bitform-conversational-' . $formIdSafe); |
| 45 |
if (isset($bfGlobals) && '' !== $bfGlobals) { |
| 46 |
wp_add_inline_script('bitform-conversational-' . $formIdSafe, (string) $bfGlobals, 'before'); |
| 47 |
} |
| 48 |
?> |
| 49 |
|
| 50 |
<!DOCTYPE html> |
| 51 |
<html lang="en"> |
| 52 |
|
| 53 |
<head> |
| 54 |
<meta charset="UTF-8"> |
| 55 |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| 56 |
<title><?php echo isset($title) ? esc_html($title) : 'Conversational Form'; ?></title> |
| 57 |
<style> |
| 58 |
* { |
| 59 |
padding: 0; |
| 60 |
margin: 0; |
| 61 |
box-sizing: border-box; |
| 62 |
} |
| 63 |
|
| 64 |
body.bit-conversational-form { |
| 65 |
height: 100vh; |
| 66 |
overflow: hidden; |
| 67 |
} |
| 68 |
|
| 69 |
@media (max-width: 575.98px) { |
| 70 |
.standalone-form-wrapper { |
| 71 |
width: 100%; |
| 72 |
} |
| 73 |
} |
| 74 |
</style> |
| 75 |
<?php if (isset($font) && '' !== $font) : ?> |
| 76 |
<link rel="preconnect" href="https://fonts.googleapis.com"> |
| 77 |
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> |
| 78 |
<?php endif; ?> |
| 79 |
<?php |
| 80 |
wp_enqueue_emoji_styles(); |
| 81 |
wp_print_styles(); |
| 82 |
?> |
| 83 |
</head> |
| 84 |
|
| 85 |
<body class="bit-conversational-form"> |
| 86 |
<?php echo wp_kses($formHTML, EscapingHelper::getFormAllowedHtml(isset($formContent) ? $formContent : null)); ?> |
| 87 |
<?php if (!empty($isPreview)) : ?> |
| 88 |
<style>.bc-live-hidden { display: none !important; }</style> |
| 89 |
<script>window.bf_preview_mode = true;</script> |
| 90 |
<?php endif; ?> |
| 91 |
<?php wp_print_scripts(); ?> |
| 92 |
<?php if (!empty($isPreview)) : ?> |
| 93 |
<script> |
| 94 |
(function() { |
| 95 |
var FID = <?php echo (int) $formID; ?>; |
| 96 |
var pfx = 'bc' + FID + '-'; |
| 97 |
|
| 98 |
function q(sel) { return document.querySelector('.' + pfx + sel); } |
| 99 |
function qa(sel) { return document.querySelectorAll('.' + pfx + sel); } |
| 100 |
function toggle(el, show) { if (el) el.classList.toggle('bc-live-hidden', !show); } |
| 101 |
function setTxt(el, val) { if (el && val !== undefined && val !== null) el.textContent = val; } |
| 102 |
|
| 103 |
var LAYOUT_CLASSES = ['normal-layout', 'compact-left-layout', 'compact-right-layout', 'padding-left-layout', 'padding-right-layout', 'centered-card-layout']; |
| 104 |
|
| 105 |
function applyLiveSettings(settings) { |
| 106 |
var nav = settings.navigationSettings || {}; |
| 107 |
var stl = settings.stepListObject || {}; |
| 108 |
var all = stl.allSteps || {}; |
| 109 |
|
| 110 |
// Visibility toggles |
| 111 |
toggle(q('nav-wrpr'), nav.show !== false); |
| 112 |
toggle(q('progress-lbl-wrpr'), !!nav.showProgressLabel); |
| 113 |
toggle(q('progress-bar-wrpr'), !!nav.showProgressBar); |
| 114 |
toggle(q('branding-wrpr'), nav.showBranding !== false); |
| 115 |
toggle(q('nav-btn-wrpr'), !!nav.showNavigateBtn); |
| 116 |
|
| 117 |
// Welcome page text content |
| 118 |
var wp = stl.welcomePage || {}; |
| 119 |
var titleEl = document.querySelector('.' + pfx + 'welcome-title h2'); |
| 120 |
setTxt(titleEl, wp.title); |
| 121 |
// Welcome page rich content (everything between title and button wrapper) |
| 122 |
var welcomeContentEl = q('welcome-content'); |
| 123 |
if (welcomeContentEl && wp.content !== undefined) { |
| 124 |
// Find the text node area — insert content after title, before button wrapper |
| 125 |
var btnWrpr = welcomeContentEl.querySelector('.' + pfx + 'step-btn-wrpr'); |
| 126 |
var titleWrpr = welcomeContentEl.querySelector('.' + pfx + 'welcome-title'); |
| 127 |
// Remove existing text nodes / content nodes between title and button |
| 128 |
var children = Array.from(welcomeContentEl.childNodes); |
| 129 |
children.forEach(function(node) { |
| 130 |
if (node !== titleWrpr && node !== btnWrpr) { |
| 131 |
welcomeContentEl.removeChild(node); |
| 132 |
} |
| 133 |
}); |
| 134 |
var tempDiv = document.createElement('div'); |
| 135 |
tempDiv.innerHTML = wp.content || ''; |
| 136 |
var frag = document.createDocumentFragment(); |
| 137 |
while (tempDiv.firstChild) frag.appendChild(tempDiv.firstChild); |
| 138 |
welcomeContentEl.insertBefore(frag, btnWrpr); |
| 139 |
} |
| 140 |
|
| 141 |
// Per-step: button text, hints, layout, image, background |
| 142 |
var wrappers = qa('step-wrapper'); |
| 143 |
var fieldKeys = Object.keys(stl).filter(function(k) { return k !== 'allSteps' && k !== 'welcomePage'; }); |
| 144 |
var welcomeOffset = (wp.enable) ? 1 : 0; |
| 145 |
|
| 146 |
Array.prototype.forEach.call(wrappers, function(wrapper, idx) { |
| 147 |
var isWelcome = wrapper.classList.contains(pfx + 'welcome'); |
| 148 |
var cfg = isWelcome ? wp : (stl[fieldKeys[idx - welcomeOffset]] || {}); |
| 149 |
|
| 150 |
// Button text (start button on welcome, ok button on steps) |
| 151 |
if (isWelcome) { |
| 152 |
var startBtn = wrapper.querySelector('.' + pfx + 'start-btn'); |
| 153 |
if (startBtn) { |
| 154 |
// preserve icon elements, update only text node |
| 155 |
var textNode = Array.from(startBtn.childNodes).find(function(n) { return n.nodeType === 3 && n.textContent.trim(); }); |
| 156 |
if (textNode) textNode.textContent = '\n ' + (cfg.btnTxt || all.btnTxt || 'Start') + '\n '; |
| 157 |
} |
| 158 |
} else { |
| 159 |
var btnEl = wrapper.querySelector('.' + pfx + 'btn-ok'); |
| 160 |
if (btnEl) { |
| 161 |
var textNode = Array.from(btnEl.childNodes).find(function(n) { return n.nodeType === 3 && n.textContent.trim(); }); |
| 162 |
if (textNode) textNode.textContent = '\n ' + (cfg.btnTxt || all.btnTxt || 'OK') + '\n '; |
| 163 |
} |
| 164 |
} |
| 165 |
|
| 166 |
// Step hints — rendered as HTML (same as PHP frontend output). |
| 167 |
var hintsEl = wrapper.querySelector('.' + pfx + 'step-hints'); |
| 168 |
var hintsVal = cfg.stepHints !== undefined ? cfg.stepHints : all.stepHints; |
| 169 |
if (hintsEl && hintsVal !== undefined && hintsVal !== null) hintsEl.innerHTML = hintsVal; |
| 170 |
|
| 171 |
// Layout class |
| 172 |
var layout = cfg.layout || all.layout || 'normal-layout'; |
| 173 |
LAYOUT_CLASSES.forEach(function(c) { wrapper.classList.remove(c); }); |
| 174 |
wrapper.classList.add(layout); |
| 175 |
|
| 176 |
// Layout image |
| 177 |
var imgEl = wrapper.querySelector('.' + pfx + 'step-img'); |
| 178 |
if (imgEl) imgEl.src = cfg.layoutImage || all.layoutImage || ''; |
| 179 |
|
| 180 |
// Show/hide image container based on layout |
| 181 |
var imgCntnr = wrapper.querySelector('.' + pfx + 'step-img-cntnr'); |
| 182 |
if (imgCntnr) imgCntnr.style.display = (layout === 'normal-layout' || layout === 'centered-card-layout') ? 'none' : ''; |
| 183 |
|
| 184 |
// Step background — Floating Card redirects to card element; others apply to wrapper. |
| 185 |
var bg = cfg.background || all.background || ''; |
| 186 |
var bgColor = typeof bg === 'object' ? (bg['background-color'] || '') : bg; |
| 187 |
var cardEl = wrapper.querySelector('.' + pfx + 'step-content'); |
| 188 |
var isRealColor = bgColor && bgColor !== 'transparent'; |
| 189 |
if (layout === 'centered-card-layout') { |
| 190 |
wrapper.style.removeProperty('background'); |
| 191 |
wrapper.style.removeProperty('background-color'); |
| 192 |
if (cardEl) { |
| 193 |
// Only override CSS white fallback when user has a real color set. |
| 194 |
if (isRealColor) cardEl.style.setProperty('background-color', bgColor, 'important'); |
| 195 |
else cardEl.style.removeProperty('background-color'); |
| 196 |
} |
| 197 |
} else { |
| 198 |
if (cardEl) cardEl.style.removeProperty('background-color'); |
| 199 |
if (isRealColor) wrapper.style.setProperty('background-color', bgColor, 'important'); |
| 200 |
else wrapper.style.removeProperty('background-color'); |
| 201 |
} |
| 202 |
}); |
| 203 |
} |
| 204 |
|
| 205 |
var BF_ALLOWED_ORIGIN = <?php |
| 206 |
$bfParsed = wp_parse_url(get_site_url()); |
| 207 |
$bfOrigin = $bfParsed['scheme'] . '://' . $bfParsed['host']; |
| 208 |
if (!empty($bfParsed['port'])) $bfOrigin .= ':' . $bfParsed['port']; |
| 209 |
echo wp_json_encode($bfOrigin); |
| 210 |
?>; |
| 211 |
window.addEventListener('message', function(e) { |
| 212 |
if (!e.origin || e.origin !== BF_ALLOWED_ORIGIN) return; |
| 213 |
var d = e.data; |
| 214 |
if (!d || !d.__bf) return; |
| 215 |
|
| 216 |
if (d.type === 'bf:css') { |
| 217 |
var el = document.getElementById('bf-live-preview-css'); |
| 218 |
if (!el) { |
| 219 |
el = document.createElement('style'); |
| 220 |
el.id = 'bf-live-preview-css'; |
| 221 |
document.head.appendChild(el); |
| 222 |
} |
| 223 |
el.textContent = d.css; |
| 224 |
} |
| 225 |
|
| 226 |
if (d.type === 'bf:settings') { |
| 227 |
applyLiveSettings(d.settings); |
| 228 |
} |
| 229 |
|
| 230 |
if (d.type === 'bf:navigate') { |
| 231 |
var contentIds = Object.keys(window.bf_globals || {}); |
| 232 |
for (var i = 0; i < contentIds.length; i++) { |
| 233 |
var g = window.bf_globals[contentIds[i]]; |
| 234 |
var inst = g && g.inits && g.inits.conversational_form; |
| 235 |
if (inst && typeof inst.goToStep === 'function') { inst.goToStep(d.stepIndex); break; } |
| 236 |
} |
| 237 |
} |
| 238 |
}); |
| 239 |
})(); |
| 240 |
</script> |
| 241 |
<?php endif; ?> |
| 242 |
</body> |
| 243 |
|
| 244 |
</html> |
| 245 |
|