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

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