PluginProbe
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses / 4.4.9
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses v4.4.9
4.4.9 4.4.8 4.4.7 4.4.6 4.4.5 4.4.4 4.4.3 4.4.2 4.4.1 4.4.0 4.3.9.1 4.3.9 4.3.8 4.3.7 4.1.6.9 4.1.6.9.1 4.1.6.9.2 4.1.6.9.3 4.1.6.9.4 4.1.7 4.1.7.1 4.1.7.2 4.1.7.3 4.1.7.3.1 4.1.7.3.2 All 140 releases
← All changes | assets/js/dist/admin/learnpress.js +294 -161 4.3.8 → 4.4.9 View file →
@@ -1,4 +1,4 @@
1 1 /******/ (() => { // webpackBootstrap
2 2 /******/ "use strict";
3 3 /******/ var __webpack_modules__ = ({
4 4
@@ -11,8 +11,9 @@
11 11 __webpack_require__.r(__webpack_exports__);
12 12 /* harmony export */ __webpack_require__.d(__webpack_exports__, {
13 13 /* harmony export */ debounce: () => (/* binding */ debounce),
14 14 /* harmony export */ eventHandlers: () => (/* binding */ eventHandlers),
15 +/* harmony export */ fullScreenView: () => (/* binding */ fullScreenView),
15 16 /* harmony export */ getDataOfForm: () => (/* binding */ getDataOfForm),
16 17 /* harmony export */ getFieldKeysOfForm: () => (/* binding */ getFieldKeysOfForm),
17 18 /* harmony export */ listenElementCreated: () => (/* binding */ listenElementCreated),
18 19 /* harmony export */ listenElementViewed: () => (/* binding */ listenElementViewed),
@@ -24,18 +25,19 @@
24 25 /* harmony export */ lpOnElementReady: () => (/* binding */ lpOnElementReady),
25 26 /* harmony export */ lpSetLoadingEl: () => (/* binding */ lpSetLoadingEl),
26 27 /* harmony export */ lpShowHideEl: () => (/* binding */ lpShowHideEl),
27 28 /* harmony export */ mergeDataWithDatForm: () => (/* binding */ mergeDataWithDatForm),
28 -/* harmony export */ toggleCollapse: () => (/* binding */ toggleCollapse)
29 +/* harmony export */ toggleCollapse: () => (/* binding */ toggleCollapse),
30 +/* harmony export */ toggleEnable: () => (/* binding */ toggleEnable)
29 31 /* harmony export */ });
30 -/**
31 - * Utils functions
32 - *
33 - * @param url
34 - * @param data
35 - * @param functions
36 - * @since 4.2.5.1
37 - * @version 1.0.6
32 +/**
33 + * Utils functions
34 + *
35 + * @param url
36 + * @param data
37 + * @param functions
38 + * @since 4.2.5.1
39 + * @version 1.0.7
38 40 */
39 41 const lpClassName = {
40 42 hidden: 'lp-hidden',
41 43 loading: 'loading',
@@ -40,9 +42,12 @@
40 42 hidden: 'lp-hidden',
41 43 loading: 'loading',
42 44 elCollapse: 'lp-collapse',
43 45 elSectionToggle: '.lp-section-toggle',
44 - elTriggerToggle: '.lp-trigger-toggle'
46 + elTriggerToggle: '.lp-trigger-toggle',
47 + elBtnFullScreen: '.lp-btn-full-screen-view',
48 + elFullScreen: 'lp-full-screen-view',
49 + elBtnFullScreenClose: 'lp-full-screen-view__close'
45 50 };
46 51 const lpFetchAPI = (url, data = {}, functions = {}) => {
47 52 if ('function' === typeof functions.before) {
48 53 functions.before();
@@ -64,12 +69,12 @@
64 69 }
65 70 });
66 71 };
67 72
68 -/**
69 - * Get current URL without params.
70 - *
71 - * @since 4.2.5.1
73 +/**
74 + * Get current URL without params.
75 + *
76 + * @since 4.2.5.1
72 77 */
73 78 const lpGetCurrentURLNoParam = () => {
74 79 let currentUrl = window.location.href;
75 80 const hasParams = currentUrl.includes('?');
@@ -85,14 +90,14 @@
85 90 });
86 91 return url;
87 92 };
88 93
89 -/**
90 - * Listen element viewed.
91 - *
92 - * @param el
93 - * @param callback
94 - * @since 4.2.5.8
94 +/**
95 + * Listen element viewed.
96 + *
97 + * @param el
98 + * @param callback
99 + * @since 4.2.5.8
95 100 */
96 101 const listenElementViewed = (el, callback) => {
97 102 const observerSeeItem = new IntersectionObserver(function (entries) {
98 103 for (const entry of entries) {
@@ -103,13 +108,13 @@
103 108 });
104 109 observerSeeItem.observe(el);
105 110 };
106 111
107 -/**
108 - * Listen element created.
109 - *
110 - * @param callback
111 - * @since 4.2.5.8
112 +/**
113 + * Listen element created.
114 + *
115 + * @param callback
116 + * @since 4.2.5.8
112 117 */
113 118 const listenElementCreated = callback => {
114 119 const observerCreateItem = new MutationObserver(function (mutations) {
115 120 mutations.forEach(function (mutation) {
@@ -128,14 +133,14 @@
128 133 });
129 134 // End.
130 135 };
131 136
132 -/**
133 - * Listen element created.
134 - *
135 - * @param selector
136 - * @param callback
137 - * @since 4.2.7.1
137 +/**
138 + * Listen element created.
139 + *
140 + * @param selector
141 + * @param callback
142 + * @since 4.2.7.1
138 143 */
139 144 const lpOnElementReady = (selector, callback) => {
140 145 const element = document.querySelector(selector);
141 146 if (element) {
@@ -276,17 +281,17 @@
276 281 };
277 282 return dataHandle;
278 283 };
279 284
280 -/**
281 - * Event trigger
282 - * For each list of event handlers, listen event on document.
283 - *
284 - * eventName: 'click', 'change', ...
285 - * eventHandlers = [ { selector: '.lp-button', callBack: function(){}, class: object } ]
286 - *
287 - * @param eventName
288 - * @param eventHandlers
285 +/**
286 + * Event trigger
287 + * For each list of event handlers, listen event on document.
288 + *
289 + * eventName: 'click', 'change', ...
290 + * eventHandlers = [ { selector: '.lp-button', callBack: function(){}, class: object } ]
291 + *
292 + * @param eventName
293 + * @param eventHandlers
289 294 */
290 295 const eventHandlers = (eventName, eventHandlers) => {
291 296 document.addEventListener(eventName, e => {
292 297 const target = e.target;
@@ -327,32 +332,32 @@
327 332 });
328 333 });
329 334 };
330 335
331 -/**
332 - * Debounce - delays function execution until after `wait` ms of inactivity.
333 - *
334 - * Each call resets the timer. Only the last call in a burst executes.
335 - *
336 - * USE CASES:
337 - * - Search inputs, form validation, window resize
338 - * - Multiple elements need independent timers
339 - * - When you need to call with different arguments
340 - *
341 - * EXAMPLES:
342 - * const debouncedSearch = debounce( (query) => fetchResults(query), 300 );
343 - * searchInput.addEventListener('input', (e) => debouncedSearch(e.target.value));
344 - *
345 - * const debouncedResize = debounce( recalculateLayout, 250 );
346 - * window.addEventListener('resize', debouncedResize);
347 - *
348 - * ⚠️ Create ONCE outside event handlers, not inside.
349 - *
350 - * @param {Function} func - Function to debounce (can be anonymous)
351 - * @param {number} wait - Milliseconds to wait (default: 500)
352 - * @return {Function} Debounced wrapper function
353 - * @since 4.3.7
354 - * @version 1.0.0
336 +/**
337 + * Debounce - delays function execution until after `wait` ms of inactivity.
338 + *
339 + * Each call resets the timer. Only the last call in a burst executes.
340 + *
341 + * USE CASES:
342 + * - Search inputs, form validation, window resize
343 + * - Multiple elements need independent timers
344 + * - When you need to call with different arguments
345 + *
346 + * EXAMPLES:
347 + * const debouncedSearch = debounce( (query) => fetchResults(query), 300 );
348 + * searchInput.addEventListener('input', (e) => debouncedSearch(e.target.value));
349 + *
350 + * const debouncedResize = debounce( recalculateLayout, 250 );
351 + * window.addEventListener('resize', debouncedResize);
352 + *
353 + * ⚠️ Create ONCE outside event handlers, not inside.
354 + *
355 + * @param {Function} func - Function to debounce (can be anonymous)
356 + * @param {number} wait - Milliseconds to wait (default: 500)
357 + * @return {Function} Debounced wrapper function
358 + * @since 4.3.7
359 + * @version 1.0.0
355 360 */
356 361 const debounce = (func, wait = 500) => {
357 362 let timer;
358 363 return args => {
@@ -360,8 +365,141 @@
360 365 timer = setTimeout(() => func(args), wait);
361 366 };
362 367 };
363 368
369 +/**
370 + * Initialize lp-toggle-enable components.
371 + *
372 + * Finds all `.lp-toggle-enable` elements and wires up toggle behavior.
373 + * Reads initial state from `data-enabled` attribute ("true"/"false").
374 + * Calls `data-on-toggle` callback (if provided via options) on state change.
375 + *
376 + * HTML structure:
377 + * <label class="lp-toggle-enable" data-enabled="true">
378 + * <input type="checkbox" class="lp-toggle-enable__input" />
379 + * <span class="lp-toggle-enable__track"></span>
380 + * </label>
381 + *
382 + * @param {string} selector CSS selector for toggle elements (default: '.lp-toggle-enable')
383 + * @param {Function} onToggle Optional callback( el, isEnabled ) called on state change
384 + * @since 4.4.5
385 + * @version 1.0.0
386 + */
387 +window.lpToggleEnableInit = 0;
388 +const toggleEnable = (onToggle = null) => {
389 + if (window.lpToggleEnableInit) {
390 + return;
391 + }
392 + window.lpToggleEnableInit = 1;
393 + const selector = '.lp-toggle-enable';
394 + const updateUI = (toggle, isEnabled) => {
395 + toggle.classList.toggle('is-enabled', isEnabled);
396 + const input = toggle.querySelector('.lp-toggle-enable__input');
397 + if (input) {
398 + input.checked = isEnabled;
399 + input.value = isEnabled ? '1' : '0';
400 + }
401 + };
402 +
403 + // Delegate click handling via eventHandlers.
404 + eventHandlers('click', [{
405 + selector,
406 + callBack: args => {
407 + const {
408 + e,
409 + target
410 + } = args;
411 + const toggle = target.closest(selector);
412 + if (!toggle || toggle.classList.contains('is-disabled')) {
413 + return;
414 + }
415 + e.preventDefault();
416 + const isEnabled = !toggle.classList.contains('is-enabled');
417 + updateUI(toggle, isEnabled);
418 + if ('function' === typeof onToggle) {
419 + onToggle(toggle, isEnabled);
420 + }
421 + }
422 + }]);
423 +};
424 +
425 +/**
426 + * Initialize custom fullscreen view buttons.
427 + *
428 + * Delegates clicks on `.lp-btn-full-screen-view` buttons to
429 + * `lpToggleFullscreenView`. Reads the `data-target` attribute to find the
430 + * target element. Falls back to the button's parent element when
431 + * `data-target` is not provided.
432 + *
433 + * @since 4.4.5
434 + * @version 1.0.0
435 + */
436 +window.lpFullScreenViewInit = 0;
437 +const fullScreenView = () => {
438 + if (window.lpFullScreenViewInit) {
439 + return;
440 + }
441 + window.lpFullScreenViewInit = 1;
442 + let lastScrollY = 0;
443 + const lpToggleFullscreenView = (elTarget, elBtnFullScreen = null) => {
444 + const isFullscreen = elTarget.classList.contains(lpClassName.elFullScreen);
445 + if (isFullscreen) {
446 + elTarget.classList.remove(lpClassName.elFullScreen);
447 + document.documentElement.classList.remove('lp-full-screen-active');
448 + window.scrollTo(0, lastScrollY);
449 + } else {
450 + lastScrollY = window.scrollY;
451 + elTarget.classList.add(lpClassName.elFullScreen);
452 + document.documentElement.classList.add('lp-full-screen-active');
453 + }
454 + if (!isFullscreen) {
455 + if (!elTarget.querySelector(`.${lpClassName.elBtnFullScreenClose}`)) {
456 + const closeButton = document.createElement('button');
457 + closeButton.type = 'button';
458 + closeButton.className = lpClassName.elBtnFullScreenClose;
459 + closeButton.setAttribute('aria-label', 'Close');
460 + closeButton.innerHTML = lpData.i18n.closeButtonFullScreen || 'Close &times;';
461 + closeButton.addEventListener('click', e => {
462 + e.preventDefault();
463 + lpToggleFullscreenView(elTarget);
464 + });
465 + elTarget.appendChild(closeButton);
466 + }
467 + } else {
468 + const closeButton = elTarget.querySelector(`.${lpClassName.elBtnFullScreenClose}`);
469 + if (closeButton) {
470 + closeButton.remove();
471 + }
472 + }
473 + };
474 + eventHandlers('click', [{
475 + selector: lpClassName.elBtnFullScreen,
476 + callBack: args => {
477 + const {
478 + e,
479 + target
480 + } = args;
481 + const elBtnFullScreen = target.closest(lpClassName.elBtnFullScreen);
482 + if (!elBtnFullScreen) {
483 + console.log('No full screen button found');
484 + return;
485 + }
486 + e.preventDefault();
487 + let elTarget = null;
488 + const targetSelector = elBtnFullScreen.dataset.targetFullscreen;
489 + console.log(targetSelector);
490 + if (targetSelector) {
491 + elTarget = document.querySelector(targetSelector);
492 + }
493 + if (!elTarget) {
494 + console.log('No target element found');
495 + return;
496 + }
497 + lpToggleFullscreenView(elTarget, elBtnFullScreen);
498 + }
499 + }]);
500 +};
501 +
364 502 /***/ }
365 503
366 504 /******/ });
367 505 /************************************************************************/
@@ -549,20 +687,19 @@
549 687 $(this).closest('.lp_course_extra_meta_box__content').find('.lp_course_extra_meta_box__field').last().find('input').trigger('focus');
550 688 return false;
551 689 });
552 690
553 - /*document.querySelectorAll( '.lp_course_extra_meta_box__fields' ).forEach( ( ele ) => {
554 - ele.addEventListener( 'keydown', ( e ) => {
555 - const inputs = ele.querySelectorAll( '.lp_course_extra_meta_box__input' );
556 -
557 - if ( e.keyCode === 13 ) {
558 - e.preventDefault();
559 - inputs.forEach( ( input ) => {
560 - input.blur();
561 - } );
562 - return false;
563 - }
564 - } );
691 + /*document.querySelectorAll( '.lp_course_extra_meta_box__fields' ).forEach( ( ele ) => {
692 + ele.addEventListener( 'keydown', ( e ) => {
693 + const inputs = ele.querySelectorAll( '.lp_course_extra_meta_box__input' );
694 + if ( e.keyCode === 13 ) {
695 + e.preventDefault();
696 + inputs.forEach( ( input ) => {
697 + input.blur();
698 + } );
699 + return false;
700 + }
701 + } );
565 702 } );*/
566 703
567 704 $('.lp_course_extra_meta_box__fields').on('click', 'a.delete', function () {
568 705 $(this).closest('.lp_course_extra_meta_box__field').remove();
@@ -584,21 +721,20 @@
584 721 $(this).closest('.lp_course_faq_meta_box__content').find('.lp_course_faq_meta_box__fields').append($(this).data('add'));
585 722 return false;
586 723 });
587 724
588 - /*document.querySelectorAll( '.lp_course_faq_meta_box__fields' ).forEach( ( ele ) => {
589 - ele.addEventListener( 'keydown', ( e ) => {
590 - const inputs = ele.querySelectorAll( '.lp_course_faq_meta_box__field input' );
591 - const textareas = ele.querySelectorAll( '.lp_course_faq_meta_box__field textarea' );
592 -
593 - if ( e.keyCode === 13 ) {
594 - e.preventDefault();
595 - [ ...inputs, ...textareas ].forEach( ( input ) => {
596 - input.blur();
597 - } );
598 - return false;
599 - }
600 - } );
725 + /*document.querySelectorAll( '.lp_course_faq_meta_box__fields' ).forEach( ( ele ) => {
726 + ele.addEventListener( 'keydown', ( e ) => {
727 + const inputs = ele.querySelectorAll( '.lp_course_faq_meta_box__field input' );
728 + const textareas = ele.querySelectorAll( '.lp_course_faq_meta_box__field textarea' );
729 + if ( e.keyCode === 13 ) {
730 + e.preventDefault();
731 + [ ...inputs, ...textareas ].forEach( ( input ) => {
732 + input.blur();
733 + } );
734 + return false;
735 + }
736 + } );
601 737 } );*/
602 738
603 739 $('.lp_course_faq_meta_box__fields').on('click', 'a.delete', function () {
604 740 $(this).closest('.lp_course_faq_meta_box__field').remove();
@@ -821,59 +957,59 @@
821 957 }).trigger('lp-metabox-course-tab-panels');
822 958 };
823 959
824 960 // use to show and hide field condition logic metabox.
825 -/*const lpMetaboxCondition = () => {
826 - const fields = document.querySelectorAll( '.lp-meta-box .form-field' );
827 -
828 - fields.forEach( ( field ) => {
829 - if ( field.hasAttribute( 'data-show' ) && field.dataset.show ) {
830 - lpMetaboxConditionType( field, field.dataset.show, 'show' );
831 - } else if ( field.hasAttribute( 'data-hide' ) && field.dataset.hide ) {
832 - lpMetaboxConditionType( field, field.dataset.hide, 'hide' );
833 - }
834 - } );
961 +/*const lpMetaboxCondition = () => {
962 + const fields = document.querySelectorAll( '.lp-meta-box .form-field' );
963 +
964 + fields.forEach( ( field ) => {
965 + if ( field.hasAttribute( 'data-show' ) && field.dataset.show ) {
966 + lpMetaboxConditionType( field, field.dataset.show, 'show' );
967 + } else if ( field.hasAttribute( 'data-hide' ) && field.dataset.hide ) {
968 + lpMetaboxConditionType( field, field.dataset.hide, 'hide' );
969 + }
970 + } );
835 971 };*/
836 972
837 -/*const lpMetaboxConditionType = ( field, conditions, typeCondition = 'show' ) => {
838 - const condition = JSON.parse( conditions ),
839 - eles = document.querySelectorAll( `input[id^="${ condition[ 0 ] }"]` ),
840 - logic = condition[ 1 ] === '=' ? '=' : '!=',
841 - dataLogic = condition[ 2 ];
842 -
843 - const switchCase = ( type, ele, target ) => {
844 - switch ( type ) {
845 - case 'checkbox':
846 - let val = dataLogic;
847 -
848 - if ( dataLogic === 'yes' || dataLogic === '1' || dataLogic === 1 || dataLogic === 'true' ) {
849 - val = true;
850 - } else if ( dataLogic === 'no' || dataLogic === '0' || dataLogic === 0 || dataLogic === 'false' ) {
851 - val = false;
852 - }
853 -
854 - if ( logic == '!=' && val !== Boolean( target ? target.checked : ele.checked ) ) {
855 - field.style.display = typeCondition === 'show' ? '' : 'none';
856 - } else if ( logic == '=' && val == Boolean( target ? target.checked : ele.checked ) ) {
857 - field.style.display = typeCondition === 'show' ? '' : 'none';
858 - } else {
859 - field.style.display = typeCondition === 'show' ? 'none' : '';
860 - }
861 - break;
862 - }
863 - };
864 -
865 - eles.forEach( ( ele ) => {
866 - const type = ele.getAttribute( 'type' );
867 -
868 - switchCase( type, ele );
869 -
870 - ele.addEventListener( 'change', ( e ) => {
871 - const target = e.target;
872 -
873 - switchCase( type, ele, target );
874 - } );
875 - } );
973 +/*const lpMetaboxConditionType = ( field, conditions, typeCondition = 'show' ) => {
974 + const condition = JSON.parse( conditions ),
975 + eles = document.querySelectorAll( `input[id^="${ condition[ 0 ] }"]` ),
976 + logic = condition[ 1 ] === '=' ? '=' : '!=',
977 + dataLogic = condition[ 2 ];
978 +
979 + const switchCase = ( type, ele, target ) => {
980 + switch ( type ) {
981 + case 'checkbox':
982 + let val = dataLogic;
983 +
984 + if ( dataLogic === 'yes' || dataLogic === '1' || dataLogic === 1 || dataLogic === 'true' ) {
985 + val = true;
986 + } else if ( dataLogic === 'no' || dataLogic === '0' || dataLogic === 0 || dataLogic === 'false' ) {
987 + val = false;
988 + }
989 +
990 + if ( logic == '!=' && val !== Boolean( target ? target.checked : ele.checked ) ) {
991 + field.style.display = typeCondition === 'show' ? '' : 'none';
992 + } else if ( logic == '=' && val == Boolean( target ? target.checked : ele.checked ) ) {
993 + field.style.display = typeCondition === 'show' ? '' : 'none';
994 + } else {
995 + field.style.display = typeCondition === 'show' ? 'none' : '';
996 + }
997 + break;
998 + }
999 + };
1000 +
1001 + eles.forEach( ( ele ) => {
1002 + const type = ele.getAttribute( 'type' );
1003 +
1004 + switchCase( type, ele );
1005 +
1006 + ele.addEventListener( 'change', ( e ) => {
1007 + const target = e.target;
1008 +
1009 + switchCase( type, ele, target );
1010 + } );
1011 + } );
876 1012 };*/
877 1013
878 1014 /** End Nhamdv code */
879 1015
@@ -1053,31 +1189,28 @@
1053 1189 }
1054 1190 }
1055 1191 });
1056 1192
1057 - /*const datePickerSelect = function( datepicker ) {
1058 - const option = $( datepicker ).is( '#_lp_sale_start' ) ? 'minDate' : 'maxDate',
1059 - otherDateField = 'minDate' === option ? $( '#_lp_sale_end' ) : $( '#_lp_sale_start' ),
1060 - date = $( datepicker ).datetimepicker( 'getDate' );
1061 -
1062 - $( otherDateField ).datetimepicker( 'option', option, date );
1063 - $( datepicker ).trigger( 'change' );
1064 - };
1065 -
1066 - $( '.lp_sale_dates_fields' ).each( function() {
1067 - $( this ).find( 'input' ).datetimepicker( {
1068 - timeFormat: 'HH:mm',
1069 - separator: ' ',
1070 - dateFormat: 'yy-mm-dd',
1071 - showButtonPanel: true,
1072 - onSelect() {
1073 - datePickerSelect( $( this ) );
1074 - },
1075 - } );
1076 -
1077 - $( this ).find( 'input' ).each( function() {
1078 - datePickerSelect( $( this ) );
1079 - } );
1193 + /*const datePickerSelect = function( datepicker ) {
1194 + const option = $( datepicker ).is( '#_lp_sale_start' ) ? 'minDate' : 'maxDate',
1195 + otherDateField = 'minDate' === option ? $( '#_lp_sale_end' ) : $( '#_lp_sale_start' ),
1196 + date = $( datepicker ).datetimepicker( 'getDate' );
1197 + $( otherDateField ).datetimepicker( 'option', option, date );
1198 + $( datepicker ).trigger( 'change' );
1199 + };
1200 + $( '.lp_sale_dates_fields' ).each( function() {
1201 + $( this ).find( 'input' ).datetimepicker( {
1202 + timeFormat: 'HH:mm',
1203 + separator: ' ',
1204 + dateFormat: 'yy-mm-dd',
1205 + showButtonPanel: true,
1206 + onSelect() {
1207 + datePickerSelect( $( this ) );
1208 + },
1209 + } );
1210 + $( this ).find( 'input' ).each( function() {
1211 + datePickerSelect( $( this ) );
1212 + } );
1080 1213 } );*/
1081 1214 };
1082 1215 const lpHidePassingGrade = () => {
1083 1216 const listHides = ['evaluate_final_quiz', 'evaluate_final_assignment'];