PageSourceSearch

https://wgm.org/Contributions/js/frontend/form/Payment.js?1789539196

js wgm.org collected 2026-09-24 09:23:00 UTC 62,464 bytes, 1,519 lines download raw bytes

1/**
2 * @author Softped Development Team <[email protected]>
3 */
4jQuery.namespace( 'SS.com.Contributions.frontend.form' );
5
6/**
7 * Control the checkout page form
8 * @param config - Array('loggedIn','personId','form','reviewUrl')
9 * @constructor
10 */
11
12SS.com.Contributions.frontend.form.Payment = function( config ){
13    var me = this;
14    this.config = config;
15    this.form = $('form[name="' + this.config.form + '"]');
16    this.formHelper = this.form.data('formHelper');
17    
18    this.formHelper.options.errorMessages.required = __d('contributions','This field is required');
19    this.formHelper.options.errorMessages.minlength = __d('contributions','This field must have at least {minLength} characters in length');
20    this.formHelper.options.errorMessages.maxlength = __d('contributions','This field must have maximum {maxLength} characters in length');
21    this.formHelper.options.errorMessages.email = __d('contributions','This email is not a valid email address');
22    this.formHelper.options.errorMessages.phone = __d('contributions','This is not a valid US phone number');
23    this.formHelper.options.errorMessages.number = __d('contributions','This field must contain only numbers');
24    this.formHelper.options.errorMessages.cardNumber = __d('contributions','This is not a valid card number');
25    
26    this.init();
27
28    SS.com.Contributions.frontend.form.instance = this;
29};
30
31SS.com.Contributions.frontend.form.Payment.prototype = {
32
33    /**
34     * Init event listeners
35     */
36    init: function()
37    {
38        var me = this;
39        
40        this.formHelper.options.customErrorMessages = {
41            required: {
42                'person[title]': __d('contributions','Title is requried'),
43                'person[title_display]': __d('contributions','Title is requried')
44            }
45        };
46
47        this.transparentRedirectWrapperInstances = {};
48        
49        // toggle payment type panels
50        this.form.find('input[name="payment[type]"]').on('change',function(){
51            me.togglePaymentType();
52        });
53
54        // toggle organization information panels
55        this.form.find('input[name="group[yes]"]').on('change', function () {
56//			me.toggleOrganizationInformation( $(this) );
57            me.toggleOrganizationSelector($(this));
58        });
59
60        this.form.find('input[name="alternate_organization"]').on('change', function () {
61            me.toggleAlternateOrganizationSelector($(this));
62        });
63        
64        var organizationField = this.form.find('input[name="group[id_display]"]');
65        organizationField.data('validator',function(field){
66            var panel = me.form.find('.form-panel-organization' );
67            if( !panel.is(':visible') ){
68                var combo = $(field).closest('.form-input-wrapper');
69                var valueField = combo.find('input[type="hidden"]');
70                if(valueField.val()==''){
71                    return __d('contributions','Select or add an Organization')
72                }
73            }
74            return false;
75        });
76
77        var confirmEmailField = this.form.find('input[name="person[confirm_email]"]');
78        confirmEmailField.data('validator',function(field){
79            var emailField = me.form.find('input[name="person[email]"]');
80            
81            if(emailField.val()!=$(field).val()){
82                return __d('contributions','Emails do not match')
83            }
84            return false;
85        });
86
87        // toggle tribute options panel
88        this.form.find('input[name="tribute[is_tribute]"]').on('change',function(){
89            me.toggleTributeOptions( $(this) );
90        });
91
92        // toggle tribute recipient panel
93        this.form.find('input[name="tribute[has_recipient]"]').on('change',function(){
94            me.toggleTributeRecipient( $(this) );
95        });
96        
97        // toggle recurring options panels and set the payment type to card
98        this.form.find('input[name="is_recurring"]').on('change', function () {
99
100            var updateCart = true;
101            var noSync = $(this).data('noSync');
102
103            if (noSync) {
104                updateCart = false;
105            }
106
107            me.toggleRecurringOptions($(this), updateCart);
108        });
109
110        this.form.find('input[name="recurring[start_date_display]"]').on('change',function(a,b){
111
112            var minEndDate = $(this).datepicker('getDate');
113
114            $(this).data('originalDate',new Date(minEndDate));
115            
116            minEndDate.setDate(minEndDate.getDate()+1);
117
118            var endDateField = me.form.find('input[name="recurring[end_date_display]"]');
119            endDateField.datepicker("option", "minDate",minEndDate);
120
121        })
122
123        if ((this.config.cart && this.config.cart.recurring) || 
124            (this.config.forceRecurring)) {
125            this.form.find('input[name="is_recurring"]').prop('checked', true);
126            this.toggleRecurringOptions(null, false);
127        }
128
129        this.form.find('input[name="recurring[type]"]').on('change',function(){
130            SS.com.Contributions.frontend.CartSummary.addRecurringToCart(null, $(this).val());
131        });
132
133        $(window).on('setRecurring',function(event,recurring){
134
135            if(me.config.forceRecurring){
136                return;
137            }
138
139            var recurringBox = me.form.find('input[name="is_recurring"]');
140
141            var checked = recurringBox.prop('checked');
142
143            if(checked!=recurring){
144                recurringBox.data('noSync',1);
145                recurringBox.prop('checked',recurring).change();
146                recurringBox.removeData('noSync');
147            }
148        })
149
150        this.form.find('.change-default-country a.form-field-button').on('click',function () {
151
152            $(this).closest('.change-default-country').find('input[name="address[location_country_id]"]').attr('disabled','disabled').data('force-disable',1);
153            $(this).closest('.change-default-country').find('input[name="group[address][location_country_id]"]').attr('disabled','disabled').data('force-disable',1);
154            $(this).closest('.change-default-country').hide();
155
156            var addressWrapper = $(this).closest('.default-country.hide-country');
157            addressWrapper.removeClass('hide-country');
158            addressWrapper.find('.location-country-field').removeClass('form-disabled').find('select').data('force-disable',null).removeAttr('disabled');
159        })
160
161        // toggle register options panels
162//		this.form.find('input[name="register"]').on('change',function(){
163//			me.toggleRegisterOptions( $(this) );
164//		});
165        
166        // show/hide new card panel for logged users when Add New button is clicked
167        this.form.find('select[name="payment[id]"]').parent('.form-input-holder').find('.form-field-button').on('click',function(){
168            me.toggleAddCardOrCheck( $(this) );
169            me.toggleText($(this),__d('contributions','Cancel'),$(this).data('add-new-text'));
170            return false;
171        });
172        // hide new card panel if any value was set on cards drop down
173        this.form.find('select[name="payment[id]"]').on('change',function(){
174            if( $(this).val() ){
175                me.formHelper.allowBlank( $(this), false );
176                me.formHelper.hidePanel( me.form.find('.form-panel-card') );
177                me.formHelper.hidePanel( me.form.find('.form-panel-check') );
178                me.formHelper.hidePanel( me.form.find('.form-panel-custom-method') );
179                me.formHelper.hidePanel( me.form.find('.form-panel-payment-type') );
180                me.formHelper.hidePanel( me.form.find('.billing-information') );
181                if( me.form.find('.form-panel-card').is(':visible') || me.form.find('.form-panel-check').is(':visible') || me.form.find('.form-panel-custom-method').is(':visible')){
182                    var addNewButton = me.form.find('select[name="payment[id]"]').parent('.form-input-holder').find('.form-field-button');
183                    me.toggleText( addNewButton,'Cancel',addNewButton.data('add-new-text') );
184                }
185                me.toggleTransferFee();
186                me.toggleStoreOption();
187            }
188        });
189        // select a month when a number is pressed while month field is focused
190        this.monthKeyString = '';
191        this.monthTimer = '';
192        this.form.find('select[name="payment[card][expiration_month]"]').on('keypress',function(event){
193            var monthField = $(this);
194            var pressedKey = String.fromCharCode(event.which);
195            if(pressedKey.match(/[0-9]/)){
196                me.monthKeyString += pressedKey;
197            }
198            if(me.monthTimer){
199                clearTimeout(me.monthTimer);
200            }
201            me.monthTimer = setTimeout(function(){
202                if(parseInt(me.monthKeyString) >= 1 && parseInt(me.monthKeyString) <= 12){
203                    me.monthKeyString = me.padNumber(me.monthKeyString, 2);
204                    monthField.val(me.monthKeyString);
205                }
206                me.monthKeyString = '';
207            },300);
208        });
209
210        // show/hide new address panel for logged users when Add New button is clicked
211        this.form.find('select[name="address[id]"]').parent('.form-input-holder').find('.form-field-button').on('click',function(){
212            me.toggleAddAddress( $(this) );
213            me.toggleText($(this),__d('contributions','Cancel'),__d('contributions','Add New'));
214            return false;
215        });
216        // hide new address panel if any value was set on cards drop down
217        this.form.find('select[name="address[id]"]').on('change',function(){
218            if( $(this).val() ){
219                me.formHelper.allowBlank( $(this), false );
220                me.formHelper.hidePanel( me.form.find('.form-panel-address') );
221            }
222        });
223
224        // Change the states field type in the billing address panel
225        this.form.find('select[name="address[city]"]').parent('.form-input-holder').find('.form-field-button').on('click',function(){
226            me.formHelper.toggleFieldType( $(this).parent().find('select[name="address[city]"]') );
227            me.toggleText($(this),__d('contributions','Back'),__d('contributions','Add New'));
228        });
229
230        // Change the states field type in the organization panel
231        this.form.find('select[name="group[address][city]"]').parent('.form-input-holder').find('.form-field-button').on('click',function(){
232            me.formHelper.toggleFieldType( $(this).parent().find('select[name="group[address][city]"]') );
233            me.toggleText($(this),__d('contributions','Back'),__d('contributions','Add New'));
234        });
235
236        // Listen to `beforeFormHelperSubmit` event
237        this.formHelper.form.on('beforeFormHelperSubmit',function(event, formValues){
238            var cartSummaryValidationErrors = SS.com.Contributions.frontend.CartSummary.validate();
239            
240            SS.com.Contributions.frontend.CartSummary.clearValidationErrors(me.formHelper.validationErrors);
241            
242            $.extend(me.formHelper.validationErrors, cartSummaryValidationErrors);
243
244            if(!$.isEmptyObject(cartSummaryValidationErrors)){
245                return false;
246            } else {
247                me.validateCurrency(event);
248            }
249        });
250
251        // Transparent Redirect
252        this.formHelper.form.on('beforeFormHelperSubmit',function(event, formValues){
253
254            var currency = me.getCurrency();
255
256
257            if(!$.isEmptyObject(me.formHelper.validationErrors)){
258                return true;
259            }
260            
261            if(formValues['payment[id]']){
262                var paymentMethodHelper = me.form.find('select[name="payment[id]"]').data('helper');
263                var selectedMethod = "";
264                if(paymentMethodHelper){
265                    selectedMethod = paymentMethodHelper.getSelectedMethod();
266                }
267
268                try {
269                    hasTransparentRedirect = me.config.transparentRedirectOptions[SS.vars.Site.id][currency][selectedMethod]['enabled'];
270                }
271                catch(e) {
272                    console && console.error && console.error(e);
273                }
274                if (hasTransparentRedirect) {
275
276                    var transparentRedirectWrapper = me.getTransparentRedirectWrapper(SS.vars.Site.id, currency, selectedMethod);
277
278                    if (transparentRedirectWrapper.needsAuthorization && transparentRedirectWrapper.needsAuthorization(selectedMethod, formValues)) {
279                        transparentRedirectWrapper.submitForAuthorization(paymentMethodHelper.getSelectedRecord());
280                        return false;
281                    }
282                }
283
284                return true;
285            }
286
287            // todo: handle exceptions during checking phase
288            
289            var methodType = me.getMethodType();
290            if(!methodType){
291                return true;
292            }
293            
294            var hasTransparentRedirect = false;
295            try {
296                hasTransparentRedirect = me.config.transparentRedirectOptions[SS.vars.Site.id][currency][methodType]['enabled'];
297            }
298            catch(e) {
299                console && console.error && console.error(e);
300            }
301            if (hasTransparentRedirect) {
302                
303                var transparentRedirectWrapper = me.getTransparentRedirectWrapper(SS.vars.Site.id,currency,methodType);
304                try{
305                    if(transparentRedirectWrapper.needsTransparentRedirection(formValues)){
306                        transparentRedirectWrapper.submitTransparentRedirect(formValues);
307                        return false;
308                    }else{
309                        return true;
310                    }
311                }catch(e){
312
313                    console && console.error && console.error(e);
314
315                    new SS.customAlert({
316                        title: 'Payment Error',
317                        msg: 'Could not load payment wrapper',
318                        closeButton: true,
319                        buttons: {
320                            ok: {
321                                btnText: 'OK',
322                                btnCls: false,
323                                overCls: false
324                            },
325                            cancel: false
326                        }
327                    });
328                    return false;
329                }
330            }
331
332        });
333
334        // Listen to `resetFormHelper` event and recall this.init() when triggered
335        this.formHelper.form.on('resetFormHelper',function(){
336            me.init();
337        });
338
339        // Listen to `formHelperIsNotValid` event
340        this.formHelper.form.on('formHelperIsNotValid',function(event, validationErrors){
341            me.formIsNotValid(validationErrors);
342        });
343        
344        // Listen to `formHelperError` event
345        this.formHelper.form.on('formHelperError',function(event, responseData, textStatus, jqXHR, errorThrown){
346            if (me.config.captcha !== null) {
347                grecaptcha.reset(me.config.captcha);
348                
349                if(responseData.show_captcha){
350                    var captchaHolder = me.form.find('.captcha-holder');
351                    captchaHolder.removeClass('hidden-captcha');
352                    captchaHolder.show();
353                }
354            }
355
356            if(responseData.auth_code){
357                var currency = me.getCurrency();
358                var methodType = me.getMethodType();
359                var wrapper = me.getTransparentRedirectWrapper(SS.vars.Site.id,currency,methodType);
360                wrapper.handleMethodAction(responseData.auth_code);
361            }
362
363            if(responseData.policy_change){
364                var policyText = me.form.find('.privacy-policy .privacy-policy-text');
365                var policyHash = me.form.find('.privacy-policy #privacy-policy-hash');
366                var policyCheckbox = me.form.find('.privacy-policy #ContributionPolicy\\[aggree\\]');
367                policyText.html(responseData.policy_change.text);
368                policyHash.val(responseData.policy_change.hash);
369                policyCheckbox.prop('checked',false).trigger('syncChecked');
370            }
371        });
372
373        // Listen to `formHelperError` event
374        this.formHelper.form.on('formHelperSuccess',function(event, responseData, textStatus, jqXHR){
375            if( responseData.success ){
376                document.location.href = responseData.thanks_url;
377            }
378        });
379
380        this.form.find('.group-id-field').find('.form-input-holder').find('.form-field-button').on('click',function(){
381            me.toggleOrganizationInformation( $(this) );
382            me.toggleText($(this),__d('contributions','Cancel'),__d('contributions','Add New'));
383            return false;
384        });
385        
386        this.form.find('input[name="group[id_display]"]').on('comboLoadComplete',function(){
387            me.autoSelectGroup($(this));
388        });
389        
390        me.inputsChanged = false;
391        
392        setTimeout(function(){
393            me.form.find('input').on('change',function(){
394                me.inputsChanged = true;
395            });
396        },5000);
397        
398        this.form.on('submit',function(){
399            me.inputsChanged = false;
400        });
401
402        me.leave_page_warning = SS.com.Contributions.frontend.componentSettings.leave_page_warning?SS.com.Contributions.frontend.componentSettings.leave_page_warning:'You have not submitted your gift.';
403
404
405        me.requireReload = false;
406        $(window).on('requireReload',function(){
407            me.requireReload = true;
408        });
409
410        window.onbeforeunload = function(e){
411            if (me.inputsChanged && me.currentPaymentType != 'paypal' && me.currentPaymentType != 'applepay' && !me.requireReload) {
412                return me.leave_page_warning;
413            }
414        }
415
416        // Listener to add or remove "checked" class on all checkboxes in the form
417        this.form.find('input[type=checkbox]').on('change syncChecked',function(){
418            if(this.checked) {
419                $(this).parent().addClass('checked');
420            } else {
421                $(this).parent().removeClass('checked');
422            }
423        });
424        // Listener to add or remove "checked" class on all radios in the form
425        this.form.find('input[type=radio]').on('change syncChecked',function(){
426            if(this.checked) {
427                me.form.find('input[name="' + $(this).attr('name') + '"]').parent().removeClass('checked');
428                $(this).parent().addClass('checked');
429            }
430        });
431
432        // Add or remove "checked" class on all radios that are rendered as checked
433        this.form.find('input[type=radio],input[type=checkbox]').each(function(index, element) {
434            if(this.checked) {
435                $(this).parent().addClass('checked');
436            } else {
437                $(this).parent().removeClass('checked');
438            }
439        });
440
441        this.form.find('select[name="payment[check][checking_type]"]').on('change',function(){
442            var nameOnAccountField = me.form.find('input[name="payment[check][name_on_account]"]');
443            
444            if (nameOnAccountField.prop('placeholder')) {
445                nameOnAccountField.prop('placeholder',$(this).val() == 'Personal' ? 'Name on Account' : 'Company Name');
446            }
447            nameOnAccountField.parent().siblings('label').html($(this).val() == 'Personal' ? 'Name on Account' : 'Company Name');
448        });
449
450        if (this.config.cart.group) {
451
452            var groupCheckbox = this.form.find('input[name="group[yes]"]');
453            //$(groupCheckbox).attr('checked','checked');
454            me.toggleOrganizationSelector($(groupCheckbox));
455            
456            var groupAddButton = this.form.find('.group-id-field').find('.form-input-holder').find('.form-field-button');
457            me.toggleOrganizationInformation($(groupAddButton));
458            me.toggleText($(groupAddButton),__d('contributions','Cancel'),__d('contributions','Add New'));
459        }
460
461        this.form.find('input[name="currency"]').on('change',function(){
462            me.toggleCheckFields( $(this).val() );
463            me.toggleActiveMethodForm( $(this).val() );
464            me.toggleMethodCurrency($(this).val(),true);
465        });
466
467        me.toggleCheckFields(this.form.find('input[name="currency"]:checked').val());
468
469        var canMakeApplePayPayments = false;
470        try {
471            canMakeApplePayPayments = (window.ApplePaySession && ApplePaySession.canMakePayments());
472        }catch (e) {
473            console.warn(e);
474        }
475
476        if (canMakeApplePayPayments) {
477            this.form.find('.payment-type-field label[for="ContributionPayment[type]Applepay"]').show();
478            var applePayTransparentWrapper = me.getTransparentRedirectWrapper(SS.vars.Site.id,'USD','applepay');
479            if(applePayTransparentWrapper && applePayTransparentWrapper.initApplePay){
480                applePayTransparentWrapper.initApplePay('#apple-pay-button',this);
481            }else{
482                $('#apple-pay-button').on('click', function(){
483                    me.payWithApplePay();
484                });
485            }
486        } else {
487            this.form.find('.payment-type-field label[for="ContributionPayment[type]Applepay"]').hide();
488        }
489
490        this.initPayPal();
491
492        this.checkAndInitTransparentRedirect();
493        this.toggleMethodCurrency(me.getCurrency());
494        this.correctTransferFee();
495    },
496    correctTransferFee:function (){
497        var currentMethodType = this.getMethodType();
498
499        if(currentMethodType && !this.config.hasInitialMethod){
500            if(currentMethodType != 'card' && SS.com.Contributions.frontend.feeEnabled){
501                SS.com.Contributions.frontend.CartSummary.update(0)
502            }
503
504        }
505    },
506    getCurrency:function(){
507        var me = this;
508        var currency = 'USD';
509        me.config.currencies.forEach(function(item){
510            if (item.is_default) {
511                currency = item.code;
512            }
513        });
514
515        if(me.form.find('input[name="currency"]').length) {
516            currency = me.form.find('input[name="currency"]:checked').val();
517        }
518        return currency;
519    },
520    getCustomMethodForm:function(currency,methodType){
521        var me = this;
522        var hasTransparentRedirect = false;
523        try {
524            hasTransparentRedirect = me.config.transparentRedirectOptions[SS.vars.Site.id][currency][methodType]['enabled'];
525        }
526        catch(e) {
527            console && console.error && console.error(e);
528        }
529        
530        if(hasTransparentRedirect){
531            var transparentRedirectWrapper = me.getTransparentRedirectWrapper(SS.vars.Site.id, currency, methodType);
532            if(transparentRedirectWrapper){
533                var customMethodForm = this.form.find('.form-panel-custom-' + currency + '-' + methodType);
534                if(customMethodForm.length){
535                    return customMethodForm;
536                }
537            }
538        }
539        
540        return false;
541    },
542    checkAndInitTransparentRedirect:function(){
543        var me = this;
544        var currency = this.getCurrency();
545
546        var methodType = this.getMethodType()
547        if(!methodType){
548            this.removeTransparentLoading();
549            return true;
550        }
551
552        var hasTransparentRedirect = false;
553        try {
554            hasTransparentRedirect = me.config.transparentRedirectOptions[SS.vars.Site.id][currency][methodType]['enabled'];
555        }
556        catch(e) {
557            console && console.error && console.error(e);
558        }
559        if (hasTransparentRedirect) {
560            var transparentRedirectWrapper = me.getTransparentRedirectWrapper(SS.vars.Site.id, currency, methodType);
561            
562            if(transparentRedirectWrapper){
563                var methodHidden = !this.form.find('.form-panel-'+methodType).is(':visible');
564                
565                if(!methodHidden){
566                    var customMethodForm = this.getCustomMethodForm(currency,methodType)
567                    if(customMethodForm) {
568                        this.formHelper.hidePanel(this.form.find('.form-panel-'+methodType));
569                        this.formHelper.showPanel(customMethodForm);
570                    }
571                }
572            }else{
573                this.removeTransparentLoading();
574            }
575        }else{
576            this.removeTransparentLoading();
577        }
578    },
579    removeTransparentLoading:function(){
580        $('.contributions .forms').removeClass('transparent-loading');
581    },
582    getTransparentRedirectWrapper:function(siteId,currency,method){
583        var me = this;
584        var wrapperConfig = false;
585        var wrapper = false;
586        try{
587            wrapperConfig = me.config.transparentRedirectOptions[siteId][currency][method];
588        }catch(e){}
589        
590        if(wrapperConfig){
591            
592            if(!wrapperConfig['enabled']){
593                return false;
594            }
595            var wrapperKey = siteId+'.'+currency+'.'+method;
596            
597            if(!this.transparentRedirectWrapperInstances[wrapperKey]){
598                try{
599                    var wrapperClassName = 'SS.com.PaymentProcessors.frontend.transparent_redirect.'+wrapperConfig.gateway;
600                    
601                    var wrapperClass = eval(wrapperClassName);
602                    var wrapperData = {
603                        paymentForm:me,
604                        siteId:siteId,
605                        currency:currency,
606                        method:method
607                    };
608                    
609                    wrapperData = $.extend(wrapperData,wrapperConfig);
610                    
611                    wrapper = new wrapperClass(wrapperData);
612                }catch(e){
613                    return false;
614                }
615                this.transparentRedirectWrapperInstances[wrapperKey] = wrapper;
616            }else{
617                return this.transparentRedirectWrapperInstances[wrapperKey];
618            }
619        }
620        
621        
622        return wrapper;
623        
624    },
625    
626    
627    /**
628     * Inform the user that the form has errors and scroll the page to the first error found
629     * @param errors
630     */
631    formIsNotValid: function( errors )
632    {
633        
634        if ($.isEmptyObject(errors)) {
635            return;
636        }
637        
638        var me = this;
639        var alertConfig = {
640            title: __d('contributions','Invalid checkout form'),
641            msg: __d('contributions','The checkout form contains errors. Please fix the fields marked in red.'),
642            closeButton: true,
643            targetLocation: this.form.find('input[type="submit"]'),
644            buttons: {
645                ok: {
646                    btnText: 'OK',
647                    btnCls: false,
648                    overCls: false
649                },
650                cancel: false
651            },
652            callback: function(){
653                for( var i in errors ){
654                    break;
655                }
656
657                var el = me.form.closest('body').find('input[name="'+i+'"], select[name="'+i+'"], textarea[name="'+i+'"]');
658
659                if(!me.inIframe()){
660                    $('html, body').animate({
661                        scrollTop: el.offset().top - 100
662                    }, 200);
663                }else{
664                    me.scrollIntoView(el[0]);
665                }
666            }
667        };
668        this.lastAlert = new SS.customAlert(alertConfig);
669    },
670
671    scrollIntoView:function(el){
672        if(this.requiresIframeScrollFix()){
673            window.parent.postMessage( JSON.stringify( {action: 'scrollTo', value: el.getBoundingClientRect().top} ), '*' );
674        }else{
675            el.scrollIntoView({
676                behavior: "smooth",
677                block: "start"
678            });
679        }
680    },
681    requiresIframeScrollFix:function() {
682        try {
683            // Check for safari
684            var is_safari = navigator.userAgent.indexOf("Safari") > -1;
685            // Chrome has Safari in the user agent so we need to filter (https://stackoverflow.com/a/7768006/1502448)
686            var is_chrome = navigator.userAgent.indexOf('Chrome') > -1;
687            if ((is_chrome) && (is_safari)) { is_safari = false; }
688
689            // If we need to narrow this down even further we can use a more robust browser detection (https://stackoverflow.com/questions/5916900)
690            // Problematic browsers can be adjusted here.
691            if (is_safari && (
692                navigator.userAgent.match(/iPad/i) ||
693                navigator.userAgent.match(/iPhone/i)
694            ))
695                return true;
696            else
697                return false;
698
699        } catch (e) {
700            alert(e.message);
701        }
702    },
703    inIframe:function(){
704        try {
705            return window.self !== window.top;
706        } catch (e) {
707            return true;
708        }
709    },
710
711    validateCurrency: function(event) {
712
713        var me = this;
714
715        if(me.form.find('input[name="currency"]').length) {
716            if(!me.form.find('input[name="currency"]:checked').val()) {
717
718                event.preventDefault();
719
720                var alertConfig = {
721                    title: __d('contributions','Invalid checkout form'),
722                    msg: __d('contributions','Please choose a currency.'),
723                    closeButton: true,
724                    buttons: {
725                        ok: {
726                            btnText: 'OK',
727                            btnCls: false,
728                            overCls: false
729                        },
730                        cancel: false
731                    }
732                };
733                new SS.customAlert(alertConfig);
734            }
735        }
736    },
737
738    /**
739     * Toggle text on element 
740     * @param element
741     * @param text1
742     * @param text2
743     */
744    toggleText: function( element, text1, text2 )
745    {
746        if( element.html() == text1 ){
747            element.html(text2);
748        }else{
749            element.html(text1);
750        }
751    },
752
753    /**
754     * Toggle payment type panel visibility
755     * @param trigger
756     */
757    togglePaymentType: function(  )
758    {
759        var me = this;
760        var type = this.getMethodType();
761        var currency = this.getCurrency();
762        var captchaHolder = this.form.find('.captcha-holder');
763        
764        this.formHelper.hidePanel( this.form.find('.form-panel-card') );
765        this.formHelper.hidePanel( this.form.find('.form-panel-check') );
766        this.formHelper.hidePanel( this.form.find('.form-panel-custom-method') );
767        
768        if(!captchaHolder.hasClass('hidden-captcha')){
769            captchaHolder.show();
770        }
771        
772        var startDateDisplay = $('input[name="recurring[start_date_display]"]');
773        
774
775        if (type=='paypal') {
776            this.formHelper.hidePanel( this.form.find('.form-panel-existing-cards') );
777            this.formHelper.hidePanel( me.form.find('.billing-information') );
778            // this.formHelper.hidePanel( me.form.find('.organization-information') );
779            $('.form-actions').hide();
780            $('.form-panel-paypal').show();
781            $('.form-panel-applepay').hide();
782            this.form.find('.captcha-holder').hide();
783            startDateDisplay.data('originalDate', startDateDisplay.datepicker('getDate'));
784            startDateDisplay.datepicker("setDate", new Date());
785            startDateDisplay.datepicker("option", 'disabled', true);
786        } else if (type=='applepay') {
787            this.formHelper.hidePanel( this.form.find('.form-panel-existing-cards') );
788            this.formHelper.hidePanel( me.form.find('.billing-information') );
789            // this.formHelper.hidePanel( me.form.find('.organization-information') );
790            $('.form-actions').hide();
791            $('.form-panel-applepay').show();
792            $('.form-panel-paypal').hide();
793        } else {
794            this.formHelper.showPanel( this.form.find('.form-panel-existing-cards') );
795            this.formHelper.showPanel( me.form.find('.billing-information'), false );
796            // this.formHelper.showPanel( me.form.find('.organization-information') );
797            $('.form-actions').show();
798            $('.form-panel-paypal').hide();
799            $('.form-panel-applepay').hide();
800            
801            var customMethodForm = this.getCustomMethodForm(currency,type)
802            if(customMethodForm){
803                this.formHelper.showPanel( customMethodForm );
804            }else{
805                this.formHelper.showPanel( this.form.find('.simple-form.form-panel-' + type) );
806            }
807        }
808        
809        if(type != 'paypal'){
810            var originalDate = startDateDisplay.data("originalDate")?startDateDisplay.data("originalDate"):null;
811            startDateDisplay.datepicker('setDate', originalDate);
812        }
813        
814        if(this.currentPaymentType == 'paypal'){ // previous method was paypal
815            var recurring = this.form.find('input[name="is_recurring"]').prop('checked');
816            if(recurring){
817                startDateDisplay.datepicker("option", 'disabled', false);
818            }
819        }
820
821        this.currentPaymentType = type;
822        me.toggleTransferFee();
823        me.toggleStoreOption();
824    },
825
826    /**
827     * Toggle organization information panel visibility
828     * @param trigger
829     */
830    toggleOrganizationInformation: function( trigger )
831    {
832        var panel = this.form.find('.form-panel-organization' );
833        if( !panel.is(':visible') ){
834            trigger.parent('.form-input-holder').find('input,select,textarea').val('').attr('disabled','disabled').removeClass('has-error').closest('label').addClass('disabled');
835            this.formHelper.showPanel( this.form.find('.form-panel-organization' ) );
836        }else{
837            trigger.parent('.form-input-holder').find('input,select,textarea').removeAttr('disabled').closest('label').removeClass('disabled');
838            this.formHelper.hidePanel( this.form.find('.form-panel-organization' ) );
839        }
840    },
841
842    /**
843     * Toggle organization selector panel visibility
844     * @param trigger
845     */
846    toggleOrganizationSelector: function( trigger )
847    {
848        var organizationPanelSelector = this.form.find('.form-panel-organization-selector' );
849        var organizationPanel = this.form.find('.form-panel-organization' );
850        var alternateOrganizationPanel = this.form.find('.form-panel-alternate-organization');
851        var alternateField = this.form.find('input[name="alternate_organization"]:checked');
852
853        var directForm = $(trigger).data('direct-form');
854
855        if(directForm) {
856            if (trigger.is(':checked')) {
857                this.formHelper.showPanel(organizationPanel);
858                this.formHelper.hidePanel(alternateOrganizationPanel);
859                alternateField.prop('checked', false).trigger('syncChecked');
860            }else{
861                this.formHelper.hidePanel(organizationPanel);
862            }
863        }else {
864            if (trigger.is(':checked')) {
865                this.formHelper.showPanel(organizationPanelSelector);
866                this.formHelper.hidePanel(alternateOrganizationPanel);
867                alternateField.prop('checked', false).trigger('syncChecked');
868            } else {
869                this.formHelper.hidePanel(organizationPanelSelector);
870                if (organizationPanel.is(':visible')) {
871                    this.formHelper.hidePanel(organizationPanel);
872                    this.toggleText(organizationPanelSelector.find('.form-field-button'), __d('contributions','Cancel'), __d('contributions','Add New'));
873                }
874            }
875        }
876    },
877    toggleAlternateOrganizationSelector: function(trigger) {
878        var alternateOrganizationPanel = this.form.find('.form-panel-alternate-organization');
879        var organizationPanelSelector = this.form.find('.form-panel-organization-selector' );
880        var organizationPanel = this.form.find('.form-panel-organization');
881        var organizationField = this.form.find('input[name="group[yes]"]:checked')
882
883        if (trigger.is(':checked')) {
884            this.formHelper.showPanel(alternateOrganizationPanel);
885            this.formHelper.hidePanel(organizationPanelSelector);
886            this.formHelper.hidePanel(organizationPanel);
887            organizationField.prop('checked', false).trigger('syncChecked');
888        }else{
889            this.formHelper.hidePanel(alternateOrganizationPanel);
890        }
891    },
892
893    /**
894     * Toggle recurring options panel visibility
895     * @param trigger
896     */
897    toggleRecurringOptions: function( trigger , updateCart)
898    {
899        var me = this;
900        var recurringType = this.form.find('input[name="recurring[type]"]:checked');
901        if (!recurringType) {
902            recurringType = this.form.find('input[name="recurring[type]"]')
903        }
904
905        if(recurringType){
906            recurringType = recurringType.val();
907        }
908
909        if(!trigger || trigger.is(':checked')){
910            this.formHelper.showPanel( this.form.find('.form-panel-recurring') );
911            this.formHelper.showPanel( this.form.find('.form-panel-register') );
912//			if( !this.form.find('input[name="register"]').is(':checked') ){
913//				this.form.find('input[name="register"]').click();
914//				this.form.find('input[name="register"]').attr('disabled',true);
915//			}
916            if(me.config.disableCheckForRecurring){
917                $('input[name="payment[type]"][value="check"]').attr('disabled','disabled').closest('label').addClass('disabled');
918                $('input[name="payment[type]"][value="card"]').trigger('click');
919            }
920
921            if (this.currentPaymentType == 'paypal') {
922                $('input[name="recurring[start_date_display]"]').datepicker("setDate", new Date());
923                $('input[name="recurring[start_date_display]"]').datepicker("option", 'disabled', true);
924            } else {
925                $('input[name="recurring[start_date_display]"]').datepicker("option", 'disabled', false);
926            }
927
928            if (updateCart) {
929                SS.com.Contributions.frontend.CartSummary.addRecurringToCart(true, recurringType);
930            }
931
932            this.formHelper.hidePanel( this.form.find('.form-panel-store-preference') );
933
934        }else{
935            this.formHelper.hidePanel( this.form.find('.form-panel-recurring') );
936            this.formHelper.hidePanel( this.form.find('.form-panel-register') );
937//			this.form.find('input[name="register"]').removeAttr('disabled');
938            if(me.config.disableCheckForRecurring){
939                $('input[name="payment[type]"][value="check"]').removeAttr('disabled').closest('label').removeClass('disabled');
940            }
941
942            if (updateCart) {
943                SS.com.Contributions.frontend.CartSummary.addRecurringToCart(false, recurringType);
944            }
945        }
946        
947        me.toggleStoreOption();
948    },
949    
950    
951    toggleStoreOption:function(){
952        var me = this;
953        var panel = this.form.find('.form-panel-store-preference');
954        if(panel){
955            var recurring = this.form.find('input[name="is_recurring"]').prop('checked');
956            
957            var card = me.form.find('.form-panel-card');
958            var check = me.form.find('.form-panel-check');
959            var customCard = me.form.find('.form-panel-custom-card');
960            var customCheck = me.form.find('.form-panel-custom-check');
961            
962            var cardVisible = typeof card.data('isHidden') == 'boolean'? !card.data('isHidden'):(card.is(':visible'));
963            var checkVisible = typeof check.data('isHidden') == 'boolean'? !check.data('isHidden'):(check.is(':visible'));
964            var customCardVisible = typeof customCard.data('isHidden') == 'boolean'? !customCard.data('isHidden'):(customCard.is(':visible'));
965            var customCheckVisible = typeof customCheck.data('isHidden') == 'boolean'? !customCheck.data('isHidden'):(customCheck.is(':visible'));
966            
967            
968            var hasAddNewMethod = cardVisible || checkVisible || customCardVisible || customCheckVisible;
969
970            if(recurring){
971                this.formHelper.hidePanel( this.form.find('.form-panel-store-preference') )
972            }else if(hasAddNewMethod){
973                this.formHelper.showPanel( this.form.find('.form-panel-store-preference') )
974            }else{
975                this.formHelper.hidePanel( this.form.find('.form-panel-store-preference') )
976            }
977        }
978    },
979
980    /**
981     * Toggle tribute options panel visibility
982     * @param trigger
983     */
984    toggleTributeOptions: function( trigger )
985    {
986        var me = this;
987        if(!trigger || trigger.is(':checked')){
988            this.formHelper.showPanel( this.form.find('.form-panel-tribute-options') );
989        }else{
990            this.formHelper.hidePanel( this.form.find('.form-panel-tribute-options') );
991            this.formHelper.hidePanel( this.form.find('.form-panel-tribute-recipient') );
992            var hasRecipient = this.form.find('.tribute-has-recipient-field');
993            if(hasRecipient.length){
994                hasRecipient.find('.form-input-label').removeClass('checked');
995                hasRecipient.find('input[name="tribute[has_recipient]"]:checked').attr('checked', false);
996            }
997        }
998    },
999
1000    /**
1001     * Toggle tribute recipient panel visibility
1002     * @param trigger
1003     */
1004    toggleTributeRecipient: function( trigger )
1005    {
1006        var me = this;
1007        if(!trigger || trigger.is(':checked')){
1008            this.formHelper.showPanel( this.form.find('.form-panel-tribute-recipient') );
1009        }else{
1010            this.formHelper.hidePanel( this.form.find('.form-panel-tribute-recipient') );
1011        }
1012    },
1013
1014    /**
1015     * Toggle register options panel visibility
1016     * @param trigger
1017     */
1018//	toggleRegisterOptions: function( trigger )
1019//	{
1020//		if(trigger.is(':checked')){
1021//			this.formHelper.showPanel( this.form.find('.form-panel-' + trigger.val()) );
1022//		}else{
1023//			this.formHelper.hidePanel( this.form.find('.form-panel-' + trigger.val()) );
1024//		}
1025//	},
1026
1027    /**
1028     * Toggle add card panel visibility
1029     * @param trigger
1030     */
1031    toggleAddCardOrCheck: function( trigger )
1032    {
1033        var me = this;
1034        var state = trigger.html();
1035        var currency = this.getCurrency();
1036        var selectedType = this.form.find('.form-panel-payment-type input:checked').val();
1037        if(!selectedType) {
1038            selectedType = 'card';
1039        }
1040        var visiblePanel = this.form.find('.simple-form.form-panel-' + selectedType);
1041        var customMethodForm = this.getCustomMethodForm(currency,selectedType)
1042        if(customMethodForm) {
1043            visiblePanel = customMethodForm;
1044        }
1045        var hasAddressCombo = me.form.find('.form-panel-saved-addresses').length;
1046        if( !visiblePanel.is(':visible') ){
1047            trigger.parent('.form-input-holder').find('input,select,textarea').val('');
1048            this.formHelper.allowBlank( trigger.parent('.form-input-holder').find('input,select,textarea'), true );
1049            this.formHelper.showPanel( visiblePanel );
1050            this.formHelper.showPanel( me.form.find('.form-panel-payment-type') );
1051            this.formHelper.showPanel( me.form.find('.billing-information'), !hasAddressCombo);
1052            this.formHelper.showPanel( me.form.find('.form-panel-saved-addresses') );
1053            this.formHelper.clearValidationErrors( trigger.parent('.form-input-holder').find('input,select,textarea') );
1054        }else{
1055            this.formHelper.hidePanel( visiblePanel );
1056            this.formHelper.hidePanel( me.form.find('.form-panel-payment-type') );
1057            this.formHelper.hidePanel( me.form.find('.billing-information') );
1058            this.formHelper.allowBlank( trigger.parent('.form-input-holder').find('input,select,textarea'), false );
1059            this.formHelper.clearValidationErrors( trigger.parent('.form-input-holder').find('input,select,textarea') );
1060        }
1061        me.toggleTransferFee();
1062        me.toggleStoreOption();
1063    },
1064
1065    /**
1066     * Toggle add address panel visibility
1067     * @param trigger
1068     */
1069    toggleAddAddress: function( trigger )
1070    {
1071        var me = this;
1072        var addAddressPanel = this.form.find('.form-panel-address');
1073        if( !addAddressPanel.is(':visible') ){
1074            trigger.parent('.form-input-holder').find('input,select,textarea').val('');
1075            this.formHelper.allowBlank( trigger.parent('.form-input-holder').find('input,select,textarea'), true );
1076            this.formHelper.showPanel( addAddressPanel );
1077            this.formHelper.clearValidationErrors( trigger.parent('.form-input-holder').find('input,select,textarea') );
1078        }else{
1079            this.formHelper.hidePanel( addAddressPanel );
1080            this.formHelper.allowBlank( trigger.parent('.form-input-holder').find('input,select,textarea'), false );
1081            this.formHelper.clearValidationErrors( trigger.parent('.form-input-holder').find('input,select,textarea') );
1082        }
1083    },
1084
1085    autoSelectGroup: function(displayField)
1086    {
1087        var me = this;
1088        var combo = displayField.closest('.form-input-wrapper');
1089        var valueField = combo.find('input[type="hidden"]');
1090        var value = this.formHelper.findValueByFieldName(valueField);
1091        if(value){
1092            var comboOptions = combo.find('.option');
1093            var comboValues = [];
1094            $.each(comboOptions,function(index, option){
1095                comboValues[$(option).data('value')] = $(option).html();
1096            });
1097            displayField.val(comboValues[value]);
1098            valueField.val(value);
1099        }
1100    },
1101    
1102    padNumber: function(num, size)
1103    {
1104        var s = num+"";
1105        while (s.length < size) s = "0" + s;
1106        return s;
1107    },
1108
1109    toggleTransferFee: function()
1110    {
1111        var me = this;
1112        var transferFeePanel = this.form.find('.form-panel-transfer-fee');
1113        var isCard = false;
1114        var paymentMethodHelper = this.form.find('select[name="payment[id]"]').data('helper');
1115        var selectedMethod = "";
1116        if(!isCard && paymentMethodHelper){
1117            selectedMethod = paymentMethodHelper.getSelectedMethod();
1118            if(selectedMethod){
1119                isCard = selectedMethod=='card';
1120            }
1121        }
1122
1123        if(!isCard && !selectedMethod){
1124            isCard = !me.form.find('.form-panel-card').data('isHidden');
1125        }
1126
1127        if(!isCard && me.currentPaymentType){
1128            isCard = me.currentPaymentType == 'card'
1129        }
1130        
1131        if(!isCard && !selectedMethod){
1132            isCard = this.getMethodType()=='card';
1133        }
1134
1135        if(isCard){
1136            me.formHelper.showPanel( transferFeePanel );
1137        }else{
1138            me.formHelper.hidePanel( transferFeePanel );
1139        }
1140
1141        if( isCard ){
1142            var transferChecked = transferFeePanel.find('input[name="transfer_fee"]:checked').val();
1143            SS.com.Contributions.frontend.CartSummary.update(transferChecked);
1144        }else{
1145            SS.com.Contributions.frontend.CartSummary.update(0);
1146        }
1147    },
1148    toggleCheckFields:function( currency ){
1149
1150        var me = this;
1151
1152        var checkForm = this.form.find('.form-panel-check');
1153        if(!currency){
1154            currency = this.config.currencies?this.config.currencies[0]['code']:'USD';
1155        }
1156
1157        if(currency=='CAD'){
1158            checkForm.addClass('form-panel-check-cad');
1159            me.formHelper.showField( checkForm.find('.payment-check-bank-number-field') )
1160            me.formHelper.showField( checkForm.find('.payment-check-transit-number-field') )
1161            me.formHelper.hideField( checkForm.find('.payment-check-routing-number-field') )
1162        }else{
1163            checkForm.removeClass('form-panel-check-cad');
1164            me.formHelper.showField( checkForm.find('.payment-check-routing-number-field') )
1165            me.formHelper.hideField( checkForm.find('.payment-check-bank-number-field') )
1166            me.formHelper.hideField( checkForm.find('.payment-check-transit-number-field') )
1167        }
1168    },
1169    getMethodType:function(){
1170        var me = this;
1171        var paymentType = me.form.find('input[name="payment[type]"]');
1172        
1173        if(paymentType.is(':radio')){
1174            return me.form.find('input[name="payment[type]"]:checked').val()
1175        }else{
1176            return paymentType.val();
1177        }
1178    },
1179    restrictCurrencyOptions(currency){
1180        var methodtypes = this.config.paymentTypeByCurrencyOptions[currency];
1181        var paymentType = this.form.find('input[name="payment[type]"]');
1182        var changed = false;
1183        var validElement = null;
1184
1185        if(paymentType.is(':radio')){
1186            paymentType.each(function(index,element){
1187                var el = $(element);
1188                var type = el.val();
1189                if(methodtypes.indexOf(type)==-1){
1190                    el.attr('disabled','disabled').data('force-disable',1);
1191                    el.closest('label').addClass('disabled');
1192                    if(el.is(':checked')){
1193                        changed = true;
1194                        el.prop('checked',false).trigger('syncChecked');
1195
1196                    }
1197                }else{
1198                    if(!validElement){
1199                        validElement = el;
1200                    }
1201                    el.data('force-disable',null).removeAttr('disabled');
1202                    el.closest('label').removeClass('disabled');
1203                }
1204            })
1205        }
1206
1207        if(changed){
1208            validElement.prop('checked',true).trigger('syncChecked');
1209            return true;
1210        }
1211
1212        return false;
1213    },
1214    toggleActiveMethodForm:function( currency ){
1215        var me = this;
1216
1217        if(!currency){
1218            currency = 'USD';
1219        }
1220
1221        var changedMethod = this.restrictCurrencyOptions(currency);
1222        if(changedMethod){
1223            var paymentTypePanel = this.form.find('.form-panel-payment-type');
1224            if(paymentTypePanel.is(':visible')){
1225                this.togglePaymentType();
1226
1227                return true;
1228            }
1229
1230        }
1231
1232        var methodType = this.getMethodType();
1233
1234        var customMethodForm = this.getCustomMethodForm(currency,methodType);
1235        var currentMethodForm = this.form.find('.simple-form.form-panel-' + methodType);
1236        if(customMethodForm) {
1237            currentMethodForm = customMethodForm;
1238        }
1239
1240        var anyForm = this.form.find('.form-panel-'+methodType);
1241        if(currentMethodForm && currentMethodForm.is(':visible')){
1242            return
1243        }if(anyForm.is(':visible') && !currentMethodForm.is(':visible')){
1244            this.formHelper.hidePanel(anyForm);
1245            this.formHelper.showPanel(currentMethodForm);
1246        }
1247    },
1248    toggleMethodCurrency: function(currency, onEvent){
1249        var methodHelper = this.form.find('select[name="payment[id]"]').data('helper');
1250
1251        if(methodHelper){
1252            methodHelper.setCurrency(currency);
1253        }
1254
1255        if(onEvent){
1256            this.updateCart();
1257        }
1258    },
1259    updateCart:function (){
1260        SS.com.Contributions.frontend.CartSummary.updateContent();
1261    },
1262    payWithApplePay: function() {
1263        var me = this;
1264        var runningTotal = SS.com.Contributions.frontend.total;
1265
1266        var paymentRequest = {
1267            currencyCode: 'USD',
1268            countryCode: 'US',
1269            requiredShippingContactFields: ['email', 'phone'],
1270            requiredBillingContactFields: ['postalAddress', 'name'],
1271            total: {
1272                label: 'Contributions',
1273                amount: runningTotal
1274            },
1275            supportedNetworks: ['amex', 'masterCard', 'visa', 'discover'],
1276            merchantCapabilities: ['supports3DS', 'supportsEMV', 'supportsCredit', 'supportsDebit']
1277        };
1278
1279        var session = new ApplePaySession(2, paymentRequest);
1280
1281        // Merchant Validation
1282        session.onvalidatemerchant = function (event) {
1283            var promise = performValidation(event.validationURL);
1284            promise.then(function (merchantSession) {
1285                session.completeMerchantValidation(merchantSession);
1286            });
1287        }
1288
1289
1290        function performValidation(valURL) {
1291            return new Promise(function(resolve, reject) {
1292                var xhr = new XMLHttpRequest();
1293                xhr.onload = function() {
1294                    var data = JSON.parse(this.responseText);
1295                    console.log(data);
1296                    resolve(data);
1297                };
1298                xhr.onerror = reject;
1299                xhr.open('GET', SS.route('PaymentProcessors','PaymentProcessorApplePay','validateMerchant',null,null)+'?url=' + valURL);
1300                xhr.send();
1301            });
1302        }
1303
1304        session.onpaymentauthorized = function (event) {
1305
1306            console.log('starting session.onpaymentauthorized');
1307            console.log(event);
1308            console.log(JSON.stringify(event.payment.token.paymentData));
1309
1310            var promise = sendPaymentToken(event.payment);
1311            promise.then(function (success) {
1312                var status;
1313                if (success){
1314                    status = ApplePaySession.STATUS_SUCCESS;
1315                } else {
1316                    status = ApplePaySession.STATUS_FAILURE;
1317                }
1318
1319                console.log( "result of sendPaymentToken() function =  " + success );
1320                session.completePayment(status);
1321            });
1322        }
1323
1324        function sendPaymentToken(paymentData) {
1325            return new Promise(function(resolve, reject) {
1326                // console.log('starting function sendPaymentToken()');
1327                // console.log(paymentToken);
1328                // console.log(JSON.stringify(paymentToken.paymentData));
1329                //
1330                // console.log("this is where you would pass the payment token to your third-party payment provider to use the token to charge the card. Only if your provider tells you the payment was successful should you return a resolve(true) here. Otherwise reject;");
1331                // console.log("defaulting to resolve(true) here, just to show what a successfully completed transaction flow looks like");
1332
1333
1334                me.form.one('beforeFormHelperSubmit',function(event, formValues) {
1335                    formValues['payment[apple_pay_data]'] = encodeURIComponent(JSON.stringify(paymentData));
1336                    return true;
1337                });
1338
1339                me.form.one('formHelperError',function(event, responseData, textStatus, jqXHR, errorThrown){
1340                    resolve(false);
1341                });
1342
1343                me.form.one('formHelperSuccess',function(event, responseData, textStatus, jqXHR){
1344                    resolve(true);
1345                });
1346
1347                console.log(me.form);
1348
1349                me.form.submit();
1350
1351            });
1352        }
1353
1354        session.oncancel = function(event) {
1355            console.log('starting session.cancel');
1356            console.log(event);
1357        }
1358
1359        session.begin();
1360    },
1361
1362    initPayPal: function() {
1363        var me = this;
1364        $('form[name="PayPal"]').on('submit', function (event) {
1365
1366            $('form[name="PayPal"] input[name="amount"]').val(SS.com.Contributions.frontend.total);
1367            $('form[name="PayPal"] input[name="custom"]').val(SS.com.Contributions.frontend.cartId);
1368
1369            me.formHelper.validatePanel();
1370            if (!$.isEmptyObject(me.formHelper.validationErrors)) {
1371                event.preventDefault();
1372                return false;
1373            } else {
1374                var values = me.formHelper.getAllFieldValues();
1375
1376                if (values['recurring[type]']) {
1377
1378                    $('input[name="recurring[start_date_display]"]').datepicker("setDate", new Date());
1379
1380                    values = me.formHelper.getAllFieldValues();
1381
1382                    var recurringType = values['recurring[type]'].split('-');
1383                    var recurringInterval = parseInt(recurringType[0]);
1384                    var recurringPeriod = recurringType[1];
1385                    var recurringPeriodShort = recurringPeriod.charAt(0).toUpperCase();
1386
1387
1388                    var srt = 52;
1389                    if (values['recurring[end_date]']) {
1390                        var startDate = $.datepicker.parseDate("yy-mm-dd", values['recurring[start_date]']);
1391                        var endDate = $.datepicker.parseDate("yy-mm-dd", values['recurring[end_date]']);
1392                        var timeDiff = Math.abs(endDate.getTime() - startDate.getTime());
1393                        var multiplier;
1394                        switch (recurringPeriodShort) {
1395                            case 'D':
1396                                multiplier = 1000 * 3600 * 24;
1397                                break;
1398                            case 'W':
1399                                multiplier = 1000 * 3600 * 24 * 7;
1400                                break;
1401                            case 'M':
1402                                multiplier = 1000 * 3600 * 24 * 31;
1403                                break;
1404                            case 'Y':
1405                                multiplier = 1000 * 3600 * 24 * 365;
1406                                break;
1407                        }
1408                        multiplier *= recurringInterval;
1409                        srt = Math.ceil(timeDiff / multiplier);
1410
1411                    }
1412                    srt = Math.max(srt, 2);
1413                    srt = Math.min(srt, 52);
1414
1415                    // console.log(startDate, endDate, srt);
1416
1417                    $('form[name="PayPal"] input[name="cmd"]').val('_xclick-subscriptions');
1418                    $('form[name="PayPal"] input[name="a3"]').val(SS.com.Contributions.frontend.total);
1419                    $('form[name="PayPal"] input[name="p3"]').val(recurringInterval);
1420                    $('form[name="PayPal"] input[name="t3"]').val(recurringPeriodShort);
1421                    $('form[name="PayPal"] input[name="src"]').val(1);
1422                    $('form[name="PayPal"] input[name="srt"]').val(srt);
1423
1424                } else {
1425
1426                    $('form[name="PayPal"] input[name="cmd"]').val('_xclick');
1427                    $('form[name="PayPal"] input[name="a3"]').val('');
1428                    $('form[name="PayPal"] input[name="p3"]').val('');
1429                    $('form[name="PayPal"] input[name="t3"]').val('');
1430                    $('form[name="PayPal"] input[name="src"]').val('');
1431                    $('form[name="PayPal"] input[name="srt"]').val('');
1432                }
1433
1434                // console.log(values);
1435                // return false;
1436
1437                $.ajax({
1438                    url: SS.route('Contributions','Contributions','setGroupToCart',null),
1439                    type: 'POST',
1440                    data: values,
1441                    dataType : 'json',
1442                    abortOnRetry: false
1443                });
1444            }
1445        });
1446    },
1447    getAddressInformation:function(){
1448        var addAddressPanel = this.form.find('.form-panel-address');
1449        var selectedAddressRecord = null;
1450        if( !addAddressPanel.is(':visible') ){
1451            var addressField = this.form.find('select[name="address[id]"]');
1452            selectedAddressRecord =  addressField.data('helper').getSelectedRecord();
1453            if(selectedAddressRecord){
1454                return {
1455                    address1:selectedAddressRecord['PersonAddress']['address1'],
1456                    address2:selectedAddressRecord['PersonAddress']['address2'],
1457                    city:selectedAddressRecord['PersonAddress']['city'],
1458                    zip:selectedAddressRecord['PersonAddress']['zip'],
1459                    stateCode:selectedAddressRecord['LocationState']['code'],
1460                    state:selectedAddressRecord['LocationState']['name'],
1461                    countryCode:selectedAddressRecord['LocationCountry']['code'],
1462                    country:selectedAddressRecord['LocationCountry']['name']
1463                }
1464            }
1465        }else{
1466            var address1Field = addAddressPanel.find('[name="address[address1]"]');
1467            var address2Field = addAddressPanel.find('[name="address[address2]"]');
1468            var cityField = addAddressPanel.find('[name="address[city]"]');
1469            var zipField = addAddressPanel.find('[name="address[zip]"]');
1470            var stateField = addAddressPanel.find('[name="address[location_state_id_display]"]');
1471            var countryField = addAddressPanel.find('[name="address[location_country_id_display]"]');
1472            
1473            if(!stateField.length){
1474                stateField=addAddressPanel.find('select[name="address[location_state_id]"]')
1475            }
1476            
1477            if(!countryField.length){
1478                countryField=addAddressPanel.find('select[name="address[location_country_id]"]')
1479            }
1480            
1481            var stateRecord = stateField.data('helper').getSelectedRecord();
1482            var countryRecord = countryField.data('helper').getSelectedRecord();
1483            
1484            var state = stateRecord?stateRecord:{};
1485            var country = countryRecord?countryRecord:{};
1486            
1487            if(stateRecord.LocationState){
1488                state = stateRecord.LocationState;
1489            }
1490            
1491            if(countryRecord.LocationCountry){
1492                country = countryRecord.LocationCountry;
1493            }
1494            
1495            return{
1496                address1:address1Field.val(),
1497                address2:address2Field.val(),
1498                city:cityField.val(),
1499                zip:zipField.val(),
1500                stateCode:state.code,
1501                state:state.name,
1502                countryCode:country.code,
1503                country:country.name
1504            }
1505        }
1506        return false;
1507    },
1508    getRecurringTypeInformation:function(){
1509        var values = this.formHelper.getAllFieldValues();
1510
1511        if (values['recurring[type]']) {
1512            var recurringType = values['recurring[type]'];
1513            if(this.config.recurringTypeOptions && this.config.recurringTypeOptions[recurringType]){
1514                return this.config.recurringTypeOptions[recurringType]
1515            }
1516        }
1517        return false;
1518    }
1519};

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.