PageSourceSearch

https://lamaletra.com/wp-content/plugins/learnpress/assets/src/js/frontend/course.js?ver=6ab9c1af5aa8b

js lamaletra.com collected 2026-09-28 01:24:47 UTC 16,530 bytes, 658 lines download raw bytes

1/**
2 * LearnPress frontend course app.
3 *
4 * @version 3.2.0
5 * @author ThimPress
6 * @package LearnPress/JS/Course
7 */
8( function( $ ) {
9	'use strict';
10	function LP_Storage( key ) {
11		const storage = window.localStorage;
12		this.key = key;
13		this.get = function( id ) {
14			const val = storage.getItem( this.key ) || '',
15				sections = val.split( ',' );
16			if ( id ) {
17				id = id + '';
18				const pos = sections.indexOf( id );
19				if ( pos >= 0 ) {
20					return sections[ pos ];
21				}
22			}
23			return sections;
24		};
25		this.set = function( sections ) {
26			if ( typeof sections !== 'string' ) {
27				sections = sections.join( ',' );
28			}
29			storage.setItem( this.key, sections );
30			return sections.split( ',' );
31		};
32		this.hasSection = function( id ) {
33			id = id + '';
34			const sections = this.get(),
35				at = sections.indexOf( id );
36
37			return at >= 0 ? at : false;
38		};
39		this.add = function( id ) {
40			id = id + '';
41			const sections = this.get();
42			if ( this.hasSection( id ) ) {
43				return;
44			}
45			sections.push( id );
46			this.set( sections );
47			return sections;
48		};
49		this.remove = function( id ) {
50			id = id + '';
51			const at = this.hasSection( id );
52			if ( at !== false ) {
53				const sections = this.get();
54				sections.splice( at, 1 );
55				this.set( sections );
56				return sections;
57			}
58			return false;
59		};
60	}
61
62	/**
63	 * LP_Course
64	 *
65	 * @param settings
66	 * @class
67	 */
68	function LP_Course( settings ) {
69		const elLPOverlay = $( '.lp-overlay' );
70		const elFormCompleteLesson = $(	'form[name=learn-press-form-complete-lesson]' );
71		let sectionStorage = new LP_Storage( 'sections' ),
72			$body = $( 'body' ),
73			$content = $( '.content-item-scrollable' ),
74			$curriculum = $( '#learn-press-course-curriculum' ),
75			$contentItem = $( '#learn-press-content-item' ),
76			$curriculumScrollable = $curriculum.find( '.curriculum-scrollable' ),
77			$header = $( '#course-item-content-header' ),
78			$footer = $( '#course-item-content-footer' ),
79			$courseItems = $curriculum.find( '.course-item' ),
80			isShowingHeader = true,
81			fullScreen,
82			contentTop = 0,
83			headerTimer,
84			inPopup = false;
85
86		/**
87		 * Toggle answer option check/uncheck
88		 *
89		 * @param event
90		 */
91		function toggleAnswerOptions( event ) {
92			let $el = $( event.target ),
93				$chk;
94			if ( $el.is( 'input.option-check' ) ) {
95				return;
96			}
97
98			$chk = $el.closest( '.answer-option' ).find( 'input.option-check' );
99
100			if ( ! $chk.length ) {
101				return;
102			}
103
104			if ( $chk.is( ':disabled' ) ) {
105				return;
106			}
107			if ( $chk.is( ':checkbox' ) ) {
108				$chk[ 0 ].checked = ! $chk[ 0 ].checked;
109			} else {
110				$chk[ 0 ].checked = true;
111			}
112		}
113
114		/**
115		 * Show/Hide section content
116		 */
117		function toggleSection() {
118			const id = $( this ).closest( '.section' ).data( 'section-id' );
119			$( this ).siblings( '.section-content' ).slideToggle( function() {
120				if ( $( this ).is( ':visible' ) ) {
121					sectionStorage.remove( id );
122				} else {
123					sectionStorage.add( id );
124				}
125			} );
126		}
127
128		/**
129		 * Init sections
130		 */
131		function initSections() {
132			let $activeSection = $( '.course-item.current' ).closest( '.section' ),
133				sections = $( '.curriculum-sections' ).find( '.section' ),
134				sectionId = $activeSection.data( 'section-id' ),
135				hiddenSections = [];
136
137			if ( $activeSection ) {
138				hiddenSections = sectionStorage.remove( sectionId );
139			} else {
140				hiddenSections = sectionStorage.get();
141			}
142
143			for ( let i = 0; i < hiddenSections.length; i++ ) {
144				sections.filter( '[data-section-id="' + hiddenSections[ i ] + '"]' ).
145					find( '.section-content' ).
146					hide();
147			}
148		}
149
150		/**
151		 * Prepare form before submitting
152		 *
153		 * @param form
154		 */
155		function prepareForm( form ) {
156			const $answerOptions = $( '.answer-options' ),
157				$form = $( form ),
158				data = $answerOptions.serializeJSON(),
159				$hidden = $( '<input type="hidden" name="question-data" />' ).
160					val( JSON.stringify( data ) );
161
162			if ( ( $form.attr( 'method' ) + '' ).toLowerCase() !== 'post' ) {
163				return;
164			}
165
166			$form.find( 'input[name="question-data"]' ).remove();
167			return $form.append( $hidden ).
168				append( $( '<div />' ).append( $answerOptions.clone() ).hide() );
169		}
170
171		/**
172		 * Tab course event
173		 *
174		 * @param e
175		 * @param tab
176		 */
177		function onTabCourseClick( e, tab ) {
178			if ( $( document.body ).hasClass( 'course-item-popup' ) ) {
179				return;
180			}
181
182			const $tab = $( tab ),
183				$parent = $tab.closest( '.course-nav' );
184
185			if ( $parent.siblings().length === 0 ) {
186				return;
187			}
188			LP.setUrl( $tab.attr( 'href' ) );
189		}
190
191		/**
192		 * Event on press any key into search
193		 *
194		 * @param e
195		 * @return {boolean}
196		 */
197		function onSearchInputKeypress( e ) {
198			if ( e.type === 'keypress' && e.keyCode === 13 ) {
199				return false;
200			}
201
202			const s = this.value,
203				r = new RegExp( s, 'ig' );
204			$courseItems.map( function() {
205				const $item = $( this ),
206					itemName = $item.find( '.item-name' ).text();
207				if ( itemName.match( r ) || ! s.length ) {
208					$item.show();
209				} else {
210					$item.hide();
211				}
212			} );
213
214			$( '.section' ).show().each( function() {
215				if ( s.length ) {
216					if ( ! $( this ).
217						find( '.section-content' ).
218						children( ':visible' ).length ) {
219						$( this ).hide();
220					} else {
221						$( this ).show();
222					}
223				} else {
224					$( this ).show();
225				}
226			} );
227			$( this ).
228				closest( '.course-item-search' ).
229				toggleClass( 'has-keyword', !! this.value.length );
230		}
231
232		function onClearSearchInputClick( e ) {
233			const $form = $( this ).closest( '.course-item-search' );
234			$form.find( 'input' ).val( '' ).trigger( 'keyup' );
235		}
236
237		function onClickQM() {
238			$( '#qm' ).css( { 'z-index': 999999999, position: 'relative' } );
239			$( 'html, body' ).css( 'overflow', 'auto' );
240		}
241
242		function getCurriculumWidth() {
243			return $curriculum.outerWidth();
244		}
245
246		function maybeShowCurriculum( e ) {
247			return;
248			const offset = $( this ).offset(),
249				offsetX = e.pageX - offset.left,
250				curriculumWidth = getCurriculumWidth();
251
252			if ( ! fullScreen || ( offsetX > 50 ) ) {
253				return;
254			}
255
256			timeoutToClose();
257
258			if ( ! isShowingHeader ) {
259				$curriculum.stop().animate( {
260					left: 0,
261				} );
262
263				$contentItem.stop().animate( {
264					left: curriculumWidth,
265				} );
266
267				$footer.stop().animate( {
268					left: curriculumWidth,
269				}, function() {
270					$( document, window ).trigger( 'learn-press/toggle-content-item' );
271				} );
272
273				$header.find( '.course-item-search' ).show();
274				toggleEventShowCurriculum( true );
275				isShowingHeader = true;
276			}
277		}
278
279		function toggleEventShowCurriculum( b ) {
280			$( document )[ b ? 'off' : 'on' ]( 'mousemove.maybe-show-curriculum',
281				'body', maybeShowCurriculum );
282		}
283
284		function timeoutToClose() {
285			headerTimer && clearTimeout( headerTimer );
286			headerTimer = setTimeout( function() {
287				const curriculumWidth = getCurriculumWidth();
288
289				if ( ! fullScreen ) {
290					return;
291				}
292
293				$curriculum.stop().animate( {
294					left: -curriculumWidth,
295				} );
296
297				$contentItem.stop().animate( {
298					left: 0,
299				} );
300
301				$footer.stop().animate( {
302					left: 0,
303				}, function() {
304					$( document, window ).trigger( 'learn-press/toggle-content-item' );
305				} );
306
307				$header.find( '.course-item-search' ).hide();
308
309				isShowingHeader = false;
310				toggleEventShowCurriculum();
311			}, 3000 );
312		}
313
314		function toggleContentItem( e ) {
315			e.preventDefault();
316			const curriculumWidth = getCurriculumWidth();
317			let sidebarPosition = 'left';
318
319			if ( 'rtl' === $( 'html' ).attr( 'dir' ) ) {
320				sidebarPosition = 'right';
321			}
322
323			fullScreen = $body.toggleClass( 'full-screen-content-item' ).
324				hasClass( 'full-screen-content-item' );
325
326			if ( 'right' === sidebarPosition ) {
327				curriculumRight();
328			} else {
329				curriculumLeft();
330			}
331
332			function curriculumRight() {
333				$curriculum.stop().animate( {
334					right: fullScreen ? -curriculumWidth : 0,
335				} );
336
337				$contentItem.stop().animate( {
338					right: fullScreen ? 0 : curriculumWidth,
339				} );
340
341				$footer.stop().animate( {
342					right: fullScreen ? 0 : curriculumWidth,
343				}, function() {
344					$( document, window ).trigger( 'learn-press/toggle-content-item' );
345				} );
346			}
347
348			function curriculumLeft() {
349				$curriculum.stop().animate( {
350					left: fullScreen ? -curriculumWidth : 0,
351				} );
352
353				$contentItem.stop().animate( {
354					left: fullScreen ? 0 : curriculumWidth,
355				} );
356
357				$footer.stop().animate( {
358					left: fullScreen ? 0 : curriculumWidth,
359				}, function() {
360					$( document, window ).trigger( 'learn-press/toggle-content-item' );
361				} );
362			}
363
364			isShowingHeader = ! fullScreen;
365			window.localStorage && window.localStorage.setItem( 'lp-full-screen',
366				fullScreen ? 'yes' : 'no' );
367
368			fullScreen && toggleEventShowCurriculum();
369			$header.find( '.course-title' ).
370				stop().
371				animate( { marginLeft: fullScreen ? -curriculumWidth : 0 } );
372			$header.find( '.course-item-search' ).
373				stop().
374				animate( { opacity: fullScreen ? 0 : 1 } ).
375				css( { visibility: fullScreen ? 'hidden' : 'visible' } );
376		}
377
378		function initEvents() {
379			// Live events
380			$( document ).
381				on( 'learn-press/nav-tabs/clicked', onTabCourseClick ).
382				on( 'keyup keypress', '.course-item-search input',
383					onSearchInputKeypress ).
384				on( 'click', '.course-item-search button', onClearSearchInputClick ).
385				on( 'click', '#wp-admin-bar-query-monitor', onClickQM ).
386				on( 'click', '.answer-options .answer-option', toggleAnswerOptions ).
387				on( 'click', '.section-header', toggleSection ).
388				on( 'submit', 'form.lp-form', function() {
389					prepareForm( this );
390				} ).
391				on( 'click', '.toggle-content-item', toggleContentItem );
392
393			$curriculum.hover( function() {
394				headerTimer && clearTimeout( headerTimer );
395			}, function() {
396				if ( fullScreen ) {
397					timeoutToClose();
398				}
399			} );
400		}
401
402		function fitVideo() {
403			const $wrapContent = $( '.content-item-summary.content-item-video' );
404
405			if ( ! $wrapContent.length ) {
406				return;
407			}
408
409			let $entryVideo = $wrapContent.find( '.entry-video' ),
410				$frame = $entryVideo.find( 'iframe' ),
411				width = $frame.attr( 'width' ),
412				height = $frame.attr( 'height' ),
413				ratio = 1,
414				contentHeight,
415				timer;
416
417			function resizeVideo() {
418				const frameWidth = $frame.width();
419				contentHeight = frameWidth * ratio;
420				$frame.css( {
421					height: contentHeight,
422					marginLeft: ( $entryVideo.width() - frameWidth ) / 2,
423				} );
424
425				$wrapContent.css( {
426					paddingTop: contentHeight,
427				} );
428			}
429
430			if ( ! $entryVideo.length ) {
431				return false;
432			}
433
434			if ( width && height ) {
435				if ( width.indexOf( '%' ) === -1 && height.indexOf( '%' ) === -1 ) {
436					ratio = height / width;
437				}
438			}
439
440			$( window ).
441				on( 'resize.fit-content-video learn-press/toggle-content-item',
442					function() {
443						timer && clearTimeout( timer );
444						timer = setTimeout( resizeVideo, 250 );
445					} ).
446				trigger( 'resize.fit-content-video' );
447
448			$( '.content-item-scrollable' ).scroll( function() {
449				$( this ).find( '.entry-video' ).css( 'padding-top', this.scrollTop );
450			} );
451		}
452
453		/**
454		 * Init
455		 */
456		function init() {
457			inPopup = $body.hasClass( 'course-item-popup' );
458			initSections();
459			initEvents();
460
461			if ( ! inPopup ) {
462				return;
463			}
464
465			// $contentItem.appendTo( $body );
466			// $curriculum.appendTo( $body );
467
468			if ( $( '#wpadminbar' ).length ) {
469				$body.addClass( 'wpadminbar' );
470				contentTop = 32;
471			}
472
473			fitVideo();
474
475			fullScreen = window.localStorage && 'yes' ===
476					window.localStorage.getItem( 'lp-full-screen' );
477			if ( $( window ).width() <= 768 ) {
478				fullScreen = true;
479			}
480			if ( fullScreen ) {
481				const curriculumWidth = getCurriculumWidth();
482				$body.addClass( 'full-screen-content-item' );
483				$contentItem.css( 'left', 0 );
484				$curriculum.css( 'left', -curriculumWidth );
485				$footer.css( 'left', 0 );
486				isShowingHeader = ! fullScreen;
487				$header.find( '.course-title' ).
488					css( { marginLeft: fullScreen ? -curriculumWidth : 0 } );
489				$header.find( '.course-item-search' ).
490					css( { opacity: fullScreen ? 0 : 1 } );
491				toggleEventShowCurriculum();
492			}
493
494			setTimeout( function() {
495				const $cs = $body.find( '.curriculum-sections' ).parent();
496				$cs.scrollTo( $cs.find( '.course-item.current' ), 100 );
497
498				if ( window.location.hash ) {
499					$( '.content-item-scrollable:last' ).
500						scrollTo( $( window.location.hash ) );
501				}
502			}, 300 );
503
504			$body.css( 'opacity', 1 );
505		}
506
507		// new LP.Alerts();
508
509		/**
510		 * Event complete items of course
511		 */
512		this.completeItemsCourse = function() {
513			const lp_course = this;
514			$( document ).on( 'click', '.lp-btn-complete-item', function( e ) {
515				e.preventDefault();
516				const elFormSubmit = $( this ).closest( 'form' );
517
518				if ( 'yes' === lpGlobalSettings.show_popup_confirm_finish ) {
519					lp_course.lpModalOverlay.setElCalledModal( elFormSubmit );
520					lp_course.lpModalOverlay.callBackYes = function() {
521						elFormSubmit.submit();
522					};
523					elLPOverlay.show();
524				} else {
525					elFormSubmit.submit();
526				}
527			} );
528		};
529
530		this.lpModalOverlay = {
531			elMessage: null,
532			elTitle: null,
533			callBackYes: null,
534			elCalledModal: null,
535			init() {
536				const lpModalOverlay = this;
537				this.elMessage = elLPOverlay.find( '.message' );
538				this.elTitle = elLPOverlay.find( '.modal-title' );
539
540				$( document ).on( 'click', '.close, .btn-no', function() {
541					elLPOverlay.hide();
542				} );
543
544				$( document ).on( 'click', '.btn-yes', function() {
545					lpModalOverlay.callBackYes();
546				} );
547			},
548			setElCalledModal( elCalledModal ) {
549				this.elCalledModal = elCalledModal;
550
551				this.setContentModal();
552			},
553			setContentModal() {
554				this.elTitle.text( this.elCalledModal.data( 'title' ) );
555				this.elMessage.text( this.elCalledModal.data( 'confirm' ) );
556			},
557		};
558
559		this.finishCourse = function() {
560			const lp_course = this;
561			const elFormFinishCourse = $( '.form-button-finish-course' );
562
563			if ( ! elFormFinishCourse ) {
564				return;
565			}
566
567			const elBtnFinishCourse = elFormFinishCourse.find( '.lp-btn-finish-course' );
568
569			elBtnFinishCourse.on( 'click', function( e ) {
570				e.preventDefault();
571
572				if ( 'yes' === lpGlobalSettings.show_popup_confirm_finish ) {
573					lp_course.lpModalOverlay.setElCalledModal( elFormFinishCourse );
574					lp_course.lpModalOverlay.callBackYes = function() {
575						elFormFinishCourse.submit();
576					};
577					elLPOverlay.show();
578				} else {
579					elFormFinishCourse.submit();
580				}
581			} );
582		};
583
584		this.scrollToItemViewing = function() {
585			const elItemViewing = $( '.viewing-course-item' );
586
587			if ( elItemViewing.length ) {
588				const heightCourseItemContentHeader = $( '#course-item-content-header' ).outerHeight();
589				const heightSectionTitle = $( '.section-title' ).outerHeight();
590				const heightSectionHeader = $( '.section-header' ).outerHeight();
591				const regex = new RegExp( '^viewing-course-item-([0-9].*)' );
592				const classList = elItemViewing.attr( 'class' );
593				const classArr = classList.split( /\s+/ );
594				let idItem = 0;
595
596				$.each( classArr, function( i, className ) {
597					const compare = regex.exec( className );
598
599					if ( compare ) {
600						idItem = compare[ 1 ];
601						return false;
602					}
603				} );
604
605				if ( 0 === idItem ) {
606					return;
607				}
608
609				const elItemCurrent = $( '.course-item-' + idItem );
610				const offSetTop = elItemCurrent.offset().top;
611				let numberOffSetTop = offSetTop - heightCourseItemContentHeader;
612
613				if ( undefined === heightSectionTitle ) {
614					numberOffSetTop = numberOffSetTop - heightSectionHeader + 20;
615				} else {
616					numberOffSetTop = numberOffSetTop - heightSectionTitle;
617				}
618
619				$( '#learn-press-course-curriculum' ).animate( {
620					scrollTop: numberOffSetTop + 7,
621				}, 800 );
622			}
623		};
624
625		init();
626	}
627
628	$( function() {
629		const lp_course = new LP_Course( {} );
630
631		lp_course.lpModalOverlay.init();
632		// lp_course.completeLesson();
633		lp_course.completeItemsCourse();
634		lp_course.finishCourse();
635		lp_course.scrollToItemViewing();
636
637		// $( '#learn-press-course-curriculum' ).animate( {
638		//   scrollTop: 300
639		// }, 800);
640
641		$( this ).on( 'submit', 'form[name="course-external-link"]', function() {
642			const redirect = $( this ).attr( 'action' );
643			if ( redirect ) {
644				window.location.href = redirect;
645				return false;
646			}
647		} );
648
649		// Reload course page when course duration expired
650		const course_item_html = $( '.course-item-is-blocked' ),
651			course_item_value = course_item_html.val();
652		if ( course_item_html.length && course_item_value < 2147483647 ) {
653			setTimeout( function() {
654				window.location.reload( true );
655			}, course_item_value );
656		}
657	} );
658}( jQuery ) );

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.