PageSourceSearch

https://www.parquedasaves.com.br/wp-content/plugins/faq-elementor/assets/js/faq-page-script.js?ver=1.2.6

js parquedasaves.com.br collected 2026-10-02 05:46:04 UTC 22,758 bytes, 599 lines download raw bytes

1/**
2 * FAQ Elementor Page Widget - JavaScript
3 */
4
5(function($) {
6    'use strict';
7
8    // Debounce function
9    function debounce(func, wait) {
10        let timeout;
11        return function executedFunction(...args) {
12            const later = () => {
13                clearTimeout(timeout);
14                func(...args);
15            };
16            clearTimeout(timeout);
17            timeout = setTimeout(later, wait);
18        };
19    }
20
21    // Initialize Sticky Categories
22    function initStickyCategories($container) {
23        // Check if sticky is enabled
24        const stickyEnabled = $container.data('sticky-categories');
25        if (stickyEnabled !== 'yes') return;
26        
27        const $categoriesTabs = $container.find('.faq-page-categories-tabs');
28        
29        if (!$categoriesTabs.length) return;
30        
31        // Create placeholder element to maintain layout when sticky
32        const $placeholder = $('<div class="faq-page-categories-placeholder"></div>');
33        $categoriesTabs.after($placeholder);
34        
35        // Get background color from container or parent
36        let bgColor = $container.find('.faq-page-main-content').css('background-color');
37        if (bgColor === 'rgba(0, 0, 0, 0)' || bgColor === 'transparent') {
38            bgColor = $container.css('background-color');
39        }
40        if (bgColor === 'rgba(0, 0, 0, 0)' || bgColor === 'transparent') {
41            bgColor = $container.closest('section, .elementor-section, .elementor-widget-container').css('background-color');
42        }
43        if (bgColor === 'rgba(0, 0, 0, 0)' || bgColor === 'transparent') {
44            bgColor = '#1a1a2e'; // Fallback color
45        }
46        
47        let originalTop = $categoriesTabs.offset().top;
48        let originalHeight = $categoriesTabs.outerHeight();
49        let isSticky = false;
50        
51        $(window).on('scroll.faqPageSticky', function() {
52            const scrollTop = $(window).scrollTop();
53            
54            // Check if we should make it sticky - just lock at top, no animation
55            if (scrollTop >= originalTop && !isSticky) {
56                isSticky = true;
57                $categoriesTabs.addClass('is-sticky');
58                $categoriesTabs.css('background-color', bgColor);
59                $placeholder.css('height', originalHeight + 'px');
60                $placeholder.addClass('is-visible');
61            } else if (scrollTop < originalTop && isSticky) {
62                isSticky = false;
63                $categoriesTabs.removeClass('is-sticky');
64                $categoriesTabs.css('background-color', '');
65                $placeholder.removeClass('is-visible');
66            }
67        });
68        
69        // Recalculate on resize
70        $(window).on('resize.faqPageSticky', debounce(function() {
71            if (!isSticky) {
72                originalTop = $categoriesTabs.offset().top;
73                originalHeight = $categoriesTabs.outerHeight();
74            }
75        }, 100));
76    }
77
78    // Initialize Category Slider with Swiper
79    function initCategorySlider($container) {
80        const $slider = $container.find('.faq-page-category-slider');
81        const $swiperEl = $slider.find('.faq-page-swiper');
82        
83        if (!$swiperEl.length) return;
84        
85        // Check if Swiper is available
86        if (typeof Swiper === 'undefined') {
87            console.warn('Swiper not loaded');
88            return;
89        }
90
91        const $nextBtn = $slider.find('.faq-page-swiper-next');
92        const $categoriesTabs = $container.find('.faq-page-categories-tabs');
93
94        // Initialize Swiper - começando do início (esquerda)
95        const swiper = new Swiper($swiperEl[0], {
96            slidesPerView: 'auto',
97            spaceBetween: 10,
98            freeMode: true,
99            grabCursor: true,
100            watchOverflow: true,
101            initialSlide: 0, // Começa no primeiro slide (esquerda)
102            navigation: {
103                nextEl: $nextBtn[0],
104            },
105            // Configurações de touch
106            touchRatio: 1,
107            touchAngle: 45,
108            simulateTouch: true,
109            shortSwipes: true,
110            longSwipesRatio: 0.5,
111            // Breakpoints para responsividade
112            breakpoints: {
113                320: {
114                    spaceBetween: 8,
115                },
116                768: {
117                    spaceBetween: 10,
118                },
119                1024: {
120                    spaceBetween: 12,
121                }
122            }
123        });
124
125        // Store swiper instance on container for later access
126        $container.data('category-swiper', swiper);
127
128        // Update swiper on window resize
129        $(window).on('resize', debounce(function() {
130            if (swiper && swiper.update) {
131                swiper.update();
132            }
133        }, 100));
134    }
135
136    // Initialize FAQ Page Widget
137    function initFAQPageWidget($container) {
138        if (!$container.length || $container.data('faq-page-initialized')) {
139            return;
140        }
141
142        $container.data('faq-page-initialized', true);
143
144        // Initialize category slider (Swiper)
145        initCategorySlider($container);
146
147        // Initialize sticky categories
148        initStickyCategories($container);
149
150        const widgetId = $container.data('widget-id');
151        const postsPerPage = parseInt($container.data('posts-per-page')) || 10;
152        const paginationType = $container.data('pagination-type') || 'load_more';
153        const allowMultiple = $container.data('allow-multiple') === 'yes';
154        let maxPages = parseInt($container.data('max-pages')) || 1;
155
156        let currentPage = 1;
157        let currentCategory = 'all';
158        let currentSearch = '';
159        let isLoading = false;
160
161        const $searchInput = $container.find('.faq-page-search-input');
162        const $searchClear = $container.find('.faq-page-search-clear');
163        const $searchIcon = $container.find('.faq-page-search-icon');
164        const $searchLoading = $container.find('.faq-page-search-loading');
165        const $categoryBtns = $container.find('.faq-page-category-btn');
166        const $itemsWrapper = $container.find('.faq-page-items-wrapper');
167        const $itemsContainer = $container.find('.faq-page-items');
168        const $noResults = $container.find('.faq-page-search-no-results');
169        const $loadMoreBtn = $container.find('.faq-page-load-more-btn');
170        const $paginationNums = $container.find('.faq-page-pagination-num');
171
172        // Esconder botão "carregar mais" se só tem uma página no carregamento inicial
173        if ($loadMoreBtn.length && maxPages <= 1) {
174            $loadMoreBtn.hide();
175        }
176
177        // Toggle FAQ item
178        function toggleItem($item) {
179            const isActive = $item.hasClass('active');
180
181            if (!allowMultiple) {
182                // Close all other items
183                $itemsContainer.find('.faq-page-item.active').not($item).removeClass('active');
184            }
185
186            $item.toggleClass('active', !isActive);
187        }
188
189        // FAQ item click handler
190        $container.on('click', '.faq-page-question-wrapper', function() {
191            const $item = $(this).closest('.faq-page-item');
192            toggleItem($item);
193        });
194
195        // Keyboard accessibility
196        $container.on('keydown', '.faq-page-question-wrapper', function(e) {
197            if (e.key === 'Enter' || e.key === ' ') {
198                e.preventDefault();
199                const $item = $(this).closest('.faq-page-item');
200                toggleItem($item);
201            }
202        });
203
204        // Search functionality - busca em tempo real
205        const performSearch = debounce(function(query) {
206            currentSearch = query.trim();
207            currentPage = 1;
208
209            if (currentSearch.length === 0) {
210                $searchClear.hide();
211                removeHighlights();
212                resetToInitial();
213                return;
214            }
215
216            $searchClear.show();
217            
218            // Primeiro faz filtro local para resposta imediata
219            filterAndHighlightLocal(currentSearch);
220            
221            // Depois busca no servidor para resultados completos
222            loadFAQs(true);
223        }, 150);
224
225        // Filtro e highlight local para resposta imediata
226        function filterAndHighlightLocal(search) {
227            if (!search || search.length < 2) return;
228            
229            const searchLower = search.toLowerCase();
230            const terms = search.split(/\s+/).filter(t => t.length > 1);
231            
232            $itemsContainer.find('.faq-page-item').each(function() {
233                const $item = $(this);
234                const questionText = $item.find('.faq-page-question').text().toLowerCase();
235                const answerText = $item.find('.faq-page-answer-content').text().toLowerCase();
236                
237                // Verifica se algum termo aparece na pergunta ou resposta
238                const matches = terms.some(term => 
239                    questionText.includes(term.toLowerCase()) || 
240                    answerText.includes(term.toLowerCase())
241                );
242                
243                if (matches) {
244                    $item.removeClass('hidden').show();
245                } else {
246                    $item.addClass('hidden').hide();
247                }
248            });
249            
250            // Aplica highlight
251            highlightSearchTerms(search);
252            
253            // Verifica se há resultados visíveis
254            const visibleItems = $itemsContainer.find('.faq-page-item:not(.hidden)').length;
255            if (visibleItems === 0) {
256                $noResults.show();
257            } else {
258                $noResults.hide();
259            }
260        }
261        
262        // Remove highlights
263        function removeHighlights() {
264            $itemsContainer.find('.faq-page-highlight').each(function() {
265                const $highlight = $(this);
266                $highlight.replaceWith($highlight.text());
267            });
268        }
269
270        $searchInput.on('input', function() {
271            const query = $(this).val();
272            performSearch(query);
273        });
274
275        // Clear search
276        $searchClear.on('click', function() {
277            $searchInput.val('');
278            currentSearch = '';
279            $searchClear.hide();
280            removeHighlights();
281            resetToInitial();
282        });
283
284        // Category filter
285        $categoryBtns.on('click', function() {
286            const $btn = $(this);
287            const category = $btn.data('category');
288
289            // Update active state
290            $categoryBtns.removeClass('active');
291            $btn.addClass('active');
292
293            // Set category and reload
294            currentCategory = category;
295            currentPage = 1;
296            
297            // Remove highlights when changing category
298            removeHighlights();
299            // Clear search when changing category
300            if (currentSearch) {
301                $searchInput.val('');
302                currentSearch = '';
303                $searchClear.hide();
304            }
305
306            loadFAQs(true);
307
308            // Track tag click
309            if (category !== 'all' && typeof faqElementor !== 'undefined') {
310                $.post(faqElementor.ajaxUrl, {
311                    action: 'faq_track_tag',
312                    nonce: faqElementor.nonce,
313                    tag: category,
314                    lang: faqElementor.lang || ''
315                });
316            }
317        });
318
319        // Load more button
320        $loadMoreBtn.on('click', function() {
321            if (isLoading) return;
322            if (currentPage >= maxPages) {
323                $loadMoreBtn.hide();
324                return;
325            }
326            currentPage++;
327            loadFAQs(false);
328        });
329
330        // Pagination numbers
331        $paginationNums.on('click', function() {
332            const page = parseInt($(this).data('page'));
333            if (isLoading || page === currentPage) return;
334
335            $paginationNums.removeClass('active');
336            $(this).addClass('active');
337
338            currentPage = page;
339            loadFAQs(true);
340
341            // Scroll to top of FAQ items
342            $('html, body').animate({
343                scrollTop: $itemsWrapper.offset().top - 100
344            }, 300);
345        });
346
347        // Infinite scroll
348        if (paginationType === 'infinite') {
349            let scrollTimeout;
350            $(window).on('scroll', function() {
351                clearTimeout(scrollTimeout);
352                scrollTimeout = setTimeout(function() {
353                    if (isLoading || currentPage >= maxPages) return;
354
355                    const scrollPos = $(window).scrollTop() + $(window).height();
356                    const triggerPos = $itemsWrapper.offset().top + $itemsWrapper.height() - 200;
357
358                    if (scrollPos >= triggerPos) {
359                        currentPage++;
360                        loadFAQs(false);
361                    }
362                }, 100);
363            });
364        }
365
366        // Load FAQs via AJAX
367        function loadFAQs(replace) {
368            if (isLoading) return;
369            isLoading = true;
370
371            // Show loading state
372            $itemsWrapper.addClass('loading');
373            $searchIcon.hide();
374            $searchLoading.show();
375            
376            if ($loadMoreBtn.length) {
377                $loadMoreBtn.prop('disabled', true);
378                $loadMoreBtn.find('.faq-page-load-more-text').hide();
379                $loadMoreBtn.find('.faq-page-load-more-loading').show();
380                $loadMoreBtn.find('.faq-page-load-more-spinner').show();
381            }
382
383            const data = {
384                action: 'faq_page_search',
385                nonce: faqElementor.nonce,
386                search: currentSearch,
387                category: currentCategory,
388                page: currentPage,
389                posts_per_page: postsPerPage,
390                lang: faqElementor.lang || ''
391            };
392
393            $.post(faqElementor.ajaxUrl, data, function(response) {
394                isLoading = false;
395                $itemsWrapper.removeClass('loading');
396                $searchIcon.show();
397                $searchLoading.hide();
398
399                if ($loadMoreBtn.length) {
400                    $loadMoreBtn.prop('disabled', false);
401                    $loadMoreBtn.find('.faq-page-load-more-text').show();
402                    $loadMoreBtn.find('.faq-page-load-more-loading').hide();
403                    $loadMoreBtn.find('.faq-page-load-more-spinner').hide();
404                }
405
406                if (response.success) {
407                    const items = response.data.items;
408                    const hasMore = response.data.has_more;
409                    const maxPagesNew = response.data.max_pages;
410                    
411                    // Atualiza maxPages global
412                    maxPages = maxPagesNew;
413
414                    if (replace) {
415                        $itemsContainer.empty();
416                    }
417
418                    if (items.length === 0 && replace) {
419                        $itemsContainer.hide();
420                        $noResults.show();
421                        // Esconde botão se não há resultados
422                        if ($loadMoreBtn.length) {
423                            $loadMoreBtn.hide();
424                        }
425                    } else {
426                        $noResults.hide();
427                        $itemsContainer.show();
428
429                        items.forEach(function(item) {
430                            const $newItem = createFAQItem(item);
431                            $itemsContainer.append($newItem);
432                        });
433
434                        // Highlight search terms
435                        if (currentSearch) {
436                            highlightSearchTerms(currentSearch);
437                        }
438                        
439                        // Update load more button visibility
440                        if ($loadMoreBtn.length) {
441                            // Esconde se não há mais páginas OU se chegamos na última página
442                            if (!hasMore || currentPage >= maxPages) {
443                                $loadMoreBtn.hide();
444                            } else {
445                                $loadMoreBtn.show();
446                            }
447                        }
448                    }
449
450                    // Update pagination numbers
451                    updatePaginationNumbers(maxPagesNew);
452                }
453            }).fail(function() {
454                isLoading = false;
455                $itemsWrapper.removeClass('loading');
456                $searchIcon.show();
457                $searchLoading.hide();
458
459                if ($loadMoreBtn.length) {
460                    $loadMoreBtn.prop('disabled', false);
461                    $loadMoreBtn.find('.faq-page-load-more-text').show();
462                    $loadMoreBtn.find('.faq-page-load-more-loading').hide();
463                    $loadMoreBtn.find('.faq-page-load-more-spinner').hide();
464                }
465            });
466        }
467
468        // Create FAQ item HTML
469        function createFAQItem(item) {
470            const tagsAttr = item.tags ? item.tags.join(',') : '';
471            const $item = $(`
472                <div class="faq-page-item" data-id="${item.id}" data-tags="${tagsAttr}">
473                    <div class="faq-page-question-wrapper" tabindex="0" role="button" aria-expanded="false">
474                        <span class="faq-page-question">${escapeHtml(item.title)}</span>
475                        <span class="faq-page-icon">
476                            <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
477                                <polyline points="6 9 12 15 18 9"></polyline>
478                            </svg>
479                        </span>
480                    </div>
481                    <div class="faq-page-answer">
482                        <div class="faq-page-answer-content">
483                            ${item.content}
484                        </div>
485                    </div>
486                </div>
487            `);
488            return $item;
489        }
490
491        // Escape HTML
492        function escapeHtml(text) {
493            const div = document.createElement('div');
494            div.textContent = text;
495            return div.innerHTML;
496        }
497
498        // Highlight search terms
499        function highlightSearchTerms(search) {
500            const terms = search.split(/\s+/).filter(t => t.length > 2);
501            if (terms.length === 0) return;
502
503            const regex = new RegExp('(' + terms.map(t => t.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|') + ')', 'gi');
504
505            $itemsContainer.find('.faq-page-question, .faq-page-answer-content').each(function() {
506                const $el = $(this);
507                const html = $el.html();
508                
509                // Only highlight text nodes, not HTML tags
510                const highlighted = html.replace(/>([^<]+)</g, function(match, text) {
511                    return '>' + text.replace(regex, '<span class="faq-page-highlight">$1</span>') + '<';
512                });
513                
514                $el.html(highlighted);
515            });
516        }
517
518        // Update pagination numbers
519        function updatePaginationNumbers(newMaxPages) {
520            const $paginationContainer = $container.find('.faq-page-pagination-numbers');
521            if (!$paginationContainer.length) return;
522
523            $paginationContainer.empty();
524
525            for (let i = 1; i <= newMaxPages; i++) {
526                const activeClass = i === currentPage ? 'active' : '';
527                $paginationContainer.append(`
528                    <button type="button" class="faq-page-pagination-num ${activeClass}" data-page="${i}">${i}</button>
529                `);
530            }
531
532            // Re-bind click events
533            $paginationContainer.find('.faq-page-pagination-num').on('click', function() {
534                const page = parseInt($(this).data('page'));
535                if (isLoading || page === currentPage) return;
536
537                $paginationContainer.find('.faq-page-pagination-num').removeClass('active');
538                $(this).addClass('active');
539
540                currentPage = page;
541                loadFAQs(true);
542
543                $('html, body').animate({
544                    scrollTop: $itemsWrapper.offset().top - 100
545                }, 300);
546            });
547        }
548
549        // Reset to initial state
550        function resetToInitial() {
551            currentCategory = 'all';
552            currentPage = 1;
553
554            // Reset category buttons
555            $categoryBtns.removeClass('active');
556            $categoryBtns.filter('[data-category="all"]').addClass('active');
557            
558            // Remove highlights
559            removeHighlights();
560            
561            // Show all items
562            $itemsContainer.find('.faq-page-item').removeClass('hidden').show();
563            $noResults.hide();
564
565            loadFAQs(true);
566        }
567
568        // Make question wrappers focusable
569        $container.find('.faq-page-question-wrapper').attr({
570            'tabindex': '0',
571            'role': 'button',
572            'aria-expanded': 'false'
573        });
574
575        // Update aria-expanded on toggle
576        $container.on('click', '.faq-page-question-wrapper', function() {
577            const $item = $(this).closest('.faq-page-item');
578            const isExpanded = $item.hasClass('active');
579            $(this).attr('aria-expanded', isExpanded);
580        });
581    }
582
583    // Initialize on document ready
584    $(document).ready(function() {
585        $('.faq-page-container').each(function() {
586            initFAQPageWidget($(this));
587        });
588    });
589
590    // Initialize for Elementor frontend
591    $(window).on('elementor/frontend/init', function() {
592        if (typeof elementorFrontend !== 'undefined') {
593            elementorFrontend.hooks.addAction('frontend/element_ready/faq_pda_page_widget.default', function($scope) {
594                initFAQPageWidget($scope.find('.faq-page-container'));
595            });
596        }
597    });
598
599})(jQuery);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.