PluginProbe
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) / 1.4.13
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) v1.4.13
1.8.12.3 1.8.12.2 1.8.12.1 1.8.12 1.8.11.3 1.8.11.2 1.8.11.1 1.8.11 1.6.6 1.6.60 1.6.7 1.6.8 1.6.9 1.7.0 1.7.0.1 1.7.0.11 1.7.0.12 1.7.0.14 1.7.0.2 1.7.0.3 1.7.0.5 1.7.0.6 1.7.0.7 1.7.0.9 1.8.0 All 210 releases
charitable / assets / js / charitable.js

charitable.js in Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) 1.4.13, at assets/js/charitable.js

769 lines 19.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 CHARITABLE = window.CHARITABLE || {};
2
3 /**
4 * Set up Donation_Form object.
5 */
6 ( function( exports, $ ){
7
8 /**
9 * Donation_Form expects a jQuery this.form object.
10 */
11 var Donation_Form = function( form ) {
12
13 /**
14 * Array of errors.
15 *
16 * @access public
17 */
18 this.errors = [];
19
20 /**
21 * Form object.
22 *
23 * @access public
24 */
25 this.form = form;
26
27 /**
28 * Flag to allow processing to be paused (i.e. while something like Stripe is processing).
29 *
30 * @access public
31 */
32 this.pause_processing = false;
33
34 /**
35 * Flag to prevent the on_submit handler from sending multiple concurrent AJAX requests
36 *
37 * @access public
38 */
39 this.submit_processing = false;
40
41 var self = this;
42 var $body = $( 'body' );
43
44 /**
45 * Focus event handler on custom donation amount input field.
46 *
47 * @access private
48 */
49 var on_focus_custom_amount = function() {
50
51 $( this ).closest( 'li' ).find( 'input[name=donation_amount]' ).prop( 'checked', true ).trigger( 'change' );
52
53 $body.off( 'focus', 'input.custom-donation-input', on_focus_custom_amount );
54
55 $( this ).focus();
56
57 $body.on( 'focus', 'input.custom-donation-input', on_focus_custom_amount );
58
59 };
60
61 /**
62 * Focus event handler for changes to custom donation amount input field.
63 *
64 * @access private
65 */
66 var on_change_custom_donation_amount = function() {
67
68 var unformatted = self.unformat_amount( $( this ).val() );
69
70 if ( $.trim( unformatted ) > 0 ) {
71 $( this ).val( self.format_amount( unformatted ) );
72 }
73
74 };
75
76 /**
77 * Select a donation amount.
78 *
79 * @return void
80 */
81 var on_select_donation_amount = function() {
82
83 var $li = $( this ).closest( 'li' );
84
85 // Already selected, quit early to prevent focus/change loop
86 if ( $li.hasClass( 'selected' ) ) {
87 return;
88 }
89
90 $li.parents( '.charitable-donation-form' ).find( '.donation-amount.selected' ).removeClass( 'selected' );
91
92 $li.addClass( 'selected' );
93
94 if ( $li.hasClass( 'custom-donation-amount' ) ) {
95 $li.find( 'input.custom-donation-input' ).focus();
96 }
97 };
98
99 /**
100 * Change event handler for payment gateway selector.
101 *
102 * @access private
103 */
104 var on_change_payment_gateway = function() {
105
106 self.hide_inactive_payment_methods();
107
108 self.show_active_payment_methods( $(this).val() );
109
110 };
111
112 /**
113 * Submit event handler for donation form.
114 *
115 * @access private
116 */
117 var on_submit = function( event ) {
118
119 var helper = event.data.helper;
120
121 if ( helper.submit_processing ) {
122 return false;
123 }
124
125 helper.submit_processing = true;
126
127 /* Display the processing spinner and hide the button */
128 helper.show_processing();
129
130 /* Validate the form submission before going further. */
131 if ( false === helper.validate() ) {
132
133 helper.hide_processing();
134
135 helper.print_errors();
136
137 helper.scroll_to_top();
138
139 return false;
140
141 }
142
143 /* If processing has been paused, return false now. */
144 if ( false !== helper.pause_processing ) {
145 return false;
146 }
147
148 /* If we're not using AJAX to process the donation further, return now. */
149 if ( 1 !== helper.form.data( 'use-ajax' ) ) {
150 return true;
151 }
152
153 /* If we're still here, trigger the processing event. */
154 $body.trigger( 'charitable:form:process', helper );
155
156 return false;
157
158 };
159
160 /**
161 * Process the donation.
162 *
163 * This is a callback for the 'charitable:form:process' event. It's called
164 * after validation has taken place.
165 *
166 * @param object Event
167 * @param object Donation_Form
168 */
169 var process_donation = function( event, helper ) {
170
171 var data = helper.get_data();
172 var form = helper.form;
173
174 /* Cancel the default Charitable action, but pass it along as the form_action variable */
175 data.action = 'make_donation';
176 data.form_action = data.charitable_action;
177 delete data.charitable_action;
178
179 $.ajax({
180 type: "POST",
181 data: data,
182 dataType: "json",
183 url: CHARITABLE_VARS.ajaxurl,
184 xhrFields: {
185 withCredentials: true
186 },
187 success: function (response) {
188
189 if ( response.success ) {
190 window.location.href = response.redirect_to;
191 }
192 else {
193 helper.hide_processing();
194
195 helper.print_errors( response.errors );
196
197 helper.scroll_to_top();
198 }
199 }
200
201 }).fail(function (response, textStatus, errorThrown) {
202
203 if ( window.console && window.console.log ) {
204 console.log( response );
205 }
206
207 helper.hide_processing();
208
209 helper.print_errors( [ CHARITABLE_VARS.error_unknown ] );
210
211 helper.scroll_to_top();
212
213 });
214
215 return false;
216
217 }
218
219 /**
220 * Initialization that should happen for every new form object.
221 *
222 * @return void
223 */
224 var init_each = function() {
225
226 self.form.find( '.donation-amount input:checked' ).each( function() {
227 $( this ).closest( 'li' ).addClass( 'selected' );
228 });
229
230 if ( self.get_all_payment_methods().length ) {
231 self.hide_inactive_payment_methods();
232 self.form.on( 'change', '#charitable-gateway-selector input[name=gateway]', on_change_payment_gateway );
233 }
234
235 // Handle donation form submission
236 self.form.on( 'submit', {
237 helper : self,
238 }, on_submit );
239
240 $body.trigger( 'charitable:form:loaded', self );
241
242 }
243
244 /**
245 * Set up event handlers for donation forms.
246 *
247 * Note: This function is only ever designed to run once.
248 *
249 * @return void
250 */
251 var init = function() {
252
253 // Init donation amount selection
254 $body.on( 'click', '.donation-amount', on_select_donation_amount );
255 $body.on( 'focus', 'input.custom-donation-input', on_focus_custom_amount );
256
257 // Init currency formatting
258 $body.on( 'blur', '.custom-donation-input', on_change_custom_donation_amount );
259
260 // Process the donation on the 'charitable:form:process' event
261 $body.on( 'charitable:form:process', process_donation );
262
263 $body.trigger( 'charitable:form:initialize', self );
264
265 CHARITABLE.forms_initialized = true;
266
267 }
268
269 init_each();
270
271 if ( false === CHARITABLE.forms_initialized ) {
272 init();
273 }
274
275 };
276
277 /**
278 * Return a particular input field.
279 *
280 * @param string The field name to retrieve.
281 * @return object
282 */
283 Donation_Form.prototype.get_input = function( field ) {
284 return this.form.find( '[name=' + field + ']' );
285 }
286
287 /**
288 * Return the submitted email address.
289 *
290 * @return string
291 */
292 Donation_Form.prototype.get_email = function() {
293 return this.form.find( '[name=email]' ).val();
294 };
295
296 /**
297 * Get the submitted amount, taking into account both the custom & suggested donation fields.
298 *
299 * @return string
300 */
301 Donation_Form.prototype.get_amount = function() {
302 var amount = suggested = parseFloat( this.form.find( '[name=donation_amount]:checked' ).val() );
303
304 if ( isNaN( suggested ) ) {
305 var custom = this.form.find( '.charitable-donation-options.active .custom-donation-input' );
306
307 if ( 0 === custom.length ) {
308 custom = this.form.find( '.custom-donation-input' );
309 }
310
311 amount = parseFloat( custom.val() );
312 }
313
314 if ( isNaN( amount ) || amount <= 0 ) {
315 amount = 0;
316 }
317
318 return amount;
319 };
320
321 /**
322 * Get a description of the donation.
323 *
324 * @return string
325 */
326 Donation_Form.prototype.get_description = function() {
327 return this.form.find( '[name=description]' ).val() || '';
328 };
329
330 /**
331 * Get credit card number.
332 *
333 * @return string
334 */
335 Donation_Form.prototype.get_cc_number = function() {
336 return this.form.find( '#charitable_field_cc_number input' ).val() || '';
337 };
338
339 /**
340 * Get credit card CVC number.
341 *
342 * @return string
343 */
344 Donation_Form.prototype.get_cc_cvc = function() {
345 return this.form.find( '#charitable_field_cc_cvc input' ).val() || '';
346 };
347
348 /**
349 * Get credit card expiry month.
350 *
351 * @return string
352 */
353 Donation_Form.prototype.get_cc_expiry_month = function() {
354 return this.form.find( '#charitable_field_cc_expiration select.month' ).val() || '';
355 };
356
357 /**
358 * Get credit card expiry year.
359 *
360 * @return string
361 */
362 Donation_Form.prototype.get_cc_expiry_year = function() {
363 return this.form.find( '#charitable_field_cc_expiration select.year' ).val() || '';
364 };
365
366 /**
367 * Clear credit card fields.
368 *
369 * This is used by gateways that create tokens through Javascript (such as Stripe), to
370 * avoid credit card details hitting the server.
371 *
372 * @return void
373 */
374 Donation_Form.prototype.clear_cc_fields = function() {
375 this.form.find( '#charitable_field_cc_number input, #charitable_field_cc_name input, #charitable_field_cc_cvc input, #charitable_field_cc_expiration select' ).removeAttr( 'name' );
376 };
377
378 /**
379 * Return the selected payment method.
380 *
381 * @return string
382 */
383 Donation_Form.prototype.get_payment_method = function() {
384 return this.form.find( '[type=hidden][name=gateway], [name=gateway]:checked' ).val() || '';
385 };
386
387 /**
388 * Return all payment methods.
389 *
390 * @return object
391 */
392 Donation_Form.prototype.get_all_payment_methods = function() {
393 return this.form.find( '#charitable-gateway-selector input[name=gateway]' );
394 }
395
396 /**
397 * Hide inactive payment methods.
398 *
399 * @return void
400 */
401 Donation_Form.prototype.hide_inactive_payment_methods = function() {
402 var active = this.get_payment_method();
403
404 this.form.find( '.charitable-gateway-fields[data-gateway!=' + active + ']' ).hide();
405 };
406
407 /**
408 * Show active payment methods.
409 *
410 * @return void
411 */
412 Donation_Form.prototype.show_active_payment_methods = function( active ) {
413 var active = active || this.get_payment_method();
414
415 this.form.find( '.charitable-gateway-fields[data-gateway=' + active + ']' ).show();
416 };
417
418 /**
419 * Select a donation amount.
420 *
421 * @param int price
422 * @param string symbol
423 * @return string
424 */
425 Donation_Form.prototype.format_amount = function( price, symbol ){
426 if ( typeof symbol === 'undefined' )
427 symbol = '';
428
429 return accounting.formatMoney( price, {
430 symbol : symbol,
431 decimal : CHARITABLE_VARS.currency_format_decimal_sep,
432 thousand: CHARITABLE_VARS.currency_format_thousand_sep,
433 precision : CHARITABLE_VARS.currency_format_num_decimals,
434 format: CHARITABLE_VARS.currency_format
435 }).trim();
436
437 };
438
439 /**
440 * Select a donation amount.
441 *
442 * @param int price
443 * @return string
444 */
445 Donation_Form.prototype.unformat_amount = function( price ) {
446 return Math.abs( parseFloat( accounting.unformat( price, CHARITABLE_VARS.currency_format_decimal_sep ) ) );
447 };
448
449 /**
450 * Add an error message.
451 *
452 * @param string message
453 * @return void
454 */
455 Donation_Form.prototype.add_error = function( message ) {
456 this.errors.push( message );
457 };
458
459 /**
460 * Return the errors.
461 *
462 * @return []
463 */
464 Donation_Form.prototype.get_errors = function() {
465 return this.errors;
466 };
467
468 /**
469 * Prints out a nice string describing the errors.
470 *
471 * @return string
472 */
473 Donation_Form.prototype.get_error_message = function() {
474 return this.errors.join( ' ' );
475 };
476
477 /**
478 * Print the errors at the top of the form.
479 *
480 * @param array
481 */
482 Donation_Form.prototype.print_errors = function( errors ) {
483
484 var e = errors || this.errors,
485 i = 0,
486 count = e.length,
487 output = '';
488
489 if ( 0 === count ) {
490 return;
491 }
492
493 if ( this.form.find( '.charitable-form-errors' ).length ) {
494 this.form.find( '.charitable-form-errors' ).remove();
495 }
496
497 output += '<div class="charitable-form-errors charitable-notice"><ul class="errors"><li>';
498 output += e.join( '</li><li>' );
499 output += '</li></ul></div>';
500
501 this.form.prepend( output );
502
503 }
504
505 /**
506 * Clear the errors and remove the printed errors.
507 */
508 Donation_Form.prototype.clear_errors = function() {
509
510 this.errors = [];
511
512 if ( this.form.find( '.charitable-form-errors' ).length ) {
513 this.form.find( '.charitable-form-errors' ).remove();
514 }
515
516 }
517
518 /**
519 * Show that the donation form is processing.
520 */
521 Donation_Form.prototype.show_processing = function() {
522 this.form.find( '.charitable-form-processing' ).show();
523 this.form.find( 'button[name="donate"]' ).hide();
524 }
525
526 /**
527 * Hide the processing spinner and show the donate button.
528 */
529 Donation_Form.prototype.hide_processing = function() {
530 this.form.find( '.charitable-form-processing' ).hide();
531 this.form.find( 'button[name="donate"]' ).show();
532
533 this.submit_processing = false;
534 }
535
536 /**
537 * Scroll to the top of the form.
538 */
539 Donation_Form.prototype.scroll_to_top = function() {
540
541 var $modal = this.form.parents( '.charitable-modal' );
542
543 if ( $modal.length ) {
544 $modal.scrollTop( 0 );
545 }
546 else {
547 window.scrollTo( this.form.position().left, this.form.position().top );
548 }
549
550 };
551
552 /**
553 * Returns all of the submitted data as key=>value object.
554 *
555 * @return object
556 */
557 Donation_Form.prototype.get_data = function() {
558
559 return this.form.serializeArray().reduce( function( obj, item ) {
560 obj[ item.name ] = item.value;
561 return obj;
562 }, {} );
563
564 };
565
566 /**
567 * Returns all of the required fields.
568 *
569 * @return object
570 */
571 Donation_Form.prototype.get_required_fields = function() {
572
573 var fields = this.form.find( '.charitable-fieldset .required-field' ).not( '#charitable-gateway-fields .required-field' ),
574 method = this.get_payment_method();
575
576 if ( '' !== method ) {
577
578 var gateway_fields = this.form.find( '[data-gateway=' + method + '] .required-field' );
579
580 if ( gateway_fields.length ) {
581 fields = $.merge( fields, gateway_fields );
582 }
583
584 }
585
586 return fields;
587
588 };
589
590 /**
591 * Make sure that the submitted amount is valid.
592 *
593 * @return boolean
594 */
595 Donation_Form.prototype.is_valid_amount = function() {
596
597 return this.get_amount() > 0;
598
599 };
600
601 /**
602 * Validate the amount. If not valid, set an error.
603 *
604 * @return boolean
605 */
606 Donation_Form.prototype.validate_amount = function() {
607
608 if ( false === this.is_valid_amount() ) {
609 this.add_error( CHARITABLE_VARS.error_invalid_amount );
610 return false;
611 }
612
613 return true;
614
615 };
616
617 /**
618 * Verify that all required fields are filled out.
619 *
620 * @return boolean
621 */
622 Donation_Form.prototype.validate_required_fields = function() {
623
624 var has_missing_vals = false;
625
626 var required = this.get_required_fields();
627
628 this.get_required_fields().each( function() {
629
630 if ( '' === $( this ).find( 'input, select, textarea' ).val() ) {
631 has_missing_vals = true;
632 }
633
634 });
635
636 if ( has_missing_vals ) {
637 this.add_error( CHARITABLE_VARS.error_required_fields );
638 }
639
640 return has_missing_vals;
641
642 };
643
644 /**
645 * Verifies the submission and returns true if it all looks ok.
646 *
647 * @param this.form The submitted form.
648 * @return boolean
649 */
650 Donation_Form.prototype.validate = function() {
651
652 /* First clear out the errors. */
653 this.clear_errors();
654
655 this.validate_amount();
656
657 this.validate_required_fields();
658
659 this.form.trigger( 'charitable:form:validate', this );
660
661 return this.errors.length === 0;
662
663 };
664
665 exports.Donation_Form = Donation_Form;
666
667 })( CHARITABLE, jQuery );
668
669 /**
670 * Set up Toggle object.
671 */
672 ( function( exports, $ ){
673
674 var Toggle = function() {
675
676 /**
677 * Hide toggle target.
678 */
679 var hide_target = function() {
680 $( '#' + $(this).data('charitable-toggle') ).addClass( 'charitable-hidden' );
681 };
682
683 /**
684 * Toggle event handler for any fields with the [data-charitable-toggle] attribute.
685 *
686 * @access private
687 */
688 var on_toggle = function() {
689
690 var $this = $( this ),
691 target = $this.data( 'charitable-toggle' );
692
693 $( '#' + target ).toggleClass( 'charitable-hidden', $this.is( ':checked' ) );
694
695 return false;
696
697 };
698
699 // Initialization only required once.
700 $( 'body' ).on( 'click', '[data-charitable-toggle]', on_toggle );
701
702 // Initialization that will be performed everytime
703 return function() {
704 $( '[data-charitable-toggle]' ).each( hide_target );
705 }
706 }
707
708 exports.Toggle = Toggle();
709
710 })( CHARITABLE, jQuery );
711
712 /**
713 * Set up Charitable helper functions.
714 */
715 ( function( exports, $ ) {
716
717 var Helpers = function() {
718
719 /**
720 * Sanitize URLs.
721 */
722 this.sanitize_url = function( input ) {
723
724 var url = input.value.toLowerCase();
725
726 if ( !/^https?:\/\//i.test( url ) && url.length > 0 ) {
727 url = 'http://' + url;
728
729 input.value = url;
730 }
731 }
732
733 };
734
735 })( CHARITABLE, jQuery );
736
737 /**
738 * URL sanitization.
739 *
740 * This is provided for backwards compatibility.
741 */
742 CHARITABLE.SanitizeURL = function( input ) {
743 CHARITABLE.Helpers.sanitize_url( input );
744 };
745
746 /***
747 * Finally queue up all the scripts.
748 */
749 ( function( $ ) {
750
751 /**
752 * Prevent re-initializing form handlers.
753 */
754 CHARITABLE.forms_initialized = false;
755
756 $( document ).ready( function() {
757
758 $( 'html' ).addClass( 'js' );
759
760 $( '.charitable-donation-form' ).each( function(){
761 new CHARITABLE.Donation_Form( $( this ) );
762 });
763
764 CHARITABLE.Toggle();
765
766 });
767
768 })( jQuery );
769