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

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

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