@automattic/newspack-blocks 4.12.3 → 4.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/dist/carousel/view.asset.php +1 -1
- package/dist/carousel/view.js +3 -3
- package/dist/editor.asset.php +1 -1
- package/dist/editor.js +3 -3
- package/dist/modal.asset.php +1 -1
- package/dist/modal.js +1 -1
- package/dist/modalCheckout-rtl.css +1 -1
- package/dist/modalCheckout.asset.php +1 -1
- package/dist/modalCheckout.css +1 -1
- package/dist/modalCheckout.js +1 -1
- package/includes/class-modal-checkout.php +32 -106
- package/includes/class-newspack-blocks.php +4 -0
- package/includes/modal-checkout/class-checkout-data.php +287 -0
- package/includes/tracking/class-data-events.php +5 -140
- package/newspack-blocks.php +3 -2
- package/package.json +5 -5
- package/src/blocks/checkout-button/view.php +8 -42
- package/src/blocks/donate/frontend/class-newspack-blocks-donate-renderer-base.php +2 -2
- package/src/blocks/donate/frontend/class-newspack-blocks-donate-renderer-frequency-based.php +4 -28
- package/src/blocks/donate/frontend/class-newspack-blocks-donate-renderer-tiers-based.php +5 -17
- package/src/modal-checkout/analytics/ga4/checkout-attempt.js +3 -2
- package/src/modal-checkout/analytics/ga4/checkout-success.js +3 -3
- package/src/modal-checkout/analytics/ga4/dismissed.js +3 -3
- package/src/modal-checkout/analytics/ga4/loaded.js +3 -3
- package/src/modal-checkout/analytics/ga4/pagination.js +3 -2
- package/src/modal-checkout/analytics/ga4/utils/index.js +31 -17
- package/src/modal-checkout/analytics/index.js +1 -1
- package/src/modal-checkout/checkout.scss +2 -1
- package/src/modal-checkout/index.js +1 -1
- package/src/modal-checkout/modal.js +232 -253
- package/src/modal-checkout/templates/thankyou.php +3 -6
- package/src/modal-checkout/utils.js +126 -0
- package/vendor/autoload.php +1 -1
- package/vendor/composer/autoload_real.php +4 -4
- package/vendor/composer/autoload_static.php +2 -2
- package/vendor/composer/installed.php +2 -2
|
@@ -10,8 +10,14 @@ import * as a11y from './accessibility.js';
|
|
|
10
10
|
* Internal dependencies
|
|
11
11
|
*/
|
|
12
12
|
import { manageDismissed, manageOpened } from './analytics';
|
|
13
|
-
import {
|
|
14
|
-
|
|
13
|
+
import {
|
|
14
|
+
domReady,
|
|
15
|
+
iframeReady,
|
|
16
|
+
createHiddenInput,
|
|
17
|
+
triggerFormSubmit,
|
|
18
|
+
getCheckoutData,
|
|
19
|
+
getFormattedAmount,
|
|
20
|
+
} from './utils';
|
|
15
21
|
|
|
16
22
|
const CLASS_PREFIX = newspackBlocksModal.newspack_class_prefix;
|
|
17
23
|
const IFRAME_NAME = 'newspack_modal_checkout_iframe';
|
|
@@ -20,11 +26,12 @@ const MODAL_CHECKOUT_ID = 'newspack_modal_checkout';
|
|
|
20
26
|
const MODAL_CLASS_PREFIX = `${ CLASS_PREFIX }__modal`;
|
|
21
27
|
const VARIATON_MODAL_CLASS_PREFIX = 'newspack-blocks__modal-variation';
|
|
22
28
|
|
|
23
|
-
// Track the checkout state for analytics.
|
|
24
|
-
let analyticsData = {};
|
|
25
29
|
// Track the checkout intent to avoid multiple analytics events.
|
|
26
30
|
let inCheckoutIntent = false;
|
|
27
31
|
|
|
32
|
+
// Checkout title.
|
|
33
|
+
let checkoutTitle = newspackBlocksModal.labels.checkout_modal_title;
|
|
34
|
+
|
|
28
35
|
// Close the modal.
|
|
29
36
|
const closeModal = el => {
|
|
30
37
|
if ( el.overlayId && window.newspackReaderActivation?.overlays ) {
|
|
@@ -44,6 +51,11 @@ window.onpageshow = event => {
|
|
|
44
51
|
}
|
|
45
52
|
}
|
|
46
53
|
|
|
54
|
+
// Register the "checkout closed" event.
|
|
55
|
+
const checkoutClosedEvent = new CustomEvent( 'checkout-closed' );
|
|
56
|
+
|
|
57
|
+
window.newspackRAS = window.newspackRAS || [];
|
|
58
|
+
|
|
47
59
|
domReady( () => {
|
|
48
60
|
const modalCheckout = document.querySelector( `#${ MODAL_CHECKOUT_ID }` );
|
|
49
61
|
if ( ! modalCheckout ) {
|
|
@@ -61,44 +73,6 @@ domReady( () => {
|
|
|
61
73
|
iframe.style.height = initialHeight;
|
|
62
74
|
iframe.style.visibility = 'hidden';
|
|
63
75
|
|
|
64
|
-
function iframeReady( cb ) {
|
|
65
|
-
if ( iframe._readyTimer ) {
|
|
66
|
-
clearTimeout( iframe._readyTimer );
|
|
67
|
-
}
|
|
68
|
-
let fired = false;
|
|
69
|
-
|
|
70
|
-
function ready() {
|
|
71
|
-
if ( ! fired ) {
|
|
72
|
-
fired = true;
|
|
73
|
-
clearTimeout( iframe._readyTimer );
|
|
74
|
-
cb.call( this );
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
function readyState() {
|
|
78
|
-
if ( this.readyState === "complete" ) {
|
|
79
|
-
ready.call( this );
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
function checkLoaded() {
|
|
83
|
-
if ( iframe._ready ) {
|
|
84
|
-
clearTimeout( iframe._readyTimer );
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
const doc = iframe.contentDocument || iframe.contentWindow?.document;
|
|
88
|
-
if ( doc && doc.URL.indexOf('about:') !== 0 ) {
|
|
89
|
-
if ( doc?.readyState === 'complete' ) {
|
|
90
|
-
ready.call( doc );
|
|
91
|
-
} else {
|
|
92
|
-
doc.addEventListener( 'DOMContentLoaded', ready );
|
|
93
|
-
doc.addEventListener( 'readystatechange', readyState );
|
|
94
|
-
}
|
|
95
|
-
} else {
|
|
96
|
-
iframe._readyTimer = setTimeout( checkLoaded, 10 );
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
checkLoaded();
|
|
100
|
-
}
|
|
101
|
-
|
|
102
76
|
/**
|
|
103
77
|
* Handle iframe load state.
|
|
104
78
|
*/
|
|
@@ -116,6 +90,9 @@ domReady( () => {
|
|
|
116
90
|
}
|
|
117
91
|
}
|
|
118
92
|
const container = iframe?.contentDocument?.querySelector( `#${ IFRAME_CONTAINER_ID }` );
|
|
93
|
+
if ( ! container ) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
119
96
|
const setModalReady = () => {
|
|
120
97
|
iframeResizeObserver.observe( container );
|
|
121
98
|
if ( spinner.style.display !== 'none' ) {
|
|
@@ -126,41 +103,43 @@ domReady( () => {
|
|
|
126
103
|
}
|
|
127
104
|
iframe._ready = true;
|
|
128
105
|
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
window.newspackRAS.push( function( ras ) {
|
|
135
|
-
ras.dispatchActivity( 'checkout_completed', params );
|
|
136
|
-
} );
|
|
106
|
+
const productDetails = container.querySelector( '#modal-checkout-product-details' );
|
|
107
|
+
const checkoutData = getCheckoutData( productDetails );
|
|
108
|
+
if ( container.checkoutComplete ) {
|
|
109
|
+
// Dispatch a `checkout_completed` activity to RAS.
|
|
110
|
+
window.newspackRAS.push( [ 'checkout_completed', checkoutData ] );
|
|
137
111
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
112
|
+
// Update the newsletters signup modal if it exists.
|
|
113
|
+
if ( window?.newspackReaderActivation?.refreshNewslettersSignupModal && window?.newspackReaderActivation?.getReader()?.email ) {
|
|
114
|
+
window.newspackReaderActivation.refreshNewslettersSignupModal( window.newspackReaderActivation.getReader().email );
|
|
115
|
+
}
|
|
142
116
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
// Store the checkout nonce for later use.
|
|
154
|
-
// We store the nonce from the iframe content window to ensure the nonce was generated for a logged in session
|
|
155
|
-
modalCheckout.checkout_nonce = iframe.contentWindow.newspackBlocksModalCheckout.checkout_nonce;
|
|
156
|
-
}
|
|
117
|
+
// Update the modal title and width to reflect successful transaction.
|
|
118
|
+
setModalSize( 'small' );
|
|
119
|
+
setModalTitle( newspackBlocksModal.labels.thankyou_modal_title );
|
|
120
|
+
setModalReady();
|
|
121
|
+
a11y.trapFocus( modalCheckout.querySelector( `.${ MODAL_CLASS_PREFIX }` ) );
|
|
122
|
+
} else {
|
|
123
|
+
// Make sure the order summary renders the correct text.
|
|
124
|
+
const summaryTextNode = productDetails?.querySelector( 'strong' );
|
|
125
|
+
if ( summaryTextNode ) {
|
|
126
|
+
summaryTextNode.textContent = checkoutData.price_summary;
|
|
157
127
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
128
|
+
|
|
129
|
+
// Revert modal title and width default value.
|
|
130
|
+
setModalSize();
|
|
131
|
+
setModalTitle( checkoutTitle );
|
|
132
|
+
if ( iframe.contentWindow?.newspackBlocksModalCheckout?.checkout_nonce ) {
|
|
133
|
+
// Store the checkout nonce for later use.
|
|
134
|
+
// We store the nonce from the iframe content window to ensure the nonce was generated for a logged in session
|
|
135
|
+
modalCheckout.checkout_nonce = iframe.contentWindow.newspackBlocksModalCheckout.checkout_nonce;
|
|
162
136
|
}
|
|
163
137
|
}
|
|
138
|
+
if ( container.checkoutReady ) {
|
|
139
|
+
setModalReady();
|
|
140
|
+
} else {
|
|
141
|
+
container.addEventListener( 'checkout-ready', setModalReady );
|
|
142
|
+
}
|
|
164
143
|
}
|
|
165
144
|
|
|
166
145
|
iframe.addEventListener( 'load', handleIframeReady );
|
|
@@ -172,11 +151,13 @@ domReady( () => {
|
|
|
172
151
|
* the session for a newly registered reader fails to carry the cart over to
|
|
173
152
|
* the checkout.
|
|
174
153
|
*
|
|
154
|
+
* @param {Object} checkoutData The checkout data.
|
|
155
|
+
*
|
|
175
156
|
* @return {Promise} The promise that resolves with the checkout URL.
|
|
176
157
|
*/
|
|
177
|
-
const generateCart = (
|
|
158
|
+
const generateCart = ( checkoutData ) => {
|
|
178
159
|
return new Promise( ( resolve, reject ) => {
|
|
179
|
-
const urlParams = new URLSearchParams(
|
|
160
|
+
const urlParams = new URLSearchParams( checkoutData );
|
|
180
161
|
urlParams.append( 'action', 'modal_checkout_request' );
|
|
181
162
|
fetch( newspackBlocksModal.ajax_url + '?' + urlParams.toString() )
|
|
182
163
|
.then( res => {
|
|
@@ -196,7 +177,7 @@ domReady( () => {
|
|
|
196
177
|
/**
|
|
197
178
|
* Empty cart via ajax.
|
|
198
179
|
*/
|
|
199
|
-
const emptyCart = () => {
|
|
180
|
+
const emptyCart = async () => {
|
|
200
181
|
const body = new FormData();
|
|
201
182
|
if ( ! newspackBlocksModal.has_unsupported_payment_gateway ) {
|
|
202
183
|
body.append( 'modal_checkout', '1' );
|
|
@@ -204,14 +185,18 @@ domReady( () => {
|
|
|
204
185
|
body.append( 'action', 'abandon_modal_checkout' );
|
|
205
186
|
body.append( '_wpnonce', modalCheckout.checkout_nonce );
|
|
206
187
|
modalCheckout.checkout_nonce = null;
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
188
|
+
try {
|
|
189
|
+
await fetch(
|
|
190
|
+
newspackBlocksModal.ajax_url,
|
|
191
|
+
{
|
|
192
|
+
method: 'POST',
|
|
193
|
+
body,
|
|
194
|
+
}
|
|
195
|
+
);
|
|
196
|
+
} catch ( error ) {
|
|
197
|
+
console.warn( 'Unable to empty cart:', error ); // eslint-disable-line no-console
|
|
198
|
+
}
|
|
199
|
+
};
|
|
215
200
|
|
|
216
201
|
/**
|
|
217
202
|
* Whether reader should be prompted with registration.
|
|
@@ -236,19 +221,45 @@ domReady( () => {
|
|
|
236
221
|
}
|
|
237
222
|
const form = ev.target;
|
|
238
223
|
form.classList.add( 'modal-processing' );
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
224
|
+
|
|
225
|
+
const checkoutData = getCheckoutData( form );
|
|
226
|
+
|
|
227
|
+
const isDonateBlock = checkoutData.newspack_donate;
|
|
228
|
+
if ( isDonateBlock ) {
|
|
229
|
+
const frequency = checkoutData.donation_frequency;
|
|
230
|
+
const donationTiers = [
|
|
231
|
+
...form.querySelectorAll(
|
|
232
|
+
`.donation-tier__${ frequency }, .donation-frequency__${ frequency }`
|
|
233
|
+
)
|
|
234
|
+
];
|
|
235
|
+
const donationTierIndex = checkoutData.donation_tier_index;
|
|
236
|
+
let donationContainer, customAmount;
|
|
237
|
+
if ( donationTierIndex ) {
|
|
238
|
+
donationContainer = donationTiers[ donationTierIndex ];
|
|
239
|
+
customAmount = checkoutData[ `donation_value_${ frequency }` ];
|
|
240
|
+
} else {
|
|
241
|
+
donationContainer = donationTiers[ 0 ];
|
|
242
|
+
customAmount = checkoutData[ `donation_value_${ frequency }_untiered` ];
|
|
243
|
+
}
|
|
244
|
+
const donationData = getCheckoutData( donationContainer );
|
|
245
|
+
for( const key in donationData ) {
|
|
246
|
+
checkoutData[ key ] = donationData[ key ];
|
|
247
|
+
}
|
|
248
|
+
checkoutData.amount = customAmount;
|
|
249
|
+
checkoutData.price_summary = checkoutData.summary_template.replace( '{{PRICE}}', getFormattedAmount( checkoutData.amount, checkoutData.currency ) );
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if ( checkoutData ) {
|
|
253
|
+
Object.keys( checkoutData ).forEach( key => {
|
|
243
254
|
const existingInputs = form.querySelectorAll( 'input[name="' + key + '"]' );
|
|
244
255
|
if ( 0 === existingInputs.length ) {
|
|
245
|
-
form.appendChild( createHiddenInput( key,
|
|
256
|
+
form.appendChild( createHiddenInput( key, checkoutData[ key ] ) );
|
|
246
257
|
}
|
|
247
258
|
} );
|
|
248
259
|
}
|
|
249
|
-
|
|
260
|
+
|
|
250
261
|
// If we're not going from variation picker to checkout, set the modal trigger:
|
|
251
|
-
if ( !
|
|
262
|
+
if ( ! checkoutData.variation_id ) {
|
|
252
263
|
modalTrigger = ev.submitter;
|
|
253
264
|
}
|
|
254
265
|
// Clear any open variation modal.
|
|
@@ -261,9 +272,9 @@ domReady( () => {
|
|
|
261
272
|
} );
|
|
262
273
|
|
|
263
274
|
// Trigger variation modal if variation is not selected.
|
|
264
|
-
if (
|
|
275
|
+
if ( checkoutData.is_variable && ! checkoutData.variation_id ) {
|
|
265
276
|
const variationModal = [ ...variationModals ].find(
|
|
266
|
-
modal => modal.dataset.productId ===
|
|
277
|
+
modal => modal.dataset.productId === checkoutData.product_id
|
|
267
278
|
);
|
|
268
279
|
if ( variationModal ) {
|
|
269
280
|
variationModal
|
|
@@ -277,12 +288,12 @@ domReady( () => {
|
|
|
277
288
|
].forEach( afterSuccessParam => {
|
|
278
289
|
const existingInputs = singleVariationForm.querySelectorAll( 'input[name="' + afterSuccessParam + '"]' );
|
|
279
290
|
if ( 0 === existingInputs.length ) {
|
|
280
|
-
singleVariationForm.appendChild( createHiddenInput( afterSuccessParam,
|
|
291
|
+
singleVariationForm.appendChild( createHiddenInput( afterSuccessParam, checkoutData[ afterSuccessParam ] ) );
|
|
281
292
|
}
|
|
282
293
|
} );
|
|
283
294
|
|
|
284
295
|
// Append the product data hidden inputs.
|
|
285
|
-
const variationData = singleVariationForm.dataset.
|
|
296
|
+
const variationData = singleVariationForm.dataset.checkout;
|
|
286
297
|
if ( variationData ) {
|
|
287
298
|
const data = JSON.parse( variationData );
|
|
288
299
|
Object.keys( data ).forEach( key => {
|
|
@@ -300,34 +311,30 @@ domReady( () => {
|
|
|
300
311
|
openModal( variationModal );
|
|
301
312
|
a11y.trapFocus( variationModal, false );
|
|
302
313
|
|
|
303
|
-
// Set up some GA4 information.
|
|
304
|
-
const formAnalyticsData = form.getAttribute( 'data-product' );
|
|
305
|
-
analyticsData = formAnalyticsData ? JSON.parse( formAnalyticsData ) : {};
|
|
306
|
-
|
|
307
314
|
// For the variation modal we will not set `inCheckoutIntent = true` and
|
|
308
315
|
// let the `opened` event get triggered once the user selects a
|
|
309
316
|
// variation so we track the selection.
|
|
310
317
|
if ( ! inCheckoutIntent ) {
|
|
311
|
-
manageOpened(
|
|
318
|
+
manageOpened( checkoutData );
|
|
312
319
|
}
|
|
313
320
|
|
|
314
321
|
// Append product data info to the modal itself, so we can grab it for manageDismissed:
|
|
315
322
|
document
|
|
316
323
|
.getElementById( 'newspack_modal_checkout' )
|
|
317
|
-
.setAttribute( 'data-
|
|
324
|
+
.setAttribute( 'data-checkout', JSON.stringify( checkoutData ) );
|
|
318
325
|
return;
|
|
319
326
|
}
|
|
320
327
|
}
|
|
321
328
|
|
|
322
329
|
// Populate cart and redirect to checkout if there is an unsupported payment gateway.
|
|
323
330
|
if ( ! isModalCheckout && ! shouldPromptRegistration() ) {
|
|
324
|
-
generateCart(
|
|
331
|
+
generateCart( checkoutData ).then( url => {
|
|
325
332
|
// Remove modal checkout query string and trailing question mark (if any).
|
|
326
333
|
window.location.href = url;
|
|
327
334
|
} );
|
|
328
335
|
// Add some animation to the Checkout Button while the non-modal checkout is loading.
|
|
329
336
|
// For now, don't do it when any popup opens, just when we go right to the checkout page.
|
|
330
|
-
if ( ! (
|
|
337
|
+
if ( ! ( checkoutData.is_variable && ! checkoutData.variation_id ) ) {
|
|
331
338
|
const buttons = form.querySelectorAll( 'button[type=submit]:focus' );
|
|
332
339
|
buttons.forEach( button => {
|
|
333
340
|
button.classList.add( 'non-modal-checkout-loading' );
|
|
@@ -338,141 +345,21 @@ domReady( () => {
|
|
|
338
345
|
return;
|
|
339
346
|
}
|
|
340
347
|
form.classList.remove( 'modal-processing' );
|
|
341
|
-
const isDonateBlock = formData.get( 'newspack_donate' );
|
|
342
|
-
const isCheckoutButtonBlock = formData.get( 'newspack_checkout' );
|
|
343
|
-
// Set up some GA4 information.
|
|
344
|
-
if ( isCheckoutButtonBlock ) { // this fires on the second in-modal variations screen, too
|
|
345
|
-
const formAnalyticsData = form.getAttribute( 'data-product' );
|
|
346
|
-
analyticsData = formAnalyticsData ? JSON.parse( formAnalyticsData ) : {};
|
|
347
|
-
} else if ( isDonateBlock ) {
|
|
348
|
-
// Get donation information and append to the modal checkout for GA4:
|
|
349
|
-
const donationFreq = formData.get( 'donation_frequency' );
|
|
350
|
-
let donationValue = '';
|
|
351
|
-
let productId = '';
|
|
352
|
-
|
|
353
|
-
for ( const key of formData.keys() ) {
|
|
354
|
-
// Find values that match the frequency name, that aren't empty
|
|
355
|
-
if (
|
|
356
|
-
key.indexOf( 'donation_value_' + donationFreq ) >= 0 &&
|
|
357
|
-
'other' !== formData.get( key ) &&
|
|
358
|
-
'' !== formData.get( key )
|
|
359
|
-
) {
|
|
360
|
-
donationValue = formData.get( key );
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
// Get IDs for donation frequencies, and compare them to the selected frequency.
|
|
365
|
-
const freqIds = JSON.parse( formData.get( 'frequency_ids' ) );
|
|
366
|
-
for ( const freq in freqIds ) {
|
|
367
|
-
if ( freq === donationFreq ) {
|
|
368
|
-
productId = freqIds[freq].toString();
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
// Get product information together to be appended to the modal for GA4 events outside of the iframe.
|
|
373
|
-
analyticsData = {
|
|
374
|
-
amount: donationValue,
|
|
375
|
-
action_type: 'donation',
|
|
376
|
-
currency: formData.get( 'donation_currency' ),
|
|
377
|
-
product_id: productId,
|
|
378
|
-
product_type: 'donation',
|
|
379
|
-
recurrence: donationFreq,
|
|
380
|
-
referrer: formData.get( '_wp_http_referer' ),
|
|
381
|
-
};
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
// If the checkout started from a content gate, add the gate ID to the payload.
|
|
385
|
-
const gateId = formData.get( 'memberships_content_gate' );
|
|
386
|
-
if ( gateId ) {
|
|
387
|
-
analyticsData.gate_post_id = gateId;
|
|
388
|
-
}
|
|
389
|
-
const popupId = formData.get( 'newspack_popup_id' );
|
|
390
|
-
if ( popupId ) {
|
|
391
|
-
analyticsData.newspack_popup_id = popupId;
|
|
392
|
-
}
|
|
393
348
|
|
|
394
349
|
// Analytics.
|
|
395
350
|
if ( ! inCheckoutIntent ) {
|
|
396
|
-
manageOpened(
|
|
351
|
+
manageOpened( checkoutData );
|
|
397
352
|
}
|
|
398
353
|
inCheckoutIntent = true;
|
|
399
354
|
|
|
400
355
|
if ( shouldPromptRegistration() ) {
|
|
401
356
|
ev.preventDefault();
|
|
402
|
-
let content = '';
|
|
403
|
-
let price = '0';
|
|
404
|
-
let priceSummary = '';
|
|
405
|
-
|
|
406
|
-
if ( isDonateBlock ) {
|
|
407
|
-
const frequency = formData.get( 'donation_frequency' );
|
|
408
|
-
const donationTiers = form.querySelectorAll(
|
|
409
|
-
`.donation-tier__${ frequency }, .donation-frequency__${ frequency }`
|
|
410
|
-
);
|
|
411
|
-
|
|
412
|
-
if ( donationTiers?.length ) {
|
|
413
|
-
const donationTierIndex = formData.get( 'donation_tier_index' );
|
|
414
357
|
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
const frequencyInputs = form.querySelectorAll(
|
|
419
|
-
`input[name="donation_value_${ frequency }"], input[name="donation_value_${ frequency }_untiered"]`
|
|
420
|
-
);
|
|
421
|
-
|
|
422
|
-
if ( frequencyInputs?.length ) {
|
|
423
|
-
// Handle frequency based donation tiers.
|
|
424
|
-
frequencyInputs.forEach( input => {
|
|
425
|
-
if ( input.checked && input.value !== 'other' ) {
|
|
426
|
-
price = input.value;
|
|
427
|
-
}
|
|
428
|
-
} );
|
|
429
|
-
|
|
430
|
-
donationTiers.forEach( el => {
|
|
431
|
-
const donationData = JSON.parse( el.dataset.product );
|
|
432
|
-
if ( donationData.hasOwnProperty( `donation_price_summary_${ frequency }` ) ) {
|
|
433
|
-
const priceData = donationData[ `donation_price_summary_${ frequency }` ];
|
|
434
|
-
const priceRegex = new RegExp( `(?<=\\D)${ price }(?=\\D)` );
|
|
435
|
-
if ( priceRegex.test( priceData ) ) {
|
|
436
|
-
priceSummary = priceData;
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
if ( price === '0' && priceSummary ) {
|
|
441
|
-
// Replace placeholder price with price input for other.
|
|
442
|
-
let otherPrice = formData.get( `donation_value_${ frequency }_other` );
|
|
443
|
-
|
|
444
|
-
// Fallback to untiered price if other price is not set.
|
|
445
|
-
if ( ! otherPrice ) {
|
|
446
|
-
otherPrice = formData.get( `donation_value_${ frequency }_untiered` );
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
if ( otherPrice ) {
|
|
450
|
-
priceSummary = priceSummary.replace( '0', otherPrice );
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
} );
|
|
454
|
-
}
|
|
455
|
-
} else {
|
|
456
|
-
const donationData = JSON.parse( donationTiers?.[ donationTierIndex ].dataset.product );
|
|
457
|
-
if ( donationData.hasOwnProperty( `donation_price_summary_${ frequency }` ) ) {
|
|
458
|
-
priceSummary = donationData[ `donation_price_summary_${ frequency }` ];
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
} else if ( isCheckoutButtonBlock ) {
|
|
463
|
-
const priceSummaryInput = form.querySelector( 'input[name="product_price_summary"]' );
|
|
464
|
-
|
|
465
|
-
if ( priceSummaryInput ) {
|
|
466
|
-
priceSummary = priceSummaryInput.value;
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
if ( priceSummary ) {
|
|
471
|
-
content = `<div class="order-details-summary ${ CLASS_PREFIX }__box ${ CLASS_PREFIX }__box--text-center"><p><strong>${ priceSummary }</strong></p></div>`;
|
|
472
|
-
}
|
|
358
|
+
const priceSummary = checkoutData.price_summary;
|
|
359
|
+
const content = priceSummary ? `<div class="order-details-summary ${ CLASS_PREFIX }__box ${ CLASS_PREFIX }__box--text-center"><p><strong>${ priceSummary }</strong></p></div>` : '';
|
|
473
360
|
|
|
474
361
|
// Generate cart asynchroneously.
|
|
475
|
-
const cartReq = generateCart(
|
|
362
|
+
const cartReq = generateCart( checkoutData );
|
|
476
363
|
|
|
477
364
|
// Update pending checkout URL.
|
|
478
365
|
cartReq.then( url => {
|
|
@@ -490,10 +377,10 @@ domReady( () => {
|
|
|
490
377
|
// Populate cart and redirect to checkout if there is an unsupported payment gateway.
|
|
491
378
|
if ( ! isModalCheckout ) {
|
|
492
379
|
// Remove modal checkout query string, and trailing question mark (if any).
|
|
493
|
-
generateCart(
|
|
380
|
+
generateCart( checkoutData ).then( window.location.href = url );
|
|
494
381
|
} else {
|
|
495
382
|
const checkoutForm = generateCheckoutPageForm( url );
|
|
496
|
-
|
|
383
|
+
triggerFormSubmit( checkoutForm );
|
|
497
384
|
}
|
|
498
385
|
} )
|
|
499
386
|
.catch( error => {
|
|
@@ -506,9 +393,9 @@ domReady( () => {
|
|
|
506
393
|
},
|
|
507
394
|
onDismiss: () => {
|
|
508
395
|
// Analytics: Track a dismissal event (modal has been manually closed without completing the checkout).
|
|
509
|
-
manageDismissed(
|
|
396
|
+
manageDismissed( checkoutData );
|
|
510
397
|
inCheckoutIntent = false;
|
|
511
|
-
document.getElementById( 'newspack_modal_checkout' ).removeAttribute( 'data-
|
|
398
|
+
document.getElementById( 'newspack_modal_checkout' ).removeAttribute( 'data-checkout' );
|
|
512
399
|
},
|
|
513
400
|
skipSuccess: true,
|
|
514
401
|
skipNewslettersSignup: true,
|
|
@@ -530,7 +417,7 @@ domReady( () => {
|
|
|
530
417
|
// Append product data info to the modal, so we can grab it for GA4 events outside of the iframe.
|
|
531
418
|
document
|
|
532
419
|
.getElementById( 'newspack_modal_checkout' )
|
|
533
|
-
.setAttribute( 'data-
|
|
420
|
+
.setAttribute( 'data-checkout', JSON.stringify( checkoutData ) );
|
|
534
421
|
}
|
|
535
422
|
};
|
|
536
423
|
|
|
@@ -582,6 +469,7 @@ domReady( () => {
|
|
|
582
469
|
iframe.style.height = iframeHeight + 'px';
|
|
583
470
|
}
|
|
584
471
|
} );
|
|
472
|
+
|
|
585
473
|
const closeCheckout = () => {
|
|
586
474
|
const container = iframe?.contentDocument?.querySelector( `#${ IFRAME_CONTAINER_ID }` );
|
|
587
475
|
const afterSuccessUrlInput = container?.querySelector( 'input[name="after_success_url"]' );
|
|
@@ -618,6 +506,8 @@ domReady( () => {
|
|
|
618
506
|
if ( modalTrigger ) {
|
|
619
507
|
modalTrigger.focus();
|
|
620
508
|
}
|
|
509
|
+
|
|
510
|
+
document.dispatchEvent( checkoutClosedEvent );
|
|
621
511
|
}
|
|
622
512
|
|
|
623
513
|
if ( container?.checkoutComplete ) {
|
|
@@ -648,19 +538,25 @@ domReady( () => {
|
|
|
648
538
|
|
|
649
539
|
// Ensure we always reset the modal title and width once the modal closes.
|
|
650
540
|
if ( shouldCloseModal ) {
|
|
541
|
+
checkoutTitle = newspackBlocksModal.labels.checkout_modal_title;
|
|
651
542
|
setModalSize();
|
|
652
|
-
setModalTitle(
|
|
543
|
+
setModalTitle( checkoutTitle );
|
|
653
544
|
}
|
|
654
545
|
} else {
|
|
655
546
|
window?.newspackReaderActivation?.setPendingCheckout?.();
|
|
656
547
|
// Analytics: Track a dismissal event (modal has been manually closed without completing the checkout).
|
|
657
548
|
manageDismissed();
|
|
658
549
|
inCheckoutIntent = false;
|
|
659
|
-
document.getElementById( 'newspack_modal_checkout' ).removeAttribute( 'data-
|
|
550
|
+
document.getElementById( 'newspack_modal_checkout' ).removeAttribute( 'data-checkout' );
|
|
660
551
|
}
|
|
552
|
+
document.removeEventListener( 'keydown', handleKeydown );
|
|
661
553
|
};
|
|
662
554
|
|
|
663
|
-
const openCheckout = () => {
|
|
555
|
+
const openCheckout = ( url ) => {
|
|
556
|
+
if ( url ) {
|
|
557
|
+
iframe.src = url;
|
|
558
|
+
}
|
|
559
|
+
|
|
664
560
|
spinner.style.display = 'flex';
|
|
665
561
|
openModal( modalCheckout );
|
|
666
562
|
modalContent.appendChild( iframe );
|
|
@@ -673,6 +569,8 @@ domReady( () => {
|
|
|
673
569
|
a11y.trapFocus( modalCheckout, iframe );
|
|
674
570
|
|
|
675
571
|
iframeReady( handleIframeReady );
|
|
572
|
+
|
|
573
|
+
document.addEventListener( 'keydown', handleKeydown );
|
|
676
574
|
};
|
|
677
575
|
|
|
678
576
|
const openModal = el => {
|
|
@@ -715,8 +613,6 @@ domReady( () => {
|
|
|
715
613
|
}
|
|
716
614
|
};
|
|
717
615
|
|
|
718
|
-
window.newspackCloseModalCheckout = closeCheckout;
|
|
719
|
-
|
|
720
616
|
/**
|
|
721
617
|
* Handle modal checkout close button.
|
|
722
618
|
*/
|
|
@@ -745,13 +641,13 @@ domReady( () => {
|
|
|
745
641
|
} );
|
|
746
642
|
|
|
747
643
|
/**
|
|
748
|
-
*
|
|
644
|
+
* Escape key handler to close the modal checkout.
|
|
749
645
|
*/
|
|
750
|
-
|
|
646
|
+
const handleKeydown = ev => {
|
|
751
647
|
if ( ev.key === 'Escape' ) {
|
|
752
648
|
closeCheckout();
|
|
753
649
|
}
|
|
754
|
-
}
|
|
650
|
+
};
|
|
755
651
|
|
|
756
652
|
/**
|
|
757
653
|
* Handle modal checkout triggers.
|
|
@@ -771,15 +667,6 @@ domReady( () => {
|
|
|
771
667
|
} );
|
|
772
668
|
} );
|
|
773
669
|
|
|
774
|
-
/**
|
|
775
|
-
* Triggers checkout form submit.
|
|
776
|
-
*
|
|
777
|
-
* @param {HTMLFormElement} form The form element.
|
|
778
|
-
*/
|
|
779
|
-
const triggerCheckout = form => {
|
|
780
|
-
// form.submit does not trigger submit event listener, so we use requestSubmit.
|
|
781
|
-
form.requestSubmit( form.querySelector( 'button[type="submit"]' ) );
|
|
782
|
-
}
|
|
783
670
|
|
|
784
671
|
/**
|
|
785
672
|
* Handle donation form triggers.
|
|
@@ -830,7 +717,7 @@ domReady( () => {
|
|
|
830
717
|
}
|
|
831
718
|
} );
|
|
832
719
|
if ( form ) {
|
|
833
|
-
|
|
720
|
+
triggerFormSubmit( form );
|
|
834
721
|
}
|
|
835
722
|
}
|
|
836
723
|
|
|
@@ -850,7 +737,7 @@ domReady( () => {
|
|
|
850
737
|
if ( variationModal ) {
|
|
851
738
|
const forms = variationModal.querySelectorAll( `form[target="${ IFRAME_NAME }"]` );
|
|
852
739
|
forms.forEach( variationForm => {
|
|
853
|
-
const productData = JSON.parse( variationForm.dataset.
|
|
740
|
+
const productData = JSON.parse( variationForm.dataset.checkout );
|
|
854
741
|
if ( productData?.variation_id === Number( variationId ) ) {
|
|
855
742
|
form = variationForm;
|
|
856
743
|
}
|
|
@@ -863,14 +750,14 @@ domReady( () => {
|
|
|
863
750
|
if ( ! checkoutButtonForm ) {
|
|
864
751
|
return;
|
|
865
752
|
}
|
|
866
|
-
const productData = JSON.parse( checkoutButtonForm.dataset.
|
|
753
|
+
const productData = JSON.parse( checkoutButtonForm.dataset.checkout );
|
|
867
754
|
if ( productData?.product_id === productId ) {
|
|
868
755
|
form = checkoutButtonForm;
|
|
869
756
|
}
|
|
870
757
|
} );
|
|
871
758
|
}
|
|
872
759
|
if ( form ) {
|
|
873
|
-
|
|
760
|
+
triggerFormSubmit( form );
|
|
874
761
|
}
|
|
875
762
|
}
|
|
876
763
|
|
|
@@ -901,11 +788,103 @@ domReady( () => {
|
|
|
901
788
|
const url = window.newspackReaderActivation?.getPendingCheckout?.();
|
|
902
789
|
if ( url ) {
|
|
903
790
|
const form = generateCheckoutPageForm( url );
|
|
904
|
-
|
|
791
|
+
triggerFormSubmit( form );
|
|
905
792
|
}
|
|
906
793
|
}
|
|
907
794
|
// Remove the URL param to prevent re-triggering.
|
|
908
795
|
window.history.replaceState( null, null, window.location.pathname );
|
|
909
796
|
};
|
|
910
797
|
handleModalCheckoutUrlParams();
|
|
798
|
+
|
|
799
|
+
/**
|
|
800
|
+
* Open the modal checkout.
|
|
801
|
+
*
|
|
802
|
+
* @param {Object} options Modal checkout options object.
|
|
803
|
+
* @param {string} options.title The title to set for the modal.
|
|
804
|
+
* @param {string} options.actionType The action type to set for the modal.
|
|
805
|
+
* @param {Object} options.afterSuccess The after success configuration object.
|
|
806
|
+
* @param {Function} options.onCheckoutComplete The callback to call when the checkout is complete.
|
|
807
|
+
* @param {Function} options.onClose The callback to call when the modal is closed.
|
|
808
|
+
*/
|
|
809
|
+
window.newspackOpenModalCheckout = ( {
|
|
810
|
+
title = null,
|
|
811
|
+
actionType = null,
|
|
812
|
+
afterSuccess = {},
|
|
813
|
+
onCheckoutComplete = null,
|
|
814
|
+
onClose = null,
|
|
815
|
+
} ) => {
|
|
816
|
+
/**
|
|
817
|
+
* Title configuration.
|
|
818
|
+
*/
|
|
819
|
+
checkoutTitle = title || newspackBlocksModal.labels.checkout_modal_title;
|
|
820
|
+
// Set the modal title early, even though it may be overridden by the modal content.
|
|
821
|
+
setModalTitle( checkoutTitle );
|
|
822
|
+
|
|
823
|
+
/**
|
|
824
|
+
* Start with the default checkout URL.
|
|
825
|
+
*/
|
|
826
|
+
const url = new URL( newspackBlocksModal.checkout_url );
|
|
827
|
+
|
|
828
|
+
/**
|
|
829
|
+
* Custom action type configuration.
|
|
830
|
+
*/
|
|
831
|
+
if ( actionType ) {
|
|
832
|
+
url.searchParams.set( 'action_type', actionType );
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
/**
|
|
836
|
+
* After success parameters.
|
|
837
|
+
*/
|
|
838
|
+
if ( afterSuccess?.url ) {
|
|
839
|
+
url.searchParams.set( 'after_success_url', afterSuccess.url );
|
|
840
|
+
}
|
|
841
|
+
if ( afterSuccess?.behavior || afterSuccess?.url ) {
|
|
842
|
+
url.searchParams.set( 'after_success_behavior', afterSuccess.behavior || 'custom' );
|
|
843
|
+
}
|
|
844
|
+
if ( afterSuccess?.buttonLabel ) {
|
|
845
|
+
url.searchParams.set( 'after_success_button_label', afterSuccess.buttonLabel );
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
/**
|
|
849
|
+
* On checkout complete callback.
|
|
850
|
+
*/
|
|
851
|
+
if ( onCheckoutComplete ) {
|
|
852
|
+
const handleCheckoutComplete = ( { detail: { action, data } } ) => {
|
|
853
|
+
if ( action !== 'checkout_completed' ) {
|
|
854
|
+
return;
|
|
855
|
+
}
|
|
856
|
+
onCheckoutComplete( data );
|
|
857
|
+
};
|
|
858
|
+
window.newspackRAS.push( ras => {
|
|
859
|
+
ras.on( 'activity', handleCheckoutComplete );
|
|
860
|
+
// Unsubscribe from the checkout complete event when the modal is closed.
|
|
861
|
+
const closeHandler = () => {
|
|
862
|
+
ras.off( 'activity', handleCheckoutComplete );
|
|
863
|
+
document.removeEventListener( 'checkout-closed', closeHandler );
|
|
864
|
+
};
|
|
865
|
+
document.addEventListener( 'checkout-closed', closeHandler );
|
|
866
|
+
} );
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
/**
|
|
870
|
+
* On close callback.
|
|
871
|
+
*/
|
|
872
|
+
if ( onClose ) {
|
|
873
|
+
const closeHandler = () => {
|
|
874
|
+
onClose();
|
|
875
|
+
document.removeEventListener( 'checkout-closed', closeHandler );
|
|
876
|
+
};
|
|
877
|
+
document.addEventListener( 'checkout-closed', closeHandler );
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
/**
|
|
881
|
+
* Open the modal checkout.
|
|
882
|
+
*/
|
|
883
|
+
openCheckout( url.toString() );
|
|
884
|
+
};
|
|
885
|
+
|
|
886
|
+
/**
|
|
887
|
+
* Close the modal checkout.
|
|
888
|
+
*/
|
|
889
|
+
window.newspackCloseModalCheckout = closeCheckout;
|
|
911
890
|
} );
|