PageSourceSearch

https://thepointmag.com/wp-content/plugins/leaky-paywall-recurring-payments/include/js/script.js?ver=1.6.4

js thepointmag.com collected 2026-09-24 08:49:38 UTC 10,130 bytes, 236 lines download raw bytes

1( function( $ )  {
2
3
4	$(document).ready( function() {
5        $('.manage-billing-link a').click(function(e) {
6            e.preventDefault();
7
8            $('.manage-billing-link').hide();
9
10            if ( $('#update-authorizenet-card-form').length > 0 ) {
11                $('#update-authorizenet-card-form').show();
12            } else {
13                $('#update-card-form').show();
14            }
15
16        });
17	});
18
19
20
21	$(window).on('load', function() {
22
23        if ( $('.leaky-paywall-profile-subscription-details').length > 0 || $('#leaky-paywall-account-wrapper').length > 0 ) {
24
25            console.log('on account');
26
27            lpWaitForElm('#leaky-paywall-cancel-modal').then((elm) => {
28
29                const $modal = $('#leaky-paywall-cancel-modal');
30                const retentionEnabled = $modal.attr('data-retention-enabled') === '1';
31                let selectedReason = '';
32                let retentionOfferShown = false;
33
34                // Stop clicks inside the container from bubbling up to MicroModal's
35                // overlay close handler. Clicks on the backdrop still close the modal.
36                $modal.on('click', '.modal__container', function(e) {
37                    if ( ! $(e.target).closest('[data-micromodal-close]').length ) {
38                        e.stopPropagation();
39                    }
40                });
41
42                function showStep(stepName) {
43                    $modal.find('.leaky-paywall-cancel-step').hide();
44                    $modal.find('.leaky-paywall-cancel-step[data-step="' + stepName + '"]').show();
45                }
46
47                // Toggle the "Other" free text field.
48                $modal.on('change', '.cancel-reason-select', function() {
49                    if ( $(this).val() === '__other__' ) {
50                        $modal.find('.cancel-reason-other-row').show();
51                    } else {
52                        $modal.find('.cancel-reason-other-row').hide();
53                    }
54                });
55
56                // Step 1 "Continue" -> go to offer or confirm.
57                $modal.on('click', '.leaky-paywall-cancel-continue-button', function(e) {
58                    e.preventDefault();
59
60                    const $select = $modal.find('.cancel-reason-select');
61                    if ( $select.length > 0 ) {
62                        let value = $select.val() || '';
63                        if ( value === '__other__' ) {
64                            const otherText = ($modal.find('.cancel-reason-other').val() || '').trim();
65                            value = otherText || 'Other';
66                        }
67                        selectedReason = value;
68                    } else {
69                        selectedReason = '';
70                    }
71
72                    // Store the reason on the final form for submission.
73                    $modal.find('.cancel-reason-input').val(selectedReason);
74
75                    if ( retentionEnabled ) {
76                        retentionOfferShown = true;
77                        showStep('offer');
78                    } else {
79                        showStep('confirm');
80                    }
81                });
82
83                // Retention offer: Accept.
84                $modal.on('click', '.leaky-paywall-retention-accept-button', function(e) {
85                    e.preventDefault();
86                    const $btn = $(this);
87                    const originalText = $btn.text();
88                    $btn.attr('disabled', true).text('Processing...');
89                    $('#leaky-paywall-retention-errors').html('').hide();
90
91                    $.post(leaky_paywall_recurring.ajaxurl, {
92                        action: 'leaky_paywall_retention_offer_accept',
93                        _ajax_nonce: leaky_paywall_recurring._ajax_nonce,
94                    }, function(resp) {
95                        if ( resp.status === 'success' ) {
96                            $('#leaky-paywall-cancel-modal .modal__container').html(resp.html);
97                        } else {
98                            $btn.attr('disabled', false).text(originalText);
99                            $('#leaky-paywall-retention-errors').html('<p>' + (resp.message || 'An error occurred.') + '</p>').show();
100                        }
101                    });
102                });
103
104                // Retention offer: Decline -> proceed to confirm.
105                $modal.on('click', '.leaky-paywall-retention-decline-button', function(e) {
106                    e.preventDefault();
107                    showStep('confirm');
108                });
109
110                // Final cancel form submission.
111                $modal.on('submit', '#leaky-paywall-cancel-form', function(e) {
112                    e.preventDefault();
113
114                    $('#leaky-paywall-cancel-errors').html('').hide();
115                    $('#leaky-paywall-cancel-form input[type="submit"]').attr('disabled', true).val('Processing...');
116
117                    let data = {
118                        action: 'leaky_paywall_cancel_form_process',
119                        reason: selectedReason,
120                        retention_offer_shown: retentionOfferShown ? '1' : '0',
121                        _ajax_nonce: leaky_paywall_recurring._ajax_nonce,
122                    };
123
124                    $.post(leaky_paywall_recurring.ajaxurl, data, function(resp) {
125                        if ( resp.message == 'success' ) {
126                            $('#leaky-paywall-cancel-modal .modal__container').html(resp.html);
127                        } else {
128                            $('#leaky-paywall-cancel-form input[type="submit"]').attr('disabled', false).val('Please cancel my subscription');
129                            $('#leaky-paywall-cancel-errors').html(resp.html).show();
130                        }
131                    });
132                });
133
134            });
135
136            let stripe = Stripe(leaky_paywall_script_ajax.stripe_pk);
137            var elements = stripe.elements();
138            var cardElement = elements.create('card');
139
140            if ( $('#update-card-element').length > 0  ) {
141                 cardElement.mount('#update-card-element');
142
143                var form = document.getElementById('update-card-form');
144                form.addEventListener('submit', function(event) {
145                    event.preventDefault();
146
147                    var $errorBox = $('#lp-card-errors');
148                    var $submitBtn = $(form).find('button[type="submit"], input[type="submit"]').first();
149                    var isButtonEl = $submitBtn.is('button');
150                    var originalBtnText = isButtonEl ? $submitBtn.html() : $submitBtn.val();
151
152                    function restoreButton() {
153                        if ( isButtonEl ) {
154                            $submitBtn.prop('disabled', false).html(originalBtnText);
155                        } else {
156                            $submitBtn.prop('disabled', false).val(originalBtnText);
157                        }
158                    }
159
160                    function showError(message) {
161                        $errorBox.html('<p class="leaky_paywall_message error">' + $('<div/>').text(message).html() + '</p>');
162                    }
163
164                    // Reset previous error + lock the submit button while in flight.
165                    $errorBox.html('');
166                    if ( isButtonEl ) {
167                        $submitBtn.prop('disabled', true).html('Processing...');
168                    } else {
169                        $submitBtn.prop('disabled', true).val('Processing...');
170                    }
171
172                    stripe.createPaymentMethod({
173                        type: 'card',
174                        card: cardElement,
175                    }).then(function(result) {
176                        if (result.error) {
177                            // Surface Stripe's error message to the user (declined
178                            // card, expired card, missing CVC, test card in live
179                            // mode, etc.). Without this the form silently swallows
180                            // the error and the user gets no feedback — they think
181                            // the button is broken.
182                            showError(result.error.message || 'Your card could not be processed. Please try again.');
183                            restoreButton();
184                            return;
185                        }
186
187                        var paymentMethodId = result.paymentMethod.id;
188                        var hiddenInput = document.createElement('input');
189                        hiddenInput.setAttribute('type', 'hidden');
190                        hiddenInput.setAttribute('name', 'payment_method_id');
191                        hiddenInput.setAttribute('value', paymentMethodId);
192                        form.appendChild(hiddenInput);
193
194                        // Hand off to the server. The browser navigates away,
195                        // so no need to restore the button here.
196                        form.submit();
197                    }).catch(function(err) {
198                        // Network failure or Stripe.js exception. Same UX as a
199                        // card error so the user has a path forward.
200                        showError('Something went wrong while processing your card. Please try again.');
201                        restoreButton();
202                        if ( window.console && console.error ) {
203                            console.error(err);
204                        }
205                    });
206                });
207            }
208
209
210        }
211
212
213
214	});
215
216    function lpWaitForElm(selector) {
217    return new Promise(resolve => {
218        if (document.querySelector(selector)) {
219            return resolve(document.querySelector(selector));
220        }
221
222        const observer = new MutationObserver(mutations => {
223            if (document.querySelector(selector)) {
224                resolve(document.querySelector(selector));
225                observer.disconnect();
226            }
227        });
228
229        observer.observe(document.body, {
230            childList: true,
231            subtree: true
232        });
233    });
234}
235
236})( jQuery );

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.