PageSourceSearch

https://www.joinonelove.org/wp-content/plugins/sfwd-lms/themes/ld30/assets/js/learndash.js?ver=5.1.10

js joinonelove.org collected 2026-09-24 08:49:49 UTC 54,994 bytes, 2,031 lines download raw bytes

1/* eslint-disable no-var */
2jQuery(function ($) {
3	var hash = window.location.hash;
4	const expandableSidebarHeightPadding = 50;
5
6	learndashFocusModeSidebarAutoScroll();
7
8	initLoginModal();
9	if ('#login' == hash) {
10		openLoginModal();
11	}
12
13	if ('undefined' !== typeof ldGetUrlVars().login) {
14		var loginStatus = ldGetUrlVars().login;
15
16		if ('failed' == loginStatus) {
17			openLoginModal();
18		}
19	}
20
21	if ('undefined' !== typeof ldGetUrlVars()['ld-topic-page']) {
22		var topicPage = ldGetUrlVars()['ld-topic-page'];
23		var topicIds = topicPage.split('-');
24		var topicId = Object.values(topicIds)[0];
25
26		var lesson = $('#ld-expand-' + topicId);
27		var button = $(lesson).find('.ld-expand-button');
28
29		ldToggleExpandableElement(button, true);
30
31		$('html, body').animate(
32			{
33				scrollTop: $(lesson).offset().top,
34			},
35			500
36		);
37	}
38
39	// a[href="#login"] is for backwards compatibility with potentially outdated templates.
40	$('body').on(
41		'click',
42		'a[href="#login"], button[data-ld-login-modal-trigger]',
43		function (e) {
44			e.preventDefault();
45			openLoginModal();
46		}
47	);
48
49	// By default, the element is not interactable with keyboard. We make the keypress event for space and enter key to close the login modal for accessibility.
50	$('body').on('keypress', '.ld-modal-closer', function (e) {
51		if (13 === e.keyCode || 32 === e.keyCode) {
52			e.preventDefault();
53			closeLoginModal();
54		}
55	});
56
57	$('body').on('click', '.ld-modal-closer', function (e) {
58		e.preventDefault();
59		closeLoginModal();
60	});
61
62	$('body').on('click', '#ld-comments-post-button', function (e) {
63		$(this).addClass('ld-open');
64		$('#ld-comments-form').removeClass('ld-collapsed');
65		$('textarea#comment').focus();
66	});
67
68	// Close modal if clicking away
69	/*
70	$('body').on('click', function(e) {
71		if ($('.learndash-wrapper').hasClass('ld-modal-open')) {
72			if ( ! $(e.target).parents('.ld-modal').length && (! $(e.target).is('a'))) {
73				closeLoginModal();
74			}
75		}
76	});
77	*/
78
79	// Close modal on Esc key
80	$(document).on('keyup', function (e) {
81		if (27 === e.keyCode) {
82			closeLoginModal();
83		}
84	});
85
86	$('.learndash-wrapper').on(
87		'click',
88		'a.user_statistic',
89		learndash_ld30_show_user_statistic
90	);
91
92	focusMobileCheck();
93	focusMobileResizeCheck();
94
95	disableFocusTrap();
96
97	$('body').on('click', '.ld-focus-sidebar-trigger', function (e) {
98		if ($('.ld-focus').hasClass('ld-focus-sidebar-collapsed')) {
99			openFocusSidebar();
100		} else {
101			closeFocusSidebar();
102		}
103	});
104
105	$('body').on('click', '.ld-trigger-mobile-nav', function (e) {
106		e.preventDefault();
107		if ($('.ld-focus').hasClass('ld-focus-sidebar-collapsed')) {
108			openFocusSidebar();
109		} else {
110			closeFocusSidebar();
111		}
112	});
113
114	$('.ld-js-register-account').on('click', function (e) {
115		e.preventDefault();
116
117		$('.ld-login-modal-register .ld-modal-text').slideUp('slow');
118		$('.ld-login-modal-register .ld-alert').slideUp('slow');
119		$(this).slideUp('slow', function () {
120			$('#ld-user-register').slideDown('slow');
121		});
122	});
123
124	// If registration login link filter not defined, allow to replace the register form with login form
125	ldRegistrationLinkInit();
126	ldRegistrationLinkInitModern();
127
128	var windowWidth = $(window).width();
129
130	// Ensure that tooltips are positioned properly after screen size changes.
131	$(window).on('resize orientationchange', function () {
132		const resizeTimer = setTimeout(() => {
133			const newWidth = $(window).width();
134
135			if (newWidth === windowWidth) {
136				return;
137			}
138
139			windowWidth = newWidth;
140
141			// Wait one more animation frame after debounce to let layout settle.
142			window.requestAnimationFrame(() => {
143				initTooltips();
144				focusMobileResizeCheck();
145				clearTimeout(resizeTimer);
146			});
147		}, 150);
148	});
149
150	if ($('.ld-course-status-content').length) {
151		var tallest = 0;
152
153		$('.ld-course-status-content').each(function () {
154			if ($(this).height() > tallest) {
155				tallest = $(this).height();
156			}
157		});
158
159		$('.ld-course-status-content').height(tallest);
160	}
161
162	function focusMobileCheck() {
163		if (1024 > $(window).width()) {
164			closeFocusSidebarPageLoad();
165		}
166	}
167
168	/**
169	 * Toggles the focus sidebar based on the window width.
170	 *
171	 * @since 3.2.0
172	 * @since 4.21.5 Now also toggles aria-modal attribute.
173	 *
174	 * @return {void}
175	 */
176	function focusMobileResizeCheck() {
177		if ($(window).width() < 1024) {
178			$('#ld-focus-sidebar').attr('aria-modal', 'true');
179
180			if (!$('.ld-focus').hasClass('ld-focus-sidebar-collapsed')) {
181				closeFocusSidebar();
182			}
183		} else {
184			$('#ld-focus-sidebar').attr('aria-modal', 'false');
185
186			if ($('.ld-focus').hasClass('ld-focus-sidebar-filtered')) {
187				closeFocusSidebar();
188			} else if (
189				!$('.ld-focus').hasClass('ld-focus-sidebar-filtered') &&
190				$('.ld-focus').hasClass('ld-focus-sidebar-collapsed')
191			) {
192				openFocusSidebar();
193			}
194		}
195	}
196
197	function focusMobileHandleOrientationChange(e) {
198		if (e.matches) {
199			if (
200				1024 <= $(window).width() &&
201				!$('.ld-focus').hasClass('ld-focus-sidebar-filtered') &&
202				$('.ld-focus').hasClass('ld-focus-sidebar-collapsed')
203			) {
204				openFocusSidebar();
205			}
206		}
207	}
208	window
209		.matchMedia('(orientation: landscape)')
210		.addListener(focusMobileHandleOrientationChange);
211
212	function closeFocusSidebarPageLoad() {
213		$('.ld-focus').addClass('ld-focus-sidebar-collapsed');
214		$('.ld-focus').removeClass('ld-focus-initial-transition');
215		$('.ld-mobile-nav').removeClass('expanded');
216		$('[aria-controls="ld-focus-sidebar"]').attr('aria-expanded', 'false');
217		positionTooltips();
218
219		const sidebar = document.querySelector('.ld-focus-sidebar');
220
221		if (sidebar) {
222			sidebar.dispatchEvent(new CustomEvent('ld-focus-sidebar-closed'));
223		}
224		dispatchSidebarEvent(false);
225	}
226
227	/**
228	 * Dispatches a custom event on to notify other scripts that the focus sidebar has been opened or closed.
229	 *
230	 * @since 4.23.2
231	 *
232	 * @param {boolean} isOpen Whether the sidebar is open.
233	 *
234	 * @return {void}
235	 */
236	function dispatchSidebarEvent(isOpen) {
237		const sidebar = document.querySelector('.ld-focus-sidebar');
238
239		if (!sidebar) {
240			return;
241		}
242
243		let eventName = 'ld-focus-sidebar-closed';
244
245		if (isOpen) {
246			eventName = 'ld-focus-sidebar-opened';
247		}
248
249		sidebar.dispatchEvent(new CustomEvent(eventName));
250	}
251
252	/**
253	 * Closes the focus sidebar.
254	 *
255	 * @since 3.0.0
256	 *
257	 * @return {void}
258	 */
259	function closeFocusSidebar() {
260		// Hide the wrapper to avoid issues with focus trap.
261		$('.ld-focus-sidebar-wrapper').hide();
262
263		$('.ld-focus').addClass('ld-focus-sidebar-collapsed');
264		$('.ld-mobile-nav').removeClass('expanded');
265		$('[aria-controls="ld-focus-sidebar"]').attr('aria-expanded', 'false');
266
267		disableFocusTrap();
268
269		// If the mobile trigger is visible, move focus to it.
270		const mobileTrigger = $('.ld-trigger-mobile-nav');
271
272		if (mobileTrigger.is(':visible')) {
273			mobileTrigger.focus();
274		}
275
276		positionTooltips();
277
278		const sidebar = document.querySelector('.ld-focus-sidebar');
279
280		if (sidebar) {
281			sidebar.dispatchEvent(new CustomEvent('ld-focus-sidebar-closed'));
282		}
283		dispatchSidebarEvent(false);
284	}
285	/**
286	 * Handles tab key press in the focus sidebar to trap focus within the sidebar.
287	 * When the last focusable element is reached, focus is redirected to the first element.
288	 *
289	 * @since 4.21.3
290	 * @param {Event} e The keyboard event object.
291	 */
292	function handleTabTrap(e) {
293		if (e.key === 'Tab') {
294			e.preventDefault();
295			$('#ld-focus-sidebar-toggle').focus();
296		}
297	}
298
299	/**
300	 * Enables focus trap for the sidebar to improve accessibility.
301	 * Makes sidebar elements tabbable, focuses the sidebar toggle button,
302	 * and adds event listener to trap focus within the sidebar.
303	 *
304	 * @since 4.21.3
305	 */
306	function enableFocusTrap() {
307		// Focus sidebar for accessibility when opened, allowing keyboard navigation easier.
308		$('#ld-focus-sidebar-toggle').focus();
309
310		// Make the course heading tabbable.
311		$('#ld-focus-mode-course-heading').attr('tabindex', '0');
312
313		// Get list of focusable elements, and when last one is focused, focus on the first element to trap focus.
314		const focusableElements = $('.ld-lesson-items a');
315		const lastFocusableElement =
316			focusableElements[focusableElements.length - 1];
317
318		lastFocusableElement.addEventListener('keydown', handleTabTrap);
319	}
320
321	/**
322	 * Disables focus trap for the sidebar when it's closed.
323	 * Makes sidebar elements non-tabbable and removes the event listener.
324	 *
325	 * @since 4.21.3
326	 */
327	function disableFocusTrap() {
328		// Make the course heading non-tabbable when the sidebar is closed.
329		$('#ld-focus-mode-course-heading').attr('tabindex', '-1');
330
331		if ($('.ld-focus-sidebar-trigger').attr('aria-expanded') === 'true') {
332			return;
333		}
334
335		// Remove focus trap when sidebar is closed.
336		const focusableElements = $('.ld-lesson-items a');
337		const lastFocusableElement =
338			focusableElements[focusableElements.length - 1];
339
340		if (lastFocusableElement) {
341			lastFocusableElement.removeEventListener('keydown', handleTabTrap);
342		}
343	}
344
345	/**
346	 * Opens the focus sidebar and enables focus trap for accessibility.
347	 * Handles mobile checks, class toggling, and icon changes.
348	 *
349	 * @since 3.0.0
350	 */
351	function openFocusSidebar() {
352		focusMobileCheck();
353
354		// Show the wrapper
355		$('.ld-focus-sidebar-wrapper').show();
356
357		// Recalculate max-heights for expanded lesson items whose height was measured while
358		// the sidebar was hidden (display: none). When hidden, outerHeight() returns 0 and
359		// data-height is set to 0. A value > 0 means it was already measured correctly.
360		$( '.ld-focus-sidebar .ld-lesson-item-expanded.ld-expanded' ).each( function () {
361			const $lessonContainer = $( this );
362			if ( parseInt( $lessonContainer.attr( 'data-height' ), 10 ) > 0 ) {
363				return;
364			}
365			let recalcHeight = 0;
366			$lessonContainer.children().each( function () {
367				recalcHeight += $( this ).outerHeight();
368			} );
369			const newHeight = recalcHeight > 0 ? recalcHeight + expandableSidebarHeightPadding : 0;
370			$lessonContainer.attr( 'data-height', newHeight ).css( 'max-height', newHeight );
371		} );
372
373		// Flip classes to open the focus sidebar.
374		$('.ld-focus').removeClass('ld-focus-sidebar-collapsed');
375		$('.ld-mobile-nav').addClass('expanded');
376
377		// We need to wait for the sidebar to be opened before we can enable the focus trap.
378		enableFocusTrap();
379
380		$('[aria-controls="ld-focus-sidebar"]').attr('aria-expanded', 'true');
381
382		const sidebar = document.querySelector('.ld-focus-sidebar');
383
384		if (sidebar) {
385			sidebar.dispatchEvent(new CustomEvent('ld-focus-sidebar-opened'));
386		}
387		dispatchSidebarEvent(true);
388
389		positionTooltips();
390	}
391
392	$('.ld-file-input').each(function () {
393		var $input = $(this),
394			$label = $input.next('label'),
395			labelVal = $label.html();
396
397		$input.on('change', function (e) {
398			var fileName = '';
399			if (this.files && 1 < this.files.length) {
400				fileName = (
401					this.getAttribute('data-multiple-caption') || ''
402				).replace('{count}', this.files.length);
403			} else if (e.target.value) {
404				fileName = e.target.value.split('\\').pop();
405			}
406			if (fileName) {
407				$label.find('span').html(fileName);
408				$label.addClass('ld-file-selected');
409				$('#uploadfile_btn').attr('disabled', false);
410			} else {
411				$label.html(labelVal);
412				$label.removeClass('ld-file-selected');
413				$('#uploadfile_btn').attr('disabled', true);
414			}
415		});
416
417		$('#uploadfile_form').on('submit', function () {
418			$label.removeClass('ld-file-selected');
419			$('#uploadfile_btn').attr('disabled', true);
420		});
421
422		// Firefox bug fix
423		$input
424			.on('focus', function () {
425				$input.addClass('has-focus');
426			})
427			.on('blur', function () {
428				$input.removeClass('has-focus');
429			});
430	});
431
432	$('body').on(
433		'click',
434		'.ld-expand-button, [data-ld-expand-button]',
435		function (e) {
436			e.preventDefault();
437
438			ldToggleExpandableElement($(this));
439
440			positionTooltips();
441		}
442	);
443
444	/**
445	 * Initialize expanded items to be expanded and collapsed elements to have [hidden="hidden"].
446	 * Expanded elements allow "Expand All" items to expand all their children by default.
447	 * Collapsed elements need [hidden="hidden"] to be set via JavaScript, so we cannot pre-load this on the server.
448	 *
449	 * @see https://designsystem.digital.gov/components/accordion/
450	 *
451	 * @since 4.21.0
452	 */
453	function initializeExpandableElements() {
454		$('.ld-expand-button, [data-ld-expand-button], .ld-search-prompt').each(
455			function (index, buttonElement) {
456				ldToggleExpandableElement(
457					$(buttonElement),
458					$(buttonElement).attr('aria-expanded') === 'true'
459				);
460			}
461		);
462	}
463
464	/**
465	 * Focuses on the first alert on the page with a role "alert".
466	 * This is required for accessibility.
467	 *
468	 * @since 4.21.3
469	 * @since 4.21.5 Switched to a focus-based approach for better compatibility with different screen readers.
470	 *
471	 * @return {void}
472	 */
473	function initializeAlerts() {
474		/**
475		 * We need to give it a tabindex of -1 to allow it to be programmatically focused so it will be
476		 * read out by screen readers. Unfortunately, we cannot remove it after focusing.
477		 * If we do, it won't be read at all.
478		 *
479		 * By using -1 rather than 0, it won't show up in the tab order when navigating by keyboard.
480		 *
481		 * We can only target the first alert because we have no way to know when the screen reader has finished
482		 * reading each alert to focus on the next one.
483		 *
484		 * The setTimeout() is used to force this to happen at the end of the event queue and improve compatibility.
485		 */
486		setTimeout(function () {
487			$('.ld-alert[role="alert"]:visible')
488				.first()
489				.attr('tabindex', '-1')
490				.focus();
491		}, 500);
492	}
493
494	$(document).on(
495		'ldAccordionPaginationComplete',
496		'.ld-accordion',
497		initializeExpandableElements
498	);
499
500	// On page load.
501	initializeExpandableElements();
502	initializeAlerts();
503
504	$('body').on('click', '.ld-search-prompt', function (e) {
505		e.preventDefault();
506
507		$('#course_name_field').focus();
508
509		ldToggleExpandableElement($(this));
510
511		const $controls = $('#' + $(this).attr('aria-controls'));
512
513		if ($controls.find('.ld-closer').length > 0) {
514			$controls
515				.find('.ld-closer')
516				.attr('aria-expanded', $(this).attr('aria-expanded'));
517		}
518	});
519
520	/**
521	 * Handles expanding and collapsing elements on button click.
522	 *
523	 * @since 4.20.2
524	 *
525	 * @param {jQuery}  $button jQuery Element for the button that was clicked.
526	 * @param {boolean} expand  Whether to expand the associated element or not. Defaults to a value based on the button's aria-expanded attribute.
527	 *
528	 * @return {void}
529	 */
530	function ldToggleExpandableElement($button, expand) {
531		if ('undefined' === typeof expand) {
532			// Checking !== true to handle undefined and false.
533			expand = $button.attr('aria-expanded') !== 'true';
534		}
535
536		const containerID = $button.attr('aria-controls');
537
538		if (
539			typeof containerID !== 'undefined' &&
540			containerID.indexOf(' ') > -1 &&
541			!$button.data('ld-expanding-all')
542		) {
543			// We're toggling multiple elements at once via an "Expand All"-type button.
544
545			containerID.split(' ').forEach(function (id) {
546				const $element = $('[aria-controls="' + id + '"]');
547
548				ldToggleExpandableElement($element, expand);
549			});
550
551			// Temporarily set the "ld-expanding-all" data to allow us to toggle the state of this specific button.
552
553			$button.data('ld-expanding-all', true);
554
555			ldToggleExpandableElement($button, expand);
556
557			$button.data('ld-expanding-all', false);
558		} else {
559			// Toggle a specific button.
560
561			// Account for edge cases where an Expand All button controls only one expandable area.
562			$button = $('[aria-controls="' + containerID + '"]');
563
564			const $container = $('#' + containerID);
565
566			if (expand && $container.length > 0) {
567				// Unhide right away.
568				$container.attr('hidden', false);
569			}
570
571			$button.each(function (index, element) {
572				/**
573				 * Pull the initial text from a cached data attribute,
574				 * that way as the button text changes we always know what it was initially.
575				 */
576				const dataInitialText =
577					$(element).data('ld-initial-text') ||
578					$(element)
579						.find('.ld-text, [data-ld-expand-button-text-element]')
580						.html();
581
582				$(element).data('ld-initial-text', dataInitialText);
583
584				const dataExpandText =
585					$(element).data('ld-expand-text') || dataInitialText;
586				const dataCollapseText =
587					$(element).data('ld-collapse-text') || dataInitialText;
588
589				$(element)
590					.attr('aria-expanded', expand)
591					.toggleClass('ld-expanded', expand);
592
593				if (expand && dataCollapseText) {
594					$(element)
595						.find('.ld-text, [data-ld-expand-button-text-element]')
596						.html(dataCollapseText);
597				} else if (!expand && dataExpandText) {
598					$(element)
599						.find('.ld-text, [data-ld-expand-button-text-element]')
600						.html(dataExpandText);
601				}
602			});
603
604			if ($container.length <= 0) {
605				/**
606				 * Expand All buttons with more than one controlled element won't have a valid Container,
607				 * so we shouldn't proceed.
608				 */
609				return;
610			}
611
612			let totalHeight = 0;
613			$container.children().each(function () {
614				totalHeight += $(this).outerHeight();
615			});
616
617			const finalHeightWithPadding = totalHeight > 0 ? totalHeight + expandableSidebarHeightPadding : 0;
618
619			// Writing to the attribute to make debugging easier.
620			$container.attr('data-height', finalHeightWithPadding);
621
622			$container.css({
623				'max-height': expand ? finalHeightWithPadding : 0,
624			});
625
626			if (!expand) {
627				// If we're collapsing, we should remove this class immediately.
628				$container.toggleClass('ld-expanded', expand);
629
630				const waitForCollapsed = setInterval(function () {
631					if ($container.outerHeight() === 0) {
632						clearInterval(waitForCollapsed);
633						$container.attr('hidden', true);
634					}
635				});
636			} else {
637				// If we're expanding, we only want to add the .ld-expanded class once fully expanded.
638				const waitForExpanded = setInterval(function () {
639					if ($container.outerHeight() === totalHeight) {
640						clearInterval(waitForExpanded);
641						$container.toggleClass('ld-expanded', expand);
642					}
643				});
644			}
645		}
646
647		positionTooltips();
648	}
649
650	/**
651	 * Initialize registration link in the classic registration page.
652	 *
653	 * @since 4.16.0
654	 *
655	 * @return {void}
656	 */
657	function ldRegistrationLinkInit() {
658		const $loginLink = $('.registration-login-link');
659
660		if ($loginLink.length === 0) {
661			return;
662		}
663
664		if ('' !== $loginLink.attr('href')) {
665			return;
666		}
667
668		$loginLink.on('click', function (e) {
669			e.preventDefault();
670			$('#learndash_registerform, .registration-login').hide();
671			$(
672				'.registration-login-form, .show-register-form, .show-password-reset-link'
673			).show();
674		});
675
676		$('.show-register-form').on('click', function (e) {
677			e.preventDefault();
678			$(
679				'.registration-login-form, .show-register-form, .show-password-reset-link'
680			).hide();
681			$('#learndash_registerform, .registration-login').show();
682		});
683	}
684
685	/**
686	 * Initialize registration link in the modern registration page.
687	 *
688	 * @since 4.16.0
689	 *
690	 * @return {void}
691	 */
692	function ldRegistrationLinkInitModern() {
693		const $loginLink = $('.ld-registration__login-link');
694
695		if ($loginLink.length === 0) {
696			return;
697		}
698
699		if ('' !== $loginLink.attr('href')) {
700			return;
701		}
702
703		$(document).on('click', '.ld-registration__login-link', function (e) {
704			e.preventDefault();
705			let $wrapper = $(this).closest('.ld-registration__wrapper');
706			$wrapper.addClass('ld-registration__wrapper--login');
707			$wrapper.removeClass('ld-registration__wrapper--register');
708		});
709
710		$(document).on(
711			'click',
712			'.ld-registration__register-link',
713			function (e) {
714				e.preventDefault();
715				let $wrapper = $(this).closest('.ld-registration__wrapper');
716				$wrapper.removeClass('ld-registration__wrapper--login');
717				$wrapper.addClass('ld-registration__wrapper--register');
718			}
719		);
720	}
721
722	$('body').on('click', '.ld-closer', function (e) {
723		ldToggleExpandableElement($('.ld-search-prompt'), false);
724		$(this).attr('aria-expanded', false);
725	});
726
727	$('body').on('touch click', '.ld-tabs-navigation .ld-tab', function () {
728		const $tabContent = $('#' + $(this).attr('aria-controls'));
729		if (!$tabContent.length) {
730			return;
731		}
732
733		// Set other Tabs as inactive.
734		$('.ld-tabs-navigation .ld-tab.ld-active')
735			.removeClass('ld-active')
736			.attr('aria-selected', 'false')
737			.attr('tabindex', '-1');
738
739		// Set current Tab as active.
740		$(this)
741			.addClass('ld-active')
742			.attr('aria-selected', 'true')
743			.removeAttr('tabindex');
744
745		// Make other Tab Content panels invisible.
746		$('.ld-tabs-content .ld-tab-content.ld-visible').removeClass(
747			'ld-visible'
748		);
749
750		$tabContent.addClass('ld-visible');
751
752		positionTooltips();
753	});
754
755	$('body').on('keydown', '.ld-tabs-navigation .ld-tab', function (event) {
756		// If the key is not a Left/Right arrow key, Home, or End, do nothing.
757		if (
758			['ArrowLeft', 'ArrowRight', 'Home', 'End'].indexOf(event.key) === -1
759		) {
760			return;
761		}
762
763		const target = event.currentTarget;
764		const $firstTab = $(target)
765			.closest('[role="tablist"]')
766			.find('[role="tab"]')
767			.first();
768		const $lastTab = $(target)
769			.closest('[role="tablist"]')
770			.find('[role="tab"]')
771			.last();
772
773		event.stopPropagation();
774		event.preventDefault();
775
776		switch (event.key) {
777			case 'ArrowLeft':
778				if (target === $firstTab[0]) {
779					$lastTab.focus();
780				} else {
781					$(this).prev().focus();
782				}
783				break;
784			case 'ArrowRight':
785				if (target === $lastTab[0]) {
786					$firstTab.focus();
787				} else {
788					$(target).next().focus();
789				}
790				break;
791			case 'Home':
792				$firstTab.focus();
793				break;
794			case 'End':
795				$lastTab.focus();
796				break;
797			default:
798				break;
799		}
800	});
801
802	/**
803	 * Initialize tooltips.
804	 * - If JS isn't enabled, the tooltips will be visible at all times.
805	 * - This adds the `ld-tooltip--initialized` class to all tooltips to signify that JS is enabled.
806	 * - The `ld-tooltip--hidden` class is removed when the mouse is over a tooltip, the tooltip is focused,
807	 * or the tooltip is focused-within.
808	 * - The `ld-tooltip--hidden` class is added if a tooltip is visible and the Escape key is pressed.
809	 *
810	 * @since 4.21.3
811	 *
812	 * @return {void}
813	 */
814	function initTooltips() {
815		$('.ld-tooltip').each(function () {
816			const $tooltip = $(this).find('[role="tooltip"]');
817
818			if (!$tooltip.length || typeof $tooltip[0] === 'undefined') {
819				return;
820			}
821
822			if (!$(this).hasClass('ld-tooltip--initialized')) {
823				$(this)
824					.addClass('ld-tooltip--initialized')
825					.addClass('ld-tooltip--hidden');
826			}
827
828			/**
829			 * We need to temporarily remove the `ld-tooltip--hidden` class
830			 * as hidden tooltips are hidden off to the left edge of the screen.
831			 *
832			 * We need to know where it would normally be positioned when visible
833			 * to know if we need to move it to the right.
834			 */
835			const isHidden = $(this).hasClass('ld-tooltip--hidden');
836
837			$(this).removeClass('ld-tooltip--hidden');
838			$(this).removeClass('ld-tooltip--position-right');
839
840			const tooltipRect = $tooltip[0].getBoundingClientRect();
841
842			let containerWidth = windowWidth;
843
844			/**
845			 * If the tooltip is within the focus sidebar, use the sidebar width.
846			 *
847			 * We have to do this because when an element is vertically scrollable, a browser will simply not
848			 * allow you to have items spill out the left or right sides even when the appropriate CSS for
849			 * overflow-x is set to visible as it will be forced to auto implicitly.
850			 *
851			 * See https://developer.mozilla.org/en-US/docs/Web/CSS/overflow-x#syntax
852			 */
853			if ($(this).closest('.ld-focus-sidebar').length) {
854				containerWidth = $(this).closest('.ld-focus-sidebar').width();
855			}
856
857			/**
858			 * If the tooltip would overflow the right edge of the window,
859			 * add the `ld-tooltip--position-right` class.
860			 */
861			$(this).toggleClass(
862				'ld-tooltip--position-right',
863				tooltipRect.right > containerWidth
864			);
865
866			// Add the .ld-tooltip--hidden class back if it was hidden.
867			$(this).toggleClass('ld-tooltip--hidden', isHidden);
868		});
869
870		$(document).on('keydown', function (event) {
871			/**
872			 * We need to check if the Escape key is pressed and if any tooltips
873			 * are currently visible before dismissing them.
874			 */
875			if (
876				event.key === 'Escape' &&
877				($('.ld-tooltip:hover').length ||
878					$('.ld-tooltip:focus').length ||
879					$('.ld-tooltip:focus-within').length)
880			) {
881				$(
882					'.ld-tooltip:hover, .ld-tooltip:focus, .ld-tooltip:focus-within'
883				).addClass('ld-tooltip--hidden');
884
885				// Remove focus to prevent automatically scrolling the page.
886				$('.ld-tooltip:focus, .ld-tooltip :focus').blur();
887			}
888		});
889
890		/**
891		 * For the next two event listeners:
892		 *
893		 * - mouseenter/mouseleave are used for hover on desktop.
894		 *
895		 * - focusin/focusout are used for keyboard focus on desktop.
896		 * 	- These are used instead of focus/blur because the focused element is likely _within_ the tooltip container.
897		 * 	- If the focused element is the tooltip container, these event names still work.
898		 *
899		 * - touchstart is used for touch devices.
900		 */
901
902		$(document).on(
903			'mouseenter focusin touchstart',
904			'.ld-tooltip--hidden',
905			function () {
906				// Show the tooltip.
907				$(this).removeClass('ld-tooltip--hidden');
908			}
909		);
910
911		$(document).on(
912			'mouseleave focusout touchstart',
913			'.ld-tooltip:not(.ld-tooltip--hidden)',
914			function () {
915				// Hide the tooltip when the mouse when the user moves away from the tooltip.
916				$(this).addClass('ld-tooltip--hidden');
917			}
918		);
919	}
920
921	/**
922	 * In order to account for container breakpoints modifying the layout,
923	 * we need to initialize tooltips twice on page load.
924	 *
925	 * TODO: Find a better way to do this.
926	 */
927	initTooltips();
928	initTooltips();
929
930	var $tooltips = $('*[data-ld-tooltip]');
931
932	initLegacyTooltips();
933
934	/**
935	 * Initialize legacy LD30 Classic tooltips.
936	 * Replaced with a new style of tooltips in 4.21.3 for accessibility reasons.
937	 * Kept for backwards compatibility.
938	 *
939	 * @since 3.0.0
940	 * @since 4.21.3 Renamed from initTooltips to initLegacyTooltips.
941	 *
942	 * @return {void}
943	 */
944	function initLegacyTooltips() {
945		// Clear out old tooltips
946
947		if ($('#learndash-tooltips').length) {
948			$('#learndash-tooltips').remove();
949			$tooltips = $('*[data-ld-tooltip]');
950		}
951
952		if ($tooltips.length) {
953			$('body').prepend('<div id="learndash-tooltips"></div>');
954			var $ctr = 1;
955			$tooltips.each(function () {
956				var anchor = $(this);
957				if (anchor.hasClass('ld-item-list-item')) {
958					anchor = anchor.find('.ld-item-title');
959				}
960
961				/**
962				 * Prevent calendar icon from being clickable.
963				 */
964				if (
965					'undefined' !== typeof anchor &&
966					$(anchor).hasClass('ld-status-waiting')
967				) {
968					$(anchor).on('click', function (e) {
969						e.preventDefault();
970						return false;
971					});
972
973					// Also prevent parent <a> from being clickable.
974					var parent_anchor = $(anchor).parents('a');
975					if ('undefined' !== typeof parent_anchor) {
976						$(parent_anchor).on('click', function (e) {
977							e.preventDefault();
978							return false;
979						});
980					}
981				}
982
983				var elementOffsets = {
984					top: anchor.offset().top,
985					left: anchor.offset().left + anchor.outerWidth() / 2,
986				};
987				var $content = $(this).attr('data-ld-tooltip');
988				var $rel_id = Math.floor(Math.random() * 99999);
989
990				//var $tooltip = '<span id="ld-tooltip-' + $rel_id + '" class="ld-tooltip" style="top:' + elementOffsets.top + 'px; left:' + elementOffsets.left + 'px;">' + $content + '</span>';
991				var $tooltip =
992					'<span id="ld-tooltip-' +
993					$rel_id +
994					'" class="ld-tooltip">' +
995					$content +
996					'</span>';
997				$(this).attr('data-ld-tooltip-id', $rel_id);
998				$('#learndash-tooltips').append($tooltip);
999				$ctr++;
1000				var $tooltip = $('#ld-tooltip-' + $rel_id);
1001				$(this)
1002					.on('mouseenter', function () {
1003						$tooltip.addClass('ld-visible');
1004					})
1005					.on('mouseleave', function () {
1006						$tooltip.removeClass('ld-visible');
1007					});
1008			});
1009
1010			$(window).on('resize', function () {
1011				// Reposition tooltips after resizing
1012				positionTooltips();
1013			});
1014
1015			$(window)
1016				.add('.ld-focus-sidebar-wrapper')
1017				.on('scroll', function () {
1018					// Hide tooltips so they don't persist while scrolling
1019					$('.ld-visible.ld-tooltip').removeClass('ld-visible');
1020
1021					// Reposition tooltips after scrolling
1022					positionTooltips();
1023				});
1024
1025			positionTooltips();
1026		}
1027	}
1028
1029	function initLoginModal() {
1030		var modal_wrapper = $('.learndash-wrapper-login-modal');
1031		if ('undefined' !== typeof modal_wrapper && modal_wrapper.length) {
1032			// Move the model to be first element of the body. See LEARNDASH-3503
1033			$(modal_wrapper).prependTo('body');
1034		}
1035	}
1036
1037	function openLoginModal() {
1038		var modal_wrapper = $('.learndash-wrapper-login-modal');
1039		if ('undefined' !== typeof modal_wrapper && modal_wrapper.length) {
1040			$(modal_wrapper).addClass('ld-modal-open');
1041			$(modal_wrapper).removeClass('ld-modal-closed');
1042
1043			// Removed LEARNDASH-3867 #4
1044			$('html, body').animate(
1045				{
1046					scrollTop: $('.ld-modal', modal_wrapper).offset().top,
1047				},
1048				50
1049			);
1050
1051			$('.ld-modal', modal_wrapper).focus();
1052		}
1053	}
1054
1055	function closeLoginModal() {
1056		var modal_wrapper = $('.learndash-wrapper-login-modal');
1057		if ('undefined' !== typeof modal_wrapper && modal_wrapper.length) {
1058			$(modal_wrapper).removeClass('ld-modal-open');
1059			$(modal_wrapper).addClass('ld-modal-closed');
1060
1061			// Return the focus to the login link that triggers the modal.
1062			$('[data-ld-login-modal-trigger]').focus();
1063		}
1064	}
1065
1066	/**
1067	 * Position legacy LD30 Classic tooltips.
1068	 * This is not used by the new tooltips added in 4.21.3.
1069	 * Kept for backwards compatibility.
1070	 *
1071	 * @since 3.0.0
1072	 *
1073	 * @return {void}
1074	 */
1075	function positionTooltips() {
1076		if ('undefined' !== typeof $tooltips) {
1077			setTimeout(function () {
1078				$tooltips.each(function () {
1079					var anchor = $(this);
1080					var $rel_id = anchor.attr('data-ld-tooltip-id');
1081					$tooltip = $('#ld-tooltip-' + $rel_id);
1082
1083					if (anchor.hasClass('ld-item-list-item')) {
1084						//anchor = anchor.find('.ld-item-title');
1085						anchor = anchor.find('.ld-status-icon');
1086					}
1087
1088					var parent_focus =
1089						jQuery(anchor).parents('.ld-focus-sidebar');
1090					var left_post =
1091						anchor.offset().left + (anchor.outerWidth() + 10);
1092					if (parent_focus.length) {
1093						left_post =
1094							anchor.offset().left + (anchor.outerWidth() - 18);
1095					}
1096
1097					// Get the main content height
1098					var focusModeMainContentHeight =
1099						$('.ld-focus-main').height();
1100
1101					// Current tooltip height
1102					var focusModeCurrentTooltipHeight =
1103						anchor.offset().top + -3;
1104
1105					// Position tooltip depending on focus mode or not
1106					if (!focusModeMainContentHeight) {
1107						var anchorTop = anchor.offset().top + -3;
1108						var anchorLeft = anchor.offset().left;
1109					} else {
1110						anchorTop =
1111							focusModeCurrentTooltipHeight <
1112							focusModeMainContentHeight
1113								? focusModeCurrentTooltipHeight
1114								: focusModeMainContentHeight;
1115						anchorLeft = left_post;
1116					}
1117
1118					$tooltip
1119						.css({
1120							top: anchorTop,
1121
1122							//'left' : anchor.offset().left + (anchor.outerWidth() / 2),
1123							//'left': left_post, //anchor.offset().left + (anchor.outerWidth() +10),
1124							left: anchorLeft, //anchor.offset().left + (anchor.outerWidth() +10),
1125							'margin-left': 0,
1126							'margin-right': 0,
1127						})
1128						.removeClass('ld-shifted-left ld-shifted-right');
1129					if ($tooltip.offset().left <= 0) {
1130						$tooltip
1131							.css({
1132								'margin-left': Math.abs($tooltip.offset().left),
1133							})
1134							.addClass('ld-shifted-left');
1135					}
1136					var $tooltipRight =
1137						$(window).width() -
1138						($tooltip.offset().left + $tooltip.outerWidth());
1139					if (0 >= $tooltipRight && 360 < $(window).width()) {
1140						$tooltip
1141							.css({ 'margin-right': Math.abs($tooltipRight) })
1142							.addClass('ld-shifted-right');
1143					}
1144				});
1145			}, 500);
1146		}
1147	}
1148
1149	$('body').on('click', '#ld-profile .ld-reset-button', function (e) {
1150		e.preventDefault();
1151
1152		$('#ld-profile #course_name_field').val('');
1153
1154		var searchVars = {
1155			shortcode_instance: $('#ld-profile').data('shortcode_instance'),
1156		};
1157
1158		searchVars['ld-profile-search'] = $(this)
1159			.parents('.ld-item-search-wrapper')
1160			.find('#course_name_field')
1161			.val();
1162		searchVars['ld-profile-search-nonce'] = $(this)
1163			.parents('.ld-item-search-wrapper')
1164			.find('form.ld-item-search-fields')
1165			.data('nonce');
1166
1167		$('#ld-profile #ld-main-course-list').addClass('ld-loading');
1168
1169		$.ajax({
1170			type: 'GET',
1171			url: ldVars.ajaxurl + '?action=ld30_ajax_profile_search',
1172			data: searchVars,
1173			success(response) {
1174				if ('undefined' !== typeof response.data.markup) {
1175					$('#ld-profile').html(response.data.markup);
1176					ldToggleExpandableElement(
1177						'#ld-profile .ld-search-prompt',
1178						true
1179					);
1180				}
1181			},
1182		});
1183	});
1184
1185	$('body').on('submit', '.ld-item-search-fields', function (e) {
1186		e.preventDefault();
1187
1188		var searchVars = {
1189			shortcode_instance: $('#ld-profile').data('shortcode_instance'),
1190		};
1191
1192		searchVars['ld-profile-search'] = $(this)
1193			.parents('.ld-item-search-wrapper')
1194			.find('#course_name_field')
1195			.val();
1196		searchVars['ld-profile-search-nonce'] = $(this)
1197			.parents('.ld-item-search-wrapper')
1198			.find('form.ld-item-search-fields')
1199			.data('nonce');
1200
1201		$('#ld-profile #ld-main-course-list').addClass('ld-loading');
1202
1203		$.ajax({
1204			type: 'GET',
1205			url: ldVars.ajaxurl + '?action=ld30_ajax_profile_search',
1206			data: searchVars,
1207			success(response) {
1208				if ('undefined' !== typeof response.data.markup) {
1209					$('#ld-profile').html(response.data.markup);
1210					ldToggleExpandableElement(
1211						'#ld-profile .ld-search-prompt',
1212						true
1213					);
1214				}
1215			},
1216		});
1217	});
1218
1219	$('body').on('click', '.ld-pagination a', function (e) {
1220		e.preventDefault();
1221
1222		var linkVars = {};
1223		var parentVars = {};
1224
1225		$(this)
1226			.attr('href')
1227			.replace(/[?&]+([^=&]+)=([^&]*)/gi, function (m, key, value) {
1228				linkVars[key] = value;
1229			});
1230
1231		linkVars.pager_nonce = $(this)
1232			.parents('.ld-pagination')
1233			.data('pager-nonce');
1234
1235		linkVars.pager_results = $(this)
1236			.parents('.ld-pagination')
1237			.data('pager-results');
1238
1239		linkVars.context = $(this).data('context');
1240
1241		parentVars.currentTarget = e.currentTarget;
1242
1243		if ('profile' != linkVars.context) {
1244			linkVars.lesson_id = $(this).data('lesson_id');
1245			linkVars.course_id = $(this).data('course_id');
1246
1247			if ($('.ld-course-nav-' + linkVars.course_id).length) {
1248				linkVars.widget_instance = $(
1249					'.ld-course-nav-' + linkVars.course_id
1250				).data('widget_instance');
1251			}
1252		}
1253
1254		if ('course_topics' == linkVars.context) {
1255			$('#ld-topic-list-' + linkVars.lesson_id).addClass('ld-loading');
1256			$('#ld-nav-content-list-' + linkVars.lesson_id).addClass(
1257				'ld-loading'
1258			);
1259		}
1260
1261		if ('course_content_shortcode' == linkVars.context) {
1262			parentVars.parent_container = $(parentVars.currentTarget).closest(
1263				'.ld-course-content-' + linkVars.course_id
1264			);
1265			if (
1266				'undefined' !== typeof parentVars.parent_container &&
1267				parentVars.parent_container.length
1268			) {
1269				$(parentVars.parent_container).addClass('ld-loading');
1270				linkVars.shortcode_instance = $(
1271					parentVars.parent_container
1272				).data('shortcode_instance');
1273			} else {
1274				$('.ld-course-content-' + linkVars.course_id).addClass(
1275					'ld-loading'
1276				);
1277				linkVars.shortcode_instance = $(
1278					'.ld-course-content-' + linkVars.course_id
1279				).data('shortcode_instance');
1280			}
1281		} else if ('course_lessons' == linkVars.context) {
1282			var parent_container;
1283
1284			// Check if we are within the Course Navigation Widget.
1285			if (
1286				'undefined' === typeof parentVars.parent_container ||
1287				!parentVars.parent_container.length
1288			) {
1289				parent_container = $(parentVars.currentTarget).parents(
1290					'.ld-lesson-navigation'
1291				);
1292				if (
1293					'undefined' !== typeof parent_container &&
1294					parent_container.length
1295				) {
1296					parentVars.context_sub = 'course_navigation_widget';
1297					parentVars.parent_container = $(
1298						parentVars.currentTarget
1299					).parents('#ld-lesson-list-' + linkVars.course_id);
1300				}
1301			}
1302
1303			// Check if we are within the Focus Mode Sidebar.
1304			if (
1305				'undefined' === typeof parentVars.parent_container ||
1306				!parentVars.parent_container.length
1307			) {
1308				parent_container = $(parentVars.currentTarget).parents(
1309					'.ld-focus-sidebar-wrapper'
1310				);
1311				if (
1312					'undefined' !== typeof parent_container &&
1313					parent_container.length
1314				) {
1315					parentVars.context_sub = 'focus_mode_sidebar';
1316					parentVars.parent_container = $(
1317						parentVars.currentTarget
1318					).parents('#ld-lesson-list-' + linkVars.course_id);
1319				}
1320			}
1321
1322			if (
1323				'undefined' === typeof parentVars.parent_container ||
1324				!parentVars.parent_container.length
1325			) {
1326				parentVars.parent_container = $(
1327					parentVars.currentTarget
1328				).closest(
1329					'#ld-item-list-' + linkVars.course_id,
1330					'#ld-lesson-list-' + linkVars.course_id
1331				);
1332			}
1333			if (
1334				'undefined' !== typeof parentVars.parent_container &&
1335				parentVars.parent_container.length
1336			) {
1337				$(parentVars.parent_container).addClass('ld-loading');
1338			} else {
1339				// Fallback solution.
1340				$('#ld-item-list-' + linkVars.course_id).addClass('ld-loading');
1341				$('#ld-lesson-list-' + linkVars.course_id).addClass(
1342					'ld-loading'
1343				);
1344			}
1345		}
1346
1347		if ('profile' == linkVars.context) {
1348			$('#ld-profile #ld-main-course-list').addClass('ld-loading');
1349			linkVars.shortcode_instance =
1350				$('#ld-profile').data('shortcode_instance');
1351		}
1352
1353		if ('profile_quizzes' == linkVars.context) {
1354			$(
1355				'#ld-course-list-item-' +
1356					linkVars.pager_results.quiz_course_id +
1357					' .ld-item-contents'
1358			).addClass('ld-loading');
1359		}
1360
1361		if ('course_info_courses' == linkVars.context) {
1362			$('.ld-user-status').addClass('ld-loading');
1363			linkVars.shortcode_instance =
1364				$('.ld-user-status').data('shortcode-atts');
1365		}
1366
1367		if ('group_courses' == linkVars.context) {
1368			linkVars.group_id = $(this).data('group_id');
1369			if ('undefined' !== typeof linkVars.group_id) {
1370				parent_container = $(parentVars.currentTarget).parents(
1371					'.ld-group-courses-' + linkVars.group_id
1372				);
1373				if (
1374					'undefined' !== typeof parent_container &&
1375					parent_container.length
1376				) {
1377					$(parent_container).addClass('ld-loading');
1378					parentVars.parent_container = parent_container;
1379				}
1380			}
1381		}
1382
1383		$.ajax({
1384			type: 'GET',
1385			url: ldVars.ajaxurl + '?action=ld30_ajax_pager',
1386			data: linkVars,
1387			success(response) {
1388				// If we have a course listing, update
1389
1390				if ('course_topics' == linkVars.context) {
1391					if ($('#ld-topic-list-' + linkVars.lesson_id).length) {
1392						if ('undefined' !== typeof response.data.topics) {
1393							$('#ld-topic-list-' + linkVars.lesson_id).html(
1394								response.data.topics
1395							);
1396						}
1397
1398						if ('undefined' !== typeof response.data.pager) {
1399							$('#ld-expand-' + linkVars.lesson_id)
1400								.find('.ld-table-list-footer')
1401								.html(response.data.pager);
1402						}
1403
1404						learndashSetMaxHeight(
1405							$('.ld-lesson-item-' + linkVars.lesson_id).find(
1406								'.ld-item-list-item-expanded'
1407							)
1408						);
1409
1410						$('#ld-topic-list-' + linkVars.lesson_id).removeClass(
1411							'ld-loading'
1412						);
1413					}
1414
1415					if (
1416						$('#ld-nav-content-list-' + linkVars.lesson_id).length
1417					) {
1418						if ('undefined' !== typeof response.data.nav_topics) {
1419							$('#ld-nav-content-list-' + linkVars.lesson_id)
1420								.find('.ld-table-list-items')
1421								.html(response.data.topics);
1422						}
1423
1424						if ('undefined' !== typeof response.data.pager) {
1425							$('#ld-nav-content-list-' + linkVars.lesson_id)
1426								.find('.ld-table-list-footer')
1427								.html(response.data.pager);
1428						}
1429
1430						$(
1431							'#ld-nav-content-list-' + linkVars.lesson_id
1432						).removeClass('ld-loading');
1433					}
1434				}
1435
1436				if ('course_content_shortcode' == linkVars.context) {
1437					if ('undefined' !== typeof response.data.markup) {
1438						if (
1439							'undefined' !==
1440								typeof parentVars.parent_container &&
1441							parentVars.parent_container.length
1442						) {
1443							$(parentVars.parent_container).replaceWith(
1444								response.data.markup
1445							);
1446						} else {
1447							$(
1448								'#learndash_post_' + linkVars.course_id
1449							).replaceWith(response.data.markup);
1450						}
1451					}
1452				} else if ('course_lessons' == linkVars.context) {
1453					if (
1454						'undefined' !== typeof parentVars.parent_container &&
1455						parentVars.parent_container.length
1456					) {
1457						if (
1458							'course_navigation_widget' == parentVars.context_sub
1459						) {
1460							if (
1461								'undefined' !== typeof response.data.nav_lessons
1462							) {
1463								$(parentVars.parent_container)
1464									.html(response.data.nav_lessons)
1465									.removeClass('ld-loading');
1466							}
1467						} else if (
1468							'focus_mode_sidebar' == parentVars.context_sub
1469						) {
1470							if (
1471								'undefined' !== typeof response.data.nav_lessons
1472							) {
1473								$(parentVars.parent_container)
1474									.html(response.data.nav_lessons)
1475									.removeClass('ld-loading');
1476							}
1477						} else if (
1478							'undefined' !== typeof response.data.lessons
1479						) {
1480							$(parentVars.parent_container)
1481								.html(response.data.lessons)
1482								.removeClass('ld-loading');
1483						}
1484					} else {
1485						if ($('#ld-item-list-' + linkVars.course_id).length) {
1486							if ('undefined' !== typeof response.data.lessons) {
1487								$('#ld-item-list-' + linkVars.course_id)
1488									.html(response.data.lessons)
1489									.removeClass('ld-loading');
1490							}
1491						}
1492
1493						if ($('#ld-lesson-list-' + linkVars.course_id).length) {
1494							if (
1495								'undefined' !== typeof response.data.nav_lessons
1496							) {
1497								$('#ld-lesson-list-' + linkVars.course_id)
1498									.html(response.data.nav_lessons)
1499									.removeClass('ld-loading');
1500							}
1501						}
1502					}
1503				}
1504
1505				if ('group_courses' == linkVars.context) {
1506					if (
1507						'undefined' !== typeof parentVars.parent_container &&
1508						parentVars.parent_container.length
1509					) {
1510						if ('undefined' !== typeof response.data.markup) {
1511							$(parentVars.parent_container)
1512								.html(response.data.markup)
1513								.removeClass('ld-loading');
1514						}
1515					}
1516				}
1517
1518				if ('profile' == linkVars.context) {
1519					if ('undefined' !== typeof response.data.markup) {
1520						$('#ld-profile').html(response.data.markup);
1521					}
1522				}
1523
1524				if ('profile_quizzes' == linkVars.context) {
1525					if ('undefined' !== typeof response.data.markup) {
1526						$(
1527							'#ld-course-list-item-' +
1528								linkVars.pager_results.quiz_course_id +
1529								' .ld-item-list-item-expanded .ld-item-contents'
1530						).replaceWith(response.data.markup);
1531						$(
1532							'#ld-course-list-item-' +
1533								linkVars.pager_results.quiz_course_id
1534						)
1535							.get(0)
1536							.scrollIntoView({ behavior: 'smooth' });
1537					}
1538				}
1539
1540				if ('course_info_courses' == linkVars.context) {
1541					if ('undefined' !== typeof response.data.markup) {
1542						$('.ld-user-status').replaceWith(response.data.markup);
1543					}
1544				}
1545
1546				$('body').trigger('ld_has_paginated');
1547
1548				initTooltips();
1549			},
1550		});
1551	});
1552
1553	if ($('#learndash_timer').length) {
1554		var timer_el = jQuery('#learndash_timer');
1555		var timer_seconds = timer_el.attr('data-timer-seconds');
1556		var timer_button_el = jQuery(timer_el.attr('data-button'));
1557
1558		var cookie_key = timer_el.attr('data-cookie-key');
1559
1560		if ('undefined' !== typeof cookie_key) {
1561			var cookie_name = 'learndash_timer_cookie_' + cookie_key;
1562		} else {
1563			var cookie_name = 'learndash_timer_cookie';
1564		}
1565
1566		cookie_timer_seconds = jQuery.cookie(cookie_name);
1567
1568		if ('undefined' !== typeof cookie_timer_seconds) {
1569			timer_seconds = parseInt(cookie_timer_seconds);
1570		}
1571
1572		if (0 == timer_seconds) {
1573			$(timer_el).hide();
1574		}
1575
1576		$(timer_button_el).on('learndash-time-finished', function () {
1577			$(timer_el).hide();
1578		});
1579	}
1580
1581	$(document).on('learndash_video_disable_assets', function (event, status) {
1582		if ('undefined' === typeof learndash_video_data) {
1583			return false;
1584		}
1585
1586		if ('BEFORE' == learndash_video_data.videos_shown) {
1587			if (true == status) {
1588				$('.ld-lesson-topic-list').hide();
1589				$('.ld-lesson-navigation')
1590					.find('#ld-nav-content-list-' + ldVars.postID)
1591					.addClass('user_has_no_access');
1592				$('.ld-quiz-list').hide();
1593			} else {
1594				$('.ld-lesson-topic-list').slideDown();
1595				$('.ld-quiz-list').slideDown();
1596				$('.ld-lesson-navigation')
1597					.find('#ld-nav-content-list-' + ldVars.postID)
1598					.removeClass('user_has_no_access');
1599			}
1600		}
1601	});
1602
1603	$('.learndash-wrapper').on(
1604		'click',
1605		'.wpProQuiz_questionListItem input[type="radio"]',
1606		function (e) {
1607			$(this)
1608				.parents('.wpProQuiz_questionList')
1609				.find('label')
1610				.removeClass('is-selected');
1611			$(this).parents('label').addClass('is-selected');
1612		}
1613	);
1614
1615	$('.learndash-wrapper').on(
1616		'click',
1617		'.wpProQuiz_questionListItem input[type="checkbox"]',
1618		function (e) {
1619			if (jQuery(e.currentTarget).is(':checked')) {
1620				$(this).parents('label').addClass('is-selected');
1621			} else {
1622				$(this).parents('label').removeClass('is-selected');
1623			}
1624		}
1625	);
1626
1627	function learndash_ld30_show_user_statistic(e) {
1628		e.preventDefault();
1629
1630		var refId = jQuery(this).data('ref-id');
1631		var quizId = jQuery(this).data('quiz-id');
1632		var userId = jQuery(this).data('user-id');
1633		var statistic_nonce = jQuery(this).data('statistic-nonce');
1634		var post_data = {
1635			action: 'wp_pro_quiz_admin_ajax_statistic_load_user',
1636			func: 'statisticLoadUser',
1637			data: {
1638				quizId,
1639				userId,
1640				refId,
1641				statistic_nonce,
1642				avg: 0,
1643			},
1644		};
1645
1646		jQuery('#wpProQuiz_user_overlay, #wpProQuiz_loadUserData').show();
1647		var content = jQuery('#wpProQuiz_user_content').hide();
1648
1649		jQuery.ajax({
1650			type: 'POST',
1651			url: ldVars.ajaxurl,
1652			dataType: 'json',
1653			cache: false,
1654			data: post_data,
1655			error(jqXHR, textStatus, errorThrown) {},
1656			success(reply_data) {
1657				if ('undefined' !== typeof reply_data.html) {
1658					content.html(reply_data.html);
1659					jQuery('#wpProQuiz_user_content').show();
1660
1661					jQuery('body').trigger(
1662						'learndash-statistics-contentchanged'
1663					);
1664
1665					jQuery('#wpProQuiz_loadUserData').hide();
1666
1667					content.find('.statistic_data').on('click', function () {
1668						jQuery(this).parents('tr').next().toggle('fast');
1669
1670						return false;
1671					});
1672				}
1673			},
1674		});
1675
1676		jQuery('#wpProQuiz_overlay_close').on('click', function () {
1677			jQuery('#wpProQuiz_user_overlay').hide();
1678		});
1679	}
1680
1681	function learndashSetMaxHeight(elm) {
1682		var totalHeight = 0;
1683
1684		elm.find('> *').each(function () {
1685			totalHeight += $(this).outerHeight();
1686		});
1687
1688		elm.attr('data-height', '' + (totalHeight + 50) + '');
1689
1690		elm.css({
1691			'max-height': totalHeight + 50,
1692		});
1693	}
1694
1695	/**
1696	 * Will scroll the position of the Focus Mode sidebar
1697	 * to the active step.
1698	 */
1699	function learndashFocusModeSidebarAutoScroll() {
1700		if (jQuery('.learndash-wrapper .ld-focus').length) {
1701			var sidebar_wrapper = jQuery(
1702				'.learndash-wrapper .ld-focus .ld-focus-sidebar-wrapper'
1703			);
1704
1705			var sidebar_current_topic = jQuery(
1706				'.learndash-wrapper .ld-focus .ld-focus-sidebar-wrapper .ld-is-current-item'
1707			);
1708			if (
1709				'undefined' !== typeof sidebar_current_topic &&
1710				sidebar_current_topic.length
1711			) {
1712				var sidebar_scrollTo = sidebar_current_topic;
1713			} else {
1714				var sidebar_current_lesson = jQuery(
1715					'.learndash-wrapper .ld-focus .ld-focus-sidebar-wrapper .ld-is-current-lesson'
1716				);
1717				if (
1718					'undefined' !== typeof sidebar_current_lesson &&
1719					sidebar_current_lesson.length
1720				) {
1721					var sidebar_scrollTo = sidebar_current_lesson;
1722				}
1723			}
1724
1725			if (
1726				'undefined' !== typeof sidebar_scrollTo &&
1727				sidebar_scrollTo.length
1728			) {
1729				var offset_top = 0;
1730				if (
1731					jQuery('.learndash-wrapper .ld-focus .ld-focus-header')
1732						.length
1733				) {
1734					var logo_height = jQuery(
1735						'.learndash-wrapper .ld-focus .ld-focus-header'
1736					).height();
1737					offset_top += logo_height;
1738				}
1739				if (
1740					jQuery(
1741						'.learndash-wrapper .ld-focus .ld-focus-sidebar .ld-course-navigation-heading'
1742					).length
1743				) {
1744					var heading_height = jQuery(
1745						'.learndash-wrapper .ld-focus .ld-focus-sidebar .ld-course-navigation-heading'
1746					).height();
1747					offset_top += heading_height;
1748				}
1749				if (
1750					jQuery(
1751						'.learndash-wrapper .ld-focus .ld-focus-sidebar .ld-focus-sidebar-wrapper'
1752					).length
1753				) {
1754					var container_height = jQuery(
1755						'.learndash-wrapper .ld-focus .ld-focus-sidebar .ld-focus-sidebar-wrapper'
1756					).height();
1757					offset_top += container_height;
1758				}
1759
1760				var current_item_height = jQuery(sidebar_scrollTo).height();
1761				offset_top -= current_item_height;
1762
1763				sidebar_wrapper.animate(
1764					{
1765						scrollTop: sidebar_scrollTo.offset().top - offset_top,
1766					},
1767					1000
1768				);
1769			}
1770		}
1771	}
1772
1773	// Coupon processing.
1774
1775	function update_payment_forms(data) {
1776		$('#total-row').attr('data-total', data.total.value);
1777
1778		// Update PayPal form amount.
1779		$('form[name="buynow"] input[name="amount"]').val(data.total.value);
1780
1781		// Update Stripe form amount.
1782		$('form.learndash-stripe-checkout input[name="stripe_price"]').val(
1783			data.total.stripe_value
1784		);
1785
1786		// Remove Stripe Connect session to respect the new amount.
1787		const stripe_course_id = $(
1788			'.learndash-stripe-checkout input[name="stripe_course_id"]'
1789		).val();
1790
1791		if (stripe_course_id) {
1792			LD_Cookies.remove('ld_stripe_session_id_' + stripe_course_id); // Stripe Plugin (Checkout).
1793			LD_Cookies.remove(
1794				'ld_stripe_connect_session_id_' + stripe_course_id
1795			); // Stripe Connect in core.
1796		}
1797
1798		// Re-init Stripe Plugin (Legacy) to respect the new amount.
1799		if (typeof ld_init_stripe_legacy === 'function') {
1800			ld_init_stripe_legacy();
1801		}
1802	}
1803
1804	$('.btn-join').on('click', function (e) {
1805		if ($(this).hasClass('btn-disabled')) {
1806			e.preventDefault();
1807			return false;
1808		}
1809
1810		const supportsCoupon = $('#total-row').attr('data-supports-coupon');
1811
1812		if (!supportsCoupon) {
1813			return;
1814		}
1815
1816		const total = parseFloat($('#total-row').attr('data-total'));
1817
1818		if (0 === total) {
1819			$.ajax({
1820				type: 'POST',
1821				url: ldVars.ajaxurl,
1822				dataType: 'json',
1823				cache: false,
1824				data: {
1825					action: 'learndash_enroll_with_zero_price',
1826					nonce: $('#apply-coupon-form').data('nonce'),
1827					post_id: $('#apply-coupon-form').data('post-id'),
1828				},
1829				success(response) {
1830					if (response.success) {
1831						window.location.replace(response.data.redirect_url);
1832					} else {
1833						alert(response.data.message);
1834					}
1835				},
1836			});
1837
1838			e.preventDefault();
1839			return false;
1840		}
1841	});
1842
1843	$('#apply-coupon-form').on('submit', function (e) {
1844		e.preventDefault();
1845		const $el = $(this);
1846		const $wrapper = $el.closest('.ld-registration-order__items');
1847
1848		$.ajax({
1849			type: 'POST',
1850			url: ldVars.ajaxurl,
1851			dataType: 'json',
1852			cache: false,
1853			data: {
1854				action: 'learndash_apply_coupon',
1855				nonce: $(this).data('nonce'),
1856				coupon_code: $(this).find('#coupon-field').val(),
1857				post_id: $(this).data('post-id'),
1858			},
1859			success(response) {
1860				const isModernRegistration = $(
1861					'.ld-form__field-coupon_field'
1862				).length;
1863
1864				if (isModernRegistration) {
1865					$('#coupon-alerts .coupon-alert').hide();
1866
1867					const $alert = $('#coupon-alerts').find(
1868						response.success
1869							? '.coupon-alert-success'
1870							: '.coupon-alert-warning'
1871					);
1872
1873					if (response.success) {
1874						$wrapper
1875							.find('.ld-coupon__label-text')
1876							.html(response.data.coupon_code); // Set coupon code in totals.
1877
1878						$wrapper
1879							.find('.ld-coupon__value')
1880							.html('(' + response.data.discount + ')'); // Set discount value in totals.
1881
1882						$wrapper
1883							.find('.ld-registration-order__item-price-value')
1884							.html(response.data.total.formatted); // Update course/group price.
1885
1886						$wrapper
1887							.find('.ld-registration-order__total-price')
1888							.html(response.data.total.formatted); // Update Total.
1889
1890						$wrapper.addClass(
1891							'ld-registration-order__items--with-coupon'
1892						);
1893
1894						update_payment_forms(response.data);
1895					}
1896
1897					$alert
1898						.find('.ld-alert-messages')
1899						.html(response.data.message);
1900					$alert.fadeIn();
1901				} else {
1902					$('#coupon-alerts .coupon-alert').hide();
1903
1904					const $alert = $('#coupon-alerts').find(
1905						response.success
1906							? '.coupon-alert-success'
1907							: '.coupon-alert-warning'
1908					);
1909
1910					const $coupon_row = $('#coupon-row');
1911
1912					if (response.success) {
1913						$coupon_row
1914							.find('.purchase-label > span')
1915							.html(response.data.coupon_code); // Set coupon code in totals.
1916						$coupon_row
1917							.find('.purchase-value span')
1918							.html(response.data.discount); // Set discount value in totals.
1919						$coupon_row.css('display', 'flex').hide().fadeIn(); // Show a coupon row in totals.
1920						$('#total-row .purchase-value').html(
1921							response.data.total.formatted
1922						); // Update Total.
1923						$('#totals').show();
1924
1925						update_payment_forms(response.data);
1926					}
1927
1928					$alert
1929						.find('.ld-alert-messages')
1930						.html(response.data.message);
1931					$alert.fadeIn();
1932				}
1933			},
1934		});
1935	});
1936
1937	$('#remove-coupon-form').on('submit', function (e) {
1938		e.preventDefault();
1939		const $el = $(this);
1940		const $wrapper = $el.closest('.ld-registration-order__items');
1941
1942		$.ajax({
1943			type: 'POST',
1944			url: ldVars.ajaxurl,
1945			dataType: 'json',
1946			cache: false,
1947			data: {
1948				action: 'learndash_remove_coupon',
1949				nonce: $(this).data('nonce'),
1950				post_id: $(this).data('post-id'),
1951			},
1952			success(response) {
1953				const isModernRegistration = $(
1954					'.ld-form__field-coupon_field'
1955				).length;
1956
1957				if (isModernRegistration) {
1958					$('#coupon-alerts .coupon-alert').hide();
1959
1960					const $alert = $('#coupon-alerts').find(
1961						response.success
1962							? '.coupon-alert-success'
1963							: '.coupon-alert-warning'
1964					);
1965
1966					if (response.success) {
1967						$wrapper.removeClass(
1968							'ld-registration-order__items--with-coupon'
1969						);
1970						$wrapper.find('.ld-form__field-coupon_field').val(''); // Set coupon field empty.
1971						$wrapper
1972							.find('.ld-registration-order__item-price-value')
1973							.html(response.data.total.formatted); // Update course/group price.
1974						$wrapper
1975							.find('.ld-registration-order__total-price')
1976							.html(response.data.total.formatted); // Update Total.
1977
1978						update_payment_forms(response.data);
1979					}
1980
1981					$alert
1982						.find('.ld-alert-messages')
1983						.html(response.data.message);
1984					$alert.fadeIn();
1985				} else {
1986					$('#coupon-alerts .coupon-alert').hide();
1987
1988					const $alert = $('#coupon-alerts').find(
1989						response.success
1990							? '.coupon-alert-success'
1991							: '.coupon-alert-warning'
1992					);
1993
1994					if (response.success) {
1995						$('#coupon-row').hide(); // Hide a coupon row in totals.
1996						$('#coupon-field').val(''); // Set coupon field empty.
1997						$('#price-row .purchase-value').html(
1998							response.data.total.formatted
1999						); // Update Price.
2000						$('#subtotal-row .purchase-value').html(
2001							response.data.total.formatted
2002						); // Update Subtotal.
2003						$('#total-row .purchase-value').html(
2004							response.data.total.formatted
2005						); // Update Total.
2006						$('#totals').hide();
2007
2008						update_payment_forms(response.data);
2009					}
2010
2011					$alert
2012						.find('.ld-alert-messages')
2013						.html(response.data.message);
2014					$alert.fadeIn();
2015				}
2016			},
2017		});
2018	});
2019});
2020
2021function ldGetUrlVars() {
2022	var vars = {};
2023	var parts = window.location.href.replace(
2024		/[?&]+([^=&]+)=([^&]*)/gi,
2025		function (m, key, value) {
2026			vars[key] = value;
2027		}
2028	);
2029
2030	return vars;
2031}

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.