PluginProbe
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) / 1.6.6
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) v1.6.6
1.8.13.1 1.8.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 All 212 releases
charitable / assets / js / charitable.js

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

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