PageSourceSearch

https://www.zaanschemolen.nl/wp-content/plugins/paytium/public/js/public.js?ver=5.0.3

js zaanschemolen.nl collected 2026-09-24 23:49:40 UTC 90,660 bytes, 2,098 lines download raw bytes

1/**
2 * Paytium Public JS
3 *
4 * @package PT
5 * @author  David de Boer <[email protected]>
6 */
7
8/* global jQuery, pt_script */
9
10(function ($) {
11    'use strict';
12
13    var currencySymbol = pt.currency_symbol;
14
15    function debug_log( message ) {
16        if ( pt.debug == true ) {
17            console.log( message );
18        }
19    }
20
21    function initializePaytiumForm(ptForm) {
22        // Check if already initialized using data (which is not copied on simple clone)
23        // We do not rely on the class 'pt-form-initialized' because Elementor might clone the HTML including the class,
24        // but without the events/data.
25        if (ptForm.data('pt-initialized')) {
26            return;
27        }
28        ptForm.data('pt-initialized', true);
29        ptForm.addClass('pt-form-initialized');
30
31        var ptFormCurrencySymbol = ptForm.data('currency') && getCurrencySymbol(ptForm.data('currency')) !== undefined ?
32                getCurrencySymbol(ptForm.data('currency')) : currencySymbol,
33            ptFormQtyDiscount = ptForm.find('input.pt-quantity-discount'),
34            ptFormQtyDiscountData = ptFormQtyDiscount.data('quantity-discount') ? ptFormQtyDiscount.data('quantity-discount') : false,
35            ptFormCurrentQtyDiscount = 0,
36            ptFormCurrentQty = 0,
37            ptFormAmountDiscount = ptForm.find('input.pt-amount-discount'),
38            ptFormAmountDiscountData = ptFormAmountDiscount.data('amount-discount') ? ptFormAmountDiscount.data('amount-discount') : false,
39            ptFormCurrentAmountDiscount = 0,
40            ptFormCurrentAmount = 0,
41            ptFormGeneralLimit = ptForm.find('.pt-general-items-left').data('general-items-left'),
42            ptFormAmounts = [],
43            ptFormFb = ptForm.hasClass('pt-fb-form');
44
45        ptForm.find(".pt-cf-amount").each(function () {
46            if ($(this).is('select')) {
47                $($(this).children('option')).each(function () {
48                    ptFormAmounts.push($(this).attr('data-pt-price'));
49                })
50            }
51            else {
52                ptFormAmounts.push($(this).attr('data-pt-price'));
53            }
54        });
55
56        // Add field that allows Paytium processing to know that JS was enabled on form
57        if (ptForm.find('input[name="pt-paytium-js-enabled"]').length === 0) {
58            $("<input>", {type: "hidden", name: 'pt-paytium-js-enabled', value: 1}).appendTo(ptForm);
59        }
60
61        // Enable form button with javascript, so it doesn't show to users that don't have JS enabled
62        ptForm.find('.pt-payment-btn')
63            .show();
64
65        //
66        // START - Paytium No Payment
67        //
68
69        function isPaytiumNoPayment() {
70
71            var noPaymentFound = false;
72
73            ptForm.find("[id^=pt-paytium-no-payment]").each(function () {
74                noPaymentFound = true;
75            });
76
77            return noPaymentFound;
78        }
79
80        //
81        // END - Paytium No Payment
82        //
83
84        //
85        // START - Show a warning about prefilled fields
86        //
87
88        ptForm.find(".pt-field-prefill-warning-hint").click(function () {
89            var ptPrefillWarningCounter = $(this).attr('data-pt-prefill-warning-counter');
90            ptForm.find("#pt-prefill-warning-counter-" + ptPrefillWarningCounter).toggle("slow");
91        });
92
93        //
94        // END - Show a warning about prefilled fields
95        //
96
97        //
98        // START - Enable/disable discount apply button depending on input
99        //
100
101        ptForm.find('.pt-discount').on('keyup', function (event) {
102            var nameInput = ptForm.find('.pt-discount').val();
103            if (nameInput.length > 0) {
104                ptForm.find('.pt-discount-button').prop('disabled', false);
105            } else {
106                ptForm.find('.pt-discount-button').prop('disabled', true);
107            }
108        });
109
110        ptForm.find('.pt-discount-button').on('click.ptDiscountFieldButton', function (event) {
111
112            event.preventDefault();
113
114            var formLoadID = ptForm.find('[name="pt-form-load"]').val(),
115                discountCode = ptForm.find('.pt-discount').val(),
116                associateWithEmail = false,
117                email = '';
118
119            if ($(this).siblings('#pt-discount-associate-with-email').length) {
120                associateWithEmail = true;
121                email = $(this).parents('.pt-form-group-field-discount').siblings('.pt-form-group-field-email').children('input[type="email"]').val();
122            }
123
124            var noEmailError = associateWithEmail && !email;
125
126            if (discountCode && formLoadID) {
127
128                $.ajax({
129                    url: paytium_localize_script_vars.admin_ajax_url,
130                    type: 'POST',
131                    dataType: 'json',
132                    data: {
133                        'action': "pt_ajax_check_discount_codes",
134                        'data': {'formLoadID': formLoadID, 'discountCode': discountCode, 'email': email}
135                    },
136                    success: function (response) {
137
138                        if (response.status && !noEmailError) {
139
140                            // Show success message after user applies valid discount code
141                            ptForm.find('.pt-discount-true').show();
142                            ptForm.find('.pt-discount-false').hide();
143                            ptForm.find('.pt-discount-used').hide();
144
145                            // Remove discount fields if they are already added
146                            ptForm.find(".pt-discount-type").remove();
147                            ptForm.find(".pt-discount-value").remove();
148                            ptForm.find(".pt-discount-times").remove();
149
150                            // If discount is valid, add discount fields to the form
151                            $("<input>", {
152                                type: "hidden",
153                                name: 'pt-discount[type]',
154                                class: 'pt-discount-type',
155                                value: response.type
156                            }).appendTo(ptForm);
157                            $("<input>", {
158                                type: "hidden",
159                                name: 'pt-discount[value]',
160                                class: 'pt-discount-value',
161                                value: response.value
162                            }).appendTo(ptForm);
163                            $("<input>", {
164                                type: "hidden",
165                                name: 'pt-discount[times]',
166                                class: 'pt-discount-times',
167                                value: response.times
168                            }).appendTo(ptForm);
169
170
171                        } else {
172
173                            // Show error message after user applies incorrect discount code
174                            ptForm.find('.pt-discount-true').hide();
175                            if (response.used) {
176                                ptForm.find('.pt-discount-email-error').hide();
177                                ptForm.find('.pt-discount-false').hide();
178                                ptForm.find('.pt-discount-used').show(); // discount feature
179                            }
180                            else if (noEmailError) {
181                                ptForm.find('.pt-discount-used').hide();
182                                ptForm.find('.pt-discount-false').hide();
183                                ptForm.find('.pt-discount-email-error').show();
184                            }
185                            else {
186                                ptForm.find('.pt-discount-email-error').hide();
187                                ptForm.find('.pt-discount-used').hide();
188                                ptForm.find('.pt-discount-false').show();
189                            }
190
191                            // Empty discount field and disable apply button, so user can try again
192                            ptForm.find('.pt-discount').val('');
193                            ptForm.find('.pt-discount-button').prop('disabled', true);
194
195                            // Remove discount fields if they are already added
196                            ptForm.find(".pt-discount-type").remove(); // Don't use parseAmount for this one!
197                            ptForm.find(".pt-discount-value").remove(); // Don't use parseAmount for this one!
198                            ptForm.find(".pt-discount-times").remove(); // Don't use parseAmount for this one!
199
200                        }
201
202                        update_totals();
203
204                    },
205                });
206            }
207        });
208
209        //
210        // END - Enable/disable discount apply button depending on input
211        //
212
213        //
214        // START - Add subscription first payment to [paytium_total /] shortcode
215        //
216
217        // If there is a subscription first payment, adjust form to allow that
218        if (ptForm.find("[id^=pt-subscription-first-payment]").length > 0) {
219            // Add "First payment" text and amount after the [paytium_total /] Shortcode
220            var ptFirstPayment = ptForm.find("[id^=pt-subscription-first-payment]").val();
221            var ptFirstPaymentLabel = ptForm.find("[id^=pt-subscription-first-payment-label]").val();
222
223            // If user set a First Payment Label, override the default translation with that
224            if (!ptFirstPaymentLabel) {
225                ptFirstPaymentLabel = paytium_localize_script_vars.subscription_first_payment;
226            }
227
228            if (ptForm.find('.pt-first-payment-amount').length === 0) {
229                ptForm.find('.pt-total-amount').after('<div class="pt-first-payment-amount">' + ptFirstPaymentLabel + ': ' + currencyFormattedAmount(ptFirstPayment, ptFormCurrencySymbol) + '</div>');
230            }
231        }
232
233        //
234        // END - Add subscription first payment to [paytium_total /] shortcode
235        //
236
237        //
238        // START - Subscription interval options and optional on page load
239        //
240
241        // Uncheck subscription interval options on page load, especially for back button
242        ptForm.find('input[name="pt-subscription-interval-options"]:checked').each(function () {
243            $('input[name="pt-subscription-interval-options"]').prop('checked', false);
244        });
245
246        // Make sure Yes is checked for subscription optional on page load, especially for back button
247        ptForm.find('input[name="pt-subscription-optional"]:checked').each(function () {
248            $('input[name="pt-subscription-optional"]:first').attr('checked', true);
249        });
250
251        //
252        // END - Subscription interval options and optional on page load
253        //
254
255        //
256        // START - Add option amount labels to field label
257        // Add option amount labels for dropdown/radio to field label, so users can see what customers selected
258        //
259
260        // Checkbox with amounts, get all already checked checkboxes and process amounts and labels
261        ptForm.find('input[type=checkbox]:checked').filter('.pt-cf-amount').each(function () {
262
263            //
264            // Update amount
265            //
266
267            // Get id/name for checkbox form group amount
268            var ptCheckboxFormGroupAmountId = $(this).attr('name').replace('[amount]', '[amount][total]');
269
270            // Get selected value
271            var ptCheckboxFieldValue = $(this).val();
272
273            // Get total amount for this checkbox group
274            var ptCheckboxTotalValue = ptForm.find("[id='" + ptCheckboxFormGroupAmountId + "']").val();
275
276            // Add selected value to total amount (don't remove those +'s)
277            var ptCheckboxNewTotalValue = +ptCheckboxTotalValue + +ptCheckboxFieldValue;
278
279            // Update the total value in value attribute
280            ptForm.find("[id='" + ptCheckboxFormGroupAmountId + "']").attr('value', parseAmount(ptCheckboxNewTotalValue));
281
282            // Update total value in pt-price data attribute
283            ptForm.find("[id='" + ptCheckboxFormGroupAmountId + "']").attr('data-pt-price', parseAmount(ptCheckboxNewTotalValue));
284
285            //
286            // Update label
287            //
288
289            // Get/convert checkbox group label ID
290            var ptCheckboxFormGroupLabelId = $(this).attr('name').replace('[amount]', '[label]');
291
292            var ptCheckboxFormValue = $(this).attr('name').replace('[amount]', '[value]');
293
294            // Get previously checked options
295            var ptCheckboxCurrentOptions = ptForm.find("[name='" + ptCheckboxFormGroupLabelId + "']").attr('data-pt-checked-options');
296
297            ptCheckboxCurrentOptions = JSON.parse(ptCheckboxCurrentOptions);
298
299            // If previously checked options are not an array, this means it's empty
300            if (Object.keys(ptCheckboxCurrentOptions).length === 0) {
301                ptCheckboxCurrentOptions = {};
302            }
303
304            ptCheckboxCurrentOptions[$(this).attr('data-pt-checkbox-id')] = $(this).parent().text();
305
306            // Start string with current group label
307            var ptCheckboxCurrentOptionsString = ptForm.find("[name='" + ptCheckboxFormGroupLabelId + "']").attr('data-pt-original-label');
308
309            // Convert current options to a string for HTML hidden field
310
311            var ptCheckboxFormValueString = '';
312            for (var key in ptCheckboxCurrentOptions) {
313                ptCheckboxFormValueString +=  ptCheckboxFormValueString == '' ? ptCheckboxCurrentOptions[key] : ', ' + ptCheckboxCurrentOptions[key];
314            }
315
316            // Convert to a format that is save for HTML fields
317            ptCheckboxCurrentOptions = JSON.stringify(ptCheckboxCurrentOptions);
318
319            // Add updated options to data-pt-checked-options attribute
320            ptForm.find("[name='" + ptCheckboxFormGroupLabelId + "']").attr('data-pt-checked-options', ptCheckboxCurrentOptions);
321
322            // Add current options string to hidden HTML field
323            ptForm.find("[name='" + ptCheckboxFormGroupLabelId + "']").attr('value', ptCheckboxCurrentOptionsString);
324            ptForm.find("[name='" + ptCheckboxFormValue + "']").attr('value', ptCheckboxFormValueString);
325
326        });
327
328        // Radio amounts, try to get first option in radio buttons to use as default if an option is not selected yet
329        ptForm.find('input[type=radio]:checked').filter('.pt-cf-amount').each(function () {
330
331            var ptRadioCustomOption = $(this).parent().text();
332
333            var ptRadioFormGroupId = $(this).attr('name').replace('[amount]', '[label]');
334
335            var ptRadioFormValue = $(this).attr('name').replace('[amount]', '[value]');
336
337            var ptRadioFormOptionId = $(this).attr('name').replace('[amount]', '[item_id]');
338
339            var ptRadioFormOptionLimit = $(this).attr('name').replace('[amount]', '[limit]');
340
341            var ptRadioOptionData = $(this).data();
342
343            var ptRadioFormGroupLabel = ptForm.find("[name='" + ptRadioFormGroupId + "']").attr('data-pt-original-label');
344
345            ptForm.find("[name='" + ptRadioFormValue + "']").val(function () {
346                return ptRadioCustomOption;
347            });
348
349            ptForm.find("[name='" + ptRadioFormOptionId + "']").val(ptRadioOptionData.item_id);
350            ptForm.find("[name='" + ptRadioFormOptionLimit + "']").val(ptRadioOptionData.limit);
351
352            if ($(this).data('general_item_id')) {
353
354                var ptRadioFormOptionGeneralId = $(this).attr('name').replace('[amount]', '[general_item_id]'),
355                    ptRadioFormOptionGeneralLimit = $(this).attr('name').replace('[amount]', '[general_limit]');
356
357                ptForm.find("[name='" + ptRadioFormOptionGeneralId + "']").val(ptRadioOptionData.general_item_id);
358                ptForm.find("[name='" + ptRadioFormOptionGeneralLimit + "']").val(ptRadioOptionData.general_limit);
359            }
360        });
361
362        // Dropdown amounts, try to get first option in Dropdown to use as default if an option is not selected yet
363        ptForm.find('select.pt-cf-amount').each(function () {
364
365            var ptDropdownCustomOption = $(this).find(':selected').text();
366
367            var ptDropdownFormGroupId = $(this).attr('name').replace('[amount]', '[label]');
368
369            var ptDropdownFormValue = $(this).attr('name').replace('[amount]', '[value]');
370
371            ptForm.find("[name='" + ptDropdownFormValue + "']").val(function () {
372                return ptDropdownCustomOption;
373            });
374        });
375
376        // Get selected option for radio and dropdown amounts, when user selects them
377        ptForm.find('.pt-cf-amount').on('change', function () {
378
379            // Process radio buttons amount labels
380            if ($(this).is('input[type="radio"]')) {
381
382                var ptRadioCustomOption = $(this).parent().text();
383
384                var ptRadioFormGroupId = $(this).attr('name').replace('[amount]', '[label]');
385
386                var ptRadioFormValue = $(this).attr('name').replace('[amount]', '[value]');
387
388                var ptRadioFormOptionId = $(this).attr('name').replace('[amount]', '[item_id]');
389
390                var ptRadioFormOptionLimit = $(this).attr('name').replace('[amount]', '[limit]');
391
392                var ptRadioOptionData = $(this).data();
393
394                ptForm.find("[name='" + ptRadioFormValue + "']").val(function () {
395                    return ptRadioCustomOption;
396                });
397
398                ptForm.find("[name='" + ptRadioFormOptionId + "']").val(ptRadioOptionData.item_id);
399                ptForm.find("[name='" + ptRadioFormOptionLimit + "']").val(ptRadioOptionData.limit);
400
401                if ($(this).data('general_item_id')) {
402
403                    var ptRadioFormOptionGeneralId = $(this).attr('name').replace('[amount]', '[general_item_id]'),
404                        ptRadioFormOptionGeneralLimit = $(this).attr('name').replace('[amount]', '[general_limit]');
405
406                    ptForm.find("[name='" + ptRadioFormOptionGeneralId + "']").val(ptRadioOptionData.general_item_id);
407                    ptForm.find("[name='" + ptRadioFormOptionGeneralLimit + "']").val(ptRadioOptionData.general_limit);
408                }
409
410            }
411
412            // Process dropdown amount labels
413            if ($(this).is('select')) {
414
415                var ptDropdownCustomOption = $(this).find(':selected').text();
416
417                var ptDropdownFormGroupId = $(this).attr('name').replace('[amount]', '[label]');
418
419                var ptDropdownFormValue = $(this).attr('name').replace('[amount]', '[value]');
420
421                var ptDropdownFormOptionId = $(this).attr('name').replace('[amount]', '[item_id]');
422
423                var ptDropdownFormOptionLimit = $(this).attr('name').replace('[amount]', '[limit]');
424
425                var ptDropdownOptionData = $(this).find(':selected').data();
426
427                ptForm.find("[name='" + ptDropdownFormValue + "']").val(function () {
428                    return ptDropdownCustomOption;
429                });
430                ptForm.find("[name='" + ptDropdownFormOptionId + "']").val(ptDropdownOptionData.item_id);
431                ptForm.find("[name='" + ptDropdownFormOptionLimit + "']").val(ptDropdownOptionData.limit);
432
433                if ($(this).find(':selected').data('general_item_id')) {
434
435                    var ptDropdownFormOptionGeneralId = $(this).attr('name').replace('[amount]', '[general_item_id]'),
436                        ptDropdownFormOptionGeneralLimit = $(this).attr('name').replace('[amount]', '[general_limit]');
437
438                    ptForm.find("[name='" + ptDropdownFormOptionGeneralId + "']").val(ptDropdownOptionData.general_item_id);
439                    ptForm.find("[name='" + ptDropdownFormOptionGeneralLimit + "']").val(ptDropdownOptionData.general_limit);
440                }
441
442            }
443
444        });
445
446        //
447        // END - Option label to field label
448        //
449
450        //
451        // START - Update total when a paid field changes
452        //
453
454        ptForm.find( '.pt-uea-custom-amount' ).on('keyup change', update_totals );
455        ptForm.find( ':checkbox' ).change(update_checkbox_field);
456        ptForm.find( '.pt-cf-amount' ).on( 'change', update_totals );
457
458        ptForm.find( '.pt-quantity-input' ).on( 'keyup mouseup change', function (e) {
459            let general_limit_control_result = general_limit_control($(this));
460            if (general_limit_control_result !== undefined && !general_limit_control_result.result) {
461                $(this).val(general_limit_control_result.new_val);
462                alert(ptForm.find('.pt-general-items-left').text());
463            }
464            update_totals(e);
465        } );
466
467        ptForm.find( '.pt-paid-field' ).on( 'input', update_totals );
468
469        ptForm.find( ':radio[name=pt-subscription-interval-options]' ).change(subscription_interval);
470        ptForm.find( ':radio[name=pt-subscription-optional]' ).change(subscription_optional);
471
472
473        //
474        // END - Update total when user enters amount (custom amount)
475        //
476
477        //
478        // START - UEA, Update individual open amounts (uea)
479        //
480
481        ptForm.find("input[id^=pt_uea_custom_amount_]").on('keyup change', update_open_field);
482
483        function update_open_field(i, element) {
484
485            var ptUEAFieldName = $(this).attr("name");
486            var ptUEAFieldID = $(this).attr("id");
487            var ptUEAFieldValue = $(this).val();
488
489            ptForm.find("[id^=" + ptUEAFieldID + "]").val(parseAmount(ptUEAFieldValue));
490            ptForm.find("[name*='" + ptUEAFieldName + "']").val(ptUEAFieldValue); // Don't use parseAmount for this one!
491            ptForm.find("[name*='" + ptUEAFieldName + "']").attr('data-pt-price', parseAmount(ptUEAFieldValue));
492            $(this).parent().siblings('.pt-uea-custom-amount-formatted').val(parseAmount(ptUEAFieldValue));
493        }
494
495        //
496        // END - UEA, Update individual open amounts (uea) user changes amount
497        //
498
499        function get_form_paid_fields() {
500            var fields = [];
501
502            ptForm.find( '.pt-cf-label-amount' ).each( function( index, element ) { fields.push( element ); }); // Label fields
503            ptForm.find( '.pt-uea-custom-amount' ).each( function( index, element ) { fields.push( element ); }); // Open amount fields
504            ptForm.find( 'select.pt-cf-amount' ).each( function( index, element ) { fields.push( element ); }); // Select fields
505            ptForm.find( 'input[type=radio].pt-cf-amount:checked' ).each( function( index, element ) { fields.push( element ); }); // Radio fields
506            ptForm.find( 'input[type=checkbox].pt-cf-amount' ).each( function( index, element ) { fields.push( element ); }); // Checkbox fields
507            ptForm.find( '.pt-paid-field' ).each( function( index, element ) { fields.push( element ); });
508
509            return fields;
510        }
511
512        function get_form_total(discount = true) {
513
514            // Get all fields/amounts
515            var total = 0;
516            var fields = get_form_paid_fields();
517
518            // Loop through each amount field
519            $( fields ).each( function( index, element ) {
520
521                // If a paid field is disabled (e.g. with interval options and subscriptions) don't process it
522                if ($(element).prop('disabled')) {
523                    return false;
524                }
525
526                var fieldAmount = 0;
527                var $quantity_input = $( element ).siblings('.pt-quantity-input').length ?
528                    $( element ).siblings('.pt-quantity-input') :
529                    $( element ).siblings('.paytium-quantity-input').children('.pt-quantity-input');
530                var quantity = $quantity_input.length ? $quantity_input.val() : 1;
531                if($(element).hasClass('pt-paid-field')) {
532                    fieldAmount = $( element ).val() ? $( element ).attr('data-pt-price') * quantity : 0;
533                    $( element ).siblings('[name$="[amount]"]').val(parseAmount(fieldAmount));
534                }
535                else if($(element).hasClass('pt-uea-custom-amount')) {
536                    fieldAmount = parseAmount($(element).val()) * quantity;
537                    $(element).parent().siblings('.pt-uea-custom-amount-formatted').val(parseAmount(fieldAmount));
538                }
539                else {
540                    switch ($(element).prop('nodeName')) {
541                        case 'SELECT' :
542                            fieldAmount = $(element).find('option:selected').attr('data-pt-price') * quantity;
543                            fieldAmount ? $(element).find('option:selected').val(parseAmount(fieldAmount)) : '';
544                            break;
545                        default:
546                        case 'INPUT' :
547                            var type = $(element).attr('type');
548                            if (type == 'hidden' || type == 'text') {
549                                fieldAmount = $(element).attr('data-pt-price') * quantity;
550                                $(element).val(parseAmount(fieldAmount));
551                            } else if (type == 'radio' ) {
552
553                                $quantity_input = $(element).parents('.pt-radio-group').find('.pt-quantity-input');
554                                quantity = $quantity_input.length ? $quantity_input.val() : 1;
555                                fieldAmount = $(element).attr('data-pt-price') * quantity;
556                                $(element).val(parseAmount(fieldAmount));
557                            }
558                            else if (type == 'checkbox') {
559                                fieldAmount = $(element).attr('data-pt-price') * quantity;
560                                $(element).val(parseAmount(fieldAmount));
561                                if(!$(element).is(':checked')) {
562                                    fieldAmount = 0;
563                                }
564                            }
565
566                            break;
567
568                    }
569
570                }
571                total = parseFloat( total ) + parseAmount( fieldAmount );
572            });
573
574            return discount ? get_form_total_with_discount(total) : total;
575        }
576
577        function get_form_total_with_discount(total) {
578
579            let total_after_discount = total,
580                zero_tax = ptForm.find(".pt-zero-tax").length ? parseFloat(ptForm.find(".pt-zero-tax").val()) : 0,
581                total_without_zero_tax = total - zero_tax;
582
583
584            // If a discount is found, calculate the new total
585            if (ptForm.find(".pt-discount-type").length > 0 && ptForm.find(".pt-discount-value").length > 0) {
586
587                let discountType = ptForm.find(".pt-discount-type").val(); // Don't use parseAmount for this one!
588                let discountValue = ptForm.find(".pt-discount-value").val(); // Don't use parseAmount for this one!
589
590                let discount_first_payment = ptForm.find('#pt-discount-first-payment').length;
591
592                if (discountType.length > 0 && discountValue.length > 0 && !discount_first_payment) {
593
594                    if ((discountType === 'percentage' )) {
595                        total_after_discount = parseAmount(total_without_zero_tax * ((100 - discountValue) / 100));
596                    }
597
598                    if ((discountType === 'amount' )) {
599                        total_after_discount = parseAmount(total_without_zero_tax - discountValue);
600                    }
601
602                }
603            }
604
605            return total_after_discount !== total ? total_after_discount + zero_tax : total;
606        }
607
608        function get_first_payment_amount() {
609
610            var first_payment_amount = ptForm.find(".pt-subscription-first-payment").val(),
611                discount_exclude_first_payment = ptForm.find('#pt-discount-exclude-first-payment').length;
612
613            if (discount_exclude_first_payment) return first_payment_amount;
614
615            if (ptForm.find(".pt-discount-type").length > 0 && ptForm.find(".pt-discount-value").length > 0 && first_payment_amount) {
616
617                var discountType = ptForm.find(".pt-discount-type").val(); // Don't use parseAmount for this one!
618                var discountValue = ptForm.find(".pt-discount-value").val(); // Don't use parseAmount for this one!
619
620                if (discountType.length > 0 && discountValue.length > 0) {
621
622                    if ((discountType === 'percentage' )) {
623                        first_payment_amount = parseAmount(first_payment_amount.replace(',','.') * ((100 - discountValue) / 100));
624                    }
625
626                    if ((discountType === 'amount' )) {
627                        first_payment_amount = parseAmount(first_payment_amount.replace(',','.') - discountValue);
628                    }
629                }
630            }
631            return first_payment_amount;
632        }
633
634        function quantity_input_validation($quantity_input) {
635            // Get values from input box
636
637            update_checkbox_field($quantity_input)
638
639            var new_qty = $quantity_input.val();
640            var step = $quantity_input.attr( 'step' );
641            var max = $quantity_input.attr( 'max' );
642            var min = $quantity_input.attr( 'min' );
643
644            // Adjust default values if values are blank
645            if ( typeof min == 'undefined' )
646                min = 0;
647
648            if ( typeof step == 'undefined')
649                step = 1;
650
651            // Max Value Validation
652            if ( +new_qty > +max && typeof max !== 'undefined' ) {
653                new_qty = max;
654
655                // Min Value Validation
656            } else if ( +new_qty < +min  ) {
657                new_qty = min;
658            }
659
660            // Calculate remainder
661            var rem = ( new_qty - min ) % step;
662
663            // Step Value Value Validation
664            if ( rem != 0 ) {
665                new_qty = +new_qty + (+step - +rem);
666
667                // Max Value Validation
668                if ( +new_qty > +max ) {
669                    new_qty = +new_qty - +step;
670                }
671            }
672
673            if (ptFormQtyDiscountData) {
674
675                let discountQtyCount = ptFormQtyDiscountData.length,
676                    i = 0;
677
678                $(ptFormQtyDiscountData).each(function (k, v) {
679
680                    if ( (ptFormCurrentQtyDiscount !== parseFloat(v.discount) && parseInt(new_qty) >= parseInt(v.quantity)) ||
681                         (ptFormCurrentQtyDiscount === parseFloat(v.discount) && parseInt(new_qty) < parseInt(v.quantity)) ) {
682
683                        if (ptFormCurrentQtyDiscount > parseFloat(v.discount) && parseInt(new_qty) >= ptFormCurrentQty) {
684                            return;
685                        }
686                        let data = v;
687                        if (parseInt(new_qty) < parseInt(v.quantity)) {
688                            if (k < discountQtyCount-1) {
689                                data = ptFormQtyDiscountData[k+1];
690                            }
691                            else {
692                                i++;
693                                return;
694                            }
695                        }
696
697                        ptFormCurrentQtyDiscount = parseFloat(data.discount);
698                        ptFormCurrentQty = parseInt(data.quantity);
699
700                        let loaderContainer = $( '<div>', {
701                            'class': 'pt-checkout-form-loading'
702                        }).appendTo( ptForm );
703
704                        let loader = $( '<div>', {
705                            'class': 'pt-loader'
706                        }).appendTo( loaderContainer );
707
708                        $.ajax({
709                            url: paytium_localize_script_vars.admin_ajax_url,
710                            type: 'POST',
711                            dataType: 'json',
712                            data: {
713                                'action': "pt_ajax_quantity_discount",
714                                'id': ptFormQtyDiscount.attr('id'),
715                                'data': {
716                                    'current_qty': new_qty,
717                                    'qty': data.quantity,
718                                    'discount': data.discount,
719                                    'type': data.type
720                                }
721                            },
722                            success: function (response) {
723
724                                if (response.error) {
725
726                                    ptFormCurrentQtyDiscount = 0;
727                                    ptFormCurrentQty = 0;
728                                    // Remove discount fields
729                                    ptForm.find(".pt-discount-type").remove();
730                                    ptForm.find(".pt-discount-value").remove();
731                                    ptForm.find(".pt-quantity-discount-hidden").remove();
732
733                                    ptForm.find('.pt-discount-true').hide();
734
735                                    update_totals();
736                                }
737                                else {
738                                    // Remove discount fields
739                                    ptForm.find(".pt-discount-type").remove();
740                                    ptForm.find(".pt-discount-value").remove();
741                                    ptForm.find(".pt-quantity-discount-hidden").remove();
742
743                                    ptForm.find('.pt-discount-true').hide();
744
745                                    $("<input>", {
746                                        type: "hidden",
747                                        name: 'pt-quantity-discount',
748                                        class: 'pt-quantity-discount-hidden',
749                                        value: response.quantity
750                                    }).appendTo(ptForm);
751                                    $("<input>", {
752                                        type: "hidden",
753                                        name: 'pt-discount-uid',
754                                        class: 'pt-discount-uid',
755                                        value: response.uid
756                                    }).appendTo(ptForm);
757                                    $("<input>", {
758                                        type: "hidden",
759                                        name: 'pt-discount[type]',
760                                        class: 'pt-discount-type',
761                                        value: response.type
762                                    }).appendTo(ptForm);
763                                    $("<input>", {
764                                        type: "hidden",
765                                        name: 'pt-discount[value]',
766                                        class: 'pt-discount-value',
767                                        value: response.value
768                                    }).appendTo(ptForm);
769
770                                    ptForm.find('.pt-discount-true').show();
771
772                                    update_totals();
773                                }
774                                loaderContainer.remove();
775                            },
776                        });
777
778                        return false;
779                    }
780                    else if(parseInt(new_qty) < parseInt(v.quantity)) {
781                        i++;
782                    }
783                });
784
785                if (discountQtyCount === i) {
786                    ptFormCurrentQtyDiscount = 0;
787                    ptFormCurrentQty = 0;
788                    // Remove discount fields
789                    ptForm.find(".pt-discount-type").remove();
790                    ptForm.find(".pt-discount-value").remove();
791                    ptForm.find(".pt-quantity-discount-hidden").remove();
792
793                    ptForm.find('.pt-discount-true').hide();
794
795                    update_totals();
796                }
797
798            }
799
800            // Set the new value
801            $quantity_input.val( new_qty );
802        }
803
804        // When custom subscription interval is checked or unchecked, process that change
805        function subscription_interval($interval) {
806
807            var $checkbox = $interval.length ? $interval.siblings(':checkbox') : $(this);
808
809            if (!$checkbox.length) {
810                return false;
811            }
812
813            // Make sure there is a value set
814            if (typeof $checkbox.attr('value') === 'undefined') {
815                return false;
816            }
817
818            // If checkbox was checked, update interval in hidden field
819            if ($checkbox.is(':checked') && ($interval instanceof $.Event)) {
820
821                var ptCheckboxFormLabel = $(this).attr('data-pt-label');
822                var ptCheckboxFormValue = $(this).attr('value');
823                var ptCheckboxFormPrice = $(this).attr('data-pt-price');
824
825                // If users select "once" as interval, disable all subscriptions
826                if ((ptCheckboxFormValue === 'once') || (ptCheckboxFormValue === 'eenmalig')) {
827
828                    ptForm.find("[id='pt-subscription-interval']").prop('disabled', true);
829                    ptForm.find("[id='pt-subscription-times']").prop('disabled', true);
830                    ptForm.find("[id='pt-subscription-first-payment']").prop('disabled', true);
831
832                } else {
833
834                    // Help users by adding an s to an interval when they forget about it
835                    if (!ptCheckboxFormValue.endsWith('s')) {
836                        ptCheckboxFormValue = ptCheckboxFormValue + 's';
837                    }
838
839                    ptForm.find("[id='pt-subscription-interval']").prop('disabled', false);
840                    ptForm.find("[name='pt-subscription-interval-options']").prop('disabled', false);
841                    ptForm.find("[id='pt-subscription-times']").prop('disabled', false);
842                    ptForm.find("[id='pt-subscription-first-payment']").prop('disabled', false);
843                    ptForm.find('.pt-subscription-custom-amount').prop('disabled', false);
844
845                    // Update selected interval into subscription field
846                    ptForm.find("[id='pt-subscription-interval']").attr('value', ptCheckboxFormValue);
847                }
848
849                if (ptCheckboxFormPrice) {
850                    ptForm.find("[id='pt-subscription-custom-amount']").attr('value', ptCheckboxFormPrice);
851                    ptForm.find("[id='pt-subscription-custom-amount']").attr('data-pt-price', ptCheckboxFormPrice);
852                    ptForm.find("[id='pt-subscription-custom-value']").attr('value', ptCheckboxFormLabel);
853                }
854
855            }
856
857            update_totals();
858        }
859
860        // When subscription optional is checked or unchecked, process that change
861        function subscription_optional($optional) {
862
863            var $checkbox = $optional.length ? $optional.siblings(':checkbox') : $(this);
864
865            if (!$checkbox.length) {
866                return false;
867            }
868
869            // Make sure there is a value set
870            if (typeof $checkbox.attr('value') === 'undefined') {
871                return false;
872            }
873
874            // If checkbox was checked, add to total, else deduct
875            if ($checkbox.is(':checked') && ($optional instanceof $.Event)) {
876
877                var $state = false;
878                var $stateRequired = false;
879                if ($(this).attr('value') === 'yes') {
880                    $state = false;
881                    $stateRequired = true;
882                }
883
884                if ($(this).attr('value') === 'no') {
885                    $state = true;
886                    $stateRequired = false;
887                }
888
889                ptForm.find("[id='pt-subscription-interval']").prop('disabled', $state);
890                ptForm.find("[name='pt-subscription-interval-options']").prop('disabled', $state);
891                ptForm.find("[name='pt-subscription-interval-options']").prop('required', $stateRequired);
892                ptForm.find("[id='pt-subscription-times']").prop('disabled', $state);
893                ptForm.find("[id='pt-subscription-first-payment']").prop('disabled', $state);
894                ptForm.find('.pt-subscription-custom-amount').prop('disabled', $state );
895            }
896
897            update_totals();
898        }
899
900        // When checkbox with amount is checked or unchecked, process that change
901        function update_checkbox_field($quantity_input) {
902
903            var $checkbox = $quantity_input.length ? (ptFormFb ? $quantity_input.parent().siblings(':checkbox:checked') : $quantity_input.siblings(':checkbox')) : $(this);
904
905            if(!$checkbox.length) {
906                return false;
907            }
908
909            // Make sure it's an amount checkbox by checking data attribute 'data-pt-price' is defined, otherwise abort.
910            if (typeof $checkbox.attr('data-pt-price') === 'undefined') {
911                return false;
912            }
913
914            var ptCheckboxFormGroupAmountId = $checkbox.attr('name').replace('[amount]', '[amount][total]');
915            var total = 0;
916            $checkbox.parents('.pt-form-group-checkbox-new').find(':checked').each(function (index, element) {
917                let cbQty = $quantity_input.length && $(element).siblings('paytium-quantity-input') ? $(element).siblings('.paytium-quantity-input').children('.pt-quantity-input') : false;
918                total += cbQty ? parseFloat($(element).data('pt-price')) * parseFloat(cbQty.val()) : parseFloat($(element).val());
919            });
920
921
922            // Update the total value in value attribute
923            ptForm.find("[id='" + ptCheckboxFormGroupAmountId + "']").attr('value', parseAmount(total));
924
925            // Update total value in pt-price data attribute
926            ptForm.find("[id='" + ptCheckboxFormGroupAmountId + "']").attr('data-pt-price', parseAmount(total));
927
928            // If checkbox was checked, add to total, else deduct
929            if ($checkbox.is(':checked') && ($quantity_input instanceof $.Event)) {
930
931                //
932                // Update label
933                //
934
935                // Get/convert checkbox group label ID
936                var ptCheckboxFormGroupLabelId = $checkbox.attr('name').replace('[amount]', '[label]');
937
938                var ptCheckboxFormValue = $(this).attr('name').replace('[amount]', '[value]');
939
940                // Get previously checked options
941                var ptCheckboxCurrentOptions = ptForm.find("[name='" + ptCheckboxFormGroupLabelId + "']").attr('data-pt-checked-options');
942                ptCheckboxCurrentOptions = JSON.parse(ptCheckboxCurrentOptions);
943
944                // If previously checked options are not an array, this means it's empty
945                if ( Object.keys(ptCheckboxCurrentOptions).length === 0 ) {
946                    ptCheckboxCurrentOptions = {};
947                }
948
949                // Get selected label, convert currency symbol to HTML entity
950                ptCheckboxCurrentOptions[$checkbox.attr('data-pt-checkbox-id')] = $checkbox.parent().text();
951
952                // Start string with current group label
953                var ptCheckboxCurrentOptionsString = ptForm.find("[name='" + ptCheckboxFormGroupLabelId + "']").attr('data-pt-original-label');
954
955                // Convert current options to a string for HTML hidden field
956                var ptCheckboxFormValueString = '';
957
958                for (var key in ptCheckboxCurrentOptions) {
959                    ptCheckboxFormValueString +=  ptCheckboxFormValueString == '' ? ptCheckboxCurrentOptions[key] : ', ' + ptCheckboxCurrentOptions[key];
960                }
961
962                // Convert to a format that is save for HTML fields
963                ptCheckboxCurrentOptions = JSON.stringify(ptCheckboxCurrentOptions);
964
965                // Add updated options to data-pt-checked-options attribute
966                ptForm.find("[name='" + ptCheckboxFormGroupLabelId + "']").attr('data-pt-checked-options', ptCheckboxCurrentOptions);
967
968
969                // Add current options string to hidden HTML field
970                ptForm.find("[name='" + ptCheckboxFormGroupLabelId + "']").attr('value', ptCheckboxCurrentOptionsString);
971                ptForm.find("[name='" + ptCheckboxFormValue + "']").attr('value', ptCheckboxFormValueString);
972
973                //
974                // Update limit
975                //
976
977                var ptCheckboxFormGroupLimitData = $checkbox.attr('name').replace('[amount]', '[limit_data]');
978
979                var ptCheckboxItemId = ptForm.find("[name='" + ptCheckboxFormGroupLimitData + "']").val();
980
981                if ( ptCheckboxItemId !== undefined ) {
982
983                    ptCheckboxItemId = JSON.parse(ptCheckboxItemId);
984
985                    // console.log(ptCheckboxItemId);
986
987                    if (Object.keys(ptCheckboxItemId).length === 0) {
988                        ptCheckboxItemId = {};
989                    }
990
991                    ptCheckboxItemId[$checkbox.data('item_id')] = {
992                        'limit': $checkbox.data('limit'),
993                        'quantity': $checkbox.siblings('.pt-quantity-input').val(),
994                    };
995                    if ($checkbox.data('general_item_id')) {
996                        ptCheckboxItemId[$checkbox.data('item_id')]['general_item_id'] = $checkbox.data('general_item_id');
997                        ptCheckboxItemId[$checkbox.data('item_id')]['general_limit'] = $checkbox.data('general_limit');
998                    }
999
1000                    ptCheckboxItemId = JSON.stringify(ptCheckboxItemId);
1001
1002                    ptForm.find("[name='" + ptCheckboxFormGroupLimitData + "']").val(ptCheckboxItemId);
1003
1004                }
1005
1006            } else if ($checkbox.not(':checked') && ($quantity_input instanceof $.Event) ) {
1007
1008                //
1009                // Update label
1010                //
1011
1012                // Get/convert checkbox group label ID
1013                var ptCheckboxFormGroupLabelId = $checkbox.attr('name').replace('[amount]', '[label]');
1014
1015                var ptCheckboxFormValue = $(this).attr('name').replace('[amount]', '[value]');
1016
1017                // Get previously checked options
1018                var ptCheckboxCurrentOptions = ptForm.find("[name='" + ptCheckboxFormGroupLabelId + "']").attr('data-pt-checked-options');
1019                ptCheckboxCurrentOptions = JSON.parse(ptCheckboxCurrentOptions);
1020
1021                // If previously checked options are an array, remove the selected label
1022                if (typeof ptCheckboxCurrentOptions === 'object') {
1023
1024                    // Only add the selected label if it's not in previously checked options array already
1025
1026                    var ptCheckboxSelectedLabelId = $checkbox.attr('data-pt-checkbox-id');
1027
1028                    delete ptCheckboxCurrentOptions[ptCheckboxSelectedLabelId];
1029
1030                    // Start string with current group label
1031                    var ptCheckboxCurrentOptionsString = ptForm.find("[name='" + ptCheckboxFormGroupLabelId + "']").attr('data-pt-original-label');
1032
1033                    // Convert current options to a string for HTML hidden field
1034
1035                    var ptCheckboxFormValueString = '';
1036                    for (var key in ptCheckboxCurrentOptions) {
1037                        ptCheckboxFormValueString += ptCheckboxFormValueString == '' ? ptCheckboxCurrentOptions[key] : ', ' + ptCheckboxCurrentOptions[key];
1038                    }
1039
1040                    // Convert to a format that is save for HTML fields
1041                    ptCheckboxCurrentOptions = JSON.stringify(ptCheckboxCurrentOptions);
1042
1043                    // Add updated options to data-pt-checked-options attribute
1044                    ptForm.find("[name='" + ptCheckboxFormGroupLabelId + "']").attr('data-pt-checked-options', ptCheckboxCurrentOptions);
1045
1046                    // Add current options string to hidden HTML field
1047                    ptForm.find("[name='" + ptCheckboxFormGroupLabelId + "']").attr('value', ptCheckboxCurrentOptionsString);
1048                    ptForm.find("[name='" + ptCheckboxFormValue + "']").attr('value', ptCheckboxFormValueString);
1049
1050                }
1051
1052                //
1053                // Update limit
1054                //
1055
1056                var ptCheckboxFormGroupLimitData = $checkbox.attr('name').replace('[amount]', '[limit_data]');
1057
1058                var ptCheckboxItemId = ptForm.find("[name='" + ptCheckboxFormGroupLimitData + "']").val();
1059
1060                if ( ptCheckboxItemId !== undefined ) {
1061
1062                    ptCheckboxItemId = JSON.parse(ptCheckboxItemId);
1063
1064                    if (typeof ptCheckboxItemId === 'object') {
1065
1066                        var ptCheckboxSelectedItemId = $checkbox.data('item_id');
1067
1068                        delete ptCheckboxItemId[ptCheckboxSelectedItemId];
1069
1070                        ptCheckboxItemId = JSON.stringify(ptCheckboxItemId);
1071
1072                        ptForm.find("[name='" + ptCheckboxFormGroupLimitData + "']").val(ptCheckboxItemId);
1073
1074                    }
1075                }
1076            }
1077        }
1078
1079
1080        function update_totals(e = false) {
1081
1082            if (e) {
1083                if($(e.target).attr('type') && $(e.target).attr('type') == 'number') {
1084                    quantity_input_validation($(e.target));
1085                }
1086
1087                if($(e.target).hasClass('paytium-spinner')) {
1088                    let input = $(e.target).siblings('.pt-quantity-input');
1089                    if ($(input).attr('type') && $(input).attr('type') == 'number') {
1090                        quantity_input_validation($(input));
1091                    }
1092                }
1093            }
1094
1095            var total = get_form_total(),
1096                first_payment_amount = get_first_payment_amount();
1097            update_checkbox_field($(this));
1098
1099            if (ptFormAmountDiscountData) {
1100
1101                let discountAmountCount = ptFormAmountDiscountData.length,
1102                    i = 0;
1103                $(ptFormAmountDiscountData).each(function (k, v) {
1104
1105                    let total_without_discount = parseFloat(get_form_total(false));
1106
1107                    if ( (ptFormCurrentAmountDiscount !== parseFloat(v.discount) && total_without_discount >= parseFloat(v.amount)) ||
1108                        (ptFormCurrentAmountDiscount === parseFloat(v.discount) && total_without_discount < parseFloat(v.amount)) ) {
1109
1110                        if (ptFormCurrentAmountDiscount > parseFloat(v.discount) && total_without_discount >= ptFormCurrentAmount) {
1111                            return;
1112                        }
1113                        let data = v;
1114                        if (total_without_discount < parseFloat(v.amount)) {
1115                            if (k < discountAmountCount-1) {
1116                                data = ptFormAmountDiscountData[k+1];
1117                            }
1118                            else {
1119                                i++;
1120                                return;
1121                            }
1122                        }
1123
1124                        ptFormCurrentAmountDiscount = parseFloat(data.discount);
1125                        ptFormCurrentAmount = parseInt(data.amount);
1126
1127                        let loaderContainer = $( '<div>', {
1128                            'class': 'pt-checkout-form-loading'
1129                        }).appendTo( ptForm );
1130
1131                        let loader = $( '<div>', {
1132                            'class': 'pt-loader'
1133                        }).appendTo( loaderContainer );
1134
1135                        $.ajax({
1136                            url: paytium_localize_script_vars.admin_ajax_url,
1137                            type: 'POST',
1138                            dataType: 'json',
1139                            data: {
1140                                'action': "pt_ajax_amount_discount",
1141                                'id': ptFormAmountDiscount.attr('id'),
1142                                'data': {
1143                                    'current_amount': total_without_discount,
1144                                    'amount': data.amount,
1145                                    'discount': data.discount,
1146                                    'type': data.type
1147                                }
1148                            },
1149                            success: function (response) {
1150
1151                                if (response.error) {
1152
1153                                    ptFormCurrentAmountDiscount = 0;
1154                                    ptFormCurrentAmount = 0;
1155                                    // Remove discount fields
1156                                    ptForm.find(".pt-discount-type").remove();
1157                                    ptForm.find(".pt-discount-value").remove();
1158                                    ptForm.find(".pt-amount-discount-hidden").remove();
1159
1160                                    ptForm.find('.pt-discount-true').hide();
1161
1162                                    total = get_form_total();
1163                                    update_totals_helper(total,first_payment_amount)
1164                                }
1165                                else {
1166                                    // Remove discount fields
1167                                    ptForm.find(".pt-discount-type").remove();
1168                                    ptForm.find(".pt-discount-value").remove();
1169                                    ptForm.find(".pt-amount-discount-hidden").remove();
1170
1171                                    ptForm.find('.pt-discount-true').hide();
1172
1173                                    $("<input>", {
1174                                        type: "hidden",
1175                                        name: 'pt-amount-discount',
1176                                        class: 'pt-amount-discount-hidden',
1177                                        value: response.amount
1178                                    }).appendTo(ptForm);
1179                                    $("<input>", {
1180                                        type: "hidden",
1181                                        name: 'pt-discount-uid',
1182                                        class: 'pt-discount-uid',
1183                                        value: response.uid
1184                                    }).appendTo(ptForm);
1185                                    $("<input>", {
1186                                        type: "hidden",
1187                                        name: 'pt-discount[type]',
1188                                        class: 'pt-discount-type',
1189                                        value: response.type
1190                                    }).appendTo(ptForm);
1191                                    $("<input>", {
1192                                        type: "hidden",
1193                                        name: 'pt-discount[value]',
1194                                        class: 'pt-discount-value',
1195                                        value: response.value
1196                                    }).appendTo(ptForm);
1197
1198                                    ptForm.find('.pt-discount-true').show();
1199
1200                                    total = get_form_total_with_discount(total_without_discount);
1201                                    update_totals_helper(total,first_payment_amount)
1202                                }
1203                                loaderContainer.remove();
1204                            },
1205                        });
1206                        return false;
1207                    }
1208                    else if(total_without_discount < parseFloat(v.amount)) {
1209                        update_totals_helper(total,first_payment_amount);
1210                        i++;
1211                    }
1212                });
1213
1214                if (discountAmountCount === i) {
1215                    ptFormCurrentAmountDiscount = 0;
1216                    ptFormCurrentAmount = 0;
1217                    // Remove discount fields
1218                    ptForm.find(".pt-discount-type").remove();
1219                    ptForm.find(".pt-discount-value").remove();
1220                    ptForm.find(".pt-amount-discount-hidden").remove();
1221
1222                    ptForm.find('.pt-discount-true').hide();
1223                    total = get_form_total();
1224
1225                    update_totals_helper(total,first_payment_amount)
1226                }
1227                else update_totals_helper(total,first_payment_amount);
1228            }
1229            else update_totals_helper(total,first_payment_amount);
1230        }
1231
1232        function update_totals_helper(total,first_payment_amount) {
1233
1234            ptForm.find('.pt-total-amount').html( currencyFormattedAmount( total, ptFormCurrencySymbol ) );
1235            ptForm.find('.pt_amount').val( total );
1236
1237            var ptFirstPaymentLabel = ptForm.find("[id^=pt-subscription-first-payment-label]").length ?
1238                ptForm.find("[id^=pt-subscription-first-payment-label]").val() + ': ' :
1239                paytium_localize_script_vars.subscription_first_payment + ' ';
1240
1241            ptForm.find('.pt-first-payment-amount').html( ptFirstPaymentLabel + currencyFormattedAmount(first_payment_amount, ptFormCurrencySymbol) );
1242
1243            var minTotalInput = ptForm.find('.pt-total-minimum');
1244            if (minTotalInput.length) {
1245                minTotalInput.val( total );
1246                if (total >= minTotalInput.data('parsley-mintotal') && ptForm.find('div[id^=pt_total_errors]').find('ul').hasClass('filled')) {
1247                    ptForm.find('div[id^=pt_total_errors]').find('ul').removeClass('filled');
1248                }
1249            }
1250
1251        }
1252        update_totals();
1253
1254        $(ptForm).off('submit').on('submit', function(e) {
1255            if (!validateFormAmounts()) e.preventDefault();
1256        });
1257
1258        function validateFormAmounts() {
1259
1260            let formAmounts = [];
1261            ptForm.find(".pt-cf-amount").each(function () {
1262                if ($(this).is('select')) {
1263                    $($(this).children('option')).each(function () {
1264                        let ptPrice = $(this).attr('data-pt-price');
1265                        formAmounts.push(ptPrice);
1266                    })
1267                }
1268                else {
1269                    let ptPrice = $(this).attr('data-pt-price');
1270                    formAmounts.push(ptPrice);
1271                }
1272            });
1273
1274            if (JSON.stringify(ptFormAmounts) !== JSON.stringify(formAmounts)) {
1275                window.alert(paytium_localize_script_vars.validation_failed);
1276                return false;
1277            }
1278            else return true;
1279        }
1280
1281        function submitFormProcessing(event) {
1282
1283            if (!validateFormAmounts()) return;
1284
1285            debug_log('click.ptPaymentBtn fired');
1286
1287            if (ptForm.parsley({
1288                excluded: 'input[type=button], input[type=submit], input[type=reset]',
1289                inputs: 'input, textarea, select, input[type=hidden], :hidden',
1290            }).validate()) {
1291
1292                // Run totals one more time to ensure the total amount is accurate
1293                update_totals();
1294
1295                //
1296                // START - Process (custom) fields
1297                //
1298
1299                // Process individual fields
1300                $(ptForm.find("[id^=pt-field-]")).each(function (index, element) {
1301
1302                    var ptFieldValue = $(element).val(); // Get the field value
1303                    var ptUserLabel = document.getElementById(this.id).getAttribute('data-pt-user-label'); // Get the user defined field label
1304                    var ptFieldType = document.getElementById(this.id).getAttribute('data-pt-field-type'); // Get the field type
1305
1306                    // Get required attribute
1307                    var required = $(element).attr("required");
1308
1309                    // Validate that required fields are filled
1310                    if ((required == 'required') && ptFieldValue == '') {
1311
1312                        window.alert(paytium_localize_script_vars.field_is_required.replace('%s', ptUserLabel));
1313                        debug_log('ProcessFailed');
1314                        return false;
1315                    }
1316
1317                    // Log everything to Console when troubleshooting
1318                    debug_log($(element));
1319                    debug_log('Processing field (type, label, value, id): ' + ptFieldType + ', ' + ptUserLabel + ', ' + ptFieldValue + ', ' + this.id);
1320
1321                    //
1322                    // Add the user's field label to form post data, so it can be used as user-facing identifier for that field
1323                    //
1324
1325                    // Create unique field ID for the user's field label
1326                    var ptUserLabelLabel = this.id + "-label";
1327
1328                    // Add the unique field ID and user's label to the form post data
1329                    $("<input>", {type: "hidden", name: ptUserLabelLabel, value: ptUserLabel}).appendTo(ptForm);
1330
1331                    //
1332                    // Check if field is set as user_data="true" and store the preference if so
1333                    //
1334                    var ptUserData = document.getElementById(this.id).getAttribute('data-pt-user-data'); // Get the field type
1335
1336                    if (ptUserData == 'true') {
1337                        // Create unique field ID for the user's field label
1338                        var ptUserDataLabel = this.id + "-user-data";
1339
1340                        // Add the unique field ID and user's label to the form post data
1341                        $("<input>", {type: "hidden", name: ptUserDataLabel, value: ptUserData}).appendTo(ptForm);
1342                    }
1343
1344                });
1345
1346                //
1347                // END - Process (custom) fields
1348                //
1349
1350                //
1351                // START - Process subscription(s)
1352                //
1353
1354                // Process subscription fields
1355                $(ptForm.find("[id^=pt-subscription-]")).each(function (index, element) {
1356
1357                    if (this.disabled) {
1358                        return false;
1359                    }
1360
1361                    // Get the field value
1362                    var ptFieldValue = $(element).val();
1363
1364                    // Get the field type
1365                    // this.id is field type
1366
1367                    // Log everything to Console when troubleshooting
1368                    debug_log($(element));
1369                    debug_log('Processing ' + this.id + ', ' + ptFieldValue);
1370
1371                    // Add the unique field ID and user's label to the form post data
1372                    $("<input>", {type: "hidden", name: this.id, value: ptFieldValue}).appendTo(ptForm);
1373
1374                });
1375
1376                // Process subscription first payment
1377                if (ptForm.find("[id^=pt-subscription-first-payment]").length > 0) {
1378
1379                    // Get recurring amount/form total before first payment
1380                    var ptRecurringTotal = ptForm.find(".pt_amount").val();
1381
1382                    // Add recurring amount
1383                    $("<input>", {
1384                        type: "hidden",
1385                        name: 'pt-subscription-recurring-payment',
1386                        value: ptRecurringTotal
1387                    }).appendTo(ptForm);
1388
1389                    // Add "First payment" text and amount after the [paytium_total /] Shortcode
1390                    var ptFirstPayment = ptForm.find("[id^=pt-subscription-first-payment]").val();
1391
1392                    // Update total amount to first payment
1393                    ptForm.find('.pt_amount').val(ptFirstPayment);
1394
1395                }
1396
1397                //
1398                // END - Process subscription fields
1399                //
1400
1401                //
1402                // START - Remove discount field if it's empty or type and value fields are missing
1403                //
1404
1405                if (ptForm.find(".pt-discount").length > 0 && !ptForm.find(".pt-discount").val()) {
1406                    ptForm.find(".pt-discount").prop('disabled', 'true');
1407                }
1408
1409                if (!ptForm.find('[name$="pt-discount[type]"]').length || !ptForm.find('[name$="pt-discount[value]"]').length) {
1410                    ptForm.find(".pt-discount").prop('disabled', 'true');
1411                }
1412
1413                //
1414                // END - Remove discount field if it's empty
1415                //
1416
1417                // If there is no amount entered or amount is too low to be processed by Mollie
1418                // block execution of script and show an alert. Why 1 euro? Lower amounts
1419                // are just not logical!
1420                if ((get_form_total() <= '0.99') && (isPaytiumNoPayment() == false)) {
1421                    window.alert(paytium_localize_script_vars.amount_too_low);
1422                    return false;
1423                }
1424
1425                // Enable the below line if you want to process the form without redirecting
1426                // The form data is not stored in Paytium at this point
1427                // Also see line 24 in /paytium/includes/process-payment-functions.php
1428                //return false;
1429
1430                // Unbind original form submit trigger before calling again to "reset" it and submit normally.
1431                ptForm.unbind('submit');
1432                ptForm.submit();
1433
1434                // Disable original payment button and change text for UI feedback while POST-ing to Mollie
1435                ptForm.find('.pt-payment-btn')
1436                    .prop('disabled', true)
1437                    .find('span')
1438                    .text(paytium_localize_script_vars.processing_please_wait);
1439
1440            }
1441
1442            if (typeof event != 'undefined') {
1443                event.preventDefault();
1444            }
1445        }
1446
1447        function check_item_limits(event) {
1448
1449            var data = {};
1450            var $item_id_input;
1451
1452            event.preventDefault();
1453
1454            $(ptForm.find('[name$="[item_id]"], .pt-checkbox-group')).each(function (index, element) {
1455
1456                var $limit_data = $(element).find('[name$="[limit_data]"]');
1457                if($limit_data.length) {
1458                    var json_data = JSON.parse($limit_data.val());
1459                    $.each(json_data, function (item_id, value) {
1460                        data[item_id] = value;
1461                    })
1462                }
1463                else {
1464                    var item_id = $(element).val();
1465                    var limit = $(element).siblings('[name$="[limit]"]').val();
1466                    var quantity = $(element).siblings('.pt-quantity-input').val();
1467                    data[item_id] = {'limit': limit, 'quantity': quantity};
1468                }
1469
1470            });
1471
1472            if(!$.isEmptyObject(data)) {
1473
1474                $.ajax({
1475                    url: paytium_localize_script_vars.admin_ajax_url,
1476                    type: 'POST',
1477                    dataType: 'json',
1478                    data: {
1479                        'action': "pt_ajax_check_item_limits",
1480                        'data': data
1481                    },
1482                    success: function (response) {
1483
1484                        $('.limit-error-message').remove();
1485                        if (response.error) {
1486                            $.each(response.limit_exceeded, function (item_id, data) {
1487                                $item_id_input = $('[value="' + item_id + '"]').length ?  $('[value="' + item_id + '"]') : $('[data-item_id="' + item_id + '"]');
1488
1489                                $item_id_input.before('<p class="limit-error-message">Limit reached! Only ' + data.items_left + ' items left</p>');
1490
1491                            });
1492                        }
1493                        else {
1494                            submitFormProcessing();
1495                        }
1496                    },
1497
1498                });
1499            }
1500
1501            else  {
1502                submitFormProcessing();
1503            }
1504
1505        }
1506
1507        //
1508        // START - Paytium Links
1509        //
1510
1511        ptForm.find("[id^=pt-paytium-links]").each(function () {
1512
1513            // Helper function to sanitize input
1514            function sanitizeInput(input) {
1515                return input ? input.replace(/[^\w\s,-|]/gi, '') : ' '; // Allows alphanumeric, space, dash, dot, and pipe ("|")
1516            }
1517
1518            // Create an object with all data
1519            function getSearchParameters() {
1520                var prmstr = window.location.search.substr(1);
1521                prmstr = decodeURIComponent(prmstr);
1522                return prmstr !== null && prmstr !== "" ? transformToAssocArray(prmstr) : {};
1523            }
1524
1525            function transformToAssocArray(prmstr) {
1526                var params = {};
1527                var prmarr = prmstr.split("&");
1528                for (var i = 0; i < prmarr.length; i++) {
1529                    var tmparr = prmarr[i].split("=");
1530                    var key = sanitizeInput(tmparr[0]);   // Sanitize keys
1531                    var value = sanitizeInput(tmparr[1]); // Sanitize values
1532                    params[key] = value;
1533                }
1534                return params;
1535            }
1536
1537            var params = getSearchParameters();
1538
1539            $.each(params, function (key, valueObj) {
1540                // Process each param and update the form
1541                ptForm.find("[id^=pt-field-]").each(function (index, element) {
1542                    var ptUserLabel = document.getElementById(this.id).getAttribute('data-pt-user-label');
1543                    if (ptUserLabel == key && !$(element).hasClass('pt-field-checkbox')) {
1544                        $(element).val(sanitizeInput(valueObj));
1545                    }
1546                });
1547
1548                if (ptForm.find('#pt-paytium-pro').length) {
1549                    ptForm.find(".pt-checkbox-group, .pt-radio-group").each(function (index, element) {
1550                        let selector = $(element),
1551                            ptUserLabel = selector.data('pt-user-label');
1552                        if (ptUserLabel == key) {
1553                            let choices = valueObj.split('|');
1554                            $(choices).each(function () {
1555                                $(selector).find('label[title="' + s
1555anitizeInput(this) + '"]').click();
1556                            });
1557                        }
1558                    });
1559                }
1560
1561                if (key === 'subscription_option') {
1562                    ptForm.find('.pt-radio-group.pt-subscription-interval-options label[title="'+sanitizeInput(valueObj)+'"]').click();
1563                }
1564
1565                if (key.toLowerCase() == 'quantity') {
1566                    ptForm.find('.pt-quantity-input').val(sanitizeInput(valueObj));
1567                }
1568
1569                if (key.toLowerCase() == 'bedrag' || key.toLowerCase() == 'amount') {
1570                    var sanitizedValue = sanitizeInput(valueObj);
1571                    ptForm.find("[name*='pt-amount']").val(sanitizedValue);
1572                    ptForm.find('.pt-uea-custom-amount').val(sanitizedValue);
1573
1574                    ptForm.find('.pt-uea-custom-amount-formatted').val(parseAmount(sanitizedValue)).attr('data-pt-price', parseAmount(sanitizedValue));
1575                    ptForm.find("[name*='pt-amount']").attr('data-pt-price', parseAmount(sanitizedValue));
1576                    ptForm.find('.pt-uea-custom-amount').attr('data-pt-price', parseAmount(sanitizedValue));
1577                }
1578            });
1579
1580            update_totals();
1581
1582            ptForm.find("[id^=pt-paytium-links-auto-redirect]").each(function () {
1583                ptForm.find('.pt-payment-btn').click(submitFormProcessing());
1584            });
1585
1586        });
1587
1588        //
1589        // END - Paytium Links
1590        //
1591
1592        ptForm.find('.pt-payment-btn').off('click.ptPaymentBtn').on('click.ptPaymentBtn', check_item_limits);
1593
1594        // Multiplier functionality
1595        ptForm.find('.pt_multiplier')
1596            .each(function () {
1597                pt_multiply($(this));
1598            })
1599            .on('change keyup', function () {
1600                pt_multiply($(this));
1601            });
1602
1603        function pt_multiply($this) {
1604
1605            let multiplier_id = $this.data('pt_multiplier_id'),
1606                multiplier_val = +$this.val();
1607
1608            $(ptForm.find('.pt-multiplied-by')).each(function () {
1609                if ($(this).data('pt_multiplied_by').indexOf(multiplier_id) > -1) {
1610
1611                    let multiplied_by = $(this).data('pt_multiplied_by'),
1612                        amount_input = $(this).siblings('.pt-cf-amount'),
1613                        amount_val = amount_input.val(),
1614                        amount_html = $(this).siblings('.pt-quantity-amount'),
1615                        multiplier = multiplier_val;
1616
1617                    if (!$(this).data('start_amount')) {
1618                        $(this).data('start_amount',+amount_val)
1619                    }
1620
1621                    if ($(this).data('pt_multiplied_by').length > multiplier_id.length) {
1622
1623                        let multipliers_arr = multiplied_by.split(',');
1624
1625                        $.each(multipliers_arr, function (i,v) {
1626                            multiplier = multiplier_id !== v ? multiplier * ptForm.find('.pt_multiplier[data-pt_multiplier_id='+v+']').val() : multiplier;
1627                        })
1628                    }
1629
1630                    let new_amount = parseAmount($(this).data('start_amount') * multiplier);
1631
1632                    amount_input.attr('data-pt-price', new_amount);
1633                    amount_html.text(currencyFormattedAmount(new_amount, ptFormCurrencySymbol));
1634                }
1635            });
1636
1637            if (ptForm.find('.pt-discount-checkbox-with-condition').length) {
1638
1639                let discountCbGroup = ptForm.find('.pt-discount-checkbox-with-condition'),
1640                    cb = discountCbGroup.find('.pt-discount-checkbox');
1641
1642                if (multiplier_id === discountCbGroup.data('condition-id')) {
1643                    if (multiplier_val >= discountCbGroup.data('condition-qty')) {
1644                        cb.prop('disabled',false);
1645                    }
1646                    else {
1647                        // Remove checkbox discount fields
1648                        ptForm.find(".pt-discount-type").remove();
1649                        ptForm.find(".pt-discount-value").remove();
1650                        ptForm.find(".pt-discount-checkbox-hidden").remove();
1651
1652                        ptForm.find('.pt-discount-true').hide();
1653                        cb.prop('checked',false).prop('disabled',true).parent().removeClass('checked');
1654                    }
1655                }
1656                $(ptForm.find('.pt-multiplied-by'));
1657            }
1658            update_totals();
1659        }
1660
1661
1662        ptForm.find('.pt-discount-checkbox').on('change', function(e) {
1663
1664            let loaderContainer = $( '<div>', {
1665                'class': 'pt-checkout-form-loading'
1666            }).appendTo( ptForm );
1667
1668            let loader = $( '<div>', {
1669                'class': 'pt-loader'
1670            }).appendTo( loaderContainer );
1671
1672            $(this).prop('disabled',true);
1673
1674            let cb = $(this),
1675                checked = '',
1676                data = {};
1677
1678            data.id = cb.attr('id');
1679            if ($(this).parents('.pt-discount-checkbox-with-condition').length) {
1680                let cbGroup = $(this).parents('.pt-discount-checkbox-with-condition'),
1681                    conditionId = cbGroup.data('condition-id'),
1682                    conditionQty = cbGroup.data('condition-qty'),
1683                    conditionField = ptForm.find('.pt_multiplier[data-pt_multiplier_id="'+conditionId+'"]'),
1684                    conditionFieldVal = +conditionField.val();
1685
1686                data.condition_qty = conditionQty;
1687                data.condition_field_val = conditionFieldVal;
1688
1689                if (conditionFieldVal < conditionQty) {
1690                    $(this).prop('checked',false);
1691                    return false;
1692                }
1693            }
1694
1695
1696            if ($(this).prop('checked')) {
1697                checked = true;
1698                $(this).parent().addClass('checked')
1699            }
1700            else {
1701                checked = false;
1702                cb.parent().removeClass('checked');
1703            }
1704
1705            $.ajax({
1706                url: paytium_localize_script_vars.admin_ajax_url,
1707                type: 'POST',
1708                dataType: 'json',
1709                data: {
1710                    'action': "pt_ajax_discount_checkbox",
1711                    'data': data
1712                },
1713                success: function (response) {
1714
1715                    if (response.error) {
1716                        // $.each(response.limit_exceeded, function (item_id, data) {
1717                        //     $item_id_input = $('[value="' + item_id + '"]').length ?  $('[value="' + item_id + '"]') : $('[data-item_id="' + item_id + '"]');
1718                        //
1719                        //     $item_id_input.before('<p class="limit-error-message">Limit reached! Only ' + data.items_left + ' items left</p>');
1720                        //
1721                        // });
1722                        console.log('discount checkbox error');
1723                    }
1724                    else {
1725                        console.log('discount checkbox success');
1726
1727                        if (checked) {
1728                            $("<input>", {
1729                                type: "hidden",
1730                                name: 'pt-discount-checkbox',
1731                                class: 'pt-discount-checkbox-hidden',
1732                                value: response.checkbox
1733                            }).appendTo(ptForm);
1734                            $("<input>", {
1735                                type: "hidden",
1736                                name: 'pt-discount-uid',
1737                                class: 'pt-discount-uid',
1738                                value: response.uid
1739                            }).appendTo(ptForm);
1740                            $("<input>", {
1741                                type: "hidden",
1742                                name: 'pt-discount[type]',
1743                                class: 'pt-discount-type',
1744                                value: response.type
1745                            }).appendTo(ptForm);
1746                            $("<input>", {
1747                                type: "hidden",
1748                                name: 'pt-discount[value]',
1749                                class: 'pt-discount-value',
1750                                value: response.value
1751                            }).appendTo(ptForm);
1752
1753                            ptForm.find('.pt-discount-true').show();
1754                        }
1755                        else {
1756                            // Remove discount fields
1757                            ptForm.find(".pt-discount-type").remove();
1758                            ptForm.find(".pt-discount-value").remove();
1759                            ptForm.find(".pt-discount-checkbox-hidden").remove();
1760
1761                            ptForm.find('.pt-discount-true').hide();
1762                        }
1763
1764                        update_totals();
1765                    }
1766
1767                    cb.prop('disabled',false);
1768                    loaderContainer.remove();
1769                },
1770
1771            });
1772        });
1773
1774        function general_limit_control(input) {
1775
1776            if (input !== undefined) {
1777
1778                if (input.hasClass('general-limit-qty')) {
1779
1780                    let currentGeneralQty = 0,
1781                        currentVal = input.val();
1782
1783                    ptForm.find(".general-limit-qty").each(function () {
1784                        currentGeneralQty = currentGeneralQty + parseInt($(this).val());
1785                    });
1786
1787                    ptForm.find(".general-limit-item").each(function () {
1788                        if ((($(this).attr('type') === 'radio' || $(this).attr('type') === 'checkbox') && $(this).prop('checked')) ||
1789                            $(this).attr('type') === 'select' || $(this).attr('label')) {
1790                            currentGeneralQty = currentGeneralQty + 1;
1791                        }
1792                    });
1793
1794                    if (currentGeneralQty > ptFormGeneralLimit) {
1795                        let newVal = currentVal - (currentGeneralQty - ptFormGeneralLimit);
1796                        return {'result': false, 'new_val': newVal};
1797                    }
1798                }
1799            }
1800        }
1801
1802        var ptPaytiumDate = ptForm.find('.pt-paytium-date');
1803        if (ptPaytiumDate.length > 0) {
1804            ptPaytiumDate.each(function() {
1805                if (!$(this).hasClass('hasDatepicker')) {
1806                    $(this).datepicker({
1807                        dateFormat: 'dd-mm-yy',
1808                        changeMonth: true,
1809                        changeYear: true,
1810                    });
1811                }
1812            });
1813        }
1814
1815        var ptPaytiumBirthday = ptForm.find('.pt-paytium-birthday');
1816        if (ptPaytiumBirthday.length > 0) {
1817            ptPaytiumBirthday.each(function() {
1818                if (!$(this).hasClass('hasDatepicker')) {
1819                    $(this).datepicker({
1820                        dateFormat: 'dd-mm-yy',
1821                        changeMonth: true,
1822                        changeYear: true,
1823                        yearRange: '-85:+0'
1824                    });
1825                }
1826            });
1827        }
1828    }
1829
1830    $(function () {
1831        var $body = $('body');
1832
1833        // Make sure each checkbox change sets the appropriate hidden value (Yes/No) to record
1834        // to Paytium payment records.
1835        $body.on('change', '.pt-checkout-form .pt-field-checkbox', function () {
1836            var checkbox = $(this);
1837            var checkboxId = checkbox.prop('id');
1838            var hiddenField = $body.find('#' + checkboxId + '_hidden'); // Hidden ID field is simply "_hidden" appended to checkbox ID field.
1839
1840            hiddenField.val(checkbox.is(':checked') ? 'Yes' : 'No'); // Change to "Yes" or "No" depending on checked or not.
1841        });
1842
1843        // Function to initialize any uninitialized forms
1844        function initializeUninitializedForms() {
1845            $('.pt-checkout-form').each(function () {
1846                initializePaytiumForm($(this));
1847            });
1848        }
1849
1850        // Initialize forms on initial page load
1851        initializeUninitializedForms();
1852
1853        // Periodically check for and initialize new forms (e.g., in Elementor popups)
1854        setInterval(initializeUninitializedForms, 500); // Check every 500 milliseconds
1855    });
1856
1857    // Convert to formatted amount
1858    function currencyFormattedAmount(amount, currencySymbol) {
1859        amount = parseAmount( amount ) + ''; // Convert to string
1860        amount = Number(Math.round(amount+'e2')+'e-2').toFixed(2);
1861        amount = amount.replace( '.', pt.decimal_separator );
1862        return currencySymbol == 'fr.' || currencySymbol == 'NOK' || currencySymbol == 'SEK' ? amount + " " + currencySymbol : currencySymbol + " " + amount;
1863    }
1864
1865    /**
1866     * Parse to a valid amount.
1867     * @returns float Valid number.
1868     */
1869    function parseAmount( amount ) {
1870        if ( typeof amount == 'string' ) {
1871            amount = amount.replace( ',', '.' );
1872        }
1873
1874        if ( isNaN( amount ) || amount == '' ) {
1875            amount = 0;
1876        }
1877
1878        amount = parseFloat( amount );
1879        return roundToTwo( amount );
1880    }
1881
1882    // https://stackoverflow.com/a/18358056/3389968
1883    function roundToTwo(num) {
1884        return +(Math.round(num + "e+2")  + "e-2");
1885    }
1886
1887
1888    window.Parsley
1889        .addValidator('filemaxmegabytes', {
1890            requirementType: 'string',
1891            validateString: function (value, requirement, parsleyInstance) {
1892
1893                var file = parsleyInstance.$element[0].files;
1894                var maxBytes = requirement * 1048576;
1895
1896                if (file.length == 0) {
1897                    return true;
1898                }
1899
1900                var files_data = [];
1901                $(file).each(function(i,v){
1902                    if (v.size > maxBytes) {
1903                        files_data.push(v.size);
1904                    }
1905                });
1906
1907                return files_data.length === 0;
1908
1909            },
1910            messages: {
1911                en: 'File is to big. Maximum allowed size is %s MB.',
1912                nl: 'Het bestand is te groot, de maximale bestandsgrootte is %s MB.'
1913            }
1914        })
1915        .addValidator('maxfiles', {
1916            requirementType: 'string',
1917            validateString: function (value, requirement, parsleyInstance) {
1918
1919                var file = parsleyInstance.$element[0].files;
1920
1921                if (file.length == 0) {
1922                    return true;
1923                }
1924
1925                return file.length <= requirement;
1926
1927            },
1928            messages: {
1929                en: 'Maximum of files can be uploaded at once is %s.',
1930                nl: 'Het maximum aantal bestanden dat tegelijkertijd verstuurd mag worden is %s. '
1931            }
1932        })
1933        .addValidator('filemimetypes', {
1934            requirementType: 'string',
1935            validateString: function (value, requirement, parsleyInstance) {
1936
1937                var file = parsleyInstance.$element[0].files;
1938
1939                if (file.length == 0) {
1940                    return true;
1941                }
1942
1943                var allowedMimeTypes = requirement.replace(/\s/g, "").split(',');
1944                var files_data = [];
1945
1946                $(file).each(function(k,v){
1947
1948                    if (allowedMimeTypes.indexOf(v.type) == -1) {
1949                        files_data.push(allowedMimeTypes.indexOf(v.type) == -1);
1950                    }
1951
1952                });
1953
1954                return files_data.length === 0;
1955            },
1956            messages: {
1957                en: 'File mime type not allowed.'
1958            }
1959        })
1960        .addValidator('postcode', {
1961            requirementType: 'string',
1962            validateString: function(value, requirement, parsleyInstance) {
1963                var postcode = parsleyInstance.value.match(/^\d{4}\s?\w{2}$/g);
1964
1965                return postcode !== null;
1966            },
1967            messages: {
1968                en: 'The postcode entered is invalid, the format should be 1234AB.',
1969                nl: 'De ingevulde postcode is niet correct, en moet formaat 1234AB hebben.'
1970            }
1971        })
1972        .addValidator('date', {
1973            requirementType: 'string',
1974            validateString: function (value, requirement, parsleyInstance) {
1975
1976                var reqs = value.split("-"),
1977                    day = reqs[0],
1978                    month = reqs[1],
1979                    year = reqs[2].substring(0, 4);
1980
1981                // check if date is a valid
1982                var checkdate = new Date(year + "-" + month + "-" + day);
1983                return Boolean(+checkdate) && parseInt(checkdate.getDate(), 10) === parseInt( day, 10);
1984
1985            },
1986            messages: {
1987                en: 'The date entered is invalid, the format should be DD-MM-YYYY.',
1988                nl: 'De ingevulde datum is niet correct, en moet formaat DD-MM-JJJJ hebben.'
1989            }
1990        })
1991        .addValidator('open', {
1992            requirementType: 'string',
1993            validateString: function (value, requirement, parsleyInstance) {
1994
1995                let actualValue = $(parsleyInstance.$element).parents('.pt-uea-container-amount').siblings('.pt-uea-custom-amount-formatted').attr('data-pt-price');
1996
1997                value = value.toString();
1998                value = parseFloat(value.replace(/,/g, '.'));
1999
2000                requirement = requirement.toString();
2001                requirement = parseFloat(requirement.replace(/,/g, '.'));
2002
2003                if (requirement <= value && value == actualValue) {
2004                    return true;
2005                }
2006                return false;
2007
2008            },
2009            messages: {
2010                en: 'The minimum amount is '+currencySymbol+'%s.',
2011                nl: 'Het minimum bedrag is '+currencySymbol+'%s.'
2012            }
2013        })
2014        .addValidator('mintotal', {
2015            requirementType: 'string',
2016            validateString: function (value, requirement, parsleyInstance) {
2017                return parseFloat(value) >= requirement;
2018            },
2019            messages: {
2020                en: 'The minimum amount is '+currencySymbol+'%s.',
2021                nl: 'Het minimum bedrag is '+currencySymbol+'%s.'
2022            }
2023        })
2024        .addValidator('phone', {
2025            requirementType: 'string',
2026            validateString: function (value, requirement, parsleyInstance) {
2027
2028                if (value.match('^[+]*[(]{0,1}[0-9]{1,4}[)]{0,1}[-\\s\\.0-9]*$')) {
2029                    return true;
2030                }
2031                else {
2032                    return false;
2033                }
2034            },
2035            messages: {
2036                en: 'Please enter a valid phone number',
2037                nl: 'Voer alstublieft een geldig telefoonnummer in'
2038            }
2039        });
2040
2041
2042    $(document).ready(function () {
2043        // Remove message after payment if more then once is shown
2044        $('.pt-payment-details-wrap').eq(1).remove();
2045
2046        // Paytium login
2047        $('body').on('click', '.paytium-login-btn', function () {
2048            $('.paytium-login-form-overlay').fadeIn(200);
2049            $('html').addClass('paytium-overflow-hidden')
2050        });
2051
2052        $('body').on('click', '.paytium-login-close', function () {
2053            $('.paytium-login-form-overlay').fadeOut(200);
2054            $('html').removeClass('paytium-overflow-hidden')
2055        });
2056
2057        $('body').on('click', '.paytium-login-form-wrapper', function (e) {
2058            if ($(e.target).is('.paytium-login-form-wrapper')) {
2059                $('.paytium-login-form-overlay').fadeOut(200);
2060                $('html').removeClass('paytium-overflow-hidden')
2061            }
2062        });
2063
2064        /* paytium custom quantity input */
2065        $('body').on('click', '.paytium-spinner.increment', function () {
2066            let input = $(this).siblings('.pt-quantity-input[type=number]'),
2067                val = parseInt(input.val()),
2068                max = input.attr('max') ? parseInt(input.attr('max')) : '',
2069                step = input.attr('step') ? parseInt(input.attr('step')) : 1;
2070
2071            if ((max && val+step <= max) || !max) {
2072                input.val(val+step).trigger('change');
2073            }
2074        });
2075        $('body').on('click', '.paytium-spinner.decrement', function () {
2076            let input = $(this).siblings('.pt-quantity-input[type=number]'),
2077                val = parseInt(input.val()),
2078                min = input.attr('min') ? parseInt(input.attr('min')) : '',
2079                step = input.attr('step') ? parseInt(input.attr('step')) : 1;
2080
2081            if ((min && val-step >= min) || (!min && val-step >= 0)) {
2082                input.val(val-step).trigger('change');
2083            }
2084        });
2085    });
2086
2087    function getCurrencySymbol(currencyCode) {
2088        var currencies = {
2089            EUR: '€',
2090            USD: '$',
2091            GBP: '£',
2092            CHF: 'fr.',
2093            NOK: 'NOK',
2094            SEK: 'SEK'
2095        };
2096        return currencies[currencyCode];
2097    }
2098}(jQuery));

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.