PluginProbe
King Addons for Elementor – 100+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce Builder, Mega Menu, Popup Builder / 51.1.63
King Addons for Elementor – 100+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce Builder, Mega Menu, Popup Builder v51.1.63
51.1.86 51.1.84 51.1.85 51.1.83 51.1.82 51.1.81 51.1.79 51.1.78 51.1.77 51.1.76 51.1.74 51.1.75 51.1.65 51.1.64 51.1.63 trunk 51.1.14 51.1.2 51.1.35 51.1.36 51.1.37 51.1.38 51.1.39 51.1.44 51.1.45 All 40 releases
king-addons / includes / extensions / Template_Catalog_Button / assets / template-catalog-button.js

template-catalog-button.js in King Addons for Elementor – 100+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce Builder, Mega Menu, Popup Builder 51.1.63, at includes/extensions/Template_Catalog_Button/assets/template-catalog-button.js

2,272 lines 95.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * King Addons Template Catalog Button for Elementor Editor
3 *
4 * This script adds a template catalog button to the Elementor editor panel
5 * that opens the King Addons template catalog in a popup for import into current page
6 */
7
8 (function($) {
9 'use strict';
10
11 /**
12 * Template Catalog Button Handler
13 */
14 class TemplateCatalogButton {
15
16 constructor() {
17 this.currentPage = 1;
18 this.currentFilters = {};
19 this.isLoading = false;
20 this.catalogData = null;
21 this.buttonCreated = false; // Flag to prevent duplicate button creation
22
23 // Sections properties
24 this.currentSectionsPage = 1;
25 this.currentSectionsFilters = {};
26 this.isSectionsLoading = false;
27 this.sectionsData = null;
28 this.sectionsLoaded = false;
29
30 this.init();
31 }
32
33 /**
34 * Initialize the button functionality
35 */
36 init() {
37 // Wait for Elementor to be fully loaded
38 $(window).on('elementor:init', () => {
39 this.onElementorInit();
40 });
41
42 // Fallback if elementor:init doesn't fire
43 setTimeout(() => {
44 this.addButton();
45 }, 2000);
46 }
47
48 /**
49 * Handle Elementor initialization
50 */
51 onElementorInit() {
52 // Add button immediately
53 this.addButton();
54
55 // Also add button when panel opens
56 if (typeof elementor !== 'undefined' && elementor.hooks) {
57 elementor.hooks.addAction('panel/open_editor/widget', () => {
58 setTimeout(() => this.addButton(), 100);
59 });
60
61 // Add button when navigator opens
62 elementor.hooks.addAction('navigator/init', () => {
63 setTimeout(() => this.addButton(), 100);
64 });
65 }
66
67 // Monitor for panel changes
68 this.observePanelChanges();
69 }
70
71 /**
72 * Reset button creation flag to allow recreating button when needed
73 */
74 resetButtonFlag() {
75 this.buttonCreated = false;
76 }
77
78 /**
79 * Add the template catalog button to the editor
80 */
81 addButton() {
82 // Check if we have the required data
83 if (!window.kingAddonsTemplateCatalog || !window.kingAddonsTemplateCatalog.templatesEnabled) {
84 return;
85 }
86
87 // Do not show this block inside King Addons Woo Builder templates.
88 // We rely on both PHP-provided flag and runtime detection because meta may not
89 // be saved yet when the editor first loads.
90 if (this.isWooBuilderTemplate()) {
91 return;
92 }
93
94 // Check if button is enabled (premium setting)
95 if (window.kingAddonsTemplateCatalog.buttonEnabled === false) {
96 return;
97 }
98
99 // Check if button was already created in this session
100 if (this.buttonCreated) {
101 return;
102 }
103
104 // Check if button already exists in DOM
105 if (document.querySelector('.king-addons-template-catalog-btn')) {
106 this.buttonCreated = true;
107 return;
108 }
109
110 // Try to add button to the content area where "Drag widget here" is shown
111 const buttonAdded = this.tryAddToContentArea();
112
113 // Mark as created if successfully added
114 if (buttonAdded) {
115 this.buttonCreated = true;
116 }
117 }
118
119 /**
120 * Detect if current Elementor document is a Woo Builder template.
121 */
122 isWooBuilderTemplate() {
123 try {
124 if (window.kingAddonsTemplateCatalog && window.kingAddonsTemplateCatalog.isWooBuilderTemplate) {
125 return true;
126 }
127
128 // Elementor runtime config (best-effort, varies across versions)
129 const cfg = (typeof elementor !== 'undefined' && elementor.config) ? elementor.config : null;
130 const doc = cfg && (cfg.document || cfg.initial_document) ? (cfg.document || cfg.initial_document) : null;
131
132 const docType = doc && doc.type ? doc.type : null;
133 if (docType === 'king-addons-woo-builder') {
134 return true;
135 }
136
137 const settings = doc && doc.settings ? doc.settings : null;
138 if (settings && settings.ka_woo_template_type) {
139 return true;
140 }
141 } catch (e) {
142 // ignore
143 }
144
145 return false;
146 }
147
148 /**
149 * Try to add button to content area with "Drag widget here"
150 */
151 tryAddToContentArea() {
152 // Look for empty section or container in the preview area
153 const preview = document.querySelector('#elementor-preview-iframe');
154 if (!preview) return false;
155
156 const previewDoc = preview.contentDocument || preview.contentWindow.document;
157 if (!previewDoc) return false;
158
159 // Check if template catalog button already exists to prevent duplicates
160 const existingButton = previewDoc.querySelector('.king-addons-template-catalog-content-area');
161 if (existingButton) {
162 return false;
163 }
164
165 // Only try to find the "Add New Section" element - no fallback methods
166 const addNewSection = previewDoc.querySelector('#elementor-add-new-section');
167 if (addNewSection && addNewSection.parentNode) {
168 const buttonContainer = this.createContentAreaButton();
169
170 // Insert after the "Add New Section" element
171 addNewSection.parentNode.insertBefore(buttonContainer, addNewSection.nextSibling);
172 return true;
173 }
174
175 // If no "Add New Section" element found, don't add button
176 return false;
177 }
178
179
180
181 /**
182 * Create button for content area placement
183 */
184 createContentAreaButton() {
185 const container = document.createElement('div');
186 container.className = 'king-addons-template-catalog-content-area';
187 container.id = 'king-addons-template-catalog-' + Date.now(); // Unique ID
188 container.style.cssText = `
189 display: flex;
190 flex-direction: column;
191 align-items: center;
192 justify-content: center;
193 padding: 40px 30px;
194 text-align: center;
195 min-height: 200px;
196 width: 100%;
197 max-width: 600px;
198 margin: 40px auto;
199 border: 3px solid #93c5fd;
200 border-radius: 16px;
201 background: linear-gradient(135deg, #f0f9ff 0%, #e0f2fe 50%, #f8fafc 100%);
202 position: relative;
203 overflow: hidden;
204 box-shadow: 0 4px 20px rgba(59, 130, 246, 0.08);
205 transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
206 cursor: pointer;
207 box-sizing: border-box;
208 `;
209
210 // Add hover effects
211 container.addEventListener('mouseenter', () => {
212 container.style.transform = 'translateY(-3px)';
213 container.style.boxShadow = '0 8px 30px rgba(59, 130, 246, 0.12)';
214 container.style.borderColor = '#60a5fa';
215 });
216
217 container.addEventListener('mouseleave', () => {
218 container.style.transform = 'translateY(0)';
219 container.style.boxShadow = '0 4px 20px rgba(59, 130, 246, 0.08)';
220 container.style.borderColor = '#93c5fd';
221 });
222
223 // Add decorative background pattern
224 const pattern = document.createElement('div');
225 pattern.style.cssText = `
226 position: absolute;
227 top: 0;
228 left: 0;
229 right: 0;
230 bottom: 0;
231 background-image: radial-gradient(circle at 25% 25%, rgba(59, 130, 246, 0.04) 0%, transparent 50%),
232 radial-gradient(circle at 75% 75%, rgba(147, 197, 253, 0.04) 0%, transparent 50%);
233 pointer-events: none;
234 z-index: 1;
235 `;
236 container.appendChild(pattern);
237
238 // Icon wrapper
239 const iconWrapper = document.createElement('div');
240 iconWrapper.style.cssText = `
241 width: 64px;
242 height: 64px;
243 background: linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%);
244 border-radius: 50%;
245 display: flex;
246 align-items: center;
247 justify-content: center;
248 margin: 0 0 20px 0;
249 position: relative;
250 z-index: 2;
251 box-shadow: 0 6px 20px rgba(59, 130, 246, 0.25);
252 animation: pulse 3s infinite;
253 `;
254
255 const icon = document.createElement('i');
256 icon.className = 'eicon-library-open';
257 icon.style.cssText = `
258 font-size: 24px;
259 color: white;
260 `;
261 iconWrapper.appendChild(icon);
262
263 const title = document.createElement('h3');
264 title.textContent = 'Start with a Template';
265 title.style.cssText = `
266 margin: 0 0 8px 0;
267 font-size: 22px;
268 font-weight: 700;
269 color: #1e293b;
270 font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
271 position: relative;
272 z-index: 2;
273 letter-spacing: -0.025em;
274 `;
275
276 const subtitle = document.createElement('p');
277 subtitle.textContent = 'Choose from hundreds of professional templates to get started quickly';
278 subtitle.style.cssText = `
279 margin: 0 0 24px 0;
280 font-size: 14px;
281 color: #64748b;
282 font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
283 position: relative;
284 z-index: 2;
285 line-height: 1.5;
286 max-width: 400px;
287 `;
288
289 const button = document.createElement('button');
290 button.className = 'king-addons-template-catalog-btn king-addons-content-area-btn';
291 button.type = 'button';
292 button.style.cssText = `
293 background: linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%);
294 color: white;
295 border: none;
296 padding: 12px 24px;
297 border-radius: 10px;
298 font-size: 14px;
299 font-weight: 600;
300 font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
301 cursor: pointer;
302 display: inline-flex;
303 align-items: center;
304 gap: 8px;
305 transition: all 0.2s ease;
306 box-shadow: 0 3px 12px rgba(59, 130, 246, 0.3);
307 position: relative;
308 z-index: 2;
309 text-decoration: none;
310 outline: none;
311 letter-spacing: 0.025em;
312 min-width: 160px;
313 justify-content: center;
314 `;
315
316 button.innerHTML = `
317 <i class="eicon-library-open" style="font-size: 16px;" aria-hidden="true"></i>
318 <span>${window.kingAddonsTemplateCatalog.buttonText}</span>
319 `;
320
321 // Add button hover effects
322 button.addEventListener('mouseenter', () => {
323 button.style.transform = 'translateY(-1px)';
324 button.style.boxShadow = '0 6px 20px rgba(59, 130, 246, 0.4)';
325 button.style.background = 'linear-gradient(135deg, #2563eb 0%, #1e40af 100%)';
326 });
327
328 button.addEventListener('mouseleave', () => {
329 button.style.transform = 'translateY(0)';
330 button.style.boxShadow = '0 4px 16px rgba(59, 130, 246, 0.3)';
331 button.style.background = 'linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%)';
332 });
333
334 button.addEventListener('mousedown', () => {
335 button.style.transform = 'translateY(0px) scale(0.98)';
336 });
337
338 button.addEventListener('mouseup', () => {
339 button.style.transform = 'translateY(-1px) scale(1)';
340 });
341
342 // Add click handler
343 button.addEventListener('click', (e) => {
344 e.preventDefault();
345 e.stopPropagation();
346 this.openTemplatePopup();
347 });
348
349 // Add pulse animation styles
350 const style = document.createElement('style');
351 style.textContent = `
352 @keyframes pulse {
353 0%, 100% {
354 transform: scale(1);
355 opacity: 1;
356 }
357 50% {
358 transform: scale(1.08);
359 opacity: 0.9;
360 }
361 }
362 `;
363 document.head.appendChild(style);
364
365 // Add responsive styles
366 if (window.innerWidth <= 768) {
367 container.style.padding = '30px 20px';
368 container.style.margin = '20px auto';
369 container.style.minHeight = '160px';
370 container.style.maxWidth = '90%';
371 title.style.fontSize = '18px';
372 subtitle.style.fontSize = '13px';
373 subtitle.style.maxWidth = '280px';
374 button.style.padding = '10px 20px';
375 button.style.minWidth = '140px';
376 button.style.fontSize = '13px';
377 iconWrapper.style.width = '48px';
378 iconWrapper.style.height = '48px';
379 icon.style.fontSize = '18px';
380 }
381
382 // Add click handler to entire container for better UX
383 container.addEventListener('click', (e) => {
384 if (e.target === container || e.target === pattern) {
385 this.openTemplatePopup();
386 }
387 });
388
389 container.appendChild(iconWrapper);
390 container.appendChild(title);
391 container.appendChild(subtitle);
392 container.appendChild(button);
393
394 return container;
395 }
396
397 /**
398 * Open template catalog popup
399 */
400 openTemplatePopup() {
401 // Create popup if it doesn't exist
402 if (!document.querySelector('.king-addons-template-popup')) {
403 this.createTemplatePopup();
404 }
405
406 // Show popup
407 const popup = document.querySelector('.king-addons-template-popup');
408 popup.classList.add('show');
409
410 // Load templates on first open
411 if (!this.catalogData) {
412 this.loadTemplateCatalog();
413 } else {
414 this.renderTemplateGrid();
415 }
416
417 // Load sections on first open
418 if (!this.sectionsData) {
419 this.loadSectionsCatalog();
420 } else {
421 this.renderSectionsGrid();
422 }
423 }
424
425 /**
426 * Create template popup HTML structure
427 */
428 createTemplatePopup() {
429 const popup = document.createElement('div');
430 popup.className = 'king-addons-template-popup';
431
432 const isProVersion = window.kingAddonsTemplateCatalog.isPremium;
433 const catalogTitle = isProVersion ? 'Templates Pro' : 'Free Templates';
434
435 popup.innerHTML = `
436 <div class="king-addons-template-popup-content">
437 <div class="king-addons-template-popup-header">
438 <div class="king-addons-template-popup-header-content">
439 <h2 class="king-addons-template-popup-title">King Addons ${catalogTitle}</h2>
440 <p class="king-addons-template-popup-subtitle">Choose from hundreds of professional templates and sections</p>
441 </div>
442 <button class="king-addons-template-popup-close" type="button">&times;</button>
443 </div>
444
445 <!-- Popup Tabs -->
446 <div class="king-addons-popup-tabs">
447 <button class="king-addons-popup-tab-button active" data-tab="templates">
448 <i class="eicon-document-file"></i>
449 Templates
450 <span class="popup-tab-count" id="templates-tab-count">0</span>
451 </button>
452 <button class="king-addons-popup-tab-button" data-tab="sections">
453 <i class="eicon-section"></i>
454 Sections
455 <span class="popup-tab-count" id="sections-tab-count">0</span>
456 </button>
457 </div>
458
459 <!-- Templates Tab Content -->
460 <div class="king-addons-popup-tab-content active" id="templates-tab">
461 <div class="king-addons-template-popup-filters">
462 <input type="text" class="king-addons-template-popup-search" placeholder="Search templates..." />
463 <select class="king-addons-template-popup-select" id="category-filter">
464 <option value="">All Categories</option>
465 </select>
466 <select class="king-addons-template-popup-select" id="collection-filter">
467 <option value="">All Collections</option>
468 </select>
469 <button class="king-addons-reset-filters-btn" id="reset-templates-filters">
470 <i class="eicon-undo" aria-hidden="true"></i> Reset
471 </button>
472 </div>
473 <div class="king-addons-template-popup-body">
474 <div class="king-addons-template-popup-loading">
475 <div class="king-addons-template-spinner"></div>
476 Loading templates...
477 </div>
478 </div>
479 </div>
480
481 <!-- Sections Tab Content -->
482 <div class="king-addons-popup-tab-content" id="sections-tab">
483 <div class="king-addons-template-popup-filters">
484 <input type="text" class="king-addons-sections-popup-search" placeholder="Search sections..." />
485 <select class="king-addons-template-popup-select" id="sections-category-filter">
486 <option value="">All Categories</option>
487 </select>
488 <select class="king-addons-template-popup-select" id="sections-type-filter">
489 <option value="">All Types</option>
490 </select>
491 <select class="king-addons-template-popup-select" id="sections-plan-filter">
492 <option value="">All Plans</option>
493 <option value="free">Free</option>
494 ${isProVersion ? '<option value="premium">Premium</option>' : ''}
495 </select>
496 <button class="king-addons-reset-filters-btn" id="reset-sections-filters">
497 <i class="eicon-undo" aria-hidden="true"></i> Reset
498 </button>
499 </div>
500 <div class="king-addons-sections-popup-body">
501 <div class="king-addons-template-popup-loading">
502 <div class="king-addons-template-spinner"></div>
503 Loading sections...
504 </div>
505 </div>
506 </div>
507 </div>
508 `;
509
510 document.body.appendChild(popup);
511
512 // Create and append premium promo popup
513 this.createPremiumPromoPopup();
514
515 // Add event listeners
516 this.attachPopupEventListeners();
517 }
518
519 /**
520 * Attach event listeners to popup elements
521 */
522 attachPopupEventListeners() {
523 const popup = document.querySelector('.king-addons-template-popup');
524
525 // Close popup
526 const closeBtn = popup.querySelector('.king-addons-template-popup-close');
527 closeBtn.addEventListener('click', () => this.closeTemplatePopup());
528
529 // Close on backdrop click
530 popup.addEventListener('click', (e) => {
531 if (e.target === popup) {
532 this.closeTemplatePopup();
533 }
534 });
535
536 // Tab switching functionality
537 const tabButtons = popup.querySelectorAll('.king-addons-popup-tab-button');
538 tabButtons.forEach(button => {
539 button.addEventListener('click', (e) => {
540 const tabId = e.currentTarget.dataset.tab;
541 this.switchPopupTab(tabId);
542 });
543 });
544
545 // Templates tab - Search functionality
546 const searchInput = popup.querySelector('.king-addons-template-popup-search');
547 let searchTimeout;
548 searchInput.addEventListener('input', (e) => {
549 clearTimeout(searchTimeout);
550 searchTimeout = setTimeout(() => {
551 // Reset other filters when searching (same as main catalog)
552 this.currentFilters = { search: e.target.value };
553 categoryFilter.value = '';
554 collectionFilter.value = '';
555 this.currentPage = 1;
556 this.loadTemplateCatalog();
557 }, 300);
558 });
559
560 // Templates tab - Category filter
561 const categoryFilter = popup.querySelector('#category-filter');
562 categoryFilter.addEventListener('change', (e) => {
563 // Reset search and other filters (same as main catalog)
564 this.currentFilters = { category: e.target.value };
565 searchInput.value = '';
566 collectionFilter.value = '';
567 this.currentPage = 1;
568 this.loadTemplateCatalog();
569 });
570
571 // Templates tab - Collection filter
572 const collectionFilter = popup.querySelector('#collection-filter');
573 collectionFilter.addEventListener('change', (e) => {
574 // Reset search and other filters (same as main catalog)
575 this.currentFilters = { collection: e.target.value };
576 searchInput.value = '';
577 categoryFilter.value = '';
578 this.currentPage = 1;
579 this.loadTemplateCatalog();
580 });
581
582 // Sections tab - Search functionality
583 const sectionsSearchInput = popup.querySelector('.king-addons-sections-popup-search');
584 let sectionsSearchTimeout;
585 sectionsSearchInput.addEventListener('input', (e) => {
586 clearTimeout(sectionsSearchTimeout);
587 sectionsSearchTimeout = setTimeout(() => {
588 // Reset other filters when searching (same as main catalog)
589 this.currentSectionsFilters = { search: e.target.value };
590 sectionsCategoryFilter.value = '';
591 sectionsTypeFilter.value = '';
592 sectionsPlanFilter.value = '';
593 this.currentSectionsPage = 1;
594 this.loadSectionsCatalog();
595 }, 300);
596 });
597
598 // Sections tab - Category filter
599 const sectionsCategoryFilter = popup.querySelector('#sections-category-filter');
600 sectionsCategoryFilter.addEventListener('change', (e) => {
601 // Reset search and other filters (same as main catalog)
602 this.currentSectionsFilters = { category: e.target.value };
603 sectionsSearchInput.value = '';
604 sectionsTypeFilter.value = '';
605 sectionsPlanFilter.value = '';
606 this.currentSectionsPage = 1;
607 this.loadSectionsCatalog();
608 });
609
610 // Sections tab - Type filter
611 const sectionsTypeFilter = popup.querySelector('#sections-type-filter');
612 sectionsTypeFilter.addEventListener('change', (e) => {
613 // Reset search and other filters (same as main catalog)
614 this.currentSectionsFilters = { section_type: e.target.value };
615 sectionsSearchInput.value = '';
616 sectionsCategoryFilter.value = '';
617 sectionsPlanFilter.value = '';
618 this.currentSectionsPage = 1;
619 this.loadSectionsCatalog();
620 });
621
622 // Sections tab - Plan filter
623 const sectionsPlanFilter = popup.querySelector('#sections-plan-filter');
624 sectionsPlanFilter.addEventListener('change', (e) => {
625 // Reset search and other filters (same as main catalog)
626 this.currentSectionsFilters = { plan: e.target.value };
627 sectionsSearchInput.value = '';
628 sectionsCategoryFilter.value = '';
629 sectionsTypeFilter.value = '';
630 this.currentSectionsPage = 1;
631 this.loadSectionsCatalog();
632 });
633
634 // Templates reset button
635 const resetTemplatesBtn = popup.querySelector('#reset-templates-filters');
636 resetTemplatesBtn.addEventListener('click', () => {
637 // Reset all templates filters and search
638 this.currentFilters = {};
639 this.currentPage = 1;
640 searchInput.value = '';
641 categoryFilter.value = '';
642 collectionFilter.value = '';
643 this.loadTemplateCatalog();
644 });
645
646 // Sections reset button
647 const resetSectionsBtn = popup.querySelector('#reset-sections-filters');
648 resetSectionsBtn.addEventListener('click', () => {
649 // Reset all sections filters and search
650 this.currentSectionsFilters = {};
651 this.currentSectionsPage = 1;
652 sectionsSearchInput.value = '';
653 sectionsCategoryFilter.value = '';
654 sectionsTypeFilter.value = '';
655 sectionsPlanFilter.value = '';
656 this.loadSectionsCatalog();
657 });
658
659 // ESC key to close
660 document.addEventListener('keydown', (e) => {
661 if (e.key === 'Escape' && popup.classList.contains('show')) {
662 this.closeTemplatePopup();
663 }
664 });
665 }
666
667 /**
668 * Close template popup
669 */
670 closeTemplatePopup() {
671 const popup = document.querySelector('.king-addons-template-popup');
672 if (popup) {
673 popup.classList.remove('show');
674 }
675 }
676
677 /**
678 * Create premium promo popup
679 */
680 createPremiumPromoPopup() {
681 const promoPopup = document.createElement('div');
682 promoPopup.className = 'king-addons-premium-promo-popup';
683
684 promoPopup.innerHTML = `
685 <div class="king-addons-premium-promo-popup-content">
686 <div class="king-addons-premium-promo-popup-wrapper">
687 <div class="king-addons-premium-promo-popup-txt">
688 <span class="king-addons-pr-popup-title">Want This Premium Template?</span>
689 <br><span class="king-addons-pr-popup-desc">
690 Get <span class="king-addons-pr-popup-desc-b">unlimited downloads</span> for just
691 <span class="king-addons-pr-popup-desc-b">$6.99/month</span> — keep them
692 <span class="king-addons-pr-popup-desc-b">even after</span> your subscription ends!
693 </span>
694 <span class="king-addons-pr-popup-desc" style="font-size: 16px;opacity: 0.6;">
695 Trusted by 20,000+ users
696 </span>
697 </div>
698 <a class="purchase-btn" href="https://kingaddons.com/pricing/?utm_source=kng-elementor-popup-pro&utm_medium=plugin&utm_campaign=kng" target="_blank">
699 <button class="king-addons-premium-promo-popup-purchase-btn purchase-btn">
700 <img src="${window.kingAddonsTemplateCatalog.pluginUrl}includes/admin/img/icon-for-admin.svg"
701 style="margin-right: 7px;width: 16px;height: 16px;"
702 alt="Unlock All Templates">
703 Unlock All Templates
704 </button>
705 </a>
706 <button class="king-addons-close-premium-promo-popup">
707 Cancel
708 </button>
709 </div>
710 </div>
711 `;
712
713 document.body.appendChild(promoPopup);
714
715 // Add event listener for close button
716 const closeBtn = promoPopup.querySelector('.king-addons-close-premium-promo-popup');
717 closeBtn.addEventListener('click', () => {
718 this.closePremiumPromoPopup();
719 });
720
721 // Close on outside click
722 promoPopup.addEventListener('click', (e) => {
723 if (e.target === promoPopup) {
724 this.closePremiumPromoPopup();
725 }
726 });
727 }
728
729 /**
730 * Show premium promo popup
731 */
732 showPremiumPromoPopup() {
733 const promoPopup = document.querySelector('.king-addons-premium-promo-popup');
734 if (promoPopup) {
735 promoPopup.classList.add('show');
736 }
737 }
738
739 /**
740 * Close premium promo popup
741 */
742 closePremiumPromoPopup() {
743 const promoPopup = document.querySelector('.king-addons-premium-promo-popup');
744 if (promoPopup) {
745 promoPopup.classList.remove('show');
746 }
747 }
748
749 /**
750 * Load template catalog data via AJAX
751 */
752 loadTemplateCatalog() {
753 if (this.isLoading) return;
754
755 this.isLoading = true;
756
757 const popup = document.querySelector('.king-addons-template-popup');
758 const bodyElement = popup.querySelector('.king-addons-template-popup-body');
759
760 bodyElement.innerHTML = `
761 <div class="king-addons-template-popup-loading">
762 <div class="king-addons-template-spinner"></div>
763 Loading templates...
764 </div>
765 `;
766
767 const formData = new FormData();
768 formData.append('action', 'king_addons_get_template_catalog');
769 formData.append('nonce', window.kingAddonsTemplateCatalog.nonce);
770 formData.append('search', this.currentFilters.search || '');
771 formData.append('category', this.currentFilters.category || '');
772 formData.append('collection', this.currentFilters.collection || '');
773 formData.append('page', this.currentPage);
774
775 fetch(window.kingAddonsTemplateCatalog.ajaxUrl, {
776 method: 'POST',
777 body: formData
778 })
779 .then(response => response.json())
780 .then(data => {
781 this.isLoading = false;
782
783 if (data.success) {
784 this.catalogData = data.data;
785 this.updateFilters();
786 this.renderTemplateGrid();
787 } else {
788 bodyElement.innerHTML = `
789 <div class="king-addons-template-popup-error">
790 Error loading templates: ${data.data || 'Unknown error'}
791 </div>
792 `;
793 }
794 })
795 .catch(error => {
796 this.isLoading = false;
797 console.error('Error loading templates:', error);
798 bodyElement.innerHTML = `
799 <div class="king-addons-template-popup-error">
800 Failed to load templates. Please try again.
801 </div>
802 `;
803 });
804 }
805
806 /**
807 * Update filter dropdowns with available options
808 */
809 updateFilters() {
810 if (!this.catalogData) return;
811
812 const popup = document.querySelector('.king-addons-template-popup');
813 const categoryFilter = popup.querySelector('#category-filter');
814 const collectionFilter = popup.querySelector('#collection-filter');
815
816 // Update categories
817 categoryFilter.innerHTML = '<option value="">All Categories</option>';
818 this.catalogData.categories.forEach(category => {
819 const count = this.catalogData.category_counts[category] || 0;
820 const option = document.createElement('option');
821 option.value = category;
822 option.textContent = `${category} (${count})`;
823 if (category === this.currentFilters.category) {
824 option.selected = true;
825 }
826 categoryFilter.appendChild(option);
827 });
828
829 // Update collections
830 collectionFilter.innerHTML = '<option value="">All Collections</option>';
831 Object.entries(this.catalogData.collections).forEach(([id, name]) => {
832 const option = document.createElement('option');
833 option.value = id;
834 option.textContent = name;
835 if (id === this.currentFilters.collection) {
836 option.selected = true;
837 }
838 collectionFilter.appendChild(option);
839 });
840 }
841
842 /**
843 * Render template grid
844 */
845 renderTemplateGrid() {
846 if (!this.catalogData) return;
847
848 const popup = document.querySelector('.king-addons-template-popup');
849 const bodyElement = popup.querySelector('.king-addons-template-popup-body');
850
851 if (!this.catalogData.templates || this.catalogData.templates.length === 0) {
852 bodyElement.innerHTML = `
853 <div class="king-addons-template-popup-empty">
854 No templates found. Try adjusting your search or filters.
855 </div>
856 `;
857 // Update templates count even when empty
858 this.updateTemplatesCount();
859 return;
860 }
861
862 const grid = document.createElement('div');
863 grid.className = 'king-addons-template-grid';
864
865 this.catalogData.templates.forEach(template => {
866 const item = document.createElement('div');
867 item.className = 'king-addons-template-item';
868 item.dataset.templateKey = template.template_key;
869 item.dataset.templatePlan = template.plan;
870
871 const thumbnailUrl = `https://thumbnails.kingaddons.com/${template.template_key}.png?v=4`;
872
873 item.innerHTML = `
874 <img class="king-addons-template-item-image"
875 src="${thumbnailUrl}"
876 alt="${template.title}"
877 loading="lazy" />
878 <div class="king-addons-template-item-content">
879 <h3 class="king-addons-template-item-title">${template.title}</h3>
880 <span class="king-addons-template-item-plan ${template.plan}">${template.plan}</span>
881 </div>
882 <div class="king-addons-template-item-overlay">
883 <div class="king-addons-template-item-actions">
884 <button class="king-addons-template-import-btn" data-template-key="${template.template_key}" data-template-plan="${template.plan}">
885 Import Template
886 </button>
887 <a href="https://demo.kingaddons.com/${template.template_key}" class="king-addons-template-preview-btn" target="_blank">
888 Live Preview
889 </a>
890 </div>
891 </div>
892 `;
893
894 grid.appendChild(item);
895 });
896
897 // Add event listeners for template actions
898 grid.querySelectorAll('.king-addons-template-import-btn').forEach(btn => {
899 btn.addEventListener('click', (e) => {
900 e.stopPropagation();
901 const templateKey = e.target.dataset.templateKey;
902 const templatePlan = e.target.dataset.templatePlan;
903 this.importTemplate(templateKey, templatePlan);
904 });
905 });
906
907 // Create pagination
908 const pagination = this.createPagination();
909
910 bodyElement.innerHTML = '';
911 bodyElement.appendChild(grid);
912 bodyElement.appendChild(pagination);
913
914 // Update templates count in tab
915 this.updateTemplatesCount();
916 }
917
918 /**
919 * Create pagination controls
920 */
921 createPagination() {
922 const paginationContainer = document.createElement('div');
923 paginationContainer.className = 'king-addons-template-pagination';
924
925 const { current_page, total_pages, total_templates, items_per_page } = this.catalogData.pagination;
926
927 // Previous button
928 const prevBtn = document.createElement('button');
929 prevBtn.textContent = '‹ Previous';
930 prevBtn.disabled = current_page <= 1;
931 prevBtn.addEventListener('click', () => {
932 if (current_page > 1) {
933 this.currentPage = current_page - 1;
934 this.loadTemplateCatalog();
935 }
936 });
937
938 // Page numbers
939 const pageNumbers = [];
940 const startPage = Math.max(1, current_page - 2);
941 const endPage = Math.min(total_pages, current_page + 2);
942
943 for (let i = startPage; i <= endPage; i++) {
944 const pageBtn = document.createElement('button');
945 pageBtn.textContent = i;
946 if (i === current_page) {
947 pageBtn.classList.add('active');
948 }
949 pageBtn.addEventListener('click', () => {
950 this.currentPage = i;
951 this.loadTemplateCatalog();
952 });
953 pageNumbers.push(pageBtn);
954 }
955
956 // Next button
957 const nextBtn = document.createElement('button');
958 nextBtn.textContent = 'Next ›';
959 nextBtn.disabled = current_page >= total_pages;
960 nextBtn.addEventListener('click', () => {
961 if (current_page < total_pages) {
962 this.currentPage = current_page + 1;
963 this.loadTemplateCatalog();
964 }
965 });
966
967 // Info text
968 const infoText = document.createElement('div');
969 infoText.className = 'king-addons-template-pagination-info';
970 const startItem = (current_page - 1) * items_per_page + 1;
971 const endItem = Math.min(current_page * items_per_page, total_templates);
972 infoText.textContent = `Showing ${startItem}-${endItem} of ${total_templates} templates`;
973
974 paginationContainer.appendChild(prevBtn);
975 pageNumbers.forEach(btn => paginationContainer.appendChild(btn));
976 paginationContainer.appendChild(nextBtn);
977 paginationContainer.appendChild(infoText);
978
979 return paginationContainer;
980 }
981
982 /**
983 * Import selected template into current page
984 */
985 importTemplate(templateKey, templatePlan) {
986 // Check permissions for premium templates
987 if (templatePlan === 'premium' && !window.kingAddonsTemplateCatalog.isPremium) {
988 this.showPremiumPromoPopup();
989 return;
990 }
991
992 // Show import progress popup
993 this.showImportProgress();
994
995 // Close template catalog popup
996 this.closeTemplatePopup();
997
998 // Get template data first
999 const formData = new FormData();
1000 formData.append('action', 'king_addons_import_template_to_page');
1001 formData.append('nonce', window.kingAddonsTemplateCatalog.nonce);
1002 formData.append('template_key', templateKey);
1003 formData.append('template_plan', templatePlan);
1004
1005 this.updateImportProgress(5, `Fetching template data from King Addons API...`);
1006
1007 fetch(window.kingAddonsTemplateCatalog.ajaxUrl, {
1008 method: 'POST',
1009 body: formData
1010 })
1011 .then(response => {
1012 this.updateImportProgress(15, 'Received template data, validating...');
1013 return response.json();
1014 })
1015 .then(data => {
1016 if (data.success) {
1017 const templateData = data.data.template_data;
1018 const imageCount = templateData.images ? templateData.images.length : 0;
1019
1020 this.updateImportProgress(25, `Template validated! Found ${imageCount} images to process...`);
1021
1022 console.log('Template data received:', {
1023 title: templateData.title,
1024 images: imageCount,
1025 hasContent: !!templateData.content
1026 });
1027
1028 this.processTemplateImport(templateData);
1029 } else {
1030 this.showImportError(data.data || 'Failed to fetch template data from API');
1031 }
1032 })
1033 .catch(error => {
1034 console.error('Error fetching template:', error);
1035 this.showImportError('Network error: ' + error.message);
1036 });
1037 }
1038
1039 /**
1040 * Process template import into current Elementor page
1041 */
1042 processTemplateImport(templateData) {
1043 if (!templateData || !templateData.content) {
1044 this.showImportError('Invalid template data received');
1045 return;
1046 }
1047
1048 const imageCount = templateData.images ? templateData.images.length : 0;
1049 this.updateImportProgress(35, `Starting import process... Preparing ${imageCount} images for download...`);
1050
1051 // Get current page ID from Elementor - try multiple methods
1052 let pageId = null;
1053
1054 // Method 1: elementor.config.post_id
1055 if (elementor && elementor.config && elementor.config.post_id) {
1056 pageId = elementor.config.post_id;
1057 }
1058 // Method 2: elementor.config.document.id
1059 else if (elementor && elementor.config && elementor.config.document && elementor.config.document.id) {
1060 pageId = elementor.config.document.id;
1061 }
1062 // Method 3: elementor.config.initial_document.id
1063 else if (elementor && elementor.config && elementor.config.initial_document && elementor.config.initial_document.id) {
1064 pageId = elementor.config.initial_document.id;
1065 }
1066 // Method 4: from URL parameters
1067 else {
1068 const urlParams = new URLSearchParams(window.location.search);
1069 const postParam = urlParams.get('post');
1070 if (postParam) {
1071 pageId = parseInt(postParam);
1072 }
1073 }
1074 // Method 5: from WordPress admin globals
1075 if (!pageId && typeof window.pagenow !== 'undefined' && window.pagenow === 'toplevel_page_elementor') {
1076 const urlParams = new URLSearchParams(window.location.search);
1077 const postParam = urlParams.get('post');
1078 if (postParam) {
1079 pageId = parseInt(postParam);
1080 }
1081 }
1082 // Method 6: from elementor globals
1083 if (!pageId && typeof elementorAdminConfig !== 'undefined' && elementorAdminConfig.post_id) {
1084 pageId = elementorAdminConfig.post_id;
1085 }
1086 // Method 7: from PHP localized data
1087 if (!pageId && window.kingAddonsTemplateCatalog && window.kingAddonsTemplateCatalog.currentPostId) {
1088 pageId = parseInt(window.kingAddonsTemplateCatalog.currentPostId);
1089 }
1090
1091 if (!pageId) {
1092 // Debug information
1093 console.log('Debug: Could not determine page ID. Available data:', {
1094 elementor: typeof elementor !== 'undefined' ? {
1095 config: elementor.config,
1096 configKeys: elementor.config ? Object.keys(elementor.config) : null
1097 } : 'undefined',
1098 windowLocation: window.location.href,
1099 urlParams: new URLSearchParams(window.location.search).toString(),
1100 elementorAdminConfig: typeof elementorAdminConfig !== 'undefined' ? elementorAdminConfig : 'undefined',
1101 kingAddonsConfig: window.kingAddonsTemplateCatalog || 'undefined'
1102 });
1103
1104 // Last resort: ask user to save the page first
1105 const userWantsToCreateNew = confirm(
1106 'Could not determine current page ID. This might happen with unsaved pages.\n\n' +
1107 'Do you want to:\n' +
1108 '• Click "OK" to create a new page with this template\n' +
1109 '• Click "Cancel" to save this page first and try again'
1110 );
1111
1112 if (!userWantsToCreateNew) {
1113 this.showImportError('Please save your page first, then try importing the template again.');
1114 return;
1115 }
1116
1117 // Create new page with template
1118 this.createNewPageWithTemplate(templateData);
1119 return;
1120 }
1121
1122 // Use the proven original import system, adapted for current page
1123 this.updateImportProgress(55, `Initializing import using proven system...`);
1124
1125 // Step 1: Initialize import with original system
1126 this.startOriginalStyleImport(templateData, pageId);
1127 }
1128
1129 /**
1130 * Show import progress popup
1131 */
1132 showImportProgress() {
1133 // Remove existing progress popup if any
1134 const existingPopup = document.querySelector('.king-addons-import-progress-popup');
1135 if (existingPopup) {
1136 existingPopup.remove();
1137 }
1138
1139 const popup = document.createElement('div');
1140 popup.className = 'king-addons-import-progress-popup show';
1141
1142 popup.innerHTML = `
1143 <div class="king-addons-import-progress-content">
1144 <h3 class="king-addons-import-progress-title">Importing Template</h3>
1145 <div class="king-addons-import-progress-bar">
1146 <div class="king-addons-import-progress-fill"></div>
1147 </div>
1148 <div class="king-addons-import-progress-text">Initializing...</div>
1149 </div>
1150 `;
1151
1152 document.body.appendChild(popup);
1153 }
1154
1155 /**
1156 * Update import progress
1157 */
1158 updateImportProgress(percentage, message, isSuccess = false) {
1159 const popup = document.querySelector('.king-addons-import-progress-popup');
1160 if (!popup) return;
1161
1162 const progressFill = popup.querySelector('.king-addons-import-progress-fill');
1163 const progressText = popup.querySelector('.king-addons-import-progress-text');
1164
1165 progressFill.style.width = percentage + '%';
1166 progressText.textContent = message;
1167
1168 if (isSuccess) {
1169 progressText.classList.add('king-addons-import-success');
1170 }
1171 }
1172
1173 /**
1174 * Show import error
1175 */
1176 showImportError(message) {
1177 const popup = document.querySelector('.king-addons-import-progress-popup');
1178 if (!popup) return;
1179
1180 const progressText = popup.querySelector('.king-addons-import-progress-text');
1181 progressText.textContent = message;
1182 progressText.classList.add('king-addons-import-error');
1183
1184 // Auto-close after 3 seconds
1185 setTimeout(() => {
1186 this.closeImportProgress();
1187 }, 3000);
1188 }
1189
1190 /**
1191 * Close import progress popup
1192 */
1193 closeImportProgress() {
1194 const popup = document.querySelector('.king-addons-import-progress-popup');
1195 if (popup) {
1196 popup.classList.remove('show');
1197 setTimeout(() => popup.remove(), 300);
1198 }
1199 }
1200
1201 /**
1202 * Start import using original system adapted for current page
1203 */
1204 startOriginalStyleImport(templateData, pageId) {
1205 const imageCount = templateData.images ? templateData.images.length : 0;
1206 this.totalImages = imageCount;
1207 this.currentImageProgress = 0;
1208 this.pageId = pageId;
1209
1210 this.updateImportProgress(60, `Setting up import session for ${imageCount} images...`);
1211
1212 // Use original import_elementor_page_with_images but modify data for current page
1213 const modifiedData = {
1214 ...templateData,
1215 existing_page_id: pageId, // Signal that we want to add to existing page
1216 create_new_page: false
1217 };
1218
1219 const formData = new URLSearchParams();
1220 formData.append('action', 'import_elementor_page_with_images');
1221 formData.append('nonce', window.kingAddonsTemplateCatalog.nonce);
1222 formData.append('data', JSON.stringify(modifiedData));
1223
1224 fetch(window.kingAddonsTemplateCatalog.ajaxUrl, {
1225 method: 'POST',
1226 body: formData
1227 })
1228 .then(response => {
1229 if (!response.ok) {
1230 return response.text().then(html => {
1231 console.error('Server error:\n' + html);
1232 throw new Error('Server error (not JSON).');
1233 });
1234 }
1235 return response.json();
1236 })
1237 .then(data => {
1238 if (data.success) {
1239 this.updateImportProgress(70, `Import initialized! Processing ${imageCount} images...`);
1240
1241 if (imageCount > 0) {
1242 this.processOriginalStyleImages();
1243 } else {
1244 // No images, proceed directly to finalization
1245 this.finalizeOriginalStyleImport();
1246 }
1247 } else {
1248 this.showImportError(data.data || 'Failed to initialize import');
1249 }
1250 })
1251 .catch(error => {
1252 console.error('Error starting import:', error);
1253 this.showImportError('Failed to start import: ' + error.message);
1254 });
1255 }
1256
1257 /**
1258 * Process images using original system
1259 */
1260 processOriginalStyleImages() {
1261 const formData = new URLSearchParams();
1262 formData.append('action', 'process_import_images');
1263 formData.append('nonce', window.kingAddonsTemplateCatalog.nonce);
1264
1265 fetch(window.kingAddonsTemplateCatalog.ajaxUrl, {
1266 method: 'POST',
1267 body: formData
1268 })
1269 .then(response => {
1270 if (!response.ok) {
1271 return response.text().then(html => {
1272 console.error('Server error:\n' + html);
1273 throw new Error('Server error:\n' + html);
1274 });
1275 }
1276 return response.json();
1277 })
1278 .then(data => {
1279 if (data.success) {
1280 if (data.data.progress !== undefined) {
1281 // Continue processing images
1282 const progress = data.data.progress;
1283 const message = data.data.message || 'Processing images...';
1284
1285 // Track image progress
1286 if (data.data.image_url) {
1287 this.currentImageProgress++;
1288 console.log(`📷 Processed image ${this.currentImageProgress}/${this.totalImages}: ${data.data.image_url}`);
1289 }
1290
1291 // Update progress (70% to 85% for image processing)
1292 const imageProgress = Math.round(70 + (progress / 100) * 15);
1293 this.updateImportProgress(
1294 imageProgress,
1295 `Processing images: ${this.currentImageProgress}/${this.totalImages} (${Math.round(progress)}%)`
1296 );
1297
1298 // Continue processing
1299 setTimeout(() => this.processOriginalStyleImages(), 300);
1300 } else {
1301 // Images completed, check if it's for existing page
1302 if (data.data.processing_complete) {
1303 console.log(`📷 Image processing complete: ${this.currentImageProgress}/${this.totalImages} for existing page`);
1304 this.finalizeOriginalStyleImport();
1305 } else {
1306 // Original behavior - page created
1307 console.log(`📷 Image processing complete: ${this.currentImageProgress}/${this.totalImages} - new page created`);
1308 this.handleNewPageCreated(data.data);
1309 }
1310 }
1311 } else {
1312 // Handle retry logic
1313 if (data.data && data.data.retry) {
1314 console.log('⚠️ Retrying image processing...');
1315 setTimeout(() => this.processOriginalStyleImages(), 1000);
1316 } else {
1317 this.showImportError(data.data || 'Image processing failed');
1318 }
1319 }
1320 })
1321 .catch(error => {
1322 console.error('Error processing images:', error);
1323 this.showImportError('Image processing error: ' + error.message);
1324 });
1325 }
1326
1327 /**
1328 * Finalize import by merging with current page
1329 */
1330 finalizeOriginalStyleImport() {
1331 this.updateImportProgress(85, 'Merging template with current page...');
1332
1333 // Use custom endpoint to merge with existing page instead of creating new one
1334 const formData = new URLSearchParams();
1335 formData.append('action', 'king_addons_merge_with_existing_page');
1336 formData.append('nonce', window.kingAddonsTemplateCatalog.nonce);
1337 formData.append('page_id', this.pageId);
1338
1339 fetch(window.kingAddonsTemplateCatalog.ajaxUrl, {
1340 method: 'POST',
1341 body: formData
1342 })
1343 .then(response => response.json())
1344 .then(data => {
1345 if (data.success) {
1346 const result = data.data;
1347 const importedCount = result.imported_elements || 0;
1348 const imagesProcessed = this.currentImageProgress;
1349
1350 this.updateImportProgress(90, 'Content merged! Refreshing editor preview...');
1351
1352 console.log('📊 Final Import Statistics:', {
1353 'Elements imported': importedCount,
1354 'Images processed': imagesProcessed,
1355 'Page ID': this.pageId
1356 });
1357
1358 let successMessage = `🎉 Template imported successfully! Added ${importedCount} elements`;
1359 if (imagesProcessed > 0) {
1360 successMessage += ` and ${imagesProcessed} images`;
1361 }
1362 successMessage += ' to your page.';
1363
1364 this.updateImportProgress(100, successMessage, true);
1365
1366 setTimeout(() => {
1367 this.closeImportProgress();
1368
1369 // Full page reload to properly show imported content
1370 console.log('Template imported successfully! Reloading page to show content...');
1371 window.location.reload();
1372 }, 3000);
1373 } else {
1374 this.showImportError(data.data || 'Failed to merge template with page');
1375 }
1376 })
1377 .catch(error => {
1378 console.error('Error finalizing import:', error);
1379 this.showImportError('Finalization error: ' + error.message);
1380 });
1381 }
1382
1383 /**
1384 * Handle new page creation (fallback scenario)
1385 */
1386 handleNewPageCreated(data) {
1387 this.updateImportProgress(100, 'New page created successfully!', true);
1388
1389 setTimeout(() => {
1390 this.closeImportProgress();
1391 this.closeTemplatePopup();
1392
1393 // Ask user if they want to open the new page
1394 if (data.page_url) {
1395 const openPage = confirm('New page created successfully! Do you want to open it in Elementor?');
1396 if (openPage) {
1397 const editUrl = data.page_url.replace(/\/$/, '') + '/?elementor';
1398 window.open(editUrl, '_blank');
1399 }
1400 }
1401 }, 2000);
1402 }
1403
1404
1405
1406 /**
1407 * Safely reload Elementor preview
1408 */
1409 reloadElementorPreview(callback) {
1410 try {
1411 // Method 1: Try to use Elementor's built-in refresh
1412 if (elementor && elementor.getPreviewView && typeof elementor.getPreviewView === 'function') {
1413 const previewView = elementor.getPreviewView();
1414
1415 if (previewView && previewView.$el && previewView.$el.length > 0) {
1416 const iframe = previewView.$el[0];
1417
1418 if (iframe && iframe.contentWindow && iframe.contentWindow.location) {
1419 console.log('Reloading preview via iframe.contentWindow.location.reload()');
1420 iframe.contentWindow.location.reload();
1421
1422 // Wait for reload and execute callback
1423 if (callback) {
1424 setTimeout(callback, 1500);
1425 }
1426 return;
1427 }
1428 }
1429 }
1430
1431 // Method 2: Try to find preview iframe by selector
1432 const previewFrame = document.querySelector('#elementor-preview-iframe');
1433 if (previewFrame && previewFrame.contentWindow && previewFrame.contentWindow.location) {
1434 console.log('Reloading preview via querySelector iframe');
1435 previewFrame.contentWindow.location.reload();
1436
1437 if (callback) {
1438 setTimeout(callback, 1500);
1439 }
1440 return;
1441 }
1442
1443 // Method 3: Try to use Elementor's saver to refresh content
1444 if (elementor && elementor.saver && typeof elementor.saver.reload === 'function') {
1445 console.log('Reloading preview via elementor.saver.reload()');
1446 elementor.saver.reload();
1447
1448 if (callback) {
1449 setTimeout(callback, 1000);
1450 }
1451 return;
1452 }
1453
1454 // Method 4: Try to trigger Elementor's preview refresh event
1455 if (elementor && elementor.channels && elementor.channels.editor) {
1456 console.log('Triggering preview refresh via Elementor channels');
1457 elementor.channels.editor.trigger('preview:reload');
1458
1459 if (callback) {
1460 setTimeout(callback, 1000);
1461 }
1462 return;
1463 }
1464
1465 // Method 5: Fallback - just execute callback without reload
1466 console.log('No preview reload method available, proceeding without reload');
1467 if (callback) {
1468 callback();
1469 }
1470
1471 } catch (error) {
1472 console.error('Error reloading preview:', error);
1473 if (callback) {
1474 callback();
1475 }
1476 }
1477 }
1478
1479 /**
1480 * Create new page with template (fallback method)
1481 */
1482 createNewPageWithTemplate(templateData) {
1483 this.updateImportProgress(30, 'Creating new page...');
1484
1485 // Use the existing template import system (like the original catalog)
1486 const formData = new FormData();
1487 formData.append('action', 'import_elementor_page_with_images');
1488 formData.append('nonce', window.kingAddonsTemplateCatalog.nonce);
1489 formData.append('data', JSON.stringify(templateData));
1490
1491 fetch(window.kingAddonsTemplateCatalog.ajaxUrl, {
1492 method: 'POST',
1493 body: formData
1494 })
1495 .then(response => response.json())
1496 .then(data => {
1497 if (data.success) {
1498 this.updateImportProgress(50, 'Processing images...');
1499 this.processImageImport();
1500 } else {
1501 this.showImportError(data.data || 'Failed to create new page');
1502 }
1503 })
1504 .catch(error => {
1505 console.error('Error creating new page:', error);
1506 this.showImportError('Network error occurred while creating new page');
1507 });
1508 }
1509
1510 /**
1511 * Process image import for new page creation
1512 */
1513 processImageImport() {
1514 const formData = new FormData();
1515 formData.append('action', 'process_import_images');
1516 formData.append('nonce', window.kingAddonsTemplateCatalog.nonce);
1517
1518 fetch(window.kingAddonsTemplateCatalog.ajaxUrl, {
1519 method: 'POST',
1520 body: formData
1521 })
1522 .then(response => response.json())
1523 .then(data => {
1524 if (data.success) {
1525 if (data.data.page_url) {
1526 // Final success - page created
1527 this.updateImportProgress(100, 'Page created successfully!', true);
1528
1529 setTimeout(() => {
1530 this.closeImportProgress();
1531 this.closeTemplatePopup();
1532
1533 // Ask user if they want to open the new page
1534 const openPage = confirm('New page created successfully! Do you want to open it in Elementor?');
1535 if (openPage) {
1536 const editUrl = data.data.page_url.replace(/\/$/, '') + '/?elementor';
1537 window.open(editUrl, '_blank');
1538 }
1539 }, 2000);
1540 } else {
1541 // Continue processing images
1542 const progress = Math.min(90, 50 + (data.data.images_processed / data.data.total_images * 40));
1543 this.updateImportProgress(progress, `Processing images... (${data.data.images_processed}/${data.data.total_images})`);
1544
1545 // Continue processing
1546 setTimeout(() => this.processImageImport(), 500);
1547 }
1548 } else {
1549 this.showImportError(data.data || 'Failed to process images');
1550 }
1551 })
1552 .catch(error => {
1553 console.error('Error processing images:', error);
1554 this.showImportError('Network error occurred during image processing');
1555 });
1556 }
1557
1558 /**
1559 * Observe panel changes to re-add button if needed
1560 */
1561 observePanelChanges() {
1562 // Observe changes in the main editor
1563 const panel = document.querySelector('#elementor-panel');
1564 if (panel) {
1565 const observer = new MutationObserver((mutations) => {
1566 let shouldCheck = false;
1567
1568 mutations.forEach((mutation) => {
1569 if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
1570 shouldCheck = true;
1571 }
1572 });
1573
1574 if (shouldCheck) {
1575 // Reset flag when content changes significantly
1576 this.resetButtonFlag();
1577 setTimeout(() => this.addButton(), 300);
1578 }
1579 });
1580
1581 observer.observe(panel, {
1582 childList: true,
1583 subtree: true
1584 });
1585 }
1586
1587 // Also observe the preview iframe for content changes
1588 this.observePreviewChanges();
1589 }
1590
1591 /**
1592 * Observe preview iframe changes
1593 */
1594 observePreviewChanges() {
1595 const preview = document.querySelector('#elementor-preview-iframe');
1596 if (!preview) {
1597 // Retry later if iframe not ready
1598 setTimeout(() => this.observePreviewChanges(), 1000);
1599 return;
1600 }
1601
1602 // Wait for iframe to load
1603 preview.addEventListener('load', () => {
1604 const previewDoc = preview.contentDocument || preview.contentWindow.document;
1605 if (!previewDoc) return;
1606
1607 // Reset button flag when iframe loads (new page/content)
1608 this.resetButtonFlag();
1609
1610 // Add button immediately when iframe loads
1611 setTimeout(() => this.addButton(), 500);
1612
1613 const observer = new MutationObserver((mutations) => {
1614 let shouldReposition = false;
1615
1616 mutations.forEach((mutation) => {
1617 // Check if "Add New Section" was added/removed
1618 if (mutation.type === 'childList') {
1619 mutation.addedNodes.forEach(node => {
1620 if (node.nodeType === 1 &&
1621 (node.id === 'elementor-add-new-section' ||
1622 node.querySelector && node.querySelector('#elementor-add-new-section'))) {
1623 shouldReposition = true;
1624 }
1625 });
1626
1627 mutation.removedNodes.forEach(node => {
1628 if (node.nodeType === 1 &&
1629 (node.id === 'elementor-add-new-section' ||
1630 node.querySelector && node.querySelector('#elementor-add-new-section'))) {
1631 shouldReposition = true;
1632 }
1633 });
1634 }
1635 });
1636
1637 if (shouldReposition) {
1638 // Remove existing button first
1639 const existingButton = previewDoc.querySelector('.king-addons-template-catalog-content-area');
1640 if (existingButton) {
1641 existingButton.remove();
1642 }
1643
1644 // Re-add in correct position
1645 setTimeout(() => this.addButton(), 200);
1646 }
1647 });
1648
1649 observer.observe(previewDoc.body, {
1650 childList: true,
1651 subtree: true
1652 });
1653 });
1654
1655 // Also check periodically to ensure button is in correct position
1656 setInterval(() => {
1657 const previewDoc = preview.contentDocument || preview.contentWindow.document;
1658 if (previewDoc) {
1659 const addNewSection = previewDoc.querySelector('#elementor-add-new-section');
1660 const existingButton = previewDoc.querySelector('.king-addons-template-catalog-content-area');
1661
1662 // If "Add New Section" exists but button is not positioned after it
1663 if (addNewSection && existingButton) {
1664 const nextSibling = addNewSection.nextSibling;
1665 if (nextSibling !== existingButton) {
1666 existingButton.remove();
1667 this.resetButtonFlag();
1668 this.addButton();
1669 }
1670 }
1671 // If "Add New Section" exists but no button exists, add it
1672 else if (addNewSection && !existingButton) {
1673 this.resetButtonFlag();
1674 this.addButton();
1675 }
1676 // If no "Add New Section" exists but button exists, remove button
1677 else if (!addNewSection && existingButton) {
1678 existingButton.remove();
1679 this.resetButtonFlag();
1680 }
1681 }
1682 }, 3000);
1683 }
1684
1685 /**
1686 * Switch popup tab
1687 */
1688 switchPopupTab(tabId) {
1689 const popup = document.querySelector('.king-addons-template-popup');
1690
1691 // Remove active class from all tabs and content
1692 popup.querySelectorAll('.king-addons-popup-tab-button').forEach(btn => btn.classList.remove('active'));
1693 popup.querySelectorAll('.king-addons-popup-tab-content').forEach(content => content.classList.remove('active'));
1694
1695 // Add active class to selected tab and content
1696 popup.querySelector(`[data-tab="${tabId}"]`).classList.add('active');
1697 popup.querySelector(`#${tabId}-tab`).classList.add('active');
1698
1699 // Load data for the tab if needed
1700 if (tabId === 'sections' && !this.sectionsLoaded) {
1701 this.loadSectionsCatalog();
1702 }
1703 }
1704
1705 /**
1706 * Load sections catalog data via AJAX
1707 */
1708 loadSectionsCatalog() {
1709 if (this.isSectionsLoading) return;
1710
1711 this.isSectionsLoading = true;
1712
1713 const popup = document.querySelector('.king-addons-template-popup');
1714 const bodyElement = popup.querySelector('.king-addons-sections-popup-body');
1715
1716 bodyElement.innerHTML = `
1717 <div class="king-addons-sections-popup-loading">
1718 <div class="king-addons-template-spinner"></div>
1719 Loading sections...
1720 </div>
1721 `;
1722
1723 const formData = new FormData();
1724 formData.append('action', 'king_addons_get_sections_catalog');
1725 formData.append('nonce', window.kingAddonsTemplateCatalog.nonce);
1726 formData.append('search', this.currentSectionsFilters.search || '');
1727 formData.append('category', this.currentSectionsFilters.category || '');
1728 formData.append('section_type', this.currentSectionsFilters.section_type || '');
1729 formData.append('plan', this.currentSectionsFilters.plan || '');
1730 formData.append('page', this.currentSectionsPage);
1731
1732 fetch(window.kingAddonsTemplateCatalog.ajaxUrl, {
1733 method: 'POST',
1734 body: formData
1735 })
1736 .then(response => response.json())
1737 .then(data => {
1738 this.isSectionsLoading = false;
1739
1740 if (data.success) {
1741 this.sectionsData = data.data;
1742 this.sectionsLoaded = true;
1743 this.renderSectionsGrid();
1744 this.updateSectionsFilters();
1745 this.updateSectionsCount();
1746 } else {
1747 bodyElement.innerHTML = `
1748 <div class="king-addons-sections-popup-empty">
1749 Error loading sections: ${data.data || 'Unknown error'}
1750 </div>
1751 `;
1752 }
1753 })
1754 .catch(error => {
1755 this.isSectionsLoading = false;
1756 console.error('Error loading sections:', error);
1757 bodyElement.innerHTML = `
1758 <div class="king-addons-sections-popup-empty">
1759 Failed to load sections. Please try again.
1760 </div>
1761 `;
1762 });
1763 }
1764
1765 /**
1766 * Render sections grid
1767 */
1768 renderSectionsGrid() {
1769 if (!this.sectionsData) return;
1770
1771 const popup = document.querySelector('.king-addons-template-popup');
1772 const bodyElement = popup.querySelector('.king-addons-sections-popup-body');
1773
1774 if (!this.sectionsData.sections || this.sectionsData.sections.length === 0) {
1775 bodyElement.innerHTML = `
1776 <div class="king-addons-sections-popup-empty">
1777 No sections found. Try adjusting your search or filters.
1778 </div>
1779 `;
1780 // Update sections count even when empty
1781 this.updateSectionsCount();
1782 return;
1783 }
1784
1785 const grid = document.createElement('div');
1786 grid.className = 'king-addons-sections-grid';
1787
1788 this.sectionsData.sections.forEach(section => {
1789 const item = document.createElement('div');
1790 item.className = 'king-addons-section-item';
1791 item.dataset.sectionKey = section.section_key;
1792 item.dataset.sectionPlan = section.plan;
1793
1794 // Use the correct screenshot URL pattern with plan-based paths
1795 const screenshotUrl = `https://thumbnails.kingaddons.com/sections/${section.plan}/${section.section_key}.png?v=4`;
1796
1797 item.innerHTML = `
1798 <img class="king-addons-section-item-image"
1799 src="${screenshotUrl}"
1800 alt="${section.title}"
1801 loading="lazy"
1802 onerror="this.src='data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzAwIiBoZWlnaHQ9IjE4MCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZGVmcz48bGluZWFyR3JhZGllbnQgaWQ9ImciIHgxPSIwJSIgeTE9IjAlIiB4Mj0iMTAwJSIgeTI9IjEwMCUiPjxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9IiNmOGY5ZmEiLz48c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiNlNWU3ZWIiLz48L2xpbmVhckdyYWRpZW50PjwvZGVmcz48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJ1cmwoI2cpIi8+PGNpcmNsZSBjeD0iMTUwIiBjeT0iNzAiIHI9IjE2IiBmaWxsPSIjOWNhM2FmIiBvcGFjaXR5PSIwLjQiLz48cmVjdCB4PSIxMzQiIHk9Ijg2IiB3aWR0aD0iMzIiIGhlaWdodD0iNCIgZmlsbD0iIzljYTNhZiIgb3BhY2l0eT0iMC40IiByeD0iMiIvPjxyZWN0IHg9IjEyNiIgeT0iOTQiIHdpZHRoPSI0OCIgaGVpZ2h0PSI0IiBmaWxsPSIjOWNhM2FmIiBvcGFjaXR5PSIwLjMiIHJ4PSIyIi8+PHRleHQgeD0iNTAlIiB5PSIxMjAiIGZvbnQtZmFtaWx5PSItYXBwbGUtc3lzdGVtLCBCbGlua01hY1N5c3RlbUZvbnQsIHNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTIiIGZpbGw9IiM2Yjc1ODQiIHRleHQtYW5jaG9yPSJtaWRkbGUiIG9wYWNpdHk9IjAuNyI+U2VjdGlvbiBQcmV2aWV3PC90ZXh0Pjwvc3ZnPg=='" />
1803 <div class="king-addons-section-item-content">
1804 <h3 class="king-addons-section-item-title">${section.title}</h3>
1805 </div>
1806 <div class="king-addons-section-item-plan ${section.plan}">${section.plan}</div>
1807 <div class="king-addons-section-item-overlay">
1808 <div class="king-addons-section-item-actions">
1809 <button class="king-addons-section-import-btn" data-section-key="${section.section_key}" data-section-plan="${section.plan}">
1810 Import Section
1811 </button>
1812 <a href="https://sections.kingaddons.com/${section.section_key}" class="king-addons-section-preview-btn" target="_blank">
1813 Live Preview
1814 </a>
1815 </div>
1816 </div>
1817 `;
1818
1819 grid.appendChild(item);
1820 });
1821
1822 // Add event listeners for section actions
1823 grid.querySelectorAll('.king-addons-section-import-btn').forEach(btn => {
1824 btn.addEventListener('click', (e) => {
1825 e.stopPropagation();
1826 const sectionKey = e.target.dataset.sectionKey;
1827 const sectionPlan = e.target.dataset.sectionPlan;
1828 this.importSection(sectionKey, sectionPlan);
1829 });
1830 });
1831
1832 // Create pagination
1833 const pagination = this.createSectionsPagination();
1834
1835 bodyElement.innerHTML = '';
1836 bodyElement.appendChild(grid);
1837 if (pagination) {
1838 bodyElement.appendChild(pagination);
1839 }
1840
1841 // Update sections count in tab
1842 this.updateSectionsCount();
1843 }
1844
1845 /**
1846 * Create sections pagination - Smart pagination like main catalog
1847 */
1848 createSectionsPagination() {
1849 if (!this.sectionsData || !this.sectionsData.pagination) return null;
1850
1851 const pagination = this.sectionsData.pagination;
1852 if (pagination.total_pages <= 1) return null;
1853
1854 const paginationDiv = document.createElement('div');
1855 paginationDiv.className = 'king-addons-sections-pagination';
1856
1857 let paginationHtml = '<div class="pagination-inner">';
1858
1859 const current = pagination.current_page;
1860 const total = pagination.total_pages;
1861 const endSize = 3; // Show 3 pages at beginning and end
1862 const midSize = 2; // Show 2 pages around current
1863
1864 // Previous page
1865 if (current > 1) {
1866 paginationHtml += `<a href="#" data-page="${current - 1}">&larr; Previous</a>`;
1867 }
1868
1869 // Smart pagination logic with proper ellipsis
1870 const pages = [];
1871
1872 // Always show first pages
1873 for (let i = 1; i <= Math.min(endSize, total); i++) {
1874 pages.push(i);
1875 }
1876
1877 // Calculate middle range around current page
1878 const start = Math.max(current - midSize, 1);
1879 const end = Math.min(current + midSize, total);
1880
1881 // Add first ellipsis if there's a gap
1882 if (start > endSize + 1) {
1883 pages.push('...');
1884 }
1885
1886 // Add middle pages around current (avoid duplicates with start/end)
1887 for (let i = Math.max(start, endSize + 1); i <= Math.min(end, total - endSize); i++) {
1888 if (pages.indexOf(i) === -1) {
1889 pages.push(i);
1890 }
1891 }
1892
1893 // Add second ellipsis if there's a gap
1894 if (end < total - endSize) {
1895 pages.push('...');
1896 }
1897
1898 // Always show last pages (avoid duplicates)
1899 for (let i = Math.max(total - endSize + 1, endSize + 1); i <= total; i++) {
1900 if (pages.indexOf(i) === -1) {
1901 pages.push(i);
1902 }
1903 }
1904
1905 const uniquePages = pages;
1906
1907 // Render pages
1908 uniquePages.forEach(page => {
1909 if (page === '...') {
1910 paginationHtml += `<span class="dots">…</span>`;
1911 } else if (page === current) {
1912 paginationHtml += `<span class="current">${page}</span>`;
1913 } else {
1914 paginationHtml += `<a href="#" data-page="${page}">${page}</a>`;
1915 }
1916 });
1917
1918 // Next page
1919 if (current < total) {
1920 paginationHtml += `<a href="#" data-page="${current + 1}">Next &rarr;</a>`;
1921 }
1922
1923 paginationHtml += '</div>';
1924 paginationDiv.innerHTML = paginationHtml;
1925
1926 // Add pagination event listeners
1927 paginationDiv.querySelectorAll('a').forEach(link => {
1928 link.addEventListener('click', (e) => {
1929 e.preventDefault();
1930 const page = parseInt(e.target.dataset.page);
1931 if (page) {
1932 this.currentSectionsPage = page;
1933 this.loadSectionsCatalog();
1934 }
1935 });
1936 });
1937
1938 return paginationDiv;
1939 }
1940
1941 /**
1942 * Update sections filters dropdowns
1943 */
1944 updateSectionsFilters() {
1945 if (!this.sectionsData) return;
1946
1947 const popup = document.querySelector('.king-addons-template-popup');
1948
1949 // Update categories dropdown
1950 const categoriesSelect = popup.querySelector('#sections-category-filter');
1951 if (categoriesSelect && this.sectionsData.categories) {
1952 let categoriesHtml = '<option value="">All Categories</option>';
1953 this.sectionsData.categories.forEach(category => {
1954 const displayName = category.charAt(0).toUpperCase() + category.slice(1).replace(/-/g, ' ');
1955 categoriesHtml += `<option value="${category}">${displayName}</option>`;
1956 });
1957 categoriesSelect.innerHTML = categoriesHtml;
1958 }
1959
1960 // Update types dropdown
1961 const typesSelect = popup.querySelector('#sections-type-filter');
1962 if (typesSelect && this.sectionsData.section_types) {
1963 let typesHtml = '<option value="">All Types</option>';
1964 this.sectionsData.section_types.forEach(type => {
1965 const displayName = type.charAt(0).toUpperCase() + type.slice(1).replace(/-/g, ' ');
1966 typesHtml += `<option value="${type}">${displayName}</option>`;
1967 });
1968 typesSelect.innerHTML = typesHtml;
1969 }
1970 }
1971
1972 /**
1973 * Update sections count in tab
1974 */
1975 updateSectionsCount() {
1976 if (this.sectionsData && this.sectionsData.pagination) {
1977 const countElement = document.querySelector('#sections-tab-count');
1978 if (countElement) {
1979 countElement.textContent = this.sectionsData.pagination.total_sections;
1980 }
1981 }
1982 }
1983
1984 /**
1985 * Update templates count in tab
1986 */
1987 updateTemplatesCount() {
1988 if (this.catalogData && this.catalogData.pagination) {
1989 const countElement = document.querySelector('#templates-tab-count');
1990 if (countElement) {
1991 countElement.textContent = this.catalogData.pagination.total_templates;
1992 }
1993 }
1994 }
1995
1996 /**
1997 * Show import success message
1998 */
1999 showImportSuccess(message) {
2000 // Update the import progress popup with success message
2001 const progressPopup = document.querySelector('.king-addons-import-progress-popup');
2002 if (progressPopup) {
2003 const messageElement = progressPopup.querySelector('.king-addons-import-progress-text');
2004 if (messageElement) {
2005 messageElement.innerHTML = `<div class="king-addons-import-success">${message}</div>`;
2006 }
2007
2008 // Auto-hide after 3 seconds
2009 setTimeout(() => {
2010 this.closeImportProgress();
2011 }, 3000);
2012 }
2013 }
2014
2015 /**
2016 * Import selected section into current page
2017 */
2018 importSection(sectionKey, sectionPlan) {
2019 // Check permissions for premium sections
2020 if (sectionPlan === 'premium' && !window.kingAddonsTemplateCatalog.isPremium) {
2021 this.showPremiumPromoPopup();
2022 return;
2023 }
2024
2025 // Show import progress popup
2026 this.showImportProgress();
2027
2028 // Close template catalog popup
2029 this.closeTemplatePopup();
2030
2031 // Get section data
2032 const formData = new FormData();
2033 formData.append('action', 'king_addons_import_section_to_page');
2034 formData.append('nonce', window.kingAddonsTemplateCatalog.nonce);
2035 formData.append('section_key', sectionKey);
2036 formData.append('section_plan', sectionPlan);
2037
2038 this.updateImportProgress(5, `Loading section data...`);
2039
2040 fetch(window.kingAddonsTemplateCatalog.ajaxUrl, {
2041 method: 'POST',
2042 body: formData
2043 })
2044 .then(response => {
2045 this.updateImportProgress(15, 'Received section data, validating...');
2046 return response.json();
2047 })
2048 .then(data => {
2049 if (data.success) {
2050 const sectionData = data.data.section_data;
2051 const imageCount = sectionData.images ? sectionData.images.length : 0;
2052
2053 this.updateImportProgress(25, `Section validated! Found ${imageCount} images to process...`);
2054
2055 console.log('Section data received:', {
2056 title: sectionData.title,
2057 images: imageCount,
2058 hasContent: !!sectionData.content
2059 });
2060
2061 this.processSectionImport(sectionData);
2062 } else {
2063 this.showImportError(data.data || 'Failed to load section data');
2064 }
2065 })
2066 .catch(error => {
2067 console.error('Error fetching section:', error);
2068 this.showImportError('Network error: ' + error.message);
2069 });
2070 }
2071
2072 /**
2073 * Process section import into current Elementor page
2074 */
2075 processSectionImport(sectionData) {
2076 if (!sectionData || !sectionData.content) {
2077 this.showImportError('Invalid section data received');
2078 return;
2079 }
2080
2081 const imageCount = sectionData.images ? sectionData.images.length : 0;
2082 this.updateImportProgress(35, `Starting import process... Preparing ${imageCount} images for download...`);
2083
2084 // Get current page ID from Elementor - try multiple methods
2085 let pageId = null;
2086
2087 // Method 1: elementor.config.post_id
2088 if (elementor && elementor.config && elementor.config.post_id) {
2089 pageId = elementor.config.post_id;
2090 }
2091 // Method 2: elementor.config.document.id
2092 else if (elementor && elementor.config && elementor.config.document && elementor.config.document.id) {
2093 pageId = elementor.config.document.id;
2094 }
2095 // Method 3: Check URL parameters
2096 else {
2097 const urlParams = new URLSearchParams(window.location.search);
2098 const postParam = urlParams.get('post');
2099 if (postParam) {
2100 pageId = parseInt(postParam);
2101 }
2102 }
2103
2104 if (!pageId) {
2105 this.showImportError('Could not determine current page ID for import');
2106 return;
2107 }
2108
2109 this.updateImportProgress(45, `Page ID determined: ${pageId}. Starting section import...`);
2110
2111 // Use existing Templates import system for sections
2112 const importData = {
2113 content: sectionData.content,
2114 images: sectionData.images || [],
2115 title: sectionData.title || 'Imported Section',
2116 elementor_version: sectionData.elementor_version || '3.0.0',
2117 existing_page_id: pageId,
2118 create_new_page: false // Always merge with existing page for sections
2119 };
2120
2121 // Call Templates import system
2122 const importFormData = new FormData();
2123 importFormData.append('action', 'import_elementor_page_with_images');
2124 importFormData.append('nonce', window.kingAddonsTemplateCatalog.nonce);
2125 importFormData.append('data', JSON.stringify(importData));
2126
2127 this.updateImportProgress(55, 'Initializing section import with Templates system...');
2128
2129 fetch(window.kingAddonsTemplateCatalog.ajaxUrl, {
2130 method: 'POST',
2131 body: importFormData
2132 })
2133 .then(response => response.json())
2134 .then(result => {
2135 if (result.success) {
2136 this.updateImportProgress(65, 'Section import initialized! Processing images...');
2137 // Start processing images using existing system
2138 this.processImportImages();
2139 } else {
2140 this.showImportError('Failed to initialize section import: ' + (result.data || 'Unknown error'));
2141 }
2142 })
2143 .catch(error => {
2144 console.error('Error initializing section import:', error);
2145 this.showImportError('Failed to initialize section import: ' + error.message);
2146 });
2147 }
2148
2149 /**
2150 * Finalize section import by merging with current page (same as templates)
2151 */
2152 finalizeSectionImport() {
2153 this.updateImportProgress(85, 'Merging section with current page...');
2154
2155 // Get current page ID - use same method as in processSectionImport
2156 let pageId = null;
2157
2158 if (elementor && elementor.config && elementor.config.post_id) {
2159 pageId = elementor.config.post_id;
2160 } else if (elementor && elementor.config && elementor.config.document && elementor.config.document.id) {
2161 pageId = elementor.config.document.id;
2162 } else {
2163 const urlParams = new URLSearchParams(window.location.search);
2164 const postParam = urlParams.get('post');
2165 if (postParam) {
2166 pageId = parseInt(postParam);
2167 }
2168 }
2169
2170 if (!pageId) {
2171 this.showImportError('Could not determine page ID for final merge');
2172 return;
2173 }
2174
2175 // Use same merge endpoint as templates
2176 const formData = new URLSearchParams();
2177 formData.append('action', 'king_addons_merge_with_existing_page');
2178 formData.append('nonce', window.kingAddonsTemplateCatalog.nonce);
2179 formData.append('page_id', pageId);
2180
2181 fetch(window.kingAddonsTemplateCatalog.ajaxUrl, {
2182 method: 'POST',
2183 body: formData
2184 })
2185 .then(response => response.json())
2186 .then(data => {
2187 if (data.success) {
2188 const result = data.data;
2189 const importedCount = result.imported_elements || 0;
2190
2191 this.updateImportProgress(90, 'Section merged! Refreshing editor preview...');
2192
2193 console.log('📊 Section Import Statistics:', {
2194 'Elements imported': importedCount,
2195 'Page ID': pageId
2196 });
2197
2198 let successMessage = `🎉 Section imported successfully! Added ${importedCount} elements to your page.`;
2199 this.updateImportProgress(100, successMessage, true);
2200
2201 setTimeout(() => {
2202 this.closeImportProgress();
2203
2204 // Full page reload to properly show imported content
2205 // elementor.reloadPreview() doesn't refresh editor data from database
2206 console.log('Section imported successfully! Reloading page to show content...');
2207 window.location.reload();
2208 }, 2000);
2209 } else {
2210 this.showImportError('Failed to merge section with page: ' + (data.data || 'Unknown error'));
2211 }
2212 })
2213 .catch(error => {
2214 console.error('Error finalizing section import:', error);
2215 this.showImportError('Finalization error: ' + error.message);
2216 });
2217 }
2218
2219 /**
2220 * Process import images for sections (reuse existing logic)
2221 */
2222 processImportImages() {
2223 const processNextImage = () => {
2224 const formData = new FormData();
2225 formData.append('action', 'process_import_images');
2226 formData.append('nonce', window.kingAddonsTemplateCatalog.nonce);
2227
2228 fetch(window.kingAddonsTemplateCatalog.ajaxUrl, {
2229 method: 'POST',
2230 body: formData
2231 })
2232 .then(response => response.json())
2233 .then(data => {
2234 if (data.success) {
2235 if (data.data.progress !== undefined) {
2236 // Update progress
2237 const progress = Math.min(65 + (data.data.progress * 0.35), 100); // Scale progress from 65% to 100%
2238 this.updateImportProgress(progress, data.data.message || 'Processing images...');
2239
2240 // Continue processing
2241 setTimeout(processNextImage, 500);
2242 } else {
2243 // Images processed, now finalize section import (merge with page)
2244 this.finalizeSectionImport();
2245 }
2246 } else {
2247 // Continue on error (skip failed images)
2248 if (data.data && data.data.retry) {
2249 setTimeout(processNextImage, 1000);
2250 } else {
2251 this.showImportError('Image processing failed: ' + (data.data || 'Unknown error'));
2252 }
2253 }
2254 })
2255 .catch(error => {
2256 console.error('Error processing images:', error);
2257 // Continue processing other images
2258 setTimeout(processNextImage, 1000);
2259 });
2260 };
2261
2262 processNextImage();
2263 }
2264 }
2265
2266 // Initialize when DOM is ready
2267 $(document).ready(() => {
2268 new TemplateCatalogButton();
2269 });
2270
2271 })(jQuery);
2272