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

sections-catalog.js in King Addons for Elementor – 100+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce Builder, Mega Menu, Popup Builder 51.1.14, at includes/extensions/Sections_Catalog/assets/sections-catalog.js

601 lines 22.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 // King Addons Sections Catalog JavaScript
2
3 jQuery(document).ready(function ($) {
4 'use strict';
5
6 /**
7 * Класс для управления каталогом секций
8 */
9 class SectionsCatalog {
10 constructor() {
11 this.currentSection = null;
12 this.isImporting = false;
13 this.bindEvents();
14 }
15
16 /**
17 * Привязка событий
18 */
19 bindEvents() {
20 // Клик по секции для предпросмотра
21 $(document).on('click', '.section-item .preview-btn', this.showSectionPreview.bind(this));
22
23 // Клик по кнопке импорта в сетке
24 $(document).on('click', '.section-item .import-btn', this.quickImportSection.bind(this));
25
26 // Клик по секции (альтернативный способ предпросмотра)
27 $(document).on('click', '.section-item', (e) => {
28 if (!$(e.target).hasClass('import-btn') && !$(e.target).hasClass('preview-btn')) {
29 this.showSectionPreview.call(this, e);
30 }
31 });
32
33 // Закрытие попапов
34 $(document).on('click', '#close-section-popup', this.closeSectionPreview.bind(this));
35 $(document).on('click', '#close-import-popup', this.closeImportPopup.bind(this));
36 $(document).on('click', '#close-premium-promo-popup', this.closePremiumPromo.bind(this));
37
38 // Импорт секции из попапа предпросмотра
39 $(document).on('click', '#import-section', this.importSectionFromPreview.bind(this));
40
41 // Поиск и фильтры
42 $(document).on('keyup', '#section-search', this.debounce(this.filterSections.bind(this), 500));
43 $(document).on('change', '#section-category', this.filterSections.bind(this));
44 $(document).on('change', '#section-type', this.filterSections.bind(this));
45 $(document).on('change', '#section-tags input', this.filterSections.bind(this));
46 $(document).on('click', '#reset-filters', this.resetFilters.bind(this));
47
48 // Пагинация
49 $(document).on('click', '.pagination a', this.handlePagination.bind(this));
50
51 // Закрытие попапов по клику вне и�
52
53 $(document).on('click', '.popup-overlay, #section-preview-popup, #section-importing-popup, #premium-promo-popup', (e) => {
54 if (e.target === e.currentTarget) {
55 this.closeSectionPreview();
56 this.closeImportPopup();
57 this.closePremiumPromo();
58 }
59 });
60
61 // ESC для закрытия попапов
62 $(document).on('keydown', (e) => {
63 if (e.key === 'Escape') {
64 this.closeSectionPreview();
65 this.closeImportPopup();
66 this.closePremiumPromo();
67 }
68 });
69 }
70
71 /**
72 * Показать предпросмотр секции
73 */
74 showSectionPreview(e) {
75 e.preventDefault();
76 e.stopPropagation();
77
78 const $sectionItem = $(e.target).closest('.section-item');
79 const sectionKey = $sectionItem.data('section-key');
80 const sectionPlan = $sectionItem.data('section-plan');
81
82 if (!sectionKey) return;
83
84 this.currentSection = {
85 key: sectionKey,
86 plan: sectionPlan,
87 element: $sectionItem
88 };
89
90 // Получаем данные секции
91 this.loadSectionData(sectionKey).then((sectionData) => {
92 if (sectionData) {
93 this.displaySectionPreview(sectionData);
94 }
95 });
96 }
97
98 /**
99 * Загружает данные секции с сервера
100 */
101 async loadSectionData(sectionKey) {
102 try {
103 const response = await $.ajax({
104 url: kingAddonsSectionsData.ajaxUrl,
105 type: 'POST',
106 data: {
107 action: 'get_section_data',
108 section_key: sectionKey,
109 nonce: kingAddonsSectionsData.nonce
110 }
111 });
112
113 if (response.success) {
114 return response.data;
115 } else {
116 console.error('Error loading section data:', response.data);
117 return null;
118 }
119 } catch (error) {
120 console.error('AJAX error:', error);
121 return null;
122 }
123 }
124
125 /**
126 * Отображает предпросмотр секции
127 */
128 displaySectionPreview(sectionData) {
129 const screenshotUrl = kingAddonsSectionsData.screenshotsUrl + this.currentSection.key + '.png';
130
131 // Обновляем попап с данными секции
132 $('#section-preview-image').attr('src', screenshotUrl);
133 $('#section-type-display').text(`Type: ${sectionData.section_type || 'General'}`);
134 $('#section-parent-template').text(`From: ${sectionData.parent_template || 'Template'}`);
135
136 // Настраиваем кнопку импорта
137 const $importBtn = $('#import-section');
138 $importBtn.attr('data-section-key', this.currentSection.key);
139 $importBtn.attr('data-section-plan', this.currentSection.plan);
140
141 if (this.currentSection.plan === 'premium' && !kingAddonsSectionsData.isPremium) {
142 $importBtn.text('Get Premium Access');
143 $importBtn.removeClass('btn-success').addClass('btn-premium');
144 } else {
145 $importBtn.text('Import Section');
146 $importBtn.removeClass('btn-premium').addClass('btn-success');
147 }
148
149 // Показываем попап
150 $('#section-preview-popup').addClass('show');
151 }
152
153 /**
154 * Быстрый импорт секции (без предпросмотра)
155 */
156 quickImportSection(e) {
157 e.preventDefault();
158 e.stopPropagation();
159
160 const $sectionItem = $(e.target).closest('.section-item');
161 const sectionKey = $sectionItem.data('section-key');
162 const sectionPlan = $sectionItem.data('section-plan');
163
164 if (!sectionKey) return;
165
166 // Проверяем права на premium секции
167 if (sectionPlan === 'premium' && !kingAddonsSectionsData.isPremium) {
168 this.showPremiumPromo();
169 return;
170 }
171
172 this.currentSection = {
173 key: sectionKey,
174 plan: sectionPlan,
175 element: $sectionItem
176 };
177
178 this.startSectionImport();
179 }
180
181 /**
182 * Импорт секции из попапа предпросмотра
183 */
184 importSectionFromPreview(e) {
185 e.preventDefault();
186
187 const sectionKey = $(e.target).attr('data-section-key');
188 const sectionPlan = $(e.target).attr('data-section-plan');
189
190 if (!sectionKey) return;
191
192 // Проверяем права на premium секции
193 if (sectionPlan === 'premium' && !kingAddonsSectionsData.isPremium) {
194 this.closeSectionPreview();
195 this.showPremiumPromo();
196 return;
197 }
198
199 this.closeSectionPreview();
200 this.startSectionImport();
201 }
202
203 /**
204 * Начинает процесс импорта секции
205 */
206 async startSectionImport() {
207 if (this.isImporting) return;
208
209 this.isImporting = true;
210
211 // Определяем ID страницы для импорта
212 const pageId = await this.getCurrentPageId();
213 if (!pageId) {
214 this.isImporting = false;
215 alert('Could not determine current page ID. Please save the page first or create a new page.');
216 return;
217 }
218
219 this.showImportProgress();
220 this.updateImportProgress(10, 'Preparing section import...');
221
222 try {
223 // Запускаем импорт секции
224 const response = await $.ajax({
225 url: kingAddonsSectionsData.ajaxUrl,
226 type: 'POST',
227 data: {
228 action: 'import_section',
229 section_key: this.currentSection.key,
230 page_id: pageId,
231 nonce: kingAddonsSectionsData.nonce
232 }
233 });
234
235 if (response.success) {
236 this.updateImportProgress(30, 'Section import started...');
237 this.processImportImages(pageId);
238 } else {
239 throw new Error(response.data || 'Unknown error');
240 }
241 } catch (error) {
242 this.handleImportError(error);
243 }
244 }
245
246 /**
247 * Обрабатывает изображения секции
248 */
249 async processImportImages(pageId) {
250 try {
251 this.updateImportProgress(50, 'Processing section images...');
252
253 const response = await $.ajax({
254 url: kingAddonsSectionsData.ajaxUrl,
255 type: 'POST',
256 data: {
257 action: 'process_import_images'
258 }
259 });
260
261 if (response.success) {
262 if (response.data.progress !== undefined) {
263 // Продолжаем обработку изображений
264 const progress = Math.round(50 + (response.data.progress / 100) * 30);
265 this.updateImportProgress(progress, response.data.message || 'Processing images...');
266
267 // Рекурсивно продолжаем обработку
268 setTimeout(() => this.processImportImages(pageId), 1000);
269 } else if (response.data.processing_complete) {
270 // Завершаем импорт
271 this.finalizeSectionImport(pageId);
272 } else {
273 // Новая страница создана (fallback)
274 this.handleNewPageCreated(response.data);
275 }
276 } else {
277 throw new Error(response.data || 'Image processing failed');
278 }
279 } catch (error) {
280 this.handleImportError(error);
281 }
282 }
283
284 /**
285 * Завершает импорт секции
286 */
287 async finalizeSectionImport(pageId) {
288 try {
289 this.updateImportProgress(85, 'Finalizing section import...');
290
291 const response = await $.ajax({
292 url: kingAddonsSectionsData.ajaxUrl,
293 type: 'POST',
294 data: {
295 action: 'king_addons_merge_with_existing_page',
296 page_id: pageId,
297 nonce: kingAddonsSectionsData.nonce
298 }
299 });
300
301 if (response.success) {
302 this.updateImportProgress(100, 'Section imported successfully!');
303 this.handleImportSuccess(pageId, response.data);
304 } else {
305 throw new Error(response.data || 'Finalization failed');
306 }
307 } catch (error) {
308 this.handleImportError(error);
309 }
310 }
311
312 /**
313 * Обрабатывает успешный импорт
314 */
315 handleImportSuccess(pageId, data) {
316 console.log('�
317 Section imported successfully:', data);
318
319 setTimeout(() => {
320 $('#close-import-popup').show();
321
322 // Показываем информацию об успешном импорте
323 this.updateImportProgress(100,
324 `Section imported! Added ${data.imported_elements || 0} new elements. ` +
325 'Reload the page to see changes or close this dialog.'
326 );
327
328 // Автоматическое закрытие через 3 секунды
329 setTimeout(() => {
330 const reload = confirm('Section imported successfully! Reload the page to see the imported content?');
331 if (reload) {
332 window.location.reload();
333 } else {
334 this.closeImportPopup();
335 }
336 }, 3000);
337 }, 1000);
338 }
339
340 /**
341 * Обрабатывает создание новой страницы
342 */
343 handleNewPageCreated(data) {
344 this.updateImportProgress(100, 'New page created with section!');
345
346 setTimeout(() => {
347 $('#close-import-popup').show();
348
349 if (data.page_url) {
350 const openPage = confirm('Section imported to a new page! Would you like to open it?');
351 if (openPage) {
352 window.open(data.page_url, '_blank');
353 }
354 }
355
356 this.closeImportPopup();
357 }, 2000);
358 }
359
360 /**
361 * Обрабатывает ошибки импорта
362 */
363 handleImportError(error) {
364 console.error('❌ Import error:', error);
365 this.updateImportProgress(0, `Import failed: ${error.message || error}`);
366
367 setTimeout(() => {
368 $('#close-import-popup').show();
369 }, 2000);
370
371 this.isImporting = false;
372 }
373
374 /**
375 * Получает ID текущей страницы
376 */
377 async getCurrentPageId() {
378 // Пытаемся получить ID из различны�
379 источников
380 if (window.elementor && window.elementor.config && window.elementor.config.post_id) {
381 return window.elementor.config.post_id;
382 }
383
384 if (window.elementorFrontend && window.elementorFrontend.config && window.elementorFrontend.config.post.id) {
385 return window.elementorFrontend.config.post.id;
386 }
387
388 // Получаем из URL параметров
389 const urlParams = new URLSearchParams(window.location.search);
390 const postId = urlParams.get('post') || urlParams.get('page_id');
391 if (postId) {
392 return parseInt(postId);
393 }
394
395 // Последняя попытка - запросить у пользователя
396 const pageId = prompt('Please enter the page ID where you want to import this section:');
397 return pageId ? parseInt(pageId) : null;
398 }
399
400 /**
401 * Показывает попап прогресса импорта
402 */
403 showImportProgress() {
404 $('#import-progress-bar').css('width', '0%').text('0%');
405 $('#import-progress-text').text('Preparing import...');
406 $('#close-import-popup').hide();
407 $('#section-importing-popup').addClass('show');
408 }
409
410 /**
411 * Обновляет прогресс импорта
412 */
413 updateImportProgress(percent, message) {
414 $('#import-progress-bar').css('width', percent + '%').text(percent + '%');
415 $('#import-progress-text').text(message);
416 }
417
418 /**
419 * Показывает премиум промо
420 */
421 showPremiumPromo() {
422 $('#premium-promo-popup').addClass('show');
423 }
424
425 /**
426 * Закрывает попап предпросмотра
427 */
428 closeSectionPreview() {
429 $('#section-preview-popup').removeClass('show');
430 this.currentSection = null;
431 }
432
433 /**
434 * Закрывает попап импорта
435 */
436 closeImportPopup() {
437 $('#section-importing-popup').removeClass('show');
438 this.isImporting = false;
439 }
440
441 /**
442 * Закрывает премиум промо
443 */
444 closePremiumPromo() {
445 $('#premium-promo-popup').removeClass('show');
446 }
447
448 /**
449 * Фильтрация секций
450 */
451 filterSections() {
452 const searchQuery = $('#section-search').val();
453 const selectedCategory = $('#section-category').val();
454 const selectedType = $('#section-type').val();
455 const selectedTags = [];
456
457 $('#section-tags input:checked').each(function() {
458 selectedTags.push($(this).val());
459 });
460
461 this.loadFilteredSections(searchQuery, selectedCategory, selectedType, selectedTags, 1);
462 }
463
464 /**
465 * Сброс фильтров
466 */
467 resetFilters() {
468 $('#section-search').val('');
469 $('#section-category').val('');
470 $('#section-type').val('');
471 $('#section-tags input:checked').prop('checked', false);
472 this.loadFilteredSections('', '', '', [], 1);
473 }
474
475 /**
476 * Обработка пагинации
477 */
478 handlePagination(e) {
479 e.preventDefault();
480
481 const href = $(e.target).attr('href');
482 const page = this.getPageFromUrl(href);
483
484 const searchQuery = $('#section-search').val();
485 const selectedCategory = $('#section-category').val();
486 const selectedType = $('#section-type').val();
487 const selectedTags = [];
488
489 $('#section-tags input:checked').each(function() {
490 selectedTags.push($(this).val());
491 });
492
493 this.loadFilteredSections(searchQuery, selectedCategory, selectedType, selectedTags, page);
494 }
495
496 /**
497 * Извлекает номер страницы из URL
498 */
499 getPageFromUrl(url) {
500 const match = url.match(/paged=(\d+)/);
501 return match ? parseInt(match[1]) : 1;
502 }
503
504 /**
505 * Загружает отфильтрованные секции
506 */
507 async loadFilteredSections(searchQuery, category, type, tags, page) {
508 try {
509 // Показываем индикатор загрузки
510 $('.sections-grid').addClass('loading');
511
512 const response = await $.ajax({
513 url: kingAddonsSectionsData.ajaxUrl,
514 type: 'POST',
515 data: {
516 action: 'filter_sections',
517 s: searchQuery,
518 category: category,
519 section_type: type,
520 tags: tags.join(','),
521 paged: page,
522 nonce: kingAddonsSectionsData.nonce
523 }
524 });
525
526 if (response.success) {
527 $('.sections-grid').html(response.data.grid_html);
528 $('.pagination').html(response.data.pagination_html);
529
530 // Скролл к началу результатов
531 this.scrollToTop();
532 } else {
533 console.error('Error filtering sections:', response.data);
534 }
535 } catch (error) {
536 console.error('AJAX error:', error);
537 } finally {
538 $('.sections-grid').removeClass('loading');
539 }
540 }
541
542 /**
543 * Скролл к началу каталога
544 */
545 scrollToTop() {
546 const $target = $('#king-addons-sections-top');
547 if ($target.length) {
548 $('html, body').animate({
549 scrollTop: $target.offset().top - 32 // Учитываем админ бар
550 }, 300);
551 }
552 }
553
554 /**
555 * Debounce функция для оптимизации поиска
556 */
557 debounce(func, wait) {
558 let timeout;
559 return function executedFunction(...args) {
560 const later = () => {
561 clearTimeout(timeout);
562 func(...args);
563 };
564 clearTimeout(timeout);
565 timeout = setTimeout(later, wait);
566 };
567 }
568 }
569
570 // Инициализация каталога секций
571 const sectionsCatalog = new SectionsCatalog();
572
573 // Добавляем индикаторы загрузки
574 const style = `
575 <style>
576 .sections-grid.loading {
577 opacity: 0.6;
578 pointer-events: none;
579 position: relative;
580 }
581 .sections-grid.loading::after {
582 content: "Loading...";
583 position: absolute;
584 top: 50%;
585 left: 50%;
586 transform: translate(-50%, -50%);
587 background: rgba(255,255,255,0.9);
588 padding: 20px;
589 border-radius: 8px;
590 font-weight: 600;
591 z-index: 10;
592 }
593 </style>
594 `;
595 $('head').append(style);
596
597 // Глобальные функции для отладки
598 window.KingAddonsSectionsCatalog = sectionsCatalog;
599
600 console.log('🎨 King Addons Sections Catalog initialized successfully!');
601 });