PluginProbe ʕ •ᴥ•ʔ
GiveWP – Donation Plugin and Fundraising Platform / 2.7.3
GiveWP – Donation Plugin and Fundraising Platform v2.7.3
4.16.3 4.16.2 4.16.1 4.16.0 4.15.5 4.15.4 4.15.3 4.15.2 4.15.1 4.15.0 2.3.0 2.3.1 2.3.2 2.30.0 2.31.0 2.31.1 2.32.0 2.33.0 2.33.1 2.33.2 2.33.3 2.33.4 2.33.5 2.4.0 2.4.1 2.4.2 2.4.3 2.4.4 2.4.5 2.4.6 2.4.7 2.5.0 2.5.1 2.5.10 2.5.11 2.5.12 2.5.13 2.5.2 2.5.3 2.5.4 2.5.5 2.5.6 2.5.7 2.5.8 2.5.9 2.6.0 2.6.1 2.6.2 2.6.3 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.8.0 2.8.1 2.9.0 2.9.1 2.9.2 2.9.3 2.9.4 2.9.5 2.9.6 2.9.7 3.0.0 3.0.1 3.0.2 3.0.3 3.0.4 3.1.0 3.1.1 3.1.2 3.10.0 3.11.0 3.12.0 3.12.1 3.12.2 3.12.3 3.13.0 3.14.0 3.14.1 3.14.2 3.15.0 3.15.1 3.16.0 3.16.1 3.16.2 3.16.3 3.16.4 3.16.5 3.17.0 3.17.1 3.17.2 3.18.0 3.19.0 3.19.1 3.19.2 3.19.3 3.19.4 3.2.0 3.2.1 3.2.2 3.20.0 3.21.0 3.21.1 3.22.0 3.22.1 3.22.2 3.3.0 3.3.1 3.4.0 3.4.1 3.4.2 3.5.0 3.5.1 3.6.0 3.6.1 3.6.2 3.7.0 3.8.0 3.9.0 4.0.0 4.1.0 4.1.1 4.10.0 4.10.1 4.11.0 4.12.0 4.13.0 4.13.1 4.13.2 4.14.0 4.14.1 4.14.2 4.14.3 4.14.4 4.14.5 4.14.6 4.2.0 4.2.1 4.3.0 4.3.1 4.3.2 4.4.0 4.5.0 4.6.1 4.7.0 4.7.1 4.8.0 4.8.1 4.9.0 trunk 1.9.0 2.0.0 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.1.0 2.1.1 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.10.0 2.10.1 2.10.2 2.10.3 2.10.4 2.11.0 2.11.1 2.11.2 2.11.3 2.12.0 2.12.1 2.12.2 2.12.3 2.13.0 2.13.1 2.13.2 2.13.3 2.13.4 2.14.0 2.15.0 2.16.0 2.16.1 2.17.0 2.17.1 2.17.3 2.18.0 2.18.1 2.19.1 2.19.2 2.19.3 2.19.4 2.19.5 2.19.6 2.19.7 2.19.8 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.2.6 2.20.0 2.20.1 2.20.2 2.21.0 2.21.1 2.21.2 2.21.3 2.21.4 2.22.0 2.22.1 2.22.2 2.22.3 2.23.0 2.23.1 2.23.2 2.24.0 2.24.1 2.24.2 2.25.0 2.25.1 2.25.2 2.25.3 2.26.0 2.27.0 2.27.1 2.27.2 2.27.3 2.28.0 2.29.0 2.29.1 2.29.2
give / src / Views / Form / Templates / Sequoia / assets / js / form.js
give / src / Views / Form / Templates / Sequoia / assets / js Last commit date
form.js 5 years ago
form.js
733 lines
1 /* globals jQuery, Give */
2 ( function( $ ) {
3 const templateOptions = window.sequoiaTemplateOptions;
4 const $container = $( '.give-embed-form' );
5 const $advanceButton = $( '.advance-btn', $container );
6 const $backButton = $( '.back-btn' );
7 const $navigatorTitle = $( '.give-form-navigator .title' );
8 const $paymentGatewayContainer = $( '#give-payment-mode-select' );
9 let gatewayAnimating = false;
10
11 const navigator = {
12 currentStep: templateOptions.introduction.enabled === 'enabled' ? 0 : 1,
13 animating: false,
14 goToStep: ( step ) => {
15 // Adjust body height before animating step, to prevent choppy iframe resizing
16 // Compare next step to current step, and increase body height if next step is taller.
17 const nextStepHeight = steps[ step ].title ? $( steps[ step ].selector ).height() + 50 : $( steps[ step ].selector ).height();
18 const currentStepHeight = steps[ navigator.currentStep ].title ? $( steps[ navigator.currentStep ].selector ).height() + 50 : $( steps[ navigator.currentStep ].selector ).height();
19 if ( nextStepHeight > currentStepHeight ) {
20 $( '.give-form-templates' ).css( 'min-height', `${ nextStepHeight + 123 }px` );
21 } else {
22 // Delay setting body height if next step is shorter than current step
23 setTimeout( function() {
24 $( '.give-form-templates' ).css( 'min-height', `${ nextStepHeight + 123 }px` );
25 }, 200 );
26 }
27
28 $( '.step-tracker' ).removeClass( 'current' );
29 $( '.step-tracker[data-step="' + step + '"]' ).addClass( 'current' );
30
31 if ( templateOptions.introduction.enabled === 'disabled' ) {
32 if ( $( '.step-tracker' ).length === 3 ) {
33 $( '.step-tracker' ).remove();
34 }
35
36 step = step > 0 ? step : 1;
37 if ( step === 1 ) {
38 $( '.back-btn', $container ).hide();
39 } else {
40 $( '.back-btn', $container ).show();
41 }
42
43 $( '.give-form-navigator', $container ).addClass( 'nav-visible' );
44 $( steps[ step ].selector ).css( 'padding-top', '50px' );
45 } else if ( step === 0 ) {
46 $( '.give-form-navigator', $container ).removeClass( 'nav-visible' );
47 $( steps[ step ].selector ).css( 'padding-top', '' );
48 } else {
49 $( '.give-form-navigator', $container ).addClass( 'nav-visible' );
50 $( steps[ step ].selector ).css( 'padding-top', '50px' );
51 }
52
53 if ( steps[ step ].title ) {
54 $navigatorTitle.text( steps[ step ].title );
55 }
56
57 const hide = steps.map( ( obj, index ) => {
58 if ( index === step || index === navigator.currentStep ) {
59 return null;
60 }
61 return obj.selector;
62 } );
63 const hideSelector = hide.filter( Boolean ).join( ', ' );
64
65 $( hideSelector ).hide();
66
67 if ( navigator.currentStep !== step ) {
68 const directionClasses = 'slide-in-right slide-in-left slide-out-right slide-out-left';
69 const outDirection = navigator.currentStep < step ? 'left' : 'right';
70 const inDirection = navigator.currentStep < step ? 'right' : 'left';
71 $( steps[ navigator.currentStep ].selector ).removeClass( directionClasses ).addClass( `slide-out-${ outDirection }` );
72 $( steps[ step ].selector ).show().removeClass( directionClasses ).addClass( `slide-in-${ inDirection }` );
73 }
74 navigator.currentStep = step;
75 setupTabOrder();
76
77 setTimeout( function() {
78 if ( steps[ navigator.currentStep ].firstFocus ) {
79 $( steps[ navigator.currentStep ].firstFocus ).focus();
80 }
81 }, 200 );
82 },
83 init: () => {
84 steps.forEach( ( step ) => {
85 if ( step.setup !== undefined ) {
86 step.setup();
87 }
88 $( step.selector ).css( 'position', 'absolute' );
89 } );
90 $advanceButton.on( 'click', function( e ) {
91 e.preventDefault();
92 navigator.forward();
93 } );
94 $backButton.on( 'click', function( e ) {
95 e.preventDefault();
96 navigator.back();
97 } );
98 $( '.step-tracker' ).on( 'click', function( e ) {
99 e.preventDefault();
100 navigator.goToStep( parseInt( $( e.target ).attr( 'data-step' ) ) );
101 } );
102 setupHeightChangeCallback( function( height ) {
103 if ( gatewayAnimating === false ) {
104 $( '.form-footer' ).css( 'transition', 'margin-top 0.2s ease' );
105 } else {
106 $( '.form-footer' ).css( 'transition', '' );
107 }
108 $( '.form-footer' ).css( 'margin-top', `${ height }px` );
109 } );
110 navigator.goToStep( getInitialStep() );
111 },
112 back: () => {
113 const prevStep = navigator.currentStep !== 0 ? navigator.currentStep - 1 : 0;
114 navigator.goToStep( prevStep );
115 navigator.currentStep = prevStep;
116 },
117 forward: () => {
118 const nextStep = navigator.currentStep !== null ? navigator.currentStep + 1 : 1;
119 navigator.goToStep( nextStep );
120 navigator.currentStep = nextStep;
121 },
122 };
123
124 const steps = [
125 {
126 id: 'introduction',
127 title: null,
128 selector: '.give-section.introduction',
129 label: templateOptions.introduction.donate_label,
130 showErrors: false,
131 tabOrder: [
132 '.introduction .advance-btn',
133 '.step-tracker',
134 ],
135 },
136 {
137 id: 'choose-amount',
138 title: templateOptions.payment_amount.header_label,
139 selector: '.give-section.choose-amount',
140 label: templateOptions.payment_amount.next_label,
141 showErrors: false,
142 tabOrder: [
143 'input.give-amount-top',
144 '.give-donation-levels-wrap button',
145 '.give-recurring-period',
146 '.give-recurring-donors-choice-period',
147 '.give_fee_mode_checkbox',
148 '.choose-amount .advance-btn',
149 '.step-tracker',
150 '.back-btn',
151 ],
152 firstFocus: '.give-default-level',
153 setup: () => {
154 // Dynamically set grid columns based on number of buttons
155 const buttonCount = $( '.give-donation-level-btn' ).length;
156 if ( buttonCount === 1 ) {
157 $( '.give-donation-levels-wrap' ).attr( 'style', 'display: none!important;' );
158 } else if ( buttonCount % 2 === 0 && buttonCount < 6 ) {
159 $( '.give-donation-levels-wrap' ).css( 'grid-template-columns', 'repeat(2, minmax(0, 1fr))' );
160 }
161
162 $( '#give-amount' ).on( 'blur', function() {
163 if ( ! Give.form.fn.isValidDonationAmount( $( 'form' ) ) ) {
164 $( '.advance-btn' ).attr( 'disabled', true );
165 } else {
166 $( '.advance-btn' ).attr( 'disabled', false );
167 }
168 } );
169 $( '.give-donation-level-btn' ).each( function() {
170 const hasTooltip = $( this ).attr( 'has-tooltip' );
171 if ( hasTooltip ) {
172 return;
173 }
174
175 const value = $( this ).attr( 'value' );
176 const text = $( this ).text();
177 const symbol = window.give_global_vars.currency_sign;
178 const position = window.give_global_vars.currency_pos;
179
180 if ( value !== 'custom' ) {
181 const html = position === 'before' ? `<div class="currency currency--before">${ symbol }</div>${ value }` : `${ value }<div class="currency currency--after">${ symbol }</div>`;
182 $( this ).html( html );
183 }
184
185 // Setup string to check tooltip label ga
186 const compare = position === 'before' ? symbol + value : value + symbol;
187 // Setup tooltip unless for custom donation level, or if level label matches value
188 if ( value !== 'custom' && text !== compare ) {
189 const wrap = `<span class="give-tooltip hint--top hint--bounce ${ text.length < 50 ? 'narrow' : '' }" style="width: 100%" aria-label="${ text.length < 50 ? text : text.substr( 0, 50 ) + '...' }" rel="tooltip"></span>`;
190 $( this ).wrap( wrap );
191 $( this ).attr( 'has-tooltip', true );
192 }
193 } );
194 },
195 },
196 {
197 id: 'payment',
198 title: templateOptions.payment_information.header_label,
199 label: templateOptions.payment_information.checkout_label,
200 selector: '.give-section.payment',
201 showErrors: true,
202 tabOrder: [
203 '.payment input, .payment a, .payment button, .payment select, .payment multiselect, .payment textarea, .payment .button',
204 '.give-submit',
205 '.step-tracker',
206 '.back-btn',
207 ],
208 firstFocus: '#give-first',
209 setup: () => {
210 // Setup payment information screen
211
212 $( '.give-section.payment' ).on( 'click', '.give-cancel-login, .give-checkout-register-cancel', clearLoginNotices );
213
214 // Show Sequoia loader on click/touchend
215 $( '.give-section.payment' ).on( 'click touchend', 'input[name="give_login_submit"]', function() {
216 //Override submit loader with Sequoia loader
217 $( 'input[name="give_login_submit"] + .give-loading-animation' ).removeClass( 'give-loading-animation' ).addClass( 'sequoia-loader spinning' );
218 } );
219
220 // Remove purchase_loading text
221 window.give_global_vars.purchase_loading = '';
222
223 // Move errors
224 $( '.give_error' ).each( function() {
225 moveErrorNotice( $( this ) );
226 } );
227
228 // Setup anonymous donations opt-in event listeners
229 setupCheckbox( {
230 container: '#give-anonymous-donation-wrap label',
231 label: '#give-anonymous-donation-wrap label',
232 input: '#give-anonymous-donation',
233 } );
234
235 // Setup recurring donations opt-in event listeners
236 setupCheckbox( {
237 container: '.give-recurring-donors-choice',
238 label: '.give-recurring-donors-choice label',
239 input: 'input[name="give-recurring-period"]',
240 } );
241
242 // Setup fee recovery opt-in event listeners
243 setupCheckbox( {
244 container: '.give-fee-recovery-donors-choice',
245 label: '.give-fee-message-label-text',
246 input: 'input[name="give_fee_mode_checkbox"]',
247 } );
248
249 // Setup activecampaign opt-in event listeners
250 setupCheckbox( {
251 container: '.give-activecampaign-fieldset',
252 label: '.give-activecampaign-optin-label',
253 input: '.give-activecampaign-optin-input',
254 } );
255
256 // Setup mailchimp opt-in event listeners
257 setupCheckbox( {
258 container: '.give-mailchimp-fieldset',
259 label: '.give-mc-message-text',
260 input: 'input[name="give_mailchimp_signup"]',
261 } );
262
263 // Setup constant contact opt-in event listeners
264 setupCheckbox( {
265 container: '.give-constant-contact-fieldset',
266 label: '.give-constant-contact-fieldset span',
267 input: 'input[name="give_constant_contact_signup"]',
268 } );
269
270 // Setup terms and conditions opt-in event listeners
271 setupCheckbox( {
272 container: '#give_terms_agreement',
273 label: '#give_terms_agreement label',
274 input: 'input[name="give_agree_to_terms"]',
275 } );
276
277 // Show Sequoia loader on click/touchend
278 $( 'body.give-form-templates' ).on( 'click touchend', 'form.give-form input[name="give-purchase"].give-submit', function() {
279 //Override submit loader with Sequoia loader
280 $( '#give-purchase-button + .give-loading-animation' ).removeClass( 'give-loading-animation' ).addClass( 'sequoia-loader' );
281
282 // Only show spinner if form is valid
283 if ( $( 'form' ).get( 0 ).checkValidity() ) {
284 $( '.sequoia-loader' ).addClass( 'spinning' );
285 }
286 } );
287
288 // Go to choose amount step when donation maximum error is clicked
289 $( 'body.give-form-templates' ).on( 'click touchend', '#give_error_invalid_donation_maximum', function() {
290 // Go to choose amount step
291 navigator.goToStep( 1 );
292 } );
293
294 // Go to choose amount step when invalid donation error is clicked
295 $( 'body.give-form-templates' ).on( 'click touchend', '#give_error_invalid_donation_amount', function() {
296 // Go to choose amount step
297 navigator.goToStep( 1 );
298 } );
299
300 // Setup gateway icons
301 setupGatewayIcons();
302
303 const observer = new window.MutationObserver( function( mutations ) {
304 mutations.forEach( function( mutation ) {
305 if ( ! mutation.addedNodes ) {
306 return;
307 }
308
309 for ( let i = 0; i < mutation.addedNodes.length; i++ ) {
310 // do things to your newly added nodes here
311 const node = mutation.addedNodes[ i ];
312
313 if ( $( node ).find( '.give_error' ).length > 0 ) {
314 moveErrorNotice( $( node ).find( '.give_error' ) );
315 }
316
317 if ( $( node ).children().hasClass( 'give_errors' ) && ! $( node ).parent().hasClass( 'donation-errors' ) ) {
318 $( node ).children( '.give_errors' ).each( function() {
319 const notice = $( this );
320 moveErrorNotice( notice );
321 } );
322 }
323
324 if ( $( node ).hasClass( 'give_errors' ) && ! $( node ).parent().hasClass( 'donation-errors' ) ) {
325 moveErrorNotice( $( node ) );
326 $( '.sequoia-loader' ).removeClass( 'spinning' );
327 }
328
329 if ( $( node ).attr( 'id' ) === 'give_tributes_address_state' ) {
330 const placeholder = $( node ).attr( 'placeholder' );
331 $( node ).prepend( `<option selected disabled>${ placeholder }</option>` );
332 }
333
334 if ( $( node ).attr( 'name' ) === 'give_tributes_address_state' && $( node ).attr( 'class' ).includes( 'give-input' ) ) {
335 $( node ).attr( 'placeholder', $( node ).siblings( 'label' ).text().trim() );
336 }
337
338 if ( $( node ).attr( 'id' ) && $( node ).attr( 'id' ).includes( 'give-checkout-login-register' ) ) {
339 $( '[id*="give-register-account-fields"]' ).on( 'click', handleFFMInput );
340 }
341
342 if ( $( node ).prop( 'tagName' ) && $( node ).prop( 'tagName' ).toLowerCase() === 'select' ) {
343 const placeholder = $( node ).attr( 'placeholder' );
344 $( node ).prepend( `<option value="" disabled selected>${ placeholder }</option>` );
345 }
346 }
347 } );
348 } );
349
350 observer.observe( document.body, {
351 childList: true,
352 subtree: true,
353 attributes: false,
354 characterData: false,
355 } );
356 },
357 },
358 ];
359
360 navigator.init();
361
362 // Check if only a single gateway is enabled
363 if ( $paymentGatewayContainer.length && $paymentGatewayContainer.css( 'display' ) !== 'none' ) {
364 // Move payment information section when document load.
365 moveFieldsUnderPaymentGateway( true );
366
367 // Move payment information section when gateway updated.
368 $( document ).on( 'give_gateway_loaded', function() {
369 setupTabOrder();
370 moveFieldsUnderPaymentGateway( true );
371 setupSelectInputs();
372 $( '#give_purchase_form_wrap' ).slideDown( 200, function() {
373 gatewayAnimating = false;
374 } );
375 } );
376 $( document ).on( 'Give:onPreGatewayLoad', function() {
377 gatewayAnimating = true;
378 $( '#give_purchase_form_wrap' ).slideUp( 200 );
379 } );
380
381 // Clear gateway related errors
382 $( document ).on( 'Give:onPreGatewayLoad', function() {
383 const persistedNotices = [
384 'give_error_test_mode',
385 ];
386
387 $( '.give_errors, .give_notices, .give_error' ).each( function() {
388 if ( ! persistedNotices.includes( $( this ).attr( 'id' ) ) ) {
389 $( this ).slideUp( 200, function() {
390 $( this ).remove();
391 } );
392 }
393 } );
394 } );
395
396 // Refresh payment information section.
397 $( document ).on( 'give_gateway_loaded', refreshPaymentInformationSection );
398 } else {
399 $( '#give_purchase_form_wrap' ).addClass( 'give-single-gateway-wrap' );
400 setupSelectInputs();
401 }
402
403 /**
404 * Move error notices to error notice container at the top of the payment section
405 * @since 2.7.0
406 * @param {node} node The error notice node to be moved
407 */
408 function moveErrorNotice( node ) {
409 //If the error is inside stripe payment button, do nothing
410 if ( $( node ).parent().hasClass( 'give-stripe-payment-request-button' ) ) {
411 return;
412 }
413
414 // First check if specific donation notice container has been set up
415 if ( $( '.donation-errors' ).length === 0 ) {
416 $( '.payment' ).prepend( '<div class="donation-errors"></div>' );
417 }
418
419 // If a specific notice does not already exist, proceed with moving the error
420 if ( typeof $( '.donation-errors' ).html() !== undefined && ! $( '.donation-errors' ).html().includes( $( node ).html() ) ) {
421 $( node ).appendTo( '.donation-errors' );
422 } else {
423 // If the specific notice already exists, do not add it
424 $( node ).remove();
425 }
426 }
427
428 /**
429 * Add listeners and starting states to FFM inputs
430 * @since 2.7.0
431 */
432 function setupFFMInputs() {
433 $( '#give-ffm-section' ).off( 'click', handleFFMInput );
434 $( '[id*="give-register-account-fields"]' ).off( 'click', handleFFMInput );
435
436 $( '#give-ffm-section' ).on( 'click', handleFFMInput );
437 $( '[id*="give-register-account-fields"]' ).on( 'click', handleFFMInput );
438
439 $( '#give-ffm-section input' ).each( function() {
440 switch ( $( this ).prop( 'type' ) ) {
441 case 'checkbox': {
442 if ( $( this ).prop( 'checked' ) ) {
443 $( this ).parent().addClass( 'checked' );
444 } else {
445 $( this ).parent().removeClass( 'checked' );
446 }
447 break;
448 }
449 case 'radio': {
450 if ( $( this ).prop( 'checked' ) ) {
451 $( this ).parent().addClass( 'selected' );
452 } else {
453 $( this ).parent().removeClass( 'selected' );
454 }
455 break;
456 }
457 }
458 } );
459 }
460
461 /**
462 * Move form field under payment gateway
463 * @since 2.7.0
464 */
465 function moveFieldsUnderPaymentGateway() {
466 // Check if donate fieldset area has been created, if not set it up below payment gateways
467 // This area is necessary for correctly placing various elements (fee recovery notice, newsletters, submit button, etc)
468 if ( $( '#donate-fieldset' ).length === 0 ) {
469 $( '#give-payment-mode-select' ).after( '<fieldset id="donate-fieldset"></fieldset>' );
470 }
471
472 // Elements to move into donate fieldset (located at bottom of form)
473 // The elements will appear in order of array
474 const donateFieldsetElements = [
475 '.give-constant-contact-fieldset',
476 '.give-activecampaign-fieldset',
477 '.give-mailchimp-fieldset',
478 '#give_terms_agreement',
479 '.give-donation-submit',
480 ];
481
482 // Handle moving elements into donate fieldset
483 donateFieldsetElements.forEach( function( selector ) {
484 if ( $( `#donate-fieldset ${ selector }` ).length === 0 ) {
485 $( '#donate-fieldset' ).append( $( `#give_purchase_form_wrap ${ selector }` ) );
486 } else if ( $( `#donate-fieldset ${ selector }` ).html() !== $( `#give_purchase_form_wrap ${ selector }` ).html() ) {
487 $( `#donate-fieldset ${ selector }` ).remove();
488 $( '#donate-fieldset' ).append( $( `#give_purchase_form_wrap ${ selector }` ) );
489 } else {
490 $( `#give_purchase_form_wrap ${ selector }` ).remove();
491 }
492 } );
493
494 // Handle per-Gateway fee option
495 // If the fee recovery option wrapper is present, move it to the choose amount screen
496 if ( $( '#give_purchase_form_wrap fieldset[id*="give-fee-recovery-wrap"]' ).length !== 0 ) {
497 let checked = false;
498 if ( $( '.choose-amount fieldset[id*="give-fee-recovery-wrap"]' ).length !== 0 ) {
499 checked = $( 'input[name="give_fee_mode_checkbox"]' ).prop( 'checked' );
500 $( '.choose-amount fieldset[id*="give-fee-recovery-wrap"]' ).remove();
501 }
502 $( '.choose-amount' ).append( $( '#give_purchase_form_wrap fieldset[id*="give-fee-recovery-wrap"]' ) );
503 $( 'input[name="give_fee_mode_checkbox"]' ).prop( 'checked', checked );
504 }
505
506 // Move purchase fields (credit card, billing, etc)
507 $( 'li.give-gateway-option-selected' ).after( $( '#give_purchase_form_wrap' ) );
508
509 // Add gateway class to fields wrapper, indicating which gateway is active
510 const gatewayClass = 'gateway-' + $( '.give-gateway-option-selected input' ).attr( 'value' ).replace( '_', '-' );
511 $( '#give_purchase_form_wrap' ).attr( 'class', gatewayClass );
512
513 setupFFMInputs();
514 setupInputIcons();
515 }
516
517 /**
518 * Refresh payment information section
519 *
520 * @since 2.7.0
521 * @param {boolean} ev Event object
522 * @param {object} response Response object
523 * @param {number} formID Form ID
524 */
525 function refreshPaymentInformationSection( ev, response, formID ) {
526 if ( navigator.currentStep === 2 ) {
527 $( '.give-form-templates' ).css( 'min-height', '' );
528 }
529
530 const $form = $( `#${ formID }` );
531
532 // This function will run only for embed donation form.
533 // Show payment information section fields.
534 if ( $form.parent().hasClass( 'give-embed-form' ) ) {
535 const data = {
536 action: 'give_cancel_login',
537 form_id: $form.find( '[name="give-form-id"]' ).val(),
538 };
539
540 // AJAX get the payment fields.
541 $.post( Give.fn.getGlobalVar( 'ajaxurl' ), data, function( postResponse ) {
542 $form.find( '[id^=give-checkout-login-register]' ).replaceWith( $.parseJSON( postResponse.fields ) );
543 $form.find( '[id^=give-checkout-login-register]' ).css( { display: 'block' } );
544 $form.find( '.give-submit-button-wrap' ).show();
545 } ).done( function() {
546 // Trigger float-labels
547 window.give_fl_trigger();
548 } );
549 }
550 }
551
552 function setupInputIcon( selector, icon ) {
553 $( selector ).each( function() {
554 if ( $( this ).html() !== '' && $( this ).html().includes( `<i class="fas fa-${ icon }"></i>` ) === false ) {
555 $( this ).prepend( `<i class="fas fa-${ icon }"></i>` );
556 $( this ).children( 'input, selector' ).each( function() {
557 $( this ).attr( 'style', 'padding-left: 33px!important;' );
558 } );
559 }
560 } );
561 }
562
563 function setupInputIcons() {
564 setupInputIcon( '#give-first-name-wrap', 'user' );
565 setupInputIcon( '#give-email-wrap', 'envelope' );
566 setupInputIcon( '#give-company-wrap', 'building' );
567 setupInputIcon( '#date_field-wrap', 'calendar-alt' );
568 setupInputIcon( '#url_field-wrap', 'globe' );
569 setupInputIcon( '#phone_field-wrap', 'phone' );
570 setupInputIcon( '#give-phone-wrap', 'phone' );
571 setupInputIcon( '#email_field-wrap', 'envelope' );
572 }
573
574 /**
575 * Setup tab order for elements in form
576 *
577 * @since 2.7.0
578 */
579 function setupTabOrder() {
580 $( 'select, button, input, textarea, multiselect, a' ).attr( 'tabindex', -1 );
581
582 const tabOrder = steps[ navigator.currentStep ].tabOrder;
583 tabOrder.forEach( ( selector, index ) => {
584 $( selector ).attr( 'tabindex', index + 1 );
585 } );
586 }
587
588 /**
589 * Loop through gateway li elements and setup fontawesome icons
590 *
591 * @since 2.7.0
592 */
593 function setupGatewayIcons() {
594 $( '#give-gateway-radio-list li' ).each( function() {
595 const value = $( 'input', this ).val();
596 let icon;
597 switch ( value ) {
598 case 'manual':
599 icon = 'fas fa-tools';
600 break;
601 case 'offline':
602 icon = 'fas fa-wallet';
603 break;
604 case 'paypal':
605 icon = 'fab fa-paypal';
606 break;
607 case 'stripe':
608 icon = 'far fa-credit-card';
609 break;
610 case 'stripe_checkout':
611 icon = 'far fa-credit-card';
612 break;
613 case 'stripe_sepa':
614 icon = 'fas fa-university';
615 break;
616 case 'stripe_ach':
617 icon = 'fas fa-university';
618 break;
619 case 'stripe_ideal':
620 icon = 'fas fa-university';
621 break;
622 case 'stripe_becs':
623 icon = 'fas fa-university';
624 break;
625 case 'paypalpro_payflow':
626 icon = 'far fa-credit-card';
627 break;
628 case 'stripe_google_pay':
629 icon = 'fab fa-google';
630 break;
631 case 'stripe_apple_pay':
632 icon = 'fab fa-apple';
633 break;
634 default:
635 icon = 'fas fa-hand-holding-heart';
636 break;
637 }
638 $( this ).append( `<i class="${ icon }"></i>` );
639 } );
640 }
641
642 /**
643 * Setup prominent checkboxes (that use persistent borders on select)
644 *
645 * @since 2.7.0
646 * @param {object} args Argument object containing: container, label, input selectors
647 */
648 function setupCheckbox( { container, label, input } ) {
649 // If checkbox is opted in by default, add border on load
650 if ( $( input ).prop( 'checked' ) === true ) {
651 $( container ).addClass( 'active' );
652 }
653
654 // Persist checkbox input border when selected
655 $( label ).on( 'click touchend', function( evt ) {
656 if ( container === label ) {
657 evt.stopPropagation();
658 evt.preventDefault();
659
660 $( input ).prop( 'checked', ! $( input ).prop( 'checked' ) );
661 }
662
663 $( container ).toggleClass( 'active' );
664 } );
665 }
666
667 function setupHeightChangeCallback( callback ) {
668 let lastHeight = 0;
669 function checkHeightChange() {
670 const selector = $( steps[ navigator.currentStep ].selector );
671 const changed = lastHeight !== $( selector ).outerHeight();
672 if ( changed ) {
673 callback( $( selector ).outerHeight() );
674 lastHeight = $( selector ).outerHeight();
675 }
676 window.requestAnimationFrame( checkHeightChange );
677 }
678 window.requestAnimationFrame( checkHeightChange );
679 }
680
681 /**
682 * Get initial step to show donor.
683 *
684 * @since 2.7.0
685 * @returns {number} Step to start on
686 */
687 function getInitialStep() {
688 return Give.fn.getParameterByName( 'showDonationProcessingError' ) || Give.fn.getParameterByName( 'showFailedDonationError' ) ? 2 : 0;
689 }
690
691 /**
692 * Handle updating label classes for FFM radios and checkboxes
693 *
694 * @since 2.7.0
695 * @param {object} evt Reference to FFM input element click event
696 */
697 function handleFFMInput( evt ) {
698 if ( $( evt.target ).is( 'input' ) ) {
699 switch ( $( evt.target ).prop( 'type' ) ) {
700 case 'checkbox': {
701 $( evt.target ).parent().toggleClass( 'checked' );
702 break;
703 }
704 case 'radio': {
705 $( evt.target ).parent().addClass( 'selected' );
706 $( evt.target ).parent().siblings().removeClass( 'selected' );
707 break;
708 }
709 }
710 }
711 }
712
713 function clearLoginNotices() {
714 $( '#give_error_must_log_in' ).remove();
715 }
716
717 /**
718 * Setup select inputs
719 *
720 * @since 2.7.0
721 */
722 function setupSelectInputs() {
723 if ( $( 'select option[selected="selected"][value=""]' ).length > 0 ) {
724 $( 'select option[selected="selected"][value=""]' ).each( function() {
725 if ( $( this ).parent().siblings( 'label' ).length ) {
726 $( this ).text( $( this ).parent().siblings( 'label' ).text().replace( '*', '' ).trim() );
727 $( this ).attr( 'disabled', true );
728 }
729 } );
730 }
731 }
732 }( jQuery ) );
733