PluginProbe
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses / 4.4.9.1
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses v4.4.9.1
4.4.9.1 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 All 141 releases
learnpress / assets / js / dist / frontend / courses.js

courses.js in LearnPress – WordPress LMS Plugin for Create and Sell Online Courses 4.4.9.1, at assets/js/dist/frontend/courses.js

1,250 lines 40.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /******/ (() => { // webpackBootstrap
2 /******/ "use strict";
3 /******/ var __webpack_modules__ = ({
4
5 /***/ "./assets/src/js/api.js"
6 /*!******************************!*\
7 !*** ./assets/src/js/api.js ***!
8 \******************************/
9 (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
10
11 __webpack_require__.r(__webpack_exports__);
12 /* harmony export */ __webpack_require__.d(__webpack_exports__, {
13 /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
14 /* harmony export */ });
15 /**
16 * List API on backend
17 *
18 * @since 4.2.6
19 * @version 1.0.2
20 */
21
22 const lplistAPI = {};
23 let lp_rest_url;
24 if ('undefined' !== typeof lpDataAdmin) {
25 lp_rest_url = lpDataAdmin.lp_rest_url;
26 lplistAPI.admin = {
27 // apiAdminNotice: lp_rest_url + 'lp/v1/admin/tools/admin-notices',
28 apiSearchCourses: lp_rest_url + 'lp/v1/admin/tools/search-course',
29 apiSearchUsers: lp_rest_url + 'lp/v1/admin/tools/search-user',
30 apiAssignUserCourse: lp_rest_url + 'lp/v1/admin/tools/assign-user-course',
31 apiUnAssignUserCourse: lp_rest_url + 'lp/v1/admin/tools/unassign-user-course'
32 };
33 }
34 if ('undefined' !== typeof lpData) {
35 lp_rest_url = lpData.lp_rest_url;
36 lplistAPI.frontend = {
37 apiWidgets: lp_rest_url + 'lp/v1/widgets/api',
38 apiCourses: lp_rest_url + 'lp/v1/courses/archive-course',
39 // Deprecated API, don't load from v4.3.7
40 apiAJAX: lp_rest_url + 'lp/v1/load_content_via_ajax/',
41 // Deprecated since 4.3.0
42 apiProfileCoverImage: lp_rest_url + 'lp/v1/profile/cover-image'
43 };
44 }
45 if (lp_rest_url) {
46 lplistAPI.apiCourses = lp_rest_url + 'lp/v1/courses/';
47 lplistAPI.apiEditCoursesArchiveBlock = lp_rest_url + 'lp/v1/courses/edit-archive-block';
48 lplistAPI.apiCoursesSuggest = lp_rest_url + 'lp/v1/courses/courses-suggest';
49 }
50 /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (lplistAPI);
51
52 /***/ },
53
54 /***/ "./assets/src/js/utils.js"
55 /*!********************************!*\
56 !*** ./assets/src/js/utils.js ***!
57 \********************************/
58 (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
59
60 __webpack_require__.r(__webpack_exports__);
61 /* harmony export */ __webpack_require__.d(__webpack_exports__, {
62 /* harmony export */ debounce: () => (/* binding */ debounce),
63 /* harmony export */ eventHandlers: () => (/* binding */ eventHandlers),
64 /* harmony export */ fullScreenView: () => (/* binding */ fullScreenView),
65 /* harmony export */ getDataOfForm: () => (/* binding */ getDataOfForm),
66 /* harmony export */ getFieldKeysOfForm: () => (/* binding */ getFieldKeysOfForm),
67 /* harmony export */ listenElementCreated: () => (/* binding */ listenElementCreated),
68 /* harmony export */ listenElementViewed: () => (/* binding */ listenElementViewed),
69 /* harmony export */ lpAddQueryArgs: () => (/* binding */ lpAddQueryArgs),
70 /* harmony export */ lpAjaxParseJsonOld: () => (/* binding */ lpAjaxParseJsonOld),
71 /* harmony export */ lpClassName: () => (/* binding */ lpClassName),
72 /* harmony export */ lpFetchAPI: () => (/* binding */ lpFetchAPI),
73 /* harmony export */ lpGetCurrentURLNoParam: () => (/* binding */ lpGetCurrentURLNoParam),
74 /* harmony export */ lpOnElementReady: () => (/* binding */ lpOnElementReady),
75 /* harmony export */ lpSetLoadingEl: () => (/* binding */ lpSetLoadingEl),
76 /* harmony export */ lpShowHideEl: () => (/* binding */ lpShowHideEl),
77 /* harmony export */ mergeDataWithDatForm: () => (/* binding */ mergeDataWithDatForm),
78 /* harmony export */ toggleCollapse: () => (/* binding */ toggleCollapse),
79 /* harmony export */ toggleEnable: () => (/* binding */ toggleEnable)
80 /* harmony export */ });
81 /**
82 * Utils functions
83 *
84 * @param url
85 * @param data
86 * @param functions
87 * @since 4.2.5.1
88 * @version 1.0.7
89 */
90 const lpClassName = {
91 hidden: 'lp-hidden',
92 loading: 'loading',
93 elCollapse: 'lp-collapse',
94 elSectionToggle: '.lp-section-toggle',
95 elTriggerToggle: '.lp-trigger-toggle',
96 elBtnFullScreen: '.lp-btn-full-screen-view',
97 elFullScreen: 'lp-full-screen-view',
98 elBtnFullScreenClose: 'lp-full-screen-view__close'
99 };
100 const lpFetchAPI = (url, data = {}, functions = {}) => {
101 if ('function' === typeof functions.before) {
102 functions.before();
103 }
104 fetch(url, {
105 method: 'GET',
106 ...data
107 }).then(response => response.json()).then(response => {
108 if ('function' === typeof functions.success) {
109 functions.success(response);
110 }
111 }).catch(err => {
112 if ('function' === typeof functions.error) {
113 functions.error(err);
114 }
115 }).finally(() => {
116 if ('function' === typeof functions.completed) {
117 functions.completed();
118 }
119 });
120 };
121
122 /**
123 * Get current URL without params.
124 *
125 * @since 4.2.5.1
126 */
127 const lpGetCurrentURLNoParam = () => {
128 let currentUrl = window.location.href;
129 const hasParams = currentUrl.includes('?');
130 if (hasParams) {
131 currentUrl = currentUrl.split('?')[0];
132 }
133 return currentUrl;
134 };
135 const lpAddQueryArgs = (endpoint, args) => {
136 const url = new URL(endpoint);
137 Object.keys(args).forEach(arg => {
138 url.searchParams.set(arg, args[arg]);
139 });
140 return url;
141 };
142
143 /**
144 * Listen element viewed.
145 *
146 * @param el
147 * @param callback
148 * @since 4.2.5.8
149 */
150 const listenElementViewed = (el, callback) => {
151 const observerSeeItem = new IntersectionObserver(function (entries) {
152 for (const entry of entries) {
153 if (entry.isIntersecting) {
154 callback(entry);
155 }
156 }
157 });
158 observerSeeItem.observe(el);
159 };
160
161 /**
162 * Listen element created.
163 *
164 * @param callback
165 * @since 4.2.5.8
166 */
167 const listenElementCreated = callback => {
168 const observerCreateItem = new MutationObserver(function (mutations) {
169 mutations.forEach(function (mutation) {
170 if (mutation.addedNodes) {
171 mutation.addedNodes.forEach(function (node) {
172 if (node.nodeType === 1) {
173 callback(node);
174 }
175 });
176 }
177 });
178 });
179 observerCreateItem.observe(document, {
180 childList: true,
181 subtree: true
182 });
183 // End.
184 };
185
186 /**
187 * Listen element created.
188 *
189 * @param selector
190 * @param callback
191 * @since 4.2.7.1
192 */
193 const lpOnElementReady = (selector, callback) => {
194 const element = document.querySelector(selector);
195 if (element) {
196 callback(element);
197 return;
198 }
199 const observer = new MutationObserver((mutations, obs) => {
200 const element = document.querySelector(selector);
201 if (element) {
202 obs.disconnect();
203 callback(element);
204 }
205 });
206 observer.observe(document.documentElement, {
207 childList: true,
208 subtree: true
209 });
210 };
211
212 // Parse JSON from string with content include LP_AJAX_START.
213 const lpAjaxParseJsonOld = data => {
214 if (typeof data !== 'string') {
215 return data;
216 }
217 const m = String.raw({
218 raw: data
219 }).match(/<-- LP_AJAX_START -->(.*)<-- LP_AJAX_END -->/s);
220 try {
221 if (m) {
222 data = JSON.parse(m[1].replace(/(?:\r\n|\r|\n)/g, ''));
223 } else {
224 data = JSON.parse(data);
225 }
226 } catch (e) {
227 data = {};
228 }
229 return data;
230 };
231
232 // status 0: hide, 1: show
233 const lpShowHideEl = (el, status = 0) => {
234 if (!el) {
235 return;
236 }
237 if (!status) {
238 el.classList.add(lpClassName.hidden);
239 } else {
240 el.classList.remove(lpClassName.hidden);
241 }
242 };
243
244 // status 0: hide, 1: show
245 const lpSetLoadingEl = (el, status) => {
246 if (!el) {
247 return;
248 }
249 if (!status) {
250 el.classList.remove(lpClassName.loading);
251 } else {
252 el.classList.add(lpClassName.loading);
253 }
254 };
255
256 // Toggle collapse section
257 const toggleCollapse = (e, target, elTriggerClassName = '', elsExclude = [], callback) => {
258 if (!elTriggerClassName) {
259 elTriggerClassName = lpClassName.elTriggerToggle;
260 }
261
262 // Exclude elements, which should not trigger the collapse toggle
263 if (elsExclude && elsExclude.length > 0) {
264 for (const elExclude of elsExclude) {
265 if (target.closest(elExclude)) {
266 return;
267 }
268 }
269 }
270 const elTrigger = target.closest(elTriggerClassName);
271 if (!elTrigger) {
272 return;
273 }
274
275 //console.log( 'elTrigger', elTrigger );
276
277 const elSectionToggle = elTrigger.closest(`${lpClassName.elSectionToggle}`);
278 if (!elSectionToggle) {
279 return;
280 }
281 elSectionToggle.classList.toggle(`${lpClassName.elCollapse}`);
282 if ('function' === typeof callback) {
283 callback(elSectionToggle);
284 }
285 };
286
287 // Get data of form
288 const getDataOfForm = form => {
289 const dataSend = {};
290 const formData = new FormData(form);
291 for (const pair of formData.entries()) {
292 const key = pair[0];
293 const value = formData.getAll(key);
294 if (!dataSend.hasOwnProperty(key)) {
295 // Convert value array to string.
296 dataSend[key] = value.join(',');
297 }
298 }
299 return dataSend;
300 };
301
302 // Get field keys of form
303 const getFieldKeysOfForm = form => {
304 const keys = [];
305 const elements = form.elements;
306 for (let i = 0; i < elements.length; i++) {
307 const name = elements[i].name;
308 if (name && !keys.includes(name)) {
309 keys.push(name);
310 }
311 }
312 return keys;
313 };
314
315 // Merge data handle with data form.
316 const mergeDataWithDatForm = (elForm, dataHandle) => {
317 const dataForm = getDataOfForm(elForm);
318 const keys = getFieldKeysOfForm(elForm);
319 keys.forEach(key => {
320 if (!dataForm.hasOwnProperty(key)) {
321 delete dataHandle[key];
322 } else if (dataForm[key][0] === '') {
323 delete dataForm[key];
324 delete dataHandle[key];
325 }
326 });
327 dataHandle = {
328 ...dataHandle,
329 ...dataForm
330 };
331 return dataHandle;
332 };
333
334 /**
335 * Event trigger
336 * For each list of event handlers, listen event on document.
337 *
338 * eventName: 'click', 'change', ...
339 * eventHandlers = [ { selector: '.lp-button', callBack: function(){}, class: object } ]
340 *
341 * @param eventName
342 * @param eventHandlers
343 */
344 const eventHandlers = (eventName, eventHandlers) => {
345 document.addEventListener(eventName, e => {
346 const target = e.target;
347 let args = {
348 e,
349 target
350 };
351 eventHandlers.forEach(eventHandler => {
352 args = {
353 ...args,
354 ...eventHandler
355 };
356
357 //console.log( args );
358
359 // Check condition before call back
360 if (eventHandler.conditionBeforeCallBack) {
361 if (eventHandler.conditionBeforeCallBack(args) !== true) {
362 return;
363 }
364 }
365
366 // Special check for keydown event with checkIsEventEnter = true
367 if (eventName === 'keydown' && eventHandler.checkIsEventEnter) {
368 if (e.key !== 'Enter') {
369 return;
370 }
371 }
372 if (target.closest(eventHandler.selector)) {
373 if (eventHandler.class) {
374 // Call method of class, function callBack will understand exactly {this} is class object.
375 eventHandler.class[eventHandler.callBack](args);
376 } else {
377 // For send args is objected, {this} is eventHandler object, not class object.
378 eventHandler.callBack(args);
379 }
380 }
381 });
382 });
383 };
384
385 /**
386 * Debounce - delays function execution until after `wait` ms of inactivity.
387 *
388 * Each call resets the timer. Only the last call in a burst executes.
389 *
390 * USE CASES:
391 * - Search inputs, form validation, window resize
392 * - Multiple elements need independent timers
393 * - When you need to call with different arguments
394 *
395 * EXAMPLES:
396 * const debouncedSearch = debounce( (query) => fetchResults(query), 300 );
397 * searchInput.addEventListener('input', (e) => debouncedSearch(e.target.value));
398 *
399 * const debouncedResize = debounce( recalculateLayout, 250 );
400 * window.addEventListener('resize', debouncedResize);
401 *
402 * ⚠️ Create ONCE outside event handlers, not inside.
403 *
404 * @param {Function} func - Function to debounce (can be anonymous)
405 * @param {number} wait - Milliseconds to wait (default: 500)
406 * @return {Function} Debounced wrapper function
407 * @since 4.3.7
408 * @version 1.0.0
409 */
410 const debounce = (func, wait = 500) => {
411 let timer;
412 return args => {
413 clearTimeout(timer);
414 timer = setTimeout(() => func(args), wait);
415 };
416 };
417
418 /**
419 * Initialize lp-toggle-enable components.
420 *
421 * Finds all `.lp-toggle-enable` elements and wires up toggle behavior.
422 * Reads initial state from `data-enabled` attribute ("true"/"false").
423 * Calls `data-on-toggle` callback (if provided via options) on state change.
424 *
425 * HTML structure:
426 * <label class="lp-toggle-enable" data-enabled="true">
427 * <input type="checkbox" class="lp-toggle-enable__input" />
428 * <span class="lp-toggle-enable__track"></span>
429 * </label>
430 *
431 * @param {string} selector CSS selector for toggle elements (default: '.lp-toggle-enable')
432 * @param {Function} onToggle Optional callback( el, isEnabled ) called on state change
433 * @since 4.4.5
434 * @version 1.0.0
435 */
436 window.lpToggleEnableInit = 0;
437 const toggleEnable = (onToggle = null) => {
438 if (window.lpToggleEnableInit) {
439 return;
440 }
441 window.lpToggleEnableInit = 1;
442 const selector = '.lp-toggle-enable';
443 const updateUI = (toggle, isEnabled) => {
444 toggle.classList.toggle('is-enabled', isEnabled);
445 const input = toggle.querySelector('.lp-toggle-enable__input');
446 if (input) {
447 input.checked = isEnabled;
448 input.value = isEnabled ? '1' : '0';
449 }
450 };
451
452 // Delegate click handling via eventHandlers.
453 eventHandlers('click', [{
454 selector,
455 callBack: args => {
456 const {
457 e,
458 target
459 } = args;
460 const toggle = target.closest(selector);
461 if (!toggle || toggle.classList.contains('is-disabled')) {
462 return;
463 }
464 e.preventDefault();
465 const isEnabled = !toggle.classList.contains('is-enabled');
466 updateUI(toggle, isEnabled);
467 if ('function' === typeof onToggle) {
468 onToggle(toggle, isEnabled);
469 }
470 }
471 }]);
472 };
473
474 /**
475 * Initialize custom fullscreen view buttons.
476 *
477 * Delegates clicks on `.lp-btn-full-screen-view` buttons to
478 * `lpToggleFullscreenView`. Reads the `data-target` attribute to find the
479 * target element. Falls back to the button's parent element when
480 * `data-target` is not provided.
481 *
482 * @since 4.4.5
483 * @version 1.0.0
484 */
485 window.lpFullScreenViewInit = 0;
486 const fullScreenView = () => {
487 if (window.lpFullScreenViewInit) {
488 return;
489 }
490 window.lpFullScreenViewInit = 1;
491 let lastScrollY = 0;
492 const lpToggleFullscreenView = (elTarget, elBtnFullScreen = null) => {
493 const isFullscreen = elTarget.classList.contains(lpClassName.elFullScreen);
494 if (isFullscreen) {
495 elTarget.classList.remove(lpClassName.elFullScreen);
496 document.documentElement.classList.remove('lp-full-screen-active');
497 window.scrollTo(0, lastScrollY);
498 } else {
499 lastScrollY = window.scrollY;
500 elTarget.classList.add(lpClassName.elFullScreen);
501 document.documentElement.classList.add('lp-full-screen-active');
502 }
503 if (!isFullscreen) {
504 if (!elTarget.querySelector(`.${lpClassName.elBtnFullScreenClose}`)) {
505 const closeButton = document.createElement('button');
506 closeButton.type = 'button';
507 closeButton.className = lpClassName.elBtnFullScreenClose;
508 closeButton.setAttribute('aria-label', 'Close');
509 closeButton.innerHTML = lpData.i18n.closeButtonFullScreen || 'Close &times;';
510 closeButton.addEventListener('click', e => {
511 e.preventDefault();
512 lpToggleFullscreenView(elTarget);
513 });
514 elTarget.appendChild(closeButton);
515 }
516 } else {
517 const closeButton = elTarget.querySelector(`.${lpClassName.elBtnFullScreenClose}`);
518 if (closeButton) {
519 closeButton.remove();
520 }
521 }
522 };
523 eventHandlers('click', [{
524 selector: lpClassName.elBtnFullScreen,
525 callBack: args => {
526 const {
527 e,
528 target
529 } = args;
530 const elBtnFullScreen = target.closest(lpClassName.elBtnFullScreen);
531 if (!elBtnFullScreen) {
532 console.log('No full screen button found');
533 return;
534 }
535 e.preventDefault();
536 let elTarget = null;
537 const targetSelector = elBtnFullScreen.dataset.targetFullscreen;
538 console.log(targetSelector);
539 if (targetSelector) {
540 elTarget = document.querySelector(targetSelector);
541 }
542 if (!elTarget) {
543 console.log('No target element found');
544 return;
545 }
546 lpToggleFullscreenView(elTarget, elBtnFullScreen);
547 }
548 }]);
549 };
550
551 /***/ },
552
553 /***/ "./assets/src/js/utils/cookies.js"
554 /*!****************************************!*\
555 !*** ./assets/src/js/utils/cookies.js ***!
556 \****************************************/
557 (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
558
559 __webpack_require__.r(__webpack_exports__);
560 /* harmony export */ __webpack_require__.d(__webpack_exports__, {
561 /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
562 /* harmony export */ });
563 const Cookies = {
564 get: (name, def, global) => {
565 let ret;
566 if (global) {
567 ret = wpCookies.get(name);
568 } else {
569 let ck = wpCookies.get('LP');
570 if (ck) {
571 ck = JSON.parse(ck);
572 ret = name ? ck[name] : ck;
573 }
574 }
575 if (!ret && ret !== def) {
576 ret = def;
577 }
578 return ret;
579 },
580 set(name, value, expires, path, domain, secure) {
581 if (arguments.length > 2) {
582 wpCookies.set(name, value, expires, path, domain, secure);
583 } else if (arguments.length == 2) {
584 let ck = wpCookies.get('LP');
585 if (ck) {
586 ck = JSON.parse(ck);
587 } else {
588 ck = {};
589 }
590 ck[name] = value;
591 wpCookies.set('LP', JSON.stringify(ck), '', '/');
592 } else {
593 wpCookies.set('LP', JSON.stringify(name), '', '/');
594 }
595 },
596 remove(name) {
597 const allCookies = Cookies.get();
598 const reg = new RegExp(name, 'g');
599 const newCookies = {};
600 const useRegExp = name.match(/\*/);
601 for (const i in allCookies) {
602 if (useRegExp) {
603 if (!i.match(reg)) {
604 newCookies[i] = allCookies[i];
605 }
606 } else if (name != i) {
607 newCookies[i] = allCookies[i];
608 }
609 }
610 Cookies.set(newCookies);
611 }
612 };
613 /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (Cookies);
614
615 /***/ }
616
617 /******/ });
618 /************************************************************************/
619 /******/ // The module cache
620 /******/ const __webpack_module_cache__ = {};
621 /******/
622 /******/ // The require function
623 /******/ function __webpack_require__(moduleId) {
624 /******/ // Check if module is in cache
625 /******/ const cachedModule = __webpack_module_cache__[moduleId];
626 /******/ if (cachedModule !== undefined) {
627 /******/ return cachedModule.exports;
628 /******/ }
629 /******/ // Create a new module (and put it into the cache)
630 /******/ const module = __webpack_module_cache__[moduleId] = {
631 /******/ // no module.id needed
632 /******/ // no module.loaded needed
633 /******/ exports: {}
634 /******/ };
635 /******/
636 /******/ // Execute the module function
637 /******/ if (!(moduleId in __webpack_modules__)) {
638 /******/ delete __webpack_module_cache__[moduleId];
639 /******/ const e = new Error("Cannot find module '" + moduleId + "'");
640 /******/ e.code = 'MODULE_NOT_FOUND';
641 /******/ throw e;
642 /******/ }
643 /******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
644 /******/
645 /******/ // Return the exports of the module
646 /******/ return module.exports;
647 /******/ }
648 /******/
649 /************************************************************************/
650 /******/ /* webpack/runtime/define property getters */
651 /******/ (() => {
652 /******/ // define getter/value functions for harmony exports
653 /******/ __webpack_require__.d = (exports, definition) => {
654 /******/ if(Array.isArray(definition)) {
655 /******/ var i = 0;
656 /******/ while(i < definition.length) {
657 /******/ var key = definition[i++];
658 /******/ var binding = definition[i++];
659 /******/ if(!__webpack_require__.o(exports, key)) {
660 /******/ if(binding === 0) {
661 /******/ Object.defineProperty(exports, key, { enumerable: true, value: definition[i++] });
662 /******/ } else {
663 /******/ Object.defineProperty(exports, key, { enumerable: true, get: binding });
664 /******/ }
665 /******/ } else if(binding === 0) { i++; }
666 /******/ }
667 /******/ } else {
668 /******/ for(var key in definition) {
669 /******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
670 /******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
671 /******/ }
672 /******/ }
673 /******/ }
674 /******/ };
675 /******/ })();
676 /******/
677 /******/ /* webpack/runtime/hasOwnProperty shorthand */
678 /******/ (() => {
679 /******/ __webpack_require__.o = (obj, prop) => (Object.hasOwn(obj, prop))
680 /******/ })();
681 /******/
682 /******/ /* webpack/runtime/make namespace object */
683 /******/ (() => {
684 /******/ // define __esModule on exports
685 /******/ __webpack_require__.r = (exports) => {
686 /******/ if(Symbol.toStringTag) {
687 /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
688 /******/ }
689 /******/ Object.defineProperty(exports, '__esModule', { value: true });
690 /******/ };
691 /******/ })();
692 /******/
693 /************************************************************************/
694 let __webpack_exports__ = {};
695 // This entry needs to be wrapped in an IIFE because it needs to be isolated against other modules in the chunk.
696 (() => {
697 /*!*******************************************!*\
698 !*** ./assets/src/js/frontend/courses.js ***!
699 \*******************************************/
700 __webpack_require__.r(__webpack_exports__);
701 /* harmony import */ var _api__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../api */ "./assets/src/js/api.js");
702 /* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../utils */ "./assets/src/js/utils.js");
703 /* harmony import */ var _utils_cookies__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../utils/cookies */ "./assets/src/js/utils/cookies.js");
704 /**
705 * @deprecated 4.2.5.8
706 * But still support for theme has html old, override.
707 * Todo: if want remove file, need to check theme override file archive, can via remove hook override file.
708 */
709
710
711
712
713 const elListCoursesIdNewDefault = '.lp-list-courses-default';
714 if ('undefined' === typeof lpData || 'undefined' === typeof lpSettingCourses) {
715 console.log('lpData || lpSettingCourses is undefined');
716 }
717
718 // Call API load courses.
719 // When LP v4.2.3.3 release a long time, we will remove this function on theme Eduma.
720 // assets/js/thim-course-filter-v2.js
721 window.lpArchiveRequestCourse = args => {
722 window.lpCourseList.updateEventTypeBeforeFetch('filter');
723 window.lpCourseList.triggerFetchAPI(args);
724 };
725
726 // Events
727 document.addEventListener('change', function (e) {
728 const target = e.target;
729 if (window.lpCourseList.checkIsNewListCourses()) {
730 return;
731 }
732 window.lpCourseList.onChangeSortBy(e, target);
733 window.lpCourseList.onChangeTypeLayout(e, target);
734 });
735 document.addEventListener('click', function (e) {
736 const target = e.target;
737 if (window.lpCourseList.checkIsNewListCourses()) {
738 return;
739 }
740 window.lpCourseList.clickLoadMore(e, target);
741 window.lpCourseList.clickNumberPage(e, target);
742 });
743 document.addEventListener('scroll', function (e) {
744 const target = e.target;
745 if (window.lpCourseList.checkIsNewListCourses()) {
746 return;
747 }
748 window.lpCourseList.scrollInfinite(e, target);
749 });
750 document.addEventListener('keyup', function (e) {
751 const target = e.target;
752 if (window.lpCourseList.checkIsNewListCourses()) {
753 return;
754 }
755 window.lpCourseList.searchCourse(e, target);
756 });
757 document.addEventListener('submit', function (e) {
758 const target = e.target;
759 if (window.lpCourseList.checkIsNewListCourses()) {
760 return;
761 }
762 window.lpCourseList.searchCourse(e, target);
763 });
764 window.lpCourseList = (() => {
765 const classArchiveCourse = 'lp-archive-courses';
766 const classListCourse = 'learn-press-courses';
767 const classPaginationCourse = 'learn-press-pagination';
768 const classSkeletonArchiveCourse = 'lp-archive-course-skeleton';
769 const classCoursesPageResult = 'courses-page-result';
770 const lpArchiveLoadAjax = parseInt(lpSettingCourses.lpArchiveLoadAjax || 0);
771 const lpArchiveNoLoadAjaxFirst = parseInt(lpSettingCourses.lpArchiveNoLoadAjaxFirst) === 1;
772 const lpArchiveSkeletonParam = lpData.urlParams || [];
773 const currentUrl = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.lpGetCurrentURLNoParam)();
774 let filterCourses = {};
775 const typePagination = lpSettingCourses.lpArchivePaginationType || 'number';
776 let typeEventBeforeFetch;
777 let timeOutSearch;
778 let isLoadingInfinite = false;
779 const fetchAPI = (args, callBack = {}) => {
780 //console.log( 'Fetch API Courses' );
781 const url = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.lpAddQueryArgs)(_api__WEBPACK_IMPORTED_MODULE_0__["default"].frontend.apiCourses, args);
782 let paramsFetch = {};
783 if (0 !== parseInt(lpData.user_id)) {
784 paramsFetch = {
785 headers: {
786 'X-WP-Nonce': lpData.nonce
787 }
788 };
789 }
790 ;(0,_utils__WEBPACK_IMPORTED_MODULE_1__.lpFetchAPI)(url, paramsFetch, callBack);
791 };
792 return {
793 init: () => {
794 const urlParams = {};
795 const urlQueryString = window.location.search;
796 const urlSearchParams = new URLSearchParams(urlQueryString);
797 for (const [key, val] of urlSearchParams.entries()) {
798 urlParams[key] = val;
799 }
800 filterCourses = {
801 ...lpArchiveSkeletonParam,
802 ...urlParams
803 };
804 filterCourses.paged = parseInt(filterCourses.paged || 1);
805 if (isNaN(filterCourses.paged)) {
806 filterCourses.paged = 1;
807 }
808 if (lpArchiveNoLoadAjaxFirst && typePagination !== 'number') {
809 filterCourses.paged = 1;
810 }
811 window.localStorage.setItem('lp_filter_courses', JSON.stringify(filterCourses));
812 },
813 updateEventTypeBeforeFetch: type => {
814 typeEventBeforeFetch = type;
815 },
816 onChangeSortBy: (e, target) => {
817 if (!target.classList.contains('courses-order-by')) {
818 return;
819 }
820 e.preventDefault();
821 const filterCourses = JSON.parse(window.localStorage.getItem('lp_filter_courses')) || {};
822 filterCourses.order_by = target.value || '';
823 if ('undefined' !== typeof lpSettingCourses && lpData.is_course_archive && lpSettingCourses.lpArchiveLoadAjax) {
824 window.lpCourseList.triggerFetchAPI(filterCourses);
825 } else {
826 window.location.href = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.lpAddQueryArgs)(currentUrl, filterCourses);
827 }
828 },
829 onChangeTypeLayout: (e, target) => {
830 if ('lp-switch-layout-btn' !== target.getAttribute('name')) {
831 return;
832 }
833 const elArchive = target.closest(`.${classArchiveCourse}`);
834 if (!elArchive) {
835 return;
836 }
837 const elListCourse = elArchive.querySelector(`.${classListCourse}`);
838 if (!elListCourse) {
839 return;
840 }
841 e.preventDefault();
842 const layout = target.value;
843 if (layout) {
844 elListCourse.dataset.layout = layout;
845 _utils_cookies__WEBPACK_IMPORTED_MODULE_2__["default"].set('courses-layout', layout);
846 }
847 },
848 clickNumberPage: (e, target) => {
849 if (!lpArchiveLoadAjax || parseInt(lpSettingCourses.noLoadCoursesJs)) {
850 return;
851 }
852 if (target.classList.contains('page-numbers')) {
853 const parentArchive = target.closest(`.${classArchiveCourse}`);
854 if (!parentArchive) {
855 return;
856 }
857 e.preventDefault();
858 const pageCurrent = filterCourses.paged;
859 if (target.classList.contains('prev')) {
860 filterCourses.paged = pageCurrent - 1;
861 } else if (target.classList.contains('next')) {
862 filterCourses.paged = pageCurrent + 1;
863 } else {
864 filterCourses.paged = parseInt(target.textContent);
865 }
866 typeEventBeforeFetch = 'number';
867 window.lpCourseList.triggerFetchAPI(filterCourses);
868 return;
869 }
870 const parent = target.closest('.page-numbers');
871 if (parent) {
872 e.preventDefault();
873 parent.click();
874 }
875 },
876 clickLoadMore: (e, target) => {
877 if (!target.classList.contains('courses-btn-load-more')) {
878 return;
879 }
880 const elArchive = target.closest(`.${classArchiveCourse}`);
881 if (!elArchive) {
882 return;
883 }
884 const elListCourse = elArchive.querySelector(`.${classListCourse}`);
885 if (!elListCourse) {
886 return;
887 }
888 e.preventDefault();
889 ++filterCourses.paged;
890 typeEventBeforeFetch = 'load-more';
891 window.lpCourseList.triggerFetchAPI(filterCourses);
892 },
893 scrollInfinite: (e, target) => {
894 const elArchive = document.querySelector(`.${classArchiveCourse}`);
895 if (!elArchive) {
896 return;
897 }
898 const elInfinite = elArchive.querySelector('.courses-load-infinite');
899 if (!elInfinite) {
900 return;
901 }
902
903 // Create an IntersectionObserver object.
904 const observer = new IntersectionObserver(function (entries) {
905 for (const entry of entries) {
906 // If the entry is intersecting, load the image.
907 if (entry.isIntersecting) {
908 if (isLoadingInfinite) {
909 return;
910 }
911 ++filterCourses.paged;
912 typeEventBeforeFetch = 'infinite';
913 window.lpCourseList.triggerFetchAPI(filterCourses);
914
915 //observer.unobserve( entry.target );
916 }
917 }
918 });
919 observer.observe(elInfinite);
920 },
921 triggerFetchAPI: args => {
922 // For case, click on pagination, filter.
923 const elArchive = document.querySelector(`.${classArchiveCourse}`);
924 if (!elArchive) {
925 return;
926 }
927 const elListCourse = elArchive.querySelector(`.${classListCourse}`);
928 if (!elListCourse) {
929 return;
930 }
931 filterCourses = args;
932 let callBack;
933 switch (typeEventBeforeFetch) {
934 case 'load-more':
935 callBack = window.lpCourseList.callBackPaginationTypeLoadMore(elArchive, elListCourse);
936 break;
937 case 'infinite':
938 callBack = window.lpCourseList.callBackPaginationTypeInfinite(elArchive, elListCourse);
939 break;
940 case 'custom':
941 callBack = args.customCallBack || false;
942 break;
943 case 'filter':
944 default:
945 // number
946 // Change url by params filter courses
947 //callBack = window.lpCourseList.callBackPaginationTypeNumber( elListCourse );
948 callBack = window.lpCourseList.callBackFilter(args, elArchive, elListCourse);
949 break;
950 }
951 if (!callBack) {
952 return;
953 }
954
955 //console.log( 'Args', args );
956
957 fetchAPI(args, callBack);
958 },
959 callBackFilter: (args, elArchive, elListCourse) => {
960 if (!elListCourse) {
961 return;
962 }
963 const skeleton = elListCourse.querySelector(`.${classSkeletonArchiveCourse}`);
964 return {
965 before: () => {
966 window.history.pushState('', '', (0,_utils__WEBPACK_IMPORTED_MODULE_1__.lpAddQueryArgs)(currentUrl, args));
967 window.localStorage.setItem('lp_filter_courses', JSON.stringify(args));
968 if (skeleton) {
969 skeleton.style.display = 'block';
970 }
971 },
972 success: res => {
973 // Remove all items before insert new items.
974 const elLis = elListCourse.querySelectorAll(`:not(.${classSkeletonArchiveCourse})`);
975 elLis.forEach(elLi => {
976 const parent = elLi.closest(`.${classSkeletonArchiveCourse}`);
977 if (parent) {
978 return;
979 }
980 elLi.remove();
981 });
982
983 // Insert new items.
984 elListCourse.insertAdjacentHTML('afterbegin', res.data.content || '');
985
986 // Check if Pagination exists will remove.
987 const elPagination = document.querySelector(`.${classPaginationCourse}`);
988 if (elPagination) {
989 elPagination.remove();
990 }
991
992 // Insert Pagination.
993 const pagination = res.data.pagination || '';
994 elListCourse.insertAdjacentHTML('afterend', pagination);
995
996 // Set showing results page.
997 const elCoursesPageResult = document.querySelector(`.${classCoursesPageResult}`);
998 if (elCoursesPageResult) {
999 elCoursesPageResult.innerHTML = res.data.from_to || '';
1000 }
1001 },
1002 error: error => {
1003 elListCourse.innerHTML += `<div class="lp-ajax-message error" style="display:block">${error.message || 'Error'}</div>`;
1004 },
1005 completed: () => {
1006 if (skeleton) {
1007 skeleton.style.display = 'none';
1008 }
1009 // Scroll to archive element
1010 const optionScroll = {
1011 behavior: 'smooth'
1012 };
1013 elListCourse.closest(`.${classArchiveCourse}`).scrollIntoView(optionScroll);
1014 }
1015 };
1016 },
1017 /*callBackPaginationTypeNumber: ( elListCourse ) => {
1018 if ( ! elListCourse ) {
1019 return;
1020 }
1021 const skeleton = elListCourse.querySelector( `.${ classSkeletonArchiveCourse }` );
1022 return {
1023 before: () => {
1024 const urlPush = lpAddQueryArgs( currentUrl, args );
1025 window.history.pushState( '', '', urlPush );
1026 // Save filter courses to Storage
1027 window.localStorage.setItem( 'lp_filter_courses', JSON.stringify( args ) );
1028 if ( skeleton ) {
1029 skeleton.style.display = 'block';
1030 }
1031 },
1032 success: ( res ) => {
1033 // Remove all items before insert new items.
1034 const elLis = elListCourse.querySelectorAll( `:not(.${ classSkeletonArchiveCourse })` );
1035 elLis.forEach( ( elLi ) => {
1036 const parent = elLi.closest( `.${ classSkeletonArchiveCourse }` );
1037 if ( parent ) {
1038 return;
1039 }
1040 elLi.remove();
1041 } );
1042 // Insert new items.
1043 elListCourse.insertAdjacentHTML( 'afterbegin', res.data.content || '' );
1044 // Check if Pagination exists will remove.
1045 const elPagination = document.querySelector( `.${ classPaginationCourse }` );
1046 if ( elPagination ) {
1047 elPagination.remove();
1048 }
1049 // Insert Pagination.
1050 const pagination = res.data.pagination || '';
1051 elListCourse.insertAdjacentHTML( 'afterend', pagination );
1052 },
1053 error: ( error ) => {
1054 elListCourse.innerHTML += `<div class="lp-ajax-message error" style="display:block">${ error.message || 'Error' }</div>`;
1055 },
1056 completed: () => {
1057 if ( skeleton ) {
1058 skeleton.style.display = 'none';
1059 }
1060 // Scroll to archive element
1061 const optionScroll = { behavior: 'smooth' };
1062 elListCourse.closest( '.lp-archive-courses' ).scrollIntoView( optionScroll );
1063 },
1064 };
1065 },*/
1066 callBackPaginationTypeLoadMore: (elArchive, elListCourse) => {
1067 //console.log( 'callBackPaginationTypeLoadMore' );
1068 if (!elListCourse || !elArchive) {
1069 return false;
1070 }
1071 const btnLoadMore = elArchive.querySelector('.courses-btn-load-more');
1072 let elLoading;
1073 if (btnLoadMore) {
1074 elLoading = btnLoadMore.querySelector('.lp-loading-circle');
1075 }
1076 //const skeleton = document.querySelector( `.${ classSkeletonArchiveCourse }` );
1077
1078 return {
1079 before: () => {
1080 if (btnLoadMore) {
1081 elLoading.classList.remove('hide');
1082 btnLoadMore.setAttribute('disabled', 'disabled');
1083 }
1084
1085 /*if ( skeleton ) {
1086 skeleton.style.display = 'block';
1087 }*/
1088 },
1089 success: res => {
1090 elListCourse.insertAdjacentHTML('beforeend', res.data.content || '');
1091 elListCourse.insertAdjacentHTML('afterend', res.data.pagination || '');
1092
1093 // Set showing results page.
1094 const elCoursesPageResult = document.querySelector(`.${classCoursesPageResult}`);
1095 if (elCoursesPageResult) {
1096 elCoursesPageResult.innerHTML = res.data.from_to || '';
1097 }
1098 },
1099 error: error => {
1100 elListCourse.innerHTML += `<div class="lp-ajax-message error" style="display:block">${error.message || 'Error'}</div>`;
1101 },
1102 completed: () => {
1103 if (btnLoadMore) {
1104 elLoading.classList.add('hide');
1105 btnLoadMore.remove();
1106 }
1107
1108 /*if ( skeleton ) {
1109 skeleton.style.display = 'none';
1110 }*/
1111 }
1112 };
1113 },
1114 callBackPaginationTypeInfinite: (elArchive, elListCourse) => {
1115 //console.log( 'callBackPaginationTypeInfinite' );
1116 if (!elListCourse || !elListCourse) {
1117 return;
1118 }
1119 const elInfinite = elArchive.querySelector('.courses-load-infinite');
1120 if (!elInfinite) {
1121 return;
1122 }
1123 const loading = elInfinite.querySelector('.lp-loading-circle');
1124 isLoadingInfinite = true;
1125 elInfinite.classList.remove('courses-load-infinite');
1126 return {
1127 before: () => {
1128 loading.classList.remove('hide');
1129 },
1130 success: res => {
1131 elListCourse.insertAdjacentHTML('beforeend', res.data.content || '');
1132 if (res.data.pagination) {
1133 elListCourse.insertAdjacentHTML('afterend', res.data.pagination || '');
1134 }
1135
1136 // Set showing results page.
1137 const elCoursesPageResult = document.querySelector(`.${classCoursesPageResult}`);
1138 if (elCoursesPageResult) {
1139 elCoursesPageResult.innerHTML = res.data.from_to || '';
1140 }
1141 },
1142 error: error => {
1143 elListCourse.innerHTML += `<div class="lp-ajax-message error" style="display:block">${error.message || 'Error'}</div>`;
1144 },
1145 completed: () => {
1146 elInfinite.remove();
1147 isLoadingInfinite = false;
1148 }
1149 };
1150 },
1151 searchCourse: (e, target) => {
1152 if ('c_search' === target.name) {
1153 e.preventDefault();
1154 const parentFormSearch = target.closest('form.search-courses');
1155 if (!parentFormSearch) {
1156 return;
1157 }
1158 const btnSearch = parentFormSearch.querySelector('button[type="submit"]');
1159 btnSearch.click();
1160 return;
1161 }
1162 if (!target.classList.contains('search-courses')) {
1163 return;
1164 }
1165 const formSearchCourses = target;
1166 e.preventDefault();
1167 const elArchive = formSearchCourses.closest(`.${classArchiveCourse}`);
1168 if (!elArchive) {
1169 return;
1170 }
1171 const elListCourse = elArchive.querySelector(`.${classListCourse}`);
1172 if (!elListCourse) {
1173 return;
1174 }
1175 const elInputSearch = formSearchCourses.querySelector('input[name=c_search]');
1176 const keyword = elInputSearch.value;
1177 if (!keyword || keyword && keyword.length > 2) {
1178 if (undefined !== timeOutSearch) {
1179 clearTimeout(timeOutSearch);
1180 }
1181 timeOutSearch = setTimeout(function () {
1182 typeEventBeforeFetch = 'filter';
1183 filterCourses.c_search = keyword;
1184 filterCourses.paged = 1;
1185 window.lpCourseList.triggerFetchAPI(filterCourses);
1186 }, 800);
1187 }
1188 },
1189 ajaxEnableLoadPage: () => {
1190 // For case enable AJAX when load page.
1191 let countTime = 0;
1192 if (!lpArchiveNoLoadAjaxFirst) {
1193 let detectedElArchive;
1194 const callBack = {
1195 success: res => {
1196 detectedElArchive = setInterval(function () {
1197 const skeleton = document.querySelector(`.${classSkeletonArchiveCourse}`);
1198 const elArchive = document.querySelector(`.${classArchiveCourse}`);
1199 let elListCourse;
1200 if (elArchive) {
1201 elListCourse = elArchive.querySelector(`.${classListCourse}`);
1202 }
1203 ++countTime;
1204 if (countTime > 5000) {
1205 clearInterval(detectedElArchive);
1206 }
1207 if (elListCourse && skeleton) {
1208 clearInterval(detectedElArchive);
1209 elListCourse.insertAdjacentHTML('afterbegin', res.data.content || '');
1210 skeleton.style.display = 'none';
1211 const pagination = res.data.pagination || '';
1212 elListCourse.insertAdjacentHTML('afterend', pagination);
1213
1214 // Set showing results page.
1215 const elCoursesPageResult = document.querySelector(`.${classCoursesPageResult}`);
1216 if (elCoursesPageResult) {
1217 elCoursesPageResult.innerHTML = res.data.from_to || '';
1218 }
1219 }
1220 }, 1);
1221 }
1222 };
1223 if ('number' !== typePagination) {
1224 filterCourses.paged = 1;
1225 }
1226 fetchAPI(filterCourses, callBack);
1227 }
1228 },
1229 getFilterParams: () => {
1230 return filterCourses;
1231 },
1232 // Check has exists new list courses.
1233 checkIsNewListCourses: () => {
1234 const elListCoursesNew = document.querySelector(elListCoursesIdNewDefault);
1235 return !!elListCoursesNew;
1236 }
1237 };
1238 })();
1239 document.addEventListener('DOMContentLoaded', function () {
1240 if (window.lpCourseList.checkIsNewListCourses()) {
1241 return;
1242 }
1243 window.lpCourseList.init();
1244 window.lpCourseList.ajaxEnableLoadPage();
1245 });
1246 })();
1247
1248 /******/ })()
1249 ;
1250 //# sourceMappingURL=courses.js.map