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

2,236 lines 94.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 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('nonce', window.kingAddonsTemplateCatalog.nonce);
1186 formData.append('data', JSON.stringify(modifiedData));
1187
1188 fetch(window.kingAddonsTemplateCatalog.ajaxUrl, {
1189 method: 'POST',
1190 body: formData
1191 })
1192 .then(response => {
1193 if (!response.ok) {
1194 return response.text().then(html => {
1195 console.error('Server error:\n' + html);
1196 throw new Error('Server error (not JSON).');
1197 });
1198 }
1199 return response.json();
1200 })
1201 .then(data => {
1202 if (data.success) {
1203 this.updateImportProgress(70, `Import initialized! Processing ${imageCount} images...`);
1204
1205 if (imageCount > 0) {
1206 this.processOriginalStyleImages();
1207 } else {
1208 // No images, proceed directly to finalization
1209 this.finalizeOriginalStyleImport();
1210 }
1211 } else {
1212 this.showImportError(data.data || 'Failed to initialize import');
1213 }
1214 })
1215 .catch(error => {
1216 console.error('Error starting import:', error);
1217 this.showImportError('Failed to start import: ' + error.message);
1218 });
1219 }
1220
1221 /**
1222 * Process images using original system
1223 */
1224 processOriginalStyleImages() {
1225 const formData = new URLSearchParams();
1226 formData.append('action', 'process_import_images');
1227 formData.append('nonce', window.kingAddonsTemplateCatalog.nonce);
1228
1229 fetch(window.kingAddonsTemplateCatalog.ajaxUrl, {
1230 method: 'POST',
1231 body: formData
1232 })
1233 .then(response => {
1234 if (!response.ok) {
1235 return response.text().then(html => {
1236 console.error('Server error:\n' + html);
1237 throw new Error('Server error:\n' + html);
1238 });
1239 }
1240 return response.json();
1241 })
1242 .then(data => {
1243 if (data.success) {
1244 if (data.data.progress !== undefined) {
1245 // Continue processing images
1246 const progress = data.data.progress;
1247 const message = data.data.message || 'Processing images...';
1248
1249 // Track image progress
1250 if (data.data.image_url) {
1251 this.currentImageProgress++;
1252 console.log(`📷 Processed image ${this.currentImageProgress}/${this.totalImages}: ${data.data.image_url}`);
1253 }
1254
1255 // Update progress (70% to 85% for image processing)
1256 const imageProgress = Math.round(70 + (progress / 100) * 15);
1257 this.updateImportProgress(
1258 imageProgress,
1259 `Processing images: ${this.currentImageProgress}/${this.totalImages} (${Math.round(progress)}%)`
1260 );
1261
1262 // Continue processing
1263 setTimeout(() => this.processOriginalStyleImages(), 300);
1264 } else {
1265 // Images completed, check if it's for existing page
1266 if (data.data.processing_complete) {
1267 console.log(`📷 Image processing complete: ${this.currentImageProgress}/${this.totalImages} for existing page`);
1268 this.finalizeOriginalStyleImport();
1269 } else {
1270 // Original behavior - page created
1271 console.log(`📷 Image processing complete: ${this.currentImageProgress}/${this.totalImages} - new page created`);
1272 this.handleNewPageCreated(data.data);
1273 }
1274 }
1275 } else {
1276 // Handle retry logic
1277 if (data.data && data.data.retry) {
1278 console.log('⚠️ Retrying image processing...');
1279 setTimeout(() => this.processOriginalStyleImages(), 1000);
1280 } else {
1281 this.showImportError(data.data || 'Image processing failed');
1282 }
1283 }
1284 })
1285 .catch(error => {
1286 console.error('Error processing images:', error);
1287 this.showImportError('Image processing error: ' + error.message);
1288 });
1289 }
1290
1291 /**
1292 * Finalize import by merging with current page
1293 */
1294 finalizeOriginalStyleImport() {
1295 this.updateImportProgress(85, 'Merging template with current page...');
1296
1297 // Use custom endpoint to merge with existing page instead of creating new one
1298 const formData = new URLSearchParams();
1299 formData.append('action', 'king_addons_merge_with_existing_page');
1300 formData.append('nonce', window.kingAddonsTemplateCatalog.nonce);
1301 formData.append('page_id', this.pageId);
1302
1303 fetch(window.kingAddonsTemplateCatalog.ajaxUrl, {
1304 method: 'POST',
1305 body: formData
1306 })
1307 .then(response => response.json())
1308 .then(data => {
1309 if (data.success) {
1310 const result = data.data;
1311 const importedCount = result.imported_elements || 0;
1312 const imagesProcessed = this.currentImageProgress;
1313
1314 this.updateImportProgress(90, 'Content merged! Refreshing editor preview...');
1315
1316 console.log('📊 Final Import Statistics:', {
1317 'Elements imported': importedCount,
1318 'Images processed': imagesProcessed,
1319 'Page ID': this.pageId
1320 });
1321
1322 let successMessage = `🎉 Template imported successfully! Added ${importedCount} elements`;
1323 if (imagesProcessed > 0) {
1324 successMessage += ` and ${imagesProcessed} images`;
1325 }
1326 successMessage += ' to your page.';
1327
1328 this.updateImportProgress(100, successMessage, true);
1329
1330 setTimeout(() => {
1331 this.closeImportProgress();
1332
1333 // Full page reload to properly show imported content
1334 console.log('Template imported successfully! Reloading page to show content...');
1335 window.location.reload();
1336 }, 3000);
1337 } else {
1338 this.showImportError(data.data || 'Failed to merge template with page');
1339 }
1340 })
1341 .catch(error => {
1342 console.error('Error finalizing import:', error);
1343 this.showImportError('Finalization error: ' + error.message);
1344 });
1345 }
1346
1347 /**
1348 * Handle new page creation (fallback scenario)
1349 */
1350 handleNewPageCreated(data) {
1351 this.updateImportProgress(100, 'New page created successfully!', true);
1352
1353 setTimeout(() => {
1354 this.closeImportProgress();
1355 this.closeTemplatePopup();
1356
1357 // Ask user if they want to open the new page
1358 if (data.page_url) {
1359 const openPage = confirm('New page created successfully! Do you want to open it in Elementor?');
1360 if (openPage) {
1361 const editUrl = data.page_url.replace(/\/$/, '') + '/?elementor';
1362 window.open(editUrl, '_blank');
1363 }
1364 }
1365 }, 2000);
1366 }
1367
1368
1369
1370 /**
1371 * Safely reload Elementor preview
1372 */
1373 reloadElementorPreview(callback) {
1374 try {
1375 // Method 1: Try to use Elementor's built-in refresh
1376 if (elementor && elementor.getPreviewView && typeof elementor.getPreviewView === 'function') {
1377 const previewView = elementor.getPreviewView();
1378
1379 if (previewView && previewView.$el && previewView.$el.length > 0) {
1380 const iframe = previewView.$el[0];
1381
1382 if (iframe && iframe.contentWindow && iframe.contentWindow.location) {
1383 console.log('Reloading preview via iframe.contentWindow.location.reload()');
1384 iframe.contentWindow.location.reload();
1385
1386 // Wait for reload and execute callback
1387 if (callback) {
1388 setTimeout(callback, 1500);
1389 }
1390 return;
1391 }
1392 }
1393 }
1394
1395 // Method 2: Try to find preview iframe by selector
1396 const previewFrame = document.querySelector('#elementor-preview-iframe');
1397 if (previewFrame && previewFrame.contentWindow && previewFrame.contentWindow.location) {
1398 console.log('Reloading preview via querySelector iframe');
1399 previewFrame.contentWindow.location.reload();
1400
1401 if (callback) {
1402 setTimeout(callback, 1500);
1403 }
1404 return;
1405 }
1406
1407 // Method 3: Try to use Elementor's saver to refresh content
1408 if (elementor && elementor.saver && typeof elementor.saver.reload === 'function') {
1409 console.log('Reloading preview via elementor.saver.reload()');
1410 elementor.saver.reload();
1411
1412 if (callback) {
1413 setTimeout(callback, 1000);
1414 }
1415 return;
1416 }
1417
1418 // Method 4: Try to trigger Elementor's preview refresh event
1419 if (elementor && elementor.channels && elementor.channels.editor) {
1420 console.log('Triggering preview refresh via Elementor channels');
1421 elementor.channels.editor.trigger('preview:reload');
1422
1423 if (callback) {
1424 setTimeout(callback, 1000);
1425 }
1426 return;
1427 }
1428
1429 // Method 5: Fallback - just execute callback without reload
1430 console.log('No preview reload method available, proceeding without reload');
1431 if (callback) {
1432 callback();
1433 }
1434
1435 } catch (error) {
1436 console.error('Error reloading preview:', error);
1437 if (callback) {
1438 callback();
1439 }
1440 }
1441 }
1442
1443 /**
1444 * Create new page with template (fallback method)
1445 */
1446 createNewPageWithTemplate(templateData) {
1447 this.updateImportProgress(30, 'Creating new page...');
1448
1449 // Use the existing template import system (like the original catalog)
1450 const formData = new FormData();
1451 formData.append('action', 'import_elementor_page_with_images');
1452 formData.append('nonce', window.kingAddonsTemplateCatalog.nonce);
1453 formData.append('data', JSON.stringify(templateData));
1454
1455 fetch(window.kingAddonsTemplateCatalog.ajaxUrl, {
1456 method: 'POST',
1457 body: formData
1458 })
1459 .then(response => response.json())
1460 .then(data => {
1461 if (data.success) {
1462 this.updateImportProgress(50, 'Processing images...');
1463 this.processImageImport();
1464 } else {
1465 this.showImportError(data.data || 'Failed to create new page');
1466 }
1467 })
1468 .catch(error => {
1469 console.error('Error creating new page:', error);
1470 this.showImportError('Network error occurred while creating new page');
1471 });
1472 }
1473
1474 /**
1475 * Process image import for new page creation
1476 */
1477 processImageImport() {
1478 const formData = new FormData();
1479 formData.append('action', 'process_import_images');
1480 formData.append('nonce', window.kingAddonsTemplateCatalog.nonce);
1481
1482 fetch(window.kingAddonsTemplateCatalog.ajaxUrl, {
1483 method: 'POST',
1484 body: formData
1485 })
1486 .then(response => response.json())
1487 .then(data => {
1488 if (data.success) {
1489 if (data.data.page_url) {
1490 // Final success - page created
1491 this.updateImportProgress(100, 'Page created successfully!', true);
1492
1493 setTimeout(() => {
1494 this.closeImportProgress();
1495 this.closeTemplatePopup();
1496
1497 // Ask user if they want to open the new page
1498 const openPage = confirm('New page created successfully! Do you want to open it in Elementor?');
1499 if (openPage) {
1500 const editUrl = data.data.page_url.replace(/\/$/, '') + '/?elementor';
1501 window.open(editUrl, '_blank');
1502 }
1503 }, 2000);
1504 } else {
1505 // Continue processing images
1506 const progress = Math.min(90, 50 + (data.data.images_processed / data.data.total_images * 40));
1507 this.updateImportProgress(progress, `Processing images... (${data.data.images_processed}/${data.data.total_images})`);
1508
1509 // Continue processing
1510 setTimeout(() => this.processImageImport(), 500);
1511 }
1512 } else {
1513 this.showImportError(data.data || 'Failed to process images');
1514 }
1515 })
1516 .catch(error => {
1517 console.error('Error processing images:', error);
1518 this.showImportError('Network error occurred during image processing');
1519 });
1520 }
1521
1522 /**
1523 * Observe panel changes to re-add button if needed
1524 */
1525 observePanelChanges() {
1526 // Observe changes in the main editor
1527 const panel = document.querySelector('#elementor-panel');
1528 if (panel) {
1529 const observer = new MutationObserver((mutations) => {
1530 let shouldCheck = false;
1531
1532 mutations.forEach((mutation) => {
1533 if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
1534 shouldCheck = true;
1535 }
1536 });
1537
1538 if (shouldCheck) {
1539 // Reset flag when content changes significantly
1540 this.resetButtonFlag();
1541 setTimeout(() => this.addButton(), 300);
1542 }
1543 });
1544
1545 observer.observe(panel, {
1546 childList: true,
1547 subtree: true
1548 });
1549 }
1550
1551 // Also observe the preview iframe for content changes
1552 this.observePreviewChanges();
1553 }
1554
1555 /**
1556 * Observe preview iframe changes
1557 */
1558 observePreviewChanges() {
1559 const preview = document.querySelector('#elementor-preview-iframe');
1560 if (!preview) {
1561 // Retry later if iframe not ready
1562 setTimeout(() => this.observePreviewChanges(), 1000);
1563 return;
1564 }
1565
1566 // Wait for iframe to load
1567 preview.addEventListener('load', () => {
1568 const previewDoc = preview.contentDocument || preview.contentWindow.document;
1569 if (!previewDoc) return;
1570
1571 // Reset button flag when iframe loads (new page/content)
1572 this.resetButtonFlag();
1573
1574 // Add button immediately when iframe loads
1575 setTimeout(() => this.addButton(), 500);
1576
1577 const observer = new MutationObserver((mutations) => {
1578 let shouldReposition = false;
1579
1580 mutations.forEach((mutation) => {
1581 // Check if "Add New Section" was added/removed
1582 if (mutation.type === 'childList') {
1583 mutation.addedNodes.forEach(node => {
1584 if (node.nodeType === 1 &&
1585 (node.id === 'elementor-add-new-section' ||
1586 node.querySelector && node.querySelector('#elementor-add-new-section'))) {
1587 shouldReposition = true;
1588 }
1589 });
1590
1591 mutation.removedNodes.forEach(node => {
1592 if (node.nodeType === 1 &&
1593 (node.id === 'elementor-add-new-section' ||
1594 node.querySelector && node.querySelector('#elementor-add-new-section'))) {
1595 shouldReposition = true;
1596 }
1597 });
1598 }
1599 });
1600
1601 if (shouldReposition) {
1602 // Remove existing button first
1603 const existingButton = previewDoc.querySelector('.king-addons-template-catalog-content-area');
1604 if (existingButton) {
1605 existingButton.remove();
1606 }
1607
1608 // Re-add in correct position
1609 setTimeout(() => this.addButton(), 200);
1610 }
1611 });
1612
1613 observer.observe(previewDoc.body, {
1614 childList: true,
1615 subtree: true
1616 });
1617 });
1618
1619 // Also check periodically to ensure button is in correct position
1620 setInterval(() => {
1621 const previewDoc = preview.contentDocument || preview.contentWindow.document;
1622 if (previewDoc) {
1623 const addNewSection = previewDoc.querySelector('#elementor-add-new-section');
1624 const existingButton = previewDoc.querySelector('.king-addons-template-catalog-content-area');
1625
1626 // If "Add New Section" exists but button is not positioned after it
1627 if (addNewSection && existingButton) {
1628 const nextSibling = addNewSection.nextSibling;
1629 if (nextSibling !== existingButton) {
1630 existingButton.remove();
1631 this.resetButtonFlag();
1632 this.addButton();
1633 }
1634 }
1635 // If "Add New Section" exists but no button exists, add it
1636 else if (addNewSection && !existingButton) {
1637 this.resetButtonFlag();
1638 this.addButton();
1639 }
1640 // If no "Add New Section" exists but button exists, remove button
1641 else if (!addNewSection && existingButton) {
1642 existingButton.remove();
1643 this.resetButtonFlag();
1644 }
1645 }
1646 }, 3000);
1647 }
1648
1649 /**
1650 * Switch popup tab
1651 */
1652 switchPopupTab(tabId) {
1653 const popup = document.querySelector('.king-addons-template-popup');
1654
1655 // Remove active class from all tabs and content
1656 popup.querySelectorAll('.king-addons-popup-tab-button').forEach(btn => btn.classList.remove('active'));
1657 popup.querySelectorAll('.king-addons-popup-tab-content').forEach(content => content.classList.remove('active'));
1658
1659 // Add active class to selected tab and content
1660 popup.querySelector(`[data-tab="${tabId}"]`).classList.add('active');
1661 popup.querySelector(`#${tabId}-tab`).classList.add('active');
1662
1663 // Load data for the tab if needed
1664 if (tabId === 'sections' && !this.sectionsLoaded) {
1665 this.loadSectionsCatalog();
1666 }
1667 }
1668
1669 /**
1670 * Load sections catalog data via AJAX
1671 */
1672 loadSectionsCatalog() {
1673 if (this.isSectionsLoading) return;
1674
1675 this.isSectionsLoading = true;
1676
1677 const popup = document.querySelector('.king-addons-template-popup');
1678 const bodyElement = popup.querySelector('.king-addons-sections-popup-body');
1679
1680 bodyElement.innerHTML = `
1681 <div class="king-addons-sections-popup-loading">
1682 <div class="king-addons-template-spinner"></div>
1683 Loading sections...
1684 </div>
1685 `;
1686
1687 const formData = new FormData();
1688 formData.append('action', 'king_addons_get_sections_catalog');
1689 formData.append('nonce', window.kingAddonsTemplateCatalog.nonce);
1690 formData.append('search', this.currentSectionsFilters.search || '');
1691 formData.append('category', this.currentSectionsFilters.category || '');
1692 formData.append('section_type', this.currentSectionsFilters.section_type || '');
1693 formData.append('plan', this.currentSectionsFilters.plan || '');
1694 formData.append('page', this.currentSectionsPage);
1695
1696 fetch(window.kingAddonsTemplateCatalog.ajaxUrl, {
1697 method: 'POST',
1698 body: formData
1699 })
1700 .then(response => response.json())
1701 .then(data => {
1702 this.isSectionsLoading = false;
1703
1704 if (data.success) {
1705 this.sectionsData = data.data;
1706 this.sectionsLoaded = true;
1707 this.renderSectionsGrid();
1708 this.updateSectionsFilters();
1709 this.updateSectionsCount();
1710 } else {
1711 bodyElement.innerHTML = `
1712 <div class="king-addons-sections-popup-empty">
1713 Error loading sections: ${data.data || 'Unknown error'}
1714 </div>
1715 `;
1716 }
1717 })
1718 .catch(error => {
1719 this.isSectionsLoading = false;
1720 console.error('Error loading sections:', error);
1721 bodyElement.innerHTML = `
1722 <div class="king-addons-sections-popup-empty">
1723 Failed to load sections. Please try again.
1724 </div>
1725 `;
1726 });
1727 }
1728
1729 /**
1730 * Render sections grid
1731 */
1732 renderSectionsGrid() {
1733 if (!this.sectionsData) return;
1734
1735 const popup = document.querySelector('.king-addons-template-popup');
1736 const bodyElement = popup.querySelector('.king-addons-sections-popup-body');
1737
1738 if (!this.sectionsData.sections || this.sectionsData.sections.length === 0) {
1739 bodyElement.innerHTML = `
1740 <div class="king-addons-sections-popup-empty">
1741 No sections found. Try adjusting your search or filters.
1742 </div>
1743 `;
1744 // Update sections count even when empty
1745 this.updateSectionsCount();
1746 return;
1747 }
1748
1749 const grid = document.createElement('div');
1750 grid.className = 'king-addons-sections-grid';
1751
1752 this.sectionsData.sections.forEach(section => {
1753 const item = document.createElement('div');
1754 item.className = 'king-addons-section-item';
1755 item.dataset.sectionKey = section.section_key;
1756 item.dataset.sectionPlan = section.plan;
1757
1758 // Use the correct screenshot URL pattern with plan-based paths
1759 const screenshotUrl = `https://thumbnails.kingaddons.com/sections/${section.plan}/${section.section_key}.png?v=4`;
1760
1761 item.innerHTML = `
1762 <img class="king-addons-section-item-image"
1763 src="${screenshotUrl}"
1764 alt="${section.title}"
1765 loading="lazy"
1766 onerror="this.src='data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzAwIiBoZWlnaHQ9IjE4MCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZGVmcz48bGluZWFyR3JhZGllbnQgaWQ9ImciIHgxPSIwJSIgeTE9IjAlIiB4Mj0iMTAwJSIgeTI9IjEwMCUiPjxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9IiNmOGY5ZmEiLz48c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiNlNWU3ZWIiLz48L2xpbmVhckdyYWRpZW50PjwvZGVmcz48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJ1cmwoI2cpIi8+PGNpcmNsZSBjeD0iMTUwIiBjeT0iNzAiIHI9IjE2IiBmaWxsPSIjOWNhM2FmIiBvcGFjaXR5PSIwLjQiLz48cmVjdCB4PSIxMzQiIHk9Ijg2IiB3aWR0aD0iMzIiIGhlaWdodD0iNCIgZmlsbD0iIzljYTNhZiIgb3BhY2l0eT0iMC40IiByeD0iMiIvPjxyZWN0IHg9IjEyNiIgeT0iOTQiIHdpZHRoPSI0OCIgaGVpZ2h0PSI0IiBmaWxsPSIjOWNhM2FmIiBvcGFjaXR5PSIwLjMiIHJ4PSIyIi8+PHRleHQgeD0iNTAlIiB5PSIxMjAiIGZvbnQtZmFtaWx5PSItYXBwbGUtc3lzdGVtLCBCbGlua01hY1N5c3RlbUZvbnQsIHNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTIiIGZpbGw9IiM2Yjc1ODQiIHRleHQtYW5jaG9yPSJtaWRkbGUiIG9wYWNpdHk9IjAuNyI+U2VjdGlvbiBQcmV2aWV3PC90ZXh0Pjwvc3ZnPg=='" />
1767 <div class="king-addons-section-item-content">
1768 <h3 class="king-addons-section-item-title">${section.title}</h3>
1769 </div>
1770 <div class="king-addons-section-item-plan ${section.plan}">${section.plan}</div>
1771 <div class="king-addons-section-item-overlay">
1772 <div class="king-addons-section-item-actions">
1773 <button class="king-addons-section-import-btn" data-section-key="${section.section_key}" data-section-plan="${section.plan}">
1774 Import Section
1775 </button>
1776 <a href="https://sections.kingaddons.com/${section.section_key}" class="king-addons-section-preview-btn" target="_blank">
1777 Live Preview
1778 </a>
1779 </div>
1780 </div>
1781 `;
1782
1783 grid.appendChild(item);
1784 });
1785
1786 // Add event listeners for section actions
1787 grid.querySelectorAll('.king-addons-section-import-btn').forEach(btn => {
1788 btn.addEventListener('click', (e) => {
1789 e.stopPropagation();
1790 const sectionKey = e.target.dataset.sectionKey;
1791 const sectionPlan = e.target.dataset.sectionPlan;
1792 this.importSection(sectionKey, sectionPlan);
1793 });
1794 });
1795
1796 // Create pagination
1797 const pagination = this.createSectionsPagination();
1798
1799 bodyElement.innerHTML = '';
1800 bodyElement.appendChild(grid);
1801 if (pagination) {
1802 bodyElement.appendChild(pagination);
1803 }
1804
1805 // Update sections count in tab
1806 this.updateSectionsCount();
1807 }
1808
1809 /**
1810 * Create sections pagination - Smart pagination like main catalog
1811 */
1812 createSectionsPagination() {
1813 if (!this.sectionsData || !this.sectionsData.pagination) return null;
1814
1815 const pagination = this.sectionsData.pagination;
1816 if (pagination.total_pages <= 1) return null;
1817
1818 const paginationDiv = document.createElement('div');
1819 paginationDiv.className = 'king-addons-sections-pagination';
1820
1821 let paginationHtml = '<div class="pagination-inner">';
1822
1823 const current = pagination.current_page;
1824 const total = pagination.total_pages;
1825 const endSize = 3; // Show 3 pages at beginning and end
1826 const midSize = 2; // Show 2 pages around current
1827
1828 // Previous page
1829 if (current > 1) {
1830 paginationHtml += `<a href="#" data-page="${current - 1}">&larr; Previous</a>`;
1831 }
1832
1833 // Smart pagination logic with proper ellipsis
1834 const pages = [];
1835
1836 // Always show first pages
1837 for (let i = 1; i <= Math.min(endSize, total); i++) {
1838 pages.push(i);
1839 }
1840
1841 // Calculate middle range around current page
1842 const start = Math.max(current - midSize, 1);
1843 const end = Math.min(current + midSize, total);
1844
1845 // Add first ellipsis if there's a gap
1846 if (start > endSize + 1) {
1847 pages.push('...');
1848 }
1849
1850 // Add middle pages around current (avoid duplicates with start/end)
1851 for (let i = Math.max(start, endSize + 1); i <= Math.min(end, total - endSize); i++) {
1852 if (pages.indexOf(i) === -1) {
1853 pages.push(i);
1854 }
1855 }
1856
1857 // Add second ellipsis if there's a gap
1858 if (end < total - endSize) {
1859 pages.push('...');
1860 }
1861
1862 // Always show last pages (avoid duplicates)
1863 for (let i = Math.max(total - endSize + 1, endSize + 1); i <= total; i++) {
1864 if (pages.indexOf(i) === -1) {
1865 pages.push(i);
1866 }
1867 }
1868
1869 const uniquePages = pages;
1870
1871 // Render pages
1872 uniquePages.forEach(page => {
1873 if (page === '...') {
1874 paginationHtml += `<span class="dots">…</span>`;
1875 } else if (page === current) {
1876 paginationHtml += `<span class="current">${page}</span>`;
1877 } else {
1878 paginationHtml += `<a href="#" data-page="${page}">${page}</a>`;
1879 }
1880 });
1881
1882 // Next page
1883 if (current < total) {
1884 paginationHtml += `<a href="#" data-page="${current + 1}">Next &rarr;</a>`;
1885 }
1886
1887 paginationHtml += '</div>';
1888 paginationDiv.innerHTML = paginationHtml;
1889
1890 // Add pagination event listeners
1891 paginationDiv.querySelectorAll('a').forEach(link => {
1892 link.addEventListener('click', (e) => {
1893 e.preventDefault();
1894 const page = parseInt(e.target.dataset.page);
1895 if (page) {
1896 this.currentSectionsPage = page;
1897 this.loadSectionsCatalog();
1898 }
1899 });
1900 });
1901
1902 return paginationDiv;
1903 }
1904
1905 /**
1906 * Update sections filters dropdowns
1907 */
1908 updateSectionsFilters() {
1909 if (!this.sectionsData) return;
1910
1911 const popup = document.querySelector('.king-addons-template-popup');
1912
1913 // Update categories dropdown
1914 const categoriesSelect = popup.querySelector('#sections-category-filter');
1915 if (categoriesSelect && this.sectionsData.categories) {
1916 let categoriesHtml = '<option value="">All Categories</option>';
1917 this.sectionsData.categories.forEach(category => {
1918 const displayName = category.charAt(0).toUpperCase() + category.slice(1).replace(/-/g, ' ');
1919 categoriesHtml += `<option value="${category}">${displayName}</option>`;
1920 });
1921 categoriesSelect.innerHTML = categoriesHtml;
1922 }
1923
1924 // Update types dropdown
1925 const typesSelect = popup.querySelector('#sections-type-filter');
1926 if (typesSelect && this.sectionsData.section_types) {
1927 let typesHtml = '<option value="">All Types</option>';
1928 this.sectionsData.section_types.forEach(type => {
1929 const displayName = type.charAt(0).toUpperCase() + type.slice(1).replace(/-/g, ' ');
1930 typesHtml += `<option value="${type}">${displayName}</option>`;
1931 });
1932 typesSelect.innerHTML = typesHtml;
1933 }
1934 }
1935
1936 /**
1937 * Update sections count in tab
1938 */
1939 updateSectionsCount() {
1940 if (this.sectionsData && this.sectionsData.pagination) {
1941 const countElement = document.querySelector('#sections-tab-count');
1942 if (countElement) {
1943 countElement.textContent = this.sectionsData.pagination.total_sections;
1944 }
1945 }
1946 }
1947
1948 /**
1949 * Update templates count in tab
1950 */
1951 updateTemplatesCount() {
1952 if (this.catalogData && this.catalogData.pagination) {
1953 const countElement = document.querySelector('#templates-tab-count');
1954 if (countElement) {
1955 countElement.textContent = this.catalogData.pagination.total_templates;
1956 }
1957 }
1958 }
1959
1960 /**
1961 * Show import success message
1962 */
1963 showImportSuccess(message) {
1964 // Update the import progress popup with success message
1965 const progressPopup = document.querySelector('.king-addons-import-progress-popup');
1966 if (progressPopup) {
1967 const messageElement = progressPopup.querySelector('.king-addons-import-progress-text');
1968 if (messageElement) {
1969 messageElement.innerHTML = `<div class="king-addons-import-success">${message}</div>`;
1970 }
1971
1972 // Auto-hide after 3 seconds
1973 setTimeout(() => {
1974 this.closeImportProgress();
1975 }, 3000);
1976 }
1977 }
1978
1979 /**
1980 * Import selected section into current page
1981 */
1982 importSection(sectionKey, sectionPlan) {
1983 // Check permissions for premium sections
1984 if (sectionPlan === 'premium' && !window.kingAddonsTemplateCatalog.isPremium) {
1985 this.showPremiumPromoPopup();
1986 return;
1987 }
1988
1989 // Show import progress popup
1990 this.showImportProgress();
1991
1992 // Close template catalog popup
1993 this.closeTemplatePopup();
1994
1995 // Get section data
1996 const formData = new FormData();
1997 formData.append('action', 'king_addons_import_section_to_page');
1998 formData.append('nonce', window.kingAddonsTemplateCatalog.nonce);
1999 formData.append('section_key', sectionKey);
2000 formData.append('section_plan', sectionPlan);
2001
2002 this.updateImportProgress(5, `Loading section data...`);
2003
2004 fetch(window.kingAddonsTemplateCatalog.ajaxUrl, {
2005 method: 'POST',
2006 body: formData
2007 })
2008 .then(response => {
2009 this.updateImportProgress(15, 'Received section data, validating...');
2010 return response.json();
2011 })
2012 .then(data => {
2013 if (data.success) {
2014 const sectionData = data.data.section_data;
2015 const imageCount = sectionData.images ? sectionData.images.length : 0;
2016
2017 this.updateImportProgress(25, `Section validated! Found ${imageCount} images to process...`);
2018
2019 console.log('Section data received:', {
2020 title: sectionData.title,
2021 images: imageCount,
2022 hasContent: !!sectionData.content
2023 });
2024
2025 this.processSectionImport(sectionData);
2026 } else {
2027 this.showImportError(data.data || 'Failed to load section data');
2028 }
2029 })
2030 .catch(error => {
2031 console.error('Error fetching section:', error);
2032 this.showImportError('Network error: ' + error.message);
2033 });
2034 }
2035
2036 /**
2037 * Process section import into current Elementor page
2038 */
2039 processSectionImport(sectionData) {
2040 if (!sectionData || !sectionData.content) {
2041 this.showImportError('Invalid section data received');
2042 return;
2043 }
2044
2045 const imageCount = sectionData.images ? sectionData.images.length : 0;
2046 this.updateImportProgress(35, `Starting import process... Preparing ${imageCount} images for download...`);
2047
2048 // Get current page ID from Elementor - try multiple methods
2049 let pageId = null;
2050
2051 // Method 1: elementor.config.post_id
2052 if (elementor && elementor.config && elementor.config.post_id) {
2053 pageId = elementor.config.post_id;
2054 }
2055 // Method 2: elementor.config.document.id
2056 else if (elementor && elementor.config && elementor.config.document && elementor.config.document.id) {
2057 pageId = elementor.config.document.id;
2058 }
2059 // Method 3: Check URL parameters
2060 else {
2061 const urlParams = new URLSearchParams(window.location.search);
2062 const postParam = urlParams.get('post');
2063 if (postParam) {
2064 pageId = parseInt(postParam);
2065 }
2066 }
2067
2068 if (!pageId) {
2069 this.showImportError('Could not determine current page ID for import');
2070 return;
2071 }
2072
2073 this.updateImportProgress(45, `Page ID determined: ${pageId}. Starting section import...`);
2074
2075 // Use existing Templates import system for sections
2076 const importData = {
2077 content: sectionData.content,
2078 images: sectionData.images || [],
2079 title: sectionData.title || 'Imported Section',
2080 elementor_version: sectionData.elementor_version || '3.0.0',
2081 existing_page_id: pageId,
2082 create_new_page: false // Always merge with existing page for sections
2083 };
2084
2085 // Call Templates import system
2086 const importFormData = new FormData();
2087 importFormData.append('action', 'import_elementor_page_with_images');
2088 importFormData.append('nonce', window.kingAddonsTemplateCatalog.nonce);
2089 importFormData.append('data', JSON.stringify(importData));
2090
2091 this.updateImportProgress(55, 'Initializing section import with Templates system...');
2092
2093 fetch(window.kingAddonsTemplateCatalog.ajaxUrl, {
2094 method: 'POST',
2095 body: importFormData
2096 })
2097 .then(response => response.json())
2098 .then(result => {
2099 if (result.success) {
2100 this.updateImportProgress(65, 'Section import initialized! Processing images...');
2101 // Start processing images using existing system
2102 this.processImportImages();
2103 } else {
2104 this.showImportError('Failed to initialize section import: ' + (result.data || 'Unknown error'));
2105 }
2106 })
2107 .catch(error => {
2108 console.error('Error initializing section import:', error);
2109 this.showImportError('Failed to initialize section import: ' + error.message);
2110 });
2111 }
2112
2113 /**
2114 * Finalize section import by merging with current page (same as templates)
2115 */
2116 finalizeSectionImport() {
2117 this.updateImportProgress(85, 'Merging section with current page...');
2118
2119 // Get current page ID - use same method as in processSectionImport
2120 let pageId = null;
2121
2122 if (elementor && elementor.config && elementor.config.post_id) {
2123 pageId = elementor.config.post_id;
2124 } else if (elementor && elementor.config && elementor.config.document && elementor.config.document.id) {
2125 pageId = elementor.config.document.id;
2126 } else {
2127 const urlParams = new URLSearchParams(window.location.search);
2128 const postParam = urlParams.get('post');
2129 if (postParam) {
2130 pageId = parseInt(postParam);
2131 }
2132 }
2133
2134 if (!pageId) {
2135 this.showImportError('Could not determine page ID for final merge');
2136 return;
2137 }
2138
2139 // Use same merge endpoint as templates
2140 const formData = new URLSearchParams();
2141 formData.append('action', 'king_addons_merge_with_existing_page');
2142 formData.append('nonce', window.kingAddonsTemplateCatalog.nonce);
2143 formData.append('page_id', pageId);
2144
2145 fetch(window.kingAddonsTemplateCatalog.ajaxUrl, {
2146 method: 'POST',
2147 body: formData
2148 })
2149 .then(response => response.json())
2150 .then(data => {
2151 if (data.success) {
2152 const result = data.data;
2153 const importedCount = result.imported_elements || 0;
2154
2155 this.updateImportProgress(90, 'Section merged! Refreshing editor preview...');
2156
2157 console.log('📊 Section Import Statistics:', {
2158 'Elements imported': importedCount,
2159 'Page ID': pageId
2160 });
2161
2162 let successMessage = `🎉 Section imported successfully! Added ${importedCount} elements to your page.`;
2163 this.updateImportProgress(100, successMessage, true);
2164
2165 setTimeout(() => {
2166 this.closeImportProgress();
2167
2168 // Full page reload to properly show imported content
2169 // elementor.reloadPreview() doesn't refresh editor data from database
2170 console.log('Section imported successfully! Reloading page to show content...');
2171 window.location.reload();
2172 }, 2000);
2173 } else {
2174 this.showImportError('Failed to merge section with page: ' + (data.data || 'Unknown error'));
2175 }
2176 })
2177 .catch(error => {
2178 console.error('Error finalizing section import:', error);
2179 this.showImportError('Finalization error: ' + error.message);
2180 });
2181 }
2182
2183 /**
2184 * Process import images for sections (reuse existing logic)
2185 */
2186 processImportImages() {
2187 const processNextImage = () => {
2188 const formData = new FormData();
2189 formData.append('action', 'process_import_images');
2190 formData.append('nonce', window.kingAddonsTemplateCatalog.nonce);
2191
2192 fetch(window.kingAddonsTemplateCatalog.ajaxUrl, {
2193 method: 'POST',
2194 body: formData
2195 })
2196 .then(response => response.json())
2197 .then(data => {
2198 if (data.success) {
2199 if (data.data.progress !== undefined) {
2200 // Update progress
2201 const progress = Math.min(65 + (data.data.progress * 0.35), 100); // Scale progress from 65% to 100%
2202 this.updateImportProgress(progress, data.data.message || 'Processing images...');
2203
2204 // Continue processing
2205 setTimeout(processNextImage, 500);
2206 } else {
2207 // Images processed, now finalize section import (merge with page)
2208 this.finalizeSectionImport();
2209 }
2210 } else {
2211 // Continue on error (skip failed images)
2212 if (data.data && data.data.retry) {
2213 setTimeout(processNextImage, 1000);
2214 } else {
2215 this.showImportError('Image processing failed: ' + (data.data || 'Unknown error'));
2216 }
2217 }
2218 })
2219 .catch(error => {
2220 console.error('Error processing images:', error);
2221 // Continue processing other images
2222 setTimeout(processNextImage, 1000);
2223 });
2224 };
2225
2226 processNextImage();
2227 }
2228 }
2229
2230 // Initialize when DOM is ready
2231 $(document).ready(() => {
2232 new TemplateCatalogButton();
2233 });
2234
2235 })(jQuery);
2236