PluginProbe
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses / 4.4.8
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses v4.4.8
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
← All changes | assets/src/js/frontend/course-builder.js +370 -370 4.3.94.4.8 View file →
@@ -1,370 +1,370 @@
1 -/**
2 - * Course builder JS handler.
3 - *
4 - * @since 4.3.6
5 - * @version 1.0.0
6 - */
7 -import * as lpUtils from 'lpAssetsJsPath/utils.js';
8 -import { BuilderTabCourse } from './course-builder/builder-course/builder-tab-course.js';
9 -import { BuilderEditCourse } from './course-builder/builder-course/builder-edit-course.js';
10 -import { CreateCourseViaAI } from '../admin/courses/generate-with-ai.js';
11 -import { GenerateWithOpenai } from '../admin/generate-with-openai.js';
12 -import { BuilderTabLesson } from './course-builder/builder-lesson/builder-tab-lesson.js';
13 -import { BuilderEditLesson } from './course-builder/builder-lesson/builder-edit-lesson.js';
14 -import { BuilderTabQuiz } from './course-builder/builder-quiz/builder-tab-quiz.js';
15 -import { BuilderStandaloneQuiz } from './course-builder/builder-quiz/builder-standalone-quiz.js';
16 -import { BuilderTabQuestion } from './course-builder/builder-question/builder-tab-question.js';
17 -import { BuilderEditQuestion } from './course-builder/builder-question/builder-edit-question.js';
18 -import { BuilderPopup } from './course-builder/builder-popup.js';
19 -import { BuilderMaterial } from './course-builder/builder-lesson/builder-material.js';
20 -import { BuilderDashboard } from './course-builder/builder-dashboard.js';
21 -import { BuilderSettings } from './course-builder/builder-settings.js';
22 -import { getFormState } from './course-builder/builder-form-state.js';
23 -import { EditCurriculumAi } from '../admin/edit-course/edit-curriculum/edit-curriculum-ai.js';
24 -import { initElsTomSelect } from 'lpAssetsJsPath/admin/init-tom-select.js';
25 -import { Utils } from 'lpAssetsJsPath/admin/utils-admin.js';
26 -import * as lpToastify from 'lpAssetsJsPath/lpToastify';
27 -
28 -// Initialize all builder components
29 -const initBuilderComponents = () => {
30 - try {
31 - new BuilderTabCourse();
32 - new BuilderEditCourse();
33 -
34 - const hasCreateCourseAiButton = document.querySelector(
35 - '.lp-btn-generate-course-with-ai, .lp-btn-warning-enable-ai'
36 - );
37 - if ( hasCreateCourseAiButton ) {
38 - new CreateCourseViaAI( {
39 - autoInsertButton: false,
40 - isCourseBuilder: true,
41 - redirectDelayMs: 1200,
42 - } );
43 - }
44 -
45 - const hasEditCourseAiButton = document.querySelector(
46 - '.cb-course-edit-ai-btn.lp-btn-generate-with-ai'
47 - );
48 - if ( hasEditCourseAiButton ) {
49 - new GenerateWithOpenai( {
50 - autoInsertButtons: false,
51 - isCourseBuilder: true,
52 - } );
53 - }
54 -
55 - new BuilderTabLesson();
56 - new BuilderEditLesson();
57 - new BuilderTabQuiz();
58 - new BuilderStandaloneQuiz();
59 - new BuilderTabQuestion();
60 - new BuilderEditQuestion();
61 - new BuilderPopup();
62 - new BuilderDashboard();
63 - new BuilderSettings();
64 - new EditCurriculumAi( { isCourseBuilder: true } );
65 -
66 - // Initialize form state management for ClassPress-style UX
67 - getFormState();
68 -
69 - // Initialize sidebar toggle
70 - initSidebarToggle();
71 - initSidebarSubMenus();
72 - initHeaderMoreActions();
73 - } catch ( e ) {
74 - console.error( 'Error initializing builder components:', e );
75 - }
76 -};
77 -
78 -/**
79 - * Initialize sidebar collapse/expand toggle
80 - * Persists state in localStorage
81 - */
82 -const initSidebarToggle = () => {
83 - const sidebar = document.getElementById( 'lp-course-builder-sidebar' );
84 - const toggleBtn = document.querySelector( '.lp-cb-sidebar__toggle' );
85 - const wrapper = document.getElementById( 'lp-course-builder' );
86 - const storageKey = 'lp_cb_sidebar_collapsed';
87 -
88 - if ( ! sidebar || ! toggleBtn ) {
89 - return;
90 - }
91 -
92 - // Restore saved state
93 - const isCollapsed = localStorage.getItem( storageKey ) === 'true';
94 - if ( isCollapsed ) {
95 - sidebar.classList.add( 'is-collapsed' );
96 - if ( wrapper ) {
97 - wrapper.classList.add( 'has-collapsed-sidebar' );
98 - }
99 - }
100 -
101 - // Handle toggle click
102 - toggleBtn.addEventListener( 'click', () => {
103 - const willCollapse = ! sidebar.classList.contains( 'is-collapsed' );
104 -
105 - sidebar.classList.toggle( 'is-collapsed' );
106 - if ( wrapper ) {
107 - wrapper.classList.toggle( 'has-collapsed-sidebar' );
108 - }
109 -
110 - // Save state
111 - localStorage.setItem( storageKey, willCollapse ? 'true' : 'false' );
112 - } );
113 -};
114 -
115 -/**
116 - * Initialize nested sidebar submenu toggles.
117 - */
118 -const initSidebarSubMenus = () => {
119 - const sidebar = document.getElementById( 'lp-course-builder-sidebar' );
120 -
121 - if ( ! sidebar || sidebar.dataset.subMenusInitialized === 'true' ) {
122 - return;
123 - }
124 -
125 - sidebar.dataset.subMenusInitialized = 'true';
126 -
127 - sidebar.addEventListener( 'click', ( e ) => {
128 - const toggleBtn = e.target.closest( '.lp-cb-sidebar__sub-menu-toggle' );
129 -
130 - if ( ! toggleBtn || ! sidebar.contains( toggleBtn ) ) {
131 - return;
132 - }
133 -
134 - const item = toggleBtn.closest( '.lp-cb-sidebar__item.has-sub-menu' );
135 - if ( ! item ) {
136 - return;
137 - }
138 -
139 - e.preventDefault();
140 -
141 - const isExpanded = item.classList.toggle( 'is-expanded' );
142 - toggleBtn.setAttribute( 'aria-expanded', isExpanded ? 'true' : 'false' );
143 - } );
144 -};
145 -
146 -/**
147 - * Initialize "More actions" menu in edit header (duplicate/trash).
148 - * Uses explicit open state to avoid focus-only behavior issues across browsers.
149 - */
150 -const initHeaderMoreActions = () => {
151 - const wrapSelector = '.cb-header-action-expanded';
152 - const toggleSelector = '.course-action-expanded';
153 - const openClass = 'is-open';
154 -
155 - const closeAll = ( exclude = null ) => {
156 - document.querySelectorAll( `${ wrapSelector }.${ openClass }` ).forEach( ( wrap ) => {
157 - if ( exclude && wrap === exclude ) {
158 - return;
159 - }
160 -
161 - wrap.classList.remove( openClass );
162 - const btn = wrap.querySelector( toggleSelector );
163 - if ( btn ) {
164 - btn.setAttribute( 'aria-expanded', 'false' );
165 - }
166 - } );
167 - };
168 -
169 - document.addEventListener( 'click', ( e ) => {
170 - const target = e.target;
171 - const toggleBtn = target.closest( toggleSelector );
172 -
173 - if ( toggleBtn ) {
174 - e.preventDefault();
175 - e.stopPropagation();
176 -
177 - const wrap = toggleBtn.closest( wrapSelector );
178 - if ( ! wrap ) {
179 - return;
180 - }
181 -
182 - const willOpen = ! wrap.classList.contains( openClass );
183 - closeAll( wrap );
184 - wrap.classList.toggle( openClass, willOpen );
185 - toggleBtn.setAttribute(
186 - 'aria-expanded',
187 - willOpen ? 'true' : 'false',
188 - );
189 - return;
190 - }
191 -
192 - if ( ! target.closest( wrapSelector ) ) {
193 - closeAll();
194 - }
195 -
196 - // New expand item
197 - const elActionExpands = document.querySelectorAll(
198 - '.lp-cb-item-action-expand',
199 - );
200 - if ( target.closest( '.lp-cb-item-action-expand-toggle' ) ) {
201 - const elParentAction = target.closest( '.lp-cb-item-action-wrap' );
202 - const elActionExpand = elParentAction.querySelector(
203 - '.lp-cb-item-action-expand',
204 - );
205 -
206 - elActionExpand.classList.toggle( lpUtils.lpClassName.hidden );
207 -
208 - // Close all
209 - elActionExpands.forEach( ( elActionExpandCheck ) => {
210 - if ( elActionExpandCheck !== elActionExpand ) {
211 - elActionExpandCheck.classList.add( lpUtils.lpClassName.hidden );
212 - }
213 - } );
214 - }
215 -
216 - if ( ! target.closest( '.lp-cb-item-action-expand-toggle' ) ) {
217 - elActionExpands.forEach( ( elActionExpandCheck ) => {
218 - elActionExpandCheck.classList.add(
219 - lpUtils.lpClassName.hidden,
220 - );
221 - } );
222 - }
223 - // End new expand item
224 -
225 - // Item action
226 - if ( target.closest( '.lp-cb-item-action' ) ) {
227 - const elAction = target.closest( '.lp-cb-item-action' );
228 - const elParentActionWrap = elAction.closest(
229 - '.lp-cb-item-action-wrap',
230 - );
231 - const elParentActionExpand = elParentActionWrap.querySelector(
232 - '.lp-cb-item-action-expand-toggle',
233 - );
234 - const dataSend = JSON.parse( elAction.dataset.send );
235 -
236 - lpUtils.lpSetLoadingEl( elParentActionExpand, 1 );
237 - const svg = elParentActionExpand.querySelector( 'svg' );
238 - //lpUtils.lpShowHideEl( svg, 0 );
239 -
240 - // Ajax to generate prompt
241 - const callBack = {
242 - success: ( response ) => {
243 - const { message, status, data } = response;
244 -
245 - lpToastify.show( message, status );
246 -
247 - if ( status === 'success' ) {
248 - if ( data.redirect_url ) {
249 - window.location.href = data.redirect_url;
250 - } else if ( data.html ) {
251 - const elParentLi = elAction.closest( 'li.course' );
252 - elParentLi.outerHTML = data.html;
253 - } else if ( dataSend.action_type === 'delete' ) {
254 - const elParentLi = elAction.closest( 'li.course' );
255 - elParentLi.remove();
256 - }
257 - }
258 - },
259 - error: ( error ) => {
260 - lpToastify.show( error, 'error' );
261 - },
262 - completed: () => {
263 - lpUtils.lpSetLoadingEl( elParentActionExpand, 0 );
264 - },
265 - };
266 -
267 - window.lpAJAXG.fetchAJAX( dataSend, callBack );
268 - }
269 - // End item action
270 - } );
271 -
272 - document.addEventListener( 'keydown', ( e ) => {
273 - if ( e.key === 'Escape' ) {
274 - closeAll();
275 - }
276 - } );
277 -};
278 -
279 -// Add loading state to search buttons on form submit (page reload)
280 -document.addEventListener( 'submit', ( e ) => {
281 - const form = e.target.closest( '.cb-search-form' );
282 - if ( ! form ) {
283 - return;
284 - }
285 -
286 - const searchBtn = form.querySelector( '.cb-search-btn' );
287 - if ( searchBtn ) {
288 - searchBtn.classList.add( 'loading' );
289 - searchBtn.disabled = true;
290 - }
291 -} );
292 -
293 -// Initialize components
294 -initBuilderComponents();
295 -
296 -// Events
297 -document.addEventListener( 'click', ( e ) => {
298 - try {
299 - initElsTomSelect();
300 - } catch ( e ) {
301 - console.warn( 'Error initializing TomSelect:', e );
302 - }
303 -} );
304 -
305 -document.addEventListener( 'DOMContentLoaded', () => {
306 - // Sure that the TomSelect is loaded if listener can't find elements.
307 - try {
308 - initElsTomSelect();
309 - } catch ( e ) {
310 - console.warn( 'Error initializing TomSelect on DOMContentLoaded:', e );
311 - }
312 -
313 - // Initialize BuilderMaterial for Course Builder Settings tab Material
314 - try {
315 - initBuilderMaterialForCourseSettings();
316 - } catch ( e ) {
317 - console.error( 'Error initializing BuilderMaterial:', e );
318 - }
319 -} );
320 -
321 -// Use lpOnElementReady safely
322 -if ( Utils?.lpOnElementReady ) {
323 - Utils.lpOnElementReady( 'select.lp-tom-select', () => {
324 - try {
325 - initElsTomSelect();
326 - } catch ( e ) {
327 - console.warn( 'Error initializing TomSelect:', e );
328 - }
329 - } );
330 -}
331 -
332 -window.lpFindTomSelect = initElsTomSelect;
333 -
334 -/**
335 - * Initialize BuilderMaterial for Course Builder Settings tab Material
336 - */
337 -function initBuilderMaterialForCourseSettings() {
338 - const initializedContainers = new WeakSet();
339 -
340 - // Listen for tab clicks in Course Settings using event delegation
341 - document.addEventListener( 'click', ( e ) => {
342 - const target = e.target.closest( 'ul.lp-meta-box__course-tab__tabs a' );
343 -
344 - if ( ! target ) {
345 - return;
346 - }
347 -
348 - const targetPanel = target.getAttribute( 'href' );
349 -
350 - // Check if Material tab is clicked
351 - if ( targetPanel && targetPanel.includes( 'material' ) ) {
352 - // Wait for DOM to update
353 - setTimeout( () => {
354 - try {
355 - const materialContainer = document.querySelector(
356 - targetPanel + ' #lp-material-container'
357 - );
358 - if ( materialContainer && ! initializedContainers.has( materialContainer ) ) {
359 - // Mark as initialized to prevent multiple instances
360 - initializedContainers.add( materialContainer );
361 - // Initialize BuilderMaterial
362 - new BuilderMaterial( materialContainer );
363 - }
364 - } catch ( e ) {
365 - console.error( 'Error initializing BuilderMaterial:', e );
366 - }
367 - }, 100 );
368 - }
369 - } );
370 -}
1 +/**
2 + * Course builder JS handler.
3 + *
4 + * @since 4.3.6
5 + * @version 1.0.0
6 + */
7 +import * as lpUtils from 'lpAssetsJsPath/utils.js';
8 +import { BuilderTabCourse } from './course-builder/builder-course/builder-tab-course.js';
9 +import { BuilderEditCourse } from './course-builder/builder-course/builder-edit-course.js';
10 +import { CreateCourseViaAI } from '../admin/courses/generate-with-ai.js';
11 +import { GenerateWithOpenai } from '../admin/generate-with-openai.js';
12 +import { BuilderTabLesson } from './course-builder/builder-lesson/builder-tab-lesson.js';
13 +import { BuilderEditLesson } from './course-builder/builder-lesson/builder-edit-lesson.js';
14 +import { BuilderTabQuiz } from './course-builder/builder-quiz/builder-tab-quiz.js';
15 +import { BuilderStandaloneQuiz } from './course-builder/builder-quiz/builder-standalone-quiz.js';
16 +import { BuilderTabQuestion } from './course-builder/builder-question/builder-tab-question.js';
17 +import { BuilderEditQuestion } from './course-builder/builder-question/builder-edit-question.js';
18 +import { BuilderPopup } from './course-builder/builder-popup.js';
19 +import { BuilderMaterial } from './course-builder/builder-lesson/builder-material.js';
20 +import { BuilderDashboard } from './course-builder/builder-dashboard.js';
21 +import { BuilderSettings } from './course-builder/builder-settings.js';
22 +import { getFormState } from './course-builder/builder-form-state.js';
23 +import { EditCurriculumAi } from '../admin/edit-course/edit-curriculum/edit-curriculum-ai.js';
24 +import { initElsTomSelect } from 'lpAssetsJsPath/admin/init-tom-select.js';
25 +import { Utils } from 'lpAssetsJsPath/admin/utils-admin.js';
26 +import * as lpToastify from 'lpAssetsJsPath/lpToastify';
27 +
28 +// Initialize all builder components
29 +const initBuilderComponents = () => {
30 + try {
31 + new BuilderTabCourse();
32 + new BuilderEditCourse();
33 +
34 + const hasCreateCourseAiButton = document.querySelector(
35 + '.lp-btn-generate-course-with-ai, .lp-btn-warning-enable-ai'
36 + );
37 + if ( hasCreateCourseAiButton ) {
38 + new CreateCourseViaAI( {
39 + autoInsertButton: false,
40 + isCourseBuilder: true,
41 + redirectDelayMs: 1200,
42 + } );
43 + }
44 +
45 + const hasEditCourseAiButton = document.querySelector(
46 + '.cb-course-edit-ai-btn.lp-btn-generate-with-ai'
47 + );
48 + if ( hasEditCourseAiButton ) {
49 + new GenerateWithOpenai( {
50 + autoInsertButtons: false,
51 + isCourseBuilder: true,
52 + } );
53 + }
54 +
55 + new BuilderTabLesson();
56 + new BuilderEditLesson();
57 + new BuilderTabQuiz();
58 + new BuilderStandaloneQuiz();
59 + new BuilderTabQuestion();
60 + new BuilderEditQuestion();
61 + new BuilderPopup();
62 + new BuilderDashboard();
63 + new BuilderSettings();
64 + new EditCurriculumAi( { isCourseBuilder: true } );
65 +
66 + // Initialize form state management for ClassPress-style UX
67 + getFormState();
68 +
69 + // Initialize sidebar toggle
70 + initSidebarToggle();
71 + initSidebarSubMenus();
72 + initHeaderMoreActions();
73 + } catch ( e ) {
74 + console.error( 'Error initializing builder components:', e );
75 + }
76 +};
77 +
78 +/**
79 + * Initialize sidebar collapse/expand toggle
80 + * Persists state in localStorage
81 + */
82 +const initSidebarToggle = () => {
83 + const sidebar = document.getElementById( 'lp-course-builder-sidebar' );
84 + const toggleBtn = document.querySelector( '.lp-cb-sidebar__toggle' );
85 + const wrapper = document.getElementById( 'lp-course-builder' );
86 + const storageKey = 'lp_cb_sidebar_collapsed';
87 +
88 + if ( ! sidebar || ! toggleBtn ) {
89 + return;
90 + }
91 +
92 + // Restore saved state
93 + const isCollapsed = localStorage.getItem( storageKey ) === 'true';
94 + if ( isCollapsed ) {
95 + sidebar.classList.add( 'is-collapsed' );
96 + if ( wrapper ) {
97 + wrapper.classList.add( 'has-collapsed-sidebar' );
98 + }
99 + }
100 +
101 + // Handle toggle click
102 + toggleBtn.addEventListener( 'click', () => {
103 + const willCollapse = ! sidebar.classList.contains( 'is-collapsed' );
104 +
105 + sidebar.classList.toggle( 'is-collapsed' );
106 + if ( wrapper ) {
107 + wrapper.classList.toggle( 'has-collapsed-sidebar' );
108 + }
109 +
110 + // Save state
111 + localStorage.setItem( storageKey, willCollapse ? 'true' : 'false' );
112 + } );
113 +};
114 +
115 +/**
116 + * Initialize nested sidebar submenu toggles.
117 + */
118 +const initSidebarSubMenus = () => {
119 + const sidebar = document.getElementById( 'lp-course-builder-sidebar' );
120 +
121 + if ( ! sidebar || sidebar.dataset.subMenusInitialized === 'true' ) {
122 + return;
123 + }
124 +
125 + sidebar.dataset.subMenusInitialized = 'true';
126 +
127 + sidebar.addEventListener( 'click', ( e ) => {
128 + const toggleBtn = e.target.closest( '.lp-cb-sidebar__sub-menu-toggle' );
129 +
130 + if ( ! toggleBtn || ! sidebar.contains( toggleBtn ) ) {
131 + return;
132 + }
133 +
134 + const item = toggleBtn.closest( '.lp-cb-sidebar__item.has-sub-menu' );
135 + if ( ! item ) {
136 + return;
137 + }
138 +
139 + e.preventDefault();
140 +
141 + const isExpanded = item.classList.toggle( 'is-expanded' );
142 + toggleBtn.setAttribute( 'aria-expanded', isExpanded ? 'true' : 'false' );
143 + } );
144 +};
145 +
146 +/**
147 + * Initialize "More actions" menu in edit header (duplicate/trash).
148 + * Uses explicit open state to avoid focus-only behavior issues across browsers.
149 + */
150 +const initHeaderMoreActions = () => {
151 + const wrapSelector = '.cb-header-action-expanded';
152 + const toggleSelector = '.course-action-expanded';
153 + const openClass = 'is-open';
154 +
155 + const closeAll = ( exclude = null ) => {
156 + document.querySelectorAll( `${ wrapSelector }.${ openClass }` ).forEach( ( wrap ) => {
157 + if ( exclude && wrap === exclude ) {
158 + return;
159 + }
160 +
161 + wrap.classList.remove( openClass );
162 + const btn = wrap.querySelector( toggleSelector );
163 + if ( btn ) {
164 + btn.setAttribute( 'aria-expanded', 'false' );
165 + }
166 + } );
167 + };
168 +
169 + document.addEventListener( 'click', ( e ) => {
170 + const target = e.target;
171 + const toggleBtn = target.closest( toggleSelector );
172 +
173 + if ( toggleBtn ) {
174 + e.preventDefault();
175 + e.stopPropagation();
176 +
177 + const wrap = toggleBtn.closest( wrapSelector );
178 + if ( ! wrap ) {
179 + return;
180 + }
181 +
182 + const willOpen = ! wrap.classList.contains( openClass );
183 + closeAll( wrap );
184 + wrap.classList.toggle( openClass, willOpen );
185 + toggleBtn.setAttribute(
186 + 'aria-expanded',
187 + willOpen ? 'true' : 'false',
188 + );
189 + return;
190 + }
191 +
192 + if ( ! target.closest( wrapSelector ) ) {
193 + closeAll();
194 + }
195 +
196 + // New expand item
197 + const elActionExpands = document.querySelectorAll(
198 + '.lp-cb-item-action-expand',
199 + );
200 + if ( target.closest( '.lp-cb-item-action-expand-toggle' ) ) {
201 + const elParentAction = target.closest( '.lp-cb-item-action-wrap' );
202 + const elActionExpand = elParentAction.querySelector(
203 + '.lp-cb-item-action-expand',
204 + );
205 +
206 + elActionExpand.classList.toggle( lpUtils.lpClassName.hidden );
207 +
208 + // Close all
209 + elActionExpands.forEach( ( elActionExpandCheck ) => {
210 + if ( elActionExpandCheck !== elActionExpand ) {
211 + elActionExpandCheck.classList.add( lpUtils.lpClassName.hidden );
212 + }
213 + } );
214 + }
215 +
216 + if ( ! target.closest( '.lp-cb-item-action-expand-toggle' ) ) {
217 + elActionExpands.forEach( ( elActionExpandCheck ) => {
218 + elActionExpandCheck.classList.add(
219 + lpUtils.lpClassName.hidden,
220 + );
221 + } );
222 + }
223 + // End new expand item
224 +
225 + // Item action
226 + if ( target.closest( '.lp-cb-item-action' ) ) {
227 + const elAction = target.closest( '.lp-cb-item-action' );
228 + const elParentActionWrap = elAction.closest(
229 + '.lp-cb-item-action-wrap',
230 + );
231 + const elParentActionExpand = elParentActionWrap.querySelector(
232 + '.lp-cb-item-action-expand-toggle',
233 + );
234 + const dataSend = JSON.parse( elAction.dataset.send );
235 +
236 + lpUtils.lpSetLoadingEl( elParentActionExpand, 1 );
237 + const svg = elParentActionExpand.querySelector( 'svg' );
238 + //lpUtils.lpShowHideEl( svg, 0 );
239 +
240 + // Ajax to generate prompt
241 + const callBack = {
242 + success: ( response ) => {
243 + const { message, status, data } = response;
244 +
245 + lpToastify.show( message, status );
246 +
247 + if ( status === 'success' ) {
248 + if ( data.redirect_url ) {
249 + window.location.href = data.redirect_url;
250 + } else if ( data.html ) {
251 + const elParentLi = elAction.closest( 'li.course' );
252 + elParentLi.outerHTML = data.html;
253 + } else if ( dataSend.action_type === 'delete' ) {
254 + const elParentLi = elAction.closest( 'li.course' );
255 + elParentLi.remove();
256 + }
257 + }
258 + },
259 + error: ( error ) => {
260 + lpToastify.show( error, 'error' );
261 + },
262 + completed: () => {
263 + lpUtils.lpSetLoadingEl( elParentActionExpand, 0 );
264 + },
265 + };
266 +
267 + window.lpAJAXG.fetchAJAX( dataSend, callBack );
268 + }
269 + // End item action
270 + } );
271 +
272 + document.addEventListener( 'keydown', ( e ) => {
273 + if ( e.key === 'Escape' ) {
274 + closeAll();
275 + }
276 + } );
277 +};
278 +
279 +// Add loading state to search buttons on form submit (page reload)
280 +document.addEventListener( 'submit', ( e ) => {
281 + const form = e.target.closest( '.cb-search-form' );
282 + if ( ! form ) {
283 + return;
284 + }
285 +
286 + const searchBtn = form.querySelector( '.cb-search-btn' );
287 + if ( searchBtn ) {
288 + searchBtn.classList.add( 'loading' );
289 + searchBtn.disabled = true;
290 + }
291 +} );
292 +
293 +// Initialize components
294 +initBuilderComponents();
295 +
296 +// Events
297 +document.addEventListener( 'click', ( e ) => {
298 + try {
299 + initElsTomSelect();
300 + } catch ( e ) {
301 + console.warn( 'Error initializing TomSelect:', e );
302 + }
303 +} );
304 +
305 +document.addEventListener( 'DOMContentLoaded', () => {
306 + // Sure that the TomSelect is loaded if listener can't find elements.
307 + try {
308 + initElsTomSelect();
309 + } catch ( e ) {
310 + console.warn( 'Error initializing TomSelect on DOMContentLoaded:', e );
311 + }
312 +
313 + // Initialize BuilderMaterial for Course Builder Settings tab Material
314 + try {
315 + initBuilderMaterialForCourseSettings();
316 + } catch ( e ) {
317 + console.error( 'Error initializing BuilderMaterial:', e );
318 + }
319 +} );
320 +
321 +// Use lpOnElementReady safely
322 +if ( Utils?.lpOnElementReady ) {
323 + Utils.lpOnElementReady( 'select.lp-tom-select', () => {
324 + try {
325 + initElsTomSelect();
326 + } catch ( e ) {
327 + console.warn( 'Error initializing TomSelect:', e );
328 + }
329 + } );
330 +}
331 +
332 +window.lpFindTomSelect = initElsTomSelect;
333 +
334 +/**
335 + * Initialize BuilderMaterial for Course Builder Settings tab Material
336 + */
337 +function initBuilderMaterialForCourseSettings() {
338 + const initializedContainers = new WeakSet();
339 +
340 + // Listen for tab clicks in Course Settings using event delegation
341 + document.addEventListener( 'click', ( e ) => {
342 + const target = e.target.closest( 'ul.lp-meta-box__course-tab__tabs a' );
343 +
344 + if ( ! target ) {
345 + return;
346 + }
347 +
348 + const targetPanel = target.getAttribute( 'href' );
349 +
350 + // Check if Material tab is clicked
351 + if ( targetPanel && targetPanel.includes( 'material' ) ) {
352 + // Wait for DOM to update
353 + setTimeout( () => {
354 + try {
355 + const materialContainer = document.querySelector(
356 + targetPanel + ' #lp-material-container'
357 + );
358 + if ( materialContainer && ! initializedContainers.has( materialContainer ) ) {
359 + // Mark as initialized to prevent multiple instances
360 + initializedContainers.add( materialContainer );
361 + // Initialize BuilderMaterial
362 + new BuilderMaterial( materialContainer );
363 + }
364 + } catch ( e ) {
365 + console.error( 'Error initializing BuilderMaterial:', e );
366 + }
367 + }, 100 );
368 + }
369 + } );
370 +}