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
learnpress / assets / js / dist / frontend / courses.js

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

1,235 lines 39.4 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 /******/ var __webpack_module_cache__ = {};
621 /******/
622 /******/ // The require function
623 /******/ function __webpack_require__(moduleId) {
624 /******/ // Check if module is in cache
625 /******/ var 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 /******/ var 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 /******/ var 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 functions for harmony exports
653 /******/ __webpack_require__.d = (exports, definition) => {
654 /******/ for(var key in definition) {
655 /******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
656 /******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
657 /******/ }
658 /******/ }
659 /******/ };
660 /******/ })();
661 /******/
662 /******/ /* webpack/runtime/hasOwnProperty shorthand */
663 /******/ (() => {
664 /******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
665 /******/ })();
666 /******/
667 /******/ /* webpack/runtime/make namespace object */
668 /******/ (() => {
669 /******/ // define __esModule on exports
670 /******/ __webpack_require__.r = (exports) => {
671 /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
672 /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
673 /******/ }
674 /******/ Object.defineProperty(exports, '__esModule', { value: true });
675 /******/ };
676 /******/ })();
677 /******/
678 /************************************************************************/
679 var __webpack_exports__ = {};
680 // This entry needs to be wrapped in an IIFE because it needs to be isolated against other modules in the chunk.
681 (() => {
682 /*!*******************************************!*\
683 !*** ./assets/src/js/frontend/courses.js ***!
684 \*******************************************/
685 __webpack_require__.r(__webpack_exports__);
686 /* harmony import */ var _api__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../api */ "./assets/src/js/api.js");
687 /* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../utils */ "./assets/src/js/utils.js");
688 /* harmony import */ var _utils_cookies__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../utils/cookies */ "./assets/src/js/utils/cookies.js");
689 /**
690 * @deprecated 4.2.5.8
691 * But still support for theme has html old, override.
692 * Todo: if want remove file, need to check theme override file archive, can via remove hook override file.
693 */
694
695
696
697
698 const elListCoursesIdNewDefault = '.lp-list-courses-default';
699 if ('undefined' === typeof lpData || 'undefined' === typeof lpSettingCourses) {
700 console.log('lpData || lpSettingCourses is undefined');
701 }
702
703 // Call API load courses.
704 // When LP v4.2.3.3 release a long time, we will remove this function on theme Eduma.
705 // assets/js/thim-course-filter-v2.js
706 window.lpArchiveRequestCourse = args => {
707 window.lpCourseList.updateEventTypeBeforeFetch('filter');
708 window.lpCourseList.triggerFetchAPI(args);
709 };
710
711 // Events
712 document.addEventListener('change', function (e) {
713 const target = e.target;
714 if (window.lpCourseList.checkIsNewListCourses()) {
715 return;
716 }
717 window.lpCourseList.onChangeSortBy(e, target);
718 window.lpCourseList.onChangeTypeLayout(e, target);
719 });
720 document.addEventListener('click', function (e) {
721 const target = e.target;
722 if (window.lpCourseList.checkIsNewListCourses()) {
723 return;
724 }
725 window.lpCourseList.clickLoadMore(e, target);
726 window.lpCourseList.clickNumberPage(e, target);
727 });
728 document.addEventListener('scroll', function (e) {
729 const target = e.target;
730 if (window.lpCourseList.checkIsNewListCourses()) {
731 return;
732 }
733 window.lpCourseList.scrollInfinite(e, target);
734 });
735 document.addEventListener('keyup', function (e) {
736 const target = e.target;
737 if (window.lpCourseList.checkIsNewListCourses()) {
738 return;
739 }
740 window.lpCourseList.searchCourse(e, target);
741 });
742 document.addEventListener('submit', function (e) {
743 const target = e.target;
744 if (window.lpCourseList.checkIsNewListCourses()) {
745 return;
746 }
747 window.lpCourseList.searchCourse(e, target);
748 });
749 window.lpCourseList = (() => {
750 const classArchiveCourse = 'lp-archive-courses';
751 const classListCourse = 'learn-press-courses';
752 const classPaginationCourse = 'learn-press-pagination';
753 const classSkeletonArchiveCourse = 'lp-archive-course-skeleton';
754 const classCoursesPageResult = 'courses-page-result';
755 const lpArchiveLoadAjax = parseInt(lpSettingCourses.lpArchiveLoadAjax || 0);
756 const lpArchiveNoLoadAjaxFirst = parseInt(lpSettingCourses.lpArchiveNoLoadAjaxFirst) === 1;
757 const lpArchiveSkeletonParam = lpData.urlParams || [];
758 const currentUrl = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.lpGetCurrentURLNoParam)();
759 let filterCourses = {};
760 const typePagination = lpSettingCourses.lpArchivePaginationType || 'number';
761 let typeEventBeforeFetch;
762 let timeOutSearch;
763 let isLoadingInfinite = false;
764 const fetchAPI = (args, callBack = {}) => {
765 //console.log( 'Fetch API Courses' );
766 const url = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.lpAddQueryArgs)(_api__WEBPACK_IMPORTED_MODULE_0__["default"].frontend.apiCourses, args);
767 let paramsFetch = {};
768 if (0 !== parseInt(lpData.user_id)) {
769 paramsFetch = {
770 headers: {
771 'X-WP-Nonce': lpData.nonce
772 }
773 };
774 }
775 (0,_utils__WEBPACK_IMPORTED_MODULE_1__.lpFetchAPI)(url, paramsFetch, callBack);
776 };
777 return {
778 init: () => {
779 const urlParams = {};
780 const urlQueryString = window.location.search;
781 const urlSearchParams = new URLSearchParams(urlQueryString);
782 for (const [key, val] of urlSearchParams.entries()) {
783 urlParams[key] = val;
784 }
785 filterCourses = {
786 ...lpArchiveSkeletonParam,
787 ...urlParams
788 };
789 filterCourses.paged = parseInt(filterCourses.paged || 1);
790 if (isNaN(filterCourses.paged)) {
791 filterCourses.paged = 1;
792 }
793 if (lpArchiveNoLoadAjaxFirst && typePagination !== 'number') {
794 filterCourses.paged = 1;
795 }
796 window.localStorage.setItem('lp_filter_courses', JSON.stringify(filterCourses));
797 },
798 updateEventTypeBeforeFetch: type => {
799 typeEventBeforeFetch = type;
800 },
801 onChangeSortBy: (e, target) => {
802 if (!target.classList.contains('courses-order-by')) {
803 return;
804 }
805 e.preventDefault();
806 const filterCourses = JSON.parse(window.localStorage.getItem('lp_filter_courses')) || {};
807 filterCourses.order_by = target.value || '';
808 if ('undefined' !== typeof lpSettingCourses && lpData.is_course_archive && lpSettingCourses.lpArchiveLoadAjax) {
809 window.lpCourseList.triggerFetchAPI(filterCourses);
810 } else {
811 window.location.href = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.lpAddQueryArgs)(currentUrl, filterCourses);
812 }
813 },
814 onChangeTypeLayout: (e, target) => {
815 if ('lp-switch-layout-btn' !== target.getAttribute('name')) {
816 return;
817 }
818 const elArchive = target.closest(`.${classArchiveCourse}`);
819 if (!elArchive) {
820 return;
821 }
822 const elListCourse = elArchive.querySelector(`.${classListCourse}`);
823 if (!elListCourse) {
824 return;
825 }
826 e.preventDefault();
827 const layout = target.value;
828 if (layout) {
829 elListCourse.dataset.layout = layout;
830 _utils_cookies__WEBPACK_IMPORTED_MODULE_2__["default"].set('courses-layout', layout);
831 }
832 },
833 clickNumberPage: (e, target) => {
834 if (!lpArchiveLoadAjax || parseInt(lpSettingCourses.noLoadCoursesJs)) {
835 return;
836 }
837 if (target.classList.contains('page-numbers')) {
838 const parentArchive = target.closest(`.${classArchiveCourse}`);
839 if (!parentArchive) {
840 return;
841 }
842 e.preventDefault();
843 const pageCurrent = filterCourses.paged;
844 if (target.classList.contains('prev')) {
845 filterCourses.paged = pageCurrent - 1;
846 } else if (target.classList.contains('next')) {
847 filterCourses.paged = pageCurrent + 1;
848 } else {
849 filterCourses.paged = parseInt(target.textContent);
850 }
851 typeEventBeforeFetch = 'number';
852 window.lpCourseList.triggerFetchAPI(filterCourses);
853 return;
854 }
855 const parent = target.closest('.page-numbers');
856 if (parent) {
857 e.preventDefault();
858 parent.click();
859 }
860 },
861 clickLoadMore: (e, target) => {
862 if (!target.classList.contains('courses-btn-load-more')) {
863 return;
864 }
865 const elArchive = target.closest(`.${classArchiveCourse}`);
866 if (!elArchive) {
867 return;
868 }
869 const elListCourse = elArchive.querySelector(`.${classListCourse}`);
870 if (!elListCourse) {
871 return;
872 }
873 e.preventDefault();
874 ++filterCourses.paged;
875 typeEventBeforeFetch = 'load-more';
876 window.lpCourseList.triggerFetchAPI(filterCourses);
877 },
878 scrollInfinite: (e, target) => {
879 const elArchive = document.querySelector(`.${classArchiveCourse}`);
880 if (!elArchive) {
881 return;
882 }
883 const elInfinite = elArchive.querySelector('.courses-load-infinite');
884 if (!elInfinite) {
885 return;
886 }
887
888 // Create an IntersectionObserver object.
889 const observer = new IntersectionObserver(function (entries) {
890 for (const entry of entries) {
891 // If the entry is intersecting, load the image.
892 if (entry.isIntersecting) {
893 if (isLoadingInfinite) {
894 return;
895 }
896 ++filterCourses.paged;
897 typeEventBeforeFetch = 'infinite';
898 window.lpCourseList.triggerFetchAPI(filterCourses);
899
900 //observer.unobserve( entry.target );
901 }
902 }
903 });
904 observer.observe(elInfinite);
905 },
906 triggerFetchAPI: args => {
907 // For case, click on pagination, filter.
908 const elArchive = document.querySelector(`.${classArchiveCourse}`);
909 if (!elArchive) {
910 return;
911 }
912 const elListCourse = elArchive.querySelector(`.${classListCourse}`);
913 if (!elListCourse) {
914 return;
915 }
916 filterCourses = args;
917 let callBack;
918 switch (typeEventBeforeFetch) {
919 case 'load-more':
920 callBack = window.lpCourseList.callBackPaginationTypeLoadMore(elArchive, elListCourse);
921 break;
922 case 'infinite':
923 callBack = window.lpCourseList.callBackPaginationTypeInfinite(elArchive, elListCourse);
924 break;
925 case 'custom':
926 callBack = args.customCallBack || false;
927 break;
928 case 'filter':
929 default:
930 // number
931 // Change url by params filter courses
932 //callBack = window.lpCourseList.callBackPaginationTypeNumber( elListCourse );
933 callBack = window.lpCourseList.callBackFilter(args, elArchive, elListCourse);
934 break;
935 }
936 if (!callBack) {
937 return;
938 }
939
940 //console.log( 'Args', args );
941
942 fetchAPI(args, callBack);
943 },
944 callBackFilter: (args, elArchive, elListCourse) => {
945 if (!elListCourse) {
946 return;
947 }
948 const skeleton = elListCourse.querySelector(`.${classSkeletonArchiveCourse}`);
949 return {
950 before: () => {
951 window.history.pushState('', '', (0,_utils__WEBPACK_IMPORTED_MODULE_1__.lpAddQueryArgs)(currentUrl, args));
952 window.localStorage.setItem('lp_filter_courses', JSON.stringify(args));
953 if (skeleton) {
954 skeleton.style.display = 'block';
955 }
956 },
957 success: res => {
958 // Remove all items before insert new items.
959 const elLis = elListCourse.querySelectorAll(`:not(.${classSkeletonArchiveCourse})`);
960 elLis.forEach(elLi => {
961 const parent = elLi.closest(`.${classSkeletonArchiveCourse}`);
962 if (parent) {
963 return;
964 }
965 elLi.remove();
966 });
967
968 // Insert new items.
969 elListCourse.insertAdjacentHTML('afterbegin', res.data.content || '');
970
971 // Check if Pagination exists will remove.
972 const elPagination = document.querySelector(`.${classPaginationCourse}`);
973 if (elPagination) {
974 elPagination.remove();
975 }
976
977 // Insert Pagination.
978 const pagination = res.data.pagination || '';
979 elListCourse.insertAdjacentHTML('afterend', pagination);
980
981 // Set showing results page.
982 const elCoursesPageResult = document.querySelector(`.${classCoursesPageResult}`);
983 if (elCoursesPageResult) {
984 elCoursesPageResult.innerHTML = res.data.from_to || '';
985 }
986 },
987 error: error => {
988 elListCourse.innerHTML += `<div class="lp-ajax-message error" style="display:block">${error.message || 'Error'}</div>`;
989 },
990 completed: () => {
991 if (skeleton) {
992 skeleton.style.display = 'none';
993 }
994 // Scroll to archive element
995 const optionScroll = {
996 behavior: 'smooth'
997 };
998 elListCourse.closest(`.${classArchiveCourse}`).scrollIntoView(optionScroll);
999 }
1000 };
1001 },
1002 /*callBackPaginationTypeNumber: ( elListCourse ) => {
1003 if ( ! elListCourse ) {
1004 return;
1005 }
1006 const skeleton = elListCourse.querySelector( `.${ classSkeletonArchiveCourse }` );
1007 return {
1008 before: () => {
1009 const urlPush = lpAddQueryArgs( currentUrl, args );
1010 window.history.pushState( '', '', urlPush );
1011 // Save filter courses to Storage
1012 window.localStorage.setItem( 'lp_filter_courses', JSON.stringify( args ) );
1013 if ( skeleton ) {
1014 skeleton.style.display = 'block';
1015 }
1016 },
1017 success: ( res ) => {
1018 // Remove all items before insert new items.
1019 const elLis = elListCourse.querySelectorAll( `:not(.${ classSkeletonArchiveCourse })` );
1020 elLis.forEach( ( elLi ) => {
1021 const parent = elLi.closest( `.${ classSkeletonArchiveCourse }` );
1022 if ( parent ) {
1023 return;
1024 }
1025 elLi.remove();
1026 } );
1027 // Insert new items.
1028 elListCourse.insertAdjacentHTML( 'afterbegin', res.data.content || '' );
1029 // Check if Pagination exists will remove.
1030 const elPagination = document.querySelector( `.${ classPaginationCourse }` );
1031 if ( elPagination ) {
1032 elPagination.remove();
1033 }
1034 // Insert Pagination.
1035 const pagination = res.data.pagination || '';
1036 elListCourse.insertAdjacentHTML( 'afterend', pagination );
1037 },
1038 error: ( error ) => {
1039 elListCourse.innerHTML += `<div class="lp-ajax-message error" style="display:block">${ error.message || 'Error' }</div>`;
1040 },
1041 completed: () => {
1042 if ( skeleton ) {
1043 skeleton.style.display = 'none';
1044 }
1045 // Scroll to archive element
1046 const optionScroll = { behavior: 'smooth' };
1047 elListCourse.closest( '.lp-archive-courses' ).scrollIntoView( optionScroll );
1048 },
1049 };
1050 },*/
1051 callBackPaginationTypeLoadMore: (elArchive, elListCourse) => {
1052 //console.log( 'callBackPaginationTypeLoadMore' );
1053 if (!elListCourse || !elArchive) {
1054 return false;
1055 }
1056 const btnLoadMore = elArchive.querySelector('.courses-btn-load-more');
1057 let elLoading;
1058 if (btnLoadMore) {
1059 elLoading = btnLoadMore.querySelector('.lp-loading-circle');
1060 }
1061 //const skeleton = document.querySelector( `.${ classSkeletonArchiveCourse }` );
1062
1063 return {
1064 before: () => {
1065 if (btnLoadMore) {
1066 elLoading.classList.remove('hide');
1067 btnLoadMore.setAttribute('disabled', 'disabled');
1068 }
1069
1070 /*if ( skeleton ) {
1071 skeleton.style.display = 'block';
1072 }*/
1073 },
1074 success: res => {
1075 elListCourse.insertAdjacentHTML('beforeend', res.data.content || '');
1076 elListCourse.insertAdjacentHTML('afterend', res.data.pagination || '');
1077
1078 // Set showing results page.
1079 const elCoursesPageResult = document.querySelector(`.${classCoursesPageResult}`);
1080 if (elCoursesPageResult) {
1081 elCoursesPageResult.innerHTML = res.data.from_to || '';
1082 }
1083 },
1084 error: error => {
1085 elListCourse.innerHTML += `<div class="lp-ajax-message error" style="display:block">${error.message || 'Error'}</div>`;
1086 },
1087 completed: () => {
1088 if (btnLoadMore) {
1089 elLoading.classList.add('hide');
1090 btnLoadMore.remove();
1091 }
1092
1093 /*if ( skeleton ) {
1094 skeleton.style.display = 'none';
1095 }*/
1096 }
1097 };
1098 },
1099 callBackPaginationTypeInfinite: (elArchive, elListCourse) => {
1100 //console.log( 'callBackPaginationTypeInfinite' );
1101 if (!elListCourse || !elListCourse) {
1102 return;
1103 }
1104 const elInfinite = elArchive.querySelector('.courses-load-infinite');
1105 if (!elInfinite) {
1106 return;
1107 }
1108 const loading = elInfinite.querySelector('.lp-loading-circle');
1109 isLoadingInfinite = true;
1110 elInfinite.classList.remove('courses-load-infinite');
1111 return {
1112 before: () => {
1113 loading.classList.remove('hide');
1114 },
1115 success: res => {
1116 elListCourse.insertAdjacentHTML('beforeend', res.data.content || '');
1117 if (res.data.pagination) {
1118 elListCourse.insertAdjacentHTML('afterend', res.data.pagination || '');
1119 }
1120
1121 // Set showing results page.
1122 const elCoursesPageResult = document.querySelector(`.${classCoursesPageResult}`);
1123 if (elCoursesPageResult) {
1124 elCoursesPageResult.innerHTML = res.data.from_to || '';
1125 }
1126 },
1127 error: error => {
1128 elListCourse.innerHTML += `<div class="lp-ajax-message error" style="display:block">${error.message || 'Error'}</div>`;
1129 },
1130 completed: () => {
1131 elInfinite.remove();
1132 isLoadingInfinite = false;
1133 }
1134 };
1135 },
1136 searchCourse: (e, target) => {
1137 if ('c_search' === target.name) {
1138 e.preventDefault();
1139 const parentFormSearch = target.closest('form.search-courses');
1140 if (!parentFormSearch) {
1141 return;
1142 }
1143 const btnSearch = parentFormSearch.querySelector('button[type="submit"]');
1144 btnSearch.click();
1145 return;
1146 }
1147 if (!target.classList.contains('search-courses')) {
1148 return;
1149 }
1150 const formSearchCourses = target;
1151 e.preventDefault();
1152 const elArchive = formSearchCourses.closest(`.${classArchiveCourse}`);
1153 if (!elArchive) {
1154 return;
1155 }
1156 const elListCourse = elArchive.querySelector(`.${classListCourse}`);
1157 if (!elListCourse) {
1158 return;
1159 }
1160 const elInputSearch = formSearchCourses.querySelector('input[name=c_search]');
1161 const keyword = elInputSearch.value;
1162 if (!keyword || keyword && keyword.length > 2) {
1163 if (undefined !== timeOutSearch) {
1164 clearTimeout(timeOutSearch);
1165 }
1166 timeOutSearch = setTimeout(function () {
1167 typeEventBeforeFetch = 'filter';
1168 filterCourses.c_search = keyword;
1169 filterCourses.paged = 1;
1170 window.lpCourseList.triggerFetchAPI(filterCourses);
1171 }, 800);
1172 }
1173 },
1174 ajaxEnableLoadPage: () => {
1175 // For case enable AJAX when load page.
1176 let countTime = 0;
1177 if (!lpArchiveNoLoadAjaxFirst) {
1178 let detectedElArchive;
1179 const callBack = {
1180 success: res => {
1181 detectedElArchive = setInterval(function () {
1182 const skeleton = document.querySelector(`.${classSkeletonArchiveCourse}`);
1183 const elArchive = document.querySelector(`.${classArchiveCourse}`);
1184 let elListCourse;
1185 if (elArchive) {
1186 elListCourse = elArchive.querySelector(`.${classListCourse}`);
1187 }
1188 ++countTime;
1189 if (countTime > 5000) {
1190 clearInterval(detectedElArchive);
1191 }
1192 if (elListCourse && skeleton) {
1193 clearInterval(detectedElArchive);
1194 elListCourse.insertAdjacentHTML('afterbegin', res.data.content || '');
1195 skeleton.style.display = 'none';
1196 const pagination = res.data.pagination || '';
1197 elListCourse.insertAdjacentHTML('afterend', pagination);
1198
1199 // Set showing results page.
1200 const elCoursesPageResult = document.querySelector(`.${classCoursesPageResult}`);
1201 if (elCoursesPageResult) {
1202 elCoursesPageResult.innerHTML = res.data.from_to || '';
1203 }
1204 }
1205 }, 1);
1206 }
1207 };
1208 if ('number' !== typePagination) {
1209 filterCourses.paged = 1;
1210 }
1211 fetchAPI(filterCourses, callBack);
1212 }
1213 },
1214 getFilterParams: () => {
1215 return filterCourses;
1216 },
1217 // Check has exists new list courses.
1218 checkIsNewListCourses: () => {
1219 const elListCoursesNew = document.querySelector(elListCoursesIdNewDefault);
1220 return !!elListCoursesNew;
1221 }
1222 };
1223 })();
1224 document.addEventListener('DOMContentLoaded', function () {
1225 if (window.lpCourseList.checkIsNewListCourses()) {
1226 return;
1227 }
1228 window.lpCourseList.init();
1229 window.lpCourseList.ajaxEnableLoadPage();
1230 });
1231 })();
1232
1233 /******/ })()
1234 ;
1235 //# sourceMappingURL=courses.js.map