PluginProbe
Bit Form – Contact Form, Payment Forms, Multi Step Forms, Calculator & Custom Form Builder / V3.3.2
Bit Form – Contact Form, Payment Forms, Multi Step Forms, Calculator & Custom Form Builder vV3.3.2
V3.3.2 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 All 139 releases
← All changes | includes/Admin/Form/FrontEndScriptGenerator.php +353 -223 2.0 → V3.3.2 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);
@@ -218,28 +222,41 @@
218 222 $fileArr = $this->_validationJsFilesNeeded['requiredFldValidation'];
219 223 $this->addScriptInLoadedScriptsList($fileArr);
220 224 }
221 225 // for regex
222 - if (Helpers::property_exists_nested($fldData, 'valid->regexr')) {
223 - $patternFile = $this->_validationJsFilesNeeded['generateBackslashPattern'];
224 - $rgx = $this->_validationJsFilesNeeded['regexPatternValidation'];
225 - $this->addScriptInLoadedScriptsList($patternFile);
226 - $this->addScriptInLoadedScriptsList($rgx);
226 + // The RegEx scripts are registered by Pro; without it there is nothing to load.
227 + if (Helpers::property_exists_nested($fldData, 'valid->regexr')
228 + && !empty($this->_validationJsFilesNeeded['generateBackslashPattern'])
229 + && !empty($this->_validationJsFilesNeeded['regexPatternValidation'])) {
230 + $this->addScriptInLoadedScriptsList($this->_validationJsFilesNeeded['generateBackslashPattern']);
231 + $this->addScriptInLoadedScriptsList($this->_validationJsFilesNeeded['regexPatternValidation']);
227 232 }
228 233
229 234 $validationScriptFileMapping = ScriptFilePriorityManager::validationScriptFileMapping($fldData->typ);
230 235 if ($validationScriptFileMapping) {
231 236 foreach ($validationScriptFileMapping as $key => $value) {
232 - foreach ($value['paths'] as $path) {
237 + $paths = $value['paths'] ?? [];
238 + $hasMatchingPath = false;
239 + foreach ($paths as $path) {
233 240 if (Helpers::property_exists_nested($fldData, $path)) {
234 - $file = $this->_validationJsFilesNeeded[$key];
235 - $this->addScriptInLoadedScriptsList($file);
241 + $hasMatchingPath = true;
242 + break; // one match is enough
236 243 }
237 244 }
238 - if (isset($value['dependencies'])) {
239 - foreach ($value['dependencies'] as $dependency) {
240 - $dependencyFile = $this->_validationJsFilesNeeded[$dependency];
241 - $this->addScriptInLoadedScriptsList($dependencyFile);
245 +
246 + if (!$hasMatchingPath) {
247 + continue;
248 + }
249 +
250 + // Add the main script once
251 + if (!empty($this->_validationJsFilesNeeded[$key])) {
252 + $this->addScriptInLoadedScriptsList($this->_validationJsFilesNeeded[$key]);
253 + }
254 +
255 + // Add dependencies once each
256 + foreach (($value['dependencies'] ?? []) as $dep) {
257 + if (!empty($this->_validationJsFilesNeeded[$dep])) {
258 + $this->addScriptInLoadedScriptsList($this->_validationJsFilesNeeded[$dep]);
242 259 }
243 260 }
244 261 }
245 262 }
@@ -246,9 +263,11 @@
246 263 }
247 264 }
248 265 }
249 266
250 - private function jsFieldsNeededFile() {
267 + private function jsFieldsNeededFile()
268 + {
269 + $filePondPluginList = apply_filters('bitform_filepond_plugins_list', []);
251 270 foreach ($this->_fields as $typ => $flds) {
252 271 if (!array_key_exists($typ, $this->_jsFilesNeeded)) {
253 272 continue;
254 273 }
@@ -258,46 +277,44 @@
258 277 if ('advanced-file-up' === $typ) {
259 278 $configs = $fld['field']->config;
260 279 foreach ($configs as $configKey => $config) {
261 280 if ($configs->$configKey) {
262 - $filepondPlugin = ScriptFilePriorityManager::filePondPlugins($configKey);
263 - if ($filepondPlugin) {
264 - $this->addScriptInLoadedScriptsList($filepondPlugin);
281 + if (array_key_exists($configKey, $filePondPluginList)) {
282 + $this->addScriptInLoadedScriptsList($filePondPluginList[$configKey]);
265 283 }
266 284 }
267 285 }
268 286 }
269 287 foreach ($fldScriptArr as $scriptFile) {
270 - if (!isset($scriptFile['scriptTyp'])) {
271 - $this->addScriptInLoadedScriptsList($scriptFile);
288 + if (isset($scriptFile['paths']) && is_array($scriptFile['paths'])) {
289 + $pathMatched = false;
290 + foreach ($scriptFile['paths'] as $path) {
291 + if (Helpers::property_exists_nested($fld['field'], $path)) {
292 + $pathMatched = true;
293 + break;
294 + }
295 + }
296 + if (!$pathMatched) {
297 + continue;
298 + }
299 + } elseif (isset($scriptFile['path']) && !Helpers::property_exists_nested($fld['field'], $scriptFile['path'])) {
272 300 continue;
273 301 }
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 - }
302 + $this->addScriptInLoadedScriptsList($scriptFile);
289 303 }
290 304 }
291 305 }
292 306 }
293 307
294 - private static function getCustomJsCodes($contentIds = []) {
295 - $script = 'let bfContentId = "", bfVars= "";';
308 + private static function getCustomJsCodes($contentIds = [])
309 + {
310 + $script = 'let bfContentId = "", bfSlNo = "1", bfVars= "";';
296 311 foreach ($contentIds as $contentId) {
297 - $jsCode = self::getCustomCodes(explode('_', $contentId)[1])['JavaScript'];
312 + $contentIdArr = explode('_', $contentId);
313 + $jsCode = self::getCustomCodes($contentIdArr[1])['JavaScript'];
314 + $bfSlNo = array_key_exists(3, $contentIdArr) ? $contentIdArr[3] : '1';
298 315 if (!empty($jsCode)) {
299 - $script .= " bfContentId = '{$contentId}'; bfVars = window.bf_globals.{$contentId}.smartTags; {$jsCode}";
316 + $script .= " if(bfSelect('#{$contentId}')){ bfContentId = '{$contentId}'; bfSlNo = '{$bfSlNo}'; bfVars = window.bf_globals.{$contentId}.smartTags; {$jsCode}}";
300 317 }
301 318 }
302 319
303 320 return $script;
@@ -302,9 +319,10 @@
302 319
303 320 return $script;
304 321 }
305 322
306 - private static function getFileFromAssetsJs($fileName) {
323 + private static function getFileFromAssetsJs($fileName)
324 + {
307 325 $sourceJsFile = BITFORMS_PLUGIN_DIR_PATH . 'assets' . DIRECTORY_SEPARATOR . $fileName;
308 326 if (file_exists($sourceJsFile)) {
309 327 return file_get_contents($sourceJsFile);
310 328 }
@@ -311,9 +329,10 @@
311 329 Log::debug_log('file not found: ' . $fileName);
312 330 return false;
313 331 }
314 332
315 - public static function saveCssFile($formId, $atomicCssText) {
333 + public static function saveCssFile($formId, $atomicCssText)
334 + {
316 335 $path = 'form-styles';
317 336 $fileName = "bitform-$formId.css";
318 337 if (!isset($formId) || '' === $formId) {
319 338 return new WP_Error('missing_form_id', __('Error Occurred, Please Reload', 'bit-form'));
@@ -320,13 +339,15 @@
320 339 }
321 340 return Helpers::saveFile($path, $fileName, $atomicCssText, 'w');
322 341 }
323 342
324 - public static function customCodeFile($formId, $customCodes) {
343 + public static function customCodeFile($formId, $customCodes)
344 + {
325 345 // for js file
326 346 $path = 'form-scripts';
327 347 $fileName = "bitform-custom-$formId.js";
328 - self::customCodeFileSaveOrDelete($customCodes->JavaScript, $path, $fileName);
348 + $filteredJs = Helpers::removeJsSingleLineComments($customCodes->JavaScript);
349 + self::customCodeFileSaveOrDelete($filteredJs, $path, $fileName);
329 350
330 351 // for css file
331 352 $path = 'form-styles';
332 353 $fileName = "bitform-custom-$formId.css";
@@ -334,22 +355,22 @@
334 355
335 356 return true;
336 357 }
337 358
338 - private static function customCodeFileSaveOrDelete($script, $path, $fileName) {
359 + public static function customCodeFileSaveOrDelete($script, $path, $fileName)
360 + {
339 361 if ($script) {
340 362 Helpers::saveFile($path, $fileName, $script, 'w');
341 363 } else {
342 364 $uploadPath = "$path/$fileName";
343 365 $uploadFilePath = Helpers::generatePathDirOrFile($uploadPath);
344 - if (file_exists($uploadFilePath)) {
345 - unlink($uploadFilePath);
346 - }
366 + FileHandler::deleteIsFileExists($uploadFilePath);
347 367 }
348 368 return true;
349 369 }
350 370
351 - public static function getCustomCodes($formId) {
371 + public static function getCustomCodes($formId)
372 + {
352 373 $customCodes = ['JavaScript' => '', 'CSS' => ''];
353 374 $customJsPath = Helpers::generatePathDirOrFile("form-scripts/bitform-custom-$formId.js");
354 375 $customCodes['JavaScript'] = Helpers::fileRead($customJsPath);
355 376
@@ -358,70 +379,165 @@
358 379
359 380 return $customCodes;
360 381 }
361 382
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;
383 + private $fldContainersByType = [
384 + 'address' => '.__$fk__-parent-fld-wrp',
385 + 'select' => '.__$fk__-dpd-fld-wrp',
386 + 'country' => '.__$fk__-country-fld-wrp',
387 + 'currency' => '.__$fk__-currency-fld-wrp',
388 + 'phone-number' => '.__$fk__-phone-fld-wrp',
389 + 'file-up' => '.__$fk__-file-up-wrpr',
390 + 'advanced-file-up' => '#filepond-__$fk__-container',
391 + 'paypal' => '.__$fk__-paypal-wrp',
392 + 'razorpay' => '.__$fk__-razorpay-wrp',
393 + 'recaptcha' => '.__$fk__-recaptcha-wrp',
394 + 'stripe' => '.__$fk__-stripe-fld',
395 + 'mollie' => '.__$fk__-mollie-wrp',
396 + 'repeater' => '.__$fk__-rpt-fld-wrp',
397 + 'signature' => '.__$fk__-inp-fld-wrp',
398 + 'rating' => '.__$fk__-inp-fld-wrp',
399 + 'email-otp' => '.__$fk__-inp-fld-wrp',
400 + 'hcaptcha' => '.__$fk__-h-captcha-wrp',
401 + 'advanced-datetime' => '.__$fk__-advanced-datetime',
402 + ];
403 +
404 + private function generateFieldConfigsJs()
405 + {
406 + $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'];
407 + $allFieldTypes = array_keys($this->_fields);
408 + $customFldsInForms = array_intersect($allFieldTypes, $customFlds);
409 + $formContents = $this->_formContents;
410 + $recaptchaV3Enabled = false;
411 + foreach ($formContents as $content) {
412 + if (isset($content->additional->enabled->recaptchav3) && $content->additional->enabled->recaptchav3) {
413 + $recaptchaV3Enabled = true;
414 + break;
415 + }
369 416 }
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']];
417 + if (empty($customFldsInForms) && !$recaptchaV3Enabled) {
418 + return '';
419 + }
420 +
421 + $containers = [];
422 + foreach ($customFldsInForms as $customFldTyp) {
423 + if (isset($this->fldContainersByType[$customFldTyp])) {
424 + $containers[$customFldTyp] = $this->fldContainersByType[$customFldTyp];
375 425 }
376 - if (empty($value) && isset($fldPath['path'])) {
377 - $value = Helpers::getDataFromNestedPath($field, $fldPath['path']);
426 + }
427 +
428 + $customFldConfigPaths = [];
429 +
430 + $allConfs = ScriptFilePriorityManager::getAllFldConfs();
431 + foreach ($customFldsInForms as $customFldTyp) {
432 + if (isset($allConfs[$customFldTyp])) {
433 + $customFldConfigPaths[$customFldTyp] = $allConfs[$customFldTyp];
434 + } else {
435 + $customFldConfigPaths[$customFldTyp] = (object) [];
378 436 }
379 - if (empty($value) && isset($fldPath['val'])) {
380 - $value = $fldPath['val'];
381 - }
382 - $fldConf = Helpers::setDataToNestedPath($fldConf, $confPath, $value);
383 437 }
384 - return $fldConf;
385 - }
386 438
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 - }
439 + $customFldConfigPaths = wp_json_encode($customFldConfigPaths);
440 + $containers = wp_json_encode($containers);
441 +
442 + $script = ' const customFldConfigPaths = ' . $customFldConfigPaths . ';
443 + const fldContainers = ' . $containers . ';
444 +
445 + function initAllCustomFlds (formContentId = null) {
446 + const allContendIds = formContentId ? [formContentId] : Object.keys(bf_globals);
447 + allContendIds.forEach((contentId) => {
448 + const contentData = bf_globals[contentId];
449 + if(!contentData?.inits) contentData.inits = {};
450 + const flds = bf_globals[contentId]?.fields || {};
451 + const fldKeys = Object.keys(flds).reverse();
452 + fldKeys.forEach((fldKey) => {
453 + const fldData = flds[fldKey];
454 + const fldType = fldData.typ;
455 + if(fldType === \'paypal\') {
456 + initPaypalFld(contentId, fldKey, fldData, fldType);
457 + } else if(fldType === \'razorpay\') {
458 + initRazorpayFld(contentId, fldKey, fldType);
459 + } else if(fldType === \'recaptcha\') {
460 + initRecaptchaFld(contentId, fldKey, fldType);
461 + } else if(fldType === \'turnstile\') {
462 + initTurnstileFld(contentId, fldKey);
463 + } else if(fldType === \'hcaptcha\') {
464 + initHCaptchaFld(contentId, fldKey, fldType);
465 + } else if(fldType === \'stripe\' || fldType === \'mollie\') {
466 + initStripeFld(contentId, fldKey, fldType);
467 + } else if (customFldConfigPaths[fldType]) {
468 + contentData.inits[fldKey] = getFldInstance(contentId, fldKey, fldType);
398 469 }
470 + });
471 + if(contentData.gRecaptchaVersion === \'v3\' && contentData.gRecaptchaSiteKey){
472 + initRecaptchaV3Fld(contentId, contentData);
399 473 }
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 - }
474 + });
475 + };
476 + function getFldInstance(contentId, fldKey, fldTyp, nestedSelector = \'\') {
477 + const fldClass = this[\'bit_\'+fldTyp.replace(/-/g, \'_\')+\'_field\'];
478 + const selector = \'#form-\'+contentId+\' \'+nestedSelector+fldContainers[fldTyp].replace("__$fk__", fldKey);
479 + if(!fldClass || !bfSelect(selector)) return;
480 + return new fldClass(selector, getFldConf(contentId, fldKey, fldTyp));
481 + };
482 + function getFldConf(contentId, fieldKey, fldTyp) {
483 + const fldData = bf_globals[contentId].fields[fieldKey];
484 + const fldConfPaths = Object.entries(customFldConfigPaths[fldTyp]);
485 + let fldConf = {};
486 + const { formId } = bf_globals[contentId];
487 + if (!("config" in customFldConfigPaths[fldTyp]) && "config" in fldData) fldConf = fldData.config;
488 + const varData = { contentId, fieldKey, formId };
489 + fldConfPaths.forEach(([ confPath, fldPath ]) => {
490 + let value = "";
491 + if (fldPath.var) value = varData[fldPath.var];
492 + if (!value && fldPath.path) {
493 + if(Array.isArray(fldPath.path)) {
494 + fldPath.path.forEach((path) => {
495 + if(!value) value = getDataFromNestedPath(fldData, path);
496 + });
497 + } else {
498 + value = getDataFromNestedPath(fldData, fldPath.path);
408 499 }
409 - } else {
410 - // for all field types
411 - $this->addScriptInLoadedScriptsList($scriptFile);
412 500 }
413 - }
414 -
415 - if (isset($funcArr['dependencies'])) {
416 - foreach ($funcArr['dependencies'] as $dependency) {
417 - $this->addScriptInLoadedScriptsList($dependency);
501 + if (!value && "val" in fldPath) {
502 + value = fldPath.val;
503 + if(typeof value === \'string\') {
504 + Object.entries(varData).forEach(([key, val]) => {
505 + value = value.replace("__$"+key+"__", val);
506 + });
507 + }
418 508 }
509 + fldConf = setDataToNestedPath(fldConf, confPath, value);
510 + });
511 + return fldConf;
512 + };
513 + function getDataFromNestedPath(data, key) {
514 + const keys = key.split("->");
515 + const lastKey = keys.pop();
516 + let current = {...data};
517 + for (const k of keys) {
518 + if (!(k in current)) return null;
519 + current = current[k];
419 520 }
521 + return current[lastKey] || null;
420 522 }
523 + function setDataToNestedPath(data, key, value) {
524 + const keys = key.split("->");
525 + const lastKey = keys.pop();
526 + let current = {...data};
527 + keys.forEach((k) => {
528 + if (!current[k]) current[k] = {};
529 + current = current[k];
530 + });
531 + current[lastKey] = value;
532 + return current;
533 + }';
534 +
535 + return $script;
421 536 }
422 537
423 - private function addScriptInLoadedScriptsList($fileArr) {
538 + private function addScriptInLoadedScriptsList($fileArr)
539 + {
424 540 $fileName = $fileArr['filename'];
425 541 if (!in_array($fileName, $this->_scriptAddedFlags)) {
426 542 $this->_loadedScriptsList[] = $fileArr;
427 543 $this->_scriptAddedFlags[] = $fileName;
@@ -428,28 +544,40 @@
428 544 }
429 545 return true;
430 546 }
431 547
432 - private function jsFrontendScript() {
548 + private function jsFrontendScript()
549 + {
433 550 foreach ($this->_formContents as $formContent) {
434 551 if (Helpers::property_exists_nested($formContent, 'workFlowExist->oninput', true)) {
435 - $fileArr = ScriptFilePriorityManager::validationScriptFile()['conditionalLogic'];
552 + $fileArr = ScriptFilePriorityManager::validationAndOtherScriptFile()['conditionalLogic'];
436 553 $this->addScriptInLoadedScriptsList($fileArr);
437 - $fileArr = ScriptFilePriorityManager::validationScriptFile()['validateFocusLost'];
554 + $fileArr = ScriptFilePriorityManager::validationAndOtherScriptFile()['resetPlaceholders'];
438 555 $this->addScriptInLoadedScriptsList($fileArr);
556 + $fileArr = ScriptFilePriorityManager::validationAndOtherScriptFile()['bfResetDefaultValue'];
557 + $this->addScriptInLoadedScriptsList($fileArr);
558 + $fileArr = ScriptFilePriorityManager::validationAndOtherScriptFile()['validateFocusLost'];
559 + $this->addScriptInLoadedScriptsList($fileArr);
439 560 $fileArr = ScriptFilePriorityManager::frontendScriptFile()['observeElm'];
440 561 $this->addScriptInLoadedScriptsList($fileArr);
441 562 }
442 563 if (Helpers::property_exists_nested($formContent, 'additional->enabled->validateFocusLost', true)) {
443 - $fileArr = ScriptFilePriorityManager::validationScriptFile()['validateFocusLost'];
564 + $fileArr = ScriptFilePriorityManager::validationAndOtherScriptFile()['validateFocusLost'];
444 565 $this->addScriptInLoadedScriptsList($fileArr);
445 566 }
446 567 }
447 568 }
448 569
449 - private function jsHiddenFieldScript() {
570 + private function jsHiddenFieldScript()
571 + {
450 572 $appConfig = get_option('bitform_app_config');
451 - if (Helpers::property_exists_nested($appConfig, 'cache_plugin', true)) {
573 + $cacheTokenEnabled = true;
574 + if (is_object($appConfig) && property_exists($appConfig, 'cache_plugin')) {
575 + $cacheTokenEnabled = (bool) $appConfig->cache_plugin;
576 + } elseif (is_array($appConfig) && array_key_exists('cache_plugin', $appConfig)) {
577 + $cacheTokenEnabled = (bool) $appConfig['cache_plugin'];
578 + }
579 + if ($cacheTokenEnabled) {
452 580 $fileArr = ScriptFilePriorityManager::frontendScriptFile()['hidden-token-field'];
453 581 $this->addScriptInLoadedScriptsList($fileArr);
454 582 return;
455 583 }
@@ -454,13 +582,15 @@
454 582 return;
455 583 }
456 584 }
457 585
458 - public function dd($data, $exit = false) {
459 - echo '+++++++++++++';
460 - echo '<pre>';
461 - var_dump($data);
462 - echo '</pre>';
463 - echo '+++++++++++++';
464 - $exit ? exit : '';
586 + private function jsRecaptchaV3Scripts()
587 + {
588 + foreach ($this->_formContents as $content) {
589 + if (isset($content->additional->enabled->recaptchav3) && $content->additional->enabled->recaptchav3) {
590 + $this->addScriptInLoadedScriptsList(['priority' => 302, 'filename' => 'scriptLoader.min.js']);
591 + $this->addScriptInLoadedScriptsList(['priority' => 303, 'filename' => 'initRecaptchaV3Fld.min.js']);
592 + return;
593 + }
594 + }
465 595 }
466 596 }