PluginProbe
King Addons for Elementor – 100+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce Builder, Mega Menu, Popup Builder / 51.1.14
King Addons for Elementor – 100+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce Builder, Mega Menu, Popup Builder v51.1.14
51.1.87 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 All 41 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.14, at includes/extensions/Template_Catalog_Button/assets/template-catalog-button.js

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