PluginProbe
Kit (formerly ConvertKit) – Email Newsletter, Email Marketing, Membership, Subscribers and Landing Pages / 3.4.5
Kit (formerly ConvertKit) – Email Newsletter, Email Marketing, Membership, Subscribers and Landing Pages v3.4.5
3.4.5 3.4.4 3.4.3 3.4.2 3.4.1 3.4.0 3.3.9 3.3.8 3.3.7 3.3.6 3.3.5 3.3.4 3.3.3 3.3.2 3.3.1 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.2.6 2.2.7 2.2.8 2.2.9 All 198 releases
convertkit / resources / frontend / js / restrict-content.js

restrict-content.js in Kit (formerly ConvertKit) – Email Newsletter, Email Marketing, Membership, Subscribers and Landing Pages 3.4.5, at resources/frontend/js/restrict-content.js

395 lines 10.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * Frontend functionality for Restrict Content functionality.
3 *
4 * @since 2.3.7
5 *
6 * @author ConvertKit
7 */
8
9 /**
10 * Register events
11 */
12 document.addEventListener('DOMContentLoaded', function () {
13 // Open modal.
14 document
15 .querySelectorAll('.convertkit-restrict-content-modal-open')
16 .forEach(function (element) {
17 element.addEventListener('click', function (e) {
18 e.preventDefault();
19 convertKitRestrictContentOpenModal();
20 });
21 });
22
23 // Handle modal form submissions.
24 document.addEventListener('submit', function (e) {
25 // Bail if the submission was not for the Restrict Content form.
26 if (!e.target.matches('form#convertkit-restrict-content-form')) {
27 return;
28 }
29
30 e.preventDefault();
31 convertKitRestrictContentFormSubmit(e);
32 });
33
34 // Close modal.
35 // Check if the element exists on screen; if another JS error occurs, OTP submission might not be async,
36 // resulting in the non-JS OTP code screen loading without a modal (i.e. ?convertkit_login=1).
37 const convertKitRestrictContentCloseElement = document.querySelector(
38 '#convertkit-restrict-content-modal-close'
39 );
40 if (convertKitRestrictContentCloseElement !== null) {
41 convertKitRestrictContentCloseElement.addEventListener(
42 'click',
43 function (e) {
44 e.preventDefault();
45 convertKitRestrictContentCloseModal();
46 }
47 );
48 }
49 });
50
51 /**
52 * Handles Restrict Content form submission.
53 *
54 * @since 2.4.2
55 *
56 * @param {Event} e Form submission event.
57 */
58 function convertKitRestrictContentFormSubmit(e) {
59 // Determine where the response should be displayed; either the embedded login
60 // form, or the modal.
61 const container = convertKitRestrictContentContainer(e.target);
62
63 // Disable inputs.
64 container
65 .querySelectorAll(
66 'input[type="text"], input[type="email"], input[type="submit"]'
67 )
68 .forEach(function (input) {
69 input.setAttribute('disabled', 'disabled');
70 });
71
72 // Show loading overlay.
73 convertKitRestrictContentLoading(true);
74
75 // Determine if this is the email or code submission.
76 const isCodeSubmission =
77 container.querySelector('input#convertkit-subscriber-code') !== null;
78
79 if (isCodeSubmission) {
80 // Code submission.
81 convertKitRestrictContentSubscriberVerification(
82 convertkit_restrict_content.nonce,
83 e.target.querySelector('input[name="subscriber_code"]').value,
84 e.target.querySelector('input[name="token"]').value,
85 e.target.querySelector('input[name="convertkit_post_id"]').value,
86 container
87 );
88
89 return false;
90 }
91
92 // Email submission.
93 convertKitRestrictContentSubscriberAuthenticationSendCode(
94 convertkit_restrict_content.nonce,
95 e.target.querySelector('input[name="convertkit_email"]').value,
96 e.target.querySelector('input[name="convertkit_resource_type"]').value,
97 e.target.querySelector('input[name="convertkit_resource_id"]').value,
98 e.target.querySelector('input[name="convertkit_post_id"]').value,
99 convertKitRestrictContentSpamProtectionResponse(e.target),
100 container
101 );
102 }
103
104 /**
105 * Re-enables the inputs in the given container, following a failed request.
106 *
107 * @since 3.4.5
108 *
109 * @param {Object} container Container element.
110 */
111 function convertKitRestrictContentEnableInputs(container) {
112 container
113 .querySelectorAll(
114 'input[type="text"], input[type="email"], input[type="submit"]'
115 )
116 .forEach(function (input) {
117 input.removeAttribute('disabled');
118 });
119 }
120
121 /**
122 * Returns whether the heading should be displayed above the login form, which is
123 * the case when the modal is used.
124 *
125 * @since 3.4.2
126 *
127 * @param {Object} container Container element.
128 * @return {boolean} Display the heading.
129 */
130 function convertKitRestrictContentDisplayHeading(container) {
131 return container.id === 'convertkit-restrict-content-modal-content';
132 }
133
134 /**
135 * Returns the element to display the response in for the given form, which is
136 * either the embedded login form's container, or the modal's content.
137 *
138 * @since 3.4.2
139 *
140 * @param {Object} form Form element.
141 * @return {Object} Container element.
142 */
143 function convertKitRestrictContentContainer(form) {
144 return (
145 form.closest('.convertkit-restrict-content-content') ||
146 document.querySelector('#convertkit-restrict-content-modal-content')
147 );
148 }
149
150 /**
151 * Shows or hides the modal's loading overlay, if the modal is used.
152 *
153 * @since 3.4.2
154 *
155 * @param {boolean} display Display the loading overlay.
156 */
157 function convertKitRestrictContentLoading(display) {
158 const loading = document.querySelector(
159 '#convertkit-restrict-content-modal-loading'
160 );
161
162 if (loading === null) {
163 return;
164 }
165
166 loading.style.display = display ? 'block' : 'none';
167 }
168
169 /**
170 * Returns the spam protection response for the given form, if a spam protection
171 * provider is enabled.
172 *
173 * @since 3.4.2
174 *
175 * @param {Object} form Form element.
176 * @return {string} Spam protection response.
177 */
178 function convertKitRestrictContentSpamProtectionResponse(form) {
179 const field = form.querySelector(
180 '[name="g-recaptcha-response"], [name="cf-turnstile-response"]'
181 );
182
183 return field === null ? '' : field.value;
184 }
185
186 /**
187 * Opens the modal, displaying the content stored within the
188 * #convertkit-restrict-content-modal-content element.
189 *
190 * @since 2.3.8
191 */
192 function convertKitRestrictContentOpenModal() {
193 document.querySelector(
194 '#convertkit-restrict-content-modal-background'
195 ).style.display = 'block';
196 document.querySelector('#convertkit-restrict-content-modal').style.display =
197 'block';
198 }
199
200 /**
201 * Closes the modal.
202 *
203 * @since 2.3.8
204 */
205 function convertKitRestrictContentCloseModal() {
206 document.querySelector(
207 '#convertkit-restrict-content-modal-background'
208 ).style.display = 'none';
209 document.querySelector(
210 '#convertkit-restrict-content-modal-loading'
211 ).style.display = 'none';
212 document.querySelector('#convertkit-restrict-content-modal').style.display =
213 'none';
214 }
215
216 /**
217 * Submits the given email address to the WP REST API kit/v1/restrict-content/subscriber-authentication
218 * endpoint, which will return either:
219 * - the email form view, with an error message e.g. invalid email,
220 * - the code form view, where the user can enter the OTP.
221 *
222 * @since 2.3.8
223 *
224 * @param {string} nonce WordPress nonce.
225 * @param {string} email Email address.
226 * @param {string} resource_type Resource Type (form|tag|product).
227 * @param {string} resource_id Resource ID (Kit Form,Tag or Product ID).
228 * @param {number} post_id WordPress Post ID being viewed / accessed.
229 * @param {string} spam_protection_response Spam protection response.
230 * @param {Object} container Element to display the response in.
231 */
232 function convertKitRestrictContentSubscriberAuthenticationSendCode(
233 nonce,
234 email,
235 resource_type,
236 resource_id,
237 post_id,
238 spam_protection_response,
239 container
240 ) {
241 fetch(convertkit_restrict_content.subscriber_authentication_url, {
242 method: 'POST',
243 headers: {
244 'Content-Type': 'application/json',
245 'X-WP-Nonce': nonce,
246 },
247 body: JSON.stringify({
248 convertkit_email: email,
249 convertkit_resource_type: resource_type,
250 convertkit_resource_id: resource_id,
251 convertkit_post_id: post_id,
252 spam_protection_response,
253 display_heading: convertKitRestrictContentDisplayHeading(container),
254 }),
255 })
256 .then(function (response) {
257 if (convertkit_restrict_content.debug) {
258 console.log(response);
259 }
260
261 return response.json();
262 })
263 .then(function (result) {
264 if (convertkit_restrict_content.debug) {
265 console.log(result);
266 }
267
268 // Output error message if the response contains a code.
269 if (typeof result.code !== 'undefined') {
270 container.innerHTML = result.message;
271 } else {
272 // Output response, which will be either:
273 // - the email form view, with an error message e.g. invalid email,
274 // - the code form view, where the user can enter the OTP.
275 container.innerHTML = result.data;
276 }
277
278 // Hide loading overlay.
279 convertKitRestrictContentLoading(false);
280
281 // Re-bind OTP listener.
282 convertKitRestrictContentOTPField();
283 })
284 .catch(function (error) {
285 if (convertkit_restrict_content.debug) {
286 console.error(error);
287 }
288
289 // Re-enable inputs and hide loading overlay, so the form can be resubmitted.
290 convertKitRestrictContentEnableInputs(container);
291 convertKitRestrictContentLoading(false);
292 });
293 }
294
295 /**
296 * Submits the given email address to the WP REST API kit/v1/restrict-content/subscriber-verification
297 * endpoint, which will return either:
298 * - the code form view, with an error message e.g. invalid code entered,
299 * - the Post's URL, with a `ck-cache-bust` parameter appended, which can then be loaded to show the content.
300 *
301 * @since 2.3.8
302 *
303 * @param {string} nonce WordPress nonce.
304 * @param {string} subscriber_code OTP Subscriber Code.
305 * @param {string} token Subscriber Token.
306 * @param {number} post_id WordPress Post ID being viewed / accessed.
307 * @param {Object} container Element to display the response in.
308 */
309 function convertKitRestrictContentSubscriberVerification(
310 nonce,
311 subscriber_code,
312 token,
313 post_id,
314 container
315 ) {
316 fetch(convertkit_restrict_content.subscriber_verification_url, {
317 method: 'POST',
318 headers: {
319 'Content-Type': 'application/json',
320 'X-WP-Nonce': nonce,
321 },
322 body: JSON.stringify({
323 subscriber_code,
324 token,
325 convertkit_post_id: post_id,
326 }),
327 })
328 .then(function (response) {
329 if (convertkit_restrict_content.debug) {
330 console.log(response);
331 }
332
333 return response.json();
334 })
335 .then(function (result) {
336 if (convertkit_restrict_content.debug) {
337 console.log(result);
338 }
339
340 // If the entered code is invalid, show the response.
341 if (!result.success) {
342 container.innerHTML = result.data;
343
344 // Hide loading overlay.
345 convertKitRestrictContentLoading(false);
346
347 // Re-bind OTP listener.
348 convertKitRestrictContentOTPField();
349 return;
350 }
351
352 // Code entered is valid; load the URL in the response data.
353 window.location = result.url;
354 })
355 .catch(function (error) {
356 if (convertkit_restrict_content.debug) {
357 console.error(error);
358 }
359
360 // Re-enable inputs and hide loading overlay, so the form can be resubmitted.
361 convertKitRestrictContentEnableInputs(container);
362 convertKitRestrictContentLoading(false);
363 });
364 }
365
366 /**
367 * Defines the `--opt-digit` CSS var, so that the background color shifts to the next input
368 * when entering the one time code.
369 *
370 * @since 2.3.8
371 */
372 function convertKitRestrictContentOTPField() {
373 const otpInput = document.querySelector('#convertkit-subscriber-code');
374
375 // Bail if the OTP input isn't displayed on screen.
376 if (otpInput === null) {
377 return;
378 }
379
380 otpInput.addEventListener('input', function () {
381 // Update the --_otp-digit property when the input value changes.
382 otpInput.style.setProperty('--_otp-digit', otpInput.value.length);
383
384 // If all 6 digits have been entered:
385 // - move the caret input to the start,
386 // - blur the input now that all numbers are entered,
387 // - submit the form.
388 if (otpInput.value.length === 6) {
389 otpInput.setSelectionRange(0, 0);
390 otpInput.blur();
391 otpInput.closest('form').requestSubmit();
392 }
393 });
394 }
395