# convertkit/3.4.6/resources/frontend/js/convertkit.js

Kit (formerly ConvertKit) – Email Newsletter, Email Marketing, Membership, Subscribers and Landing Pages, version 3.4.6. 256 lines.

- Page: https://pluginprobe.com/plugins/convertkit/3.4.6/code/resources/frontend/js/convertkit.js
- Raw: https://pluginprobe.com/plugins/convertkit/3.4.6/raw/resources/frontend/js/convertkit.js
- Modified: 2026-10-07T09:35:56+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/convertkit/3.4.6/code/resources/frontend/js/convertkit.js#L10-L20`.

```javascript
/**
 * Frontend functionality for subscribers and tags.
 *
 * @since   1.9.6
 *
 * @author ConvertKit
 */

/**
 * Remove the url subscriber_id url param
 *
 * The 'ck_subscriber_id' should only be set on URLs included on
 * links from a ConvertKit email with no other URL parameters.
 * This function removes the parameters so a customer won't share
 * a URL with their subscriber ID in it.
 *
 * @param {string} url URL.
 */
function convertKitRemoveSubscriberIDFromURL(url) {
	// Parse URL.
	const url_object = new URL(url);
	const ck_subscriber_id = url_object.searchParams.get('ck_subscriber_id');

	// If ck_subscriber_id is null, it's not included in the URL.
	// Don't modify the URL.
	if (ck_subscriber_id === null) {
		return;
	}

	// Remove ck_subscriber_id from URL params.
	url_object.searchParams.delete('ck_subscriber_id');

	// Get title and string of parameters.
	const title = document.getElementsByTagName('title')[0].innerHTML;
	let params = url_object.searchParams.toString();

	// Only add '?' if there are parameters.
	if (params.length > 0) {
		params = '?' + params;
	}

	// Update history.
	window.history.replaceState(
		null,
		title,
		url_object.pathname + params + url_object.hash
	);

	// Emit custom event with the removed subscriber ID.
	convertKitEmitCustomEvent('kit_subscriber_id_removed_from_url', {
		id: ck_subscriber_id,
	});
}

/**
 * Emit a custom event with optional detail data.
 *
 * This function creates and dispatches a custom event with the specified
 * event name and detail data.
 *
 * @since 2.5.0
 *
 * @param {string} eventName   The name of the custom event to emit.
 * @param {Object} [detail={}] Optional detail data to include with the event.
 */
function convertKitEmitCustomEvent(eventName, detail) {
	const event = new CustomEvent(eventName, { detail });
	document.dispatchEvent(event);
}

/**
 * Holds the most recently clicked reCAPTCHA submit button, so the reCAPTCHA
 * callback submits that button's form when a page has multiple forms.
 *
 * @since 3.4.6
 */
let convertKitRecaptchaSubmitButton = null;

// Store the clicked reCAPTCHA submit button. This runs in the capture phase,
// before reCAPTCHA's own click handler on the button.
document.addEventListener(
	'click',
	function (e) {
		if (!(e.target instanceof Element)) {
			return;
		}

		const button = e.target.closest(
			'[type="submit"][data-callback="convertKitRecaptchaFormSubmit"]'
		);
		if (button) {
			convertKitRecaptchaSubmitButton = button;
		}
	},
	true
);

/* eslint-disable no-unused-vars */
/**
 * Handles form submissions when reCAPTCHA is enabled.
 *
 * @param {string} token reCAPTCHA token.
 */
function convertKitRecaptchaFormSubmit(token) {
	// Use the clicked submit button, falling back to the first reCAPTCHA submit button on the page.
	const submitButton =
		convertKitRecaptchaSubmitButton ||
		document.querySelector(
			'[type="submit"][data-callback="convertKitRecaptchaFormSubmit"]'
		);

	// Get the parent form of the submit button.
	const form = submitButton.closest('form');

	// Submit the form, using requestSubmit() so any submit event listeners are honored
	// e.g. the Member Content login form, which submits using AJAX.
	form.requestSubmit();
}

// Scope the function to the window object as webpack will wrap everything in a closure,
// resulting in the function not being available globally.
window.convertKitRecaptchaFormSubmit = convertKitRecaptchaFormSubmit;

/**
 * Holds the form and submit button awaiting a Cloudflare Turnstile token.
 *
 * @since 3.4.6
 */
let convertKitTurnstileForm = null;
let convertKitTurnstileSubmitter = null;

// Generate a Cloudflare Turnstile token when a form is submitted, before other submit listeners run.
document.addEventListener(
	'submit',
	function (e) {
		const form = e.target;
		if (!(form instanceof HTMLFormElement)) {
			return;
		}

		// Bail if the form doesn't contain a Turnstile widget.
		const widget = form.querySelector(
			'.cf-turnstile[data-callback="convertKitTurnstileFormSubmit"]'
		);
		if (!widget) {
			return;
		}

		// Permit the submission if the form has a token, resetting the widget as each token can only be used once.
		const response = form.querySelector('[name="cf-turnstile-response"]');
		if (response !== null && response.value !== '') {
			setTimeout(function () {
				convertKitTurnstileReset(widget);
			}, 0);
			return;
		}

		// Permit the submission if the Turnstile script isn't loaded, so the server returns an error.
		if (typeof window.turnstile === 'undefined') {
			return;
		}

		// Prevent the submission until a token is generated.
		e.preventDefault();
		e.stopImmediatePropagation();

		// Store the form and submit button, so the callback submits this form.
		convertKitTurnstileForm = form;
		convertKitTurnstileSubmitter = e.submitter || null;

		// Render the widget if it was inserted after the page loaded e.g. following a failed Member Content login.
		if (response === null) {
			window.turnstile.render(widget);
		}

		// Generate a token.
		window.turnstile.execute(widget);
	},
	true
);

/**
 * Resets the given Cloudflare Turnstile widget, so a new token is generated on the
 * next submission.
 *
 * @since 3.4.6
 *
 * @param {Element} widget Turnstile widget.
 */
function convertKitTurnstileReset(widget) {
	if (typeof window.turnstile === 'undefined' || !widget.isConnected) {
		return;
	}

	window.turnstile.reset(widget);
}

/**
 * Submits the form awaiting a Cloudflare Turnstile token, once Turnstile has
 * generated the token.
 *
 * Turnstile populates a hidden `cf-turnstile-response` input inside the form,
 * which is included when the form is submitted.
 *
 * @param {string} token Turnstile response token.
 */
function convertKitTurnstileFormSubmit(token) {
	// Bail if no form is awaiting a token.
	if (convertKitTurnstileForm === null) {
		return;
	}

	const form = convertKitTurnstileForm;
	const submitter = convertKitTurnstileSubmitter;
	convertKitTurnstileForm = null;
	convertKitTurnstileSubmitter = null;

	// Submit the form, using requestSubmit() so any submit event listeners are honored
	// e.g. the Member Content login form, which submits using AJAX.
	if (submitter !== null && submitter.form === form) {
		form.requestSubmit(submitter);
	} else {
		form.requestSubmit();
	}
}

// Scope the function to the window object as webpack will wrap everything in a closure,
// resulting in the function not being available globally.
window.convertKitTurnstileFormSubmit = convertKitTurnstileFormSubmit;
/* eslint-enable no-unused-vars */

/**
 * Register events on frontend.
 *
 * @since   3.2.0
 */
if (typeof convertkit !== 'undefined') {
	document.addEventListener('DOMContentLoaded', function () {
		// Removes `ck_subscriber_id` from the URI.
		convertKitRemoveSubscriberIDFromURL(window.location.href);

		// Set a cookie if any scripts with data-kit-limit-per-session attribute exist.
		if (
			document.querySelectorAll('script[data-kit-limit-per-session="1"]')
				.length > 0
		) {
			document.cookie = 'ck_non_inline_form_displayed=1; path=/';
			if (convertkit.debug) {
				console.log(
					'Set `ck_non_inline_form_displayed` cookie for non-inline form limit'
				);
			}
		}
	});
}

```
