PluginProbe
WS Form LITE – Drag & Drop Contact Form Builder / trunk
WS Form LITE – Drag & Drop Contact Form Builder vtrunk
1.12.10 1.12.8 1.12.6 1.12.5 1.12.3 1.12.0 1.11.19 1.11.17 1.11.16 1.11.15 1.11.14 1.11.11 1.11.8 1.11.5 1.11.4 1.9.248 1.9.249 1.9.26 1.9.43 1.9.46 1.9.47 1.9.5 1.9.6 1.9.64 1.9.7 All 363 releases
ws-form / shared / js / ws-form.js

ws-form.js in WS Form LITE – Drag & Drop Contact Form Builder trunk, at shared/js/ws-form.js

8,109 lines 251.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 (function($) {
2
3 'use strict';
4
5 $.WS_Form = function(atts) {
6
7 // Global "current" instance (admin UI); public pages with multiple forms should use ws_this in closures, not $.WS_Form.this
8 $.WS_Form.this = this;
9
10 // Admin?
11 this.is_admin = this.set_is_admin();
12
13 // User roles
14 this.user_roles = ws_form_settings.user_roles;
15
16 // Form interface built?
17 this.form_interface = false;
18
19 // Group data cache
20 this.group_data_cache = [];
21 this.group_index = 0;
22
23 // Section data cache
24 this.section_data_cache = [];
25
26 // Field data cache
27 this.field_data_cache = [];
28
29 // Action data cache
30 this.action_data_cache = [];
31
32 // Validation message cache
33 this.validation_message_cache = [];
34
35 // Invalid feedback cache
36 this.invalid_feedback_cache = [];
37
38 // Object cache
39 this.object_cache = {};
40 this.object_cache.condition = [];
41 this.object_cache.then = [];
42 this.object_cache.else = [];
43
44 // Actions
45 this.action = false;
46
47 // New object data (for reverting field data back to an older state)
48 this.object_data_scratch = false;
49 this.sidebar_meta_checksum = false;
50
51 // Form history
52 this.form_history = [];
53 this.history_index = 0;
54
55 // Framework
56 this.framework_id = false;
57 this.framework = false;
58 this.framework_fields = false;
59
60 // Column resizing
61 this.column_resize_obj = false;
62 this.column_size_value = 0;
63 this.column_size_value_old = 0;
64 this.offset_value = 0;
65 this.offset_value_old = 0;
66
67 // Draggable
68 this.dragged_field = null;
69 this.dragged_field_in_section = null;
70 this.dragged_section = null;
71 this.dragged_section_in_group = null;
72 this.dragging = false;
73
74 // Sortable
75 this.next_sibling_id_old = null;
76 this.section_id_old = null;
77 this.group_id_old = null;
78 this.section_repeatable_dragged = false;
79
80 // Repeatable sections
81 this.section_repeatable_indexes = {};
82
83 // Checksum
84 this.checksum = false;
85 this.checksum_setTimeout = false;
86 this.published_checksum = '';
87
88 // reCAPTCHA
89 this.recaptchas = [];
90 this.recaptchas_v2_default = [];
91 this.recaptchas_v2_invisible = [];
92 this.recaptchas_v3_default = [];
93 this.recaptchas_conditions = [];
94 this.timeout_recaptcha = 30000;
95
96 // hCaptcha
97 this.hcaptchas = [];
98 this.hcaptchas_default = [];
99 this.hcaptchas_invisible = [];
100 this.hcaptchas_conditions = [];
101 this.timeout_hcaptcha = 30000;
102
103 // Turnstile
104 this.turnstiles = [];
105 this.turnstiles_default = [];
106 this.turnstiles_conditions = [];
107 this.timeout_turnstile = 30000;
108
109 // CaptchaFox
110 this.captchafoxes = [];
111 this.captchafoxes_default = [];
112 this.captchafoxes_invisible = [];
113 this.captchafoxes_conditions = [];
114 this.timeout_captchafox = 30000;
115
116 // Events reset array (Used on form rebuild)
117 this.form_events_reset = [];
118
119 // Key down events
120 this.keydown = [];
121
122 // Page component load timeout
123 this.timeout_interval = 100;
124
125 // API
126 this.api_call_queue = [];
127 this.api_call_queue_running = false;
128
129 // Number to word
130 this.number_to_word = ['nought','one','two','three','four','five','six','seven','eight','nine','ten','eleven','twelve','thirteen','fourteen','fifteen','sixteen','seventeen','eighteen','nineteen','twenty'];
131
132 // Old object label
133 this.label_old = '';
134
135 // Submit data
136 this.submit = false;
137 this.action_js = [];
138 this.form_draft = false;
139
140 // Populate
141 this.submit_auto_populate = false;
142
143 // Prefixes
144 this.form_id_prefix = 'wsf-';
145 this.field_name_prefix = ws_form_settings.field_prefix;
146
147 // Meta key options
148 this.meta_key_options_cache = [];
149
150 // Options cache
151 this.options_action_objects = [];
152 this.options_action_cache = [];
153
154 // Sidebars
155 this.sidebar_conditions = [];
156 this.sidebar_expanded_obj = false;
157 this.sidebar_lock_count = 0;
158
159 // Devices
160 this.touch_device = ('ontouchend' in document);
161
162 // Hash
163 this.hash = '';
164
165 // Token
166 this.token = '';
167 this.token_validated = false;
168
169 // Form locking
170 this.form_post_locked = false;
171 this.form_post_lock_start = 0;
172 this.form_post_lock_duration_max = 1000;
173
174 // Form loader
175 this.form_loader_showing = false;
176 this.form_loader_show_start = 0;
177 this.form_loader_show_duration_max = this.form_post_lock_duration_max;
178 this.form_loader_timeout_id = false;
179
180 // Required fields bypass
181 this.field_required_bypass = [];
182
183 // Real time form validation
184 this.form_valid = false;
185 this.form_valid_old = null;
186 this.form_validation_real_time_hooks = [];
187
188
189 // Custom action URL
190 this.form_action_custom = false;
191 this.form_ajax = true;
192
193 // Object focus
194 this.object_focus = false;
195
196 // Field type click drag check
197 this.field_type_click_drag_check = false;
198 this.section_id_click_drag_check = false;
199
200 // Password strength meter
201 this.password_strength_status = 0;
202
203 // Cascade cache
204 this.cascade_cache = [];
205
206 // Search AJAX cache
207 this.select_ajax_cache = [];
208
209 // Preview
210 this.preview_window = undefined;
211
212 // Media picker
213 this.file_frame = false;
214 this.file_frame_input_obj = false;
215
216 // API call handle
217 this.api_call_handle = [];
218
219 // Is this form in a visual builder?
220 this.visual_builder = false;
221
222 // Form bypass enabled
223 this.form_bypass_enabled = false;
224
225 // Focus fields with validation errors after submit
226 this.action_js_process_validation_focus = true;
227
228 // Actions require Google Analytics
229 this.action_ga = false;
230
231
232 // Form stat add lock
233 this.form_stat_add_lock = false;
234
235 // Form geo cache
236 this.form_geo_cache = false;
237 this.form_geo_cache_request = false;
238 this.form_geo_stack = [];
239
240 // Client-side parse variable resolver registry
241 //
242 // Add-ons can register handlers to resolve their own parameterized parse
243 // variables (e.g. #my_var("arg")) live in the browser, e.g.:
244 //
245 // $.WS_Form.parse_variable_handlers = $.WS_Form.parse_variable_handlers || {};
246 // $.WS_Form.parse_variable_handlers['my_var'] = function(attributes, context) {
247 //
248 // // attributes: Parsed argument array, e.g. ['pa_color']
249 // // context.ws_form: WS Form instance (also bound to 'this')
250 // // context.field_to: Field object the value is being resolved for
251 // // context.field_part: Field part
252 // // context.section_id: Section ID
253 // // context.section_repeatable_index: Repeatable section row index
254 // // context.parse_variable: Variable name, e.g. 'my_var'
255 // // context.parse_variable_full: Full match, e.g. '#my_var("pa_color")'
256 // // context.current_value: Value resolved so far (string)
257 //
258 // return '<string value>';
259 // };
260 //
261 // The variable must still be declared in PHP via the
262 // wsf_config_parse_variables filter (with 'attributes' defined). Set
263 // 'single_parse' => true in its config to make it dynamic so dependent
264 // conditional logic, calculations, default values and merge tags update live.
265 $.WS_Form.parse_variable_handlers = $.WS_Form.parse_variable_handlers || {};
266 }
267
268 // Render
269 $.WS_Form.prototype.render = function(atts) {
270
271 var ws_this = this;
272
273 // Check attributes
274 if(typeof atts === 'undefined') { this.error('error_attributes'); }
275 if(typeof atts.obj === 'undefined') { this.error('error_attributes_obj'); }
276 if(typeof atts.form_id === 'undefined') { this.error('error_attributes_form_id'); }
277
278 // Form canvas (Could be something other than form if element defined)
279 this.form_canvas_obj = atts.obj instanceof $ ? atts.obj : $(atts.obj);
280
281 // Form object ID
282 this.form_obj_id = this.form_canvas_obj.attr('id');
283
284 // Form ID
285 this.form_id = parseInt(atts.form_id, 10);
286 if(this.form_id === 0) { return; }
287
288 // Form instance (admin edit UI may omit data-instance-id; parseInt then is NaN — do not treat like 0)
289 this.form_instance_id = parseInt(this.form_canvas_obj.attr('data-instance-id'), 10);
290 if(this.form_instance_id === 0) { return; }
291
292 // Form ID prefix
293 this.form_id_prefix = this.is_admin ? 'wsf-' : 'wsf-' + this.form_instance_id + '-';
294 this.form_id_prefix_function = this.is_admin ? 'wsf_' : 'wsf_' + this.form_instance_id + '_';
295
296 // Form object (Form tag)
297 this.form_obj = this.form_canvas_obj.closest('form');
298 if(this.form_obj.length) {
299
300 this.form_obj.attr('novalidate', '');
301
302 } else {
303
304 this.form_obj = this.form_canvas_obj;
305 }
306
307 // Move attributes and classes to outer form tag
308 if(this.form_obj[0] != this.form_canvas_obj[0]) {
309
310 // Add wsf-form to form
311 if(!this.form_obj.hasClass('wsf-form')) { this.form_obj.addClass('wsf-form'); }
312
313 // Remove wsf-form from canvas
314 if(this.form_canvas_obj.hasClass('wsf-form')) { this.form_canvas_obj.removeClass('wsf-form'); }
315
316 // Move data-wsf-style-id from canvas to form
317 if(typeof this.form_canvas_obj.attr('data-wsf-style-id') !== 'undefined') {
318
319 // Add data-wsf-style-id to form
320 this.form_obj.attr('data-wsf-style-id', this.form_canvas_obj.attr('data-wsf-style-id'));
321
322 // Remove data-wsf-style-id from canvas
323 this.form_canvas_obj.removeAttr('data-wsf-style-id');
324 }
325 }
326
327 // Empty form canvas
328 this.form_canvas_obj.html('');
329
330 // Get configuration
331 this.get_configuration(function() {
332
333 // Get form
334 ws_this.get_form(function() {
335
336 // Initialize
337 ws_this.init();
338 });
339 });
340 }
341
342 // Configuration objects
343 $.WS_Form.configured = false;
344 $.WS_Form.css_rendered = false;
345 $.WS_Form.styler;
346 $.WS_Form.styler_rendered = false;
347 $.WS_Form.settings_plugin;
348 $.WS_Form.settings_form = null;
349 $.WS_Form.frameworks;
350 $.WS_Form.parse_variables;
351 $.WS_Form.parse_variable_help;
352 $.WS_Form.parse_variable_repairable;
353 $.WS_Form.actions;
354 $.WS_Form.field_types;
355 $.WS_Form.field_type_cache = [];
356 $.WS_Form.file_types;
357 $.WS_Form.meta_keys;
358 $.WS_Form.meta_keys_required_setting = [];
359 $.WS_Form.breakpoints;
360 $.WS_Form.data_sources;
361 $.WS_Form.templates_section;
362
363 /**
364 * Normalize a BCP47 language tag to POSIX-style locale (underscore separator).
365 *
366 * @param {string} language_tag Tag or locale string.
367 * @returns {string} Normalized locale or empty string.
368 */
369 $.WS_Form.language_tag_to_locale = function(language_tag) {
370
371 if(typeof language_tag !== 'string') { return ''; }
372 language_tag = language_tag.trim();
373 if(language_tag === '') { return ''; }
374 return language_tag.replace(/-/g, '_');
375 };
376
377 /**
378 * Convert POSIX-style locale to BCP47 language tag (hyphen; language lower, region upper).
379 *
380 * @param {string} locale Locale string.
381 * @returns {string} BCP47-style tag or empty string.
382 */
383 $.WS_Form.locale_to_language_tag = function(locale) {
384
385 if(typeof locale !== 'string') { return ''; }
386 var posix = $.WS_Form.language_tag_to_locale(locale);
387 if(posix === '') { return ''; }
388 var parts = posix.split('_');
389 var lang = parts[0] ? parts[0].toLowerCase() : '';
390 if(lang === '') { return ''; }
391 if((typeof parts[1] === 'undefined') || (parts[1] === '')) { return lang; }
392 return lang + '-' + parts[1].toUpperCase();
393 };
394
395 /**
396 * Primary language subtag from a locale or tag.
397 *
398 * @param {string} locale Locale or tag.
399 * @returns {string} Lowercase language code or empty string.
400 */
401 $.WS_Form.get_language_code = function(locale) {
402
403 var posix = $.WS_Form.language_tag_to_locale(locale);
404 if(posix === '') { return ''; }
405 var parts = posix.split('_');
406 return (parts[0] ? parts[0].toLowerCase() : '');
407 };
408
409 /**
410 * Region subtag from a locale or tag.
411 *
412 * @param {string} locale Locale or tag.
413 * @returns {string} Uppercase region code or empty string.
414 */
415 $.WS_Form.get_country_code = function(locale) {
416
417 var posix = $.WS_Form.language_tag_to_locale(locale);
418 if(posix === '') { return ''; }
419 var parts = posix.split('_');
420 if((typeof parts[1] === 'undefined') || (parts[1] === '')) { return ''; }
421 return parts[1].toUpperCase();
422 };
423
424 // Get configuration
425 $.WS_Form.prototype.get_configuration = function(success_callback, force, bypass_loader) {
426
427 if(typeof force === 'undefined') { force = false; }
428 if(typeof bypass_loader === 'undefined') { bypass_loader = false; }
429
430 // Clear caches
431 this.options_action_cache = [];
432
433 // Loader on
434 if(!bypass_loader) { this.loader_on(); }
435
436 if(!$.WS_Form.configured || force) {
437
438 if(typeof wsf_form_json_config === 'undefined') {
439
440 // Get configuration via AJAX
441 var ws_this = this;
442 this.api_call('config', 'GET', false, function(response) {
443
444 // Set configuration
445 ws_this.set_configuration(response.data);
446
447 window.wsf_form_json_config = response.data;
448
449 if(typeof success_callback === 'function') { success_callback(); }
450
451 }, false, bypass_loader);
452
453 } else {
454
455 // Get configuration from dom
456 this.set_configuration(wsf_form_json_config);
457
458 if(typeof success_callback === 'function') { success_callback(); }
459 }
460
461 } else {
462
463 // Get form without configuration (Configuration already loaded)
464 if(typeof success_callback === 'function') { success_callback(); }
465 }
466 }
467
468 // Set configuration
469 $.WS_Form.prototype.set_configuration = function(config) {
470
471 // Store configuration
472 $.WS_Form.settings_plugin = config.settings_plugin;
473 $.WS_Form.settings_form = config.settings_form;
474 $.WS_Form.frameworks = config.frameworks;
475 $.WS_Form.field_types = config.field_types;
476 $.WS_Form.file_types = config.file_types;
477 $.WS_Form.meta_keys = config.meta_keys;
478 $.WS_Form.parse_variables = config.parse_variables;
479 $.WS_Form.parse_variables_repairable = config.parse_variables_repairable;
480 $.WS_Form.parse_variable_help = config.parse_variable_help;
481 $.WS_Form.actions = config.actions;
482 $.WS_Form.data_sources = config.data_sources;
483 $.WS_Form.templates_section = config.templates_section;
484
485 // Build field type cache
486 this.field_type_cache_build();
487
488 // Set that WS Form is configured
489 $.WS_Form.configured = true;
490 }
491
492 // Get configuration
493 $.WS_Form.prototype.get_form = function(success_callback) {
494
495 // Show error if form ID is 0 or unspecified
496 if(this.form_id == 0) {
497
498 this.error('error_form_id');
499
500 this.loader_off();
501
502 return;
503 }
504
505 // Set form data-id attribute
506 $('#' + this.form_obj_id).attr('data-id', this.form_id);
507
508 if(
509 (typeof window.wsf_form_json === 'undefined') ||
510 (typeof window.wsf_form_json[this.form_id] === 'undefined')
511 ) {
512
513 // Get form from API
514 var ws_this = this;
515 this.api_call('form/' + this.form_id + '/full/?wsf_fp=true', 'GET', false, function(response) {
516
517 // Store form data
518 ws_this.form = response.form;
519
520 // Build cache
521 if(typeof window.wsf_form_json === 'undefined') { window.wsf_form_json = []; }
522 window.wsf_form_json[ws_this.form_id] = response.form;
523
524 // Build data cache
525 ws_this.data_cache_build();
526
527 // Success callback
528 if(typeof success_callback === 'function') { success_callback(); }
529
530 // Loader off
531 ws_this.loader_off();
532 });
533
534 } else {
535
536 // Get form from dom
537 this.form = window.wsf_form_json[this.form_id];
538
539 // Build data cache
540 this.data_cache_build();
541
542 // Success callback
543 if(typeof success_callback === 'function') { success_callback(); }
544
545 // Loader off
546 this.loader_off();
547 }
548 }
549
550 // Build form
551 $.WS_Form.prototype.form_build = function() {
552
553 // Timer - Start
554 this.timer_start = new Date();
555
556 // Clear form HTML
557 this.form_canvas_obj.children().not('.wsf-loader').remove();
558
559 // Append form HTML
560 this.form_canvas_obj.append(this.get_form_html(this.form));
561
562 // Add container class
563 if(!this.is_admin) {
564
565 var class_form_wrapper = this.get_object_meta_value(this.form, 'class_form_wrapper', '');
566 if(class_form_wrapper != '') {
567
568 var form_class = this.form_canvas_obj.attr('class');
569 form_class += ' ' + class_form_wrapper.trim();
570 this.form_canvas_obj.attr('class', form_class);
571 }
572 }
573
574 // Render form
575 this.form_render();
576
577 // Timer - Duration
578 this.timer_duration = new Date() - this.timer_start;
579
580 }
581
582 // Get form style ID
583 $.WS_Form.prototype.get_form_style_id = function() {
584
585 var framework_id = this.is_admin ? 'ws-form' : $.WS_Form.settings_plugin.framework;
586 return $.WS_Form.frameworks.types[framework_id];
587 }
588
589 // Get current framework
590 $.WS_Form.prototype.get_framework = function() {
591
592 var framework_id = this.is_admin ? 'ws-form' : $.WS_Form.settings_plugin.framework;
593 return $.WS_Form.frameworks.types[framework_id];
594 }
595
596 // Get form HTML
597 $.WS_Form.prototype.get_form_html = function(form) {
598
599 // Form
600 if(typeof form === 'undefined') { return ''; }
601 if(typeof form.groups === 'undefined') { return ''; }
602
603 // Get current framework
604 var framework = this.get_framework();
605 var framework_form = framework.form[this.is_admin ? 'admin' : 'public'];
606
607 // Label
608 var form_label = this.esc_html(form.label);
609 var label_render = !this.is_admin && (this.get_object_meta_value(form, 'label_render', 'on') == 'on') ? true : false;
610
611 if(label_render) {
612
613 var label_mask_form = !this.is_admin ? this.get_object_meta_value(this.form, 'label_mask_form', '') : '';
614 var mask = (label_mask_form != '') ? label_mask_form : (typeof framework_form.mask_label !== 'undefined') ? framework_form.mask_label : '';
615 var mask_values = {'label': form_label};
616 var label_html_parsed = this.mask_parse(mask, mask_values);
617
618 } else {
619
620 var label_html_parsed = '';
621 }
622
623 // Tabs
624 var form_html = this.get_tabs_html(form);
625
626 // Groups
627 form_html += this.get_groups_html(form.groups);
628
629 // Parse wrapper form
630 var mask = framework_form.mask_single;
631 var mask_values = {
632
633 'form': form_html,
634 'id': this.form_id_prefix + 'tabs',
635 'label': label_html_parsed
636 };
637 var form_html_parsed = this.comment_html('Form: ' + form_label) + this.mask_parse(mask, mask_values) + this.comment_html('Form: ' + form_label, true);
638
639 return form_html_parsed;
640 }
641
642 // Get tabs HTML
643 $.WS_Form.prototype.get_tabs_html = function(form) {
644
645 // Get groups
646 if(typeof form.groups === 'undefined') { return ''; }
647 var groups = form.groups;
648
649 // Get group count
650 var group_count = Object.keys(groups).length;
651
652 // No tabs if there is only 1 group and we are not in admin
653 if((group_count == 1) && !this.is_admin) { return ''; }
654
655 var tabs_html = '';
656
657 // Get current framework
658 var framework = this.get_framework();
659 var framework_tabs = framework.tabs[this.is_admin ? 'admin' : 'public'];
660
661 // Get tab index cookie if settings require it
662 var index = (this.get_object_meta_value(this.form, 'cookie_tab_index')) ? this.cookie_get('tab_index', 0) : 0;
663
664 // Groups
665 if((group_count > 1) || this.is_admin) {
666
667 // Build group, section and field data caches
668 for(var group_index in groups) {
669
670 if(!groups.hasOwnProperty(group_index)) { continue; }
671
672 var group = groups[group_index];
673
674 tabs_html += this.get_tab_html(group, group_index, (index == group_index));
675 }
676
677 // Attributes
678 var attributes = '';
679
680 // Hidden tabs?
681 if(!this.is_admin) {
682
683 var tabs_hidden = this.get_object_meta_value(form, 'tabs_hide', false);
684 if(tabs_hidden) {
685
686 attributes += ' style="display: none;"';
687 }
688 }
689
690 // Classes
691 var class_array = ['wsf-group-tabs'];
692
693 // Class wrapper
694 if(!this.is_admin) {
695
696 var class_tabs_wrapper = this.get_object_meta_value(form, 'class_tabs_wrapper', false);
697 if(class_tabs_wrapper != '') { class_array.push(class_tabs_wrapper.trim()); }
698 }
699
700 // Parse wrapper tabs
701 var mask = framework_tabs.mask_wrapper;
702 var mask_values = {
703
704 'attributes': attributes,
705 'class': class_array.join(' '),
706 'tabs': tabs_html,
707 'id': this.form_id_prefix + 'tabs'
708 };
709 var tabs_html_parsed = this.comment_html(this.language('comment_group_tabs')) + this.mask_parse(mask, mask_values) + this.comment_html(this.language('comment_group_tabs'), true);
710
711 return tabs_html_parsed;
712
713 } else {
714
715 return '';
716 }
717 }
718
719 // Get tab HTML
720 $.WS_Form.prototype.get_tab_html = function(group, index, is_active) {
721
722 if(typeof index === 'undefined') { index = 0; }
723 if(typeof is_active === 'undefined') { is_active = false; }
724
725 // Get current framework for tabs
726 var framework = this.get_framework();
727 var framework_tabs = framework.tabs[this.is_admin ? 'admin' : 'public'];
728
729 // Get group label
730 var group_label = this.esc_html(group.label);
731
732 // Attributes
733 var attributes = '';
734
735 // Hidden
736 var hidden = !this.is_admin && (this.get_object_meta_value(group, 'hidden', '') == 'on') ? true : false;
737 if(hidden) {
738
739 attributes = ' style="display: none;" data-wsf-group-hidden';
740 }
741
742 // Parse and return wrapper tab
743 var mask = framework_tabs.mask_single;
744 var mask_values = {
745
746 'attributes': attributes,
747 'data_id': group.id,
748 'href': '#' + this.form_id_prefix + 'group-' + group.id,
749 'label': group_label
750 };
751
752 // Active tab
753 if(is_active && (typeof framework_tabs.active !== 'undefined')) {
754
755 mask_values.active = framework_tabs.active;
756
757 } else {
758
759 mask_values.active = '';
760 }
761
762 return this.mask_parse(mask, mask_values);
763 }
764
765 // Get groups HTML
766 $.WS_Form.prototype.get_groups_html = function(groups) {
767
768 var group_html = '';
769
770 // Check groups
771 if(typeof groups === 'undefined') { return ''; }
772
773 // Get group count
774 var group_count = Object.keys(groups).length;
775
776 // Get current framework
777 var framework = this.get_framework();
778 var framework_groups = framework.groups[this.is_admin ? 'admin' : 'public'];
779
780 // Get tab index cookie if settings require it
781 var group_index_current = (this.get_object_meta_value(this.form, 'cookie_tab_index')) ? this.cookie_get('tab_index', 0) : 0;
782
783 // Build tabs content
784 var groups_html = '';
785 var use_mask = this.is_admin || (group_count > 1);
786
787 var group_index_zero = 0;
788 for(var group_index in groups) {
789
790 if(!groups.hasOwnProperty(group_index)) { continue; }
791
792 var group = groups[group_index];
793
794 // Render group
795 groups_html += this.get_group_html(group, (group_index == group_index_current), use_mask, group_index_zero++);
796 }
797
798 // Add container class
799 var class_array = ['wsf-groups'];
800
801 // Parse wrapper form
802 var mask = (use_mask ? framework_groups.mask_wrapper : '#groups');
803 var mask_values = {
804
805 'class': class_array.join(' '),
806 'column_count' : $.WS_Form.settings_plugin.framework_column_count,
807 'groups': groups_html,
808 'id': this.form_id_prefix + 'tabs'
809 };
810 var groups_html_parsed = (use_mask ? this.comment_html(this.language('comment_groups')) : '') + this.mask_parse(mask, mask_values) + (use_mask ? this.comment_html(this.language('comment_groups'), true) : '');
811
812 return groups_html_parsed;
813 }
814
815 // Get group HTML
816 $.WS_Form.prototype.get_group_html = function(group, is_active, use_mask, group_index) {
817
818 if(typeof is_active === 'undefined') { is_active = false; }
819 if(typeof use_mask === 'undefined') { use_mask = true; }
820
821 // Get current framework
822 var framework = this.get_framework();
823 var framework_groups = framework.groups[this.is_admin ? 'admin' : 'public'];
824
825 var group_id = this.esc_html(group.id);
826
827 // Label
828 var group_label = this.esc_html(group.label);
829 var label_render = !this.is_admin && (this.get_object_meta_value(group, 'label_render', 'on') == 'on') ? true : false;
830
831 if(label_render) {
832
833 var label_mask_group = !this.is_admin ? this.get_object_meta_value(this.form, 'label_mask_group', '') : '';
834 var mask = (label_mask_group != '') ? label_mask_group : (typeof framework_groups.mask_label !== 'undefined') ? framework_groups.mask_label : '';
835 var mask_values = {'label': group_label};
836 var label_html_parsed = this.mask_parse(mask, mask_values);
837
838 } else {
839
840 var label_html_parsed = '';
841 }
842
843 // HTML
844 var sections_html = this.get_sections_html(group);
845
846 // Classes
847 var class_array = [];
848
849 // Class - Base
850 if(typeof framework_groups.class !== 'undefined') {
851
852 class_array.push(framework_groups.class);
853 }
854
855 // Class - Wrapper
856 if(!this.is_admin) {
857
858 // Wrapper set at form level
859 var class_group_wrapper = this.get_object_meta_value(this.form, 'class_group_wrapper', '');
860 if(class_group_wrapper != '') { class_array.push(class_group_wrapper.trim()); }
861
862 // Wrapper set at group level
863 var class_group_wrapper = this.get_object_meta_value(group, 'class_group_wrapper', '');
864 if(class_group_wrapper != '') { class_array.push(class_group_wrapper.trim()); }
865 }
866
867 // Class - Active
868 if(is_active && (typeof framework_groups.class_active !== 'undefined')) {
869
870 class_array.push(framework_groups.class_active);
871 }
872
873 // Attributes
874 var attributes_array = [];
875
876 // Class - Hidden
877 if(!this.is_admin) {
878
879 var hidden = !this.is_admin && (this.get_object_meta_value(group, 'hidden', '') == 'on') ? true : false;
880 if(hidden) { attributes_array.push('data-wsf-group-hidden'); }
881 }
882
883 // Parse wrapper tabs content
884 var mask = (use_mask ? framework_groups.mask_single : '#group');
885 var mask_values = {
886
887 'attributes': ((attributes_array.length > 0) ? ' ' : '') + attributes_array.join(' '),
888 'class': class_array.join(' '),
889 'column_count' : $.WS_Form.settings_plugin.framework_column_count,
890 'data_id': group.id,
891 'data_group_index': group_index,
892 'group': sections_html,
893 'id': this.form_id_prefix + 'group-' + group_id,
894 'label': label_html_parsed
895 };
896
897 var group_html_parsed = (use_mask ? this.comment_html(this.language('comment_group') + ': ' + group_label) : '') + this.mask_parse(mask, mask_values) + (use_mask ? this.comment_html(this.language('comment_group') + ': ' + group_label, true) : '');
898
899 return group_html_parsed;
900 }
901
902 // Get sections html
903 $.WS_Form.prototype.get_sections_html = function(group) {
904
905 var sections_html = '';
906
907 // Get current framework
908 var framework = this.get_framework();
909 var framework_sections = framework.sections[this.is_admin ? 'admin' : 'public'];
910
911 var group_id = group.id;
912 var sections = group.sections
913
914 // Check to see if section_repeatable data is available
915 var section_repeatable = {};
916 if(typeof this.submit === 'object') {
917
918 if(typeof this.submit.section_repeatable !== 'undefined') {
919
920 section_repeatable = this.submit.section_repeatable;
921 }
922
923 } else {
924
925 // Check to see if auto populate data exists
926 if(this.submit_auto_populate !== false) {
927
928 if(typeof this.submit_auto_populate.section_repeatable !== 'undefined') {
929
930 section_repeatable = this.submit_auto_populate.section_repeatable;
931 }
932 }
933 }
934
935 // Sections
936 if(typeof sections === 'undefined') { return ''; }
937
938 for(var section_index in sections) {
939
940 if(!sections.hasOwnProperty(section_index)) { continue; }
941
942 var section = sections[section_index];
943
944 // Check for section repeaters
945 var section_id_string = 'section_' + section.id;
946 var section_repeatable_array = (
947
948 (section_repeatable !== false) &&
949 (typeof section_repeatable[section_id_string] !== 'undefined') &&
950 (typeof section_repeatable[section_id_string].index !== 'undefined')
951
952 ) ? section_repeatable[section_id_string].index : [false];
953
954 // Loop through section_repeatable_array
955 for(var section_repeatable_array_index in section_repeatable_array) {
956
957 if(!section_repeatable_array.hasOwnProperty(section_repeatable_array_index)) { continue; }
958
959 // Get repeatable index
960 var section_repeatable_index = section_repeatable_array[section_repeatable_array_index];
961
962 // Render section
963 sections_html += this.get_section_html(section, section_repeatable_index);
964 }
965 }
966
967 // Parse wrapper section
968 var mask = framework_sections.mask_wrapper;
969 var mask_values = {
970
971 'class': 'wsf-sections',
972 'column_count' : $.WS_Form.settings_plugin.framework_column_count,
973 'data_id': group.id, 'sections': sections_html,
974 'id': this.form_id_prefix + 'sections-' + group.id
975 };
976 var sections_html_parsed = this.comment_html(this.language('comment_sections')) + this.mask_parse(mask, mask_values) + this.comment_html(this.language('comment_sections'), true);
977
978 return sections_html_parsed;
979 }
980
981 // Get section html
982 $.WS_Form.prototype.get_section_html = function(section, section_repeatable_index) {
983
984 if(typeof section_repeatable_index === 'undefined') { section_repeatable_index = false; }
985
986 // Is section repeatable?
987 var section_repeatable = false;
988 if(!this.is_admin) {
989
990 var section_repeatable = (this.get_object_meta_value(section, 'section_repeatable', '') == 'on') ? true : false;
991 if(section_repeatable) {
992
993 if(section_repeatable_index === false) {
994
995 // Find next available section_repeatable_index
996 section_repeatable_index = 0;
997
998 do {
999
1000 section_repeatable_index++;
1001
1002 } while($('#' + this.form_id_prefix + 'section-' + section.id + '-repeat-' + section_repeatable_index).length);
1003 }
1004 }
1005 }
1006
1007 // Attributes
1008 var attributes = '';
1009
1010 // Get current framework
1011 var framework = this.get_framework();
1012 var framework_sections = framework.sections[this.is_admin ? 'admin' : 'public'];
1013
1014 // Get column class array
1015 var class_array = this.column_class_array(section);
1016
1017 // Is section repeatable?
1018 if(section_repeatable && !this.is_admin) {
1019
1020 attributes = this.attribute_modify(attributes, 'data-repeatable', '', true);
1021 attributes = this.attribute_modify(attributes, 'data-repeatable-index', section_repeatable_index, true);
1022 }
1023
1024 // Add any base classes
1025 if(typeof framework_sections.class_single !== 'undefined') { class_array = class_array.concat(framework_sections.class_single); }
1026
1027 // Public
1028 if(!this.is_admin) {
1029
1030 // Wrapper set at form level
1031 var class_section_wrapper = this.get_object_meta_value(this.form, 'class_section_wrapper', '');
1032 if(class_section_wrapper != '') { class_array.push(class_section_wrapper.trim()); }
1033
1034 // Wrapper set at section level
1035 var class_section_wrapper = this.get_object_meta_value(section, 'class_section_wrapper', '');
1036 if(class_section_wrapper != '') { class_array.push(class_section_wrapper.trim()); }
1037
1038 // Vertical alignment
1039 var class_single_vertical_align = this.get_object_meta_value(section, 'class_single_vertical_align', '');
1040 if(class_single_vertical_align) {
1041
1042 var class_single_vertical_align_config = this.get_field_value_fallback(false, false, 'class_single_vertical_align');
1043
1044 if(typeof class_single_vertical_align_config[class_single_vertical_align] !== 'undefined') {
1045
1046 class_array.push(class_single_vertical_align_config[class_single_vertical_align]);
1047 }
1048 }
1049
1050 // Inline validation
1051 var validate_inline = this.get_object_meta_value(section, 'validate_inline', '');
1052 if(validate_inline != '') {
1053
1054 var class_validated_array = (typeof this.framework.fields.public.class_form_validated !== 'undefined') ? this.framework.fields.public.class_form_validated : [];
1055
1056 switch(validate_inline) {
1057
1058 case 'on' :
1059
1060 // Get validated class
1061 if(this.is_iterable(class_validated_array)) {
1062
1063 class_array.push(...class_validated_array);
1064
1065 } else {
1066
1067 class_array.push(class_validated_array);
1068 }
1069
1070 break;
1071
1072 case 'change_blur' :
1073
1074 attributes = this.attribute_modify(attributes, 'data-wsf-section-validated-class', class_validated_array.join(' '), true);
1075
1076 break;
1077 }
1078 }
1079
1080 // Conversational
1081 var form_conversational = this.get_object_meta_value(this.form, 'conversational', false);
1082 var conversational_full_height_section = this.get_object_meta_value(section, 'conversational_full_height_section', false);
1083 if(form_conversational && conversational_full_height_section) { class_array.push('wsf-form-conversational-section-full-height'); }
1084 }
1085
1086 // Legend
1087 var section_label = this.esc_html(section.label)
1088 var label_render = this.is_admin || ((this.get_object_meta_value(section, 'label_render', 'on') == 'on') ? true : false);
1089
1090 if(label_render) {
1091
1092 var label_mask_section = !this.is_admin ? this.get_object_meta_value(this.form, 'label_mask_section', '') : '';
1093 var mask = (label_mask_section != '') ? label_mask_section : ((typeof framework_sections.mask_label !== 'undefined') ? framework_sections.mask_label : '');
1094 var mask_values = {'label': section_label};
1095 var label_html_parsed = this.mask_parse(mask, mask_values);
1096
1097 } else {
1098
1099 var label_html_parsed = '';
1100 }
1101
1102 if(!this.is_admin) {
1103
1104 // Disabled
1105 var disabled_section = this.get_object_meta_value(section, 'disabled_section', '');
1106 if(disabled_section == 'on') {
1107
1108 attributes = this.attribute_modify(attributes, 'disabled', '', true);
1109 attributes = this.attribute_modify(attributes, 'aria-disabled', 'true', true);
1110 }
1111
1112 // Hidden
1113 var hidden_section = this.get_object_meta_value(section, 'hidden_section', '');
1114 if(hidden_section == 'on') {
1115
1116 attributes = this.attribute_modify(attributes, 'style', 'display: none;', true);
1117 attributes = this.attribute_modify(attributes, 'aria-live', 'polite', true);
1118 attributes = this.attribute_modify(attributes, 'aria-hidden', 'true', true);
1119 }
1120
1121 // ARIA label
1122 var aria_label = this.strip_html(this.get_object_meta_value(section, 'aria_label', ''));
1123 if(aria_label == '') { aria_label = this.strip_html(section.label); }
1124 if(aria_label == '') { aria_label = this.strip_html(this.language('comment_section')); }
1125 attributes = this.attribute_modify(attributes, 'aria-label', aria_label, true);
1126
1127 // Custom attributes
1128 attributes = this.custom_attributes(attributes, section, 'section', section_repeatable_index);
1129 }
1130
1131 // HTML
1132 var section_single_html = this.get_fields_html(section, section_repeatable_index);
1133
1134 // Parse wrapper section
1135 var mask = framework_sections.mask_single;
1136 var mask_values = {
1137
1138 'attributes': (attributes ? ' ' : '') + attributes,
1139 'class': class_array.join(' '),
1140 'column_count' : $.WS_Form.settings_plugin.framework_column_count,
1141 'data_id': section.id,
1142 'id': this.form_id_prefix + 'section-' + section.id + (section_repeatable_index ? ('-repeat-' + section_repeatable_index) : ''),
1143 'label': label_html_parsed,
1144 'section': section_single_html,
1145 'section_id': this.is_admin ? ('<span class="wsf-section-id">' + this.language('id') + ': ' + section.id + '</span>') : ''
1146 };
1147
1148 var section_html_parsed = this.comment_html(this.language('comment_section') + ': ' + section_label) + this.mask_parse(mask, mask_values) + this.comment_html(this.language('comment_section') + ': ' + section_label, true);
1149
1150 return section_html_parsed;
1151 }
1152
1153 // Process custom attributes
1154 $.WS_Form.prototype.custom_attributes = function(attributes, object, object_type, section_repeatable_index, obj) {
1155
1156 // Get custom attributes
1157 var custom_attributes = this.custom_attributes_get(object, object_type, section_repeatable_index);
1158
1159 // Process each custom attribute
1160 for(const attribute_name in custom_attributes) {
1161
1162 // Get custom attribute value
1163 var attribute_value = custom_attributes[attribute_name];
1164
1165 // Build attribute (Only add value if one is specified)
1166 attributes = this.attribute_modify(attributes, attribute_name, attribute_value, true);
1167 }
1168
1169 return attributes;
1170 }
1171
1172 // Process custom attributes
1173 $.WS_Form.prototype.custom_attributes_get = function(object, object_type, section_repeatable_index) {
1174
1175 var custom_attributes = {};
1176
1177 var mask_field_attributes_custom = this.get_object_meta_value(object, 'custom_attributes', false);
1178
1179 if(
1180 (mask_field_attributes_custom !== false) &&
1181 (typeof mask_field_attributes_custom === 'object') &&
1182 (mask_field_attributes_custom.length > 0)
1183 ) {
1184
1185 // If object is not a field then set object to false ready for parse_variables_process
1186 if(object_type != 'field') { object = false; }
1187
1188 // Run through each custom attribute
1189 for(var mask_field_attributes_custom_index in mask_field_attributes_custom) {
1190
1191 if(!mask_field_attributes_custom.hasOwnProperty(mask_field_attributes_custom_index)) { continue; }
1192
1193 // Get custom attribute name/value pair
1194 var mask_field_attribute_custom = mask_field_attributes_custom[mask_field_attributes_custom_index];
1195
1196 // Check attribute name exists
1197 if(mask_field_attribute_custom.custom_attribute_name == '') { continue; }
1198
1199 // Parse custom attribute value
1200 mask_field_attribute_custom.custom_attribute_value = this.parse_variables_process(mask_field_attribute_custom.custom_attribute_value, section_repeatable_index, false, object).output;
1201
1202 // Build attribute (Only add value if one is specified)
1203 custom_attributes[mask_field_attribute_custom.custom_attribute_name] = mask_field_attribute_custom.custom_attribute_value;
1204 }
1205 }
1206
1207 return custom_attributes;
1208 }
1209
1210 // Get fields html
1211 $.WS_Form.prototype.get_fields_html = function(section, section_repeatable_index) {
1212
1213 // Is section repeatable?
1214 var section_repeatable = (this.get_object_meta_value(section, 'section_repeatable', '') == 'on') ? true : false;
1215 if(typeof section_repeatable_index === 'undefined') { section_repeatable_index = (section_repeatable ? 0 : false); }
1216
1217 var fields_html = '';
1218
1219 // Get current framework for tabs
1220 var framework = this.get_framework();
1221 var framework_fields = framework.fields[this.is_admin ? 'admin' : 'public'];
1222
1223 var section_id = section.id;
1224 var fields = section.fields;
1225
1226 // Legend
1227 var section_label = this.esc_html(section.label)
1228 var label_render = !this.is_admin && (this.get_object_meta_value(section, 'label_render', 'on') == 'on') ? true : false;
1229
1230 if(label_render && (section_label != '')) {
1231
1232 var label_mask_section = !this.is_admin ? this.get_object_meta_value(this.form, 'label_mask_section', '') : '';
1233 var mask = (label_mask_section != '') ? label_mask_section : ((typeof framework_fields.mask_wrapper_label !== 'undefined') ? framework_fields.mask_wrapper_label : '');
1234 var mask_values = {'label': section_label};
1235 var label_html_parsed = this.mask_parse(mask, mask_values);
1236
1237 } else {
1238
1239 var label_html_parsed = this.is_admin ? '' : '<legend class="wsf-hidden-element">' + (section_label != '' ? section_label : this.esc_html(this.language('comment_section'))) + '</legend>';
1240 }
1241
1242 // Fields
1243 if(typeof fields === 'undefined') { return ''; }
1244
1245 for(var field_index in fields) {
1246
1247 if(!fields.hasOwnProperty(field_index)) { continue; }
1248
1249 var field = fields[field_index];
1250
1251 // Render field
1252 fields_html += this.get_field_html(field, section_repeatable_index);
1253 }
1254
1255 // Parse wrapper section
1256 var mask = framework_fields.mask_wrapper;
1257 var mask_values = {
1258
1259 'column_count' : $.WS_Form.settings_plugin.framework_column_count,
1260 'data_id': section.id,
1261 'fields': fields_html,
1262 'id': this.form_id_prefix + 'fields-' + section.id,
1263 'label': label_html_parsed
1264 };
1265 var fields_html_parsed = this.comment_html(this.language('comment_fields')) + this.mask_parse(mask, mask_values) + this.comment_html(this.language('comment_fields'), true);
1266
1267 return fields_html_parsed;
1268 }
1269 // Process values for auto population
1270 $.WS_Form.prototype.value_populate_process = function(value, field) {
1271
1272 if(value === null) { return ''; }
1273
1274 // Parse by field type
1275 switch(field.type) {
1276
1277 case 'datetime' :
1278
1279 return ((typeof this.get_date_by_type === 'function') ? this.get_date_by_type(value, field) : '');
1280
1281 case 'select' :
1282 case 'checkbox' :
1283 case 'radio' :
1284 case 'price_select' :
1285 case 'price_checkbox' :
1286 case 'price_radio' :
1287
1288 // indexOf does a === check so convert object values to strings
1289 return Array.isArray(value) ? value.map(function(value) { return value ? value.toString() : ''; }) : value;
1290
1291 case 'quantity' :
1292
1293 // Get decimal separator
1294 var decimal_separator = $.WS_Form.settings_plugin.price_decimal_separator;
1295
1296 // Check if decimal separator is not a period and value contains decimal separator
1297 if(
1298 (decimal_separator !== '.') &&
1299 (value.indexOf(decimal_separator) !== -1)
1300 ) {
1301
1302 // Process using currency settings
1303 value = this.get_number(value);
1304 }
1305
1306 return value;
1307
1308 default :
1309
1310 return this.esc_html(value);
1311 }
1312 }
1313
1314 // Get field html
1315 $.WS_Form.prototype.get_field_html = function(field, section_repeatable_index) {
1316
1317 if(typeof section_repeatable_index === 'undefined') { section_repeatable_index = false; }
1318
1319 // Attributes
1320 var attributes = [];
1321
1322 // Repeatable
1323 if(section_repeatable_index !== false) { attributes.push('data-repeatable-index="' + this.esc_attr(section_repeatable_index) + '"'); }
1324
1325 // Get current framework for tabs
1326 var framework = this.get_framework();
1327 var framework_fields = framework.fields[this.is_admin ? 'admin' : 'public'];
1328
1329 // Hidden
1330 if(!this.is_admin) {
1331
1332 var hidden = (this.get_object_meta_value(field, 'hidden', '') == 'on');
1333 if(hidden) { attributes.push('style="display: none;" aria-live="polite" aria-hidden="true"'); }
1334 }
1335
1336 // Get column class array
1337 var class_array = this.column_class_array(field);
1338
1339 // Get sub type
1340 var sub_type = this.get_object_meta_value(field, 'sub_type', false);
1341 if(sub_type == '') { sub_type = false; }
1342
1343 // Add any base classes
1344 var class_array_config = this.get_field_value_fallback(field.type, false, 'class_single', false, framework_fields, sub_type);
1345 if(class_array_config !== false) { class_array = class_array.concat(class_array_config); }
1346
1347 // Add container class
1348 if(!this.is_admin) {
1349
1350 // Wrapper set at form level
1351 var class_field_wrapper = this.get_object_meta_value(this.form, 'class_field_wrapper', '');
1352 if(class_field_wrapper != '') { class_array.push(class_field_wrapper.trim()); }
1353
1354 // Wrapper set at field level
1355 var class_field_wrapper = this.get_object_meta_value(field, 'class_field_wrapper', '');
1356 if(class_field_wrapper != '') { class_array.push(class_field_wrapper.trim()); }
1357
1358 // Vertical alignment
1359 var class_single_vertical_align = this.get_object_meta_value(field, 'class_single_vertical_align', '');
1360 if(class_single_vertical_align) {
1361
1362 var class_single_vertical_align_config = this.get_field_value_fallback(field.type, false, 'class_single_vertical_align', false, framework_fields, sub_type);
1363
1364 if(typeof class_single_vertical_align_config[class_single_vertical_align] !== 'undefined') {
1365
1366 class_array.push(class_single_vertical_align_config[class_single_vertical_align]);
1367 }
1368 }
1369
1370 // Inline validation
1371 var validate_inline = this.get_object_meta_value(field, 'validate_inline', '');
1372 if(validate_inline != '') {
1373
1374 var class_validated_array = (typeof this.framework.fields.public.class_form_validated !== 'undefined') ? this.framework.fields.public.class_form_validated : [];
1375
1376 switch(validate_inline) {
1377
1378 case 'on' :
1379
1380 // Get validated class
1381 if(this.is_iterable(class_validated_array)) {
1382
1383 class_array.push(...class_validated_array);
1384
1385 } else {
1386
1387 class_array.push(class_validated_array);
1388 }
1389
1390 break;
1391
1392 case 'change_blur' :
1393
1394 attributes.push('data-wsf-field-validated-class="' + class_validated_array.join(' ') + '"');
1395
1396 break;
1397 }
1398 }
1399 }
1400
1401 // Check to see if this field is available in the submit data
1402 var repeatable_suffix = ((section_repeatable_index !== false) ? '_' + section_repeatable_index : '');
1403 var submit_meta_key = ws_form_settings.field_prefix + field.id + repeatable_suffix;
1404 if(typeof this.submit === 'object') {
1405
1406 if(
1407 (typeof this.submit.meta !== 'undefined') &&
1408 (typeof this.submit.meta[submit_meta_key] !== 'undefined') &&
1409 (typeof this.submit.meta[submit_meta_key].value !== 'undefined') &&
1410 (this.submit.meta[submit_meta_key].value !== null)
1411 ) {
1412
1413 var value = this.submit.meta[submit_meta_key].value;
1414
1415 value = this.value_populate_process(value, field);
1416 }
1417
1418 } else {
1419
1420 // Check to see if auto populate data exists
1421 if(this.submit_auto_populate !== false) {
1422
1423 if(
1424 (typeof this.submit_auto_populate.data !== 'undefined') &&
1425 (typeof this.submit_auto_populate.data[submit_meta_key] !== 'undefined') &&
1426 (this.submit_auto_populate.data[submit_meta_key] !== null)
1427 ) {
1428
1429 var value = this.submit_auto_populate.data[submit_meta_key];
1430
1431 value = this.value_populate_process(value, field);
1432 }
1433 }
1434 }
1435
1436 // Get field HTML (Admin returns blank, Public returns rendered field)
1437 var field_html = (this.is_admin ? '' : this.get_field_html_single(field, value, false, section_repeatable_index));
1438
1439 // Field label (For comments only)
1440 var field_label = this.esc_html(field.label)
1441
1442 // Get field type config
1443 if(typeof $.WS_Form.field_type_cache[field.type] === 'undefined') { return ''; }
1444 var field_config = $.WS_Form.field_type_cache[field.type];
1445
1446 // Check field is licensed
1447 if((typeof field_config.pro_required !== 'undefined') && field_config.pro_required) {
1448
1449 return '';
1450 }
1451
1452 // Check to see if mask_single should be ignored
1453 var mask_wrappers_drop = this.is_admin ? false : ((typeof field_config.mask_wrappers_drop !== 'undefined') ? field_config.mask_wrappers_drop : false);
1454
1455
1456 // If wrappers should be dropped, disregard them
1457 if(mask_wrappers_drop) {
1458
1459 var mask_single = '#field';
1460
1461 } else {
1462
1463 var mask_single = this.get_field_value_fallback(field.type, false, 'mask_single', false, framework_fields, sub_type);
1464 }
1465
1466 // Select / Checkbox - Min / Max checked
1467 switch(field.type) {
1468
1469 case 'checkbox' :
1470 case 'price_checkbox' :
1471
1472 var checkbox_min = this.get_object_meta_value(field, 'checkbox_min', false);
1473 var checkbox_max = this.get_object_meta_value(field, 'checkbox_max', false);
1474 var select_all = this.get_object_meta_value(field, 'select_all', false);
1475
1476 if((checkbox_min === false) && (checkbox_max === false)) { break; }
1477
1478 // Checks
1479 if(checkbox_min !== false) {
1480
1481 checkbox_min = parseInt(checkbox_min, 10);
1482 if(checkbox_min > 0) {
1483
1484 attributes.push('data-checkbox-min="' + this.esc_attr(checkbox_min) + '"');
1485 }
1486 }
1487
1488 if(
1489 (checkbox_max !== false) &&
1490 !select_all
1491 ) {
1492
1493 checkbox_max = parseInt(checkbox_max, 10);
1494 if(checkbox_max >= 0) {
1495
1496 attributes.push('data-checkbox-max="' + this.esc_attr(checkbox_max) + '"');
1497 }
1498 }
1499
1500 break;
1501
1502 case 'select' :
1503 case 'price_select' :
1504
1505 var select_min = this.get_object_meta_value(field, 'select_min', false);
1506 var select_max = this.get_object_meta_value(field, 'select_max', false);
1507
1508 if((select_min === false) && (select_max === false)) { break; }
1509
1510 // Checks
1511 if(select_min !== false) {
1512
1513 select_min = parseInt(select_min, 10);
1514 if(select_min > 0) {
1515
1516 attributes.push('data-select-min="' + this.esc_attr(select_min) + '"');
1517 }
1518 }
1519
1520 if(select_max !== false) {
1521
1522 select_max = parseInt(select_max, 10);
1523 if(select_max >= 0) {
1524
1525 attributes.push('data-select-max="' + this.esc_attr(select_max) + '"');
1526 }
1527 }
1528
1529 break;
1530 }
1531
1532 // Build parse values
1533 var mask_values = {
1534
1535 'attributes': ((attributes.length > 0) ? ' ' : '') + attributes.join(' '),
1536 'class': class_array.join(' '),
1537 'data_id': field.id,
1538 'field': field_html,
1539 'id': this.get_part_id(field.id, section_repeatable_index, 'field-wrapper'),
1540 'type': field.type
1541 };
1542
1543 // Parse wrapper field
1544 var field_html_parsed = this.comment_html(this.language('comment_field') + ': ' + field_label) + this.mask_parse(mask_single, mask_values) + this.comment_html(this.language('comment_field') + ': ' + field_label, true);
1545
1546 return field_html_parsed;
1547 }
1548
1549 // Build field type cache
1550 $.WS_Form.prototype.field_type_cache_build = function() {
1551
1552 // If public, set field_type_cache to field_types, already in correct format
1553 if(!this.is_admin) {
1554
1555 $.WS_Form.field_type_cache = $.WS_Form.field_types;
1556 return true;
1557 }
1558
1559 // If already built, do not build
1560 if(Object.keys($.WS_Form.field_type_cache.length) > 0) { return true; }
1561
1562 // Add field types
1563 for (var group_key in $.WS_Form.field_types) {
1564
1565 var group = $.WS_Form.field_types[group_key];
1566 var types = group.types;
1567
1568 // Add field types
1569 for (var type in types) {
1570
1571 // Store field type to cache
1572 $.WS_Form.field_type_cache[type] = types[type];
1573 }
1574 }
1575 }
1576
1577 // HTML encode string
1578 $.WS_Form.prototype.esc_html = function(value, encode_new_lines) {
1579
1580 if(typeof value !== 'string') { return value; }
1581
1582 if(typeof encode_new_lines === 'undefined') { encode_new_lines = false; }
1583
1584 // Process by value type
1585 switch(typeof value) {
1586
1587 // String
1588 case 'string' :
1589
1590 value = this.esc_html_do(value, encode_new_lines);
1591 break;
1592
1593 // Arrays / objects
1594 case 'object' :
1595
1596 for(var value_index in value) {
1597
1598 if(!value.hasOwnProperty(value_index)) { continue; }
1599
1600 if(typeof value[value_index] === 'string') {
1601
1602 value[value_index] = this.esc_html_do(value[value_index], encode_new_lines);
1603 }
1604 }
1605 }
1606
1607 return value;
1608 }
1609
1610 $.WS_Form.prototype.esc_html_do = function(value, encode_new_lines) {
1611
1612 if(typeof encode_new_lines === 'undefined') { encode_new_lines = false; }
1613
1614 var return_html = this.replace_all(value, '&', '&#38;');
1615 return_html = this.replace_all(return_html, '<', '&lt;');
1616 return_html = this.replace_all(return_html, '>', '&gt;');
1617 return_html = this.replace_all(return_html, '"', '&quot;');
1618
1619 if(encode_new_lines) {
1620
1621 return_html = this.replace_all(return_html, '\n', '&#13;'); // Preserves new lines at beginning of textarea elements
1622 }
1623 return return_html;
1624 }
1625
1626 $.WS_Form.prototype.esc_html_undo = function(value) {
1627
1628 return new DOMParser().parseFromString(value, 'text/html').documentElement.textContent;
1629 }
1630
1631 $.WS_Form.prototype.strip_html = function(value) {
1632
1633 if(typeof value === 'number') { value = value.toString(); }
1634 if(typeof value !== 'string') { return ''; }
1635 if((value.indexOf('<') === -1) && (value.indexOf('>') === -1)) { return value.trim(); }
1636
1637 // Decode encoded markup before parsing, e.g. &lt;strong&gt;Text&lt;/strong&gt;.
1638 var textarea = document.createElement('textarea');
1639 textarea.innerHTML = value;
1640 value = textarea.value;
1641
1642 var doc = new DOMParser().parseFromString(value, 'text/html');
1643 var elements_remove = doc.querySelectorAll('script, style, template');
1644
1645 for(var elements_remove_index = 0; elements_remove_index < elements_remove.length; elements_remove_index++) {
1646
1647 elements_remove[elements_remove_index].remove();
1648 }
1649
1650 value = doc.body.textContent;
1651
1652 return (value === null) ? '' : value.trim();
1653 }
1654
1655 // Escape attribute
1656 $.WS_Form.prototype.esc_attr = function(value, encode_new_lines) {
1657 if (typeof value === 'number') { value = value.toString(); }
1658 if (typeof value !== 'string') { return ''; }
1659
1660 var return_html = value.replace(/[&<>"']/g, function(char) {
1661 switch (char) {
1662 case '&': return '&amp;';
1663 case '<': return '&lt;';
1664 case '>': return '&gt;';
1665 case '"': return '&quot;';
1666 case "'": return '&apos;';
1667 default: return char;
1668 }
1669 });
1670
1671 if (encode_new_lines) {
1672 return_html = return_html.replace(/\n/g, '&#13;');
1673 }
1674
1675 return return_html;
1676 }
1677
1678 // Escape URL with support for relative paths
1679 $.WS_Form.prototype.esc_url = function (url) {
1680
1681 try {
1682
1683 // Decode URL to catch encoded exploits
1684 url = decodeURIComponent(url);
1685
1686 // Allow literal hash like "#wsf-something-0"
1687 if (/^#[a-zA-Z0-9\-]+$/.test(url)) {
1688 return url;
1689 }
1690
1691 // Extract the hash if present
1692 var hash = '';
1693 var hash_index = url.indexOf('#');
1694 if (hash_index !== -1) {
1695 hash = url.substring(hash_index); // Save the hash
1696 url = url.substring(0, hash_index); // Remove the hash from the URL for further processing
1697 }
1698
1699 // Add a default scheme if none is provided, but handle schemes like "tel:"
1700 if (!/^[a-zA-Z][a-zA-Z\d+\-.]*:/.test(url)) {
1701 url = 'https://' + url;
1702 }
1703
1704 // Parse the URL
1705 var url_parsed = new URL(url);
1706
1707 // Allowed schemes
1708 var schemes_valid = ['http', 'https', 'ftp', 'mailto', 'tel'];
1709
1710 // Check if the scheme is valid
1711 if (!schemes_valid.includes(url_parsed.protocol.replace(':', ''))) {
1712 return '';
1713 }
1714
1715 // Disallowed patterns
1716 var disallowed_patterns = [
1717 /javascript:/i, // Block "javascript:" scheme
1718 /data:/i, // Block "data:" scheme
1719 /vbscript:/i, // Block "vbscript:" scheme
1720 /file:/i, // Block "file:" scheme
1721 /<.*?>/i, // Block HTML/XSS
1722 /%00/i, // Block null bytes
1723 /(\.\.\/|\.\\)/i, // Block path traversal
1724 /(redirect|url|next)=http/i, // Block untrusted redirects
1725 /(%25)+/i // Block double-encoding
1726 ];
1727
1728 // Check for disallowed patterns in the URL
1729 for (var pattern of disallowed_patterns) {
1730 if (pattern.test(url)) {
1731 return '';
1732 }
1733 }
1734
1735 // Reconstruct the URL to ensure it's properly formatted, appending the hash if present
1736 return url_parsed.toString() + hash;
1737
1738 } catch (e) {
1739
1740 // If URL is invalid or cannot be parsed, return an empty string
1741 return '';
1742 }
1743 }
1744
1745 // Escape selector
1746 $.WS_Form.prototype.esc_selector = function(value) {
1747
1748 if(typeof value !== 'string') { return value; }
1749
1750 var return_html = this.replace_all(value, '"', '\\"');
1751 return_html = this.replace_all(return_html, "'", "\\'");
1752 return_html = this.replace_all(return_html, '[', '\\[');
1753 return_html = this.replace_all(return_html, ']', '\\]');
1754 return_html = this.replace_all(return_html, ',', '\\,');
1755 return_html = this.replace_all(return_html, '=', '\\=');
1756
1757 return return_html;
1758 }
1759
1760 // Strip HTML
1761 $.WS_Form.prototype.html_strip = function(value) {
1762
1763 // Process by value type
1764 switch(typeof value) {
1765
1766 // String
1767 case 'string' :
1768
1769 value = this.html_strip_do(value);
1770 break;
1771
1772 // Arrays / objects
1773 case 'object' :
1774
1775 for(var value_index in value) {
1776
1777 if(!value.hasOwnProperty(value_index)) { continue; }
1778
1779 if(typeof value[value_index] === 'string') {
1780
1781 value[value_index] = this.html_strip_do(value[value_index]);
1782 }
1783 }
1784 }
1785
1786 return value;
1787 }
1788
1789 $.WS_Form.prototype.html_strip_do = function(value) {
1790
1791 return this.esc_html(value.replace(/<[^>]*>/g, ''));
1792 }
1793
1794 // JS string encode so it can be used in single quotes
1795 $.WS_Form.prototype.js_string_encode = function(input) {
1796
1797 if(typeof input !== 'string') { return input; }
1798
1799 var return_html = this.replace_all(input, "'", "\\'");
1800
1801 return return_html;
1802 }
1803
1804 // Loader - On
1805 $.WS_Form.prototype.loader_on = function() {
1806
1807 $('#wsf-loader').addClass('wsf-loader-on');
1808 }
1809
1810 // Loader - Off
1811 $.WS_Form.prototype.loader_off = function() {
1812
1813 $('#wsf-loader').removeClass('wsf-loader-on');
1814 }
1815
1816 // HTML encode string
1817 $.WS_Form.prototype.comment_html = function(string, end) {
1818
1819 if(typeof end === 'undefined') { end = false; }
1820
1821 return ('<!-- ' + (end ? '/' : '') + string + " -->\n") + (end ? "\n" : '');
1822 }
1823
1824 // HTML encode string
1825 $.WS_Form.prototype.comment_css = function(string) {
1826
1827 return ("\t/* " + string + " */\n");
1828 }
1829
1830 // Get object value
1831 $.WS_Form.prototype.get_object_value = function(object, element, default_return) {
1832
1833 if(typeof default_return === 'undefined') { default_return = false; }
1834
1835 // Check object and return value if found
1836 if(typeof object === 'undefined') { return default_return; }
1837 if(typeof object[element] === 'undefined') { return default_return; }
1838 return object[element];
1839 }
1840
1841 // Get object value (with fallback)
1842 $.WS_Form.prototype.get_field_value_fallback = function(field_type, label_position, element, default_return, framework_fields, sub_type) {
1843
1844 if(typeof default_return === 'undefined') { default_return = false; }
1845 if(!framework_fields) { framework_fields = this.framework_fields; }
1846 if(typeof sub_type === 'undefined') { sub_type = false; }
1847
1848 if(sub_type === false) {
1849
1850 var return_value = this.get_field_value_fallback_process(field_type, label_position, element, default_return, framework_fields);
1851 return return_value;
1852
1853 } else {
1854
1855 var sub_type_return = this.get_field_value_fallback_process(field_type, label_position, element + '_' + sub_type, default_return, framework_fields);
1856 if(sub_type_return === default_return) {
1857
1858 var sub_type_return = this.get_field_value_fallback_process(field_type, label_position, element, default_return, framework_fields);
1859 }
1860
1861 return sub_type_return;
1862 }
1863 }
1864
1865 $.WS_Form.prototype.get_field_value_fallback_process = function(field_type, label_position, element, default_return, framework_fields) {
1866
1867 // Get field to check
1868 var object = framework_fields;
1869 var object_fallback = $.WS_Form.field_type_cache[field_type];
1870
1871 // object[label_position] checks
1872 if(label_position !== false) {
1873
1874 // object[label_position].field_types[field_type][element]
1875 var object_not_found = (typeof object === 'undefined') || (typeof object[label_position] === 'undefined') || (typeof object[label_position].field_types === 'undefined') || (typeof object[label_position].field_types[field_type] === 'undefined') || (typeof object[label_position].field_types[field_type][element] === 'undefined');
1876 if(!object_not_found) { return object[label_position].field_types[field_type][element]; }
1877
1878 // object[label_position][element]
1879 var object_not_found = (typeof object === 'undefined') || (typeof object[label_position] === 'undefined') || (typeof (object[label_position][element]) === 'undefined');
1880 if(!object_not_found) { return object[label_position][element]; }
1881 }
1882
1883 // object.field_types[field_type][element]
1884 var object_not_found = (typeof object === 'undefined') || (typeof object.field_types === 'undefined') || (typeof object.field_types[field_type] === 'undefined') || (typeof object.field_types[field_type][element] === 'undefined');
1885 if(!object_not_found) { return object.field_types[field_type][element]; }
1886
1887 // object[element]
1888 var object_not_found = (typeof object === 'undefined') || (typeof object[element] === 'undefined');
1889 if(!object_not_found) { return object[element]; }
1890
1891 // object_fallback[element]
1892 if(typeof object_fallback === 'undefined') { return default_return; }
1893 if(typeof object_fallback[element] === 'undefined') { return default_return; }
1894 return object_fallback[element];
1895 }
1896
1897 // Get object data
1898 $.WS_Form.prototype.get_object_data = function(object, object_id, use_scratch) {
1899
1900 if(typeof use_scratch === 'undefined') { use_scratch = false; }
1901
1902 // Get object data
1903 switch(object) {
1904
1905 case 'form' :
1906
1907 return use_scratch ? this.object_data_scratch : this.form;
1908
1909 case 'group' :
1910
1911 return use_scratch ? this.object_data_scratch : this.group_data_cache[object_id];
1912
1913 case 'section' :
1914
1915 return use_scratch ? this.object_data_scratch : this.section_data_cache[object_id];
1916
1917 case 'field' :
1918
1919 return use_scratch ? this.object_data_scratch : this.field_data_cache[object_id];
1920
1921
1922 case 'action' :
1923
1924 return this.action;
1925 }
1926
1927 return false;
1928 }
1929
1930 // Get object meta
1931 $.WS_Form.prototype.get_object_meta = function(object, object_id) {
1932
1933 switch(object) {
1934
1935 case 'form' :
1936
1937 var object_meta = $.WS_Form.settings_form.sidebars.form.meta;
1938 break;
1939
1940
1941 case 'group' :
1942
1943 var object_meta = $.WS_Form.settings_form.sidebars.group.meta;
1944 break;
1945
1946 case 'section' :
1947
1948 var object_meta = $.WS_Form.settings_form.sidebars.section.meta;
1949 break;
1950
1951 case 'field' :
1952
1953 var object_data = this.field_data_cache[object_id];
1954 var object_meta = $.WS_Form.field_type_cache[object_data.type];
1955 break;
1956 }
1957
1958 return object_meta;
1959 }
1960
1961 // Get object meta value
1962 $.WS_Form.prototype.get_object_meta_value = function(object, key, default_return, create, parse_variables_process) {
1963
1964 if(typeof default_return === 'undefined') { default_return = false; }
1965
1966 if(typeof create === 'undefined') { create = false; }
1967
1968 if(typeof parse_variables_process === 'undefined') { parse_variables_process = false; }
1969
1970 if(typeof object === 'undefined') { return default_return; }
1971
1972 if(typeof object.meta === 'undefined') { return default_return; }
1973
1974 if(typeof object.meta[key] === 'undefined') {
1975
1976 if(create) {
1977
1978 this.set_object_meta_value(object, key, default_return);
1979
1980 } else {
1981
1982 return default_return;
1983 }
1984 }
1985 return (parse_variables_process && (typeof object.meta[key] === 'string')) ? this.parse_variables_process(object.meta[key]).output : object.meta[key];
1986 }
1987
1988 // Get object meta value
1989 $.WS_Form.prototype.has_object_meta_key = function(object, key) {
1990
1991 return (
1992
1993 (typeof object !== 'undefined') &&
1994 (typeof object.meta !== 'undefined') &&
1995 (typeof object.meta[key] !== 'undefined') &&
1996 (object.meta[key] != '')
1997 );
1998 }
1999
2000 // Parse WS Form variables
2001 $.WS_Form.prototype.parse_variables_process = function(parse_string, section_repeatable_index, calc_type, field_to, field_part, calc_register, section_id, depth, parse_string_original, field_to_original) {
2002
2003 var ws_this = this;
2004
2005 // Checks parse_string
2006 if(typeof parse_string !== 'string') { return this.parse_variables_process_error(parse_string); }
2007 if(parse_string.indexOf('#') == -1) { return this.parse_variables_process_error(parse_string); }
2008
2009 // Check section_repeatable_index
2010 if(typeof section_repeatable_index === 'undefined') { section_repeatable_index = false; }
2011
2012 // Check calc type
2013 if(typeof calc_type === 'undefined') { calc_type = false; }
2014 var calc = (calc_type === 'calc');
2015
2016 // Check field_to
2017 if(typeof field_to === 'undefined') { field_to = false; }
2018
2019 // Check field_part
2020 if(typeof field_part === 'undefined') { field_part = false; }
2021
2022 // Check calc_register
2023 if(typeof calc_register === 'undefined') { calc_register = true; }
2024
2025 // Check section_id
2026 if(typeof section_id === 'undefined') { section_id = false; }
2027
2028 // Check for too many iterations
2029 if(typeof depth === 'undefined') { depth = 1; }
2030
2031 // Check for original parse string (used for calc)
2032 if(typeof parse_string_original === 'undefined') { parse_string_original = parse_string; }
2033
2034 // Check for original field (used for calc)
2035 if(typeof field_to_original === 'undefined') { field_to_original = field_to; }
2036
2037 // Initialize variables
2038 var variables = {};
2039 var variables_single_parse = {};
2040
2041 // Parse type
2042 var lookups_contain_singles = false;
2043
2044 // Groups touched
2045 var variable_groups = [];
2046
2047 // Sections touched
2048 var variable_sections = [];
2049
2050 // Fields touched
2051 var variable_fields = [];
2052
2053 // Dynamic variables should be assessed on each validation pass
2054 var variable_dynamic = false;
2055
2056 var variable_dependencies_merge = function(parse_variables_process_return) {
2057
2058 if(typeof parse_variables_process_return.groups === 'object') { variable_groups = variable_groups.concat(parse_variables_process_return.groups); }
2059 if(typeof parse_variables_process_return.sections === 'object') { variable_sections = variable_sections.concat(parse_variables_process_return.sections); }
2060 if(typeof parse_variables_process_return.fields === 'object') { variable_fields = variable_fields.concat(parse_variables_process_return.fields); }
2061 if(parse_variables_process_return.dynamic) { variable_dynamic = true; }
2062 };
2063
2064 // Check for too many iterations
2065 if(depth > 100) {
2066
2067 this.error('error_parse_variable_syntax_error_depth', '', 'error-parse-variables');
2068 return this.parse_variables_process_error(this.language('error_parse_variable_syntax_error_depth'));
2069 }
2070
2071 // Process each parse variable key
2072 for(var parse_variables_key in $.WS_Form.parse_variables) {
2073
2074 if(!$.WS_Form.parse_variables.hasOwnProperty(parse_variables_key)) { continue; }
2075
2076 // Process each parse variable
2077 var parse_variables = $.WS_Form.parse_variables[parse_variables_key];
2078
2079 // Check for prefix (for performance)
2080 var var_lookup_found = false;
2081
2082 for(var var_lookups_index in parse_variables.var_lookups) {
2083
2084 if(!parse_variables.var_lookups.hasOwnProperty(var_lookups_index)) { continue; }
2085
2086 // Check if parse string contains lookup value
2087 if(parse_string.indexOf(parse_variables.var_lookups[var_lookups_index]) !== -1) {
2088
2089 var_lookup_found = true;
2090 break;
2091 }
2092 }
2093
2094 // If lookup not found, skip this variable group
2095 if(!var_lookup_found) { continue; }
2096
2097 for(var parse_variable in parse_variables.variables) {
2098
2099 if(!parse_variables.variables.hasOwnProperty(parse_variable)) { continue; }
2100
2101 if(parse_string.indexOf('#' + parse_variable) === -1) { continue; }
2102
2103 var parse_variable_config = parse_variables.variables[parse_variable];
2104
2105 // Assign value
2106 var parse_variable_value = (typeof parse_variable_config.value !== 'undefined') ? parse_variable_config.value : false;
2107 var parse_variable_attributes = (typeof parse_variable_config.attributes === 'object') ? parse_variable_config.attributes : false;
2108
2109 // Single parse? (Used if different value returned each parse, e.g. random_number)
2110 var parse_variable_single_parse = (typeof parse_variable_config.single_parse !== 'undefined') ? parse_variable_config.single_parse : false;
2111 if(parse_variable_single_parse) { variable_dynamic = true; }
2112
2113 // If no attributes specified, then just set the value
2114 if(parse_variable_attributes === false) {
2115
2116 // Prefer a registered client-side resolver over the static config value
2117 // so add-ons can use the same mechanism for no-arg and parameterized variables
2118 if(
2119 (typeof $.WS_Form.parse_variable_handlers === 'object') &&
2120 (typeof $.WS_Form.parse_variable_handlers[parse_variable] === 'function')
2121 ) {
2122
2123 var parsed_variable = $.WS_Form.parse_variable_handlers[parse_variable].call(
2124
2125 this,
2126 [],
2127 {
2128 ws_form: this,
2129 section_repeatable_index: section_repeatable_index,
2130 field_to: field_to,
2131 field_part: field_part,
2132 section_id: section_id,
2133 parse_variable: parse_variable,
2134 parse_variable_full: '#' + parse_variable,
2135 current_value: (parse_variable_value !== false) ? parse_variable_value : ''
2136 }
2137 );
2138
2139 if(parse_variable_single_parse) {
2140
2141 variables_single_parse[parse_variable] = parsed_variable;
2142
2143 } else {
2144
2145 variables[parse_variable] = parsed_variable;
2146 }
2147
2148 continue;
2149 }
2150
2151 if(parse_variable_value !== false) { variables[parse_variable] = parse_variable_value; continue; }
2152 }
2153
2154 // Get number of attributes required
2155 var variable_attribute_count = (typeof parse_variable_config.attributes === 'object') ? parse_variable_config.attributes.length : 0;
2156
2157 if(variable_attribute_count > 0) {
2158
2159 // Do until no more found
2160 var variable_index_start = 0;
2161 do {
2162
2163 // Find position of variable and brackets
2164 var variable_index_of = parse_string.indexOf('#' + parse_variable, variable_index_start);
2165
2166 // No more instances of variable found
2167 if(variable_index_of === -1) { continue; }
2168
2169 // Find bracket positions
2170 var variable_index_of_bracket_start = -1;
2171 var variable_index_of_bracket_finish = -1;
2172 var parse_string_function = parse_string.substring(variable_index_of + parse_variable.length + 1);
2173
2174 // Bracket should immediately follow the variable name
2175 if(parse_string_function.substring(0, 1) === '(') {
2176
2177 variable_index_of_bracket_start = variable_index_of + parse_variable.length + 1;
2178 variable_index_of_bracket_finish = this.get_bracket_finish_index(parse_string_function);
2179
2180 if(variable_index_of_bracket_finish !== -1) {
2181
2182 variable_index_of_bracket_finish += variable_index_of_bracket_start;
2183 }
2184 }
2185
2186 // Check brackets found
2187 if( (variable_index_of_bracket_start === -1) ||
2188 (variable_index_of_bracket_finish === -1) ) {
2189
2190 // Shift index to look for next instance
2191 variable_index_start += parse_variable.length + 1;
2192
2193 // Get full string to parse
2194 parse_variable_full = '#' + parse_variable;
2195
2196 // No brackets found so set attributes as blank
2197 var variable_attribute_array = [];
2198
2199 } else {
2200
2201 // Shift index to look for next instance
2202 variable_index_start = variable_index_of_bracket_finish;
2203
2204 // Get attribute string
2205 var variable_attribute_string = parse_string.substring(variable_index_of_bracket_start + 1, variable_index_of_bracket_finish);
2206
2207 // Get full string to parse
2208 var parse_variable_full = parse_string.substring(variable_index_of, variable_index_of_bracket_finish + 1);
2209
2210 // Get separator
2211 var separator = (typeof parse_variable_config.attribute_separator !== 'undefined') ? parse_variable_config.attribute_separator : ',';
2212
2213 // Convert string to attributes
2214 var variable_attribute_array = this.string_to_attributes(variable_attribute_string, separator);
2215 }
2216
2217 // Check each attribute
2218 for(var parse_variable_attributes_index in parse_variable_attributes) {
2219
2220 if(!parse_variable_attributes.hasOwnProperty(parse_variable_attributes_index)) { continue; }
2221
2222 var parse_variable_attribute = parse_variable_attributes[parse_variable_attributes_index];
2223
2224 var parse_variable_attribute_id = parse_variable_attribute.id;
2225
2226 // Was attribute provided for this index?
2227 var parse_variable_attribute_supplied = (typeof variable_attribute_array[parse_variable_attributes_index] !== 'undefined');
2228
2229 // Check required
2230 var parse_variable_attribute_required = (typeof parse_variable_attribute.required !== 'undefined') ? parse_variable_attribute.required : true;
2231 if(parse_variable_attribute_required && !parse_variable_attribute_supplied) {
2232
2233 // Syntax error - Attribute count
2234 this.error('error_parse_variable_syntax_error_attribute', '#' + parse_variable + ' (Expected ' + parse_variable_attribute_id + ')', 'error-parse-variables');
2235 return this.parse_variables_process_error(this.language('error_parse_variable_syntax_error_attribute', '#' + parse_variable + ' (Expected ' + parse_variable_attribute_id + ')'));
2236 }
2237
2238 // Check default
2239 var parse_variable_attribute_default = (typeof parse_variable_attribute.default !== 'undefined') ? parse_variable_attribute.default : false;
2240 if((parse_variable_attribute_default !== false) && !parse_variable_attribute_supplied) {
2241
2242 variable_attribute_array[parse_variable_attributes_index] = parse_variable_attribute_default;
2243 }
2244
2245 // Check trim
2246 var parse_variable_attribute_trim = (typeof parse_variable_attribute.trim !== 'undefined') ? parse_variable_attribute.trim : true;
2247 if(parse_variable_attribute_trim) {
2248
2249 var parse_variable_attribute_value = variable_attribute_array[parse_variable_attributes_index];
2250
2251 if(typeof parse_variable_attribute_value === 'string') {
2252
2253 variable_attribute_array[parse_variable_attributes_index] = parse_variable_attribute_value.trim();
2254 }
2255 }
2256
2257 // Check valid
2258 var parse_variable_attribute_valid = (typeof parse_variable_attribute.valid !== 'undefined') ? parse_variable_attribute.valid : false;
2259 if(parse_variable_attribute_valid !== false) {
2260
2261 if(!parse_variable_attribute_valid.includes(variable_attribute_array[parse_variable_attributes_index])) {
2262
2263 // Syntax error - Invalid attribute value
2264 this.error('error_parse_variable_syntax_error_attribute_invalid', '#' + parse_variable + ' (Expected ' + parse_variable_attribute_valid.join(', ') + ')', 'error-parse-variables');
2265 return this.parse_variables_process_error(this.language('error_parse_variable_syntax_error_attribute_invalid', '#' + parse_variable + ' (Expected ' + parse_variable_attribute_valid.join(', ') + ')'));
2266 }
2267 }
2268 }
2269
2270 // Process variable
2271 var parsed_variable = '';
2272 var esc_html = true;
2273
2274 switch(parse_variable) {
2275 case 'tab_label' :
2276
2277 if(isNaN(variable_attribute_array[0])) {
2278
2279 this.error('error_parse_variable_syntax_error_group_id', variable_attribute_array[0], 'error-parse-variables');
2280 return this.parse_variables_process_error(this.language('error_parse_variable_syntax_error_group_id', variable_attribute_array[0]));
2281 }
2282
2283 var group_id = parseInt(variable_attribute_array[0], 10);
2284 variable_groups.push(group_id);
2285
2286 if(
2287 (typeof this.group_data_cache[group_id] !== 'undefined') &&
2288 (typeof this.group_data_cache[group_id].label !== 'undefined')
2289 ) {
2290
2291 parsed_variable = this.group_data_cache[group_id].label;
2292
2293 } else {
2294
2295 this.error('error_parse_variable_syntax_error_group_id', group_id, 'error-parse-variables');
2296 return this.parse_variables_process_error(this.language('error_parse_variable_syntax_error_group_id', group_id));
2297 }
2298
2299 break;
2300
2301 case 'section_label' :
2302
2303 if(isNaN(variable_attribute_array[0])) {
2304
2305 this.error('error_parse_variable_syntax_error_section_id', variable_attribute_array[0], 'error-parse-variables');
2306 return this.parse_variables_process_error(this.language('error_parse_variable_syntax_error_section_id', variable_attribute_array[0]));
2307 }
2308
2309 var section_id_label = parseInt(variable_attribute_array[0], 10);
2310 variable_sections.push(section_id_label);
2311
2312 if(
2313 (typeof this.section_data_cache[section_id_label] !== 'undefined') &&
2314 (typeof this.section_data_cache[section_id_label].label !== 'undefined')
2315 ) {
2316
2317 parsed_variable = this.section_data_cache[section_id_label].label;
2318
2319 } else {
2320
2321 this.error('error_parse_variable_syntax_error_section_id', section_id_label, 'error-parse-variables');
2322 return this.parse_variables_process_error(this.language('error_parse_variable_syntax_error_section_id', section_id_label));
2323 }
2324
2325 break;
2326
2327 case 'cookie_get' :
2328
2329 // Get cookie value
2330 parsed_variable = this.cookie_get_raw(variable_attribute_array[0]);
2331
2332 break;
2333
2334 case 'session_storage_get' :
2335
2336 // Get session storage value
2337 parsed_variable = this.session_storage_get_raw(variable_attribute_array[0]);
2338
2339 break;
2340
2341 case 'local_storage_get' :
2342
2343 // Get local storage value
2344 parsed_variable = this.local_storage_get_raw(variable_attribute_array[0]);
2345
2346 break;
2347
2348 case 'field_label' :
2349
2350 if(isNaN(variable_attribute_array[0])) {
2351
2352 this.error('error_parse_variable_syntax_error_field_id', variable_attribute_array[0], 'error-parse-variables');
2353 return this.parse_variables_process_error(this.language('error_parse_variable_syntax_error_field_id', variable_attribute_array[0]));
2354 }
2355
2356 var field_id = parseInt(variable_attribute_array[0], 10);
2357 variable_fields.push(field_id);
2358
2359 if(
2360 (typeof this.field_data_cache[field_id] !== 'undefined') &&
2361 (typeof this.field_data_cache[field_id].label !== 'undefined')
2362 ) {
2363
2364 parsed_variable = this.field_data_cache[field_id].label;
2365
2366 } else {
2367
2368 this.error('error_parse_variable_syntax_error_field_id', field_id, 'error-parse-variables');
2369 return this.parse_variables_process_error(this.language('error_parse_variable_syntax_error_field_id', field_id));
2370 }
2371
2372 break;
2373
2374 case 'field_min_id' :
2375 case 'field_max_id' :
2376 case 'field_min_value' :
2377 case 'field_max_value' :
2378 case 'field_min_label' :
2379 case 'field_max_label' :
2380
2381 var field_ids = [];
2382 var field_min_id = 0;
2383 var field_min_value = 0;
2384 var field_min_label = '';
2385 var field_max_id = 0;
2386 var field_max_value = 0;
2387 var field_max_label = '';
2388 var variable_attribute_array_index = 0;
2389 var field_value_index = 0;
2390
2391 // Loop through provided field ID's
2392 while(!isNaN(variable_attribute_array[variable_attribute_array_index])) {
2393
2394 // Get field ID
2395 var field_id = parseInt(variable_attribute_array[variable_attribute_array_index], 10);
2396 if(!field_id) { variable_attribute_array_index++; continue; }
2397
2398 // Add to fields touched
2399 variable_fields.push(field_id);
2400
2401 // Get field
2402 if(typeof this.field_data_cache[field_id] !== 'object') { continue; }
2403 var field = this.field_data_cache[field_id];
2404
2405 // Get field value
2406 var field_value_array = this.get_field_value(field, field.section_repeatable);
2407
2408 if(
2409 (typeof field_value_array != 'object') ||
2410 (typeof field_value_array[0] === 'undefined')
2411 ) {
2412 field_value_array = [0];
2413 }
2414
2415 /// Get field label
2416 var field_label = field.label;
2417
2418 // Process each field value (Multiple for repeatable sections)
2419 for(var field_value of field_value_array) {
2420
2421 // Convert to number
2422 field_value = this.get_number(field_value, 0, true);
2423
2424 // Set min and max
2425 if(field_value_index == 0) {
2426
2427 // Initial values
2428 field_max_id = field_min_id = field_id;
2429 field_max_value = field_min_value = field_value;
2430 field_max_label = field_min_label = field_label;
2431
2432 } else {
2433
2434 // Min / max calculations
2435 if(field_value < field_min_value) {
2436
2437 field_min_id = field_id;
2438 field_min_value = field_value;
2439 field_min_label = field_label;
2440 }
2441
2442 if(field_value > field_max_value) {
2443
2444 field_max_id = field_id;
2445 field_max_value = field_value;
2446 field_max_label = field_label;
2447 }
2448 }
2449
2450 field_value_index++;
2451 }
2452
2453 // Go to next variable attribute
2454 variable_attribute_array_index++;
2455 }
2456
2457 switch(parse_variable) {
2458
2459 case 'field_min_id' :
2460
2461 parsed_variable = field_min_id;
2462 break;
2463
2464 case 'field_max_id' :
2465
2466 parsed_variable = field_max_id;
2467 break;
2468
2469 case 'field_min_value' :
2470
2471 parsed_variable = field_min_value;
2472 break;
2473
2474 case 'field_max_value' :
2475
2476 parsed_variable = field_max_value;
2477 break;
2478
2479 case 'field_min_label' :
2480
2481 parsed_variable = field_min_label;
2482 break;
2483
2484 case 'field_max_label' :
2485
2486 parsed_variable = field_max_label;
2487 break;
2488 }
2489
2490 break;
2491
2492 case 'date_format' :
2493
2494 // Parse date
2495 var date_input = this.parse_variables_process(variable_attribute_array[0], section_repeatable_index, calc_type, field_from, field_part, calc_register, section_id, depth).output;
2496
2497 // Parse date format
2498 var date_format = this.parse_variables_process(variable_attribute_array[1], section_repeatable_index, calc_type, field_from, field_part, calc_register, section_id, depth).output;
2499
2500 // Get date
2501 var parsed_variable_date = new Date(date_input);
2502
2503 // Check date
2504 if(isNaN(parsed_variable_date.getTime())) {
2505
2506 this.error('error_parse_variable_syntax_error_date_format', date_input, 'error-parse-variables');
2507 return this.parse_variables_process_error(this.language('error_parse_variable_syntax_error_date_format', date_input));
2508 }
2509
2510 // Process date
2511 parsed_variable = this.date_format(parsed_variable_date, date_format);
2512
2513 break;
2514
2515 case 'field' :
2516 case 'field_float' :
2517 case 'field_date_age' :
2518 case 'field_date_format' :
2519 case 'field_date_offset' :
2520 case 'field_count_word' :
2521 case 'field_count_char' :
2522 case 'ecommerce_field_price' :
2523
2524 if(isNaN(variable_attribute_array[0])) {
2525
2526 this.error('error_parse_variable_syntax_error_field_id', variable_attribute_array[0], 'error-parse-variables');
2527 return this.parse_variables_process_error(this.language('error_parse_variable_syntax_error_field_id', variable_attribute_array[0]));
2528 }
2529
2530 var field_id = parseInt(variable_attribute_array[0], 10);
2531
2532 // Check for infinite loops
2533 if(
2534 (
2535 (field_part === 'field_value') ||
2536 (field_part === false)
2537 ) &&
2538 (field_id === parseInt(field_to_original.id, 10))
2539 ) {
2540
2541 // Syntax error - Infinite loop
2542 this.error('error_parse_variable_syntax_error_self_ref', parse_variable_full, 'error-parse-variables');
2543 return this.parse_variables_process_error(this.language('error_parse_variable_syntax_error_self_ref', parse_variable_full));
2544 }
2545
2546 // Check field exists
2547 if(typeof this.field_data_cache[field_id] === 'undefined') {
2548
2549 this.error('error_parse_variable_syntax_error_field_id', field_id, 'error-parse-variables');
2550 return this.parse_variables_process_error(this.language('error_parse_variable_syntax_error_field_id', field_id));
2551 }
2552
2553 // Add to fields touched array if we are still dealing with the original field
2554 if(field_to.id === field_to_original.id) {
2555
2556 variable_fields.push(field_id);
2557 }
2558
2559 // Get field config
2560 var field_from = this.field_data_cache[field_id];
2561 if(typeof $.WS_Form.field_type_cache[field_from.type] === 'undefined') { break; }
2562 var field_type_config_from = $.WS_Form.field_type_cache[field_from.type];
2563
2564 // Check #calc and #text
2565 if(calc_type !== false) {
2566
2567 // Check field configuration calc_out / text_out
2568 var allow_out = typeof field_type_config_from[calc_type + '_out'] ? field_type_config_from[calc_type + '_out'] : false;
2569 if(!allow_out) {
2570
2571 this.error('error_parse_variable_syntax_error_' + calc_type + '_out', field_from.label + ' (ID: ' + field_from.id + ')', 'error-parse-variables');
2572 return this.parse_variables_process_error(this.language('error_parse_variable_syntax_error_' + calc_type + '_out', field_from.label + ' (ID: ' + field_from.id + ')'));
2573 }
2574 }
2575
2576 // Check for static value
2577 var field_static = typeof field_type_config_from.static ? field_type_config_from.static : false;
2578
2579 if(field_static) {
2580
2581 if(field_static === true) {
2582
2583 // If static set to true, we use the mask_field
2584 var value = (typeof field_type_config_from.mask_field_static !== 'undefined') ? field_type_config_from.mask_field_static : '';
2585
2586 } else {
2587
2588 // Get value
2589 var value = this.get_object_meta_value(field_from, field_static, '');
2590
2591 // wpautop?
2592 if(this.wpautop_parse_variable(field_from, field_type_config_from)) {
2593
2594 value = this.wpautop(value);
2595 esc_html = false;
2596 }
2597 }
2598
2599 var parse_variables_process_return = this.parse_variables_process(value, section_repeatable_index, calc_type, field_from, field_part, calc_register, section_id, depth + 1, parse_string_original, field_to_original);
2600 variable_dependencies_merge(parse_variables_process_return);
2601 parsed_variable = [parse_variables_process_return.output];
2602
2603 break;
2604 }
2605
2606 // Check if submitted as array
2607 var submit_array_from = (typeof field_type_config_from.submit_array !== 'undefined') ? field_type_config_from.submit_array : false;
2608
2609 // Get criteria needed to work out how to get field value
2610 var section_repeatable_section_id_from = (typeof field_from.section_repeatable_section_id !== 'undefined') ? parseInt(field_from.section_repeatable_section_id, 10) : false;
2611 var section_repeatable_section_id_to = (typeof field_to.section_repeatable_section_id !== 'undefined') ? parseInt(field_to.section_repeatable_section_id, 10) : false;
2612 var section_repeatable_index_to = section_repeatable_index;
2613
2614 var parsed_variable = false;
2615
2616 var delimiter = (typeof variable_attribute_array[1] !== 'undefined') ? variable_attribute_array[1] : ',';
2617
2618 var column = (typeof variable_attribute_array[2] !== 'undefined') ? variable_attribute_array[2] : false;
2619
2620 // REPEATABLE TO REPEATABLE
2621 // In this scenario, we want the single field in the from section
2622 if(
2623 (
2624 (section_repeatable_index_to !== false) &&
2625 (section_repeatable_section_id_from === section_id)
2626 )
2627 ||
2628 (
2629 (section_repeatable_section_id_from !== false) &&
2630 (section_repeatable_section_id_to !== false)
2631 )
2632 ) {
2633
2634 // Get source repeatable index
2635 var parsed_variable = this.get_field_value(field_from, section_repeatable_index_to, submit_array_from, column);
2636 }
2637
2638 // REPEATABLE TO NON-REPEATABLE
2639 // In this scenario, we want the sum of all fields in the from sections
2640 if(
2641 (parsed_variable === false) &&
2642 (section_repeatable_section_id_from !== false) &&
2643 (section_repeatable_section_id_to === false)
2644 ) {
2645
2646 // Get source repeatable index
2647 var parsed_variable = this.get_field_value(field_from, true, submit_array_from, column);
2648 }
2649
2650 // NON-REPEATABLE TO ANYTHING
2651 // In this scenario, simply get the field
2652 if(
2653 (parsed_variable === false) &&
2654 (section_repeatable_section_id_from === false)
2655 ) {
2656 // Get source repeatable index
2657 var parsed_variable = this.get_field_value(field_from, false, submit_array_from, column);
2658 }
2659
2660 // Value still not found, fallback to default
2661 if(parsed_variable === false) {
2662
2663 switch(field_from.type) {
2664
2665 case 'price_select' :
2666 case 'price_checkbox' :
2667 case 'price_radio' :
2668 case 'select' :
2669 case 'checkbox' :
2670 case 'radio' :
2671
2672 // Get default from data source
2673 var data_source = this.get_data_source(field_from);
2674
2675 if(data_source.default_value !== false) {
2676
2677 parsed_variable = data_source.default_value;
2678
2679 } else {
2680
2681 parsed_variable = [];
2682 }
2683
2684 break;
2685
2686 default :
2687
2688 // Get default value from field
2689 var default_value = this.get_object_meta_value(field_from, 'default_value', '');
2690 var parse_variables_process_return = this.parse_variables_process(default_value, section_repeatable_index, calc_type, field_from, field_part, calc_register, section_id, depth + 1, parse_string_original, field_to_original);
2691 variable_dependencies_merge(parse_variables_process_return);
2692 parsed_variable = [parse_variables_process_return.output];
2693 }
2694 }
2695
2696 // Process by field type
2697 switch(parse_variable) {
2698
2699 case 'field' :
2700
2701 if(column !== false) {
2702
2703 parsed_variable = this.get_data_grid_column(parsed_variable, field_from, column);
2704 }
2705 break;
2706
2707 case 'field_count_char' :
2708 case 'field_count_word' :
2709
2710 var parsed_variable_string = ((typeof parsed_variable === 'object') ? parsed_variable.join(delimiter) : '');
2711
2712 if(typeof variable_attribute_array[1] !== 'undefined') {
2713
2714 var regex_parts = /\/(.*)\/(.*)/.exec(variable_attribute_array[1]);
2715 parsed_variable_string = parsed_variable_string.replace(new RegExp(regex_parts[1], regex_parts[2]), '');
2716 }
2717
2718 var parsed_variable = [(parse_variable == 'field_count_char') ? parsed_variable_string.length : this.get_word_count(parsed_variable_string)];
2719
2720 break;
2721 }
2722
2723 // If this parse is for a calculation, handle the values differently
2724 if(calc) {
2725
2726 if(parsed_variable.length) {
2727
2728 switch(field_from.type) {
2729
2730 case 'price_select' :
2731 case 'price_checkbox' :
2732 case 'price_radio' :
2733 case 'price' :
2734 case 'price_subtotal' :
2735 case 'cart_price' :
2736 case 'cart_total' :
2737
2738 var parsed_variable_total = 0;
2739
2740 // Get value, processing currency
2741 for(var parsed_variable_index in parsed_variable) {
2742
2743 if(!parsed_variable.hasOwnProperty(parsed_variable_index)) { continue; }
2744 parsed_variable_total += this.get_number(parsed_variable[parsed_variable_index], 0, true);
2745 }
2746
2747 // Round to e-commerce decimals setting (This removes floating point errors, e.g. 123.4500000000002)
2748 var price_decimals = parseInt($.WS_Form.settings_plugin.price_decimals, 10);
2749 parsed_variable = this.get_number(parsed_variable_total, 0, false, price_decimals);
2750
2751 break;
2752
2753 case 'datetime' :
2754
2755 // Get input date
2756 if(parsed_variable[0] === '') { parsed_variable = ''; break; }
2757
2758 var input_type_datetime = this.get_object_meta_value(field_from, 'input_type_datetime', 'date');
2759
2760 var format_date = this.get_object_meta_value(field_from, 'format_date', ws_form_settings.date_format);
2761 if(!format_date) { format_date = ws_form_settings.date_format; }
2762
2763 var format_time = this.get_object_meta_value(field_from, 'format_time', ws_form_settings.time_format);
2764 if(!format_time) { format_time = ws_form_settings.time_format; }
2765
2766 // Convert input to JS date
2767 var input_datetime = this.get_date(parsed_variable[0], input_type_datetime, format_date, format_time);
2768
2769 // Get date represented in seconds
2770 parsed_variable = this.get_number(input_datetime.getTime()) / 1000;
2771
2772 break;
2773
2774 default :
2775
2776 var parsed_variable_total = 0;
2777
2778 // Get value, ignoring currency
2779 for(var parsed_variable_index in parsed_variable) {
2780
2781 if(!parsed_variable.hasOwnProperty(parsed_variable_index)) { continue; }
2782
2783 parsed_variable_total += ws_this.get_number(parsed_variable[parsed_variable_index], 0, false);
2784 }
2785
2786 parsed_variable = parsed_variable_total;
2787 }
2788
2789 } else {
2790
2791 parsed_variable = 0;
2792 }
2793
2794 } else {
2795
2796 // HTML encode each parsed_variable array element
2797 parsed_variable = $.map(parsed_variable, function(value) { return ws_this.esc_html(value); });
2798
2799 // Join with delimiter
2800 parsed_variable = parsed_variable.join(delimiter);
2801
2802 // Already HTML encoded the field values, so disable further HTML encoding
2803 esc_html = false;
2804 }
2805
2806 switch(parse_variable) {
2807
2808 case 'ecommerce_field_price' :
2809
2810 var parsed_variable = this.get_price(this.get_number(parsed_variable));
2811 break;
2812
2813 case 'field_date_age' :
2814
2815 var date_start = new Date((typeof parsed_variable == 'number') ? (parsed_variable * 1000) : parsed_variable);
2816 var date_end = new Date();
2817
2818 // Check start date
2819 if(isNaN(date_start.getTime())) {
2820
2821 this.error('error_parse_variable_field_date_age_invalid', parsed_variable, 'error-parse-variables');
2822 }
2823
2824 // Check for period
2825 var period = (typeof variable_attribute_array[1] === 'string') ? variable_attribute_array[1] : 'y';
2826 period = period.toLowerCase();
2827 if(![
2828
2829 'y','m','d','h','n','s',
2830 'year', 'month', 'day', 'hour', 'minute', 'second',
2831 'years', 'months', 'days', 'hours', 'minutes', 'seconds'
2832
2833 ].includes(period)) {
2834
2835 this.error('error_parse_variable_syntax_error_field_date_age_period', period, 'error-parse-variables');
2836 }
2837
2838 switch(period) {
2839
2840 // Seconds
2841 case 's' :
2842 case 'second' :
2843 case 'seconds' :
2844
2845 parsed_variable = Math.floor((date_end - date_start) / 1000);
2846 break;
2847
2848 // Minutes
2849 case 'n' :
2850 case 'minute' :
2851 case 'minutes' :
2852
2853 parsed_variable = Math.floor((date_end - date_start) / (1000 * 60));
2854 break;
2855
2856 // Hours
2857 case 'h' :
2858 case 'hour' :
2859 case 'hours' :
2860
2861 parsed_variable = Math.floor((date_end - date_start) / (1000 * 60 * 60));
2862 break;
2863
2864 // Days
2865 case 'd' :
2866 case 'day' :
2867 case 'days' :
2868
2869 parsed_variable = Math.floor((date_end - date_start) / (1000 * 60 * 60 * 24));
2870 break;
2871
2872 // Weeks
2873 case 'w' :
2874 case 'week' :
2875 case 'weeks' :
2876
2877 parsed_variable = Math.floor((date_end - date_start) / (1000 * 60 * 60 * 24 * 7));
2878 break;
2879
2880 // Months
2881 case 'm' :
2882 case 'month' :
2883 case 'months' :
2884
2885 parsed_variable = (
2886 (date_end.getFullYear() - date_start.getFullYear()) * 12 +
2887 (date_end.getMonth() - date_start.getMonth()) -
2888 (date_end.getDate() < date_start.getDate() ? 1 : 0)
2889 );
2890 break;
2891
2892 // Years (default)
2893 default:
2894
2895 parsed_variable = (
2896 date_end.getFullYear() - date_start.getFullYear() -
2897 (date_end.getMonth() < date_start.getMonth() ||
2898 (date_end.getMonth() === date_start.getMonth() && date_end.getDate() < date_start.getDate()) ? 1 : 0)
2899 );
2900 }
2901
2902 break;
2903
2904 case 'field_date_format' :
2905
2906 if(field_from.type !== 'datetime') {
2907
2908 this.error('error_parse_variable_syntax_error_field_date_offset', field_id, 'error-parse-variables');
2909 return this.parse_variables_process_error(this.language('error_parse_variable_syntax_error_field_date_offset', field_id));
2910 }
2911
2912 // Parse date
2913 var date_input = this.parse_variables_process(parsed_variable, section_repeatable_index, calc_type, field_from, field_part, calc_register, section_id, depth).output;
2914
2915 // Get input date format
2916 var format_date_input = this.get_object_meta_value(field_from, 'format_date', ws_form_settings.date_format)
2917 if(!format_date_input) { format_date_input = ws_form_settings.date_format; }
2918
2919 // Get input time format
2920 var format_time_input = this.get_object_meta_value(field_from, 'format_time', ws_form_settings.time_format)
2921 if(!format_time_input) { format_time_input = ws_form_settings.time_format; }
2922
2923 // Process as timestamp?
2924 if(this.is_integer(date_input)) {
2925
2926 parsed_variable_date = new Date(parseInt(date_input, 10) * 1000);
2927
2928 } else {
2929
2930 var input_type_datetime = this.get_object_meta_value(field_from, 'input_type_datetime', 'date');
2931
2932 parsed_variable_date = this.get_date(date_input, input_type_datetime, format_date_input, format_time_input);
2933 }
2934
2935 // Ensure parsed_variable_date is a date
2936 if(
2937 (parsed_variable_date !== false) &&
2938 !isNaN(parsed_variable_date.getTime())
2939 ) {
2940
2941 // Check for format
2942 if(
2943 (typeof variable_attribute_array[1] !== 'undefined') &&
2944 (variable_attribute_array[1] != '')
2945 ) {
2946
2947 var format_date = variable_attribute_array[1];
2948
2949 } else {
2950
2951 var format_date = format_date_input;
2952 }
2953 if(!format_date) { format_date = ws_form_settings.date_format; }
2954
2955 // Process date
2956 parsed_variable = ((typeof this.date_format === 'function') ? this.date_format(parsed_variable_date, format_date) : '');
2957 }
2958
2959 break;
2960
2961 case 'field_date_offset' :
2962
2963 if(field_from.type !== 'datetime') {
2964
2965 this.error('error_parse_variable_syntax_error_field_date_offset', field_id, 'error-parse-variables');
2966 return this.parse_variables_process_error(this.language('error_parse_variable_syntax_error_field_date_offset', field_id));
2967 }
2968
2969 // Parse date
2970 var date_input = this.parse_variables_process(parsed_variable, section_repeatable_index, calc_type, field_from, field_part, calc_register, section_id, depth).output;
2971
2972 // Get input date format
2973 var format_date_input = this.get_object_meta_value(field_from, 'format_date', ws_form_settings.date_format)
2974 if(!format_date_input) { format_date_input = ws_form_settings.date_format; }
2975
2976 // Get input time format
2977 var format_time_input = this.get_object_meta_value(field_from, 'format_time', ws_form_settings.time_format)
2978 if(!format_time_input) { format_time_input = ws_form_settings.time_format; }
2979
2980 // Process as timestamp?
2981 if(this.is_integer(date_input)) {
2982
2983 parsed_variable_date = new Date(parseInt(date_input, 10) * 1000);
2984
2985 } else {
2986
2987 var input_type_datetime = this.get_object_meta_value(field_from, 'input_type_datetime', 'date');
2988
2989 parsed_variable_date = this.get_date(date_input, input_type_datetime, format_date_input, format_time_input);
2990 }
2991
2992 // Ensure parsed_variable_date is a date
2993 if(
2994 (parsed_variable_date !== false) &&
2995 !isNaN(parsed_variable_date.getTime())
2996 ) {
2997
2998 // Check for offset
2999 var seconds_offset = parseInt(this.parse_variables_process(variable_attribute_array[1], section_repeatable_index, calc_type, field_to, field_part, calc_register, section_id, depth).output, 10);
3000
3001 if(seconds_offset) {
3002
3003 parsed_variable_date.setSeconds(parsed_variable_date.getSeconds() + seconds_offset);
3004 }
3005
3006 // Check for format
3007 if(
3008 (typeof variable_attribute_array[2] !== 'undefined') &&
3009 (variable_attribute_array[2] != '')
3010 ) {
3011
3012 var format_date = variable_attribute_array[2];
3013
3014 } else {
3015
3016 var format_date = format_date_input;
3017 }
3018 if(!format_date) { format_date = ws_form_settings.date_format; }
3019
3020 // Process date
3021 parsed_variable = ((typeof this.date_format === 'function') ? this.date_format(parsed_variable_date, format_date) : '');
3022 }
3023
3024 break;
3025
3026 case 'field_float' :
3027
3028 var parsed_variable = this.get_number(parsed_variable);
3029 break;
3030 }
3031
3032 // wpautop?
3033 if(parse_variable === 'field') {
3034
3035 if(this.wpautop_parse_variable(field_from, field_type_config_from)) {
3036
3037 parsed_variable = this.wpautop(parsed_variable);
3038 esc_html = false;
3039 }
3040 }
3041
3042 break;
3043
3044 case 'ecommerce_price' :
3045
3046 var parse_variables_process_return = this.parse_variables_process(variable_attribute_array[0], section_repeatable_index, calc_type, field_to, field_part, calc_register, section_id, depth);
3047 variable_dependencies_merge(parse_variables_process_return);
3048 var number_input = this.calc_string(parse_variables_process_return.output, parse_string_original, false, field_to_original, field_part);
3049
3050 var parsed_variable = this.get_price(number_input);
3051
3052 break;
3053
3054 case 'number_format' :
3055
3056 // Get num
3057 var parse_variables_process_return = this.parse_variables_process(variable_attribute_array[0], section_repeatable_index, calc_type, field_to, field_part, calc_register, section_id, depth);
3058 variable_dependencies_merge(parse_variables_process_return);
3059 var num = this.calc_string(parse_variables_process_return.output, parse_string_original, false, field_to_original, field_part);
3060
3061 // Get decimals
3062 var decimals = variable_attribute_array[1];
3063
3064 // Get decimal separator
3065 var decimal_separator = variable_attribute_array[2];
3066
3067 // Get thousands separator
3068 var thousands_separator = variable_attribute_array[3];
3069
3070 var parsed_variable = this.number_format(num, decimals, decimal_separator, thousands_separator);
3071
3072 break;
3073
3074 case 'select_option_text' :
3075
3076 if(isNaN(variable_attribute_array[0])) {
3077
3078 this.error('error_parse_variable_syntax_error_field_id', variable_attribute_array[0], 'error-parse-variables');
3079 return this.parse_variables_process_error(this.language('error_parse_variable_syntax_error_field_id', variable_attribute_array[0]));
3080 }
3081
3082 // Read attributes
3083 var field_id = parseInt(variable_attribute_array[0], 10);
3084 var delimiter = variable_attribute_array[1];
3085 if(typeof delimiter === 'undefined') { delimiter = ', '; }
3086
3087 // Add to fields touched array if we are still dealing with the original field
3088 if(field_to.id === field_to_original.id) {
3089
3090 variable_fields.push(field_id);
3091 }
3092
3093 // Get field name
3094 var field_name = ws_form_settings.field_prefix + field_id.toString() + ((section_repeatable_index) ? '[' + section_repeatable_index + ']' : '');
3095
3096 // Get field selected options
3097 var field_obj = $('[name="' + this.esc_selector(field_name) + '[]"] option:selected', this.form_canvas_obj);
3098
3099 // Build parsed variable
3100 if(field_obj.length) {
3101
3102 var field_obj_text_array = $.map(field_obj, function(n, i) { return $(n).text(); });
3103 parsed_variable = field_obj_text_array.join(delimiter);
3104 }
3105
3106 break;
3107
3108 case 'query_var' :
3109
3110 parsed_variable = this.get_query_var(variable_attribute_array[0], variable_attribute_array[1]);
3111 break;
3112
3113 case 'checkbox_label' :
3114 case 'radio_label' :
3115
3116 if(isNaN(variable_attribute_array[0])) {
3117
3118 this.error('error_parse_variable_syntax_error_field_id', variable_attribute_array[0], 'error-parse-variables');
3119 return this.parse_variables_process_error(this.language('error_parse_variable_syntax_error_field_id', variable_attribute_array[0]));
3120 }
3121
3122 // Read attributes
3123 var field_id = parseInt(variable_attribute_array[0], 10);
3124 var delimiter = variable_attribute_array[1];
3125 if(typeof delimiter === 'undefined') { delimiter = ', '; }
3126
3127 // Add to fields touched array if we are still dealing with the original field
3128 if(field_to.id === field_to_original.id) {
3129
3130 variable_fields.push(field_id);
3131 }
3132
3133 // Get field name
3134 var field_name = ws_form_settings.field_prefix + field_id.toString() + ((section_repeatable_index) ? '[' + section_repeatable_index + ']' : '');
3135
3136 // Get field selected options
3137 var field_obj = $('[name="' + this.esc_selector(field_name) + '[]"]:checked', this.form_canvas_obj);
3138
3139 // Build parsed variable
3140 if(field_obj.length) {
3141
3142 var field_obj_text_array = $.map(field_obj, function(n, i) {
3143
3144 return $('label[for="' + $(n).attr('id') + '"]').text();
3145 });
3146 parsed_variable = field_obj_text_array.join(delimiter);
3147 }
3148
3149 break;
3150
3151 case 'checkbox_count' :
3152 case 'checkbox_count_total' :
3153 case 'select_count' :
3154 case 'select_count_total' :
3155
3156 if(isNaN(variable_attribute_array[0])) {
3157
3158 this.error('error_parse_variable_syntax_error_field_id', variable_attribute_array[0], 'error-parse-variables');
3159 return this.parse_variables_process_error(this.language('error_parse_variable_syntax_error_field_id', variable_attribute_array[0]));
3160 }
3161
3162 // Read attributes
3163 var field_id = parseInt(variable_attribute_array[0], 10);
3164
3165 // Add to fields touched array if we are still dealing with the original field
3166 if(field_to.id === field_to_original.id) {
3167
3168 variable_fields.push(field_id);
3169 }
3170
3171 // Get field name
3172 var field_name = ws_form_settings.field_prefix + field_id.toString() + ((section_repeatable_index) ? '[' + section_repeatable_index + ']' : '');
3173
3174 switch(parse_variable) {
3175
3176 case 'checkbox_count' :
3177 case 'checkbox_count_total' :
3178
3179 // Include hidden checkboxes?
3180 var include_hidden = variable_attribute_array[1] && this.is_true(variable_attribute_array[1]);
3181
3182 // Get checked checkboxes
3183 var field_selector_suffix = ' [data-row-checkbox]' + (include_hidden ? '' : ':not([style*="display: none"])') + ' input' + (include_hidden ? '' : ':not([data-hidden])') + ((parse_variable == 'checkbox_count') ? ':checked' : '');
3184
3185 var field_obj = $(
3186 '[data-type="checkbox"][data-id="' + field_id + '"]' + field_selector_suffix + ', ' +
3187 '[data-type="price_checkbox"][data-id="' + field_id + '"]' + field_selector_suffix,
3188 this.form_canvas_obj
3189 );
3190
3191 break;
3192
3193 case 'select_count' :
3194 case 'select_count_total' :
3195
3196 // Get field selected options
3197 var field_obj = $('select[name="' + this.esc_selector(field_name) + '[]"] option:not([data-placeholder])' + ((parse_variable == 'select_count') ? ':selected' : ''), this.form_canvas_obj);
3198 break;
3199 }
3200
3201 // Build parsed variable
3202 parsed_variable = field_obj ? field_obj.length : 0;
3203
3204 break;
3205
3206 case 'post_date_custom' :
3207 case 'post_date_modified_custom' :
3208 case 'server_date_custom' :
3209 case 'blog_date_custom' :
3210
3211 var parsed_variable_date = this.get_new_date(parse_variable_value);
3212
3213 if(typeof variable_attribute_array[1] !== 'undefined') {
3214
3215 var seconds_offset = parseInt(this.parse_variables_process(variable_attribute_array[1], section_repeatable_index, calc_type, field_to, field_part, calc_register, section_id, depth).output, 10);
3216
3217 if(seconds_offset) {
3218
3219 parsed_variable_date.setSeconds(parsed_variable_date.getSeconds() + seconds_offset);
3220 }
3221 }
3222
3223 parsed_variable = ((typeof this.date_format === 'function') ? this.date_format(parsed_variable_date, variable_attribute_array[0]) : '');
3224
3225 break;
3226
3227 case 'client_date_custom' :
3228
3229 var parsed_variable_date = new Date();
3230
3231 if(typeof variable_attribute_array[1] !== 'undefined') {
3232
3233 var seconds_offset = parseInt(this.parse_variables_process(variable_attribute_array[1], section_repeatable_index, calc_type, field_to, field_part, calc_register, section_id, depth).output, 10);
3234
3235 if(seconds_offset) {
3236
3237 parsed_variable_date.setSeconds(parsed_variable_date.getSeconds() + seconds_offset);
3238 }
3239 }
3240
3241 parsed_variable = ((typeof this.date_format === 'function') ? this.date_format(parsed_variable_date, variable_attribute_array[0]) : '');
3242
3243 break;
3244
3245 case 'random_number' :
3246
3247 var random_number_min = parseInt(this.get_number(variable_attribute_array[0]), 10);
3248 var random_number_max = parseInt(this.get_number(variable_attribute_array[1]), 10);
3249 parsed_variable = Math.floor(Math.random() * (random_number_max - random_number_min + 1)) + random_number_min;
3250 break;
3251
3252 case 'random_string' :
3253
3254 var random_string_length = parseInt(this.get_number(variable_attribute_array[0]), 10);
3255 var random_string_characters = variable_attribute_array[1];
3256 var random_string_character_length = random_string_characters.length;
3257 parsed_variable = '';
3258 for (var random_string_index = 0; random_string_index < random_string_length; random_string_index++) { parsed_variable += random_string_characters[Math.floor(Math.random() * random_string_character_length)]; }
3259 break;
3260
3261 case 'abs' :
3262 case 'acos' :
3263 case 'asin' :
3264 case 'atan' :
3265 case 'ceil' :
3266 case 'cos' :
3267 case 'exp' :
3268 case 'floor' :
3269 case 'log' :
3270 case 'negative' :
3271 case 'positive' :
3272 case 'sin' :
3273 case 'sqrt' :
3274 case 'tan' :
3275
3276 var parse_variables_process_return = this.parse_variables_process(variable_attribute_array[0], section_repeatable_index, calc_type, field_to, field_part, calc_register, section_id, depth);
3277 variable_dependencies_merge(parse_variables_process_return);
3278 var number_input = this.calc_string(parse_variables_process_return.output, parse_string_original, false, field_to_original, field_part);
3279
3280 switch(parse_variable) {
3281
3282 case 'abs' :
3283
3284 parsed_variable = Math.abs(number_input);
3285 break;
3286
3287 case 'acos' :
3288
3289 parsed_variable = Math.acos(number_input);
3290 break;
3291
3292 case 'asin' :
3293
3294 parsed_variable = Math.asin(number_input);
3295 break;
3296
3297 case 'atan' :
3298
3299 parsed_variable = Math.atan(number_input);
3300 break;
3301
3302 case 'ceil' :
3303
3304 parsed_variable = Math.ceil(number_input);
3305 break;
3306
3307 case 'cos' :
3308
3309 parsed_variable = Math.cos(number_input);
3310 break;
3311
3312 case 'exp' :
3313
3314 parsed_variable = Math.exp(number_input);
3315 break;
3316
3317 case 'floor' :
3318
3319 parsed_variable = Math.floor(number_input);
3320 break;
3321
3322 case 'log' :
3323
3324 parsed_variable = Math.log(number_input);
3325 break;
3326
3327 case 'negative' :
3328 case 'positive' :
3329
3330 parsed_variable = (parse_variable == 'positive') ? Math.max(0, number_input) : Math.min(0, number_input);
3331 break;
3332
3333 case 'sin' :
3334
3335 parsed_variable = Math.sin(number_input);
3336 break;
3337
3338 case 'sqrt' :
3339
3340 parsed_variable = Math.sqrt(number_input);
3341 break;
3342
3343 case 'tan' :
3344
3345 parsed_variable = Math.tan(number_input);
3346 break;
3347 }
3348
3349 break;
3350
3351 case 'lower' :
3352 case 'upper' :
3353 case 'ucwords' :
3354 case 'ucfirst' :
3355 case 'capitalize' :
3356 case 'sentence' :
3357 case 'wpautop' :
3358 case 'trim' :
3359 case 'slug' :
3360 case 'name_prefix' :
3361 case 'name_first' :
3362 case 'name_middle' :
3363 case 'name_last' :
3364 case 'name_suffix' :
3365
3366 if(typeof variable_attribute_array[0] === 'string') {
3367
3368 var parse_variables_process_return = this.parse_variables_process(variable_attribute_array[0], section_repeatable_index, calc_type, field_to, field_part, calc_register, section_id, depth);
3369 variable_dependencies_merge(parse_variables_process_return);
3370 parsed_variable = parse_variables_process_return.output;
3371
3372 switch(parse_variable) {
3373
3374 case 'lower' :
3375
3376 parsed_variable = parsed_variable.toLowerCase();
3377 break;
3378
3379 case 'upper' :
3380
3381 parsed_variable = parsed_variable.toUpperCase();
3382 break;
3383
3384 case 'ucwords' :
3385
3386 parsed_variable = this.ucwords(parsed_variable);
3387 break;
3388
3389 case 'ucfirst' :
3390
3391 parsed_variable = this.ucfirst(parsed_variable);
3392 break;
3393
3394 case 'capitalize' :
3395
3396 parsed_variable = this.capitalize(parsed_variable);
3397 break;
3398
3399 case 'sentence' :
3400
3401 parsed_variable = this.sentence(parsed_variable);
3402 break;
3403
3404 case 'wpautop' :
3405
3406 parsed_variable = this.wpautop(parsed_variable);
3407 esc_html = false;
3408 break;
3409
3410 case 'trim' :
3411
3412 parsed_variable = parsed_variable.trim();
3413 esc_html = false;
3414 break;
3415
3416 case 'slug' :
3417
3418 parsed_variable = this.get_slug(parsed_variable);
3419 break;
3420
3421 case 'name_prefix' :
3422 case 'name_first' :
3423 case 'name_middle' :
3424 case 'name_last' :
3425 case 'name_suffix' :
3426
3427 parsed_variable = this.get_full_name_components(parsed_variable)[parse_variable];
3428 break;
3429 }
3430 }
3431
3432 break;
3433
3434 case 'pow' :
3435
3436 // Base
3437 var parse_variables_process_return = this.parse_variables_process(variable_attribute_array[0], section_repeatable_index, calc_type, field_to, field_part, calc_register, section_id, depth);
3438 variable_dependencies_merge(parse_variables_process_return);
3439 var base = this.calc_string(parse_variables_process_return.output, parse_string_original, decimals, field_to_original, field_part);
3440
3441 // Exponent
3442 var parse_variables_process_return = this.parse_variables_process(variable_attribute_array[1], section_repeatable_index, calc_type, field_to, field_part, calc_register, section_id, depth);
3443 variable_dependencies_merge(parse_variables_process_return);
3444 var exponent = this.calc_string(parse_variables_process_return.output, parse_string_original, decimals, field_to_original, field_part);
3445
3446 parsed_variable = Math.pow(base, exponent);
3447
3448 break;
3449
3450 case 'round' :
3451
3452 var parse_variables_process_return = this.parse_variables_process(variable_attribute_array[1], section_repeatable_index, calc_type, field_to, field_part, calc_register, section_id, depth);
3453 var decimals = this.get_number(parse_variables_process_return.output);
3454 var parse_variables_process_return = this.parse_variables_process(variable_attribute_array[0], section_repeatable_index, calc_type, field_to, field_part, calc_register, section_id, depth);
3455 variable_dependencies_merge(parse_variables_process_return);
3456 parsed_variable = this.calc_string(parse_variables_process_return.output, parse_string_original, decimals, field_to_original, field_part);
3457
3458 break;
3459
3460 case 'min' :
3461 case 'max' :
3462
3463 var values = [];
3464
3465 // If we can only find one parameter, try splitting that up by comma
3466 if(variable_attribute_array.length === 1) {
3467
3468 var parse_variables_process_return = this.parse_variables_process(variable_attribute_array[0], section_repeatable_index, calc_type, field_to, field_part, calc_register, section_id, depth);
3469 variable_dependencies_merge(parse_variables_process_return);
3470 variable_attribute_array = this.string_to_attributes(parse_variables_process_return.output);
3471 }
3472
3473 for(var variable_attribute_array_index in variable_attribute_array) {
3474
3475 if(!variable_attribute_array.hasOwnProperty(variable_attribute_array_index)) { continue; }
3476
3477 var parse_variables_process_return = this.parse_variables_process(variable_attribute_array[variable_attribute_array_index], section_repeatable_index, calc_type, field_to, field_part, calc_register, section_id, depth);
3478 variable_dependencies_merge(parse_variables_process_return);
3479 var number_input = this.calc_string(parse_variables_process_return.output, parse_string_original, false, field_to_original, field_part);
3480 values.push(number_input);
3481 }
3482
3483 parsed_variable = (values.length > 0) ? ((parse_variable == 'min') ? Math.min(...values) : Math.max(...values)) : 0;
3484
3485 break;
3486
3487 case 'avg' :
3488
3489 var parsed_variable_total = 0;
3490
3491 // If we can only find one parameter, try splitting that up by comma
3492 if(variable_attribute_array.length === 1) {
3493
3494 var parse_variables_process_return = this.parse_variables_process(variable_attribute_array[0], section_repeatable_index, calc_type, field_to, field_part, calc_register, section_id, depth);
3495 variable_dependencies_merge(parse_variables_process_return);
3496 variable_attribute_array = this.string_to_attributes(parse_variables_process_return.output);
3497 }
3498
3499 // Run through each attribute and add it to total
3500 for(var variable_attribute_array_index in variable_attribute_array) {
3501
3502 if(!variable_attribute_array.hasOwnProperty(variable_attribute_array_index)) { continue; }
3503
3504 var parse_variables_process_return = this.parse_variables_process(variable_attribute_array[variable_attribute_array_index], section_repeatable_index, calc_type, field_to, field_part, calc_register, section_id, depth);
3505 variable_dependencies_merge(parse_variables_process_return);
3506 var number_input = this.calc_string(parse_variables_process_return.output, parse_string_original, false, field_to_original, field_part);
3507 parsed_variable_total += number_input;
3508 }
3509
3510 // Work out average
3511 parsed_variable = parsed_variable_total / variable_attribute_array.length;
3512
3513 break;
3514 }
3515
3516 // Client-side parse variable resolver registry
3517 //
3518 // Allow add-ons to resolve their own parameterized parse variables
3519 // in the browser. Runs after the core switch so registered handlers
3520 // can resolve variables that are not handled in core.
3521 if(
3522 (typeof $.WS_Form.parse_variable_handlers === 'object') &&
3523 (typeof $.WS_Form.parse_variable_handlers[parse_variable] === 'function')
3524 ) {
3525
3526 parsed_variable = $.WS_Form.parse_variable_handlers[parse_variable].call(
3527
3528 this,
3529 variable_attribute_array,
3530 {
3531 ws_form: this,
3532 section_repeatable_index: section_repeatable_index,
3533 field_to: field_to,
3534 field_part: field_part,
3535 section_id: section_id,
3536 parse_variable: parse_variable,
3537 parse_variable_full: parse_variable_full,
3538 current_value: parsed_variable
3539 }
3540 );
3541 }
3542
3543 // Assign value
3544 if(parsed_variable !== false) {
3545
3546 // HTML encode?
3547 if(esc_html) {
3548
3549 parsed_variable = this.esc_html(parsed_variable);
3550 }
3551
3552 if(parse_variable_single_parse) {
3553
3554 variables_single_parse[parse_variable_full.substring(1)] = parsed_variable;
3555
3556 } else {
3557
3558 variables[parse_variable_full.substring(1)] = parsed_variable;
3559 }
3560 }
3561
3562 } while (variable_index_of !== -1);
3563
3564 // Parse function
3565 parse_string = this.mask_parse(parse_string, variables);
3566 }
3567 }
3568 }
3569
3570 // Form
3571 if(parse_string.indexOf('form') != -1) {
3572
3573 variables.form_id = this.form_id;
3574 variables.form_instance_id = this.form_instance_id;
3575 variables.form_obj_id = this.form_obj_id;
3576 variables.form_label = this.form.label;
3577 variables.form_checksum = this.form.published_checksum;
3578 variables.form_framework = this.framework.name;
3579 }
3580
3581 // Section
3582 if(parse_string.indexOf('section') != -1) {
3583
3584 variables.section_row_index = section_repeatable_index;
3585
3586 // Get section ID
3587 var section_row_number = 1;
3588
3589 // Get section row number
3590 if(
3591 (section_id === false) &&
3592 (field_to !== false) &&
3593 (typeof field_to.section_id !== 'undefined') &&
3594 (parseInt(field_to.section_id, 10) > 0)
3595 ) {
3596
3597 section_id = parseInt(field_to.section_id, 10);
3598 }
3599
3600 if(typeof this.section_repeatable_indexes['section_' + section_id] === 'object') {
3601
3602 section_row_number = this.section_repeatable_indexes['section_' + section_id].indexOf(section_repeatable_index.toString()) + 1;
3603 if(section_row_number <= 0) { section_row_number = 1; }
3604 }
3605
3606 variables.section_row_number = section_row_number;
3607 }
3608
3609 // Submit
3610 if(parse_string.indexOf('submit') != -1) {
3611
3612 variables.submit_hash = this.hash;
3613 }
3614
3615 // Client
3616 if(parse_string.indexOf('client') != -1) {
3617
3618 var client_date_time = new Date();
3619
3620 variables.client_time = ((typeof this.date_format === 'function') ? this.date_format(client_date_time, ws_form_settings.time_format) : '');
3621 variables.client_date = ((typeof this.date_format === 'function') ? this.date_format(client_date_time, ws_form_settings.date_format) : '');
3622
3623 // Browser language (navigator.language); BCP47 vs POSIX aligned with WS_Form_Common PHP helpers.
3624 var nav_lang = '';
3625 if((typeof navigator !== 'undefined') && (typeof navigator.language === 'string')) {
3626
3627 nav_lang = navigator.language.trim();
3628 }
3629 var client_locale = $.WS_Form.language_tag_to_locale(nav_lang);
3630 variables.client_locale = client_locale;
3631 variables.client_language = client_locale ? $.WS_Form.locale_to_language_tag(client_locale) : '';
3632 variables.client_locale_language_code = $.WS_Form.get_language_code(client_locale);
3633 variables.client_locale_country_code = $.WS_Form.get_country_code(client_locale);
3634 }
3635
3636 // Seconds
3637 if(parse_string.indexOf('seconds_epoch_midnight') != -1) {
3638
3639 var now = new Date();
3640 now.setHours(0,0,0,0);
3641
3642 variables.seconds_epoch_midnight = Math.round(now.getTime() / 1000);
3643 }
3644 if(parse_string.indexOf('seconds_epoch') != -1) {
3645
3646 var now = new Date();
3647
3648 variables.seconds_epoch = Math.round(now.getTime() / 1000);
3649 }
3650
3651
3652 // Parse until no more changes made
3653 var parse_string_before = parse_string;
3654 parse_string = this.mask_parse(parse_string, variables);
3655 parse_string = this.mask_parse(parse_string, variables_single_parse, true);
3656 if(
3657 (parse_string !== parse_string_before) &&
3658 (parse_string.indexOf('#') !== -1)
3659 ) {
3660 var parse_variables_process_return = this.parse_variables_process(parse_string, section_repeatable_index, calc_type, field_to, field_part, calc_register, section_id, depth + 1, parse_string_original, field_to_original);
3661 parse_string = parse_variables_process_return.output;
3662
3663 variable_dependencies_merge(parse_variables_process_return);
3664 }
3665
3666 var return_object = {
3667
3668 'output' : parse_string,
3669 'groups' : variable_groups.filter(function(value, index, self) { return self.indexOf(value) === index; }),
3670 'sections' : variable_sections.filter(function(value, index, self) { return self.indexOf(value) === index; }),
3671 'fields' : variable_fields.filter(function(value, index, self) { return self.indexOf(value) === index; }),
3672 'dynamic' : variable_dynamic
3673 };
3674
3675 return return_object;
3676 }
3677
3678 // Get data grid column values
3679 $.WS_Form.prototype.get_data_grid_column = function(parsed_variable, field, column) {
3680
3681 if(parsed_variable.length === 0) { return parsed_variable; }
3682
3683 var parsed_variable_return = [];
3684
3685 var meta_key_prefix = field.type;
3686
3687 switch(meta_key_prefix) {
3688
3689 case 'select' :
3690 case 'checkbox' :
3691 case 'radio' :
3692
3693 break;
3694
3695 case 'price_select' :
3696
3697 var meta_key_prefix = 'select_price';
3698 break;
3699
3700 case 'price_checkbox' :
3701
3702 var meta_key_prefix = 'checkbox_price';
3703 break;
3704
3705 case 'price_radio' :
3706
3707 var meta_key_prefix = 'radio_price';
3708 break;
3709
3710 default :
3711
3712 return parsed_variable;
3713 }
3714
3715 var meta_key = 'data_grid_' + meta_key_prefix;
3716
3717 // Get column mappings
3718 var column_mapping_value_id = this.get_object_meta_value(field, meta_key_prefix + '_field_value', '0');
3719 var column_mapping_label_id = this.get_object_meta_value(field, meta_key_prefix + '_field_label', '0');
3720 var column_mapping_price_id = this.get_object_meta_value(field, meta_key_prefix + '_field_price', '0');
3721 var column_mapping_parse_variable_id = this.get_object_meta_value(field, meta_key_prefix + '_field_parse_variable', '0');
3722 var column_mapping_wc_id = this.get_object_meta_value(field, meta_key_prefix + '_field_wc', '0');
3723
3724 // Get data grid
3725 var data_grid = this.get_object_meta_value(field, meta_key, false);
3726 if(data_grid === false) { return parsed_variable; }
3727
3728 // Check for columns and groups
3729 if(
3730 (typeof data_grid.columns !== 'object') ||
3731 (typeof data_grid.groups !== 'object')
3732 ) {
3733 return parsed_variable;
3734 }
3735
3736 var value_column_index = 0;
3737 var label_column_index = 0;
3738 var price_column_index = 0;
3739 var parse_variable_column_index = 0;
3740 var wc_column_index = 0;
3741
3742 var columns = data_grid.columns;
3743
3744 // Get column index value and check return_column_index in case it is a column label
3745 var return_column_index = parseInt(column, 10);
3746
3747 for(var column_index in columns) {
3748
3749 if(!columns.hasOwnProperty(column_index)) { continue; }
3750
3751 var column_single = columns[column_index];
3752
3753 if(
3754 (typeof column_single.id === 'undefined') ||
3755 (typeof column_single.label === 'undefined')
3756 ) {
3757 continue;
3758 }
3759
3760 // Get column mapping for value
3761 if(column_single.id == column_mapping_value_id) {
3762
3763 value_column_index = column_index;
3764 }
3765
3766 // Get column mapping for label
3767 if(column_single.id == column_mapping_label_id) {
3768
3769 label_column_index = column_index;
3770 }
3771
3772 // Get column mapping for price
3773 if(column_single.id == column_mapping_price_id) {
3774
3775 price_column_index = column_index;
3776 }
3777
3778 // Get column mapping for parse_variable
3779 if(column_single.id == column_mapping_parse_variable_id) {
3780
3781 parse_variable_column_index = column_index;
3782 }
3783
3784 // Get column mapping for wc
3785 if(column_single.id == column_mapping_wc_id) {
3786
3787 wc_column_index = column_index;
3788 }
3789
3790 // Check for column by label
3791 if(column_single.label === column) {
3792
3793 return_column_index = column_index;
3794 }
3795 }
3796
3797 // Check if value matches
3798 for(var parsed_variable_index in parsed_variable) {
3799
3800 if(!parsed_variable.hasOwnProperty(parsed_variable_index)) { continue; }
3801
3802 var parsed_variable_single = parsed_variable[parsed_variable_index];
3803
3804 // Process groups
3805 var groups = data_grid.groups;
3806 for(var group_index in groups) {
3807
3808 if(!groups.hasOwnProperty(group_index)) { continue; }
3809
3810 var group = groups[group_index];
3811
3812 // Get rows
3813 if(typeof group.rows === 'undefined') { continue; }
3814 var rows = group.rows;
3815
3816 // Process rows
3817 for(var row_index in rows) {
3818
3819 if(!rows.hasOwnProperty(row_index)) { continue; }
3820
3821 var row = rows[row_index];
3822
3823 // Get row data
3824 if(
3825 (row === null) ||
3826 (typeof row.data !== 'object')
3827 ) {
3828 continue;
3829 }
3830 var data = row.data;
3831
3832 // Clone row data
3833 var data_cloned = $.extend(true, [], data);
3834
3835 // Check value and return indexes exist
3836 if(typeof data_cloned[value_column_index] === 'undefined') { continue; }
3837 if(typeof data_cloned[return_column_index] === 'undefined') { continue; }
3838
3839 // Pre-parsing
3840 var mask_values_row = {
3841
3842 'data_grid_row_value': data_cloned[value_column_index],
3843 'data_grid_row_action_variable': '',
3844 'data_grid_row_label': ''
3845 };
3846
3847 // Label
3848 if(
3849 (label_column_index !== false) &&
3850 (typeof data_cloned[label_column_index] !== 'undefined')
3851 ) {
3852
3853 mask_values_row.data_grid_row_label = data_cloned[label_column_index];
3854 }
3855
3856 // Parse Variable
3857 if(
3858 (parse_variable_column_index !== false) &&
3859 (typeof data_cloned[parse_variable_column_index] !== 'undefined')
3860 ) {
3861
3862 mask_values_row.data_grid_row_action_variable = data_cloned[parse_variable_column_index];
3863 }
3864
3865 // Parse columns
3866 for(var data_index in data_cloned) {
3867
3868 if(!data_cloned.hasOwnProperty(data_index)) { continue; }
3869
3870 if(typeof data_cloned[data_index] === 'number') { data_cloned[data_index] = data_cloned[data_index].toString(); }
3871
3872 data_cloned[data_index] = this.mask_parse(data_cloned[data_index], mask_values_row);
3873 }
3874
3875 // Check for value column match
3876 if(parsed_variable_single === data_cloned[value_column_index].toString()) {
3877
3878 // Add data to default value
3879 if(typeof row.data[return_column_index] === 'undefined') { continue; }
3880 parsed_variable_return.push(row.data[return_column_index]);
3881 }
3882 }
3883 }
3884 }
3885
3886 return parsed_variable_return;
3887 }
3888
3889 // WPAutoP
3890 $.WS_Form.prototype.wpautop_parse_variable = function(field, field_type_config) {
3891
3892 // Check for wpautop do not process
3893 if(this.get_object_meta_value(field, 'wpautop_do_not_process', '') == 'on') { return false; }
3894
3895 // Meta wpautop
3896 var wpautop_parse_variable = (typeof field_type_config.wpautop_parse_variable !== 'undefined') ? field_type_config.wpautop_parse_variable : false;
3897
3898 if(typeof wpautop_parse_variable === 'object') {
3899
3900 var condition_output = false;
3901
3902 for(var wpautop_parse_variable_index in wpautop_parse_variable) {
3903
3904 if(!wpautop_parse_variable.hasOwnProperty(wpautop_parse_variable_index)) { continue; }
3905
3906 var condition = wpautop_parse_variable[wpautop_parse_variable_index];
3907
3908 if(this.get_object_meta_value(field, condition.meta_key, '') === condition.meta_value) {
3909
3910 condition_output = true;
3911 }
3912 }
3913
3914 wpautop_parse_variable = condition_output;
3915 }
3916
3917 return wpautop_parse_variable;
3918 }
3919
3920 $.WS_Form.prototype.wpautop = function(i, br) {
3921
3922 if(typeof i !== 'string') { return ''; }
3923 if(typeof br === 'undefined') { br = true; }
3924
3925 var pre_tags = new Map;
3926 if(i.trim() === '') { return ''; }
3927
3928 i = i + "\n";
3929 if(i.indexOf( '<pre' ) > -1) {
3930
3931 var i_parts = i.split( '</pre>' );
3932 var last_i = i_parts.pop()
3933 i = '';
3934 i_parts.forEach(function(i_part, index) {
3935
3936 var start = i_part.indexOf( '<pre' );
3937
3938 if(start === -1) {
3939
3940 i += i_part;
3941 return;
3942 }
3943
3944 var name = "<pre wp-pre-tag-" + index + "></pre>";
3945 pre_tags[name] = i_part.slice(start) + '</pre>';
3946 i += i_part.substring(0, start) + name;
3947 });
3948
3949 i += last_i;
3950 }
3951
3952 i = i.replace(/<br \/>\s*<br \/>/, "\n\n");
3953
3954 var allblocks = '(?:table|thead|tfoot|caption|col|colgroup|tbody|tr|td|th|div|dl|dd|dt|ul|ol|li|pre|form|map|area|blockquote|address|math|style|p|h[1-6]|hr|fieldset|legend|section|article|aside|hgroup|header|footer|nav|figure|figcaption|details|menu|summary)';
3955 i = i.replace( new RegExp('(<' + allblocks + '[^>]*>)', 'gmi'), "\n$1");
3956 i = i.replace( new RegExp('(</' + allblocks + '>)', 'gmi'), "$1\n\n");
3957 i = i.replace( /\r\n|\r/, "\n" )
3958
3959 if(i.indexOf( '<option' ) > -1) {
3960
3961 i = i.replace( /\s*<option'/gmi, '<option');
3962 i = i.replace( /<\/option>\s*/gmi, '</option>');
3963 }
3964
3965 if(i.indexOf('</object>') > -1) {
3966
3967 i = i.replace( /(<object[^>]*>)\s*/gmi, '$1');
3968 i = i.replace( /\s*<\/object>/gmi, '</object>' );
3969 i = i.replace( /\s*(<\/?(?:param|embed)[^>]*>)\s*/gmi, '$1');
3970 }
3971
3972 if(i.indexOf('<source') > -1 || i.indexOf('<track') > -1) {
3973 // no P/BR around source and track
3974 i = i.replace( /([<\[](?:audio|video)[^>\]]*[>\]])\s*/gmi, '$1');
3975 i = i.replace( /\s*([<\[]\/(?:audio|video)[>\]])/gmi, '$1');
3976 i = i.replace( /\s*(<(?:source|track)[^>]*>)\s*/gmi, '$1');
3977 }
3978
3979 i = i.replace(/\n\n+/gmi, "\n\n");
3980
3981 var is = i.split(/\n\s*\n/);
3982 i = '';
3983 is.forEach(function(tinkle) {
3984
3985 i += '<p>' + tinkle.replace( /^\s+|\s+$/g, '' ) + "</p>\n";
3986 });
3987
3988 i = i.replace(/<p>\s*<\/p>/gmi, '');
3989 i = i.replace(/<p>([^<]+)<\/(div|address|form)>/gmi, "<p>$1</p></$2>");
3990 i = i.replace(new RegExp('<p>\s*(</?' + allblocks + '[^>]*>)\s*</p>', 'gmi'), "$1", i); // don't i all over a tag
3991 i = i.replace(/<p>(<li.+?)<\/p>/gmi, "$1");
3992 i = i.replace(/<p><blockquote([^>]*)>/gmi, "<blockquote$1><p>");
3993 i = i.replace(/<\/blockquote><\/p>/gmi, '</p></blockquote>');
3994 i = i.replace(new RegExp('<p>\s*(</?' + allblocks + '[^>]*>)', 'gmi'), "$1");
3995 i = i.replace(new RegExp('(</?' + allblocks + '[^>]*>)\s*</p>', 'gmi'), "$1");
3996
3997 if(br) {
3998
3999 i = i.replace(/<(script|style)(?:.|\n)*?<\/\\1>/gmi, function ( matches ) {
4000
4001 return matches[0].replace( "\n", "<WPPreserveNewline />" );
4002 });
4003 i = i.replace(/(<br \/>)?\s*\n/gmi, "<br />\n");
4004 i = i.replace( '<WPPreserveNewline />', "\n" );
4005 }
4006
4007 i = i.replace(new RegExp('(</?' + allblocks + '[^>]*>)\s*<br />', 'gmi'), "$1");
4008 i = i.replace(/<br \/>(\s*<\/?(?:p|li|div|dl|dd|dt|th|pre|td|ul|ol)[^>]*>)/gmi, '$1');
4009 i = i.replace(/\n<\/p>$/gmi, '</p>');
4010
4011 if(Object.keys(pre_tags).length) {
4012
4013 i = i.replace( new RegExp( Object.keys( pre_tags ).join( '|' ), "gi" ), function (matched) {
4014 return pre_tags[matched];
4015 });
4016 }
4017
4018 return i;
4019 }
4020
4021 // Calculate string
4022 $.WS_Form.prototype.calc_string = function(calc_input, parse_string_original, decimals, field, field_part) {
4023
4024 try {
4025
4026 // Attempt to calculate
4027 var calc_output = new Function('return ' + calc_input)();
4028
4029 // Check for infinity
4030 if(calc_output === Infinity) {
4031
4032 // Convert infinity to 0
4033 calc_output = 0;
4034 }
4035
4036 // Clean up calc output (Ensure only numbers returned)
4037 return this.get_number(calc_output, 0, false, decimals);
4038
4039 } catch(e) {
4040
4041 // Handle calc error
4042 if(
4043 (typeof field === 'object') &&
4044 (typeof field.id !== 'undefined') &&
4045 (typeof field.label !== 'undefined')
4046 ) {
4047
4048 this.error('error_parse_variable_syntax_error_calc', field.id + ' | ' + this.esc_html(field.label) + (field_part ? ' | ' + this.esc_html(field_part) : '') + ' | ' + e.message + ': ' + this.esc_html(parse_string_original));
4049 }
4050
4051 return 0;
4052 }
4053 }
4054
4055 // Find closing string
4056 $.WS_Form.prototype.closing_string_index = function(parse_string, closing_string, opening_string, index) {
4057
4058 var depth = 1;
4059
4060 while(depth > 0) {
4061
4062 // Look for embedded if
4063 var opening_string_index = parse_string.indexOf(opening_string, index);
4064 var closing_string_index = parse_string.indexOf(closing_string, index);
4065
4066 // Embedded opening string
4067 if(
4068 (opening_string_index !== -1) &&
4069 (closing_string_index !== -1) &&
4070 (opening_string_index < closing_string_index)
4071 ) {
4072 index = opening_string_index + opening_string.length;
4073 depth++;
4074 continue;
4075 }
4076
4077 // Embedded closing string
4078 if(
4079 (closing_string_index !== -1) &&
4080 (depth > 1)
4081 ) {
4082 index = closing_string_index + closing_string.length;
4083 depth--;
4084 continue;
4085 }
4086
4087 // Associated closing string
4088 if(
4089 (closing_string_index !== -1) &&
4090 (depth === 1)
4091 ) {
4092 break;
4093 }
4094
4095 break;
4096 }
4097
4098 return closing_string_index;
4099 }
4100
4101 // Parse string to attributes
4102 $.WS_Form.prototype.string_to_attributes = function(input_string, separator) {
4103
4104 if(
4105 (typeof input_string !== 'string') ||
4106 (input_string == '')
4107 ) {
4108 return [];
4109 }
4110
4111 if(typeof separator !== 'string') { separator = ','; }
4112
4113 var bracket_index = 1;
4114 var input_string_index = 0;
4115 var skip_double_quotes = false;
4116 var attribute_single = '';
4117 var attribute_array = [];
4118
4119 // Replace non standard double quotes
4120 input_string.replace('', '"');
4121 input_string.replace('', '"');
4122
4123 // Get input string length
4124 var input_string_length = input_string.length;
4125
4126 while(input_string_index < input_string_length) {
4127
4128 // Get character
4129 var character = input_string[input_string_index];
4130
4131 // If end of string, break
4132 if(character === undefined) { break; }
4133
4134 if(
4135 (character === separator) &&
4136 (bracket_index === 1) &&
4137 !skip_double_quotes
4138 ) {
4139
4140 // Create attribute
4141 attribute_array.push(attribute_single);
4142 attribute_single = '';
4143
4144 // Jump to next character
4145 input_string_index++;
4146
4147 continue;
4148 }
4149
4150 // If double quotes that are not in another function
4151 if(
4152 (character === '"') &&
4153 (bracket_index === 1)
4154 ) {
4155
4156 // Clear attribute_single if start
4157 if(!skip_double_quotes) { attribute_single = ''; }
4158
4159 // Toggle skip double quotes
4160 skip_double_quotes = !skip_double_quotes;
4161
4162 // Jump to next character
4163 input_string_index++;
4164
4165 continue;
4166 }
4167
4168 // If not in double quotes, process brackets
4169 if(!skip_double_quotes) {
4170
4171 switch(character) {
4172
4173 case '(' : bracket_index++; break;
4174
4175 case ')' : bracket_index--; break;
4176 }
4177
4178 if(bracket_index === 0) { break; }
4179 }
4180
4181 // Add character to attribute_single
4182 attribute_single += character;
4183
4184 input_string_index++;
4185 }
4186
4187 attribute_array.push(attribute_single);
4188
4189 // Strip double quotes at the beginning and end of each attribute
4190 if(attribute_array.length) {
4191
4192 attribute_array = attribute_array.map(function(e) {
4193
4194 e = e.replace(/^"(.+(?="$))"$/, '$1');
4195 return e;
4196 });
4197 }
4198
4199 return attribute_array;
4200 }
4201
4202 $.WS_Form.prototype.get_bracket_finish_index = function(input_string) {
4203
4204 if(input_string === '') { return -1; }
4205
4206 // Replace non standard double quotes
4207 input_string.replace('', '"');
4208 input_string.replace('', '"');
4209
4210 // Look for closing bracket
4211 var bracket_index = 1;
4212 var input_string_index = 1; // Start at index 1 to avoid the already found opening bracket
4213 var skip_double_quotes = false;
4214
4215 while(
4216 (bracket_index > 0) ||
4217 (input_string_index < input_string.length)
4218 ) {
4219
4220 // Get character
4221 var character = input_string[input_string_index];
4222
4223 // If end of string, break
4224 if(character === undefined) { break; }
4225
4226 // If double quotes
4227 if(character === '"') {
4228
4229 // Toggle skip double quotes
4230 skip_double_quotes = !skip_double_quotes;
4231
4232 // Jump to next character
4233 input_string_index++;
4234
4235 continue;
4236 }
4237
4238 // If not in double quotes, process brackets
4239 if(!skip_double_quotes) {
4240
4241 switch(character) {
4242
4243 case '(' : bracket_index++; break;
4244
4245 case ')' : bracket_index--; break;
4246 }
4247
4248 if(bracket_index === 0) { break; }
4249 }
4250
4251 input_string_index++;
4252 }
4253
4254 if(bracket_index === 0) {
4255
4256 return input_string_index;
4257
4258 } else {
4259
4260 // Syntax error - Closing bracket not found
4261 this.error('error_parse_variable_syntax_error_bracket_closing', input_string, 'error-parse-variables');
4262 return -1;
4263 }
4264 }
4265
4266 // Parse variable error
4267 $.WS_Form.prototype.parse_variables_process_error = function(error_message) {
4268
4269 return {'output' : error_message, 'functions': [], 'groups': [], 'sections': [], 'fields': [], 'dynamic': false};
4270 }
4271
4272 // Get value from field
4273 $.WS_Form.prototype.get_field_value = function(field, section_repeatable_index, submit_array, column) {
4274
4275 var ws_this = this;
4276
4277 if(typeof section_repeatable_index === 'undefined') { section_repeatable_index = false; }
4278 if(typeof submit_array === 'undefined') { submit_array = false; }
4279 if(typeof column === 'undefined') { column = false; }
4280
4281 if(section_repeatable_index === false) {
4282
4283 // Not in a repeatable section
4284 var field_selector = '[name="' + this.esc_selector(ws_form_settings.field_prefix + field.id);
4285
4286 // If this is a select, checkbox or radio
4287 if(submit_array) { field_selector += '[]'; }
4288
4289 } else if(section_repeatable_index === true) {
4290
4291 // If we are getting from all repeatable sections
4292 var field_selector = '[name^="' + this.esc_selector(ws_form_settings.field_prefix + field.id) + '[';
4293
4294 } else {
4295
4296 // If we are getting from a specific repeatable section
4297 var field_selector = '[name="' + this.esc_selector(ws_form_settings.field_prefix + field.id + '[' + section_repeatable_index + ']');
4298
4299 // If this is a select, checkbox or radio
4300 if(submit_array) { field_selector += '[]'; }
4301 }
4302
4303 field_selector += '"]';
4304
4305 // Check field(s) exist
4306 if(!$(field_selector, this.form_canvas_obj).length) { return false; }
4307
4308 // Return values
4309 switch(field.type) {
4310
4311 case 'select' :
4312 case 'price_select' :
4313
4314 field_selector += ' option:selected';
4315 break;
4316
4317 case 'checkbox' :
4318 case 'radio' :
4319 case 'price_checkbox' :
4320 case 'price_radio' :
4321
4322 field_selector += ':checked';
4323 break;
4324 }
4325
4326 var return_array = [];
4327
4328 $(field_selector, this.form_canvas_obj).each(function() {
4329
4330 switch(field.type) {
4331
4332 case 'price_select' :
4333
4334 if(column === false) {
4335
4336 return_array.push($(this).attr('data-price'));
4337
4338 } else {
4339
4340 return_array.push($(this).val());
4341 }
4342 break;
4343
4344 case 'price_checkbox' :
4345 case 'price_radio' :
4346
4347 if(column === false) {
4348
4349 return_array.push($(this).attr('data-price'));
4350
4351 } else {
4352
4353 return_array.push($(this).val());
4354 }
4355 break;
4356
4357 case 'mediacapture' :
4358
4359 var value = $(this).val();
4360
4361 if(value) {
4362
4363 var value_decoded = JSON.parse(value);
4364
4365 if(
4366 (typeof value_decoded === 'object') &&
4367 value_decoded.filename
4368 ) {
4369 return_array.push(value_decoded.filename);
4370 }
4371 }
4372
4373 break;
4374
4375 case 'file' :
4376
4377 var files = [];
4378
4379 switch($(this).attr('data-file-type')) {
4380
4381 case 'dropzonejs' :
4382
4383 var obj_wrapper = $(this).closest('[data-type="file"]');
4384
4385 if(obj_wrapper) {
4386
4387 var dropzone = $('.dropzone', obj_wrapper)[0].dropzone;
4388
4389 if(dropzone && dropzone.files) {
4390
4391 var files = dropzone.files;
4392 }
4393 }
4394
4395 break;
4396
4397 default :
4398
4399 var files = $(this)[0].files;
4400 }
4401
4402 var filenames = [];
4403
4404 for(var file_index in files) {
4405
4406 if(!files.hasOwnProperty(file_index)) { continue; }
4407
4408 var file = files[file_index];
4409
4410 filenames.push(file.name);
4411 }
4412
4413 return_array.push(filenames.join(','));
4414
4415 break;
4416
4417 case 'googlemap' :
4418
4419 var value_json = $(this).val();
4420 if(!value_json) { break; }
4421
4422 try {
4423
4424 var value = JSON.parse(value_json);
4425
4426 } catch(e) { break; }
4427
4428 if(
4429 (typeof value.lat !== 'undefined') &&
4430 (typeof value.lng !== 'undefined')
4431 ) {
4432
4433 return_array.push(value.lat + ',' + value.lng);
4434 }
4435
4436 break;
4437
4438 default :
4439
4440 var value = $(this).val();
4441
4442 if(ws_this.is_iterable(value)) {
4443
4444 return_array.push(...value);
4445
4446 } else {
4447
4448 return_array.push(value);
4449 }
4450 }
4451 });
4452
4453 return return_array;
4454 }
4455
4456 // Get data source
4457 $.WS_Form.prototype.get_data_source = function(field) {
4458
4459 var data_source_return = { default_value: [] };
4460
4461 // Get field config
4462 var field = this.field_data_cache[field.id];
4463 if(typeof $.WS_Form.field_type_cache[field.type] === 'undefined') { return false; }
4464 var field_type_config = $.WS_Form.field_type_cache[field.type];
4465
4466 // Get data source
4467 if(typeof field_type_config.data_source === 'undefined') { return false; }
4468 var data_source = field_type_config.data_source;
4469
4470 // Get data source type
4471 if(typeof data_source.type === 'undefined') { return false; }
4472 data_source_return.type = data_source.type;
4473
4474 // Get data source by type
4475 switch(data_source_return.type) {
4476
4477 case 'data_grid' :
4478
4479 // Get data source meta key
4480 if(typeof data_source.id === 'undefined') { return false; }
4481 data_source_return.meta_key_data_grid = data_source.id;
4482
4483 // Get data source value key
4484 data_source_return.meta_key_value_column = (typeof field_type_config.datagrid_column_value === 'undefined' ? field_type_config.datagrid_column_value : false);
4485
4486 // Get data grid
4487 data_source_return.data_grid = this.get_object_meta_value(field, data_source_return.meta_key_data_grid, false);
4488
4489 // Get value column
4490 data_source_return.value_column_id = this.get_object_meta_value(field, data_source_return.meta_key_value_column, false);
4491
4492 // Run through the data grid columns until we find the ID
4493 var data_columns = (typeof data_source_return.data_grid.columns) ? data_source_return.data_grid.columns : [];
4494
4495 data_source_return.value_column_index = false;
4496 for(var data_columns_index in data_columns) {
4497
4498 if(!data_columns.hasOwnProperty(data_columns_index)) { continue; }
4499
4500 var data_column = data_columns[data_columns_index];
4501
4502 if(typeof data_column.id === 'undefined') { continue; }
4503
4504 // Match found, store in cache
4505 if(data_column.id == data_source_return.value_column_id) { data_source_return.value_column_index = data_columns_index; break; }
4506 }
4507
4508 // Build default value
4509 if(
4510 (data_source_return.value_column_id !== false) &&
4511 (typeof data_source_return.data_grid.groups !== 'undefined')
4512 ) {
4513
4514 // Process groups
4515 var groups = data_source_return.data_grid.groups;
4516 for(var group_index in groups) {
4517
4518 if(!groups.hasOwnProperty(group_index)) { continue; }
4519
4520 var group = groups[group_index];
4521
4522 // Get rows
4523 if(typeof group.rows === 'undefined') { continue; }
4524 var rows = group.rows;
4525
4526 // Process rows
4527 for(var row_index in rows) {
4528
4529 if(!rows.hasOwnProperty(row_index)) { continue; }
4530
4531 var row = rows[row_index];
4532
4533 if(row === null) { continue; }
4534
4535 // Process default row
4536 var row_default = (typeof row.default !== 'undefined') ? (row.default === 'on') : false;
4537 if(row_default) {
4538
4539 if(typeof row.data === 'undefined') { continue; }
4540 if(typeof row.data[data_source_return.value_column_index] === 'undefined') { continue; }
4541
4542 // Add data to default value
4543 data_source_return.default_value.push(row.data[data_source_return.value_column_index]);
4544 }
4545 }
4546 }
4547 }
4548
4549 break;
4550 }
4551
4552 return data_source_return;
4553 }
4554
4555 // Get query variable
4556 $.WS_Form.prototype.get_query_var = function(query_var, default_value) {
4557
4558 if(!default_value) { default_value = ''; }
4559
4560 var url = window.location.href;
4561 if(!url) { return default_value; }
4562
4563 try {
4564
4565 query_var = query_var.replace(/[\[\]]/g, "\\$&");
4566 var regex = new RegExp("[?&]" + query_var + "(=([^&#]*)|&|#|$)");
4567 var results = regex.exec(url);
4568
4569 if (!results) return default_value;
4570 if (!results[2]) return default_value;
4571
4572 try {
4573
4574 return decodeURIComponent(results[2].replace(/\+/g, " "));
4575
4576 } catch(e) {
4577
4578 return default_value;
4579 }
4580
4581 } catch(e) {
4582
4583 return default_value;
4584 }
4585 }
4586
4587 // Set object meta
4588 $.WS_Form.prototype.set_object_meta_value = function (object, key, value) {
4589
4590 if(typeof object === 'undefined') { return value; }
4591
4592 if(typeof object.meta === 'undefined') { return value; }
4593
4594 // Set value
4595 object.meta[key] = value;
4596 }
4597
4598 // Get column class array
4599 $.WS_Form.prototype.column_class_array = function(object, type) {
4600
4601 if(typeof type === 'undefined') { type = 'breakpoint'; }
4602
4603 var column_class_array = [];
4604
4605 // Get current framework breakpoints
4606 var framework_breakpoints = this.framework.breakpoints;
4607
4608 // Get class masks
4609 var column_class = this.framework.columns.column_class;
4610 var offset_class = this.framework.columns.offset_class;
4611
4612 var column_size_value_old = 0;
4613 var offset_value_old = 0;
4614
4615 for(var breakpoint in framework_breakpoints) {
4616
4617 if(!framework_breakpoints.hasOwnProperty(breakpoint)) { continue; }
4618
4619 var column_framework = framework_breakpoints[breakpoint];
4620
4621 var column_size_value = this.get_object_meta_value(object, type + '_size_' + breakpoint, '');
4622 if(column_size_value == '') { column_size_value = '0'; }
4623 column_size_value = parseInt(column_size_value, 10);
4624
4625 // If a framework breakpoint size is not found, but column_size_default is set, then use configured or specified value as size (Used for Bootstrap 4 that does not fallback to full column width)
4626 if(column_size_value == 0) {
4627
4628 if(typeof column_framework.column_size_default !== 'undefined') {
4629
4630 switch(column_framework.column_size_default) {
4631
4632 case 'column_count' :
4633
4634 column_size_value = parseInt($.WS_Form.settings_plugin.framework_column_count, 10);
4635 break;
4636
4637 default :
4638
4639 column_size_value = parseInt(column_framework.column_size_default, 10);
4640 }
4641 }
4642
4643 } else {
4644
4645 column_size_value = parseInt(column_size_value, 10);
4646 }
4647
4648 // Process breakpoint (only if it differs from the previous breakpoint size, otherwise it just inheris the size from the previous breakpoint)
4649 if((column_size_value > 0) && (column_size_value != column_size_value_old)) {
4650
4651 // Get ID for parsing
4652 var id = column_framework.id;
4653
4654 // Build mask values for parser
4655 var mask_values = {
4656
4657 'id': id,
4658 'size_word': (typeof this.number_to_word[column_size_value] === 'undefined') ? column_size_value : this.number_to_word[column_size_value],
4659 'size': column_size_value
4660 };
4661
4662 // Check for breakpoint specific column class mask
4663 if(typeof column_framework.column_class !== 'undefined') {
4664
4665 var column_class_single = column_framework.column_class;
4666
4667 } else {
4668
4669 var column_class_single = column_class;
4670 }
4671
4672 // Get single class
4673 var class_single = this.mask_parse(column_class_single, mask_values);
4674
4675 // Push to class array
4676 column_class_array.push(class_single);
4677
4678 // Remember framework size
4679 column_size_value_old = column_size_value;
4680 }
4681
4682 // Offset
4683 var offset_value = this.get_object_meta_value(object, type + '_offset_' + breakpoint, '');
4684
4685 // Process breakpoint (only if it differs from the previous breakpoint offset, otherwise it just inheris the offset from the previous breakpoint)
4686 var offset_found = false;
4687 if((offset_value !== '') && (offset_value != offset_value_old)) {
4688
4689 // Get ID for parsing
4690 var id = column_framework.id;
4691
4692 // Build mask values for parser
4693 var mask_values = {
4694
4695 'id': id,
4696 'offset_word': (typeof this.number_to_word[offset_value] === 'undefined') ? offset_value : this.number_to_word[offset_value],
4697 'offset': offset_value
4698 };
4699
4700 // Check for breakpoint specific column class mask
4701 if(typeof column_framework.offset_class !== 'undefined') {
4702
4703 var offset_class_single = column_framework.offset_class;
4704
4705 } else {
4706
4707 var offset_class_single = offset_class;
4708 }
4709
4710 // Get single class
4711 var class_single = this.mask_parse(offset_class_single, mask_values);
4712
4713 // Push to class array
4714 column_class_array.push(class_single);
4715
4716 // Remember framework size
4717 offset_value_old = offset_value;
4718
4719 offset_found = true;
4720 }
4721
4722 if(offset_found) { column_class_array.push('wsf-has-offset'); }
4723 }
4724
4725 return column_class_array;
4726 }
4727
4728 // Render field classes (add/remove)
4729 $.WS_Form.prototype.column_classes_render = function(obj, column, add) {
4730
4731 // Set wrapper height to auto
4732 obj.closest('.wsf-sections').css('min-height', 'auto');
4733 obj.closest('.wsf-fields').css('min-height', 'auto');
4734
4735 if(typeof add === 'undefined') { add = true; }
4736
4737 // Get column class array before change
4738 var class_array = this.column_class_array(column);
4739
4740 // Add/Remove old classes
4741 for(var i=0; i < class_array.length; i++) {
4742
4743 if(add) {
4744
4745 obj.addClass(class_array[i]);
4746
4747 } else {
4748
4749 obj.removeClass(class_array[i]);
4750 }
4751 }
4752 }
4753
4754 // Mask parse
4755 $.WS_Form.prototype.mask_parse = function(mask, lookups, single_parse) {
4756
4757 if(typeof mask !== 'string') { return ''; }
4758 if(typeof single_parse === 'undefined') { single_parse = false; }
4759
4760 // Sort variables descending by key
4761 var lookups_sorted = [];
4762 var keys = Object.keys(lookups);
4763 keys.sort(function(variable_a, variable_b) {
4764
4765 if(variable_a === variable_b) { return 0; }
4766
4767 var variable_a_is_function = (variable_a.indexOf('(') !== -1);
4768 var variable_b_is_function = (variable_b.indexOf('(') !== -1);
4769
4770 if(variable_a_is_function && variable_b_is_function) {
4771
4772 return variable_a < variable_b ? 1 : -1;
4773 }
4774
4775 if(
4776 (!variable_a_is_function && variable_b_is_function) ||
4777 (variable_a_is_function && !variable_b_is_function)
4778 ) {
4779
4780 return variable_a_is_function < variable_b_is_function ? 1 : -1;
4781 }
4782
4783 return variable_a < variable_b ? 1 : -1;
4784 });
4785 for(var i = 0; i < keys.length; i++) { lookups_sorted[keys[i]] = lookups[keys[i]]; }
4786 lookups = lookups_sorted;
4787
4788 // Process mask_lookups array
4789 for(var key in lookups) {
4790
4791 if(!lookups.hasOwnProperty(key)) { continue; }
4792
4793 var value = lookups[key];
4794
4795 if(single_parse) {
4796
4797 mask = mask.replace('#' + key, value);
4798
4799 } else {
4800
4801 mask = this.replace_all(mask, '#' + key, value);
4802 }
4803 }
4804
4805 return mask;
4806 }
4807
4808 // Get localized language string
4809 $.WS_Form.prototype.language = function(id, value, esc_html, bypass_error) {
4810
4811 if(typeof value === 'undefined') { value = false; }
4812 if(typeof esc_html === 'undefined') { esc_html = true; }
4813 if(typeof bypass_error === 'undefined') { bypass_error = false; }
4814
4815 var language_string = '';
4816 var return_string = '';
4817
4818 if(id === 'error_language') {
4819
4820 language_string = 'Language reference not found: %s';
4821
4822 } else {
4823
4824 if($.WS_Form.settings_form !== null) {
4825
4826 if(typeof $.WS_Form.settings_form.language !== 'undefined') {
4827
4828 if(typeof $.WS_Form.settings_form.language[id] !== 'undefined') {
4829
4830 var language_string = $.WS_Form.settings_form.language[id];
4831 }
4832 }
4833 }
4834 }
4835
4836 if(language_string == '') {
4837
4838 if(
4839 (id !== 'error_language') &&
4840 !bypass_error
4841 ) {
4842
4843 this.error('error_language', id);
4844 }
4845
4846 } else {
4847
4848 if(value !== false) { language_string = this.replace_all(language_string, '%s', value); }
4849
4850 return_string = esc_html ? this.esc_html(language_string) : language_string;
4851 }
4852
4853 if(return_string == '') {
4854
4855 return_string = (value == '') ? '[LANGUAGE NOT FOUND: ' + id + ']' : value;
4856 }
4857
4858 return return_string;
4859 }
4860
4861 // Set cookie
4862 $.WS_Form.prototype.cookie_set = function(cookie_name, cookie_value, cookie_expiry, bind_to_form_id) {
4863
4864 if(typeof cookie_expiry === 'undefined') { cookie_expiry = true; }
4865 if(typeof bind_to_form_id === 'undefined') { bind_to_form_id = true; }
4866
4867 // Read cookie prefix
4868 var cookie_prefix = this.get_object_value($.WS_Form.settings_plugin, 'cookie_prefix');
4869 if(!cookie_prefix) { return false; }
4870
4871 // Check for blank value
4872 if(cookie_value == '') {
4873
4874 // Check if cookie already exists
4875 var cookie_existing_value = this.cookie_get(cookie_name, '');
4876
4877 if(cookie_existing_value == '') { return false; }
4878
4879 // Build negative expiry to clear cookie
4880 var d = new Date();
4881 d.setTime(d.getTime() - (86400 * 1000));
4882 var expires = 'expires=' + d.toUTCString() + ';';
4883
4884 } else {
4885
4886 // Read cookie timeout
4887 if(cookie_expiry) {
4888
4889 // Get cookie timeout value
4890 var cookie_timeout = this.get_object_value($.WS_Form.settings_plugin, 'cookie_timeout');
4891 if(!cookie_timeout) { return false; }
4892
4893 // Build expiry
4894 var d = new Date();
4895 d.setTime(d.getTime() + (cookie_timeout * 1000));
4896 var expires = ' expires=' + d.toUTCString() + ';';
4897
4898 } else {
4899
4900 var expires = '';
4901 }
4902 }
4903
4904 // Set document.cookie
4905 var cookie_string = cookie_prefix + '_' + (bind_to_form_id ? (this.form_id + '_') : '') + cookie_name + "=" + cookie_value + ";" + expires + " path=/; SameSite=Strict; Secure";
4906 document.cookie = cookie_string;
4907
4908 return true;
4909 }
4910
4911 // Field parameter with language fallback
4912 $.WS_Form.prototype.field_param_language = function(field, param, language_id) {
4913
4914 // Get the parameter value from the field
4915 var param_value = this.get_object_meta_value(field, param, '');
4916
4917 // If parameter has a value, return it
4918 if(param_value !== '') {
4919 return param_value;
4920 }
4921
4922 // Otherwise, return the language fallback
4923 return this.language(language_id);
4924 }
4925
4926 // Get cookie
4927 $.WS_Form.prototype.cookie_get = function(cookie_name, default_value, bind_to_form_id) {
4928
4929 if(typeof bind_to_form_id === 'undefined') { bind_to_form_id = true; }
4930
4931 // Read cookie configuration
4932 var cookie_prefix = this.get_object_value($.WS_Form.settings_plugin, 'cookie_prefix');
4933 if(!cookie_prefix) { return default_value; }
4934
4935 // Build name
4936 cookie_name = cookie_prefix + '_' + (bind_to_form_id ? (this.form_id + '_') : '') + cookie_name;
4937
4938 // Return value
4939 return this.cookie_get_raw(cookie_name, default_value);
4940 }
4941
4942 // Get cookie raw
4943 $.WS_Form.prototype.cookie_get_raw = function(cookie_name, default_value) {
4944
4945 // Check default value
4946 if(typeof default_value === 'undefined') { default_value = ''; }
4947
4948 // Check cookie name and docuent.cookie
4949 if(
4950 (cookie_name === '') ||
4951 !document.cookie ||
4952 (typeof document.cookie !== 'string')
4953 ) {
4954 return default_value;
4955 }
4956
4957 // Split cookie by semi-colon
4958 var cookie_elements = document.cookie.split(';');
4959
4960 // Add = to cookie_name for searching
4961 cookie_name += '=';
4962
4963 // Run through each cookie element
4964 for(var cookie_element_index = 0; cookie_element_index < cookie_elements.length; cookie_element_index++) {
4965
4966 // Get cookie element
4967 var cookie_element = cookie_elements[cookie_element_index];
4968
4969 // Skip spaces
4970 while(cookie_element.charAt(0) == ' ') {
4971
4972 cookie_element = cookie_element.substring(1);
4973 }
4974
4975 if(cookie_element.indexOf(cookie_name) == 0) {
4976
4977 // Get cookie value
4978 var cookie_value = cookie_element.substring(cookie_name.length, cookie_element.length);
4979
4980 // Attempt to URL decode
4981 try {
4982
4983 return decodeURIComponent(cookie_value);
4984
4985 } catch(e) {
4986
4987 return cookie_value;
4988 }
4989 }
4990 }
4991
4992 return default_value;
4993 }
4994
4995 // Clear cookie
4996 $.WS_Form.prototype.cookie_clear = function(cookie_name, bind_to_form_id) {
4997
4998 if(typeof bind_to_form_id === 'undefined') { bind_to_form_id = true; }
4999
5000 // Read cookie prefix
5001 var cookie_prefix = this.get_object_value($.WS_Form.settings_plugin, 'cookie_prefix');
5002 if(!cookie_prefix) { return false; }
5003
5004 // Build expiry
5005 var d = new Date();
5006 d.setTime(d.getTime() - (3600 * 1000));
5007
5008 // Clear cookie (because of negative expiry date)
5009 var cookie_string = cookie_prefix + '_' + (bind_to_form_id ? (this.form_id + '_') : '') + cookie_name + "=''; expires=" + d.toUTCString() + "; path=/; SameSite=Strict; Secure";
5010 document.cookie = cookie_string;
5011
5012 return true;
5013 }
5014
5015 // Get session storage raw
5016 $.WS_Form.prototype.session_storage_get_raw = function(key, default_value) {
5017
5018 if(typeof default_value === 'undefined') { default_value = ''; }
5019
5020 if(
5021 (key === '') ||
5022 (typeof sessionStorage !== 'object') ||
5023 (sessionStorage.getItem(key) === null)
5024 ) {
5025 return default_value;
5026 }
5027
5028 return sessionStorage.getItem(key);
5029 }
5030
5031 // Get local storage raw
5032 $.WS_Form.prototype.local_storage_get_raw = function(key, default_value) {
5033
5034 if(typeof default_value === 'undefined') { default_value = ''; }
5035
5036 if(
5037 (key === '') ||
5038 (typeof localStorage !== 'object') ||
5039 (localStorage.getItem(key) === null)
5040 ) {
5041 return default_value;
5042 }
5043
5044 return localStorage.getItem(key);
5045 }
5046
5047 // Tabs
5048 $.WS_Form.prototype.tabs = function(obj, atts) {
5049
5050 if(typeof atts === 'undefined') { atts = {}; }
5051 var tab_selector = (typeof atts.selector !== 'undefined') ? atts.selector : 'li';
5052 var tab_active_index = (typeof atts.active !== 'undefined') ? parseInt(atts.active, 10) : 0;
5053 if(isNaN(tab_active_index)) { tab_active_index = 0; }
5054 var tab_activate = (typeof atts.activate !== 'undefined') ? atts.activate : false;
5055
5056 var ws_this = this;
5057 var tab_index = 0;
5058
5059 obj.addClass('wsf-tabs');
5060
5061 $(tab_selector, obj).each(function() {
5062
5063 var tab_obj_outer = $(this);
5064
5065 tab_obj_outer.find('a[href*="#"]:not([href="#"])').each(function() {
5066
5067 // Add tab index data attribute
5068 $(this).attr('data-tab-index', tab_index);
5069
5070 // Click event
5071 $(this).off('click').on('click', function(e) {
5072
5073 e.preventDefault();
5074
5075 // Trigger any conditional logic associated with a tab click
5076 $(this).trigger('wsf-click');
5077
5078 // Stop further propagation
5079 e.stopPropagation();
5080 e.stopImmediatePropagation();
5081
5082 ws_this.tab_show($(this), tab_obj_outer, tab_activate);
5083 });
5084
5085 // Initialize tab
5086 if(tab_index === tab_active_index) {
5087
5088 ws_this.tab_show($(this), tab_obj_outer);
5089 }
5090 });
5091
5092 tab_index++;
5093 });
5094 }
5095
5096 // Tabs - Destroy
5097 $.WS_Form.prototype.tabs_destroy = function(obj, atts) {
5098
5099 if(typeof atts === 'undefined') { atts = {}; }
5100 var tab_selector = (typeof atts.selector !== 'undefined') ? atts.selector : 'li';
5101
5102 $(tab_selector, obj).each(function() {
5103
5104 var tab_obj_outer = $(this);
5105
5106 tab_obj_outer.find('a').each(function() {
5107
5108 // Remove tab index data attribute
5109 $(this).removeAttr('data-tab-index');
5110
5111 // Remove click event
5112 $(this).off('click');
5113 });
5114 });
5115
5116 obj.removeClass('wsf-tabs');
5117 }
5118
5119 // Tabs - Show
5120 $.WS_Form.prototype.tab_show = function(tab_obj, tab_obj_outer, tab_activate) {
5121
5122 // Hide siblings
5123 var ws_this = this;
5124 tab_obj_outer.siblings().each(function() {
5125
5126 var tab_obj_sibling = $(this).find('a').first();
5127 ws_this.tab_hide(tab_obj_sibling, $(this));
5128 });
5129
5130 // Tab
5131 tab_obj_outer.addClass('wsf-tab-active');
5132
5133 // Tab content
5134 var tab_hash = tab_obj.attr('href');
5135 $(tab_hash).show();
5136
5137 // Tab activate function
5138 if(typeof tab_activate === 'function') {
5139
5140 var tab_index = tab_obj.attr('data-tab-index');
5141 tab_activate(tab_index);
5142 }
5143
5144 // Fire event
5145 tab_obj.trigger('tab_show');
5146 }
5147
5148 // Tabs - Hide
5149 $.WS_Form.prototype.tab_hide = function(tab_obj, tab_obj_outer) {
5150
5151 // Tab
5152 tab_obj_outer.removeClass('wsf-tab-active');
5153
5154 // Tab content
5155 var tab_hash = tab_obj.attr('href');
5156 $(tab_hash).hide();
5157 }
5158
5159 // Build group_data_cache, section_data_cache, field_data_cache and action_data_cache
5160 $.WS_Form.prototype.data_cache_build = function() {
5161
5162 // Check we can build the caches
5163 if(typeof this.form === 'undefined') { return false; }
5164 if(typeof this.form.groups === 'undefined') { return false; }
5165
5166 // Clear data caches
5167 this.group_data_cache = [];
5168 this.section_data_cache = [];
5169 this.field_data_cache = [];
5170 this.action_data_cache = [];
5171
5172 // Build group, section and field data caches
5173 for(var group_index in this.form.groups) {
5174
5175 if(!this.form.groups.hasOwnProperty(group_index)) { continue; }
5176
5177 var group = this.form.groups[group_index];
5178
5179 // Process group
5180 this.data_cache_build_group(group);
5181 }
5182
5183 // Build action data cache
5184 var action = this.get_object_meta_value(this.form, 'action', false);
5185 if(!(
5186 (action === false) ||
5187 (typeof action.groups === 'undefined') ||
5188 (typeof action.groups[0] === 'undefined') ||
5189 (typeof action.groups[0].rows !== 'object') ||
5190 (action.groups[0].rows.length == 0)
5191 )) {
5192
5193 var rows = action.groups[0].rows;
5194 for(var row_index in rows) {
5195
5196 if(!rows.hasOwnProperty(row_index)) { continue; }
5197
5198 var row = rows[row_index];
5199
5200 if(
5201 (row === null) ||
5202 (typeof row !== 'object') ||
5203 (typeof row.data !== 'object') ||
5204 (row.data.length == 0)
5205 ) {
5206 continue;
5207 }
5208
5209 this.action_data_cache[row.id] = {'label': row.data[0]};
5210 }
5211 }
5212
5213 return true;
5214 }
5215
5216 // Build group_data_cache
5217 $.WS_Form.prototype.data_cache_build_group = function(group) {
5218
5219 // Store to group_data_cache array
5220 this.group_data_cache[group.id] = group;
5221
5222 for(var section_index in group.sections) {
5223
5224 if(!group.sections.hasOwnProperty(section_index)) { continue; }
5225
5226 var section = group.sections[section_index];
5227
5228 // Process section
5229 this.data_cache_build_section(section);
5230 }
5231
5232 return true;
5233 }
5234
5235 // Build section_data_cache and field_data_cache
5236 $.WS_Form.prototype.data_cache_build_section = function(section) {
5237
5238 // Store to section_data_cache array
5239 this.section_data_cache[section.id] = section;
5240
5241 var section_repeatable = (
5242
5243 (typeof section.meta !== 'undefined') &&
5244 (typeof section.meta.section_repeatable !== 'undefined') &&
5245 (section.meta.section_repeatable == 'on')
5246 );
5247
5248 // HTML
5249 for(var field_index in section.fields) {
5250
5251 if(!section.fields.hasOwnProperty(field_index)) { continue; }
5252
5253 var field = section.fields[field_index];
5254
5255 // Repeatable?
5256 if(section_repeatable) {
5257
5258 field.section_repeatable_section_id = section.id;
5259 }
5260
5261 // Skip fields that are unlicensed (Required for published data)
5262 if(typeof $.WS_Form.field_type_cache[field.type] === 'undefined') { continue; }
5263
5264 // Store to field_data_cache array
5265 this.field_data_cache[field.id] = field;
5266 }
5267
5268 return true;
5269 }
5270
5271 // Randomize array
5272 $.WS_Form.prototype.array_randomize = function(array_to_randomize) {
5273
5274 for (var i = array_to_randomize.length - 1; i > 0; i--) {
5275
5276 var j = Math.floor(Math.random() * (i + 1));
5277 var temp = array_to_randomize[i];
5278 array_to_randomize[i] = array_to_randomize[j];
5279 array_to_randomize[j] = temp;
5280 }
5281
5282 return array_to_randomize;
5283 }
5284
5285 // Get nice duration
5286 $.WS_Form.prototype.get_nice_duration = function(duration, show_seconds) {
5287
5288 if(typeof show_seconds == 'undefined') { show_seconds = true; }
5289
5290 if(duration == 0) { return '-'; }
5291
5292 var duration_hours = Math.floor(duration / 3600);
5293
5294 if(show_seconds) {
5295
5296 var duration_minutes = Math.floor((duration % 3600) / 60);
5297 var duration_seconds = duration % 60;
5298
5299 } else {
5300
5301 var duration_minutes = Math.ceil((duration % 3600) / 60);
5302 }
5303
5304 var return_array = [];
5305
5306 if(duration_hours > 0) { return_array.push(duration_hours + ' ' + ((duration_hours == 1) ? this.language('hour') : this.language('hours'))); }
5307 if(duration_minutes > 0) { return_array.push(duration_minutes + ' ' + ((duration_minutes == 1) ? this.language('minute') : this.language('minutes'))); }
5308
5309 if(show_seconds) {
5310
5311 return_array.push(duration_seconds + ' ' + ((duration_seconds == 1) ? this.language('second') : this.language('seconds')));
5312 }
5313
5314 return return_array.join(' ');
5315 }
5316
5317 // Get nice distance
5318 $.WS_Form.prototype.get_nice_distance = function(distance_meters, unit_system) {
5319
5320 if(distance_meters == 0) { return '-'; }
5321
5322 switch(unit_system) {
5323
5324 // Miles
5325 case 'IMPERIAL' :
5326
5327 var distance_miles = distance_meters / 1609;
5328
5329 if(distance_miles >= 1) {
5330
5331 return Math.floor(distance_miles) + ' ' + ((distance_miles == 1) ? this.language('mile') : this.language('miles'));
5332
5333 } else {
5334
5335 var distance_feet = distance_meters * 3.2808399;
5336
5337 return Math.floor(distance_feet) + ' ' + ((distance_feet == 1) ? this.language('feet') : this.language('feets'));
5338 }
5339
5340 // Kilometers
5341 default :
5342
5343 var distance_kilometers = distance_meters / 1000;
5344
5345 if(distance_kilometers >= 1) {
5346
5347 return (Math.round(distance_kilometers * 10) / 10) + ' ' + ((distance_kilometers == 1) ? this.language('kilometer') : this.language('kilometers'));
5348
5349 } else {
5350
5351 return (Math.round(distance_meters * 10) / 10) + ' ' + ((distance_meters == 1) ? this.language('meter') : this.language('meters'));
5352 }
5353 }
5354 }
5355
5356 // ucwords
5357 $.WS_Form.prototype.ucwords = function(input_string) {
5358
5359 var input_string_array = input_string.split(' ');
5360
5361 for(var i = 0; i < input_string_array.length; i++) {
5362
5363 input_string_array[i] = input_string_array[i].charAt(0).toUpperCase() + input_string_array[i].slice(1);
5364 }
5365
5366 return input_string_array.join(' ');
5367 }
5368
5369 // ucfirst
5370 $.WS_Form.prototype.ucfirst = function(input_string) {
5371
5372 var return_string = '';
5373
5374 if(input_string.length > 0) {
5375
5376 return_string = input_string[0].toUpperCase();
5377 if(input_string.length > 1) { return_string += input_string.slice(1); }
5378 }
5379
5380 return return_string;
5381 }
5382
5383 // capitalize
5384 $.WS_Form.prototype.capitalize = function(input_string) {
5385
5386 return this.ucwords(input_string.toLowerCase());
5387 }
5388
5389 // sentence
5390 $.WS_Form.prototype.sentence = function(input_string) {
5391
5392 return this.ucfirst(input_string.toLowerCase());
5393 }
5394
5395 // Get label position
5396 $.WS_Form.prototype.get_label_position = function(field) {
5397
5398 // Get sub type
5399 var sub_type = this.get_object_meta_value(field, 'sub_type', false);
5400 if(sub_type == '') { sub_type = false; }
5401
5402 // Get label parameters
5403 var label_position = this.get_object_meta_value(field, 'label_position', 'default');
5404 label_position = this.get_field_value_fallback(field.type, false, 'label_position_force', label_position, false, sub_type);
5405
5406 // Field is using default position, so read default label position of form
5407 if(label_position == 'default') {
5408
5409 label_position = this.get_object_meta_value(this.form, 'label_position_form', 'top');
5410 }
5411
5412 return label_position;
5413 }
5414
5415 // Get help position
5416 $.WS_Form.prototype.get_help_position = function(field) {
5417
5418 // Get sub type
5419 var sub_type = this.get_object_meta_value(field, 'sub_type', false);
5420 if(sub_type == '') { sub_type = false; }
5421
5422 // Get help parameters
5423 var help_position = this.get_object_meta_value(field, 'help_position', 'default');
5424 help_position = this.get_field_value_fallback(field.type, false, 'help_position_force', help_position, false, sub_type);
5425
5426 // Field is using default position, so read default help position of form
5427 if(help_position == 'default') {
5428
5429 help_position = this.get_object_meta_value(this.form, 'help_position_form', 'bottom');
5430 }
5431
5432 return help_position;
5433 }
5434
5435 // Get form invalid feedback mask (Default invalid feedback message)
5436 $.WS_Form.prototype.get_form_invalid_feedback_mask = function() {
5437
5438 var invalid_feedback_default = this.get_object_meta_value(this.form, 'invalid_feedback_mask', '');
5439
5440 if(invalid_feedback_default == '') {
5441
5442 // Get invalid feedback mask
5443 var meta_key_config = $.WS_Form.meta_keys.invalid_feedback_mask;
5444
5445 // Return placeholder
5446 return meta_key_config.p ? meta_key_config.p : meta_key_config.mask_placeholder;
5447
5448 } else {
5449
5450 // Return invalid feedback mask value
5451 return invalid_feedback_default;
5452 }
5453 }
5454
5455 // Get form invalid feedback mask (Default invalid feedback message)
5456 $.WS_Form.prototype.get_invalid_feedback_mask_parsed = function(invalid_feedback, label) {
5457
5458 // Parse invalid_feedback_mask_placeholder
5459 invalid_feedback = this.replace_all(invalid_feedback, '#label_lowercase', label.toLowerCase());
5460 invalid_feedback = this.replace_all(invalid_feedback, '#label', label);
5461
5462 return invalid_feedback;
5463 }
5464
5465 // Get field html
5466 $.WS_Form.prototype.get_field_html_single = function(field, value, is_submit, section_repeatable_index) {
5467
5468 if(typeof is_submit === 'undefined') { is_submit = false; }
5469 if(typeof section_repeatable_index === 'undefined') { section_repeatable_index = false; }
5470
5471 var field_html = '';
5472 var attributes_values_field = [];
5473 var has_value = (typeof value !== 'undefined');
5474
5475 // If we are rendering a field for submission editing, don't register calculations
5476 var calc_register = !is_submit;
5477
5478 // Build repeatable suffix
5479 var repeatable_suffix = ((section_repeatable_index !== false) ? '-repeat-' + section_repeatable_index : '');
5480
5481 // Build field ID
5482 var field_id = this.get_part_id(field.id, section_repeatable_index);
5483
5484 // Build field name
5485 var field_name = this.get_field_name(field.id, section_repeatable_index);
5486
5487 // Submit only config
5488 var submit_attributes_field = ['default', 'class', 'input_type_datetime', 'multiple', 'min', 'max', 'step'];
5489 var submit_attributes_field_label = ['class'];
5490
5491 if(typeof $.WS_Form.field_type_cache[field.type] === 'undefined') { return ''; }
5492
5493 // Get field type
5494 var field_type_config = $.WS_Form.field_type_cache[field.type];
5495
5496 // Check to see if this field can be used in the current edition
5497 var pro_required = field_type_config.pro_required;
5498 if(pro_required) { return this.language('error_pro_required'); }
5499
5500 // Should label be rendered?
5501 if(is_submit) {
5502
5503 var label_render = true;
5504
5505 } else {
5506
5507 var label_render = this.get_object_meta_value(field, 'label_render', true);
5508 }
5509
5510 // Get sub type
5511 var sub_type = this.get_object_meta_value(field, 'sub_type', false);
5512 if(sub_type == '') { sub_type = false; }
5513
5514 // Check for label disable override
5515 var label_disabled = this.get_field_value_fallback(field.type, false, 'label_disabled', false, false, sub_type);
5516 if(label_disabled) { label_render = false; }
5517
5518 // Should field name be suffixed with []?
5519 var submit_array = (typeof field_type_config.submit_array !== 'undefined') ? field_type_config.submit_array : false;
5520 if(submit_array) { field_name += '[]'; }
5521
5522 // Determine label_position (If we are not rendering the label, then set to top so no position specific framework masks are used)
5523 if(label_render && !is_submit) {
5524
5525 var label_position = this.get_label_position(field);
5526
5527 } else {
5528
5529 var label_position = 'top';
5530 }
5531 var label_position_inside = false;
5532
5533 // Check label position is value
5534 var framework = this.get_framework();
5535 if(framework.label_positions.indexOf(label_position) === -1) { label_position = 'top'; }
5536
5537 // Get mask field attributes
5538 var mask_field_attributes = ($.extend(true, [], this.get_field_value_fallback(field.type, label_position, 'mask_field_attributes', [], false, sub_type)));
5539
5540 // Check to see if wrappers should be ignored
5541 var mask_wrappers_drop = (typeof field_type_config.mask_wrappers_drop !== 'undefined') ? field_type_config.mask_wrappers_drop : false;
5542
5543
5544 // Load masks
5545 var mask = mask_wrappers_drop ? '#field' : this.get_field_value_fallback(field.type, label_position, 'mask', '#field', false, sub_type);
5546 var mask_field = this.get_field_value_fallback(field.type, label_position, 'mask_field', '', false, sub_type);
5547 if(is_submit) {
5548
5549 var mask_field_submit = this.get_field_value_fallback(field.type, label_position, 'mask_field_submit', false, false, sub_type);
5550 if(mask_field_submit !== false) { mask_field = mask_field_submit; }
5551 }
5552
5553 // If label is position left or right, remove help mask value from mask field (Wrapper has #pre_help and #post_help in it)
5554 if(label_position == 'left' || label_position == 'right') {
5555
5556 mask_field = mask_field.replace('#pre_help', '');
5557 mask_field = mask_field.replace('#post_help', '');
5558 }
5559
5560 var mask_field_label = label_render ? this.get_field_value_fallback(field.type, label_position, 'mask_field_label', '', false, sub_type) : '';
5561 var mask_field_label_hide_group = this.get_field_value_fallback(field.type, label_position, 'mask_field_label_hide_group', false, false, sub_type);
5562 var mask_group_wrapper_role = false;
5563 var mask_help = this.get_field_value_fallback(field.type, label_position, 'mask_help', '', false, sub_type);
5564 var mask_help_append = this.get_field_value_fallback(field.type, label_position, 'mask_help_append', '', false, sub_type);
5565 var mask_help_append_separator = this.get_field_value_fallback(field.type, label_position, 'mask_help_append_separator', '', false, sub_type);
5566 var mask_invalid_feedback = this.get_field_value_fallback(field.type, label_position, 'mask_invalid_feedback', '', false, sub_type);
5567
5568 switch(field.type) {
5569
5570 case 'checkbox' :
5571 case 'price_checkbox' :
5572
5573 mask_group_wrapper_role = 'group';
5574 break;
5575
5576 case 'radio' :
5577 case 'price_radio' :
5578
5579 mask_group_wrapper_role = 'radiogroup';
5580 break;
5581 }
5582
5583 // Get values
5584 var default_value = this.get_object_meta_value(field, 'default_value', '', false, true);
5585 var text_editor = this.get_object_meta_value(field, 'text_editor', '', false, true);
5586 var html_editor = this.get_object_meta_value(field, 'html_editor', '', false, true);
5587 var dedupe_value_scope = this.get_object_meta_value(field, 'dedupe_value_scope', '', false, true);
5588 var hidden_bypass = this.get_object_meta_value(field, 'hidden_bypass', '', false, true);
5589 var exclude_cart_total = this.get_object_meta_value(field, 'exclude_cart_total', '', false, true);
5590
5591 // Get text editor and html editor values
5592
5593 // Get default value
5594 if(!has_value) {
5595
5596 value = '';
5597 if(default_value != '') { value = this.esc_html(default_value); }
5598 if(text_editor != '') { value = text_editor; }
5599 if(html_editor != '') { value = html_editor; }
5600 }
5601
5602 // Input group
5603 var process_input_group = true;
5604 var append = '';
5605
5606 // Field type checks
5607 switch(field.type) {
5608
5609 case 'textarea' :
5610
5611 process_input_group = (this.get_object_meta_value(field, 'input_type_textarea', '') === '');
5612 break;
5613
5614 case 'select' :
5615 case 'checkbox' :
5616 case 'radio' :
5617 case 'price_select' :
5618 case 'price_checkbox' :
5619 case 'price_radio' :
5620
5621 if(value) {
5622
5623 // Split comma separate values into a trimmed value array
5624 value = (typeof value === 'string') ? value.split(',').map(function(value) { return value.trim(); }) : value;
5625
5626 has_value = true;
5627 }
5628
5629 break;
5630
5631 }
5632
5633 // Prepend / append
5634 var prepend = process_input_group ? this.parse_variables_process(this.get_object_meta_value(field, 'prepend', ''), section_repeatable_index, false, field, 'field_prepend', calc_register).output : '';
5635 append += process_input_group ? this.parse_variables_process(this.get_object_meta_value(field, 'append', ''), section_repeatable_index, false, field, 'field_prepend', calc_register).output : '';
5636
5637 // Classes
5638 var class_field = '';
5639 if(!is_submit) {
5640
5641 var class_field_array = [];
5642
5643 var class_field_form = this.get_object_meta_value(this.form, 'class_field', '', false, true);
5644 if(class_field_form != '') { class_field_array.push(class_field_form); }
5645
5646 var class_field = this.get_object_meta_value(field, 'class_field', '', false, true);
5647 if(class_field != '') { class_field_array.push(class_field); }
5648
5649 // Full width class for buttons
5650 if(!this.get_object_meta_value(field, 'class_field_full_button_remove', '')) {
5651
5652 var class_field_full_button = this.get_field_value_fallback(field.type, label_position, 'class_field_full_button', '', false, sub_type);
5653 if(typeof class_field_full_button === 'object') {
5654 class_field_array.push(class_field_full_button.join(' '));
5655 }
5656 }
5657
5658 // Type class for buttons
5659 var class_field_button_type = this.get_object_meta_value(field, 'class_field_button_type', false);
5660 if(!class_field_button_type) {
5661
5662 var class_field_button_type = this.get_field_value_fallback(field.type, label_position, 'class_field_button_type_fallback', false, false, sub_type);
5663 }
5664 if(class_field_button_type) {
5665
5666 var class_field_button_type_config = this.get_field_value_fallback(field.type, label_position, 'class_field_button_type', '', false, sub_type);
5667 if(typeof class_field_button_type_config[class_field_button_type] !== 'undefined') {
5668
5669 class_field_array.push(class_field_button_type_config[class_field_button_type]);
5670 }
5671 }
5672
5673 // Checkbox and radio styles
5674 switch(field.type) {
5675
5676 case 'checkbox' :
5677 case 'radio' :
5678
5679 switch(this.get_object_meta_value(field, field.type + '_style', '')) {
5680
5681 case 'button' : class_field_array.push('wsf-button'); break;
5682 case 'button-full' : class_field_array.push('wsf-button wsf-button-full'); break;
5683 case 'color' : class_field_array.push('wsf-color'); break;
5684 case 'color-circle' : class_field_array.push('wsf-color wsf-circle'); break;
5685 case 'circle' : class_field_array.push('wsf-circle'); break;
5686 case 'image' : class_field_array.push('wsf-image'); break;
5687 case 'image-circle' : class_field_array.push('wsf-image wsf-circle'); break;
5688 case 'image-responsive' : class_field_array.push('wsf-image wsf-responsive wsf-image-full'); break;
5689 case 'image-circle-responsive' : class_field_array.push('wsf-image wsf-responsive wsf-image-full wsf-circle'); break;
5690 case 'switch' : class_field_array.push('wsf-switch'); break;
5691 }
5692
5693 break;
5694 }
5695
5696
5697 // Input group
5698 if(
5699 (prepend !== '') ||
5700 (append !== '')
5701 ) {
5702
5703 var class_field_input_group = this.get_field_value_fallback(field.type, label_position, 'class_field_input_group', '', false, sub_type);
5704 if(class_field_input_group !== '') {
5705
5706 class_field_array.push(class_field_input_group);
5707 }
5708 }
5709
5710 // Label position
5711 var label_inside = (typeof field_type_config.label_inside !== 'undefined') ? field_type_config.label_inside : false;
5712 if(
5713 label_inside &&
5714 (label_position === 'inside')
5715 ) {
5716
5717 switch(field.type) {
5718
5719 case 'textarea' :
5720
5721 var input_type_textarea = this.get_object_meta_value(field, 'input_type_textarea', false);
5722
5723 label_position_inside = !(
5724
5725 (input_type_textarea === 'tinymce') ||
5726 (input_type_textarea === 'html')
5727 );
5728
5729 break;
5730
5731 case 'select' :
5732 case 'price_select' :
5733
5734 var select2 = this.get_object_meta_value(field, 'select2', false);
5735
5736 label_position_inside = !select2;
5737
5738 break;
5739
5740 default :
5741
5742 label_position_inside = true;
5743 }
5744
5745 // Fallback to top
5746 if(!label_position_inside) { label_position = 'top'; }
5747 }
5748
5749 if(!label_inside && (label_position === 'inside')) { label_position = 'top'; }
5750
5751 if(label_position_inside) {
5752
5753 field.meta.placeholder = this.parse_variables_process(field.label, section_repeatable_index, false, field, 'field_label', calc_register).output;
5754 }
5755
5756 class_field = class_field_array.join(' ');
5757 }
5758
5759 // Label / field column widths (For left/right label positioning)
5760 var framework_column_count = parseInt($.WS_Form.settings_plugin.framework_column_count, 10);
5761
5762 var column_width_label_form = parseInt(this.get_object_meta_value(this.form, 'label_column_width_form', 3), 10);
5763 var column_width_label = this.get_object_meta_value(field, 'label_column_width', 'default');
5764
5765 switch(column_width_label) {
5766
5767 case 'default' :
5768 case '' :
5769
5770 column_width_label = column_width_label_form;
5771 break;
5772
5773 default :
5774
5775 column_width_label = parseInt(column_width_label, 10);
5776 }
5777 if(column_width_label >= framework_column_count) { column_width_label = (framework_column_count - 1); }
5778
5779 var column_width_field = framework_column_count - column_width_label;
5780
5781 // Field - Mask values
5782 var mask_values_field = {
5783
5784 'id': field_id,
5785 'form_id_prefix': this.form_id_prefix,
5786 'form_id': this.form_id,
5787 'form_instance_id': this.form_instance_id,
5788 'field_id': field.id,
5789
5790 'name': field_name,
5791 'label': this.parse_variables_process(field.label, section_repeatable_index, false, field, 'field_label', calc_register).output,
5792 'value': value,
5793 'required': (this.get_object_meta_value(this.form, 'label_required')) ? '<span class="wsf-required-wrapper"></span>' : '',
5794
5795 'column_width_label': column_width_label,
5796 'column_width_field': column_width_field,
5797
5798 'max_upload_size': ws_form_settings.max_upload_size,
5799 'locale': ws_form_settings.locale,
5800 'currency': $.WS_Form.settings_plugin.currency
5801 };
5802
5803 // Field - Mask values - Meta data
5804 var meta_key_parse_variables = this.get_field_value_fallback(field.type, label_position, 'meta_key_parse_variables', [], false, sub_type);
5805 for(var meta_key_parse_variables_index in meta_key_parse_variables) {
5806
5807 if(!meta_key_parse_variables.hasOwnProperty(meta_key_parse_variables_index)) { continue; }
5808
5809 // Get meta key
5810 var meta_key = meta_key_parse_variables[meta_key_parse_variables_index];
5811
5812 // Get default value
5813 var meta_key_config = (typeof $.WS_Form.meta_keys[meta_key] === 'undefined') ? false : $.WS_Form.meta_keys[meta_key];
5814 var meta_key_value_default = (meta_key_config !== false) ? ((typeof meta_key_config.d === 'undefined') ? '' : meta_key_config.d) : '';
5815
5816 // Get meta value
5817 var meta_value = this.get_object_meta_value(field, meta_key, meta_key_value_default);
5818
5819 // If value is an array, turn it into a JSON string
5820 if(typeof meta_value === 'object') { meta_value = JSON.stringify(meta_value); }
5821
5822 // Encode single quotes for JS purposes
5823 meta_value = this.replace_all(meta_value, "'", '&#39;');
5824
5825 mask_values_field[meta_key] = meta_value;
5826 }
5827
5828 // Field label - Mask values
5829 var mask_values_field_label = $.extend(true, {}, mask_values_field);
5830 mask_values_field_label.label_id = this.get_part_id(field.id, section_repeatable_index, 'label');
5831
5832 // Help
5833 var help_id = this.get_part_id(field.id, section_repeatable_index, 'help');
5834 var help = !is_submit ? this.get_object_meta_value(field, 'help', '', false, false) : '';
5835 var has_help = (help !== '');
5836
5837 // Initial parse to prevent syntax errors
5838 var mask_values_help = {
5839
5840 // Value
5841 'value': 0,
5842
5843 // Characters
5844 'character_count': 0,
5845 'character_count_label': '',
5846 'character_remaining': 0,
5847 'character_remaining_label': '',
5848 'character_min': 0,
5849 'character_min_label': '',
5850 'character_max': 0,
5851 'character_max_label': '',
5852
5853 // Words
5854 'word_count': 0,
5855 'word_count_label': '',
5856 'word_remaining': 0,
5857 'word_remaining_label': '',
5858 'word_min': 0,
5859 'word_min_label': '',
5860 'word_max': 0,
5861 'word_max_label': ''
5862 };
5863 help = this.mask_parse(help, mask_values_help);
5864
5865 // Parse help
5866 var help = this.parse_variables_process(help, section_repeatable_index, false, field, 'field_help', calc_register).output;
5867
5868 // Help - When editing a submission, change help by field type
5869 if(is_submit) {
5870
5871 switch(field.type) {
5872
5873 case 'range' :
5874
5875 help = '#value';
5876 break;
5877 }
5878 }
5879
5880 // Help position
5881 var help_position = this.get_help_position(field);
5882
5883 // Help classes
5884 var class_help_array = this.get_field_value_fallback(field.type, label_position, 'class_help_' + ((help_position == 'bottom') ? 'post' : 'pre'), [], false, sub_type);
5885
5886 // Help mask values
5887 mask_values_help.id = field_id;
5888 mask_values_help.help_id = help_id;
5889 mask_values_help.help_class = class_help_array.join(' ');
5890 mask_values_help.help = help;
5891
5892 mask_values_field.help_class = class_help_array.join(' ');
5893
5894 // Get invalid_feedback parameters
5895 var invalid_feedback_render = (is_submit ? false : this.get_object_meta_value(field, 'invalid_feedback_render', false, false, true));
5896
5897 // Invalid feedback
5898 var invalid_feedback_last_row = false;
5899 if(invalid_feedback_render) {
5900
5901 var mask_values_invalid_feedback = ($.extend(true, {}, mask_values_field));
5902
5903 // Invalid feedback ID
5904 var invalid_feedback_id = this.form_id_prefix + 'invalid-feedback-' + field.id + repeatable_suffix;
5905
5906 // Invalid feedback classes
5907 var class_invalid_feedback_array = this.get_field_value_fallback(field.type, label_position, 'class_invalid_feedback', [], false, sub_type);
5908 var invalid_feedback_last_row = this.get_field_value_fallback(field.type, label_position, 'invalid_feedback_last_row', false, false, sub_type);
5909
5910 // Get invalid feedback string
5911 var invalid_feedback = this.get_object_meta_value(field, 'invalid_feedback', '', false, true);
5912
5913 if(invalid_feedback == '') {
5914
5915 invalid_feedback = this.get_form_invalid_feedback_mask();
5916
5917 }
5918
5919 // Parse invalid feedback
5920 invalid_feedback = this.get_invalid_feedback_mask_parsed(invalid_feedback, field.label);
5921
5922 // Invalid feedback mask values
5923 mask_values_invalid_feedback.invalid_feedback_id = invalid_feedback_id;
5924 mask_values_invalid_feedback.invalid_feedback_class = class_invalid_feedback_array.join(' ');
5925 mask_values_invalid_feedback.invalid_feedback = invalid_feedback;
5926 mask_values_invalid_feedback.attributes = '';
5927
5928 var invalid_feedback_parsed = this.mask_parse(mask_invalid_feedback, mask_values_invalid_feedback);
5929
5930 } else {
5931
5932 var invalid_feedback_id = false;
5933 var invalid_feedback_parsed = '';
5934 }
5935
5936 mask_values_field.invalid_feedback = invalid_feedback_parsed;
5937 mask_values_field_label.invalid_feedback = invalid_feedback_parsed;
5938
5939 // Field - Attributes
5940 mask_values_field.attributes = '';
5941
5942 if(is_submit) {
5943
5944 var mask_field_attributes = submit_attributes_field.filter(function(val) {
5945
5946 return mask_field_attributes.indexOf(val) != -1;
5947 });
5948 }
5949
5950 if(mask_field_attributes.length > 0) {
5951 var get_attributes_return = this.get_attributes(field, mask_field_attributes, false, section_repeatable_index);
5952 mask_values_field.attributes += ' ' + get_attributes_return.attributes;
5953 mask_field_attributes = get_attributes_return.mask_attributes;
5954 attributes_values_field = get_attributes_return.attribute_values;
5955 }
5956
5957 // If there is no wrapper (e.g. hidden field) then add data-repeatable-index attribute directly to field
5958 if(section_repeatable_index !== false) {
5959
5960 mask_values_field.attributes += ' data-repeatable-index="' + this.esc_attr(section_repeatable_index) + '"';
5961 }
5962
5963 // Attributes to inherit at a row level
5964 var mask_values_row_attributes_source = '';
5965
5966 // Custom attributes
5967 mask_values_field.attributes = this.custom_attributes(mask_values_field.attributes, field, 'field', section_repeatable_index);
5968
5969 // Field - Attributes - Orientation
5970 var orientation = this.get_object_meta_value(field, 'orientation', false);
5971
5972 if(orientation == 'grid') {
5973
5974 // Get wrapper and row classes
5975 var class_orientation_wrapper_array = this.get_field_value_fallback(field.type, label_position, 'class_orientation_wrapper', [], false, sub_type);
5976 var class_orientation_row_array = this.get_field_value_fallback(field.type, label_position, 'class_orientation_row', [], false, sub_type);
5977 var orientation_group_wrapper_class = class_orientation_wrapper_array.join(' ');
5978
5979 // Get class array
5980 var orientation_class_array = this.column_class_array(field, 'orientation_breakpoint');
5981 orientation_class_array = class_orientation_row_array.concat(orientation_class_array);
5982 var orientation_row_class = orientation_class_array.join(' ');
5983 }
5984
5985 // Field label - Attributes
5986 mask_values_field_label.attributes = '';
5987 var mask_field_label_attributes = ($.extend(true, [], this.get_field_value_fallback(field.type, label_position, 'mask_field_label_attributes', [], false, sub_type)));
5988
5989 if(is_submit) {
5990
5991 var mask_field_label_attributes = submit_attributes_field_label.filter(function(val) {
5992
5993 return mask_field_label_attributes.indexOf(val) != -1;
5994 });
5995 }
5996
5997 if(mask_field_label_attributes.length > 0) {
5998 var get_attributes_return = this.get_attributes(field, mask_field_label_attributes, false, section_repeatable_index);
5999 mask_values_field_label.attributes += get_attributes_return.attributes;
6000 mask_field_label_attributes = get_attributes_return.mask_attributes;
6001 }
6002
6003 // Mask values - Data
6004 var data = '';
6005 var data_source = this.get_object_value(field_type_config, 'data_source', false);
6006 var data_row_count = 0;
6007
6008 var data_source_process = (data_source !== false);
6009
6010 if(data_source_process) {
6011
6012 // Get data source type
6013 if(typeof data_source.type === 'undefined') {
6014
6015 data_source_process = false;
6016
6017 } else {
6018
6019 var data_source_type = data_source.type;
6020 }
6021
6022 // Get data source ID
6023 if(typeof data_source.id === 'undefined') {
6024
6025 data_source_process = false;
6026
6027 } else {
6028
6029 var data_source_id = data_source.id;
6030 }
6031 }
6032
6033 if(data_source_process) {
6034
6035 // Get array of data
6036 switch(data_source_type) {
6037
6038 case 'data_grid' :
6039
6040 var data_source_object_data = this.get_object_meta_value(field, data_source_id, false);
6041 break;
6042 }
6043 if(data_source_object_data === false) { data_source_process = false; }
6044
6045 // Columns
6046 if(typeof data_source_object_data.columns === 'undefined') {
6047
6048 data_source_process = false;
6049
6050 } else {
6051
6052 var data_columns = data_source_object_data.columns;
6053 }
6054 }
6055
6056 if(data_source_process) {
6057
6058 // Data masks
6059 var mask_group = this.get_field_value_fallback(field.type, label_position, 'mask_group', '', false, sub_type);
6060 var mask_group_wrapper = this.get_field_value_fallback(field.type, label_position, 'mask_group_wrapper', '', false, sub_type);
6061 var mask_group_label = this.get_field_value_fallback(field.type, label_position, 'mask_group_label', '', false, sub_type);
6062 var mask_group_always = this.get_field_value_fallback(field.type, label_position, 'mask_group_always', false, false, sub_type);
6063
6064 var mask_row = this.get_field_value_fallback(field.type, label_position, 'mask_row', '', false, sub_type);
6065 var mask_row_placeholder = this.get_field_value_fallback(field.type, label_position, 'mask_row_placeholder', '', false, sub_type);
6066 var mask_row_field = this.get_field_value_fallback(field.type, label_position, 'mask_row_field', '', false, sub_type);
6067
6068 // Mask row label can be defined at a framework level for field types to support inline and wrapping labels
6069 var mask_row_label = this.get_field_value_fallback(field.type, label_position, 'mask_row_label', '', false, sub_type);
6070
6071 var mask_row_lookups = this.get_field_value_fallback(field.type, label_position, 'mask_row_lookups', [], false, sub_type);
6072 var datagrid_column_value = this.get_field_value_fallback(field.type, label_position, 'datagrid_column_value', false, false, sub_type);
6073
6074 var mask_row_default = this.get_field_value_fallback(field.type, label_position, 'mask_row_default', '', false, sub_type);
6075 var mask_row_required = this.get_field_value_fallback(field.type, label_position, 'mask_row_required', ' required data-required', false, sub_type);
6076 var mask_row_disabled = this.get_field_value_fallback(field.type, label_position, 'mask_row_disabled', ' disabled', false, sub_type);
6077 var mask_row_visible = this.get_field_value_fallback(field.type, label_position, 'mask_row_visible', ' visible', false, sub_type);
6078
6079 // Randomize rows
6080 var rows_randomize = this.get_object_meta_value(field, 'data_grid_rows_randomize', '', false, true);
6081 if(rows_randomize) {
6082
6083 // Check to see if term hierarchy is enabled, if it is we should force row_randomize to false
6084 var data_source_term_hierarchy = this.get_object_meta_value(field, 'data_source_term_hierarchy', '', false, true);
6085 if(data_source_term_hierarchy) { rows_randomize = false; }
6086 }
6087
6088 // Placeholder row (e.g. Adds Select... as the first row)
6089 var placeholder_row = this.get_object_meta_value(field, 'placeholder_row', '', false, true);
6090 var multiple = this.get_object_meta_value(field, 'multiple', '', false, true);
6091 if(
6092 (placeholder_row != '') &&
6093 !multiple
6094 ) {
6095
6096 // Inject placeholder row
6097 var mask_values_row_placeholder = $.extend(true, {}, mask_values_field);
6098 mask_values_row_placeholder.value = placeholder_row;
6099 data += this.mask_parse(mask_row_placeholder, mask_values_row_placeholder);
6100 }
6101
6102 // Value should be an array
6103 if(has_value && (typeof value !== 'object')) {
6104
6105 // If value is a number, change it to a string (Ensures indexOf works below)
6106 if(typeof value !== 'string') { value = value.toString(); }
6107
6108 // Convert to array
6109 value = [value];
6110 }
6111
6112 // Has value an index of
6113 var has_value_indexof = has_value && (typeof value.indexOf !== 'undefined');
6114
6115 // Build mask lookup cache
6116 var mask_row_lookup_array = [];
6117
6118 // Run through each data mask field
6119 for(var mask_row_lookup_key in mask_row_lookups) {
6120
6121 if(!mask_row_lookups.hasOwnProperty(mask_row_lookup_key)) { continue; }
6122
6123 // Read data mask field value (this will be the ID for that data grid column)
6124 var mask_row_lookup = mask_row_lookups[mask_row_lookup_key];
6125
6126 // Reset
6127 mask_row_lookup_array[mask_row_lookup] = false;
6128
6129 // Read value from data
6130 var mask_row_lookup_value = this.get_object_meta_value(field, mask_row_lookup, false, false, true);
6131
6132 // If not found...
6133 if(mask_row_lookup_value === false) {
6134
6135 if(typeof $.WS_Form.meta_keys[mask_row_lookup] !== 'undefined') {
6136
6137 // Check for a default value
6138 var meta_key_config = $.WS_Form.meta_keys[mask_row_lookup];
6139
6140 mask_row_lookup_value = (typeof meta_key_config.d !== 'undefined') ? meta_key_config.d : 0;
6141
6142 // If default value is larger than the number of available columns, set it to zero
6143 if(mask_row_lookup_value > data_columns.length) { mask_row_lookup_value = 0; }
6144
6145 } else {
6146
6147 continue;
6148 }
6149 }
6150
6151 // Run through the data grid columns until we find the ID
6152 var data_column_index = false;
6153 for(var data_columns_index in data_columns) {
6154
6155 if(!data_columns.hasOwnProperty(data_columns_index)) { continue; }
6156
6157 var data_column = data_columns[data_columns_index];
6158 var data_column_id = data_column.id;
6159
6160 // Match found, store in cache
6161 if(data_column_id == mask_row_lookup_value) { data_column_index = data_columns_index; break; }
6162 }
6163
6164 if(data_column_index) { mask_row_lookup_array[mask_row_lookup] = data_column_index; }
6165 }
6166
6167 // Read groups
6168 if(typeof data_source_object_data.groups === 'undefined') { this.error('error_data_source_groups'); return ''; }
6169 var data_groups = data_source_object_data.groups;
6170 var data_groups_count = Object.keys(data_groups).length;
6171
6172 // Randomize data groups?
6173 if(rows_randomize) { data_groups = this.array_randomize(data_groups); }
6174
6175 // Cycle through groups
6176 for(var data_group_index in data_groups) {
6177
6178 if(!data_groups.hasOwnProperty(data_group_index)) { continue; }
6179
6180 // Mask values
6181 var mask_values_group = $.extend(true, {}, mask_values_field);
6182 mask_values_group.group_id = this.form_id_prefix + 'datagrid-' + field.id + '-group-' + data_group_index + repeatable_suffix;
6183
6184 var data_group = data_groups[data_group_index];
6185
6186 // Check group
6187 if(
6188 (data_group == null) ||
6189 (typeof data_group !== 'object')
6190 ) {
6191 continue;
6192 }
6193
6194 // Should group data mask be used?
6195 var mask_group_use = (
6196 (typeof data_group.mask_group !== 'undefined') ? this.is_true(data_group.mask_group) : false
6197 ) || mask_group_always;
6198
6199 // Get group label
6200 if(typeof data_group.label === 'undefined') { this.error('error_data_group_label'); return ''; }
6201 switch(field.type) {
6202
6203 case 'select' :
6204 case 'price_select' :
6205
6206 var mask_values_group_label_render = true;
6207 break;
6208
6209 default :
6210
6211 var mask_values_group_label_render = (typeof data_group.label_render === 'undefined') ? true : data_group.label_render;
6212 }
6213
6214 // Group label mask values
6215 if(mask_values_group_label_render) {
6216
6217 var mask_values_group_label = $.extend(true, {}, mask_values_field);
6218 mask_values_group_label.group_label = data_group.label;
6219 mask_values_group_label.label_row_id = this.form_id_prefix + 'label-' + field.id + '-group-' + data_group_index + repeatable_suffix;
6220
6221 // Parse group label mask to build group_label value
6222 mask_values_group.group_label = this.parse_variables_process(this.mask_parse(mask_group_label, mask_values_group_label), section_repeatable_index).output;
6223
6224 } else {
6225
6226 if((mask_group !== false) && mask_group_use && (mask_group.indexOf('<fieldset') !== -1)) {
6227
6228 mask_values_group.group_label = '<legend class="wsf-hidden-element">' + this.esc_html(data_group.label != '' ? data_group.label : this.language('comment_group')) + '</legend>';
6229
6230 } else {
6231
6232 mask_values_group.group_label = '';
6233 }
6234 }
6235
6236 // Get group disabled (optional)
6237 mask_values_group.disabled = (typeof data_group.disabled !== 'undefined') ? (data_group.disabled == 'on' ? ' disabled' : '') : '';
6238
6239 // Should field label be hidden if groups are in use
6240 if(mask_group_use && mask_field_label_hide_group) { mask_field_label = ''; }
6241
6242 // Get group rows (If there are no rows, data_group.rows = undefined)
6243 var group = '';
6244 if(typeof data_group.rows !== 'undefined') {
6245
6246 // Clone data group rows
6247 var data_rows = JSON.parse(JSON.stringify(data_group.rows));
6248
6249 // Add 'Select All' row
6250 var select_all_row = this.get_object_meta_value(field, 'select_all', '');
6251 if(((field.type == 'checkbox') || (field.type == 'price_checkbox')) && (select_all_row == 'on')) {
6252
6253 // Mask values - Data mask fields
6254 var select_all_value_index = false;
6255 var select_all_label_index = false;
6256 for(var mask_row_lookup in mask_row_lookup_array) {
6257
6258 if(!mask_row_lookup_array.hasOwnProperty(mask_row_lookup)) { continue; }
6259
6260 var select_all_index = mask_row_lookup_array[mask_row_lookup];
6261
6262 if(mask_row_lookup == 'checkbox_field_value') { select_all_value_index = select_all_index; }
6263 if(mask_row_lookup == 'checkbox_field_label') { select_all_label_index = select_all_index; }
6264 if(mask_row_lookup == 'checkbox_price_field_value') { select_all_value_index = select_all_index; }
6265 if(mask_row_lookup == 'checkbox_price_field_label') { select_all_label_index = select_all_index; }
6266 }
6267
6268 // Inject new row
6269 var select_all_row = {
6270
6271 id: 0,
6272 default: '',
6273 required: '',
6274 hidden: '',
6275 disabled: '',
6276 select_all: true,
6277 data: []
6278 }
6279 var select_all_label = this.get_object_meta_value(field, 'select_all_label', '');
6280 if(select_all_label == '') { select_all_label = this.language('select_all_label'); }
6281 select_all_row.data[select_all_value_index] = select_all_label;
6282 select_all_row.data[select_all_label_index] = select_all_label;
6283 data_rows.unshift(select_all_row);
6284 }
6285
6286 // Randomize data rows?
6287 if(rows_randomize) { data_rows = this.array_randomize(data_rows); }
6288
6289 // Cycle through rows
6290 for(var data_row_index in data_rows) {
6291
6292 if(!data_rows.hasOwnProperty(data_row_index)) { continue; }
6293
6294 // Get row of data from data grid
6295 var data_row = data_rows[data_row_index];
6296
6297 // Skip null rows
6298 if(
6299 (data_row === null) ||
6300 (typeof data_row !== 'object') ||
6301 (typeof data_row.data !== 'object')
6302 ) {
6303 continue;
6304 }
6305
6306 // Is this the last row?
6307 var last_row = !invalid_feedback_last_row || (data_row_index == (data_rows.length - 1));
6308
6309 // Mask values
6310 var mask_values_row = $.extend(true, {}, mask_values_field);
6311
6312 // Clear values
6313 mask_values_row.data_grid_row_value = '';
6314 mask_values_row.data_grid_row_action_variable = '';
6315 mask_values_row.data_grid_row_label = '';
6316 mask_values_row.data_grid_row_price = '';
6317 mask_values_row.data_grid_row_price_currency = '';
6318 mask_values_row.data_grid_row_woocommerce_cart = '';
6319
6320 // Mask values - Data mask fields
6321 for(var mask_row_lookup in mask_row_lookup_array) {
6322
6323 if(!mask_row_lookup_array.hasOwnProperty(mask_row_lookup)) { continue; }
6324
6325 // Clear value
6326 mask_values_row[mask_row_lookup] = '';
6327
6328 var data_column_index = mask_row_lookup_array[mask_row_lookup];
6329
6330 if(data_column_index === false) { continue; }
6331 if(typeof data_row.data[data_column_index] === 'undefined') { continue; }
6332
6333 var mask_row_lookup_value = data_row.data[data_column_index];
6334 if(mask_row_lookup_value === null) { mask_row_lookup_value = ''; }
6335
6336 mask_row_lookup_value = this.parse_variables_process(mask_row_lookup_value.toString(), section_repeatable_index, false, field, 'data_grid_row', calc_register).output;
6337
6338 // HTML version of lookup (This is used for encoding labels in values, e.g. price_select option values)
6339 mask_values_row[mask_row_lookup + '_html'] = this.esc_html(mask_row_lookup_value);
6340
6341 // Range slider percentage
6342 switch(field.type) {
6343
6344 case 'range' :
6345 case 'price_range' :
6346
6347 // Get min values
6348 var range_min = this.get_object_meta_value(field, 'min', 0);
6349 if((range_min == '') || isNaN(range_min)) { range_min = 0; }
6350
6351 // Get max value
6352 var range_max = this.get_object_meta_value(field, 'max', 100);
6353 if((range_max == '') || isNaN(range_max)) { range_max = 100; }
6354
6355 // Get range value
6356 var range_value = this.get_number(mask_row_lookup_value, true);
6357
6358 // Get range
6359 var range = parseFloat(range_max) - parseFloat(range_min);
6360
6361 // Get percentage
6362 var range_percentage = (range > 0) ? (((range_value - range_min) / range) * 100) : 0;
6363
6364 var mask_row_lookup_percentage = mask_row_lookup + '_percentage';
6365 mask_values_row[mask_row_lookup_percentage] = range_percentage;
6366
6367 // Build datagrid row variable
6368 if(mask_row_lookup.indexOf('_field_percentage') !== -1) { mask_values_row.data_grid_row_value_percentage = range_percentage; }
6369
6370 break;
6371 }
6372
6373 // Store before any encoding
6374 mask_values_row[mask_row_lookup + '_compare'] = this.esc_html(mask_row_lookup_value);
6375
6376 // Check for HTML encoding or a price
6377 var price = false;
6378 if(typeof $.WS_Form.meta_keys[mask_row_lookup] !== 'undefined') {
6379
6380 // Get meta key config
6381 var mask_row_lookup_config = $.WS_Form.meta_keys[mask_row_lookup];
6382
6383 // Check for HTML encoding
6384 var esc_html = (typeof mask_row_lookup_config.h !== 'undefined') ? mask_row_lookup_config.h : false;
6385 if(esc_html) { mask_row_lookup_value = this.esc_html(mask_row_lookup_value); }
6386
6387 // Check for price
6388 var price = (typeof mask_row_lookup_config.pr !== 'undefined') ? mask_row_lookup_config.pr : false;
6389 }
6390
6391 if(price) {
6392
6393 var mask_row_lookup_value_number = this.get_number(mask_row_lookup_value);
6394 var mask_row_lookup_currency = mask_row_lookup + '_currency';
6395 var mask_row_lookup_value_currency= this.get_price(mask_row_lookup_value_number);
6396 mask_values_row[mask_row_lookup_currency] = mask_row_lookup_value_currency;
6397 mask_values_row[mask_row_lookup] = mask_row_lookup_value_number;
6398
6399 // Build datagrid row variable
6400 if(mask_row_lookup.indexOf('_price_field_price') !== -1) { mask_values_row.data_grid_row_price = mask_row_lookup_value_number; }
6401 if(mask_row_lookup_currency.indexOf('_price_field_price_currency') !== -1) { mask_values_row.data_grid_row_price_currency = mask_row_lookup_value_currency; }
6402
6403 } else {
6404
6405 mask_values_row[mask_row_lookup] = mask_row_lookup_value;
6406 }
6407
6408 // Build datagrid row variables
6409 if(mask_row_lookup.indexOf('_field_value') !== -1) { mask_values_row.data_grid_row_value = mask_row_lookup_value; }
6410 if(mask_row_lookup.indexOf('_field_parse_variable') !== -1) { mask_values_row.data_grid_row_action_variable = mask_row_lookup_value; }
6411 if(mask_row_lookup.indexOf('_field_label') !== -1) { mask_values_row.data_grid_row_label = mask_row_lookup_value; }
6412 if(mask_row_lookup.indexOf('_field_wc') !== -1) { mask_values_row.data_grid_row_woocommerce_cart = mask_row_lookup_value; }
6413 }
6414
6415 // Check for row value mask (Used by price_select, price_radio and price_checkbox)
6416 if(typeof field_type_config.mask_row_value !== 'undefined') {
6417
6418 mask_values_row.row_value = this.mask_parse(field_type_config.mask_row_value, mask_values_row);
6419
6420 // Re-parse if user has variables in the row value
6421 if(mask_values_row.row_value.indexOf('#') > -1) {
6422
6423 mask_values_row.row_value = this.mask_parse(mask_values_row.row_value, mask_values_row);
6424 }
6425 }
6426 if(typeof field_type_config.mask_row_price !== 'undefined') {
6427
6428 mask_values_row.row_price = this.mask_parse(field_type_config.mask_row_price, mask_values_row);
6429 }
6430
6431 // Mask values row
6432 if(data_row.select_all) {
6433
6434 mask_values_row.row_id = this.form_id_prefix + 'field-' + field.id + '-group-' + data_group_index + '-row-' + data_row.id + repeatable_suffix;
6435
6436 } else {
6437
6438 mask_values_row.row_id = this.form_id_prefix + 'field-' + field.id + '-row-' + data_row.id + repeatable_suffix;
6439 }
6440 mask_values_row.data_id = data_row.id;
6441
6442 // Copy to row label and field mask values
6443 var mask_values_row_field = $.extend(true, {}, mask_values_row);
6444 var mask_values_row_label = $.extend(true, {}, mask_values_row);
6445 mask_values_row_label.label_id = this.form_id_prefix + 'label-' + field.id + repeatable_suffix;
6446 mask_values_row_label.label_row_id = this.form_id_prefix + 'label-' + field.id + '-row-' + data_row.id + repeatable_suffix;
6447
6448 // Build default extra values
6449 var extra_values_default = [];
6450 if(
6451 (!has_value && data_row.default) ||
6452 (has_value && (datagrid_column_value !== false) && has_value_indexof && (value.indexOf(mask_values_row[datagrid_column_value + '_compare']) > -1)) ||
6453 (has_value && (typeof mask_values_row.row_value !== 'undefined') && has_value_indexof && (value.indexOf(mask_values_row.row_value) > -1))
6454 ) { extra_values_default.default = mask_row_default; }
6455 if(data_row.disabled) { extra_values_default.disabled = mask_row_disabled; }
6456 if(data_row.required) { extra_values_default.required = mask_row_required; }
6457
6458 // These attributes we manually push across to rows
6459 extra_values_default.dedupe_value_scope = dedupe_value_scope;
6460 extra_values_default.hidden_bypass = hidden_bypass;
6461 extra_values_default.exclude_cart_total = exclude_cart_total;
6462
6463 mask_values_row.attributes = mask_values_row_attributes_source;
6464 mask_values_row_label.attributes = '';
6465 mask_values_row_field.attributes = '';
6466
6467 // Row - Attributes
6468 var extra_values = $.extend(true, [], extra_values_default);
6469
6470 if(!is_submit) {
6471
6472 // class (Inline)
6473 var class_inline_array = (orientation == 'horizontal') ? this.get_field_value_fallback(field.type, label_position, 'class_inline', false, false, sub_type) : false;
6474 if(class_inline_array !== false) { extra_values.class = class_inline_array.join(' '); }
6475
6476 // class (Row)
6477 var class_row_array = this.get_field_value_fallback(field.type, label_position, 'class_row', false, false, sub_type)
6478 if(class_row_array !== false) { extra_values.class = (class_inline_array !== false) ? (extra_values.class + ' ' + class_row_array.join(' ')) : class_row_array.join(' '); }
6479
6480 // class if disabled
6481 if(data_row.disabled) {
6482 var class_row_disabled_array = this.get_field_value_fallback(field.type, label_position, 'class_row_disabled', false, false, sub_type);
6483 if(class_row_disabled_array !== false) { extra_values.class += ' ' + class_row_disabled_array.join(' '); }
6484 }
6485 }
6486
6487 var mask_row_attributes = ($.extend(true, [], this.get_field_value_fallback(field.type, label_position, 'mask_row_attributes', [], false, sub_type)));
6488 if(mask_row_attributes.length > 0) {
6489 var get_attributes_return = this.get_attributes(field, mask_row_attributes, extra_values, section_repeatable_index);
6490 mask_values_row.attributes += ' ' + get_attributes_return.attributes;
6491 }
6492
6493 // Skip hidden rows
6494 if((typeof data_row.hidden !== 'undefined') && data_row.hidden && !is_submit) {
6495
6496 switch(field.type) {
6497
6498 // Select
6499 case 'select' :
6500
6501 continue;
6502
6503 // Checkboxes and radios
6504 default :
6505
6506 mask_values_row.attributes += ' style="display: none;"';
6507 }
6508 }
6509
6510 // Orientation
6511 if(
6512 (orientation == 'grid') &&
6513 (orientation_row_class != '')
6514 ) {
6515
6516 mask_values_row.attributes = this.attribute_modify(mask_values_row.attributes, 'class', orientation_row_class, true);
6517 }
6518
6519 // Row - Label - Attributes
6520 var extra_values = $.extend(true, [], extra_values_default);
6521
6522 // Class
6523 var class_row_field_label_array = this.get_field_value_fallback(field.type, label_position, 'class_row_field_label', false, false, sub_type);
6524 if(class_row_field_label_array !== false) { extra_values.class = class_row_field_label_array.join(' '); }
6525
6526 var mask_row_label_attributes = ($.extend(true, [], this.get_field_value_fallback(field.type, label_position, 'mask_row_label_attributes', [], false, sub_type)));
6527
6528 if(is_submit) {
6529
6530 var mask_row_label_attributes = submit_attributes_field_label.filter(function(val) {
6531
6532 return mask_row_label_attributes.indexOf(val) != -1;
6533 });
6534 }
6535
6536 if(mask_row_label_attributes.length > 0) {
6537 var get_attributes_return = this.get_attributes(field, mask_row_label_attributes, extra_values, section_repeatable_index);
6538 mask_values_row_label.attributes += ' ' + get_attributes_return.attributes;
6539 }
6540
6541 // Row - Field - Attributes
6542 var extra_values = $.extend(true, [], extra_values_default);
6543
6544 // class
6545 var class_row_field_array = this.get_field_value_fallback(field.type, label_position, 'class_row_field', false, false, sub_type);
6546 if(class_row_field_array !== false) { extra_values.class = class_row_field_array.join(' '); }
6547
6548 // class (Field setting)
6549 if(!is_submit && (class_field != '')) { extra_values.class += ' ' + class_field.trim(); }
6550
6551 // aria-labelledby
6552 extra_values.aria_labelledby = mask_values_row_label.label_row_id;
6553
6554 // Row attributes
6555 if(
6556 (!has_value && data_row.default) ||
6557 (has_value && (datagrid_column_value !== false) && has_value_indexof && (value.indexOf(mask_values_row[datagrid_column_value + '_compare']) > -1))
6558 ) { extra_values.default = mask_row_default; }
6559
6560 if(data_row.disabled) { extra_values.disabled = mask_row_disabled; }
6561 if(data_row.required) { extra_values.required = mask_row_required; }
6562
6563 // Copy field level attributes to row
6564 extra_values.required_row = attributes_values_field.required;
6565
6566 // Build row field attributes
6567 var mask_row_field_attributes = ($.extend(true, [], this.get_field_value_fallback(field.type, label_position, 'mask_row_field_attributes', [], false, sub_type)));
6568
6569 if(is_submit) {
6570
6571 var mask_row_field_attributes = submit_attributes_field.filter(function(val) {
6572
6573 return mask_row_field_attributes.indexOf(val) != -1;
6574 });
6575 }
6576
6577 if(mask_row_field_attributes.length > 0) {
6578 var get_attributes_return = this.get_attributes(field, mask_row_field_attributes, extra_values, section_repeatable_index);
6579 mask_values_row_field.attributes += ' ' + get_attributes_return.attributes;
6580 }
6581 if(mask_values_row_field.attributes != '') { mask_values_row_field.attributes = ' ' + mask_values_row_field.attributes; }
6582
6583 if(typeof data_row.select_all !== 'undefined') {
6584
6585 mask_values_row_field.attributes += ' data-wsf-select-all';
6586 }
6587
6588 // Hierarchy (Vertical only)
6589 if(
6590 (typeof data_row.hierarchy !== 'undefined') &&
6591 (orientation == '') &&
6592 !rows_randomize
6593 ) {
6594
6595 switch(field.type) {
6596
6597 case 'checkbox' :
6598 case 'price_checkbox' :
6599 case 'radio' :
6600 case 'price_radio' :
6601
6602 mask_values_row.attributes += ' data-wsf-hierarchy="' + this.esc_attr(data_row.hierarchy) + '"';
6603 break;
6604
6605 case 'select' :
6606 case 'price_select' :
6607
6608 if(data_row.hierarchy > 0) {
6609
6610 mask_values_row.select_field_label = '&nbsp;&nbsp;&nbsp;'.repeat(data_row.hierarchy) + mask_values_row.select_field_label;
6611 }
6612 break;
6613 }
6614 }
6615
6616 // Parse invalid feedback for rows
6617 if(invalid_feedback_render && !invalid_feedback_last_row) {
6618
6619 invalid_feedback_id = this.form_id_prefix + 'invalid-feedback-' + field.id + '-row-' + data_row.id + repeatable_suffix;
6620 mask_values_invalid_feedback.invalid_feedback_id = invalid_feedback_id;
6621 if(invalid_feedback_render) {
6622
6623 var invalid_feedback_parsed = this.mask_parse(mask_invalid_feedback, mask_values_invalid_feedback);
6624
6625 } else {
6626
6627 var invalid_feedback_parsed = '';
6628 }
6629 }
6630
6631 // Invalid feedback
6632 mask_values_row_field.invalid_feedback = (last_row ? invalid_feedback_parsed : '');
6633 mask_values_row_label.invalid_feedback = (last_row ? invalid_feedback_parsed : '');
6634
6635 // Parse field
6636 var row_field_html = this.mask_parse(mask_row_field, mask_values_row_field);
6637 mask_values_row_label.row_field = row_field_html;
6638 mask_values_row.row_field = row_field_html;
6639
6640 // Parse label
6641 var row_field_label = this.mask_parse(mask_row_label, mask_values_row_label);
6642 mask_values_row.row_label = row_field_label;
6643
6644 // Parse row
6645 group += this.mask_parse(mask_row, mask_values_row);
6646
6647 // Increment data row count
6648 data_row_count++;
6649 }
6650 }
6651
6652 // Check for group wrapper
6653 if(mask_group_wrapper != '') {
6654
6655 var mask_group_wrapper_parse = mask_group_wrapper;
6656
6657 var mask_values_group_wrapper = {
6658
6659 group : group,
6660 attributes : ''
6661 };
6662
6663 if(
6664
6665 (orientation == 'grid') &&
6666 (orientation_group_wrapper_class != '')
6667 ) {
6668
6669 mask_values_group_wrapper.attributes = ' class="' + this.esc_attr(orientation_group_wrapper_class) + '"';
6670 }
6671
6672 if(
6673 !mask_group_use &&
6674 label_render &&
6675 (mask_group_wrapper_role !== false) &&
6676 (mask_field_label.indexOf('#attributes') !== -1)
6677 ) {
6678
6679 if(mask_group_wrapper.indexOf(' role=') === -1) { mask_values_group_wrapper.attributes = this.attribute_modify(mask_values_group_wrapper.attributes, 'role', mask_group_wrapper_role); }
6680 mask_values_group_wrapper.attributes = this.attribute_modify(mask_values_group_wrapper.attributes, 'aria-labelledby', this.form_id_prefix + 'label-' + field.id + repeatable_suffix);
6681 }
6682 if(mask_group_use && (mask_group_wrapper_role !== false)) {
6683
6684 mask_group_wrapper_parse = mask_group_wrapper_parse.replace(/\srole=(["'])(?:radio)?group\1/g, '');
6685 mask_group_wrapper_parse = mask_group_wrapper_parse.replace(/\srole=(?:radio)?group(?=[\s>])/g, '');
6686 }
6687 group = this.mask_parse(mask_group_wrapper_parse, mask_values_group_wrapper);
6688 }
6689
6690 if((mask_group !== false) && mask_group_use) {
6691
6692 // Parse mask_group
6693 mask_values_group.group = group;
6694 data += this.mask_parse(mask_group, mask_values_group);
6695
6696 } else {
6697
6698 // Ignore group mask, there is only one group
6699 data += group;
6700 }
6701 }
6702 }
6703
6704 // Add to mask array
6705 if(data_row_count > 0) {
6706
6707 mask_values_field.datalist = data;
6708
6709 } else {
6710
6711 mask_values_field.datalist = '';
6712 }
6713
6714 // Field - Attributes
6715 if(mask_field_attributes.length > 0) {
6716
6717 var extra_values = [];
6718
6719 // list
6720 if(typeof mask_values_group !== 'undefined') {
6721 if(
6722 (typeof mask_values_group.group_id !== 'undefined') &&
6723 data_row_count > 0
6724
6725 ) { extra_values.list = mask_values_group.group_id; }
6726 }
6727
6728 // aria_labelledby (Used if aria_label is blank)
6729 var aria_label = this.strip_html(this.get_object_meta_value(field, 'aria_label', false, false, true));
6730 if(aria_label === '') {
6731
6732 var field_label_stripped = this.strip_html(field.label);
6733
6734 if(
6735 label_render &&
6736 (mask_field_label.indexOf('#attributes') !== -1) && // Without attributes, we cannot reference the ID
6737 (field_label_stripped === field.label)
6738 ) {
6739
6740 // Use aria_labelledby instead of aria_label
6741 extra_values.aria_labelledby = this.form_id_prefix + 'label-' + field.id + repeatable_suffix;
6742
6743 } else {
6744
6745 // Set to label
6746 extra_values.aria_label = field_label_stripped;
6747 }
6748 }
6749
6750 // aria_describedby
6751 if(has_help) { extra_values.aria_describedby = help_id; }
6752
6753 // class (Config)
6754 var class_field_array = this.get_field_value_fallback(field.type, label_position, 'class_field', false, false, sub_type);
6755 if(class_field_array !== false) { extra_values.class = class_field_array.join(' '); }
6756
6757 // class (Field setting)
6758 if(class_field != '') { extra_values.class += ' ' + class_field.trim(); }
6759
6760 // Process attributes
6761 var get_attributes_return = this.get_attributes(field, mask_field_attributes, extra_values, section_repeatable_index);
6762
6763 // Store as mask value
6764 if(get_attributes_return.attributes != '') { mask_values_field.attributes += ' ' + get_attributes_return.attributes; }
6765 }
6766
6767 // Field Label - Attributes
6768 if(mask_field_label_attributes.length > 0) {
6769
6770 var extra_values = [];
6771
6772 // class
6773 var class_field_label_array = this.get_field_value_fallback(field.type, label_position, 'class_field_label', false, false, sub_type);
6774 if(class_field_label_array !== false) { extra_values.class = class_field_label_array.join(' '); }
6775
6776 // Process attributes
6777 var get_attributes_return = this.get_attributes(field, mask_field_label_attributes, extra_values, section_repeatable_index);
6778
6779 // Store as mask value
6780 if(get_attributes_return.attributes != '') { mask_values_field_label.attributes += ' ' + get_attributes_return.attributes; }
6781 }
6782
6783 // Parse help mask append
6784 mask_values_help.help_append_separator = has_help ? this.mask_parse(mask_help_append_separator, mask_values_help) : '';
6785 if(mask_help_append != '') {
6786
6787 mask_values_help.text_clear = this.get_object_meta_value(field, 'text_clear', '');
6788 if(mask_values_help.text_clear == '') { mask_values_help.text_clear = this.language('clear'); }
6789
6790 mask_values_help.text_reset = this.get_object_meta_value(field, 'text_reset', '');
6791 if(mask_values_help.text_reset == '') { mask_values_help.text_reset = this.language('reset'); }
6792
6793 var help_append_parsed = this.mask_parse(mask_help_append, mask_values_help);
6794
6795 } else {
6796
6797 var help_append_parsed = '';
6798 }
6799 mask_values_help.help_append = help_append_parsed;
6800 mask_values_help.attributes = '';
6801
6802 // Parse help mask
6803 var help_parsed = (has_help || help_append_parsed != '') ? this.mask_parse(mask_help, mask_values_help) : '';
6804
6805 // Pre and post help
6806 mask_values_field.pre_help = mask_values_field_label.pre_help = ((help_position == 'bottom') ? '' : help_parsed);
6807 mask_values_field.post_help = mask_values_field_label.post_help = ((help_position == 'bottom') ? help_parsed : '');
6808
6809 // Legacy
6810 mask_values_field.help = help_parsed;
6811 mask_values_field_label.help = help_parsed;
6812
6813 // Trim attributes
6814 if(mask_values_field.attributes != '') { mask_values_field.attributes = ' ' + mask_values_field.attributes.trim(); }
6815 if(mask_values_field_label.attributes != '') { mask_values_field_label.attributes = ' ' + mask_values_field_label.attributes.trim(); }
6816
6817 // Parse label
6818 var label_parsed = this.mask_parse(mask_field_label, mask_values_field_label);
6819
6820 // Parse label wrapper
6821 if(label_parsed != '' && !mask_wrappers_drop) {
6822
6823 var mask_field_label_wrapper = this.get_field_value_fallback(field.type, label_position, 'mask_field_label_wrapper', false, false, sub_type);
6824 if(mask_field_label_wrapper !== false) {
6825 var mask_field_label_wrapper_values = {'label': label_parsed};
6826 label_parsed = this.mask_parse(mask_field_label_wrapper, mask_field_label_wrapper_values);
6827 }
6828 }
6829
6830 var field_in_label = (mask_field_label.indexOf('#field') !== -1);
6831
6832 // Build field mask based upon label position
6833 switch(label_position) {
6834
6835 // Bottom / Inside
6836 case 'inside' :
6837 case 'bottom' :
6838
6839 mask_values_field.pre_label = '';
6840 mask_values_field.post_label = field_in_label ? mask_values_field.label : label_parsed;
6841 break;
6842
6843 // Top
6844 case 'top' :
6845
6846 mask_values_field.pre_label = field_in_label ? mask_values_field.label : label_parsed;
6847 mask_values_field.post_label = '';
6848 break;
6849
6850 default :
6851
6852 mask_values_field.pre_label = '';
6853 mask_values_field.post_label = '';
6854 mask_values_field_label.pre_label = '';
6855 mask_values_field_label.post_label = '';
6856 break;
6857 }
6858
6859 // Input group
6860 if(
6861 (prepend !== '') ||
6862 (append !== '')
6863 ) {
6864
6865 var mask_values_input_group = $.extend(true, {}, mask_values_field);
6866 mask_values_input_group.invalid_feedback = mask_values_field.invalid_feedback;
6867 mask_values_input_group.pre_help = mask_values_field.pre_help;
6868 mask_values_input_group.post_help = mask_values_field.post_help;
6869 mask_values_input_group.pre_label = mask_values_field.pre_label;
6870 mask_values_input_group.post_label = mask_values_field.post_label;
6871 mask_values_field.invalid_feedback = '';
6872 mask_values_field.pre_help = '';
6873 mask_values_field.post_help = '';
6874 mask_values_field.pre_label = '';
6875 mask_values_field.post_label = '';
6876
6877 // Legacy
6878 mask_values_input_group.help = mask_values_field.help;
6879 mask_values_field.help = '';
6880 }
6881
6882 // Parse field
6883 var field_parsed = this.mask_parse(mask_field, mask_values_field);
6884
6885 // Input group parsing
6886 if(
6887 (prepend !== '') ||
6888 (append !== '')
6889 ) {
6890
6891 // Calculate column widths
6892 var col_small_prepend_factor = this.get_field_value_fallback(field.type, label_position, 'col_small_prepend_factor', false, false, sub_type);
6893 var col_small_append_factor = this.get_field_value_fallback(field.type, label_position, 'col_small_append_factor', false, false, sub_type);
6894 if(
6895 (col_small_prepend_factor !== false) &&
6896 (col_small_append_factor !== false)
6897 ) {
6898
6899 var col_small_prepend = (prepend !== '') ? Math.round(framework_column_count * col_small_prepend_factor, 0) : 0;
6900 mask_values_input_group.col_small_prepend = col_small_prepend;
6901
6902 var col_small_append = (append !== '') ? Math.round(framework_column_count * col_small_append_factor, 0) : 0;
6903 mask_values_input_group.col_small_append = col_small_append;
6904
6905 mask_values_input_group.col_small_field = framework_column_count - (col_small_prepend + col_small_append);
6906 }
6907
6908 // Field
6909 mask_values_input_group.field = field_parsed;
6910
6911 var mask_field_input_group_field = this.get_field_value_fallback(field.type, label_position, 'mask_field_input_group_field', '#field', false, sub_type);
6912
6913 if(mask_field_input_group_field !== '') {
6914
6915 field_parsed = this.mask_parse(mask_field_input_group_field, mask_values_input_group);
6916 }
6917
6918 // Prepend
6919 if(prepend !== '') {
6920
6921 mask_values_input_group.prepend = prepend;
6922
6923 var mask_field_input_group_prepend = this.get_field_value_fallback(field.type, label_position, 'mask_field_input_group_prepend', '#prepend', false, sub_type);
6924
6925 if(mask_field_input_group_prepend !== '') {
6926
6927 var prepend_parsed = this.mask_parse(mask_field_input_group_prepend, mask_values_input_group);
6928
6929 field_parsed = prepend_parsed + field_parsed;
6930 }
6931 }
6932
6933 // Append
6934 if(append !== '') {
6935
6936 mask_values_input_group.append = append;
6937
6938 var mask_field_input_group_append = this.get_field_value_fallback(field.type, label_position, 'mask_field_input_group_append', '#append', false, sub_type);
6939
6940 if(mask_field_input_group_append !== '') {
6941
6942 var append_parsed = this.mask_parse(mask_field_input_group_append, mask_values_input_group);
6943
6944 field_parsed = field_parsed + append_parsed;
6945 }
6946 }
6947
6948 // Wrapper
6949 var mask_field_input_group = this.get_field_value_fallback(field.type, label_position, 'mask_field_input_group', '#field', false, sub_type);
6950
6951 // Wrapper classes
6952 var css_input_group_array = [];
6953 if(prepend !== '') { css_input_group_array.push('wsf-input-group-has-prepend'); }
6954 if(append !== '') { css_input_group_array.push('wsf-input-group-has-append'); }
6955 var css_input_group = css_input_group_array.join(' ');
6956 if(css_input_group) { css_input_group = ' ' + css_input_group; }
6957
6958 // Final parse
6959 mask_values_input_group.css_input_group = css_input_group;
6960 mask_values_input_group.field = field_parsed;
6961
6962 field_parsed = this.mask_parse(mask_field_input_group, mask_values_input_group);
6963 }
6964
6965 // Check to see if #field is in the label
6966 if(field_in_label) {
6967
6968 // Render field in label
6969 mask_values_field_label.field = field_parsed; // Make the field available in the label mask values
6970 label_parsed = this.mask_parse(mask_field_label, mask_values_field_label);
6971
6972 // Finished with field
6973 field_parsed = '';
6974 mask_values_field_label.field = '';
6975 }
6976
6977 // Check to see if the #label is in the field
6978 var label_in_field = (mask_field.indexOf('#label') !== -1);
6979 if(label_in_field) {
6980
6981 // Render label in field
6982 mask_values_field.label = label_parsed; // Make the label available in the field mask values
6983 field_parsed = this.mask_parse(mask_field, mask_values_field);
6984
6985 // Finished with label
6986 label_parsed = '';
6987 mask_values_field.label = '';
6988 }
6989
6990 // Parse field wrapper
6991 if(field_parsed != '' && !mask_wrappers_drop) {
6992
6993 var mask_field_wrapper = this.get_field_value_fallback(field.type, label_position, 'mask_field_wrapper', false, false, sub_type);
6994 if(mask_field_wrapper !== false) {
6995 var mask_field_wrapper_values = {'field': field_parsed};
6996 field_parsed = this.mask_parse(mask_field_wrapper, mask_field_wrapper_values);
6997 }
6998 }
6999
7000 // Build field mask based upon label position
7001 switch(label_position) {
7002
7003 // Right
7004 case 'right' :
7005
7006 field_parsed = field_parsed + label_parsed;
7007 break;
7008
7009 // Left
7010 case 'left' :
7011
7012 field_parsed = label_parsed + field_parsed;
7013 break;
7014
7015 default :
7016
7017 if(field_in_label) {
7018
7019 field_parsed = label_parsed;
7020 }
7021 }
7022
7023 // Final parse
7024 mask_values_field.field = field_parsed;
7025 field_html = this.mask_parse(mask, mask_values_field);
7026
7027 return field_html;
7028 }
7029
7030 // Modify attributes
7031 $.WS_Form.prototype.attribute_modify = function(attributes_string, key, value, append) {
7032
7033 var ws_this = this;
7034
7035 var key_found = false;
7036 var return_attribute_string = '';
7037
7038 // Run through each attribute key / value
7039 var obj = $('<div ' + attributes_string + ' />');
7040 obj.each(function() {
7041
7042 $.each(this.attributes, function() {
7043
7044 if(this.specified) {
7045
7046 var attribute_key = this.name;
7047 var attribute_value = this.value;
7048
7049 if(attribute_key == key) {
7050
7051 if(append) {
7052
7053 attribute_value += ' ' + value;
7054
7055 } else {
7056
7057 attribute_value = value;
7058 }
7059 attribute_value.trim();
7060
7061 key_found = true;
7062 }
7063
7064 return_attribute_string += ' ' + attribute_key;
7065 if(attribute_value !== '') { return_attribute_string += '="' + ws_this.esc_attr(attribute_value) + '"'; }
7066 }
7067 });
7068 });
7069
7070 if(!key_found) {
7071
7072 return_attribute_string += ' ' + key;
7073 if(value !== '') { return_attribute_string += '="' + this.esc_attr(value) + '"'; }
7074 }
7075
7076 return return_attribute_string;
7077 }
7078
7079
7080 // Get attributes
7081 $.WS_Form.prototype.get_attributes = function(object, mask_attributes, extra_values, section_repeatable_index) {
7082
7083 if(typeof extra_values !== 'object') { extra_values = false; }
7084
7085 // Build attributes array
7086 var attributes = [];
7087 var attribute_values = [];
7088
7089 if(mask_attributes !== false) {
7090
7091 for(var mask_attributes_key in mask_attributes) {
7092
7093 if(!mask_attributes.hasOwnProperty(mask_attributes_key)) { continue; }
7094
7095 var mask_attribute_meta_key = mask_attributes[mask_attributes_key];
7096
7097 // Skip unknown meta_keys
7098 if(typeof $.WS_Form.meta_keys[mask_attribute_meta_key] === 'undefined') { continue; }
7099
7100 var meta_key = $.WS_Form.meta_keys[mask_attribute_meta_key];
7101
7102 // Read meta key mask data
7103 if(this.is_admin) {
7104
7105 var meta_key_mask = (typeof meta_key.mask !== 'undefined') ? meta_key.mask : '';
7106 var meta_key_mask_disregard_on_empty = (typeof meta_key.mask_disregard_on_empty !== 'undefined') ? meta_key.mask_disregard_on_empty : false;
7107 var meta_key_mask_disregard_on_zero = (typeof meta_key.mask_disregard_on_zero !== 'undefined') ? meta_key.mask_disregard_on_zero : false;
7108 var meta_key_default = (typeof meta_key.default !== 'undefined') ? meta_key.default : '';
7109
7110 } else {
7111
7112 var meta_key_mask = (typeof meta_key.m !== 'undefined') ? meta_key.m : '';
7113 var meta_key_mask_disregard_on_empty = (typeof meta_key.e !== 'undefined') ? meta_key.e : false;
7114 var meta_key_mask_disregard_on_zero = (typeof meta_key.z !== 'undefined') ? meta_key.z : false;
7115 var meta_key_default = (typeof meta_key.d !== 'undefined') ? meta_key.d : '';
7116 }
7117
7118 if(extra_values !== false) {
7119
7120 // Use extra values
7121 if(typeof extra_values[mask_attribute_meta_key] !== 'undefined') {
7122
7123 var meta_value = extra_values[mask_attribute_meta_key].trim();
7124
7125 } else {
7126
7127 var meta_value = '';
7128 }
7129
7130 } else {
7131
7132 // If meta_key key parameter is set, use that to get the object meta value
7133 if(this.is_admin) {
7134
7135 var get_object_meta_value_key = (typeof meta_key.key !== 'undefined') ? meta_key.key : mask_attribute_meta_key;
7136
7137 } else {
7138
7139 var get_object_meta_value_key = (typeof meta_key.k !== 'undefined') ? meta_key.k : mask_attribute_meta_key;
7140 }
7141
7142 // Get value
7143 var field_part = (typeof meta_key.c !== 'undefined') ? meta_key.c : false;
7144 if(field_part !== false) {
7145
7146 var meta_value = this.get_object_meta_value(object, get_object_meta_value_key, meta_key_default, false, false);
7147 meta_value = this.parse_variables_process(meta_value, section_repeatable_index, false, object, field_part).output
7148
7149 } else {
7150
7151 var meta_value = this.get_object_meta_value(object, get_object_meta_value_key, meta_key_default, false, true);
7152 }
7153
7154 // Check for global fallback
7155 if(
7156 (meta_value == '') &&
7157 (typeof meta_key.g !== 'undefined')
7158 ) {
7159
7160 var required_setting_global_meta_key = meta_key.g;
7161
7162 if(typeof $.WS_Form.settings_plugin[required_setting_global_meta_key] !== 'undefined') {
7163
7164 if($.WS_Form.settings_plugin[required_setting_global_meta_key]) {
7165
7166 meta_value = $.WS_Form.settings_plugin[required_setting_global_meta_key];
7167 }
7168 }
7169 }
7170
7171 // Remember value
7172 attribute_values[get_object_meta_value_key] = meta_value;
7173 }
7174
7175 // Strip HTML tags
7176 var meta_key_strip_tags = this.is_admin ? ((typeof meta_key.strip_tags !== 'undefined') ? meta_key.strip_tags : false) : ((typeof meta_key.s !== 'undefined') ? meta_key.s : false);
7177 if(meta_key_strip_tags) { meta_value = this.strip_html(meta_value); }
7178
7179 // Attribute encode value
7180 meta_value = this.esc_attr(meta_value);
7181
7182 // Parse mask
7183 var attribute_meta_value = this.mask_parse(meta_key_mask, {'value': meta_value});
7184
7185 // Push attribute key value pair to attributes array
7186 if(
7187 ((meta_key_mask_disregard_on_empty && (meta_value != '')) || !meta_key_mask_disregard_on_empty)
7188 &&
7189 ((meta_key_mask_disregard_on_zero && (parseInt(meta_value, 10) != 0)) || !meta_key_mask_disregard_on_zero)
7190
7191 ) {
7192
7193 // Push field to attribute array
7194 attributes.push(attribute_meta_value);
7195
7196 // Remove this key from the array (so that it is not re-processed further down)
7197 delete mask_attributes[mask_attributes_key];
7198 }
7199 }
7200 }
7201
7202 // Remove empty array elements
7203 mask_attributes = this.array_remove_empty(mask_attributes);
7204
7205 // Return field attributes string
7206 return {
7207
7208 'attributes': attributes.join(' '),
7209 'mask_attributes': mask_attributes,
7210 'attribute_values': attribute_values
7211 };
7212 }
7213
7214 // Get number
7215 $.WS_Form.prototype.get_number = function(number_input, default_value, process_currency, decimals) {
7216
7217 if(typeof default_value === 'undefined') { default_value = 0; }
7218 if(typeof process_currency === 'undefined') { process_currency = true; }
7219 if(typeof decimals === 'undefined') { decimals = false; }
7220
7221 // Convert numbers to text
7222 if(typeof number_input === 'number') { number_input = number_input.toString(); }
7223
7224 // Check input is a string
7225 if(typeof number_input !== 'string') { return 0; }
7226
7227 // Trim input
7228 number_input = number_input.trim();
7229
7230 // If input string contains currency symbol, process currency
7231 if(number_input.includes(ws_form_settings.currency_symbol)) {
7232
7233 process_currency = true;
7234 }
7235
7236 // Get decimal separator
7237 var decimal_separator = $.WS_Form.settings_plugin.price_decimal_separator;
7238
7239 // Convert from current currency
7240 if(process_currency) {
7241
7242 // Get thousand separator
7243 var thousand_separator = $.WS_Form.settings_plugin.price_thousand_separator;
7244
7245 // Get currency symbol
7246 var currency_symbol = ws_form_settings.currency_symbol;
7247
7248 // Decode HTML characters
7249 if(number_input.includes('&')) {
7250
7251 number_input = this.parse_html_entities(number_input);
7252 }
7253
7254 // Filter out currency symbol (We do not in isolation in case the currency symbol contains the decimal separator character, e.g. UAE)
7255 number_input = number_input.replace(currency_symbol, '');
7256
7257 // Ensure the decimal separator setting is included in the regex (Add ,. too in case default value includes alternatives)
7258 var number_input_regex = new RegExp('[^0-9-' + decimal_separator + ']', 'g');
7259 number_input = number_input.replace(number_input_regex, '');
7260
7261 if(
7262 (decimal_separator !== '') &&
7263 (thousand_separator !== '') &&
7264 (decimal_separator === thousand_separator)
7265 ) {
7266
7267 // Convert decimal separators to periods so parseFloat works
7268 if(number_input.slice(-3, -2) === decimal_separator) {
7269
7270 var decimal_index = (number_input.length - 3);
7271 number_input = number_input.substring(0, decimal_index) + '[dec]' + number_input.substring(decimal_index + 1);
7272 }
7273
7274 // Remove thousand separators
7275 number_input = number_input.replace(thousand_separator, '');
7276
7277 // Replace [dec] back to decimal separator for parseFloat
7278 number_input = number_input.replace('[dec]', '.');
7279
7280 } else {
7281
7282 // Replace decimal separator to period for parseFloat
7283 if(decimal_separator !== '') {
7284
7285 number_input = number_input.replace(decimal_separator, '.');
7286 }
7287 }
7288
7289 } else {
7290
7291 // Replace decimal separator to period for parseFloat
7292 if(decimal_separator !== '') {
7293
7294 number_input = number_input.replace(decimal_separator, '.');
7295 }
7296 }
7297
7298 // parseFloat converts decimal separator to period to ensure that function works
7299 var number_output = (number_input.trim() === '') ? default_value : (isNaN(number_input) ? default_value : parseFloat(number_input));
7300
7301 // Round
7302 if(decimals !== false) { number_output = this.get_number_rounded(parseFloat(number_output), decimals); }
7303
7304 return number_output;
7305 }
7306
7307 // Get number rounded
7308 $.WS_Form.prototype.get_number_rounded = function(value, decimals) {
7309
7310 return Number(Math.round(value + 'e' + decimals) + 'e-' + decimals);
7311 }
7312
7313 // Get number to step
7314 $.WS_Form.prototype.get_number_to_step = function(value, step) {
7315
7316 if(isNaN(step)) { return value; }
7317
7318 step || (step = 1.0);
7319 var inv = 1.0 / step;
7320 return Math.round(value * inv) / inv;
7321 }
7322
7323 // Get float
7324 $.WS_Form.prototype.get_float = function(number_input, default_value) {
7325
7326 if(typeof default_value === 'undefined') { default_value = 0; }
7327
7328 // Convert numbers to text
7329 if(typeof number_input === 'number') { number_input = number_input.toString(); }
7330
7331 // Check input is a string
7332 if(typeof number_input !== 'string') { return 0; }
7333
7334 // Trim input
7335 number_input = number_input.trim();
7336
7337 return parseFloat(number_input);
7338 }
7339
7340 // Get currency
7341 $.WS_Form.prototype.get_currency = function() {
7342
7343 var return_obj = {};
7344
7345 var price_decimals = parseInt($.WS_Form.settings_plugin.price_decimals, 10);
7346
7347 return_obj.prefix = ws_form_settings.currency_symbol;
7348 return_obj.suffix = '';
7349
7350 var currency_position = $.WS_Form.settings_plugin.currency_position;
7351 switch(currency_position) {
7352
7353 case 'right' :
7354
7355 return_obj.prefix = '';
7356 return_obj.suffix = ws_form_settings.currency_symbol;
7357 break;
7358
7359 case 'left_space' :
7360
7361 return_obj.prefix = ws_form_settings.currency_symbol + ' ';
7362 break;
7363
7364 case 'right_space' :
7365
7366 return_obj.prefix = '';
7367 return_obj.suffix = ' ' + ws_form_settings.currency_symbol;
7368 break;
7369 }
7370
7371 // Price decimals
7372 return_obj.decimals = (price_decimals < 0) ? 0 : price_decimals;
7373
7374 // Separators
7375 return_obj.decimal_separator = $.WS_Form.settings_plugin.price_decimal_separator || '.';
7376 return_obj.thousand_separator = $.WS_Form.settings_plugin.price_thousand_separator;
7377
7378 return return_obj;
7379 }
7380
7381 // Get price
7382 $.WS_Form.prototype.get_price = function(price_float, currency, currency_symbol_render) {
7383
7384 if(typeof price_float !== 'number') { price_float = parseFloat(price_float); }
7385 if(typeof currency === 'undefined') { currency = this.get_currency(); }
7386 if(typeof currency_symbol_render === 'undefined') { currency_symbol_render = true; }
7387
7388 var price = (currency_symbol_render ? currency.prefix : '') + this.replace_all(this.replace_all(price_float.toFixed(currency.decimals).replace(/\B(?=(\d{3})+(?!\d))/g, '[thousand]'), '.', currency.decimal_separator), '[thousand]', currency.thousand_separator) + (currency_symbol_render ? currency.suffix : '');
7389
7390 return price;
7391 }
7392
7393 // Get slug
7394 $.WS_Form.prototype.get_slug = function(input) {
7395
7396 // Parse HTML entities first
7397 input = this.parse_html_entities(input.toString());
7398
7399 return input
7400 .toString()
7401 .normalize('NFD')
7402 .replace(/[\u0300-\u036f]/g, '')
7403 .toLowerCase()
7404 .trim()
7405 .replace(/\s+/g, '-')
7406 .replace(/[^\w\-]+/g, '')
7407 .replace(/\-\-+/g, '-');
7408 }
7409
7410 // Parse HTML entities without use of DOM element for improved security
7411 $.WS_Form.prototype.parse_html_entities = function(str) {
7412
7413 // Basic named entity mapping
7414 var named_entities = {
7415 'amp': '&',
7416 'lt': '<',
7417 'gt': '>',
7418 'quot': '"',
7419 'apos': "'",
7420 'nbsp': ' ',
7421 'copy': '©',
7422 'reg': '®',
7423 'euro': '',
7424 'cent': '¢',
7425 'pound': '£',
7426 'yen': '¥',
7427 'hellip': ''
7428 };
7429
7430 // Decode numeric entities (decimal)
7431 str = str.replace(/&#([0-9]{1,5});/g, function(_, num_str) {
7432 var num = parseInt(num_str, 10);
7433 return String.fromCharCode(num);
7434 });
7435
7436 // Decode named entities
7437 str = str.replace(/&([a-z]+);/gi, function(_, name) {
7438 return named_entities[name.toLowerCase()] || '&' + name + ';';
7439 });
7440
7441 return str;
7442 }
7443
7444 // Add hidden field to canvas
7445 $.WS_Form.prototype.form_add_hidden_input = function(name, value, id, attributes, single_quote, type) {
7446
7447 // Do not add if it already exists
7448 var obj = $('input[name="' + this.esc_selector(name) + '"]', this.form_canvas_obj);
7449 if(obj.length && (name.indexOf('[]') === -1)) {
7450
7451 // Just set value if already exists and it is not an array
7452 obj.val(value);
7453 return;
7454 }
7455
7456 // Check function attributes
7457 if(typeof value === 'undefined') { value = ''; }
7458 if(typeof id === 'undefined') { id = false; }
7459 if(typeof attributes === 'undefined') { attributes = false; }
7460 if(typeof single_quote === 'undefined') { single_quote = false; }
7461 if(typeof type === 'undefined') { type = 'hidden'; }
7462
7463 // Append to form
7464 this.form_canvas_obj.append('<input type="' + this.esc_attr(type) + '" name="' + this.esc_attr(name) + '" value=' + (single_quote ? "'" : '"') + this.esc_attr(value) + (single_quote ? "'" : '"') + ((id !== false) ? (' id="' + this.esc_attr(id) + '"') : '') + ((attributes !== false) ? (' ' + attributes) : '') + ' />');
7465 }
7466
7467 // Get part ID
7468 $.WS_Form.prototype.get_part_id = function(field_id, section_repeatable_index, identifier) {
7469
7470 if(typeof section_repeatable_index === 'undefined') { section_repeatable_index = false; }
7471 if(typeof identifier === 'undefined') { identifier = 'field'; }
7472
7473 var repeatable_suffix = ((section_repeatable_index !== false) ? '-repeat-' + section_repeatable_index : '');
7474 return this.form_id_prefix + identifier + '-' + field_id + repeatable_suffix;
7475 }
7476
7477 // Get field name
7478 $.WS_Form.prototype.get_field_name = function(field_id, section_repeatable_index, is_submit) {
7479
7480 if(typeof section_repeatable_index === 'undefined') { section_repeatable_index = false; }
7481 if(typeof is_submit === 'undefined') { is_submit = false; }
7482
7483 var repeatable_suffix = ((section_repeatable_index !== false) ? '-repeat-' + section_repeatable_index : '');
7484 return this.field_name_prefix + field_id + ((section_repeatable_index !== false) ? (is_submit ? ('_' + section_repeatable_index) : ('[' + section_repeatable_index + ']')) : '');
7485 }
7486
7487 // Is iterable?
7488 $.WS_Form.prototype.is_iterable = function(obj) {
7489
7490 if(obj == null) { return false; }
7491
7492 if(typeof obj !== 'object') { return false; }
7493
7494 return typeof obj[Symbol.iterator] === 'function';
7495 }
7496
7497 // Is integer?
7498 $.WS_Form.prototype.is_integer = function(input) {
7499
7500 if(isNaN(input)) { return false; }
7501
7502 return /^\d+\.?\d*$/.test(input);
7503 }
7504
7505 // Is invalid? Fixes compatibility with jQuery mobile which breaks .is(':invalid')
7506 $.WS_Form.prototype.is_invalid = function(obj) {
7507
7508 if(!obj[0] || !obj[0].validity) { return true; }
7509
7510 return !obj[0].validity.valid;
7511 }
7512
7513 // Is valid? Fixes compatibility with jQuery mobile which breaks .is(':valid')
7514 $.WS_Form.prototype.is_valid = function(obj) {
7515
7516 if(!obj[0] || !obj[0].validity) { return false; }
7517
7518 return obj[0].validity.valid;
7519 }
7520
7521 // Is not a number
7522 $.WS_Form.prototype.is_not_number = function(input_number) {
7523
7524 return (
7525
7526 isNaN(input_number) ||
7527 (typeof input_number === 'boolean') ||
7528 (input_number === '')
7529 );
7530 }
7531
7532 // Is true
7533 $.WS_Form.prototype.is_true = function(input) {
7534
7535 // Check for boolean
7536 if(typeof input === 'boolean') {
7537 return input;
7538 }
7539
7540 // Check for object (including arrays)
7541 if(typeof input === 'object' && input !== null) {
7542 // If it's an array, get the first element
7543 if(Array.isArray(input)) {
7544 input = input[0] !== undefined ? input[0] : '';
7545 } else {
7546 // Convert object to array of values and take the first one
7547 input = Object.values(input)[0] || '';
7548 }
7549 }
7550
7551 // If numeric, convert to string
7552 if(typeof input === 'number') {
7553 input = String(input);
7554 }
7555
7556 // If not a string at this point, return false
7557 if(typeof input !== 'string') {
7558 return false;
7559 }
7560
7561 // Trim and lowercase
7562 input = input.trim().toLowerCase();
7563
7564 // Check against true values
7565 var true_array = ['1', 'on', 'yes', 'true'];
7566 return true_array.includes(input);
7567 }
7568
7569 // Replace all
7570 $.WS_Form.prototype.replace_all = function(input, search, replace) {
7571
7572 if(replace === undefined) { return input.toString(); }
7573
7574 return input.split(search).join(replace);
7575 }
7576
7577 // Remove empty and null elements from an array
7578 $.WS_Form.prototype.array_remove_empty = function(arr) {
7579
7580 if(typeof arr !== 'object') { return arr; }
7581
7582 var arr_out = [];
7583
7584 for(var key in arr) {
7585
7586 // Check array has property key
7587 if(!arr.hasOwnProperty(key)) { continue; }
7588
7589 // Strip null values
7590 if(arr[key] === null) { continue; }
7591
7592 arr_out.push(arr[key]);
7593 }
7594
7595 return arr_out;
7596 }
7597
7598 // Attribute add item
7599 $.WS_Form.prototype.attribute_add_item = function(obj, attribute_name, item) {
7600
7601 // Get existing attribute value
7602 var attribute_value = obj.attr(attribute_name);
7603
7604 // If attribute exists
7605 if(typeof attribute_value !== 'undefined') {
7606
7607 // If existing aria-describedby exists and invalid feedback ID is not in the string
7608 if(attribute_value.indexOf(item) === -1) {
7609
7610 obj.attr(attribute_name, this.esc_attr(item) + (attribute_value ? ' ' : '') + attribute_value);
7611 }
7612
7613 } else {
7614
7615 // Attribute doesn't exist, so create it
7616 obj.attr(attribute_name, this.esc_attr(item));
7617 }
7618
7619 }
7620
7621 // Attribute remove item
7622 $.WS_Form.prototype.attribute_remove_item = function(obj, attribute_name, item) {
7623
7624 // Get existing attribute value
7625 var attribute_value = obj.attr(attribute_name);
7626
7627 // If attribute exists and it contains item
7628 if(
7629 (typeof attribute_value !== 'undefined') &&
7630 (attribute_value.indexOf(item) !== -1)
7631 ) {
7632 // Split attribute value into array
7633 var attribute_value_array = attribute_value.split(' ');
7634
7635 // Remove item element
7636 var ws_this = this;
7637 attribute_value_array = attribute_value_array.filter(function(e) { return e !== ws_this.esc_attr(item); });
7638
7639 // Rebuild attribute value
7640 attribute_value = attribute_value_array.join(' ');
7641
7642 // If attribute value is not blank, set it, otherwise remove it
7643 if(attribute_value) {
7644
7645 obj.attr(attribute_name, attribute_value);
7646
7647 } else {
7648
7649 obj.removeAttr(attribute_name);
7650 }
7651 }
7652 }
7653
7654 // Convert date in string format to JS Date
7655 $.WS_Form.prototype.get_date = function(input_datetime, input_type_datetime, format_date, format_time) {
7656
7657 if(!input_type_datetime) { input_type_datetime = 'date'; }
7658 if(!format_date) { format_date = ws_form_settings.date_format; }
7659 if(!format_time) { format_time = ws_form_settings.time_format; }
7660
7661 // Process time
7662 switch(input_type_datetime) {
7663
7664 case 'time' :
7665
7666 return new Date('01/01/1970 ' + input_datetime);
7667
7668 case 'date' :
7669
7670 input_datetime = input_datetime + ' 00:00:00';
7671 break;
7672
7673 case 'week' :
7674 case 'month' :
7675
7676 return false;
7677 }
7678
7679 // Convert d/m/Y formats to m/d/Y for JavaScript compatibility
7680 var dm_to_md_date_separator = false;
7681 switch(format_date) {
7682
7683 case 'd/m/Y' :
7684
7685 dm_to_md_date_separator = '/';
7686 break;
7687
7688 case 'd.m.Y' :
7689 case 'j.n.Y' :
7690
7691 dm_to_md_date_separator = '.';
7692 break;
7693
7694 case 'd-m-Y' :
7695
7696 dm_to_md_date_separator = '-';
7697 break;
7698 }
7699
7700 if(dm_to_md_date_separator !== false) {
7701
7702 // Split out date and time
7703 switch(input_type_datetime) {
7704
7705 case 'datetime-local' :
7706
7707 var date_time_delimiter_index = input_datetime.indexOf(' ');
7708 var input_date = input_datetime.substring(0, date_time_delimiter_index);
7709 var input_time = input_datetime.substring(date_time_delimiter_index);
7710 break;
7711
7712 default :
7713
7714 var input_date = input_datetime;
7715 var input_time = '';
7716 break;
7717 }
7718
7719 // Convert d/m/Y to m/d/Y
7720 var date_array = input_date.split(dm_to_md_date_separator);
7721 if(date_array.length === 3) {
7722
7723 var d = parseInt(date_array[0], 10),
7724 m = parseInt(date_array[1], 10),
7725 y = parseInt(date_array[2], 10);
7726 var input_date = m + '/' + d + '/' + y; // Force slashes so Date class works
7727 }
7728
7729 // Put it back together again
7730 input_datetime = input_date + input_time;
7731 }
7732
7733 // Strip ordinal indicators
7734 input_datetime = input_datetime.replace(/(\d+)(st|nd|rd|th)/g, '$1');
7735
7736 // Change period characters to characters
7737 input_datetime = this.replace_all(input_datetime, '.', ' ');
7738
7739 // Convert alternative time formats
7740 if(format_time.indexOf(' \\h ') !== -1) {
7741
7742 input_datetime = input_datetime.replace(/\b(\d{1,2})\s*h\s*(\d{1,2})\b/gi, "$1:$2");
7743 }
7744
7745 // Remove double spacing
7746 input_datetime = this.replace_all(input_datetime, ' ', ' ');
7747
7748 // Convert string to date
7749 return this.get_new_date(input_datetime);
7750 }
7751
7752 // Get new date
7753 $.WS_Form.prototype.get_new_date = function(input_date) {
7754
7755 // Modify date for browsers that don't support Y-m-d 00:00:00 formats. Convert - to /.
7756 if(
7757 (input_date.charAt(4) == '-') &&
7758 (input_date.charAt(7) == '-') &&
7759 (input_date.charAt(10) != 'T') &&
7760 (input_date.charAt(13) == ':') &&
7761 !this.date_valid(input_date)
7762 ) {
7763
7764 // Convert dashes to slashes (Gives same time output but compatible with older browsers)
7765 input_date = input_date.replace(/-/g, '/');
7766 }
7767
7768 // Modify date for browsers that don't support Y-m-d 00:00 am/pm formats. Convert - to /.
7769 if(
7770 (input_date.charAt(4) == '-') &&
7771 !this.date_valid(input_date) &&
7772 (
7773 (input_date.toLowerCase().indexOf('am') !== -1) ||
7774 (input_date.toLowerCase().indexOf('pm') !== -1)
7775 )
7776 ) {
7777
7778 // Convert dashes to slashes (Gives same time output but compatible with older browsers)
7779 input_date = input_date.replace(/-/g, '/');
7780 }
7781
7782 return new Date(input_date);
7783 }
7784
7785 // Check date is valid
7786 $.WS_Form.prototype.date_valid = function(input_date) {
7787
7788 return input_date instanceof Date && !isNaN(input_date.valueOf())
7789 }
7790
7791 // Get object_row_id as an array of integers
7792 $.WS_Form.prototype.get_object_row_id = function(condition) {
7793
7794 var object_row_id = condition.object_row_id;
7795
7796 if((typeof object_row_id === 'undefined') || (object_row_id == '')) { return false; }
7797
7798 // Process object_id so values are integers
7799 if(typeof object_row_id === 'object') {
7800
7801 object_row_id = object_row_id.map(function(id) { return parseInt(id, 10); });
7802 if(!object_row_id.length) { object_row_id = false; }
7803
7804 } else {
7805
7806 object_row_id = (object_row_id !== '') ? [parseInt(object_row_id, 10)] : false;
7807 }
7808
7809 return object_row_id;
7810 }
7811
7812 // Get nice date by type
7813 $.WS_Form.prototype.get_date_by_type = function(date, field) {
7814
7815 if(!date) { return ''; }
7816
7817 var format_date = this.get_object_meta_value(field, 'format_date', ws_form_settings.date_format);
7818 if(!format_date) { format_date = ws_form_settings.date_format; }
7819 var format_time = this.get_object_meta_value(field, 'format_time', ws_form_settings.time_format);
7820 if(!format_time) { format_time = ws_form_settings.time_format; }
7821
7822 var input_type_datetime = this.get_object_meta_value(field, 'input_type_datetime', 'date');
7823
7824 switch(input_type_datetime) {
7825
7826 case 'datetime-local' :
7827 case 'date' :
7828
7829 // Convert to m/d/Y format for JavaScript Date class
7830 switch(format_date) {
7831
7832 case 'd/m/Y' :
7833
7834 var date_time_array = (date.indexOf(' ') !== -1) ? [date.substring(0, date.indexOf(' ')), date.substring(date.indexOf(' ') + 1)] : [date];
7835 var date_array = date_time_array[0].split('/');
7836 date = date_array[1] + '/' + date_array[0] + '/' + date_array[2] + ((typeof date_time_array[1] !== 'undefined') ? ' ' + date_time_array[1] : '');
7837 break;
7838
7839 case 'd-m-Y' :
7840
7841 var date_time_array = (date.indexOf(' ') !== -1) ? [date.substring(0, date.indexOf(' ')), date.substring(date.indexOf(' ') + 1)] : [date];
7842 var date_array = date_time_array.split('-');
7843 date = date_array[1] + '/' + date_array[0] + '/' + date_array[2] + ((typeof date_time_array[1] !== 'undefined') ? ' ' + date_time_array[1] : '');
7844 break;
7845
7846 case 'd.m.Y' :
7847 case 'j.n.Y' :
7848
7849 var date_time_array = (date.indexOf(' ') !== -1) ? [date.substring(0, date.indexOf(' ')), date.substring(date.indexOf(' ') + 1)] : [date];
7850 var date_array = date_time_array[0].split('.');
7851 date = date_array[1] + '/' + date_array[0] + '/' + date_array[2] + ((typeof date_time_array[1] !== 'undefined') ? ' ' + date_time_array[1] : '');
7852 break;
7853 }
7854 }
7855
7856 if(input_type_datetime !== 'time') {
7857
7858 // Check if date is in format of yyyy-mm-dd and does not have a time, if so add time so it is not localized to UTC
7859 if(
7860 (date.indexOf('-') === 4) &&
7861 (date.indexOf(':') === -1)
7862
7863 ) { date += ' 00:00:00'; }
7864 }
7865
7866 switch(input_type_datetime) {
7867
7868 case 'date' :
7869
7870 date = this.get_new_date(date);
7871 return this.date_format(date, format_date);
7872
7873 case 'month' :
7874
7875 date = this.get_new_date(date);
7876 return this.date_format(date, 'F Y');
7877
7878 case 'time' :
7879
7880 date = this.get_new_date('1970-01-01 ' + date);
7881 return this.date_format(date, format_time);
7882
7883 case 'week' :
7884
7885 date = this.get_new_date(date);
7886 return this.language('week') + ' ' + this.date_format(date, 'W, Y');
7887
7888 default :
7889
7890 date = this.get_new_date(date);
7891 return this.date_format(date, format_date + ' ' + format_time);
7892 }
7893 }
7894
7895 // Equivalent to PHP's number_format function
7896 $.WS_Form.prototype.number_format = function(num, decimals, decimal_separator, thousands_separator) {
7897
7898 // Check num
7899 if((typeof num === 'undefined') || num == null || !isFinite(num)) { return 0; }
7900
7901 // Check decimals
7902 if(!decimals) {
7903
7904 var len = num.toString().split('.').length;
7905 decimals = len > 1 ? len : 0;
7906 }
7907
7908 // Check decimal points
7909 if(typeof decimal_separator === 'undefined') {
7910
7911 decimal_separator = '.';
7912 }
7913
7914 // Check thousands point
7915 if(typeof thousands_separator === 'undefined') {
7916
7917 thousands_separator = ',';
7918 }
7919
7920 // Check decimals
7921 if((decimals < 0) || (decimals > 100)) { decimals = 0; }
7922
7923 // Convert num to floating point and fix it to decimal places (Returns a string)
7924 num = parseFloat(num).toFixed(decimals);
7925
7926 // Replace decimal point
7927 num = num.replace('.', decimal_separator);
7928
7929 // Split num into array by decimal point
7930 var num_array = num.split(decimal_separator);
7931
7932 // Add thousand separators
7933 num_array[0] = num_array[0].replace(/\B(?=(?:\d{3})+(?!\d))/g, thousands_separator);
7934
7935 // Rejoin string
7936 num = num_array.join(decimal_separator);
7937
7938 // Return num as string
7939 return num;
7940 }
7941
7942 $.WS_Form.prototype.get_full_name_components = function(full_name) {
7943
7944 if(typeof full_name !== 'string') {
7945
7946 return {
7947
7948 name_prefix: '',
7949 first_name: '',
7950 name_middle: '',
7951 name_last: '',
7952 suffix: ''
7953 };
7954 }
7955
7956 var parts = full_name.trim().split(/\s+/);
7957
7958 var name_prefix = '';
7959 var name_suffix = '';
7960 var name_first = '';
7961 var name_middle = '';
7962 var name_last = '';
7963
7964 // Remove and save prefix
7965 var prefixes = $.WS_Form.settings_form.name.prefixes;
7966 if(typeof prefixes !== 'object') { prefixes = []; }
7967 if(parts.length && prefixes.includes(parts[0].toLowerCase().replace(/\./g, ''))) {
7968
7969 name_prefix = parts.shift();
7970 }
7971
7972 // Remove and save suffix
7973 var suffixes = $.WS_Form.settings_form.name.suffixes;
7974 if(typeof suffixes !== 'object') { prefixes = []; }
7975 if(parts.length > 1 && suffixes.includes(parts[parts.length - 1].toLowerCase().replace(/\./g, ''))) {
7976
7977 name_suffix = parts.pop();
7978 }
7979
7980 if(parts.length === 1) {
7981
7982 name_first = parts[0];
7983
7984 } else if (parts.length === 2) {
7985
7986 name_first = parts[0];
7987 name_last = parts[1];
7988
7989 } else if (parts.length > 2) {
7990
7991 name_first = parts[0];
7992 name_last = parts[parts.length - 1];
7993 name_middle = parts.slice(1, -1).join(' ');
7994 }
7995
7996 return {
7997
7998 name_prefix: name_prefix,
7999 name_first: name_first,
8000 name_middle: name_middle,
8001 name_last: name_last,
8002 name_suffix: name_suffix
8003 };
8004 }
8005
8006 // Equivalent to PHP's date function
8007 $.WS_Form.prototype.date_format = function (date, format) {
8008
8009 var ws_this = this;
8010
8011 // Defining locale
8012 var short_months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
8013 var long_months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']
8014 var short_days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
8015 var long_days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']
8016
8017 // Defining patterns
8018 var replace_chars = {
8019
8020 // Day
8021 d: function () { var d = this.getDate(); return (d < 10 ? '0' : '') + d },
8022 D: function () { return short_days[this.getDay()] },
8023 j: function () { return this.getDate() },
8024 l: function () { return long_days[this.getDay()] },
8025 N: function () { var N = this.getDay(); return (N === 0 ? 7 : N) },
8026 S: function () { var S = this.getDate(); return (S % 10 === 1 && S !== 11 ? 'st' : (S % 10 === 2 && S !== 12 ? 'nd' : (S % 10 === 3 && S !== 13 ? 'rd' : 'th'))) },
8027 w: function () { return this.getDay() },
8028 z: function () { var d = new Date(this.getFullYear(), 0, 1); return Math.ceil((this - d) / 86400000) },
8029
8030 // Week
8031 W: function () {
8032 var target = new Date(this.valueOf())
8033 var day_number = (this.getDay() + 6) % 7
8034 target.setDate(target.getDate() - day_number + 3)
8035 var first_thursday = target.valueOf()
8036 target.setMonth(0, 1)
8037 if (target.getDay() !== 4) {
8038 target.setMonth(0, 1 + ((4 - target.getDay()) + 7) % 7)
8039 }
8040 var return_Value = 1 + Math.ceil((first_thursday - target) / 604800000)
8041
8042 return (return_Value < 10 ? '0' + return_Value : return_Value)
8043 },
8044
8045 // Month
8046 F: function () { return long_months[this.getMonth()] },
8047 m: function () { var m = this.getMonth(); return (m < 9 ? '0' : '') + (m + 1) },
8048 M: function () { return short_months[this.getMonth()] },
8049 n: function () { return this.getMonth() + 1 },
8050 t: function () {
8051 var year = this.getFullYear()
8052 var next_month = this.getMonth() + 1
8053 if (next_month === 12) {
8054 year = year++
8055 next_month = 0
8056 }
8057 return new Date(year, next_month, 0).getDate()
8058 },
8059
8060 // Year
8061 L: function () { var L = this.getFullYear(); return (L % 400 === 0 || (L % 100 !== 0 && L % 4 === 0)) },
8062 o: function () { var d = new Date(this.valueOf()); d.setDate(d.getDate() - ((this.getDay() + 6) % 7) + 3); return d.getFullYear() },
8063 Y: function () { return this.getFullYear() },
8064 y: function () { return ('' + this.getFullYear()).slice(2) },
8065
8066 // Time
8067 a: function () { return this.getHours() < 12 ? 'am' : 'pm' },
8068 A: function () { return this.getHours() < 12 ? 'AM' : 'PM' },
8069 B: function () { return Math.floor((((this.getUTCHours() + 1) % 24) + this.getUTCMinutes() / 60 + this.getUTCSeconds() / 3600) * 1000 / 24) },
8070 g: function () { return this.getHours() % 12 || 12 },
8071 G: function () { return this.getHours() },
8072 h: function () { var h = this.getHours(); return ((h % 12 || 12) < 10 ? '0' : '') + (h % 12 || 12) },
8073 H: function () { var H = this.getHours(); return (H < 10 ? '0' : '') + H },
8074 i: function () { var i = this.getMinutes(); return (i < 10 ? '0' : '') + i },
8075 s: function () { var s = this.getSeconds(); return (s < 10 ? '0' : '') + s },
8076 v: function () { var v = this.getMilliseconds(); return (v < 10 ? '00' : (v < 100 ? '0' : '')) + v },
8077
8078 // Timezone
8079 e: function () { return Intl.DateTimeFormat().resolvedOptions().timeZone },
8080 I: function () {
8081 var DST = null
8082 for (var i = 0; i < 12; ++i) {
8083 var d = new Date(this.getFullYear(), i, 1)
8084 var offset = d.getTimezoneOffset()
8085
8086 if (DST === null) DST = offset
8087 else if (offset < DST) { DST = offset; break } else if (offset > DST) break
8088 }
8089 return (this.getTimezoneOffset() === DST) | 0
8090 },
8091 O: function () { var O = this.getTimezoneOffset(); return (-O < 0 ? '-' : '+') + (Math.abs(O / 60) < 10 ? '0' : '') + Math.floor(Math.abs(O / 60)) + (Math.abs(O % 60) === 0 ? '00' : ((Math.abs(O % 60) < 10 ? '0' : '')) + (Math.abs(O % 60))) },
8092 P: function () { var P = this.getTimezoneOffset(); return (-P < 0 ? '-' : '+') + (Math.abs(P / 60) < 10 ? '0' : '') + Math.floor(Math.abs(P / 60)) + ':' + (Math.abs(P % 60) === 0 ? '00' : ((Math.abs(P % 60) < 10 ? '0' : '')) + (Math.abs(P % 60))) },
8093 T: function () { var tz = this.toLocaleTimeString(navigator.language, {timeZoneName: 'short'}).split(' '); return tz[tz.length - 1] },
8094 Z: function () { return -this.getTimezoneOffset() * 60 },
8095
8096 // Full Date/Time
8097 c: function () { return ws_this.date_format(date, 'Y-m-d\\TH:i:sP') },
8098 r: function () { return this.toString() },
8099 U: function () { return Math.floor(this.getTime() / 1000) }
8100 }
8101
8102 return format.replace(/(\\?)(.)/g, function (_, esc, chr) {
8103
8104 return (esc === '' && replace_chars[chr]) ? replace_chars[chr].call(date) : chr;
8105 });
8106 }
8107
8108 })(jQuery);
8109