PluginProbe
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) / 1.8.0
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) v1.8.0
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.8.0, at assets/js/charitable.js

1,061 lines 29.8 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 * Pending processes array.
22 *
23 * @access public
24 */
25 this.pending_processes = [];
26
27 /**
28 * Form object.
29 *
30 * @access public
31 */
32 this.form = form;
33
34 /**
35 * Flag to allow processing to be paused (i.e. while something like Stripe is processing).
36 *
37 * @access public
38 */
39 this.pause_processing = false;
40
41 /**
42 * Flag to prevent the on_submit handler from sending multiple concurrent AJAX requests
43 *
44 * @access public
45 */
46 this.submit_processing = false;
47
48 /**
49 * The donation amount.
50 *
51 * @access public
52 */
53 this.total = 0;
54
55 /**
56 * Whether to scroll to the top of the form after a submission with errors.
57 *
58 * @access private
59 */
60 this.prevent_scroll_to_top = false;
61
62 /**
63 * Reference to this object.
64 *
65 * @access private
66 */
67 var self = this;
68
69 /**
70 * Body element reference.
71 *
72 * @access private
73 */
74 var $body = $( 'body' );
75
76 /**
77 * Handle click of terms link.
78 *
79 * @access private
80 */
81 var on_click_terms = function() {
82 self.form.find( '.charitable-terms-text' ).addClass( 'active' );
83 return false;
84 };
85
86 /**
87 * Focus event handler on custom donation amount input field.
88 *
89 * @access private
90 */
91 var on_focus_custom_amount = function() {
92 $( this ).closest( 'li' ).trigger( 'click' ).find( 'input[name=donation_amount]' ).prop( 'checked', true ).trigger( 'change' );
93
94 self.form.off( 'focus', 'input.custom-donation-input', on_focus_custom_amount );
95
96 $( this ).focus();
97
98 self.form.on( 'focus', 'input.custom-donation-input', on_focus_custom_amount );
99 };
100
101 /**
102 * Trigger jQuery events to broadcast the change in donation amount.
103 *
104 * @since 1.6.19
105 *
106 * @access private
107 */
108 var trigger_amount_change_events = function() {
109 /* The chosen donation amount has changed. */
110 $body.trigger( 'charitable:form:amount:changed', self );
111
112 /* The overall donation amount has changed. */
113 $body.trigger( 'charitable:form:total:changed', self );
114 };
115
116 /**
117 * Focus event handler for changes to custom donation amount input field.
118 *
119 * @access private
120 */
121 var on_change_custom_donation_amount = function() {
122 var unformatted = self.unformat_amount( $( this ).val() );
123
124 if ( $.trim( unformatted ) > 0 ) {
125 $( this ).val( self.format_amount( unformatted ) );
126 }
127
128 trigger_amount_change_events();
129 };
130
131 /**
132 * Select a donation amount.
133 *
134 * @return void
135 */
136 var on_select_donation_amount = function() {
137 var $li = $( this ).closest( 'li' );
138
139 // Already selected, quit early to prevent focus/change loop
140 if ( $li.hasClass( 'selected' ) ) {
141 return;
142 }
143
144 $li.parents( '.charitable-donation-form' ).find( '.donation-amount.selected' ).removeClass( 'selected' );
145
146 $li.addClass( 'selected' ).find( '[name=donation_amount]' ).prop( 'checked', true );
147
148 if ( $li.hasClass( 'custom-donation-amount' ) ) {
149 $li.find( 'input.custom-donation-input' ).focus();
150 }
151
152 trigger_amount_change_events();
153 };
154
155 /**
156 * Change event handler for payment gateway selector.
157 *
158 * @access private
159 */
160 var on_change_payment_gateway = function() {
161 self.hide_inactive_payment_methods();
162 self.show_active_payment_methods( $(this).val() );
163 };
164
165 /**
166 * Event handler when the "Change" link is clicked for pre-filled donations.
167 */
168 var on_click_change_amount_link = function() {
169 $(this).parent().addClass( 'charitable-hidden' );
170 };
171
172 /**
173 * Submit event handler for donation form.
174 *
175 * @access private
176 */
177 var on_submit = function( event ) {
178 var helper = event.data.helper;
179
180 if ( helper.submit_processing ) {
181 return false;
182 }
183
184 helper.submit_processing = true;
185
186 /* Display the processing spinner and hide the button */
187 helper.show_processing();
188
189 /* Validate the form submission before going further. */
190 if ( false === helper.validate() ) {
191 helper.cancel_processing();
192 return false;
193 }
194
195 /* If processing has been paused, return false now. */
196 if ( false !== helper.pause_processing ) {
197 return false;
198 }
199
200 /* If we're not using AJAX to process the donation further, return now. */
201 if ( 1 !== helper.form.data( 'use-ajax' ) ) {
202 return true;
203 }
204
205 /* Continue on to process the donation. */
206 maybe_process( helper, function() {
207 $body.trigger( 'charitable:form:process', helper );
208 } );
209
210 return false;
211 };
212
213 /**
214 * Check whether there are any asynchronous threads we need to wait for.
215 *
216 * If not, trigger processing. If there are, check again in 500ms.
217 */
218 var maybe_process = function( helper, callback ) {
219 if ( ! helper.waiting() ) {
220 /* Double-check that there are still no errors. */
221 if ( helper.get_errors().length > 0 ) {
222 helper.cancel_processing();
223 } else {
224 callback();
225 }
226
227 return;
228 }
229
230 setTimeout( maybe_process, 500, helper, callback );
231 }
232
233 /**
234 * Process the donation.
235 *
236 * This is a callback for the 'charitable:form:process' event. It's called
237 * after validation has taken place.
238 *
239 * @param object Event
240 * @param object Donation_Form
241 */
242 var process_donation = function( event, helper ) {
243 var data = helper.get_data();
244 var form = helper.form;
245
246 /* Cancel the default Charitable action, but pass it along as the form_action variable */
247 data.action = 'make_donation';
248 data.form_action = data.charitable_action;
249 delete data.charitable_action;
250
251 $.ajax({
252 type: "POST",
253 data: data,
254 dataType: "json",
255 url: CHARITABLE_VARS.ajaxurl,
256 timeout: 0,
257 xhrFields: {
258 withCredentials: true
259 },
260 success: function (response) {
261 $body.trigger( 'charitable:form:processed', [ response, helper ] );
262
263 if ( response.success ) {
264 maybe_process( helper, function() {
265 window.location.href = response.redirect_to;
266 } );
267 }
268 else {
269 helper.cancel_processing( response.errors );
270
271 if ( response.donation_id ) {
272 helper.set_donation_id( response.donation_id );
273 }
274 }
275 }
276 }).fail(function (response, textStatus, errorThrown) {
277 if ( window.console && window.console.log ) {
278 console.log( response );
279 }
280
281 helper.cancel_processing( [ CHARITABLE_VARS.error_unknown ] );
282 });
283
284 return false;
285 }
286
287 /**
288 * Set up event handlers for donation forms.
289 *
290 * @return void
291 */
292 var init = function() {
293 // Init donation amount selection
294 self.form.on( 'click', '.donation-amount', on_select_donation_amount );
295 self.form.on( 'focus', 'input.custom-donation-input', on_focus_custom_amount );
296 self.form.on( 'click', '.charitable-terms-link', on_click_terms );
297
298 // Init currency formatting
299 self.form.on( 'blur', '.custom-donation-input', on_change_custom_donation_amount );
300
301 self.form.find( '.donation-amount input:checked' ).each( function() {
302 $( this ).closest( 'li' ).addClass( 'selected' );
303 });
304
305 if ( self.get_all_payment_methods().length ) {
306 self.hide_inactive_payment_methods();
307 self.form.on( 'change', '#charitable-gateway-selector input[name=gateway]', on_change_payment_gateway );
308 }
309
310 self.form.on( 'click', '.change-donation', on_click_change_amount_link );
311
312 // Handle donation form submission
313 self.form.on( 'submit', {
314 helper : self,
315 }, on_submit );
316
317 // This only fires after the first form instance is initialized.
318 if ( false === CHARITABLE.forms_initialized ) {
319
320 // Process the donation on the 'charitable:form:process' event.
321 $body.on( 'charitable:form:process', process_donation );
322
323 $body.trigger( 'charitable:form:initialize', self );
324
325 CHARITABLE.forms_initialized = true;
326 }
327
328 $body.trigger( 'charitable:form:loaded', self );
329 }
330
331 init();
332 };
333
334 /**
335 * Return a particular input field.
336 *
337 * @param string The field name to retrieve.
338 * @return object
339 */
340 Donation_Form.prototype.get_input = function( field ) {
341 return this.form.find( '[name=' + field + ']' );
342 }
343
344 /**
345 * Return the submitted email address.
346 *
347 * @return string
348 */
349 Donation_Form.prototype.get_email = function() {
350 return this.form.find( '[name=email]' ).val();
351 };
352
353 /**
354 * Returns whether this is a recurring donation.
355 *
356 * @since 1.4.21
357 *
358 * @return boolean
359 */
360 Donation_Form.prototype.is_recurring_donation = function() {
361 var recurring = this.form.find( '[name=recurring_donation]:checked, [name=recurring_donation][type=hidden]' );
362
363 return recurring.length && 'once' !== recurring.val();
364 };
365
366 /**
367 * Get the chosen suggested amount.
368 *
369 * Note that when no option has been selected, or a custom donation amount has been added,
370 * this will return 0.
371 *
372 * @since 1.4.19
373 *
374 * @return float
375 */
376 Donation_Form.prototype.get_suggested_amount = function() {
377 return accounting.unformat(
378 this.form.find( '[name=donation_amount]:checked, input[type=hidden][name=donation_amount]' ).val(),
379 CHARITABLE_VARS.currency_format_decimal_sep
380 );
381 }
382
383 /**
384 * Get the custom amount.
385 *
386 * Note that this will return 0 when no custom amount has been entered or a 0 has been entered.
387 *
388 * @since 1.4.19
389 *
390 * @return float
391 */
392 Donation_Form.prototype.get_custom_amount = function() {
393 var input = this.form.find( '.charitable-donation-options.active input.custom-donation-input,.charitable-donation-options.active input.custom-donation-amount' );
394
395 if ( 0 === input.length ) {
396 input = this.form.find( 'input.custom-donation-input' );
397 }
398
399 return accounting.unformat(
400 input.val(),
401 CHARITABLE_VARS.currency_format_decimal_sep
402 );
403 }
404
405 /**
406 * Get the donation subtotal, which is the amount passed by the donor.
407 *
408 * Notably, this does not include any additional amounts that may be added onto the donation
409 * total, such as processing fees that the donor agrees to pay.
410 *
411 * @since 1.6.7
412 *
413 * @return float
414 */
415 Donation_Form.prototype.get_subtotal = function() {
416 return this.get_suggested_amount() || this.get_custom_amount();
417 };
418
419 /**
420 * Get the submitted amount, taking into account both the custom & suggested donation fields.
421 *
422 * @return float
423 */
424 Donation_Form.prototype.get_amount = function() {
425 this.total = this.get_subtotal();
426
427 this.form.trigger( 'charitable:form:get_amount', this );
428
429 return this.total;
430 };
431
432 /**
433 * Get the submitted amount, taking into account both the custom & suggested donation fields.
434 *
435 * @since 1.6.8
436 *
437 * @param float add
438 * @return void
439 */
440 Donation_Form.prototype.add_amount = function( add ) {
441 this.total += add;
442 };
443
444 /**
445 * Get the submitted amount, taking into account both the custom & suggested donation fields.
446 *
447 * @since 1.6.8
448 *
449 * @param float remove Amount to be removed
450 * @return void
451 */
452 Donation_Form.prototype.remove_amount = function( remove ) {
453 this.total -= remove;
454 };
455
456 /**
457 * Get a description of the donation.
458 *
459 * @return string
460 */
461 Donation_Form.prototype.get_description = function() {
462 return this.form.find( '[name=description]' ).val() || '';
463 };
464
465 /**
466 * Get credit card number.
467 *
468 * @return string
469 */
470 Donation_Form.prototype.get_cc_number = function() {
471 return this.form.find( '#charitable_field_cc_number input' ).val() || '';
472 };
473
474 /**
475 * Get credit card CVC number.
476 *
477 * @return string
478 */
479 Donation_Form.prototype.get_cc_cvc = function() {
480 return this.form.find( '#charitable_field_cc_cvc input' ).val() || '';
481 };
482
483 /**
484 * Get credit card expiry month.
485 *
486 * @return string
487 */
488 Donation_Form.prototype.get_cc_expiry_month = function() {
489 return this.form.find( '#charitable_field_cc_expiration select.month' ).val() || '';
490 };
491
492 /**
493 * Get credit card expiry year.
494 *
495 * @return string
496 */
497 Donation_Form.prototype.get_cc_expiry_year = function() {
498 return this.form.find( '#charitable_field_cc_expiration select.year' ).val() || '';
499 };
500
501 /**
502 * Clear credit card fields.
503
504 *
505 * This is used by gateways that create tokens through Javascript (such as Stripe), to
506 * avoid credit card details hitting the server.
507 *
508 * @return void
509 */
510 Donation_Form.prototype.clear_cc_fields = function() {
511 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' );
512 };
513
514 /**
515 * Return the selected payment method.
516 *
517 * @return string
518 */
519 Donation_Form.prototype.get_payment_method = function() {
520 return this.form.find( '[type=hidden][name=gateway], [name=gateway]:checked' ).val() || '';
521 };
522
523 /**
524 * Return all payment methods.
525 *
526 * @return object
527 */
528 Donation_Form.prototype.get_all_payment_methods = function() {
529 return this.form.find( '#charitable-gateway-selector input[name=gateway]' );
530 }
531
532 /**
533 * Hide inactive payment methods.
534 *
535 * @return void
536 */
537 Donation_Form.prototype.hide_inactive_payment_methods = function() {
538 var active = this.get_payment_method();
539 var fields = this.form.find( '.charitable-gateway-fields[data-gateway!=' + active + ']' );
540
541 fields.hide();
542 fields.find( 'input[required],select[required],textarea[required]' ).attr( 'data-required', 1 ).attr( 'required', false );
543 };
544
545 /**
546 * Show active payment methods.
547 *
548 * @return void
549 */
550 Donation_Form.prototype.show_active_payment_methods = function( active ) {
551 var active = active || this.get_payment_method();
552 var fields = this.form.find( '.charitable-gateway-fields[data-gateway=' + active + ']' );
553
554 fields.show();
555 fields.find( '[data-required]' ).attr( 'required', true );
556 };
557
558 /**
559 * Select a donation amount.
560 *
561 * @param int price
562 * @param string symbol
563 * @return string
564 */
565 Donation_Form.prototype.format_amount = function( price, symbol ){
566 if ( typeof symbol === 'undefined' )
567 symbol = '';
568
569 return accounting.formatMoney( price, {
570 symbol : symbol,
571 decimal : CHARITABLE_VARS.currency_format_decimal_sep,
572 thousand: CHARITABLE_VARS.currency_format_thousand_sep,
573 precision : CHARITABLE_VARS.currency_format_num_decimals,
574 format: CHARITABLE_VARS.currency_format
575 }).trim();
576 };
577
578 /**
579 * Select a donation amount.
580 *
581 * @param int price
582 * @return string
583 */
584 Donation_Form.prototype.unformat_amount = function( price ) {
585 return Math.abs( parseFloat( accounting.unformat( price, CHARITABLE_VARS.currency_format_decimal_sep ) ) );
586 };
587
588 /**
589 * Add an error message.
590 *
591 * @param string message
592 * @return void
593 */
594 Donation_Form.prototype.add_error = function( message ) {
595 this.errors.push( message );
596 };
597
598 /**
599 * Return the errors.
600 *
601 * @return []
602 */
603 Donation_Form.prototype.get_errors = function() {
604 return this.errors;
605 };
606
607 /**
608 * Prints out a nice string describing the errors.
609 *
610 * @return string
611 */
612 Donation_Form.prototype.get_error_message = function() {
613 return this.errors.join( ' ' );
614 };
615
616 /**
617 * Print the errors at the top of the form.
618 *
619 * @param array
620 */
621 Donation_Form.prototype.print_errors = function( errors ) {
622 var e = errors || this.errors,
623 i = 0,
624 count = e.length,
625 output = '',
626 error_notice_donation_display = typeof CHARITABLE_VARS.error_notice_donation_display === 'undefined' ? 'top' : CHARITABLE_VARS.error_notice_donation_display;
627
628 if ( 0 === count ) {
629 return;
630 }
631
632 if ( this.form.find( '.charitable-form-errors' ).length ) {
633 this.form.find( '.charitable-form-errors' ).remove();
634 }
635
636 output += '<div class="charitable-form-errors charitable-notice"><ul class="errors"><li>';
637 output += e.join( '</li><li>' );
638 output += '</li></ul></div>';
639
640 if ( error_notice_donation_display === 'bottom' ) {
641 this.form.append( output );
642 } else {
643 this.form.prepend( output );
644 }
645
646 }
647
648 /**
649 * Clear the errors and remove the printed errors.
650 */
651 Donation_Form.prototype.clear_errors = function() {
652 this.errors = [];
653
654 if ( this.form.find( '.charitable-form-errors' ).length ) {
655 this.form.find( '.charitable-form-errors' ).remove();
656 }
657 }
658
659 /**
660 * Return whether we are waiting for an asynchronous process to finish.
661 *
662 * @since 1.6.9
663 *
664 * @return boolean
665 */
666 Donation_Form.prototype.waiting = function() {
667 return this.pending_processes.length > 0;
668 }
669
670 /**
671 * Add a pending process.
672 *
673 * @since 1.6.9
674 *
675 * @param string Process identifier.
676 * @return int Index of the process.
677 */
678 Donation_Form.prototype.add_pending_process = function( process ) {
679 var index = this.pending_processes.indexOf( process );
680 return -1 === index ? ( this.pending_processes.push( process ) - 1 ) : index;
681 }
682
683 /**
684 * Remove a pending process by index.
685 *
686 * @since 1.6.9
687 *
688 * @param int Index of the process.
689 * @return void
690 */
691 Donation_Form.prototype.remove_pending_process = function( index ) {
692 this.pending_processes.splice( index, 1 );
693 }
694
695 /**
696 * Remove a pending process by process name.
697 *
698 * @since 1.6.17
699 *
700 * @param string Name of the process.
701 * @return void
702 */
703 Donation_Form.prototype.remove_pending_process_by_name = function( process ) {
704 var index = this.pending_processes.indexOf( process );
705 return -1 !== index && this.remove_pending_process( index );
706 }
707
708 /**
709 * Show that the donation form is processing.
710 */
711 Donation_Form.prototype.show_processing = function() {
712 this.form.find( '.charitable-form-processing' ).show();
713 this.form.find( 'button[name="donate"]' ).hide();
714 }
715
716 /**
717 * Hide the processing spinner and show the donate button.
718 */
719 Donation_Form.prototype.hide_processing = function() {
720 this.form.find( '.charitable-form-processing' ).hide();
721 this.form.find( 'button[name="donate"]' ).show();
722
723 this.submit_processing = false;
724 this.pause_processing = false;
725 }
726
727 /**
728 * Scroll to the top of the form.
729 */
730 Donation_Form.prototype.scroll_to_top = function() {
731 if ( this.prevent_scroll_to_top ) {
732 this.prevent_scroll_to_top = false;
733 return;
734 }
735
736 var $modal = this.form.parents( '.charitable-modal' );
737
738 if ( $modal.length ) {
739 $modal.scrollTop( 0 );
740 }
741 else {
742 window.scrollTo( this.form.offset().left, this.form.offset().top );
743 }
744 };
745
746 /**
747 * Cancel donation processing.
748 *
749 * @param array errors Error messages to show.
750 */
751 Donation_Form.prototype.cancel_processing = function( errors ) {
752 this.hide_processing();
753 this.print_errors( errors );
754 var error_notice_donation_display = typeof CHARITABLE_VARS.error_notice_donation_display === 'undefined' ? 'top' : CHARITABLE_VARS.error_notice_donation_display;
755 if ( '' === error_notice_donation_display || 'top' === error_notice_donation_display ) {
756 this.scroll_to_top();
757 }
758
759 this.form.trigger( 'charitable:form:cancelled', this );
760 };
761
762 /**
763 * Set the donation ID.
764 */
765 Donation_Form.prototype.set_donation_id = function( donation_id ) {
766 this.form.find( '[name=ID]' ).val( donation_id );
767 }
768
769 /**
770 * Returns all of the submitted data as key=>value object.
771 *
772 * @return object
773 */
774 Donation_Form.prototype.get_data = function() {
775 return this.form.serializeArray().reduce( function( obj, item ) {
776 if ( '[]' === item.name.slice( -2 ) ) {
777 var name = item.name.slice( 0, -2 );
778
779 if ( ! obj.hasOwnProperty( name ) ) {
780 obj[name] = [];
781 }
782
783 obj[name].push( item.value );
784 } else {
785 obj[ item.name ] = item.value;
786 }
787
788 return obj;
789 }, {} );
790
791 };
792
793 /**
794 * Returns all of the required fields.
795 *
796 * @return object
797 */
798 Donation_Form.prototype.get_required_fields = function() {
799 var fields = this.form.find( '.charitable-fieldset .required-field' ).not( '#charitable-gateway-fields .required-field' ),
800 method = this.get_payment_method();
801
802 if ( '' !== method ) {
803
804 var gateway_fields = this.form.find( '[data-gateway=' + method + '] .required-field' );
805
806 if ( gateway_fields.length ) {
807 fields = $.merge( fields, gateway_fields );
808 }
809
810 }
811
812 return fields;
813 };
814
815 /**
816 * Check whether the submitted amount is greater than the maximum permitted.
817 *
818 * @returns boolean
819 */
820 Donation_Form.prototype.subtotal_exceeds_maximum = function() {
821 return '' !== CHARITABLE_VARS.maximum_donation && this.get_subtotal() > parseFloat( CHARITABLE_VARS.maximum_donation );
822 }
823
824 /**
825 * Make sure that the submitted amount is valid.
826 *
827 * @return boolean
828 */
829 Donation_Form.prototype.is_valid_amount = function() {
830 var minimum = parseFloat( CHARITABLE_VARS.minimum_donation );
831
832 if ( this.subtotal_exceeds_maximum() ) {
833 return false;
834 }
835
836 return minimum > 0 || CHARITABLE_VARS.permit_0_donation
837 ? this.get_subtotal() >= minimum
838 : this.get_subtotal() > minimum;
839 };
840
841 /**
842 * Validate the amount. If not valid, set an error.
843 *
844 * @return boolean
845 */
846 Donation_Form.prototype.validate_amount = function() {
847 if ( false === this.is_valid_amount() ) {
848 if ( this.get_subtotal() > 0 && this.subtotal_exceeds_maximum() ) {
849 this.add_error( CHARITABLE_VARS.error_max_exceeded );
850 } else {
851 this.add_error( CHARITABLE_VARS.error_invalid_amount );
852 }
853 return false;
854 }
855
856 return true;
857 };
858
859 /**
860 * Verify that all required fields are filled out.
861 *
862 * @return boolean
863 */
864 Donation_Form.prototype.validate_required_fields = function() {
865 var has_all_required_fields = true;
866
867 this.get_required_fields().each( function() {
868 if ( '' === $( this ).find( 'input, select, textarea' ).val() ) {
869 has_all_required_fields = false;
870 }
871 });
872
873 if ( ! has_all_required_fields ) {
874 this.add_error( CHARITABLE_VARS.error_required_fields );
875 }
876
877 return has_all_required_fields;
878 };
879
880 /**
881 * Verifies the submission and returns true if it all looks ok.
882 *
883 * @param this.form The submitted form.
884 * @return boolean
885 */
886 Donation_Form.prototype.validate = function() {
887 /* First clear out the errors. */
888 this.clear_errors();
889
890 this.validate_amount();
891
892 this.validate_required_fields();
893
894 this.form.trigger( 'charitable:form:validate', this );
895
896 return this.errors.length === 0;
897 };
898
899 exports.Donation_Form = Donation_Form;
900
901 })( CHARITABLE, jQuery );
902
903 /**
904 * Set up Toggle object.
905 */
906 ( function( exports, $ ){
907 var Toggle = function() {
908
909 /**
910 * Hide toggle target.
911 */
912 var hide_target = function() {
913 get_target( $(this) ).addClass( 'charitable-hidden' );
914 };
915
916 /**
917 * Get the target element.
918 */
919 var get_target = function( $el ) {
920 var target = $el.data('charitable-toggle');
921
922 if ( target[0] !== '.' && target[0] !== '#' ) {
923 target = '#' + target;
924 }
925
926 return $( target );
927 }
928
929 /**
930 * Toggle event handler for any fields with the [data-charitable-toggle] attribute.
931 *
932 * @access private
933 */
934 var on_toggle = function() {
935 var $this = $( this ),
936 $target = get_target( $this );
937
938 $target.toggleClass( 'charitable-hidden', ( function(){
939 if ( $this.is( ':checkbox' ) ) {
940 return ! $this.is( ':checked' );
941 }
942 return false === $target.hasClass( 'charitable-hidden' );
943 })()
944 );
945
946 return false;
947 };
948
949 /**
950 * Initializing function.
951 */
952 var init = function() {
953 $( '[data-charitable-toggle]' ).each( hide_target );
954 };
955
956 // Initialization only required once.
957 $( 'body' ).on( 'click', '[data-charitable-toggle]', on_toggle );
958
959 // Check whether content is being loaded from the session.
960 if ( exports.content_loading ) {
961 var timer = setInterval(function(){
962 if ( false === exports.content_loading ) {
963 init();
964 clearInterval(timer);
965 }
966 }, 500);
967 }
968
969 // Initialization that will be performed everytime
970 return init;
971 }
972
973 exports.Toggle = Toggle();
974
975 })( CHARITABLE, jQuery );
976
977
978 /**
979 * Do a version check.
980 *
981 * @since 1.6.19
982 *
983 * @param string version The version we are comparing with.
984 * @param string compare If compare is left empty, use the Charitable version.
985 * @return integer|boolean
986 */
987 CHARITABLE.VersionCompare = function( version, compare ) {
988 compare = compare || CHARITABLE_VARS.version;
989
990 if ( typeof version + typeof compare != 'stringstring')
991 return false;
992
993 var a = version.split( '.' ),
994 b = compare.split( '.' ),
995 i = 0,
996 len = Math.max( a.length, b.length );
997
998 for ( ; i < len; i++ ) {
999 if ((a[i] && !b[i] && parseInt(a[i]) > 0) || (parseInt(a[i]) > parseInt(b[i]))) {
1000 return 1;
1001 } else if ((b[i] && !a[i] && parseInt(b[i]) > 0) || (parseInt(a[i]) < parseInt(b[i]))) {
1002 return -1;
1003 }
1004 }
1005
1006 return 0;
1007 };
1008
1009 /**
1010 * Set up ResetSuggestedDonations object.
1011 */
1012 ( function( exports, $ ){
1013
1014 CHARITABLE.ResetSuggestedDonations = function() {
1015
1016 console.log( 'ResetSuggestedDonations' );
1017
1018 if ( $('.charitable-donation-options').length = 0 ) {
1019 // the options probably aren't on this page.
1020 return false;
1021 }
1022
1023 $( 'ul.donation-amounts input[type="radio"]' ).each( function() {
1024 if ( $( this ).is(':checked') ) {
1025 $( this ).closest( "li" ).addClass( "selected" );
1026 } else {
1027 $( this ).closest( "li" ).removeClass( "selected" );
1028 }
1029 });
1030
1031 }
1032
1033 })( CHARITABLE, jQuery );
1034
1035 /***
1036 * Finally queue up all the scripts.
1037 */
1038 ( function( $ ) {
1039
1040 /**
1041 * Prevent re-initializing form handlers.
1042 */
1043 CHARITABLE.forms_initialized = false;
1044
1045 $( document ).ready( function() {
1046
1047 $( 'html' ).addClass( 'js' );
1048
1049 var forms = [];
1050
1051 $( '.charitable-donation-form' ).each( function() {
1052 forms.push( new CHARITABLE.Donation_Form( $( this ) ) );
1053 });
1054
1055 CHARITABLE.Toggle();
1056
1057 CHARITABLE.ResetSuggestedDonations();
1058 });
1059
1060 })( jQuery );
1061