# learnpress/4.4.9/assets/src/js/frontend/course-builder/builder-popup.js

LearnPress – WordPress LMS Plugin for Create and Sell Online Courses, version 4.4.9. 1,895 lines.

- Page: https://pluginprobe.com/plugins/learnpress/4.4.9/code/assets/src/js/frontend/course-builder/builder-popup.js
- Raw: https://pluginprobe.com/plugins/learnpress/4.4.9/raw/assets/src/js/frontend/course-builder/builder-popup.js
- Modified: 2026-09-27T10:32:46+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/learnpress/4.4.9/code/assets/src/js/frontend/course-builder/builder-popup.js#L10-L20`.

```javascript
/**
 * Builder Popup Handler
 * Handles AJAX popup loading for lesson, quiz, and question builders.
 *
 * @since 4.3.0
 * @version 1.0.1
 */

import * as lpUtils from 'lpAssetsJsPath/utils.js';
import * as lpToastify from 'lpAssetsJsPath/lpToastify.js';
import SweetAlert from 'sweetalert2';
import { SWAL_ICON_TRASH_DRAFT } from './swal-icons.js';
import { BuilderEditQuiz } from './builder-quiz/builder-edit-quiz.js';
import { BuilderEditQuestion } from './builder-question/builder-edit-question.js';
import { BuilderMaterial } from './builder-lesson/builder-material.js';

export class BuilderPopup {
	constructor() {
		this.popupContainer = null;
		this.currentType = null;
		this.currentId = null;
		this.currentTemplate = '';
		this.isNewItem = false;
		this.savedData = null;
		this.openContext = this.getDefaultOpenContext();
		this.builderEditQuiz = null;
		this.builderEditQuestion = null;
		this.builderMaterial = null;
		this.loadedTabAssets = new Set();
		this.initializedTabs = new Map();
		this.init();
	}

	static selectors = {
		popupContainer: '#lp-builder-popup-container',
		popupOverlay: '.lp-builder-popup-overlay',
		popup: '.lp-builder-popup',
		closeBtn: '.lp-builder-popup__close',
		resizeBtn: '.lp-builder-popup__resize',
		cancelBtn: '.lp-builder-popup__btn--cancel',
		saveBtn: '.lp-builder-popup__btn--save',
		draftBtn: '.lp-builder-popup__btn--draft',
		trashBtn: '.lp-builder-popup__btn--trash',
		tabs: '.lp-builder-popup__tabs',
		tab: '.lp-builder-popup__tab',
		tabPane: '.lp-builder-popup__tab-pane',
		permalinkSlugInput: '.cb-permalink-slug-input',
		permalinkUrl: '.cb-permalink-url',
		permalinkBaseUrl: '#cb-permalink-base-url',
		permalinkDisplay: '.cb-permalink-display',
		permalinkEditor: '.cb-permalink-editor',
		permalinkRoot: '.cb-item-edit-permalink, .cb-course-edit-permalink',
		permalinkPlaceholder: '.cb-item-edit-permalink__placeholder',
		// Trigger buttons
		popupTrigger:
			'[data-popup-lesson], [data-popup-quiz], [data-popup-question], [data-add-new-lesson], [data-template][data-popup-type]',
		triggerLesson: '[data-popup-lesson]',
		triggerQuiz: '[data-popup-quiz]',
		triggerQuestion: '[data-popup-question]',
		// Add new buttons
		addNewLesson: '[data-add-new-lesson]',
	};

	init() {
		let popupContainer = document.querySelector( BuilderPopup.selectors.popupContainer );

		if ( ! popupContainer ) {
			popupContainer = document.createElement( 'div' );
			popupContainer.id = 'lp-builder-popup-container';
			document.body.appendChild( popupContainer );
		}

		this.popupContainer = popupContainer;
		this.events();
	}

	events() {
		if ( BuilderPopup._loadedEvents ) {
			return;
		}
		BuilderPopup._loadedEvents = true;

		lpUtils.eventHandlers( 'click', [
			{
				selector: BuilderPopup.selectors.popupTrigger,
				class: this,
				callBack: this.openPopup.name,
			},
			{
				selector: `${ BuilderPopup.selectors.closeBtn }, ${ BuilderPopup.selectors.cancelBtn }, ${ BuilderPopup.selectors.popupOverlay }`,
				class: this,
				callBack: this.closePopup.name,
				conditionBeforeCallBack: () => this.isPopupOpen(),
			},
			{
				selector: BuilderPopup.selectors.resizeBtn,
				class: this,
				callBack: this.toggleFullscreen.name,
				conditionBeforeCallBack: () => this.isPopupOpen(),
			},
			{
				selector: BuilderPopup.selectors.tab,
				class: this,
				callBack: this.switchTab.name,
				conditionBeforeCallBack: () => this.isPopupOpen(),
			},
			{
				selector: BuilderPopup.selectors.saveBtn,
				class: this,
				callBack: this.handleSave.name,
				conditionBeforeCallBack: () => this.isPopupOpen(),
			},
			{
				selector: BuilderPopup.selectors.draftBtn,
				class: this,
				callBack: this.handleDraft.name,
				conditionBeforeCallBack: () => this.isPopupOpen(),
			},
			{
				selector: BuilderPopup.selectors.trashBtn,
				class: this,
				callBack: this.handleTrash.name,
				conditionBeforeCallBack: () => this.isPopupOpen(),
			},
		] );

		lpUtils.eventHandlers( 'keydown', [
			{
				selector: 'body',
				class: this,
				callBack: this.closePopup.name,
				conditionBeforeCallBack: ( args ) => args.e.key === 'Escape' && this.isPopupOpen(),
			},
		] );
	}

	/**
	 * Toggle fullscreen mode for popup
	 */
	toggleFullscreen() {
		const popup = this.popupContainer.querySelector( BuilderPopup.selectors.popup );
		if ( ! popup ) {
			return;
		}

		popup.classList.toggle( 'lp-builder-popup--fullscreen' );

		// Update resize button icon
		const resizeBtn = popup.querySelector( BuilderPopup.selectors.resizeBtn );
		if ( resizeBtn ) {
			const icon = resizeBtn.querySelector( 'i' );
			if ( icon ) {
				const isFullscreen = popup.classList.contains( 'lp-builder-popup--fullscreen' );
				icon.classList.toggle( 'lp-icon-expand', ! isFullscreen );
				icon.classList.toggle( 'lp-icon-compress', isFullscreen );
			}
		}

		document.dispatchEvent(
			new CustomEvent( 'lp-builder-popup-fullscreen-toggled', {
				detail: {
					isFullscreen: popup.classList.contains( 'lp-builder-popup--fullscreen' ),
					type: this.currentType,
					id: this.currentId,
				},
			} )
		);
	}

	openPopup( args ) {
		const { target } = args;
		const triggerEl = target.closest( BuilderPopup.selectors.popupTrigger );
		if ( ! triggerEl ) {
			return;
		}

		let type = '';
		let id = 0;

		if ( triggerEl.matches( BuilderPopup.selectors.addNewLesson ) ) {
			type = 'lesson';
		} else if ( triggerEl.dataset.popupType ) {
			type = triggerEl.dataset.popupType;
			id = parseInt( triggerEl.dataset.popupId ) || 0;
		} else if ( triggerEl.dataset.popupLesson !== undefined ) {
			type = 'lesson';
			id = parseInt( triggerEl.dataset.popupLesson ) || 0;
		} else if ( triggerEl.dataset.popupQuiz !== undefined ) {
			type = 'quiz';
			id = parseInt( triggerEl.dataset.popupQuiz ) || 0;
		} else if ( triggerEl.dataset.popupQuestion !== undefined ) {
			type = 'question';
			id = parseInt( triggerEl.dataset.popupQuestion ) || 0;
		}

		if ( ! type ) {
			return;
		}

		this.showPopup( triggerEl, type, id, this.resolveOpenContext( triggerEl ) );
	}

	getDefaultOpenContext() {
		return {
			isCurriculum: false,
			courseId: 0,
		};
	}

	resolveOpenContext( triggerEl ) {
		const isCurriculumContainer =
			!! triggerEl?.closest( '#lp-course-edit-curriculum' ) ||
			!! triggerEl?.closest( '.lp-edit-curriculum-wrap' );
		const courseId = parseInt( triggerEl?.dataset?.courseId ) || 0;
		const isCurriculum = isCurriculumContainer && courseId > 0;

		return {
			isCurriculum,
			courseId,
		};
	}

	showPopup( triggerEl, type, id, openContext = null ) {
		const templateId = triggerEl?.dataset?.template || '';
		const templateEl = document.querySelector( templateId );
		if ( ! templateId || ! templateEl ) {
			return;
		}

		this.currentType = type;
		this.currentId = id;
		this.currentTemplate = templateId;
		this.isNewItem = id === 0;
		this.openContext = openContext ? { ...openContext } : this.getDefaultOpenContext();

		if ( ! this.popupContainer ) {
			return;
		}

		this.popupContainer.innerHTML = templateEl.innerHTML;
		this.popupContainer.classList.add( 'active' );
		document.body.classList.add( 'lp-popup-open' );

		const elLPTarget = this.popupContainer.querySelector( '.lp-target' );
		if ( ! elLPTarget || ! window.lpAJAXG ) {
			return;
		}

		const dataSend = window.lpAJAXG.getDataSetCurrent( elLPTarget );
		dataSend.args = dataSend.args || {};
		dataSend.args[ `${ type }_id` ] = id;
		window.lpAJAXG.setDataSetCurrent( elLPTarget, dataSend );

		this.requestPopupContent( dataSend );
	}

	reloadCurrentPopup() {
		if ( ! this.currentTemplate || ! this.currentType ) {
			return;
		}

		const templateEl = document.querySelector( this.currentTemplate );
		if ( ! templateEl ) {
			return;
		}

		if ( ! this.popupContainer ) {
			return;
		}

		this.popupContainer.innerHTML = templateEl.innerHTML;
		this.popupContainer.classList.add( 'active' );
		document.body.classList.add( 'lp-popup-open' );

		const elLPTarget = this.popupContainer.querySelector( '.lp-target' );
		if ( ! elLPTarget || ! window.lpAJAXG ) {
			return;
		}

		const dataSend = window.lpAJAXG.getDataSetCurrent( elLPTarget );
		dataSend.args = dataSend.args || {};
		dataSend.args[ `${ this.currentType }_id` ] = this.currentId;
		window.lpAJAXG.setDataSetCurrent( elLPTarget, dataSend );

		this.requestPopupContent( dataSend );
	}

	requestPopupContent( dataSend ) {
		if ( ! this.popupContainer || ! window.lpAJAXG ) {
			return;
		}

		const callBack = {
			success: ( response ) => {
				const { status, data } = response;
				if ( status === 'success' && data?.content ) {
					this.popupContainer.innerHTML = data.content;
					this.popupContainer.classList.add( 'active' );
					document.body.classList.add( 'lp-popup-open' );

					this.loadedTabAssets.clear();
					this.initializedTabs.clear(); // Clear initialized tabs cache
					const ajaxElements = this.popupContainer.querySelectorAll(
						'.lp-load-ajax-element.loaded'
					);
					ajaxElements.forEach( ( el ) => el.classList.remove( 'loaded' ) );

					setTimeout( () => window.lpAJAXG.getElements(), 50 );

					const popup = this.popupContainer.querySelector( BuilderPopup.selectors.popup );
					const activeTab = popup?.querySelector( `${ BuilderPopup.selectors.tab }.active` );
					const activeTabName = activeTab?.dataset.tab || 'overview';
					const activePane = popup?.querySelector(
						`${ BuilderPopup.selectors.tabPane }[data-tab="${ activeTabName }"]`
					);

					if ( activePane ) {
						this.loadTabAssets( activeTabName, activePane );
					}

					if ( activeTabName === 'overview' ) {
						setTimeout( () => this.initTinyMCE(), 50 );
					}

					if ( this.currentType === 'quiz' ) {
						if ( ! this.builderEditQuiz ) {
							this.builderEditQuiz = new BuilderEditQuiz();
						}

						if ( activeTabName === 'questions' ) {
							const tabKey = `${ this.currentType }-${ activeTabName }`;
							setTimeout( () => {
								this.triggerAjaxLoadForTab( activePane );
								this.builderEditQuiz.reinit( this.popupContainer );
								this.initializedTabs.set( tabKey, true );
							}, 100 );
						}
					} else if ( this.currentType === 'question' ) {
						if ( ! this.builderEditQuestion ) {
							this.builderEditQuestion = new BuilderEditQuestion();
						}

						if ( activeTabName === 'settings' ) {
							const tabKey = `${ this.currentType }-${ activeTabName }`;
							setTimeout( () => {
								this.triggerAjaxLoadForTab( activePane );
								this.builderEditQuestion.reinit( this.popupContainer );
								this.initializedTabs.set( tabKey, true );
							}, 100 );
						}
					} else if ( this.currentType === 'lesson' ) {
						if ( ! this.builderMaterial ) {
							this.builderMaterial = new BuilderMaterial();
						}

						if ( activeTabName === 'settings' ) {
							const tabKey = `${ this.currentType }-${ activeTabName }`;
							setTimeout( () => {
								this.triggerAjaxLoadForTab( activePane );
								this.builderMaterial.reinit( this.popupContainer );
								this.initializedTabs.set( tabKey, true );
							}, 100 );
						}
					}

					document.dispatchEvent(
						new CustomEvent( 'lp-builder-popup-opened', {
							detail: { type: this.currentType, id: this.currentId, isNew: this.isNewItem },
						} )
					);
				} else {
					lpToastify.show( response.message || 'Failed to load popup', 'error' );
					this.popupContainer.innerHTML = '';
					this.popupContainer.classList.remove( 'active' );
					document.body.classList.remove( 'lp-popup-open' );
				}
			},
			error: ( error ) => {
				lpToastify.show( error.message || 'Failed to load popup', 'error' );
				this.popupContainer.innerHTML = '';
				this.popupContainer.classList.remove( 'active' );
				document.body.classList.remove( 'lp-popup-open' );
			},
			completed: () => {
				// Loading hidden in success/error
			},
		};

		window.lpAJAXG.fetchAJAX( dataSend, callBack );
	}

	/**
	 * Close popup
	 */
	closePopup() {
		const closedType = this.currentType;
		const closedId = this.currentId;
		const savedData = this.savedData;

		this.destroyAllTinyMCE();

		this.popupContainer.innerHTML = '';
		this.popupContainer.classList.remove( 'active' );
		document.body.classList.remove( 'lp-popup-open' );

		this.loadedTabAssets.clear();
		this.initializedTabs.clear(); // Clear initialized tabs cache

		if ( savedData && closedId ) {
			this.updateListItem( closedType, closedId, savedData );
		}

		document.dispatchEvent(
			new CustomEvent( 'lp-builder-popup-closed', {
				detail: { type: closedType, id: closedId, savedData },
			} )
		);

		this.currentType = null;
		this.currentId = null;
		this.currentTemplate = '';
		this.isNewItem = false;
		this.savedData = null;
		this.openContext = this.getDefaultOpenContext();
	}

	/**
	 * Update list item in the background list
	 */
	updateListItem( type, id, savedData ) {
		if ( ! type || ! id || ! savedData ) {
			return;
		}

		const { formData, data, wasNewItem } = savedData;
		let listItems = this.findListItems( type, id );

		// New items created from popup need to be inserted into the list first.
		if ( ( ! listItems || listItems.length === 0 ) && wasNewItem ) {
			const newItem = this.insertNewListItem( type, id, data?.list_item_html );
			if ( newItem ) {
				listItems = [ newItem ];
			}
		}

		if ( ! listItems || listItems.length === 0 ) {
			return;
		}

		listItems.forEach( ( listItem ) => {
			let currentItem = listItem;

			// Replace the entire item HTML if returned from the server
			if ( data?.section_item_html && currentItem.classList.contains( 'section-item' ) ) {
				const template = document.createElement( 'template' );
				template.innerHTML = data.section_item_html.trim();
				const newListItem = template.content.firstElementChild;
				if ( newListItem ) {
					currentItem.replaceWith( newListItem );
					currentItem = newListItem;
				}
			} else if ( data?.list_item_html && ! currentItem.classList.contains( 'section-item' ) ) {
				const template = document.createElement( 'template' );
				template.innerHTML = data.list_item_html.trim();
				const newListItem = template.content.firstElementChild;
				if ( newListItem ) {
					currentItem.replaceWith( newListItem );
					currentItem = newListItem;
				}
			} else {
				// Fallback: manually update elements if HTML replacement isn't used
				// Update title
				const newTitle = formData[ `${ type }_title` ];
				if ( newTitle ) {
					this.updateElementText(
						currentItem,
						[
							'.item-title',
							'.lp-item-title',
							`.lp-${ type }-title`,
							'.curriculum-item-title',
							'.item-name',
							'span.title',
							'.lp-question-title-input',
							'.section-item-title input',
							'.section-item-title span',
							'.lp-item-title-input',
						],
						newTitle
					);
				}

				// Update status
				if ( data?.status ) {
					this.updateElementClass(
						currentItem,
						[ `.${ type }-status`, '.item-status', '.post-status' ],
						data.status
					);
				}

				if ( type === 'lesson' ) {
					const duration = formData._lp_duration || data?.duration;
					if ( duration ) {
						this.updateDuration( currentItem, duration );
					}

					const preview = formData._lp_preview || data?.preview;
					const isPreview = preview === 'yes' || preview === true || preview === '1';
					const previewEl = currentItem.querySelector(
						'.lp-btn-set-preview-item a, .course-item-preview'
					);
					if ( previewEl ) {
						if ( isPreview ) {
							previewEl.classList.remove( 'lp-icon-eye-slash' );
							previewEl.classList.add( 'lp-icon-eye' );
						} else {
							previewEl.classList.remove( 'lp-icon-eye' );
							previewEl.classList.add( 'lp-icon-eye-slash' );
						}
					}

					const checkbox = currentItem.querySelector( 'input[type="checkbox"].preview-checkbox' );
					if ( checkbox ) {
						checkbox.checked = isPreview;
					}

					currentItem.classList.toggle( 'is-preview', isPreview );
					currentItem.classList.toggle( 'preview-item', isPreview );
				} else if ( type === 'quiz' ) {
					const duration = formData._lp_duration || data?.duration;
					if ( duration ) {
						this.updateDuration( currentItem, duration );
					}

					const questionCount = data?.question_count || data?.questions_count;
					if ( questionCount !== null && questionCount !== undefined ) {
						const questionCountEl = currentItem.querySelector( '.question-count' );
						if ( questionCountEl ) {
							questionCountEl.textContent = `${ questionCount } ${
								questionCount === 1 ? 'Question' : 'Questions'
							}`;
						}
					}

					const passingGrade = formData._lp_passing_grade || data?.passing_grade;
					if ( passingGrade ) {
						const passingGradeEl = currentItem.querySelector( '.passing-grade' );
						if ( passingGradeEl ) {
							passingGradeEl.textContent = `${ passingGrade }%`;
						}
					}
				} else if ( type === 'question' ) {
					const questionType = formData._lp_type || data?.type;
					if ( questionType ) {
						const typeMap = {
							true_or_false: 'True or False',
							single_choice: 'Single Choice',
							multi_choice: 'Multi Choice',
							fill_in_blanks: 'Fill in Blanks',
						};

						this.updateElementText(
							currentItem,
							[ '.question-type', '.item-type' ],
							typeMap[ questionType ] || questionType
						);

						const typeClasses = [
							'true_or_false',
							'single_choice',
							'multi_choice',
							'fill_in_blanks',
						];
						typeClasses.forEach( ( cls ) => currentItem.classList.remove( cls ) );
						currentItem.classList.add( questionType );
					}

					const mark = formData._lp_mark || data?.mark;
					if ( mark ) {
						const questionMarkEl = currentItem.querySelector( '.question-mark' );
						if ( questionMarkEl ) {
							questionMarkEl.textContent = mark;
						}
					}
				}
			}
		} );

		document.dispatchEvent(
			new CustomEvent( 'lp-builder-list-item-updated', {
				detail: { type, id, formData, data },
			} )
		);
	}

	/**
	 * Find all instances of a list item by type and ID
	 */
	findListItems( type, id ) {
		const selectors = [
			`[data-${ type }-id="${ id }"]`,
			`[data-id="${ id }"]`,
			`[data-popup-${ type }="${ id }"]`,
			`[data-item-id="${ id }"]`,
			`.section-item[data-item-id="${ id }"]`,
			`.lp-${ type }-item[data-id="${ id }"]`,
		];

		const foundItems = new Set();

		for ( const selector of selectors ) {
			const items = document.querySelectorAll( selector );
			for ( const item of items ) {
				// Ensure we don't select elements inside the popup itself
				if ( ! item.closest( '#lp-builder-popup-container' ) ) {
					// Exclude elements that are merely trigger buttons but not the list item container itself
					// If it's just a generic button to open a popup, it might not be the actual item container.
					// However, some UI lists use the trigger button as the container. We rely on the DOM structure.
					// We can assume `.section-item` and `.lp-lesson-item` and `.cb-list-item` are containers.
					if (
						item.classList.contains( 'section-item' ) ||
						item.classList.contains( `lp-${ type }-item` ) ||
						item.classList.contains( 'list-item' ) ||
						item.classList.contains( 'cb-list-item' ) ||
						item.tagName === 'LI'
					) {
						foundItems.add( item );
					} else {
						// If it's a wrapper, like a div in Content Bank
						if ( item.closest( 'ul' ) ) {
							foundItems.add( item );
						}
					}
				}
			}
		}

		return Array.from( foundItems );
	}

	/**
	 * Insert a newly created list item into the current tab list.
	 */
	insertNewListItem( type, id, listItemHtml ) {
		if ( ! listItemHtml ) {
			return null;
		}

		const existingListItems = this.findListItems( type, id );
		if ( existingListItems && existingListItems.length > 0 ) {
			return existingListItems[ 0 ];
		}

		const listContainer = this.findListContainer( type );
		if ( ! listContainer ) {
			return null;
		}

		const template = document.createElement( 'template' );
		template.innerHTML = listItemHtml.trim();
		const newListItem = template.content.firstElementChild;

		if ( ! newListItem ) {
			return null;
		}

		listContainer.prepend( newListItem );
		const highlightClassByType = {
			lesson: 'highlight-new-lesson',
			quiz: 'highlight-new-quiz',
			question: 'highlight-new-question',
		};
		const highlightClass = highlightClassByType[ type ];

		if ( highlightClass ) {
			newListItem.classList.add( highlightClass );
			newListItem.scrollIntoView( {
				behavior: 'smooth',
				block: 'nearest',
			} );

			setTimeout( () => {
				newListItem.classList.remove( highlightClass );
			}, 1500 );
		}

		const finalListItems = this.findListItems( type, id );
		return finalListItems && finalListItems.length > 0 ? finalListItems[ 0 ] : newListItem;
	}

	/**
	 * Find list container for type; create one if tab currently shows empty message.
	 */
	findListContainer( type ) {
		const listSelectorByType = {
			lesson: '.cb-list-lesson',
			quiz: '.cb-list-quiz',
			question: '.cb-list-question',
		};

		const tabSelectorByType = {
			lesson: '.courses-builder__lesson-tab',
			quiz: '.courses-builder__quiz-tab',
			question: '.courses-builder__question-tab',
		};

		const listClassByType = {
			lesson: 'cb-list-lesson',
			quiz: 'cb-list-quiz',
			question: 'cb-list-question',
		};

		const listSelector =
			listSelectorByType[ type ] || `.cb-list-${ type }, [data-builder-list="${ type }"]`;
		if ( ! listSelector ) {
			return null;
		}

		const existingList = document.querySelector( listSelector );
		if ( existingList ) {
			return existingList;
		}

		const tabContainer = document.querySelector(
			tabSelectorByType[ type ] || `.courses-builder__${ type }-tab, [data-builder-tab="${ type }"]`
		);
		const listClass = listClassByType[ type ] || `cb-list-${ type }`;

		if ( ! tabContainer || ! listClass ) {
			return null;
		}

		const emptyMessage = tabContainer.querySelector( '.learn-press-message' );
		if ( emptyMessage ) {
			emptyMessage.remove();
		}

		const listContainer = document.createElement( 'ul' );
		listContainer.className = listClass;
		tabContainer.appendChild( listContainer );

		return listContainer;
	}

	/**
	 * Update element text (input value or textContent)
	 */
	updateElementText( parent, selectors, newText ) {
		for ( const selector of selectors ) {
			const el = parent.querySelector( selector );
			if ( el ) {
				if ( el.tagName === 'INPUT' ) {
					el.value = newText;
				} else {
					el.textContent = newText;
				}
				return true;
			}
		}
		return false;
	}

	/**
	 * Update element class
	 */
	updateElementClass( parent, selectors, newClass ) {
		for ( const selector of selectors ) {
			const el = parent.querySelector( selector );
			if ( el ) {
				const baseClass = selector.replace( '.', '' );
				el.className = el.className.replace( /\b(publish|draft|pending|trash)\b/g, '' ).trim();
				el.classList.add( baseClass, newClass );
				el.textContent = newClass;
				return true;
			}
		}
		return false;
	}

	/**
	 * Update duration meta
	 */
	updateDuration( listItem, duration ) {
		const durationStr = this.formatDuration( duration );
		const updated = this.updateElementText(
			listItem,
			[ '.item-meta.duration', '.duration', '.course-item-duration', '.meta-duration' ],
			durationStr
		);

		if ( ! updated && durationStr ) {
			const metaContainer = listItem.querySelector(
				'.course-item__right, .item-meta-container, .course-item-meta'
			);
			if ( metaContainer ) {
				let durationEl = metaContainer.querySelector( '.duration' );
				if ( ! durationEl ) {
					durationEl = document.createElement( 'span' );
					durationEl.className = 'duration';
					metaContainer.insertBefore( durationEl, metaContainer.firstChild );
				}
				durationEl.textContent = durationStr;
			}
		}
	}

	/**
	 * Format duration value
	 */
	formatDuration( duration ) {
		if ( ! duration ) {
			return '';
		}

		if ( typeof duration === 'string' && duration.match( /\d+\s+\w+/ ) ) {
			return duration;
		}

		const parts = String( duration ).trim().split( /\s+/ );
		if ( parts.length >= 2 ) {
			const value = parseInt( parts[ 0 ] ) || 0;
			const unit = parts[ 1 ].toLowerCase();

			if ( value === 0 ) {
				return '';
			}

			const unitMap = {
				minute: value === 1 ? 'Minute' : 'Minutes',
				hour: value === 1 ? 'Hour' : 'Hours',
				day: value === 1 ? 'Day' : 'Days',
				week: value === 1 ? 'Week' : 'Weeks',
			};

			return `${ value } ${ unitMap[ unit ] || unit }`;
		}

		const numValue = parseInt( duration ) || 0;
		return numValue > 0 ? `${ numValue } ${ numValue === 1 ? 'Minute' : 'Minutes' }` : '';
	}

	/**
	 * Check if popup is open
	 */
	isPopupOpen() {
		return this.popupContainer?.classList.contains( 'active' );
	}

	/**
	 * Switch tab with dynamic asset loading
	 */
	switchTab( args ) {
		const tabEl = args?.target ? args.target.closest( BuilderPopup.selectors.tab ) : args;
		if ( ! tabEl ) {
			return;
		}

		const tabName = tabEl.dataset.tab;
		const popup = tabEl.closest( BuilderPopup.selectors.popup );

		if ( ! popup || ! tabName ) {
			return;
		}

		// Sync TinyMCE before switching
		this.syncAllTinyMCE();

		// Update tab states
		popup.querySelectorAll( BuilderPopup.selectors.tab ).forEach( ( tab ) => {
			tab.classList.remove( 'active' );
		} );
		tabEl.classList.add( 'active' );

		// Update pane states
		popup.querySelectorAll( BuilderPopup.selectors.tabPane ).forEach( ( pane ) => {
			pane.classList.remove( 'active' );
		} );

		const targetPane = popup.querySelector(
			`${ BuilderPopup.selectors.tabPane }[data-tab="${ tabName }"]`
		);

		if ( ! targetPane ) {
			return;
		}

		targetPane.classList.add( 'active' );
		this.loadTabAssets( tabName, targetPane );
		const tabKey = `${ this.currentType }-${ tabName }`;

		if ( ! this.initializedTabs.has( tabKey ) ) {
			if ( tabName === 'overview' ) {
				setTimeout( () => this.initTinyMCE(), 100 );
				this.initializedTabs.set( tabKey, true );
			} else if ( tabName === 'questions' && this.currentType === 'quiz' ) {
				this.triggerAjaxLoadForTab( targetPane );
				if ( this.builderEditQuiz ) {
					setTimeout( () => {
						this.builderEditQuiz.reinit( this.popupContainer );
						this.initializedTabs.set( tabKey, true );
					}, 100 );
				}
			} else if ( tabName === 'settings' && this.currentType === 'question' ) {
				this.triggerAjaxLoadForTab( targetPane );
				if ( this.builderEditQuestion ) {
					setTimeout( () => {
						this.builderEditQuestion.reinit( this.popupContainer );
						this.initializedTabs.set( tabKey, true );
					}, 100 );
				}
			} else if ( tabName === 'settings' && this.currentType === 'lesson' ) {
				this.triggerAjaxLoadForTab( targetPane );
				if ( this.builderMaterial ) {
					setTimeout( () => {
						this.builderMaterial.reinit( this.popupContainer );
						this.initializedTabs.set( tabKey, true );
					}, 100 );
				}
			}
		}

		document.dispatchEvent(
			new CustomEvent( 'lp-builder-tab-switched', {
				detail: { tabName, type: this.currentType, id: this.currentId },
			} )
		);
	}

	/**
	 * Trigger AJAX loading for tab elements
	 */
	triggerAjaxLoadForTab( tabPane ) {
		if ( ! tabPane || ! window.lpAJAXG ) {
			return;
		}

		const ajaxElements = tabPane.querySelectorAll( '.lp-load-ajax-element:not(.loaded)' );

		if ( ajaxElements.length > 0 ) {
			ajaxElements.forEach( ( el ) => el.classList.remove( 'loaded' ) );
			window.lpAJAXG.getElements();
		}
	}

	/**
	 * Initialize TinyMCE for current popup type
	 */
	initTinyMCE() {
		const editorId = `${ this.currentType }_description_editor`;
		const textarea = document.getElementById( editorId );

		if ( ! textarea || typeof tinymce === 'undefined' ) {
			return;
		}

		this.destroyTinyMCE( editorId );

		if ( typeof wp !== 'undefined' && wp.editor?.initialize ) {
			wp.editor.initialize( editorId, {
				tinymce: {
					wpautop: true,
					content_style:
						"body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Helvetica Neue', sans-serif; font-size: 14px; line-height: 1.6; color: #1e1e1e; }",
					plugins:
						'charmap colorpicker compat3x directionality fullscreen hr image lists media paste tabfocus textcolor wordpress wpautoresize wplink wptextpattern',
					toolbar1:
						'formatselect,bold,italic,underline,bullist,numlist,blockquote,alignleft,aligncenter,alignright,link,unlink,spellchecker,wp_adv',
					toolbar2:
						'strikethrough,hr,forecolor,pastetext,removeformat,charmap,outdent,indent,undo,redo,wp_help',
					wordpress_adv_hidden: true,
				},
				quicktags: { buttons: 'strong,em,link,block,del,ins,img,ul,ol,li,code,more,close' },
				mediaButtons: true,
			} );
		} else {
			tinymce.init( {
				selector: '#' + editorId,
				height: 300,
				menubar: false,
				content_style:
					"body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Helvetica Neue', sans-serif; font-size: 14px; line-height: 1.6; color: #1e1e1e; }",
				plugins: [
					'advlist autolink lists link image charmap print preview anchor',
					'searchreplace visualblocks code fullscreen',
					'insertdatetime media table paste code help wordcount',
				],
				toolbar:
					'undo redo | formatselect | bold italic backcolor | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | removeformat | help',
			} );
		}
	}

	/**
	 * Sync all TinyMCE instances
	 */
	syncAllTinyMCE() {
		if ( typeof tinymce === 'undefined' || ! this.currentType ) {
			return;
		}

		const editorId = `${ this.currentType }_description_editor`;
		const editor = tinymce.get( editorId );

		if ( editor && ! this.isEditorInCodeMode( editorId ) ) {
			editor.save();
		}

		// Sync additional editors
		tinymce.editors.forEach( ( ed ) => {
			if ( ed.id?.includes( this.currentType ) && ! this.isEditorInCodeMode( ed.id ) ) {
				ed.save();
			}
		} );
	}

	isEditorInCodeMode( editorId ) {
		const wrapper =
			document.getElementById( `wp-${ editorId }-wrap` ) ||
			document.getElementById( `${ editorId }-wrap` );
		if ( wrapper?.classList.contains( 'html-active' ) ) {
			return true;
		}

		const editor = typeof tinymce !== 'undefined' ? tinymce.get( editorId ) : null;

		return !! ( editor?.isHidden && editor.isHidden() );
	}

	getEditorContent( editorId, root = document ) {
		const textarea =
			root?.querySelector?.( `#${ editorId }` ) || document.getElementById( editorId );

		if ( this.isEditorInCodeMode( editorId ) ) {
			return textarea ? textarea.value : '';
		}

		if ( typeof tinymce !== 'undefined' ) {
			const editor = tinymce.get( editorId );
			if ( editor ) {
				return editor.getContent();
			}
		}

		return textarea ? textarea.value : '';
	}

	/**
	 * Destroy specific TinyMCE instance
	 */
	destroyTinyMCE( editorId ) {
		if ( typeof tinymce !== 'undefined' ) {
			const editor = tinymce.get( editorId );
			if ( editor ) {
				editor.remove();
			}
		}

		if ( typeof wp !== 'undefined' && wp.editor?.remove ) {
			wp.editor.remove( editorId );
		}
	}

	/**
	 * Destroy all TinyMCE editors in popup
	 */
	destroyAllTinyMCE() {
		if ( ! this.currentType || typeof tinymce === 'undefined' ) {
			return;
		}

		const editorId = `${ this.currentType }_description_editor`;
		this.destroyTinyMCE( editorId );

		const editorsToRemove = [];
		tinymce.editors.forEach( ( ed ) => {
			if ( ed.id && this.popupContainer?.querySelector( `#${ ed.id }` ) ) {
				editorsToRemove.push( ed.id );
			}
		} );
		editorsToRemove.forEach( ( id ) => this.destroyTinyMCE( id ) );
	}

	getStatusFromPublishPanel( fallbackStatus = 'publish' ) {
		if ( ! this.currentType || ! this.popupContainer ) {
			return fallbackStatus;
		}

		const statusSelect = this.popupContainer.querySelector(
			`#cb-${ this.currentType }-publish-status`
		);
		if ( ! statusSelect ) {
			return fallbackStatus;
		}

		const selectedStatus = statusSelect.value;
		if ( selectedStatus === 'publish' || selectedStatus === 'draft' ) {
			return selectedStatus;
		}

		return fallbackStatus;
	}

	syncPublishPanelStatus( status ) {
		if ( ! this.currentType || ! this.popupContainer ) {
			return;
		}

		const statusSelect = this.popupContainer.querySelector(
			`#cb-${ this.currentType }-publish-status`
		);
		if ( ! statusSelect ) {
			return;
		}

		statusSelect.value = status === 'publish' ? 'publish' : 'draft';
	}

	/**
	 * Handle save action
	 */
	handleSave( args ) {
		const saveBtn = args?.target ? args.target.closest( BuilderPopup.selectors.saveBtn ) : args;
		if ( ! saveBtn ) {
			return;
		}

		if ( ! this.currentType ) {
			return;
		}

		const publishLabel = ( saveBtn?.dataset?.titlePublish || '' ).toString().trim().toLowerCase();
		const currentLabel = ( saveBtn?.textContent || '' ).toString().trim().toLowerCase();
		const forcePublish = !! publishLabel && currentLabel === publishLabel;
		const targetStatus = forcePublish ? 'publish' : this.getStatusFromPublishPanel( 'publish' );
		if ( forcePublish ) {
			this.syncPublishPanelStatus( 'publish' );
		}
		this.syncAllTinyMCE();

		const formData = this.getFormData();
		const validation = this.validateFormData( formData );

		if ( ! validation.valid ) {
			lpToastify.show( validation.errors.join( '. ' ), 'error' );
			return;
		}

		lpUtils.lpSetLoadingEl( saveBtn, 1 );

		const actionMap = {
			lesson: 'builder_update_lesson',
			quiz: 'builder_update_quiz',
			question: 'builder_update_question',
		};

		const wasNewItem = this.isNewItem;

		const dataSend = {
			...formData,
			action: actionMap[ this.currentType ] || `builder_update_${ this.currentType }`,
			args: { id_url: `builder-update-${ this.currentType }` },
			[ `${ this.currentType }_status` ]: targetStatus,
			return_html: 'yes',
		};

		const callBack = {
			success: ( response ) => {
				const { status, message, data } = response;

				lpToastify.show( message, status );

				if ( status === 'success' ) {
					this.handleSaveSuccess( data, formData, wasNewItem );
				}
			},
			error: ( error ) => {
				lpToastify.show( error.message || 'Save failed', 'error' );
			},
			completed: () => {
				lpUtils.lpSetLoadingEl( saveBtn, 0 );
			},
		};

		window.lpAJAXG.fetchAJAX( dataSend, callBack );
	}

	/**
	 * Handle save as draft action
	 */
	async handleDraft( args ) {
		const draftBtn = args?.target ? args.target.closest( BuilderPopup.selectors.draftBtn ) : args;
		if ( ! draftBtn ) {
			return;
		}

		if ( ! this.currentType ) {
			return;
		}

		// Check if published to show confirm unpublish modal
		const statusEl = this.popupContainer.querySelector( `.${ this.currentType }-status` );
		const isPublished = statusEl && statusEl.classList.contains( 'publish' );
		if ( isPublished ) {
			const confirmMsg =
				draftBtn.dataset.confirmUnpublish ||
				'Saving as draft will unpublish this item from the course.';
			const result = await SweetAlert.fire( {
				title: 'Are you sure?',
				text: confirmMsg,
				iconHtml: SWAL_ICON_TRASH_DRAFT,
				customClass: { icon: 'lp-cb-swal-icon-html' },
				showCloseButton: true,
				showCancelButton: true,
				cancelButtonText: lpData.i18n.cancel,
				confirmButtonText: lpData.i18n.yes,
				reverseButtons: true,
			} );

			if ( ! result.isConfirmed ) {
				return;
			}
		}

		this.syncAllTinyMCE();

		const formData = this.getFormData();
		const validation = this.validateFormData( formData );

		if ( ! validation.valid ) {
			lpToastify.show( validation.errors.join( '. ' ), 'error' );
			return;
		}

		lpUtils.lpSetLoadingEl( draftBtn, 1 );

		const actionMap = {
			lesson: 'builder_update_lesson',
			quiz: 'builder_update_quiz',
			question: 'builder_update_question',
		};

		const wasNewItem = this.isNewItem;

		const dataSend = {
			...formData,
			action: actionMap[ this.currentType ] || `builder_update_${ this.currentType }`,
			args: { id_url: `builder-update-${ this.currentType }` },
			[ `${ this.currentType }_status` ]: 'draft',
			return_html: 'yes',
		};

		const callBack = {
			success: ( response ) => {
				const { status, message, data } = response;

				lpToastify.show( message, status );

				if ( status === 'success' ) {
					this.handleSaveSuccess( data, formData, wasNewItem );
				}
			},
			error: ( error ) => {
				lpToastify.show( error.message || 'Save draft failed', 'error' );
			},
			completed: () => {
				lpUtils.lpSetLoadingEl( draftBtn, 0 );
			},
		};

		window.lpAJAXG.fetchAJAX( dataSend, callBack );
	}

	/**
	 * Handle save success
	 */
	handleSaveSuccess( data, formData, wasNewItem ) {
		if ( data?.button_title ) {
			const primarySaveBtn = this.popupContainer.querySelector( BuilderPopup.selectors.saveBtn );
			if ( primarySaveBtn ) {
				primarySaveBtn.textContent = data.button_title;
			}
		}

		// Update status
		if ( data?.status ) {
			this.syncPublishPanelStatus( data.status );

			const statusEl = this.popupContainer.querySelector( `.${ this.currentType }-status` );
			if ( statusEl ) {
				statusEl.className = `${ this.currentType }-status ${ data.status }`;
				statusEl.textContent = data.status;
			}

			if ( this.shouldRemoveFromCurriculum( data.status ) ) {
				this.removeItemFromCurriculum( this.currentId );
			}

			if ( this.shouldRemoveQuestionFromAssignedQuiz( data.status ) ) {
				this.removeQuestionFromAssignedQuiz( this.currentId );
			}
		}

		this.updatePermalinkUIAfterSave( data );

		// Handle new item
		const newIdKey = `${ this.currentType }_id_new`;
		if ( data?.[ newIdKey ] ) {
			const newId = data[ newIdKey ];
			this.currentId = newId;
			this.isNewItem = false;

			const wrapper = this.popupContainer.querySelector( `[data-${ this.currentType }-id]` );
			if ( wrapper ) {
				wrapper.dataset[ `${ this.currentType }Id` ] = newId;
			}

			const popup = this.popupContainer.querySelector( BuilderPopup.selectors.popup );
			if ( popup ) {
				popup.dataset[ `${ this.currentType }Id` ] = newId;
			}
		}

		// Store saved data
		this.savedData = { formData, data, wasNewItem };

		// Update the list item immediately
		this.updateListItem( this.currentType, this.currentId, this.savedData );

		// Handle new item creation
		if ( wasNewItem && this.currentId ) {
			document.dispatchEvent(
				new CustomEvent( 'lp-builder-popup-saved', {
					detail: {
						type: this.currentType,
						id: this.currentId,
						data,
						formData,
						wasNewItem,
						listItemHtml: data?.list_item_html || null,
					},
				} )
			);

			// Reload popup to show all tabs
			setTimeout( () => {
				this.destroyAllTinyMCE();
				this.reloadCurrentPopup();
			}, 300 );
		} else {
			document.dispatchEvent(
				new CustomEvent( 'lp-builder-popup-saved', {
					detail: { type: this.currentType, id: this.currentId, data, formData, wasNewItem: false },
				} )
			);
		}
	}

	/**
	 * Handle trash action
	 */
	async handleTrash( args ) {
		const trashBtn = args?.target ? args.target.closest( BuilderPopup.selectors.trashBtn ) : args;
		if ( ! trashBtn ) {
			return;
		}

		if ( ! this.currentType || ! this.currentId ) {
			return;
		}

		const confirmMsg =
			trashBtn.dataset.confirmTrash ||
			'Moving it to the trash will cause this item to be removed from the course.';
		const result = await SweetAlert.fire( {
			title: 'Are you sure?',
			text: confirmMsg,
			iconHtml: SWAL_ICON_TRASH_DRAFT,
			customClass: { icon: 'lp-cb-swal-icon-html' },
			showCloseButton: true,
			showCancelButton: true,
			cancelButtonText: lpData.i18n.cancel,
			confirmButtonText: lpData.i18n.yes,
			reverseButtons: true,
		} );

		if ( ! result.isConfirmed ) {
			return;
		}

		lpUtils.lpSetLoadingEl( trashBtn, 1 );

		const actionMap = {
			lesson: 'move_trash_lesson',
			quiz: 'move_trash_quiz',
			question: 'move_trash_question',
		};

		const dataSend = {
			action: actionMap[ this.currentType ] || `move_trash_${ this.currentType }`,
			args: { id_url: `move-trash-${ this.currentType }` },
			[ `${ this.currentType }_id` ]: this.currentId,
		};
		if (
			!! this.openContext?.isCurriculum &&
			( parseInt( this.openContext?.courseId ) || 0 ) > 0
		) {
			dataSend.course_id = parseInt( this.openContext.courseId ) || 0;
		}

		const callBack = {
			success: ( response ) => {
				const { status, message, data } = response;
				lpToastify.show( message, status );

				if ( status === 'success' ) {
					if ( data?.button_title ) {
						const saveBtn = this.popupContainer.querySelector( BuilderPopup.selectors.saveBtn );
						if ( saveBtn ) {
							saveBtn.textContent = data.button_title;
						}
					}

					if ( data?.status ) {
						const statusEl = this.popupContainer.querySelector( `.${ this.currentType }-status` );
						if ( statusEl ) {
							statusEl.className = `${ this.currentType }-status ${ data.status }`;
							statusEl.textContent = data.status;
						}

						if ( this.shouldRemoveFromCurriculum( data.status ) ) {
							this.removeItemFromCurriculum( this.currentId );
						}

						if ( this.shouldRemoveQuestionFromAssignedQuiz( data.status ) ) {
							this.removeQuestionFromAssignedQuiz( this.currentId );
						}
					}

					this.updatePermalinkUIAfterSave( data );

					this.savedData = { formData: this.getFormData(), data, wasNewItem: false };

					document.dispatchEvent(
						new CustomEvent( 'lp-builder-popup-trashed', {
							detail: { type: this.currentType, id: this.currentId, data },
						} )
					);
				}
			},
			error: ( error ) => {
				lpToastify.show( error.message || 'Trash failed', 'error' );
			},
			completed: () => {
				lpUtils.lpSetLoadingEl( trashBtn, 0 );
			},
		};

		window.lpAJAXG.fetchAJAX( dataSend, callBack );
	}

	shouldRemoveFromCurriculum( status ) {
		const normalizedStatus = ( status || '' ).toString().toLowerCase();
		const removableStatuses = [ 'draft', 'trash' ];

		return (
			!! this.openContext?.isCurriculum &&
			removableStatuses.includes( normalizedStatus ) &&
			( parseInt( this.currentId ) || 0 ) > 0
		);
	}

	shouldRemoveQuestionFromAssignedQuiz( status ) {
		const normalizedStatus = ( status || '' ).toString().toLowerCase();
		return (
			this.currentType === 'question' &&
			[ 'draft', 'trash' ].includes( normalizedStatus ) &&
			( parseInt( this.currentId ) || 0 ) > 0
		);
	}

	removeItemFromCurriculum( itemId ) {
		const parsedItemId = parseInt( itemId ) || 0;
		if ( parsedItemId <= 0 ) {
			return;
		}

		const curriculumRoot =
			document.querySelector( '#lp-course-edit-curriculum' ) ||
			document.querySelector( '.lp-edit-curriculum-wrap' );
		if ( ! curriculumRoot ) {
			return;
		}

		const items = curriculumRoot.querySelectorAll(
			`.section-item[data-item-id="${ parsedItemId }"]`
		);
		if ( ! items.length ) {
			return;
		}

		const sectionsToUpdate = new Set();

		items.forEach( ( item ) => {
			const section = item.closest( '.section' );
			if ( section ) {
				sectionsToUpdate.add( section );
			}

			item.remove();
		} );

		this.syncCurriculumCounters( curriculumRoot, sectionsToUpdate );
	}

	removeQuestionFromAssignedQuiz( questionId ) {
		const parsedQuestionId = parseInt( questionId ) || 0;
		if ( parsedQuestionId <= 0 ) {
			return;
		}

		const questionItems = document.querySelectorAll(
			`.lp-question-item[data-question-id="${ parsedQuestionId }"]`
		);

		questionItems.forEach( ( item ) => item.remove() );
	}

	syncCurriculumCounters( curriculumRoot, sectionsToUpdate = new Set() ) {
		if ( ! curriculumRoot ) {
			return;
		}

		const allItems = curriculumRoot.querySelectorAll( '.section-item:not(.clone)' );
		const totalItemsCount = allItems.length;
		const totalItemsEl = curriculumRoot.querySelector( '.total-items' );

		if ( totalItemsEl ) {
			totalItemsEl.dataset.count = totalItemsCount;

			const totalItemsCountEl = totalItemsEl.querySelector( '.count' );
			if ( totalItemsCountEl ) {
				totalItemsCountEl.textContent = totalItemsCount;
			}
		}

		const sections =
			sectionsToUpdate.size > 0
				? Array.from( sectionsToUpdate )
				: Array.from( curriculumRoot.querySelectorAll( '.section' ) );

		sections.forEach( ( section ) => {
			const sectionItemsCountEl = section.querySelector( '.section-items-counts' );
			if ( ! sectionItemsCountEl ) {
				return;
			}

			const sectionItemsCount = section.querySelectorAll( '.section-item:not(.clone)' ).length;
			sectionItemsCountEl.dataset.count = sectionItemsCount;

			const countEl = sectionItemsCountEl.querySelector( '.count' );
			if ( countEl ) {
				countEl.textContent = sectionItemsCount;
			}
		} );
	}

	/**
	 * Validate form data
	 */
	validateFormData( formData ) {
		const errors = [];
		const titleKey = `${ this.currentType }_title`;
		const title = formData[ titleKey ] || '';

		if ( ! title.trim() ) {
			errors.push(
				`${
					this.currentType.charAt( 0 ).toUpperCase() + this.currentType.slice( 1 )
				} title is required`
			);
		}

		if ( title.length > 200 ) {
			errors.push( 'Title must be less than 200 characters' );
		}

		return { valid: errors.length === 0, errors };
	}

	/**
	 * Get form data from popup
	 */
	getFormData() {
		const data = {};
		const popup = this.popupContainer.querySelector( BuilderPopup.selectors.popup );

		if ( ! popup ) {
			return data;
		}

		const idKey = `${ this.currentType }_id`;
		data[ idKey ] = this.currentId || 0;
		if (
			!! this.openContext?.isCurriculum &&
			( parseInt( this.openContext?.courseId ) || 0 ) > 0
		) {
			data.course_id = parseInt( this.openContext.courseId ) || 0;
		}

		// Get title
		const titleInput = popup.querySelector(
			'input[name$="_title"], #title, #' + this.currentType + '_title'
		);
		if ( titleInput ) {
			data[ `${ this.currentType }_title` ] = titleInput.value;
		}

		// Get description
		const editorId = `${ this.currentType }_description_editor`;
		const descContent = this.getEditorContent( editorId, popup );

		data[ `${ this.currentType }_description` ] = descContent;

		// Get form settings
		const formSettings = popup.querySelector( `.lp-form-setting-${ this.currentType }` );
		if ( formSettings ) {
			data[ `${ this.currentType }_settings` ] = true;
			this.collectFormData( formSettings, data );
		}

		// Capture permalink slug in overview tab (quiz/question popup).
		const permalinkInput = popup.querySelector(
			`input[name="${ this.currentType }_permalink"], #${ this.currentType }_permalink, ${ BuilderPopup.selectors.permalinkSlugInput }`
		);
		if ( permalinkInput && permalinkInput.value ) {
			data[ `${ this.currentType }_permalink` ] = permalinkInput.value;
		}

		return data;
	}

	updatePermalinkUIAfterSave( data = {} ) {
		if ( ! this.currentType || ! this.popupContainer ) {
			return;
		}

		const popup = this.popupContainer.querySelector( BuilderPopup.selectors.popup );
		if ( ! popup ) {
			return;
		}

		const slugInput = popup.querySelector(
			`input[name="${ this.currentType }_permalink"], #${ this.currentType }_permalink, ${ BuilderPopup.selectors.permalinkSlugInput }`
		);
		const permalinkRoot = popup.querySelector( BuilderPopup.selectors.permalinkRoot );
		const permalinkPlaceholder = permalinkRoot?.querySelector(
			BuilderPopup.selectors.permalinkPlaceholder
		);

		const responseSlug = data?.[ `${ this.currentType }_slug` ];
		if ( slugInput && responseSlug ) {
			slugInput.value = responseSlug;
			slugInput.dataset.originalValue = responseSlug;
		}

		const responsePermalink = data?.[ `${ this.currentType }_permalink` ];
		const isCourseItem = [ 'lesson', 'quiz' ].includes( this.currentType );
		const shouldShowUnavailable =
			data?.permalink_available === false ||
			( isCourseItem &&
				( data?.status === 'draft' || data?.status === 'trash' || ! responsePermalink ) );

		if ( shouldShowUnavailable ) {
			if ( ! permalinkRoot ) {
				return;
			}

			const permalinkDisplay = permalinkRoot.querySelector(
				BuilderPopup.selectors.permalinkDisplay
			);
			const label =
				permalinkRoot.querySelector( '.cb-item-edit-permalink__label' ) ||
				permalinkRoot.querySelector( '.cb-permalink-label' );
			const editor = permalinkRoot.querySelector( BuilderPopup.selectors.permalinkEditor );
			let placeholder = permalinkPlaceholder;

			if ( ! placeholder ) {
				placeholder = document.createElement( 'span' );
				placeholder.className = 'cb-item-edit-permalink__placeholder';

				if ( label ) {
					label.insertAdjacentElement( 'afterend', placeholder );
				} else {
					permalinkRoot.prepend( placeholder );
				}
			}

			placeholder.textContent =
				data?.permalink_notice ||
				'Permalink is only available if the item is already assigned to a course.';
			placeholder.classList.remove( 'lp-hidden' );

			if ( permalinkDisplay ) {
				permalinkDisplay.classList.add( 'lp-hidden' );
			}

			if ( editor ) {
				editor.classList.add( 'lp-hidden' );
			}

			return;
		}

		const urlLink = popup.querySelector( BuilderPopup.selectors.permalinkUrl );
		const permalinkDisplay = permalinkRoot?.querySelector(
			BuilderPopup.selectors.permalinkDisplay
		);
		const baseUrlInput = popup.querySelector( BuilderPopup.selectors.permalinkBaseUrl );
		const normalizedBaseUrl = typeof baseUrlInput?.value === 'string' ? baseUrlInput.value : '';
		const normalizedSlug = typeof responseSlug === 'string' ? responseSlug.trim() : '';
		let permalinkDisplayUrl = '';

		if ( normalizedBaseUrl && normalizedSlug ) {
			permalinkDisplayUrl = `${ normalizedBaseUrl }${ normalizedSlug }`;
		} else if ( typeof responsePermalink === 'string' ) {
			permalinkDisplayUrl = responsePermalink;
		}

		if ( permalinkPlaceholder ) {
			permalinkPlaceholder.classList.add( 'lp-hidden' );
		}

		if ( permalinkDisplay ) {
			permalinkDisplay.classList.remove( 'lp-hidden' );
		}

		if ( urlLink && responsePermalink ) {
			urlLink.href = responsePermalink;
			urlLink.textContent = permalinkDisplayUrl || responsePermalink;
		} else if ( urlLink && permalinkDisplayUrl ) {
			urlLink.textContent = permalinkDisplayUrl;
		}
	}

	/**
	 * Collect form data from form element
	 */
	collectFormData( form, data ) {
		const formElements = form.querySelectorAll( 'input, select, textarea' );

		formElements.forEach( ( element ) => {
			const name = element.name || element.id;

			if ( ! name || name === 'learnpress_meta_box_nonce' || name === '_wp_http_referer' ) {
				return;
			}

			const fieldName = name.replace( '[]', '' );

			if ( element.type === 'checkbox' ) {
				if ( ! data.hasOwnProperty( fieldName ) ) {
					data[ fieldName ] = element.checked ? 'yes' : 'no';
				}
			} else if ( element.type === 'radio' ) {
				if ( element.checked ) {
					data[ fieldName ] = element.value;
				}
			} else if ( element.type === 'file' ) {
				if ( element.files?.length > 0 ) {
					data[ fieldName ] = element.files;
				}
			} else if ( name.endsWith( '[]' ) ) {
				if ( ! data.hasOwnProperty( fieldName ) ) {
					data[ fieldName ] = [];
				}
				if ( Array.isArray( data[ fieldName ] ) ) {
					data[ fieldName ].push( element.value );
				}
			} else if ( ! data.hasOwnProperty( fieldName ) ) {
				data[ fieldName ] = element.value;
			}
		} );

		// Convert arrays to comma-separated strings
		Object.keys( data ).forEach( ( key ) => {
			if ( Array.isArray( data[ key ] ) ) {
				data[ key ] = data[ key ].join( ',' );
			}
		} );
	}

	/**
	 * Load tab-specific assets (CSS/JS)
	 */
	loadTabAssets( tabName, tabPane ) {
		const tabKey = `${ this.currentType }-${ tabName }`;

		if ( this.loadedTabAssets.has( tabKey ) ) {
			return;
		}

		const assetsData = tabPane.dataset.tabAssets;
		if ( ! assetsData ) {
			this.loadedTabAssets.add( tabKey );
			return;
		}

		try {
			const assets = JSON.parse( assetsData );

			// Load CSS
			if ( assets.css && Array.isArray( assets.css ) ) {
				assets.css.forEach( ( cssUrl ) => {
					if ( ! document.querySelector( `link[href="${ cssUrl }"]` ) ) {
						const link = document.createElement( 'link' );
						link.rel = 'stylesheet';
						link.href = cssUrl;
						link.dataset.tabAsset = tabKey;
						document.head.appendChild( link );
					}
				} );
			}

			// Load JS
			if ( assets.js && Array.isArray( assets.js ) ) {
				assets.js.forEach( ( jsUrl ) => {
					if ( ! document.querySelector( `script[src="${ jsUrl }"]` ) ) {
						const script = document.createElement( 'script' );
						script.src = jsUrl;
						script.dataset.tabAsset = tabKey;
						document.head.appendChild( script );
					}
				} );
			}

			this.loadedTabAssets.add( tabKey );
		} catch ( e ) {
			console.warn( `Failed to load assets for tab "${ tabName }":`, e );
			this.loadedTabAssets.add( tabKey );
		}
	}

	/**
	 * Static method to open popup programmatically
	 */
	static open( type, id = 0 ) {
		if ( ! BuilderPopup._instance ) {
			BuilderPopup._instance = new BuilderPopup();
		}

		const selectors = {
			lesson: id ? `[data-popup-lesson="${ id }"]` : BuilderPopup.selectors.addNewLesson,
			quiz: id ? `[data-popup-quiz="${ id }"]` : '',
			question: id ? `[data-popup-question="${ id }"]` : '',
		};
		const triggerSelector =
			selectors[ type ] ||
			( id
				? `[data-popup-type="${ type }"][data-popup-id="${ id }"]`
				: `[data-popup-type="${ type }"][data-template]` );
		if ( ! triggerSelector ) {
			return;
		}

		const triggerEl = document.querySelector( triggerSelector );
		if ( ! triggerEl ) {
			return;
		}

		BuilderPopup._instance.showPopup(
			triggerEl,
			type,
			id,
			BuilderPopup._instance.resolveOpenContext( triggerEl )
		);
	}

	/**
	 * Static method to close popup programmatically
	 */
	static close() {
		if ( BuilderPopup._instance ) {
			BuilderPopup._instance.closePopup();
		}
	}
}

// Auto-initialize
document.addEventListener( 'DOMContentLoaded', () => {
	BuilderPopup._instance = new BuilderPopup();
} );

export default BuilderPopup;

```
