/** * Site Preloader Admin Script. * * Handles admin UI interactions, live preview, color pickers, media uploaders, and AJAX operations. * Premium style inspired smooth interactions. * * @package King_Addons * @since 1.0.0 */ (function ($) { 'use strict'; /** * Admin Controller */ const KngPreloaderAdmin = { /** * Cached elements */ elements: { form: null, preview: null, previewFrame: null, previewContent: null, colorPickers: null, mediaButtons: null, tabLinks: null, rulesList: null }, /** * Current settings state */ settings: {}, /** * Initialize admin */ init: function () { this.cacheElements(); this.initColorPickers(); this.initMediaUploaders(); this.initTabs(); this.initPresetSelection(); this.initLivePreview(); this.initRulesManager(); this.initImportExport(); this.initDarkModeToggle(); this.initFormSubmit(); this.initPreviewModal(); }, /** * Cache DOM elements */ cacheElements: function () { this.elements.form = $('#ka-preloader-settings-form'); if (!this.elements.form.length) { this.elements.form = $('#kng-preloader-settings-form'); } this.elements.preview = $('.ka-preloader-live-preview'); this.elements.previewFrame = $('.ka-preloader-preview-frame'); this.elements.previewContent = $('.ka-preloader-preview-content'); this.elements.colorPickers = $('.ka-preloader-color-input, .ka-color-picker, .ka-preloader-wp-color-picker'); this.elements.mediaButtons = $('.ka-preloader-media-button'); this.elements.tabLinks = $('.ka-preloader-tab-link'); this.elements.rulesList = $('.ka-preloader-rules-list'); }, /** * Initialize color pickers */ initColorPickers: function () { // Legacy custom preview inputs (if any exist) $('.ka-preloader-color-input').each(function () { const $input = $(this); const $preview = $input.siblings('.ka-preloader-color-preview'); $input.off('input.kngPreloader change.kngPreloader'); $input.on('input.kngPreloader change.kngPreloader', function () { const color = $(this).val(); $preview.css('background-color', color); KngPreloaderAdmin.updatePreview(); }); $preview.css('background-color', $input.val()); }); // Alpha-enabled WordPress color picker if ($.fn.wpColorPicker) { const $pickers = $('.ka-color-picker, .ka-preloader-wp-color-picker'); $pickers.each(function () { const $input = $(this); if ($input.hasClass('wp-color-picker')) { return; } $input.wpColorPicker({ defaultColor: $input.data('default-color'), change: function () { KngPreloaderAdmin.updatePreview(); }, clear: function () { KngPreloaderAdmin.updatePreview(); } }); }); } }, /** * Initialize media uploaders */ initMediaUploaders: function () { this.elements.mediaButtons.on('click', function (e) { e.preventDefault(); const $button = $(this); const $container = $button.closest('.ka-preloader-media-field'); const $input = $container.find('input[type="hidden"]'); const $preview = $container.find('.ka-preloader-media-preview'); const $removeBtn = $container.find('.ka-preloader-media-remove'); // Create media frame const frame = wp.media({ title: $button.data('title') || 'Select Image', button: { text: $button.data('button-text') || 'Use Image' }, multiple: false }); // Handle selection frame.on('select', function () { const attachment = frame.state().get('selection').first().toJSON(); $input.val(attachment.id); let imgUrl = attachment.url; if (attachment.sizes && attachment.sizes.thumbnail) { imgUrl = attachment.sizes.thumbnail.url; } $preview.html('').show(); $removeBtn.show(); $button.text('Change Image'); KngPreloaderAdmin.updatePreview(); }); frame.open(); }); // Handle remove button $('.ka-preloader-media-remove').on('click', function (e) { e.preventDefault(); const $button = $(this); const $container = $button.closest('.ka-preloader-media-field'); const $input = $container.find('input[type="hidden"]'); const $preview = $container.find('.ka-preloader-media-preview'); const $uploadBtn = $container.find('.ka-preloader-media-button'); $input.val(''); $preview.empty().hide(); $button.hide(); $uploadBtn.text('Upload Image'); KngPreloaderAdmin.updatePreview(); }); }, /** * Initialize tabs */ initTabs: function () { this.elements.tabLinks.on('click', function (e) { e.preventDefault(); const $link = $(this); const tab = $link.data('tab'); // Update URL without reload const url = new URL(window.location); url.searchParams.set('tab', tab); window.history.replaceState({}, '', url); // Update active states KngPreloaderAdmin.elements.tabLinks.removeClass('active'); $link.addClass('active'); // Show target panel $('.ka-preloader-tab-panel').removeClass('active'); $('#ka-preloader-tab-' + tab).addClass('active'); }); }, /** * Initialize preset selection */ initPresetSelection: function () { $(document).on('click', '.ka-preloader-preset-card', function () { const $card = $(this); const presetId = $card.data('preset'); // Update active state $('.ka-preloader-preset-card').removeClass('active'); $card.addClass('active'); // Update hidden input $('input[name="template"], input[name="preset"]').val(presetId); // Update preview KngPreloaderAdmin.updatePreview(); }); }, /** * Initialize live preview */ initLivePreview: function () { // Collect all form inputs const $formInputs = this.elements.form.length ? this.elements.form.find('input, select, textarea') : $('input, select, textarea'); // Update preview on input change $formInputs.on('input change', function () { KngPreloaderAdmin.debounce(KngPreloaderAdmin.updatePreview, 150)(); }); // Initial preview render this.updatePreview(); }, /** * Update live preview */ updatePreview: function () { if (!this.elements.previewContent.length) { return; } // Collect current settings const settings = this.collectFormSettings(); const bgColor = settings.bg_color || settings.background_color || 'rgba(0,0,0,0)'; const presetId = settings.template || settings.preset || 'spinner-circle'; const sizePx = (settings.spinner_size || settings.animation_size || 48) + 'px'; // Update preview CSS variables const $frame = this.elements.previewFrame; $frame.css({ '--kng-preloader-accent': settings.accent_color || '#0071e3', '--kng-preloader-bg': bgColor, '--kng-preloader-text': settings.text_color || '#1d1d1f', '--kng-preloader-size': sizePx }); // Update background overlay const $overlay = this.elements.previewContent.find('.ka-preloader-preview-overlay'); if ($overlay.length) { $overlay.css('background-color', bgColor); } // Update preset animation this.updatePresetAnimation(presetId); // Update text const $text = this.elements.previewContent.find('.ka-preloader-preview-text'); if (settings.text_content) { $text.text(settings.text_content).show(); } else { $text.hide(); } }, /** * Update preset animation in preview */ updatePresetAnimation: function (presetId) { const $animation = this.elements.previewContent.find('.ka-preloader-preview-animation'); if (!$animation.length) { return; } // Generate preset HTML const presetHtml = this.getPresetHtml(presetId); $animation.html(presetHtml); }, /** * Get preset HTML */ getPresetHtml: function (presetId) { const presets = { 'spinner-circle': '
', 'dual-ring': '
', 'dots-bounce': '
', 'bar-loader': '
', 'pulse-logo': '', 'minimal-line': '
', 'gradient-spinner': '
', 'fade-text': '
Loading
', 'cube-grid': '
' + '
'.repeat(9) + '
', 'wave-bars': '
' + '
'.repeat(5) + '
', 'rotating-squares': '
', 'morphing-circle': '
' }; return presets[presetId] || presets['spinner-circle']; }, /** * Collect form settings */ collectFormSettings: function () { const settings = {}; this.elements.form.find('input, select, textarea').each(function () { const $field = $(this); const name = $field.attr('name'); if (!name) return; if ($field.is(':checkbox')) { settings[name] = $field.is(':checked') ? '1' : '0'; } else if ($field.is(':radio')) { if ($field.is(':checked')) { settings[name] = $field.val(); } } else { settings[name] = $field.val(); } }); return settings; }, /** * Initialize rules manager */ initRulesManager: function () { // Add rule button - support both class and ID $(document).on('click', '#ka-add-rule-btn, .ka-preloader-add-rule', function (e) { e.preventDefault(); KngPreloaderAdmin.addNewRule(); }); // Remove rule button (both classes) - use deleteRule with AJAX $(document).on('click', '.ka-preloader-rule-remove, .ka-rule-delete-btn', function (e) { e.preventDefault(); e.stopPropagation(); if (!confirm(kngPreloaderAdmin.strings.confirmDelete || 'Are you sure you want to delete this rule?')) { return; } const $rule = $(this).closest('.ka-preloader-rule-item'); KngPreloaderAdmin.deleteRule($rule); }); // Edit rule button - toggle details $(document).on('click', '.ka-rule-edit-btn', function (e) { e.preventDefault(); e.stopPropagation(); const $rule = $(this).closest('.ka-preloader-rule-item'); const $details = $rule.find('.ka-preloader-rule-item__details'); $details.slideToggle(200); }); // Save rule button $(document).on('click', '.ka-rule-save-btn', function (e) { e.preventDefault(); const $rule = $(this).closest('.ka-preloader-rule-item'); KngPreloaderAdmin.saveRule($rule); }); // Cancel rule button $(document).on('click', '.ka-rule-cancel-btn', function (e) { e.preventDefault(); const $rule = $(this).closest('.ka-preloader-rule-item'); KngPreloaderAdmin.cancelRuleEdit($rule); }); // Action buttons (Show/Hide/Override) - both old and new class names $(document).on('click', '.ka-rule-action-btn, .ka-rule-action-btns .ka-btn', function (e) { e.preventDefault(); const $btn = $(this); const $rule = $btn.closest('.ka-preloader-rule-item'); const action = $btn.data('action'); // Update active state $btn.siblings().removeClass('active'); $btn.addClass('active'); // Store in hidden input if exists $rule.find('input.ka-rule-action').val(action); // Show/hide template row (only for override) const $templateRow = $rule.find('.ka-rule-template-row'); const $colorsToggleRow = $rule.find('.ka-rule-colors-toggle-row'); const $colorsRow = $rule.find('.ka-rule-colors-row'); if (action === 'override') { $templateRow.slideDown(200); $colorsToggleRow.slideDown(200); if ($rule.find('.ka-rule-override-colors').is(':checked')) { $colorsRow.slideDown(200); } } else { $templateRow.slideUp(200); $colorsToggleRow.slideUp(200); $colorsRow.slideUp(200); } // Update header badge const actionLabels = { 'show': 'Show', 'hide': 'Hide', 'override': 'Override' }; const actionIcons = { 'show': 'visibility', 'hide': 'hidden', 'override': 'admin-customizer' }; $rule.find('.ka-preloader-rule-item__summary .ka-rule-action') .removeClass('ka-rule-action--show ka-rule-action--hide ka-rule-action--override') .addClass('ka-rule-action--' + action) .html(' ' + actionLabels[action]); }); // Toggle rule expansion (header click) $(document).on('click', '.ka-preloader-rule-item__header', function (e) { // Don't toggle if clicking buttons if ($(e.target).closest('button, .ka-toggle, input').length) { return; } const $rule = $(this).closest('.ka-preloader-rule-item'); const $details = $rule.find('.ka-preloader-rule-item__details'); $details.slideToggle(200); }); // Toggle rule expansion (old class for JS-generated rules) $(document).on('click', '.ka-preloader-rule-header', function (e) { if ($(e.target).closest('button').length) { return; } const $rule = $(this).closest('.ka-preloader-rule-item'); $rule.toggleClass('expanded'); }); // Rule condition change - show/hide appropriate fields $(document).on('change', '.ka-rule-condition', function () { const $rule = $(this).closest('.ka-preloader-rule-item'); const condition = $(this).val(); KngPreloaderAdmin.updateConditionFields($rule, condition); }); // Also support old class names $(document).on('change', '.ka-preloader-rule-condition, .ka-rule-condition-type', function () { const $rule = $(this).closest('.ka-preloader-rule-item'); const condition = $(this).val(); KngPreloaderAdmin.updateRuleConditions($rule, condition); }); // Override colors toggle $(document).on('change', '.ka-rule-override-colors', function () { const $rule = $(this).closest('.ka-preloader-rule-item'); const $colorRows = $rule.find('.ka-rule-colors-row, .ka-rule-colors-grid'); if ($(this).is(':checked')) { $colorRows.slideDown(200); } else { $colorRows.slideUp(200); } }); // Rule type change $(document).on('change', '.ka-preloader-rule-type', function () { const $rule = $(this).closest('.ka-preloader-rule-item'); const type = $(this).val(); KngPreloaderAdmin.updateRuleConditions($rule, type); }); // Initialize condition fields on page load $('.ka-preloader-rule-item').each(function() { const $rule = $(this); const condition = $rule.find('.ka-rule-condition').val(); if (condition) { KngPreloaderAdmin.updateConditionFields($rule, condition); } }); }, /** * Update condition fields visibility based on selected condition */ updateConditionFields: function ($rule, condition) { const $pagesRow = $rule.find('.ka-rule-pages-row'); const $posttypeRow = $rule.find('.ka-rule-posttype-row'); const $urlRow = $rule.find('.ka-rule-url-row'); // Hide all conditional rows first $pagesRow.hide(); $posttypeRow.hide(); $urlRow.hide(); // Show appropriate row based on condition switch (condition) { case 'specific_pages': $pagesRow.slideDown(200); break; case 'post_type': $posttypeRow.slideDown(200); break; case 'url_contains': case 'url_equals': $urlRow.slideDown(200); break; // Other conditions don't need additional fields } }, /** * Save rule via AJAX */ saveRule: function ($rule) { const $saveBtn = $rule.find('.ka-rule-save-btn'); const originalText = $saveBtn.text(); // Get action from the active button or hidden input let action = 'show'; const $activeAction = $rule.find('.ka-rule-action-btn.active, .ka-rule-action-btns .ka-btn.active'); if ($activeAction.length) { action = $activeAction.data('action'); } else { const $actionInput = $rule.find('input.ka-rule-action'); if ($actionInput.length && $actionInput.val()) { action = $actionInput.val(); } } // Get condition const condition = $rule.find('.ka-rule-condition').val() || 'specific_pages'; // Get pages as array const pages = []; $rule.find('.ka-rule-pages option:selected').each(function() { pages.push($(this).val()); }); // Collect rule data with new structure const ruleData = { action: 'king_addons_preloader_save_rule', nonce: kngPreloaderAdmin.nonce, rule_id: $rule.attr('data-rule-id') || '', enabled: $rule.find('.ka-rule-enabled').is(':checked') ? '1' : '0', priority: $rule.find('.ka-rule-priority').val() || 10, rule_action: action, // show / hide / override condition: condition, condition_value: $rule.find('.ka-rule-condition-value, .ka-rule-url-value, .ka-rule-posttype').val() || '', pages: pages, template: $rule.find('.ka-rule-template').val() || '', override_colors: $rule.find('.ka-rule-override-colors').is(':checked') ? '1' : '0', bg_color: $rule.find('.ka-rule-bg-color').val() || '#ffffff', accent_color: $rule.find('.ka-rule-accent-color').val() || '#0071e3' }; // Show saving state $saveBtn.text('Saving...').prop('disabled', true); console.log('Saving rule:', ruleData); // Send AJAX request $.ajax({ url: kngPreloaderAdmin.ajaxUrl, type: 'POST', data: ruleData, success: function (response) { console.log('Save response:', response); if (response.success) { // Update rule ID if it was a new rule if (response.data.rule && response.data.rule.id) { $rule.attr('data-rule-id', response.data.rule.id); } // Update header display const actionLabels = { 'show': 'Show', 'hide': 'Hide', 'override': 'Override' }; const conditionLabels = { 'specific_pages': 'Specific Pages', 'all_pages': 'All Pages', 'front_page': 'Front Page', 'blog_page': 'Blog Page', 'all_posts': 'All Posts', 'post_type': 'Post Type', 'archive': 'Archive', 'search': 'Search', '404': '404 Page', 'url_contains': 'URL Contains', 'url_equals': 'URL Equals' }; $rule.find('.ka-preloader-rule-item__action').text(actionLabels[action] || action); $rule.find('.ka-preloader-rule-item__type').text(conditionLabels[condition] || condition); // Show pages count for specific_pages if (condition === 'specific_pages' && pages.length > 0) { $rule.find('.ka-preloader-rule-item__value').text(pages.length + ' page(s)'); } else { $rule.find('.ka-preloader-rule-item__value').text(ruleData.condition_value || ''); } // Hide details $rule.find('.ka-preloader-rule-item__details').slideUp(200); // Show success $saveBtn.text('Saved!'); setTimeout(() => { $saveBtn.text(originalText).prop('disabled', false); }, 1500); } else { alert(response.data?.message || kngPreloaderAdmin.strings.error); $saveBtn.text(originalText).prop('disabled', false); } }, error: function () { alert(kngPreloaderAdmin.strings.error); $saveBtn.text(originalText).prop('disabled', false); } }); }, /** * Delete rule via AJAX */ deleteRule: function ($rule) { const ruleId = $rule.attr('data-rule-id'); // If it's a new unsaved rule, just remove from DOM if (!ruleId || ruleId.startsWith('new_')) { $rule.slideUp(200, function () { $(this).remove(); KngPreloaderAdmin.checkEmptyRules(); }); return; } // Send AJAX request $.ajax({ url: kngPreloaderAdmin.ajaxUrl, type: 'POST', data: { action: 'king_addons_preloader_delete_rule', nonce: kngPreloaderAdmin.nonce, rule_id: ruleId }, success: function (response) { if (response.success) { $rule.slideUp(200, function () { $(this).remove(); KngPreloaderAdmin.checkEmptyRules(); KngPreloaderAdmin.updateRuleIndices(); }); } else { alert(response.data?.message || kngPreloaderAdmin.strings.error); } }, error: function () { alert(kngPreloaderAdmin.strings.error); } }); }, /** * Cancel rule edit */ cancelRuleEdit: function ($rule) { const ruleId = $rule.attr('data-rule-id'); // If it's a new unsaved rule, remove it if (ruleId && ruleId.startsWith('new_')) { $rule.slideUp(200, function () { $(this).remove(); KngPreloaderAdmin.checkEmptyRules(); }); } else { // Just hide details for existing rules $rule.find('.ka-preloader-rule-item__details').slideUp(200); } }, /** * Check if rules list is empty and show empty state */ checkEmptyRules: function () { const $rulesList = $('#ka-rules-list'); const rulesCount = $rulesList.find('.ka-preloader-rule-item').length; if (rulesCount === 0) { $('#ka-rules-empty').show(); } }, /** * Add new rule */ addNewRule: function () { // Get rules list, try ID first then class let $rulesList = $('#ka-rules-list'); if (!$rulesList.length) { $rulesList = this.elements.rulesList; } if (!$rulesList.length) { console.error('Rules list container not found'); return; } // Hide empty state $('#ka-rules-empty').hide(); // Try to use template from PHP first const $template = $('#ka-rule-template'); let $newRule; if ($template.length) { // Clone template content $newRule = $($template.html()).clone(); $newRule.attr('data-rule-id', 'new_' + Date.now()); $newRule.find('.ka-preloader-rule-item__details').show(); } else { // Fallback to JS-generated HTML const ruleIndex = $rulesList.find('.ka-preloader-rule-item').length; $newRule = $(`
New Rule
`); } $rulesList.append($newRule); // Initialize WP color picker on newly added rule (alpha-enabled) if ($newRule && $newRule.length) { const $newPickers = $newRule.find('.ka-color-picker, .ka-preloader-wp-color-picker'); if ($.fn.wpColorPicker && $newPickers.length) { $newPickers.each(function () { const $input = $(this); if ($input.hasClass('wp-color-picker')) { return; } $input.wpColorPicker({ defaultColor: $input.data('default-color'), change: function () { KngPreloaderAdmin.updatePreview(); }, clear: function () { KngPreloaderAdmin.updatePreview(); } }); }); } } // Scroll to new rule if ($newRule && $newRule[0]) { $newRule[0].scrollIntoView({ behavior: 'smooth', block: 'center' }); } }, /** * Update rule conditions based on type */ updateRuleConditions: function ($rule, condition) { // Find value field (support both class naming conventions) const $valueField = $rule.find('.ka-preloader-rule-value-field'); const $conditionValueRow = $rule.find('.ka-row').filter(function() { return $(this).find('.ka-rule-condition-value').length > 0; }); // Conditions that need a value input const needsValue = ['url_contains', 'url_equals', 'url_regex', 'query_param', 'specific_page', 'specific_post', 'specific_cpt', 'post_type', 'page_template']; if (needsValue.includes(condition)) { $valueField.slideDown(200); $conditionValueRow.slideDown(200); } else { $valueField.slideUp(200); $conditionValueRow.slideUp(200); } }, /** * Update rule indices after deletion */ updateRuleIndices: function () { this.elements.rulesList.find('.ka-preloader-rule-item').each(function (index) { const $rule = $(this); $rule.attr('data-index', index); // Update all input names $rule.find('input, select').each(function () { const $field = $(this); const name = $field.attr('name'); if (name) { $field.attr('name', name.replace(/\[\d+\]/, '[' + index + ']')); } }); }); }, /** * Initialize import/export */ initImportExport: function () { // Export button $(document).on('click', '.ka-preloader-export-btn', function (e) { e.preventDefault(); KngPreloaderAdmin.exportSettings(); }); // Import button $(document).on('click', '.ka-preloader-import-btn', function (e) { e.preventDefault(); KngPreloaderAdmin.importSettings(); }); // Copy to clipboard $(document).on('click', '.ka-preloader-copy-btn', function (e) { e.preventDefault(); const $textarea = $(this).siblings('textarea'); $textarea.select(); document.execCommand('copy'); const $btn = $(this); const originalText = $btn.text(); $btn.text('Copied!'); setTimeout(() => $btn.text(originalText), 2000); }); }, /** * Export settings */ exportSettings: function () { const settings = this.collectFormSettings(); const exportData = JSON.stringify(settings, null, 2); const $exportArea = $('.ka-preloader-export-area'); $exportArea.val(exportData).show(); }, /** * Import settings */ importSettings: function () { const $importArea = $('.ka-preloader-import-area'); const importData = $importArea.val().trim(); if (!importData) { alert('Please paste settings JSON to import.'); return; } try { const settings = JSON.parse(importData); this.applyImportedSettings(settings); alert('Settings imported successfully! Please save to apply.'); } catch (e) { alert('Invalid JSON format. Please check your import data.'); } }, /** * Apply imported settings to form */ applyImportedSettings: function (settings) { for (const [key, value] of Object.entries(settings)) { const $field = this.elements.form.find('[name="' + key + '"]'); if (!$field.length) continue; if ($field.is(':checkbox')) { $field.prop('checked', value === '1'); } else if ($field.is(':radio')) { $field.filter('[value="' + value + '"]').prop('checked', true); } else if ($field.is('select')) { $field.val(value); } else { $field.val(value); } } // Update preview this.updatePreview(); // Update preset card selection const preset = settings.template || settings.preset; if (preset) { $('.ka-preloader-preset-card').removeClass('active'); $('.ka-preloader-preset-card[data-preset="' + preset + '"]').addClass('active'); } }, /** * Initialize dark mode toggle */ initDarkModeToggle: function () { $(document).on('change', '.ka-preloader-dark-toggle input', function () { const isDark = $(this).is(':checked'); if (isDark) { $('.ka-preloader-admin-page').addClass('dark-mode'); localStorage.setItem('kng_preloader_dark_mode', '1'); } else { $('.ka-preloader-admin-page').removeClass('dark-mode'); localStorage.setItem('kng_preloader_dark_mode', '0'); } }); // Restore preference const savedDarkMode = localStorage.getItem('kng_preloader_dark_mode'); if (savedDarkMode === '1') { $('.ka-preloader-dark-toggle input').prop('checked', true).trigger('change'); } }, /** * Initialize form submit */ initFormSubmit: function () { this.elements.form.on('submit', function (e) { // Let the form submit naturally, just show a loading state const $submitBtn = $(this).find('button[type="submit"]'); $submitBtn.prop('disabled', true).text('Saving...'); }); }, /** * Initialize preview modal */ initPreviewModal: function () { $(document).on('click', '.ka-preloader-full-preview-btn', function (e) { e.preventDefault(); KngPreloaderAdmin.openPreviewModal(); }); // Close on Escape key $(document).on('keydown', function (e) { if (e.key === 'Escape') { KngPreloaderAdmin.closePreviewModal(); } }); }, /** * Open preview modal */ openPreviewModal: function () { // Prefer template-provided modal implementation when available. if (typeof window.kaPreloaderOpenPreview === 'function') { window.kaPreloaderOpenPreview(); return; } const settings = this.collectFormSettings(); const $modal = $('.ka-preloader-preview-modal'); if (!$modal.length) { return; } // Build preview HTML const bgColor = settings.bg_color || settings.background_color || 'rgba(0,0,0,0)'; const presetId = settings.template || settings.preset || 'spinner-circle'; const sizePx = (settings.spinner_size || settings.animation_size || 48) + 'px'; const presetHtml = this.getPresetHtml(presetId); const previewHtml = `
${presetHtml}
${settings.text_content ? '
' + settings.text_content + '
' : ''}
`; const $container = $('#ka-preloader-preview-container'); if ($container.length) { $container.html(previewHtml); $modal.css('display', 'flex'); $('body').css('overflow', 'hidden'); } }, /** * Close preview modal */ closePreviewModal: function () { // Prefer template-provided modal implementation when available. if (typeof window.kaPreloaderClosePreview === 'function') { window.kaPreloaderClosePreview(); return; } const $modal = $('.ka-preloader-preview-modal'); if ($modal.length) { $modal.css('display', 'none').removeClass('active'); } $('body').css('overflow', '').removeClass('ka-preloader-modal-open'); }, /** * Debounce helper */ debounce: function (func, wait) { let timeout; return function executedFunction(...args) { const later = () => { clearTimeout(timeout); func.apply(KngPreloaderAdmin, args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; } }; // Initialize on DOM ready $(document).ready(function () { KngPreloaderAdmin.init(); }); // Expose globally for debugging window.KngPreloaderAdmin = KngPreloaderAdmin; })(jQuery);