PluginProbe
Bit Form – Contact Form, Payment Forms, Multi Step Forms, Calculator & Custom Form Builder / 3.3.1
Bit Form – Contact Form, Payment Forms, Multi Step Forms, Calculator & Custom Form Builder v3.3.1
3.3.1 V-3.3.0 3.2.2 3.2.1 3.2.0 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 V3.0.3 V3.0.2 -3.0.1 V_3.0.0 1.1.1 1.1.8 1.2 1.3 1.4 1.4.18 1.5.2 1.9 2.0 2.10.0 2.10.1 All 138 releases
← All changes | includes/Admin/Form/FrontEndScriptGenerator.php +347 -218 2.03.3.1 View file →
@@ -1,15 +1,18 @@
1 1 <?php
2 2
3 3 namespace BitCode\BitForm\Admin\Form;
4 4
5 -use BitCode\BitForm\Admin\Form\InitJs\RecaptchaV3;
6 -use BitCode\BitForm\Core\Form\FormManager;
7 -use BitCode\BitForm\Core\Integration\IntegrationHandler;
5 +if (!defined('ABSPATH')) {
6 + exit;
7 +}
8 +
9 +use BitCode\BitForm\Core\Util\FileHandler;
8 10 use BitCode\BitForm\Core\Util\Log;
9 11 use WP_Error;
10 12
11 -class FrontEndScriptGenerator {
13 +class FrontEndScriptGenerator
14 +{
12 15 private $_scriptAddedFlags;
13 16 private $_fields;
14 17 private $_formContents;
15 18 private $_jsFilesNeeded;
@@ -15,35 +18,48 @@
15 18 private $_jsFilesNeeded;
16 19 private $_loadedScriptsList;
17 20 private $_validationJsFilesNeeded;
18 21
19 - public function __construct() {
22 + public function __construct()
23 + {
20 24 $this->_scriptAddedFlags = [];
21 25 $this->_fields = [];
22 26 $this->_jsFilesNeeded = [];
23 27 $this->_loadedScriptsList = [];
24 - $this->_validationJsFilesNeeded = ScriptFilePriorityManager::validationScriptFile();
28 + $this->_validationJsFilesNeeded = ScriptFilePriorityManager::validationAndOtherScriptFile();
25 29 }
26 30
27 - private static function generateBfGlobalObjJS($contentIds = []) {
28 - // ["bitforms_3_submit_85_1", "bitforms_3_submit_85_2"]
29 - // bf_globals.{contentId}.inits
30 - $contentidArray = json_encode($contentIds);
31 - return <<<GLOBALOBJ
32 - if(!window.bf_globals){ window.bf_globals = {};}
33 - $contentidArray.forEach(function(contentId){
34 - if(!window.bf_globals[contentId]){
35 - window.bf_globals[contentId] = {inits: {}, contentId: contentId};
36 - }else{
37 - window.bf_globals[contentId].inits = {};
38 - window.bf_globals[contentId].contentId = contentId;
31 + private static function generateBfGlobalObjJS($contentIds = [])
32 + {
33 + $contentidArray = wp_json_encode($contentIds);
34 + return ' if(!window.bf_globals){ window.bf_globals = {};}
35 + (function(){
36 + var bfSetupGlobals = function(){
37 + ' . $contentidArray . '.forEach(function(contentId){
38 + const form = document.getElementById(contentId);
39 + if(!form){
40 + delete window.bf_globals[contentId];
41 + return;
42 + }
43 + if(!window.bf_globals[contentId]){
44 + window.bf_globals[contentId] = {inits: {}, contentId: contentId};
45 + }else{
46 + if(!window.bf_globals[contentId].inits) window.bf_globals[contentId].inits = {};
47 + window.bf_globals[contentId].contentId = contentId;
48 + }
49 + });
50 + };
51 + if(document.readyState === "loading"){
52 + document.addEventListener("DOMContentLoaded", bfSetupGlobals, { once: true });
53 + } else {
54 + bfSetupGlobals();
39 55 }
40 - });
41 -GLOBALOBJ;
56 + })();';
42 57 }
43 58
44 - private static function isValidationNeeded($fldData) {
45 - $validationsToCheck = ['valid->req', 'valid->regexr', 'mn', 'mx', 'opt', 'err->invalid->show'];
59 + private static function isValidationNeeded($fldData)
60 + {
61 + $validationsToCheck = ['valid->req', 'valid->regexr', 'mn', 'mx', 'opt', 'err->invalid->show', 'err->notVerified->show'];
46 62 foreach ($validationsToCheck as $property) {
47 63 $needValidation = Helpers::property_exists_nested($fldData, $property);
48 64 if ($needValidation) {
49 65 return true;
@@ -51,9 +67,10 @@
51 67 }
52 68 return false;
53 69 }
54 70
55 - public function generateJsFile($formContents, $fields, $contentIds, $postId, $formIDs = null, $preview = false) {
71 + public function generateJsFile($formContents, $fields, $contentIds, $postId, $formIDs = null, $preview = 'classic')
72 + {
56 73 add_option('bitforms_frontend_js_generating', true);
57 74 $this->_formContents = $formContents;
58 75 $this->_fields = $fields;
59 76
@@ -59,11 +76,8 @@
59 76
60 77 $this->appendJs(self::generateBfGlobalObjJS($contentIds), $postId, $preview, 'w');
61 78 $this->_jsFilesNeeded = ScriptFilePriorityManager::jsFile();
62 79
63 - // Recaptcha v3 script is loaded separately
64 - $this->appendJs($this->getRecaptchaV3Script($formIDs), $postId, $preview);
65 -
66 80 // helper js files for whole form
67 81 foreach ($this->_jsFilesNeeded['helperScript'] as $jsFile) {
68 82 $this->addScriptInLoadedScriptsList($jsFile);
69 83 }
@@ -76,13 +90,33 @@
76 90
77 91 // for frontend helper js files
78 92 $this->jsFrontendScript();
79 93
80 - // for hidden Field when catch plugin on
94 + // for hidden Field when cache plugin on
81 95 $this->jsHiddenFieldScript();
82 96
97 + // for recaptcha v3 (form-level setting)
98 + $this->jsRecaptchaV3Scripts();
99 +
100 + // for form abandonment scripts
101 + $this->jsFormAbandonmentScripts();
102 +
103 + // multi step form scripts
104 + $this->multiStepFormScripts();
105 +
106 + // conversational form scripts
107 + $this->conversationalFormScripts();
108 +
83 109 //⚠👆 every script file generate method call before this method
84 110 $this->appendJs($this->getScript(), $postId, $preview);
111 + $this->appendJs($this->generateFieldConfigsJs($contentIds), $postId, $preview);
112 + // init all js events and functions at last
113 + $this->appendJs(self::getFileFromAssetsJs('bitform-init.min.js'), $postId, $preview);
114 + // picks up forms injected after load (AJAX, popups, lazy content)
115 + $this->appendJs(self::getFileFromAssetsJs('bitform-observer.min.js'), $postId, $preview);
116 + if (defined('ELEMENTOR_PRO_VERSION')) {
117 + $this->appendJs(self::getFileFromAssetsJs('bitform-elementor.min.js'), $postId, $preview);
118 + }
85 119
86 120 // for js file minification
87 121 $this->appendJs(self::getCustomJsCodes($contentIds), $postId, $preview);
88 122 delete_option('bitforms_frontend_js_generating');
@@ -88,63 +122,62 @@
88 122 delete_option('bitforms_frontend_js_generating');
89 123 return true;
90 124 }
91 125
92 -/*
93 - public function generateJsFile($formContents, $fields, $contentIds, $postId, $formIDs = null, $preview = false) {
94 - $this->_formContents = $formContents;
95 - $this->_fields = $fields;
126 + private function multiStepFormScripts()
127 + {
128 + foreach ($this->_formContents as $formContent) {
129 + $layout = $formContent->layout;
130 + if (is_array($layout) && count($layout) > 1) {
131 + $files = ScriptFilePriorityManager::multiStepFiles();
132 + foreach ($files as $file) {
133 + $this->addScriptInLoadedScriptsList($file);
134 + }
135 + }
136 + }
137 + }
96 138
97 - $script = '';
98 - $script .= self::generateBfGlobalObjJS($contentIds);
99 - $this->_jsFilesNeeded = ScriptFilePriorityManager::jsFile();
139 + private function conversationalFormScripts()
140 + {
141 + foreach ($this->_formContents as $formContent) {
142 + if (!empty($formContent->formInfo->conversationalSettings) && $formContent->formInfo->conversationalSettings->enable) {
143 + $files = ScriptFilePriorityManager::conversationalFormFiles();
144 + foreach ($files as $file) {
145 + $this->addScriptInLoadedScriptsList($file);
146 + }
147 + }
148 + }
149 + }
100 150
101 - // Recaptcha v3 script is loaded separately
102 - $script .= $this->getRecaptchaV3Script($formIDs);
103 -
104 - // custom js files for whole form
105 - foreach ($this->_jsFilesNeeded['helperScript'] as $jsFile) {
106 - $this->addScriptInLoadedScriptsList($jsFile);
151 + private function jsFormAbandonmentScripts()
152 + {
153 + /**
154 + * Add-ons can inject required script files here.
155 + *
156 + * @param array $files Array of file arrays: ['priority' => int, 'filename' => string]
157 + * @param array $formContents
158 + * @param array $fields
159 + */
160 + $files = apply_filters('bitform_form_abandonment_script_files', [], $this->_formContents, $this->_fields);
161 + if (empty($files) || !is_array($files)) {
162 + return;
107 163 }
164 + foreach ($files as $jsFile) {
165 + if (!empty($jsFile['filename']) && !empty($jsFile['priority'])) {
166 + $this->addScriptInLoadedScriptsList($jsFile);
167 + }
168 + }
169 + }
108 170
109 - // for add validation js files
110 - $this->jsValidationNeededFile();
111 -
112 - // js files for all fields
113 - $this->jsFieldsNeededFile();
114 -
115 - // for frontend helper js files
116 - $this->jsFrontendScript();
117 -
118 - // for hidden Field when catch plugin on
119 - $this->jsHiddenFieldScript();
120 -
121 - //⚠👆 every script file generate method call before this method
122 - $script .= $this->getScript();
123 -
124 - // for js file minification
125 - $script = Helpers::minifyJS($script);
126 -
127 - $script .= self::getCustomJsCodes($contentIds);
128 -
129 - // write script file
171 + private function appendJs($script, $postId, $previewMode, $mode = 'a')
172 + {
130 173 $fileName = '';
131 174 $path = '';
132 - if ($preview) {
133 - $fileName = "preview-{$postId}.js";
175 +
176 + if ('conversational' === $previewMode) {
177 + $fileName = "bitform-conversational-{$postId}.js";
134 178 $path = 'form-scripts';
135 - } else {
136 - $fileName = "bitform-js-$postId.js";
137 - $path = "form-scripts/{$postId}";
138 - }
139 - Helpers::saveFile($path, $fileName, $script, 'w');
140 - return true;
141 - }
142 -*/
143 - private function appendJs($script, $postId, $preview, $mode = 'a') {
144 - $fileName = '';
145 - $path = '';
146 - if ($preview) {
179 + } elseif ('preview' === $previewMode) {
147 180 $fileName = "preview-{$postId}.js";
148 181 $path = 'form-scripts';
149 182 } else {
150 183 $fileName = "bitform-js-$postId.js";
@@ -149,28 +182,13 @@
149 182 } else {
150 183 $fileName = "bitform-js-$postId.js";
151 184 $path = "form-scripts/{$postId}";
152 185 }
153 - Helpers::saveFile($path, $fileName, Helpers::minifyJS($script), $mode);
186 + Helpers::saveFile($path, $fileName, Helpers::minifyJs($script), $mode);
154 187 }
155 188
156 - private function getRecaptchaV3Script($formIds) {
157 - foreach ($formIds as $formID) {
158 - if (in_array('RecaptchaV3', $this->_scriptAddedFlags)) {
159 - break;
160 - }
161 - $getCaptchaV3Settings = (new FormManager($formID))->getCaptchaV3Settings();
162 - if ($getCaptchaV3Settings) {
163 - $integrationHandler = new IntegrationHandler(0);
164 - $allFormIntegrations = $integrationHandler->getAllIntegration('app', 'gReCaptchaV3');
165 - $siteKey = json_decode($allFormIntegrations[0]->integration_details)->siteKey;
166 - $this->_scriptAddedFlags[] = 'RecaptchaV3';
167 - return RecaptchaV3::init($siteKey);
168 - }
169 - }
170 - }
171 -
172 - private function getScript() {
189 + private function getScript()
190 + {
173 191 usort($this->_loadedScriptsList, function ($a, $b) {
174 192 return $a['priority'] - $b['priority'];
175 193 });
176 194
@@ -176,39 +194,25 @@
176 194
177 195 $script = '';
178 196 foreach ($this->_loadedScriptsList as $scriptFileArr) {
179 197 $fileNam = $scriptFileArr['filename'];
180 - if (!isset($scriptFileArr['scriptTyp']) || 'script' === $scriptFileArr['scriptTyp']) {
181 - if (self::getFileFromAssetsJs($fileNam)) {
182 - $script .= self::getFileFromAssetsJs($fileNam);
183 - continue;
184 - }
185 - continue;
198 + $fileContent = self::getFileFromAssetsJs($fileNam);
199 + if ($fileContent) {
200 + $script .= $fileContent;
186 201 }
187 - if ('init' === $scriptFileArr['scriptTyp']) {
188 - $fieldObjInstance = $scriptFileArr['source'] . $fileNam;
189 - if (!class_exists($fieldObjInstance)) {
190 - continue;
191 - } // when class not found, skip load script
192 - $encodedConf = json_encode($scriptFileArr['field']->fldConfigs);
193 - $script .= $fieldObjInstance::init($scriptFileArr['fk'], $encodedConf, $scriptFileArr['contentId']);
194 - continue;
195 - }
196 - if ('custom' === $scriptFileArr['scriptTyp']) {
197 - $fileInstance = $scriptFileArr['source'] . $fileNam;
198 - if (!class_exists($fileInstance)) {
199 - continue;
200 - } // when class not found, skip load script
201 - $script .= $fileInstance::init($scriptFileArr['fk'], $scriptFileArr['field'], $scriptFileArr['contentId']);
202 - }
203 202 }
204 203 return $script;
205 204 }
206 205
207 - private function jsValidationNeededFile() {
206 + private function jsValidationNeededFile()
207 + {
208 208 foreach ($this->_fields as $flds) {
209 209 foreach ($flds as $fld) {
210 210 $fldData = $fld['field'];
211 + // Corrupt/partial form JSON can produce a null field or one without typ.
212 + if (!is_object($fldData)) {
213 + continue;
214 + }
211 215 // for validation js files
212 216 if (self::isValidationNeeded($fldData)) {
213 217 $validation = $this->_validationJsFilesNeeded['validation'];
214 218 $this->addScriptInLoadedScriptsList($validation);
@@ -228,18 +232,30 @@
228 232
229 233 $validationScriptFileMapping = ScriptFilePriorityManager::validationScriptFileMapping($fldData->typ);
230 234 if ($validationScriptFileMapping) {
231 235 foreach ($validationScriptFileMapping as $key => $value) {
232 - foreach ($value['paths'] as $path) {
236 + $paths = $value['paths'] ?? [];
237 + $hasMatchingPath = false;
238 + foreach ($paths as $path) {
233 239 if (Helpers::property_exists_nested($fldData, $path)) {
234 - $file = $this->_validationJsFilesNeeded[$key];
235 - $this->addScriptInLoadedScriptsList($file);
240 + $hasMatchingPath = true;
241 + break; // one match is enough
236 242 }
237 243 }
238 - if (isset($value['dependencies'])) {
239 - foreach ($value['dependencies'] as $dependency) {
240 - $dependencyFile = $this->_validationJsFilesNeeded[$dependency];
241 - $this->addScriptInLoadedScriptsList($dependencyFile);
244 +
245 + if (!$hasMatchingPath) {
246 + continue;
247 + }
248 +
249 + // Add the main script once
250 + if (!empty($this->_validationJsFilesNeeded[$key])) {
251 + $this->addScriptInLoadedScriptsList($this->_validationJsFilesNeeded[$key]);
252 + }
253 +
254 + // Add dependencies once each
255 + foreach (($value['dependencies'] ?? []) as $dep) {
256 + if (!empty($this->_validationJsFilesNeeded[$dep])) {
257 + $this->addScriptInLoadedScriptsList($this->_validationJsFilesNeeded[$dep]);
242 258 }
243 259 }
244 260 }
245 261 }
@@ -246,9 +262,11 @@
246 262 }
247 263 }
248 264 }
249 265
250 - private function jsFieldsNeededFile() {
266 + private function jsFieldsNeededFile()
267 + {
268 + $filePondPluginList = apply_filters('bitform_filepond_plugins_list', []);
251 269 foreach ($this->_fields as $typ => $flds) {
252 270 if (!array_key_exists($typ, $this->_jsFilesNeeded)) {
253 271 continue;
254 272 }
@@ -258,46 +276,44 @@
258 276 if ('advanced-file-up' === $typ) {
259 277 $configs = $fld['field']->config;
260 278 foreach ($configs as $configKey => $config) {
261 279 if ($configs->$configKey) {
262 - $filepondPlugin = ScriptFilePriorityManager::filePondPlugins($configKey);
263 - if ($filepondPlugin) {
264 - $this->addScriptInLoadedScriptsList($filepondPlugin);
280 + if (array_key_exists($configKey, $filePondPluginList)) {
281 + $this->addScriptInLoadedScriptsList($filePondPluginList[$configKey]);
265 282 }
266 283 }
267 284 }
268 285 }
269 286 foreach ($fldScriptArr as $scriptFile) {
270 - if (!isset($scriptFile['scriptTyp'])) {
271 - $this->addScriptInLoadedScriptsList($scriptFile);
287 + if (isset($scriptFile['paths']) && is_array($scriptFile['paths'])) {
288 + $pathMatched = false;
289 + foreach ($scriptFile['paths'] as $path) {
290 + if (Helpers::property_exists_nested($fld['field'], $path)) {
291 + $pathMatched = true;
292 + break;
293 + }
294 + }
295 + if (!$pathMatched) {
296 + continue;
297 + }
298 + } elseif (isset($scriptFile['path']) && !Helpers::property_exists_nested($fld['field'], $scriptFile['path'])) {
272 299 continue;
273 300 }
274 - if ('init' === $scriptFile['scriptTyp']) {
275 - $scriptFile['field'] = $fld['field'];
276 - $scriptFile['fk'] = $fld['fk'];
277 - $scriptFile['formID'] = $fld['formID'];
278 - $scriptFile['contentId'] = $fld['contentId'];
279 - $fld['field']->fldConfigs = self::generateFieldConfig($fld['fk'], $fld['field'], $fld['formID']);
280 - $this->_loadedScriptsList[] = $scriptFile;
281 - continue;
282 - }
283 - if ('custom' === $scriptFile['scriptTyp']) {
284 - $scriptFile['field'] = $fld['field'];
285 - $scriptFile['fk'] = $fld['fk'];
286 - $scriptFile['contentId'] = $fld['contentId'];
287 - $this->addScriptInLoadedScriptsList($scriptFile);
288 - }
301 + $this->addScriptInLoadedScriptsList($scriptFile);
289 302 }
290 303 }
291 304 }
292 305 }
293 306
294 - private static function getCustomJsCodes($contentIds = []) {
295 - $script = 'let bfContentId = "", bfVars= "";';
307 + private static function getCustomJsCodes($contentIds = [])
308 + {
309 + $script = 'let bfContentId = "", bfSlNo = "1", bfVars= "";';
296 310 foreach ($contentIds as $contentId) {
297 - $jsCode = self::getCustomCodes(explode('_', $contentId)[1])['JavaScript'];
311 + $contentIdArr = explode('_', $contentId);
312 + $jsCode = self::getCustomCodes($contentIdArr[1])['JavaScript'];
313 + $bfSlNo = array_key_exists(3, $contentIdArr) ? $contentIdArr[3] : '1';
298 314 if (!empty($jsCode)) {
299 - $script .= " bfContentId = '{$contentId}'; bfVars = window.bf_globals.{$contentId}.smartTags; {$jsCode}";
315 + $script .= " if(bfSelect('#{$contentId}')){ bfContentId = '{$contentId}'; bfSlNo = '{$bfSlNo}'; bfVars = window.bf_globals.{$contentId}.smartTags; {$jsCode}}";
300 316 }
301 317 }
302 318
303 319 return $script;
@@ -302,9 +318,10 @@
302 318
303 319 return $script;
304 320 }
305 321
306 - private static function getFileFromAssetsJs($fileName) {
322 + private static function getFileFromAssetsJs($fileName)
323 + {
307 324 $sourceJsFile = BITFORMS_PLUGIN_DIR_PATH . 'assets' . DIRECTORY_SEPARATOR . $fileName;
308 325 if (file_exists($sourceJsFile)) {
309 326 return file_get_contents($sourceJsFile);
310 327 }
@@ -311,9 +328,10 @@
311 328 Log::debug_log('file not found: ' . $fileName);
312 329 return false;
313 330 }
314 331
315 - public static function saveCssFile($formId, $atomicCssText) {
332 + public static function saveCssFile($formId, $atomicCssText)
333 + {
316 334 $path = 'form-styles';
317 335 $fileName = "bitform-$formId.css";
318 336 if (!isset($formId) || '' === $formId) {
319 337 return new WP_Error('missing_form_id', __('Error Occurred, Please Reload', 'bit-form'));
@@ -320,13 +338,15 @@
320 338 }
321 339 return Helpers::saveFile($path, $fileName, $atomicCssText, 'w');
322 340 }
323 341
324 - public static function customCodeFile($formId, $customCodes) {
342 + public static function customCodeFile($formId, $customCodes)
343 + {
325 344 // for js file
326 345 $path = 'form-scripts';
327 346 $fileName = "bitform-custom-$formId.js";
328 - self::customCodeFileSaveOrDelete($customCodes->JavaScript, $path, $fileName);
347 + $filteredJs = Helpers::removeJsSingleLineComments($customCodes->JavaScript);
348 + self::customCodeFileSaveOrDelete($filteredJs, $path, $fileName);
329 349
330 350 // for css file
331 351 $path = 'form-styles';
332 352 $fileName = "bitform-custom-$formId.css";
@@ -334,22 +354,22 @@
334 354
335 355 return true;
336 356 }
337 357
338 - private static function customCodeFileSaveOrDelete($script, $path, $fileName) {
358 + public static function customCodeFileSaveOrDelete($script, $path, $fileName)
359 + {
339 360 if ($script) {
340 361 Helpers::saveFile($path, $fileName, $script, 'w');
341 362 } else {
342 363 $uploadPath = "$path/$fileName";
343 364 $uploadFilePath = Helpers::generatePathDirOrFile($uploadPath);
344 - if (file_exists($uploadFilePath)) {
345 - unlink($uploadFilePath);
346 - }
365 + FileHandler::deleteIsFileExists($uploadFilePath);
347 366 }
348 367 return true;
349 368 }
350 369
351 - public static function getCustomCodes($formId) {
370 + public static function getCustomCodes($formId)
371 + {
352 372 $customCodes = ['JavaScript' => '', 'CSS' => ''];
353 373 $customJsPath = Helpers::generatePathDirOrFile("form-scripts/bitform-custom-$formId.js");
354 374 $customCodes['JavaScript'] = Helpers::fileRead($customJsPath);
355 375
@@ -358,70 +378,165 @@
358 378
359 379 return $customCodes;
360 380 }
361 381
362 - public static function generateFieldConfig($fieldKey, $field, $formID) {
363 - $fieldType = $field->typ;
364 - $missingConfigs = ScriptFilePriorityManager::fieldMissingConfigsProps($fieldType, $field) ?: [];
365 - $hasConfigProp = in_array('config', $missingConfigs);
366 - $fldConf = (object) [];
367 - if (false === $hasConfigProp && isset($field->config)) {
368 - $fldConf = $field->config;
382 + private $fldContainersByType = [
383 + 'address' => '.__$fk__-parent-fld-wrp',
384 + 'select' => '.__$fk__-dpd-fld-wrp',
385 + 'country' => '.__$fk__-country-fld-wrp',
386 + 'currency' => '.__$fk__-currency-fld-wrp',
387 + 'phone-number' => '.__$fk__-phone-fld-wrp',
388 + 'file-up' => '.__$fk__-file-up-wrpr',
389 + 'advanced-file-up' => '#filepond-__$fk__-container',
390 + 'paypal' => '.__$fk__-paypal-wrp',
391 + 'razorpay' => '.__$fk__-razorpay-wrp',
392 + 'recaptcha' => '.__$fk__-recaptcha-wrp',
393 + 'stripe' => '.__$fk__-stripe-fld',
394 + 'mollie' => '.__$fk__-mollie-wrp',
395 + 'repeater' => '.__$fk__-rpt-fld-wrp',
396 + 'signature' => '.__$fk__-inp-fld-wrp',
397 + 'rating' => '.__$fk__-inp-fld-wrp',
398 + 'email-otp' => '.__$fk__-inp-fld-wrp',
399 + 'hcaptcha' => '.__$fk__-h-captcha-wrp',
400 + 'advanced-datetime' => '.__$fk__-advanced-datetime',
401 + ];
402 +
403 + private function generateFieldConfigsJs()
404 + {
405 + $customFlds = ['address', 'select', 'country', 'currency', 'phone-number', 'file-up', 'advanced-file-up', 'paypal', 'razorpay', 'stripe', 'mollie', 'recaptcha', 'repeater', 'signature', 'rating', 'turnstile', 'hcaptcha', 'advanced-datetime', 'email-otp'];
406 + $allFieldTypes = array_keys($this->_fields);
407 + $customFldsInForms = array_intersect($allFieldTypes, $customFlds);
408 + $formContents = $this->_formContents;
409 + $recaptchaV3Enabled = false;
410 + foreach ($formContents as $content) {
411 + if (isset($content->additional->enabled->recaptchav3) && $content->additional->enabled->recaptchav3) {
412 + $recaptchaV3Enabled = true;
413 + break;
414 + }
369 415 }
370 - $vars = ['fieldKey' => $fieldKey, 'formID' => $formID];
371 - foreach ($missingConfigs as $confPath => $fldPath) {
372 - $value = '';
373 - if (isset($fldPath['var']) && $vars[$fldPath['var']]) {
374 - $value = $vars[$fldPath['var']];
416 + if (empty($customFldsInForms) && !$recaptchaV3Enabled) {
417 + return '';
418 + }
419 +
420 + $containers = [];
421 + foreach ($customFldsInForms as $customFldTyp) {
422 + if (isset($this->fldContainersByType[$customFldTyp])) {
423 + $containers[$customFldTyp] = $this->fldContainersByType[$customFldTyp];
375 424 }
376 - if (empty($value) && isset($fldPath['path'])) {
377 - $value = Helpers::getDataFromNestedPath($field, $fldPath['path']);
425 + }
426 +
427 + $customFldConfigPaths = [];
428 +
429 + $allConfs = ScriptFilePriorityManager::getAllFldConfs();
430 + foreach ($customFldsInForms as $customFldTyp) {
431 + if (isset($allConfs[$customFldTyp])) {
432 + $customFldConfigPaths[$customFldTyp] = $allConfs[$customFldTyp];
433 + } else {
434 + $customFldConfigPaths[$customFldTyp] = (object) [];
378 435 }
379 - if (empty($value) && isset($fldPath['val'])) {
380 - $value = $fldPath['val'];
381 - }
382 - $fldConf = Helpers::setDataToNestedPath($fldConf, $confPath, $value);
383 436 }
384 - return $fldConf;
385 - }
386 437
387 - private function getJsFuncForValidation() {
388 - $validationCmnFunc = ScriptFilePriorityManager::validationCommonFunction();
389 - foreach ($validationCmnFunc as $funcName => $funcArr) {
390 - // get need validation function from validationScriptFile()
391 - $scriptFile = $this->_validationJsFilesNeeded[$funcName];
392 - if (isset($funcArr['notFields'])) {
393 - if (is_array($funcArr['notFields'])) {
394 - foreach ($funcArr['notFields'] as $notField) {
395 - if (!isset($this->_fields[$notField])) {
396 - $this->addScriptInLoadedScriptsList($scriptFile);
397 - }
438 + $customFldConfigPaths = wp_json_encode($customFldConfigPaths);
439 + $containers = wp_json_encode($containers);
440 +
441 + $script = ' const customFldConfigPaths = ' . $customFldConfigPaths . ';
442 + const fldContainers = ' . $containers . ';
443 +
444 + function initAllCustomFlds (formContentId = null) {
445 + const allContendIds = formContentId ? [formContentId] : Object.keys(bf_globals);
446 + allContendIds.forEach((contentId) => {
447 + const contentData = bf_globals[contentId];
448 + if(!contentData?.inits) contentData.inits = {};
449 + const flds = bf_globals[contentId]?.fields || {};
450 + const fldKeys = Object.keys(flds).reverse();
451 + fldKeys.forEach((fldKey) => {
452 + const fldData = flds[fldKey];
453 + const fldType = fldData.typ;
454 + if(fldType === \'paypal\') {
455 + initPaypalFld(contentId, fldKey, fldData, fldType);
456 + } else if(fldType === \'razorpay\') {
457 + initRazorpayFld(contentId, fldKey, fldType);
458 + } else if(fldType === \'recaptcha\') {
459 + initRecaptchaFld(contentId, fldKey, fldType);
460 + } else if(fldType === \'turnstile\') {
461 + initTurnstileFld(contentId, fldKey);
462 + } else if(fldType === \'hcaptcha\') {
463 + initHCaptchaFld(contentId, fldKey, fldType);
464 + } else if(fldType === \'stripe\' || fldType === \'mollie\') {
465 + initStripeFld(contentId, fldKey, fldType);
466 + } else if (customFldConfigPaths[fldType]) {
467 + contentData.inits[fldKey] = getFldInstance(contentId, fldKey, fldType);
398 468 }
469 + });
470 + if(contentData.gRecaptchaVersion === \'v3\' && contentData.gRecaptchaSiteKey){
471 + initRecaptchaV3Fld(contentId, contentData);
399 472 }
400 - }
401 - if (isset($funcArr['fields'])) {
402 - if (is_array($funcArr['fields'])) {
403 - foreach ($funcArr['fields'] as $fieldType) {
404 - if (isset($this->_fields[$fieldType])) {
405 - // for set function array in $this->_loadedScriptsList
406 - $this->addScriptInLoadedScriptsList($scriptFile);
407 - }
473 + });
474 + };
475 + function getFldInstance(contentId, fldKey, fldTyp, nestedSelector = \'\') {
476 + const fldClass = this[\'bit_\'+fldTyp.replace(/-/g, \'_\')+\'_field\'];
477 + const selector = \'#form-\'+contentId+\' \'+nestedSelector+fldContainers[fldTyp].replace("__$fk__", fldKey);
478 + if(!fldClass || !bfSelect(selector)) return;
479 + return new fldClass(selector, getFldConf(contentId, fldKey, fldTyp));
480 + };
481 + function getFldConf(contentId, fieldKey, fldTyp) {
482 + const fldData = bf_globals[contentId].fields[fieldKey];
483 + const fldConfPaths = Object.entries(customFldConfigPaths[fldTyp]);
484 + let fldConf = {};
485 + const { formId } = bf_globals[contentId];
486 + if (!("config" in customFldConfigPaths[fldTyp]) && "config" in fldData) fldConf = fldData.config;
487 + const varData = { contentId, fieldKey, formId };
488 + fldConfPaths.forEach(([ confPath, fldPath ]) => {
489 + let value = "";
490 + if (fldPath.var) value = varData[fldPath.var];
491 + if (!value && fldPath.path) {
492 + if(Array.isArray(fldPath.path)) {
493 + fldPath.path.forEach((path) => {
494 + if(!value) value = getDataFromNestedPath(fldData, path);
495 + });
496 + } else {
497 + value = getDataFromNestedPath(fldData, fldPath.path);
408 498 }
409 - } else {
410 - // for all field types
411 - $this->addScriptInLoadedScriptsList($scriptFile);
412 499 }
413 - }
414 -
415 - if (isset($funcArr['dependencies'])) {
416 - foreach ($funcArr['dependencies'] as $dependency) {
417 - $this->addScriptInLoadedScriptsList($dependency);
500 + if (!value && "val" in fldPath) {
501 + value = fldPath.val;
502 + if(typeof value === \'string\') {
503 + Object.entries(varData).forEach(([key, val]) => {
504 + value = value.replace("__$"+key+"__", val);
505 + });
506 + }
418 507 }
508 + fldConf = setDataToNestedPath(fldConf, confPath, value);
509 + });
510 + return fldConf;
511 + };
512 + function getDataFromNestedPath(data, key) {
513 + const keys = key.split("->");
514 + const lastKey = keys.pop();
515 + let current = {...data};
516 + for (const k of keys) {
517 + if (!(k in current)) return null;
518 + current = current[k];
419 519 }
520 + return current[lastKey] || null;
420 521 }
522 + function setDataToNestedPath(data, key, value) {
523 + const keys = key.split("->");
524 + const lastKey = keys.pop();
525 + let current = {...data};
526 + keys.forEach((k) => {
527 + if (!current[k]) current[k] = {};
528 + current = current[k];
529 + });
530 + current[lastKey] = value;
531 + return current;
532 + }';
533 +
534 + return $script;
421 535 }
422 536
423 - private function addScriptInLoadedScriptsList($fileArr) {
537 + private function addScriptInLoadedScriptsList($fileArr)
538 + {
424 539 $fileName = $fileArr['filename'];
425 540 if (!in_array($fileName, $this->_scriptAddedFlags)) {
426 541 $this->_loadedScriptsList[] = $fileArr;
427 542 $this->_scriptAddedFlags[] = $fileName;
@@ -428,28 +543,40 @@
428 543 }
429 544 return true;
430 545 }
431 546
432 - private function jsFrontendScript() {
547 + private function jsFrontendScript()
548 + {
433 549 foreach ($this->_formContents as $formContent) {
434 550 if (Helpers::property_exists_nested($formContent, 'workFlowExist->oninput', true)) {
435 - $fileArr = ScriptFilePriorityManager::validationScriptFile()['conditionalLogic'];
551 + $fileArr = ScriptFilePriorityManager::validationAndOtherScriptFile()['conditionalLogic'];
436 552 $this->addScriptInLoadedScriptsList($fileArr);
437 - $fileArr = ScriptFilePriorityManager::validationScriptFile()['validateFocusLost'];
553 + $fileArr = ScriptFilePriorityManager::validationAndOtherScriptFile()['resetPlaceholders'];
438 554 $this->addScriptInLoadedScriptsList($fileArr);
555 + $fileArr = ScriptFilePriorityManager::validationAndOtherScriptFile()['bfResetDefaultValue'];
556 + $this->addScriptInLoadedScriptsList($fileArr);
557 + $fileArr = ScriptFilePriorityManager::validationAndOtherScriptFile()['validateFocusLost'];
558 + $this->addScriptInLoadedScriptsList($fileArr);
439 559 $fileArr = ScriptFilePriorityManager::frontendScriptFile()['observeElm'];
440 560 $this->addScriptInLoadedScriptsList($fileArr);
441 561 }
442 562 if (Helpers::property_exists_nested($formContent, 'additional->enabled->validateFocusLost', true)) {
443 - $fileArr = ScriptFilePriorityManager::validationScriptFile()['validateFocusLost'];
563 + $fileArr = ScriptFilePriorityManager::validationAndOtherScriptFile()['validateFocusLost'];
444 564 $this->addScriptInLoadedScriptsList($fileArr);
445 565 }
446 566 }
447 567 }
448 568
449 - private function jsHiddenFieldScript() {
569 + private function jsHiddenFieldScript()
570 + {
450 571 $appConfig = get_option('bitform_app_config');
451 - if (Helpers::property_exists_nested($appConfig, 'cache_plugin', true)) {
572 + $cacheTokenEnabled = true;
573 + if (is_object($appConfig) && property_exists($appConfig, 'cache_plugin')) {
574 + $cacheTokenEnabled = (bool) $appConfig->cache_plugin;
575 + } elseif (is_array($appConfig) && array_key_exists('cache_plugin', $appConfig)) {
576 + $cacheTokenEnabled = (bool) $appConfig['cache_plugin'];
577 + }
578 + if ($cacheTokenEnabled) {
452 579 $fileArr = ScriptFilePriorityManager::frontendScriptFile()['hidden-token-field'];
453 580 $this->addScriptInLoadedScriptsList($fileArr);
454 581 return;
455 582 }
@@ -454,13 +581,15 @@
454 581 return;
455 582 }
456 583 }
457 584
458 - public function dd($data, $exit = false) {
459 - echo '+++++++++++++';
460 - echo '<pre>';
461 - var_dump($data);
462 - echo '</pre>';
463 - echo '+++++++++++++';
464 - $exit ? exit : '';
585 + private function jsRecaptchaV3Scripts()
586 + {
587 + foreach ($this->_formContents as $content) {
588 + if (isset($content->additional->enabled->recaptchav3) && $content->additional->enabled->recaptchav3) {
589 + $this->addScriptInLoadedScriptsList(['priority' => 302, 'filename' => 'scriptLoader.min.js']);
590 + $this->addScriptInLoadedScriptsList(['priority' => 303, 'filename' => 'initRecaptchaV3Fld.min.js']);
591 + return;
592 + }
593 + }
465 594 }
466 595 }