PluginProbe
Kit (formerly ConvertKit) – Email Newsletter, Email Marketing, Membership, Subscribers and Landing Pages / 3.4.6
Kit (formerly ConvertKit) – Email Newsletter, Email Marketing, Membership, Subscribers and Landing Pages v3.4.6
3.4.6 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 All 199 releases
convertkit / resources / frontend / js / convertkit.js

convertkit.js in Kit (formerly ConvertKit) – Email Newsletter, Email Marketing, Membership, Subscribers and Landing Pages 3.4.6, at resources/frontend/js/convertkit.js

256 lines 7.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * Frontend functionality for subscribers and tags.
3 *
4 * @since 1.9.6
5 *
6 * @author ConvertKit
7 */
8
9 /**
10 * Remove the url subscriber_id url param
11 *
12 * The 'ck_subscriber_id' should only be set on URLs included on
13 * links from a ConvertKit email with no other URL parameters.
14 * This function removes the parameters so a customer won't share
15 * a URL with their subscriber ID in it.
16 *
17 * @param {string} url URL.
18 */
19 function convertKitRemoveSubscriberIDFromURL(url) {
20 // Parse URL.
21 const url_object = new URL(url);
22 const ck_subscriber_id = url_object.searchParams.get('ck_subscriber_id');
23
24 // If ck_subscriber_id is null, it's not included in the URL.
25 // Don't modify the URL.
26 if (ck_subscriber_id === null) {
27 return;
28 }
29
30 // Remove ck_subscriber_id from URL params.
31 url_object.searchParams.delete('ck_subscriber_id');
32
33 // Get title and string of parameters.
34 const title = document.getElementsByTagName('title')[0].innerHTML;
35 let params = url_object.searchParams.toString();
36
37 // Only add '?' if there are parameters.
38 if (params.length > 0) {
39 params = '?' + params;
40 }
41
42 // Update history.
43 window.history.replaceState(
44 null,
45 title,
46 url_object.pathname + params + url_object.hash
47 );
48
49 // Emit custom event with the removed subscriber ID.
50 convertKitEmitCustomEvent('kit_subscriber_id_removed_from_url', {
51 id: ck_subscriber_id,
52 });
53 }
54
55 /**
56 * Emit a custom event with optional detail data.
57 *
58 * This function creates and dispatches a custom event with the specified
59 * event name and detail data.
60 *
61 * @since 2.5.0
62 *
63 * @param {string} eventName The name of the custom event to emit.
64 * @param {Object} [detail={}] Optional detail data to include with the event.
65 */
66 function convertKitEmitCustomEvent(eventName, detail) {
67 const event = new CustomEvent(eventName, { detail });
68 document.dispatchEvent(event);
69 }
70
71 /**
72 * Holds the most recently clicked reCAPTCHA submit button, so the reCAPTCHA
73 * callback submits that button's form when a page has multiple forms.
74 *
75 * @since 3.4.6
76 */
77 let convertKitRecaptchaSubmitButton = null;
78
79 // Store the clicked reCAPTCHA submit button. This runs in the capture phase,
80 // before reCAPTCHA's own click handler on the button.
81 document.addEventListener(
82 'click',
83 function (e) {
84 if (!(e.target instanceof Element)) {
85 return;
86 }
87
88 const button = e.target.closest(
89 '[type="submit"][data-callback="convertKitRecaptchaFormSubmit"]'
90 );
91 if (button) {
92 convertKitRecaptchaSubmitButton = button;
93 }
94 },
95 true
96 );
97
98 /* eslint-disable no-unused-vars */
99 /**
100 * Handles form submissions when reCAPTCHA is enabled.
101 *
102 * @param {string} token reCAPTCHA token.
103 */
104 function convertKitRecaptchaFormSubmit(token) {
105 // Use the clicked submit button, falling back to the first reCAPTCHA submit button on the page.
106 const submitButton =
107 convertKitRecaptchaSubmitButton ||
108 document.querySelector(
109 '[type="submit"][data-callback="convertKitRecaptchaFormSubmit"]'
110 );
111
112 // Get the parent form of the submit button.
113 const form = submitButton.closest('form');
114
115 // Submit the form, using requestSubmit() so any submit event listeners are honored
116 // e.g. the Member Content login form, which submits using AJAX.
117 form.requestSubmit();
118 }
119
120 // Scope the function to the window object as webpack will wrap everything in a closure,
121 // resulting in the function not being available globally.
122 window.convertKitRecaptchaFormSubmit = convertKitRecaptchaFormSubmit;
123
124 /**
125 * Holds the form and submit button awaiting a Cloudflare Turnstile token.
126 *
127 * @since 3.4.6
128 */
129 let convertKitTurnstileForm = null;
130 let convertKitTurnstileSubmitter = null;
131
132 // Generate a Cloudflare Turnstile token when a form is submitted, before other submit listeners run.
133 document.addEventListener(
134 'submit',
135 function (e) {
136 const form = e.target;
137 if (!(form instanceof HTMLFormElement)) {
138 return;
139 }
140
141 // Bail if the form doesn't contain a Turnstile widget.
142 const widget = form.querySelector(
143 '.cf-turnstile[data-callback="convertKitTurnstileFormSubmit"]'
144 );
145 if (!widget) {
146 return;
147 }
148
149 // Permit the submission if the form has a token, resetting the widget as each token can only be used once.
150 const response = form.querySelector('[name="cf-turnstile-response"]');
151 if (response !== null && response.value !== '') {
152 setTimeout(function () {
153 convertKitTurnstileReset(widget);
154 }, 0);
155 return;
156 }
157
158 // Permit the submission if the Turnstile script isn't loaded, so the server returns an error.
159 if (typeof window.turnstile === 'undefined') {
160 return;
161 }
162
163 // Prevent the submission until a token is generated.
164 e.preventDefault();
165 e.stopImmediatePropagation();
166
167 // Store the form and submit button, so the callback submits this form.
168 convertKitTurnstileForm = form;
169 convertKitTurnstileSubmitter = e.submitter || null;
170
171 // Render the widget if it was inserted after the page loaded e.g. following a failed Member Content login.
172 if (response === null) {
173 window.turnstile.render(widget);
174 }
175
176 // Generate a token.
177 window.turnstile.execute(widget);
178 },
179 true
180 );
181
182 /**
183 * Resets the given Cloudflare Turnstile widget, so a new token is generated on the
184 * next submission.
185 *
186 * @since 3.4.6
187 *
188 * @param {Element} widget Turnstile widget.
189 */
190 function convertKitTurnstileReset(widget) {
191 if (typeof window.turnstile === 'undefined' || !widget.isConnected) {
192 return;
193 }
194
195 window.turnstile.reset(widget);
196 }
197
198 /**
199 * Submits the form awaiting a Cloudflare Turnstile token, once Turnstile has
200 * generated the token.
201 *
202 * Turnstile populates a hidden `cf-turnstile-response` input inside the form,
203 * which is included when the form is submitted.
204 *
205 * @param {string} token Turnstile response token.
206 */
207 function convertKitTurnstileFormSubmit(token) {
208 // Bail if no form is awaiting a token.
209 if (convertKitTurnstileForm === null) {
210 return;
211 }
212
213 const form = convertKitTurnstileForm;
214 const submitter = convertKitTurnstileSubmitter;
215 convertKitTurnstileForm = null;
216 convertKitTurnstileSubmitter = null;
217
218 // Submit the form, using requestSubmit() so any submit event listeners are honored
219 // e.g. the Member Content login form, which submits using AJAX.
220 if (submitter !== null && submitter.form === form) {
221 form.requestSubmit(submitter);
222 } else {
223 form.requestSubmit();
224 }
225 }
226
227 // Scope the function to the window object as webpack will wrap everything in a closure,
228 // resulting in the function not being available globally.
229 window.convertKitTurnstileFormSubmit = convertKitTurnstileFormSubmit;
230 /* eslint-enable no-unused-vars */
231
232 /**
233 * Register events on frontend.
234 *
235 * @since 3.2.0
236 */
237 if (typeof convertkit !== 'undefined') {
238 document.addEventListener('DOMContentLoaded', function () {
239 // Removes `ck_subscriber_id` from the URI.
240 convertKitRemoveSubscriberIDFromURL(window.location.href);
241
242 // Set a cookie if any scripts with data-kit-limit-per-session attribute exist.
243 if (
244 document.querySelectorAll('script[data-kit-limit-per-session="1"]')
245 .length > 0
246 ) {
247 document.cookie = 'ck_non_inline_form_displayed=1; path=/';
248 if (convertkit.debug) {
249 console.log(
250 'Set `ck_non_inline_form_displayed` cookie for non-inline form limit'
251 );
252 }
253 }
254 });
255 }
256