Kategorimeny

Kategorimeny

Fel uppstod under bearbetning av mallen.
Java method "com.sun.proxy.$Proxy65.getCategory(long)" threw an exception when invoked on com.sun.proxy.$Proxy65 object "com.liferay.asset.categories.internal.service.AssetCategoryPropertyAssetCategoryLocalServiceWrapper@2a8a62c2"; see cause exception in the Java stack trace.

----
FTL stack trace ("~" means nesting-related):
	- Failed at: #local category = assetCategoryLocalS...  [in template "20096#20122#119328" in macro "getCategoryName" at line 15, column 17]
	- Reached through: @getCategoryName categoryId=categoryP...  [in template "20096#20122#119328" at line 23, column 106]
----
1<#assign AssetEntryLocalService = serviceLocator.findService("com.liferay.asset.kernel.service.AssetEntryLocalService") /> 
2<#assign ClassNameLocalService= serviceLocator.findService("com.liferay.portal.kernel.service.ClassNameLocalService") /> 
3<#assign journalClassNameId = ClassNameLocalService.getClassNameId("com.liferay.journal.model.JournalArticle") /> 
4<#assign AssetEntryAssetCategoryRelLocalService = serviceLocator.findService("com.liferay.asset.entry.rel.service.AssetEntryAssetCategoryRelLocalService") /> 
5<#if entries?has_content> 
6    <#list entries as cur_vocabulary> 
7        <#if themeDisplay.getURLCurrent()?contains('/autores')> 
8            <#assign serviceContextThreadLocal = staticUtil["com.liferay.portal.kernel.service.ServiceContextThreadLocal"] /> 
9            <#assign serviceContext = serviceContextThreadLocal.getServiceContext() /> 
10            <#assign categoryParam = paramUtil.get(serviceContext, "cFilter", "")  /> 
11 
12            <#macro getCategoryName categoryId> 
13                <#local assetCategoryLocalService = serviceLocator.findService("com.liferay.asset.kernel.service.AssetCategoryLocalService") /> 
14                <#local categoryIdLong = categoryId?number /> 
15                <#local category = assetCategoryLocalService.getCategory(categoryIdLong) /> 
16                ${category.getName()} 
17            </#macro> 
18 
19            <#if categoryParam?has_content > 
20                <div class="bg-gray-5 w-100 px-4 py-3 c-mb-32 br-8 shadow-md" > 
21                    <p class="text-up-04 font-weight-bold text-primary-80 d-flex align-items-end"> 
22                        <#if categoryParam?has_content> 
23                            Filtrando por: <span class="text-primary-80 text-uppercase text-up-03 ml-2">"<@getCategoryName categoryId = categoryParam />"</span></span> 
24                        </#if> 
25                        <a data-senna-off="true" class="ml-auto" href=${themeDisplay.getURLCurrent()?keep_before("?")}><i class="far fa-times-circle text-hover-primary-80"></i></a> 
26                    </p> 
27                </div> 
28            </#if> 
29 
30 
31            <div class="row rounded bg-primary-5 p-4 mb-4 w-100 mx-auto no-gutters c-eb-search"> 
32                <div class="col-md-12 d-flex align-items-end"> 
33                    <label class="d-flex flex-column text-base w-100"> 
34                        Buscar Autores 
35                        <input 
36                                id="filter-categories" 
37                                type="text" 
38                                class="p-2 border-0 mt-2 rounded" 
39                                placeholder="Pesquisar artigos por nome de autor" 
40
41                    </label> 
42 
43                    <button disabled class="br-button primary ml-4 mr-md-4" id="btn-filter-categories"> 
44                        Filtrar 
45                    </button> 
46                </div> 
47            </div> 
48        </#if> 
49        <div id="pg-load" class="loading my-8 medium"></div> 
50        <div class="d-none" id="infinity-wrapper" data-vocabularyId="${cur_vocabulary.getVocabularyId()}"></div> 
51        <div id="infinity-pagination"></div> 
52    </#list> 
53</#if> 
54 
55 
56<script> 
57    if (typeof window.categoryImgSources !== 'undefined') { 
58        var categoryImgSources = window.categoryImgSources; 
59    } else { 
60        var categoryImgSources = {}; 
61
62 
63    if (typeof window.categoryArrays !== 'undefined') { 
64        var categoryArrays = window.categoryArrays; 
65    } else { 
66        var categoryArrays = {}; 
67
68 
69    if (typeof window.viewCountArray !== 'undefined') { 
70        var viewCountArray = window.viewCountArray; 
71    } else { 
72        var viewCountArray = {}; 
73
74 
75    if (typeof window.vocabularyId !== 'undefined') { 
76        var  vocabularyId = window.vocabularyId; 
77    } else { 
78        var vocabularyId = document.querySelector('#infinity-wrapper').getAttribute('data-vocabularyid'); 
79
80 
81 
82    if (typeof window.categoryArticlesCount !== 'undefined') { 
83        var categoryArticlesCount = window.categoryArticlesCount; 
84    } else { 
85        var categoryArticlesCount = {}; 
86
87 
88 
89    <#if entries?has_content> 
90    <#list entries as curVocabulary> 
91    <#if curVocabulary.getCategories()?has_content> 
92    <#list curVocabulary.getCategories() as category> 
93    <#assign assetEntryAssetCategoryRels = AssetEntryAssetCategoryRelLocalService.getAssetEntryAssetCategoryRelsByAssetCategoryId(category.getCategoryId())> 
94    <#list assetEntryAssetCategoryRels as assetEntryAssetCategoryRel> 
95    <#assign assetEntry = AssetEntryLocalService.getAssetEntry(assetEntryAssetCategoryRel.getAssetEntryId())> 
96    <#if assetEntry.getClassNameId() == journalClassNameId > 
97    if (!categoryArrays["category_" + "${category.getCategoryId()}"]) { 
98        categoryArrays["category_" + "${category.getCategoryId()}"] = []; 
99        categoryImgSources["category_" + "${category.getCategoryId()}"] = []; 
100        categoryArticlesCount["category_" + "${category.getCategoryId()}"] = []; 
101
102    viewCountArray["${assetEntry.getClassPK()}"] = "${assetEntry.getViewCount()}"; 
103    categoryImgSources["category_" + "${category.getCategoryId()}"].push("${getCategoryImageSrc(category)}"); 
104    categoryArticlesCount["category_" + "${category.getCategoryId()}"].push("${getCategoryCount(category)}"); 
105    categoryArrays["category_" + "${category.getCategoryId()}"].push("${assetEntry.getClassPK()}"); 
106 
107    </#if> 
108    </#list> 
109    </#list> 
110    </#if> 
111    </#list> 
112    </#if> 
113 
114    function toggleDescription(categoryId) { 
115        let fullDescription = document.getElementById('fullDescription-' + categoryId); 
116        let toggleButton = document.getElementById('toggle-' + categoryId); 
117 
118        if (fullDescription.style.display === 'none') { 
119            fullDescription.style.display = 'block'; 
120            toggleButton.textContent = 'Ver Menos'; 
121        } else { 
122            fullDescription.style.display = 'none'; 
123            toggleButton.textContent = 'Ver Mais'; 
124
125
126 
127 
128 
129    Liferay.on("allPortletsReady", function(){ 
130        fetchAndRenderCategoriesAndArticles(); 
131        renderPagination(vocabularyId); 
132 
133 
134 
135        function fetchAndRenderCategoriesAndArticles() { 
136            const pageSize = 10; 
137            const currentPageNumber = isNaN(parseInt($('.br-pagination').attr('data-current'))) || $('.br-pagination').attr('data-current') === null ? 1 : parseInt($('.br-pagination').attr('data-current')); 
138            const infinityWrapper = $('#infinity-wrapper'); 
139            const animationDuration = 1000; 
140 
141            const animateRemoval = function(element) { 
142                let height = element.clientHeight; 
143                element.style.transition = 'opacity ' + animationDuration + 'ms, transform ' + animationDuration + 'ms'; 
144                element.style.opacity = 0; 
145                element.style.transform = 'translateY(-' + height + 'px)'; 
146                setTimeout(function() { 
147                    element.remove(); 
148                }, animationDuration); 
149            }; 
150 
151            const existingItems = infinityWrapper.children(); 
152            existingItems.each(function(index, item) { 
153                animateRemoval(item); 
154            }); 
155 
156            setTimeout(function() { 
157                infinityWrapper.empty(); 
158                fetchTaxonomyCategories(vocabularyId, currentPageNumber, pageSize) 
159                        .then(function(data) { 
160                            renderCategories(data); 
161                            data.items.forEach(function(category) { 
162                                let categoryId = category.id; 
163                                let classPKs = categoryArrays["category_" + categoryId]; 
164                                renderArticles(categoryId, classPKs, classPKs.length); 
165                            }); 
166                            updatePagination(); 
167                        }); 
168            }, animationDuration); 
169 
170 
171 
172            $('html, body').animate({ 
173                scrollTop: 0 
174            }, animationDuration); 
175
176 
177        async function fetchAllCategories(vocabularyId, categories = [], pageNumber = 1) { 
178            const pageSize = 100; 
179            const url = '${themeDisplay.getURLPortal()}/o/headless-admin-taxonomy/v1.0/taxonomy-vocabularies/' + vocabularyId + '/taxonomy-categories?page=' + pageNumber + '&pageSize=' + pageSize + '&sort=name:asc&fields=name,id'; 
180            try { 
181                const response = await Liferay.Util.fetch(url, {}); 
182                const data = await response.json(); 
183                const newCategories = data.items.map(item => ({name: item.name, id: item.id})); 
184                categories = [...categories, ...newCategories]; 
185                while (data.page < data.lastPage) { 
186                    pageNumber++; 
187                    return fetchAllCategories(vocabularyId, categories, pageNumber); 
188
189                return categories; 
190            } catch (error) { 
191
192
193        async function setupAutocomplete(vocabularyId) { 
194            const input = document.getElementById('filter-categories'); 
195            if (!input) { 
196                return; 
197
198 
199            const categories = await fetchAllCategories(vocabularyId); 
200            const dropdown = document.createElement('div'); 
201            dropdown.setAttribute('id', 'autocomplete-dropdown'); 
202            dropdown.setAttribute('class', 'dropdown-menu'); 
203            input.parentNode.appendChild(dropdown); 
204 
205            input.addEventListener('input', function() { 
206                dropdown.innerHTML = ''; 
207                dropdown.classList.remove('show'); 
208 
209                const searchTerm = input.value.toLowerCase(); 
210                const matchingCategories = categories.filter(category => category.name.toLowerCase().includes(searchTerm)); 
211 
212                for (const category of matchingCategories) { 
213                    const button = document.createElement('button'); 
214                    button.textContent = category.name; 
215                    button.setAttribute('class', 'dropdown-item'); 
216                    button.setAttribute('data-category-id', category.id); 
217                    button.addEventListener('click', function() { 
218                        input.value = category.name; 
219                        input.setAttribute('data-category-id', category.id); 
220                        dropdown.classList.remove('show'); 
221                        document.getElementById('btn-filter-categories').removeAttribute('disabled'); 
222                    }); 
223                    dropdown.appendChild(button); 
224
225 
226                if (matchingCategories.length > 0) { 
227                    dropdown.classList.add('show'); 
228                } else { 
229                    document.getElementById('btn-filter-categories').setAttribute('disabled', 'disabled'); 
230
231            }); 
232 
233            input.addEventListener('change', function() { 
234                document.getElementById('btn-filter-categories').setAttribute('disabled', 'disabled'); 
235            }); 
236 
237            document.getElementById('btn-filter-categories').addEventListener('click', function() { 
238                const categoryId = input.getAttribute('data-category-id'); 
239                if (categoryId) { 
240                    const currentUrl = '${themeDisplay.getURLCurrent()?keep_before("?")}?cFilter='; 
241                    const newUrl = currentUrl + categoryId; 
242                    window.location.href = newUrl; 
243
244            }); 
245
246 
247        function fetchTaxonomyCategories(vocabularyId, pageNumber, pageSize) { 
248            const urlParams = new URLSearchParams(window.location.search); 
249            const authorElements = document.getElementsByClassName('author-id'); 
250            const cFilterValue = urlParams.get('cFilter'); 
251            let filter = ''; 
252            let promises = []; 
253 
254            if (authorElements.length > 0) { 
255                for (let i = 0; i < authorElements.length; i++) { 
256                    const authorId = authorElements[i].id; 
257                    filter = 'id%20eq%20%27' + authorId + '%27'; 
258                    const url = `${themeDisplay.getURLPortal()}/o/headless-admin-taxonomy/v1.0/taxonomy-vocabularies/` + vocabularyId + '/taxonomy-categories?page=' + pageNumber + '&pageSize=' + pageSize + '&sort=name:asc&filter=' + filter; 
259                    promises.push( 
260                            Liferay.Util.fetch(url, {}) 
261                                    .then(function(response) { 
262                                        return response.json(); 
263                                    }) 
264                                    .catch(function(error) { 
265                                    }) 
266                    ); 
267
268            } else if (cFilterValue) { 
269                filter = 'id%20eq%20%27' + cFilterValue + '%27'; 
270                const url = `${themeDisplay.getURLPortal()}/o/headless-admin-taxonomy/v1.0/taxonomy-vocabularies/` + vocabularyId + '/taxonomy-categories?page=' + pageNumber + '&pageSize=' + pageSize + '&sort=name:asc&filter=' + filter; 
271                promises.push( 
272                        Liferay.Util.fetch(url, {}) 
273                                .then(function(response) { 
274                                    return response.json(); 
275                                }) 
276                                .catch(function(error) { 
277                                }) 
278                ); 
279            } else { 
280                const url = `${themeDisplay.getURLPortal()}/o/headless-admin-taxonomy/v1.0/taxonomy-vocabularies/` + vocabularyId + '/taxonomy-categories?page=' + pageNumber + '&pageSize=' + pageSize + '&sort=name:asc'; 
281                promises.push( 
282                        Liferay.Util.fetch(url, {}) 
283                                .then(function(response) { 
284                                    return response.json(); 
285                                }) 
286                                .catch(function(error) { 
287                                }) 
288                ); 
289
290 
291            return Promise.all(promises).then(function(results) { 
292                let combinedData = []; 
293                results.forEach(function(data) { 
294                    if (data && data.items) { 
295                        combinedData = combinedData.concat(data.items); 
296
297                }); 
298                return { items: combinedData }; 
299            }); 
300
301 
302 
303 
304 
305        function renderCategories(data) { 
306            let infinityWrapper = document.getElementById('infinity-wrapper'); 
307 
308            if (data && data.items && Array.isArray(data.items)) { 
309                data.items.forEach(function(category) { 
310                    let categoryId = category.id; 
311                    let categoryTitle = category.name; 
312                    let categoryDescription = category.description; 
313                    const MAX_DESCRIPTION_LENGTH = 300; 
314 
315                    if (categoryDescription && categoryDescription.length > 0) { 
316                        if (categoryDescription.length > MAX_DESCRIPTION_LENGTH) { 
317                            categoryDescription ='<h6 class="font-weight-extra-bold">CURRÍCULO</h6>' + '<div class="text-uppercase">' + categoryDescription.substring(0, MAX_DESCRIPTION_LENGTH) + 
318                                    '<span id="ellip-' + categoryId + '">...</span>' + 
319                                    '<span id="fullDescription-' + categoryId + '" class="m-0 p-0 panel-collapse collapse" role="tabpanel" style="display:none;">' + 
320                                    categoryDescription.substring(MAX_DESCRIPTION_LENGTH) + 
321                                    '</span>' + 
322                                    '</div>' + 
323                                    '<button ' + 
324                                    'id="toggle-' + categoryId + '" ' + 
325                                    'class="mt-4 text-mint-cool-50 br-button secondary" ' + 
326                                    'style="background:transparent;" ' + 
327                                    'onclick="toggleDescription(' + categoryId +')"' + 
328                                    '>' + 
329                                    'Ver Mais' + 
330                                    '</button>'; 
331                        } else { 
332                            categoryDescription =   '<h6 class="font-weight-extra-bold">CURRÍCULO</h6>' +'<div class="text-uppercase">' + categoryDescription + '</div>'; 
333
334                    } else { 
335                        categoryDescription = ''; 
336
337 
338 
339                    let articleCount = categoryArticlesCount['category_' + categoryId][0]; 
340                    let articleText = articleCount == 1 ? 'artigo' : 'artigos'; 
341 
342                    let categoryDiv = document.createElement('div'); 
343                    categoryDiv.id = 'pageWrapper-' + categoryId; 
344                    categoryDiv.classList.add('d-flex', 'flex-column', 'array-wrapper', 'card-interactive-shadow', 'overflow-hidden', 'c-mb-32', 'p-0'); 
345 
346                    let headerDiv = document.createElement('div'); 
347                    headerDiv.classList.add('order-first', 'asset-entries-group-header', 'bg-gray-5', 'p-3'); 
348                    headerDiv.innerHTML = 
349                            '<div class="row pb-3">' + 
350                            '    <div class="d-flex flex-column col-md-3 author-col">' + 
351                            '        <img height="154" width="auto" class="mx-auto rounded-circle shadow-lg" src="' + categoryImgSources['category_' + categoryId][0] + '"/>' + 
352                            '        <div class="mt-4">' + 
353                            '            <div class="d-flex align-items-center justify-content-center text-cyan-vivid-80 font-weight-extra-bold">' + 
354                            '                <i class="fas fa-file-alt mr-2"></i>' + 
355                            '                <span class="text-uppercase">' + articleCount + ' ' + articleText + '</span>' + 
356                            '            </div>' + 
357                            '        </div>' + 
358                            '    </div>' + 
359                            '    <div class="col-md-9">' + 
360                            '        <h2>' + categoryTitle + '</h2>' + 
361                            '        ' + categoryDescription + 
362                            '    </div>' + 
363                            '</div>'; 
364                    categoryDiv.appendChild(headerDiv); 
365 
366                    let articlesList = document.createElement('ul'); 
367                    articlesList.classList.add('articles-wrapper', 'asset-entries-group-wrapper', 'p-0', 'd-flex', 'flex-column', 'list-unstyled', 'm-0'); 
368                    articlesList.id = 'div_' + categoryId; 
369                    categoryDiv.appendChild(articlesList); 
370                    if (articleCount > 3) { 
371                        let renderButton = document.createElement('button'); 
372                        renderButton.classList.add('btn', 'btn-unstyled', 'panel-header', 'panel-header-link','text-pure-0', 'text-weight-bold', 'text-center', 'text-uppercase', 'bg-mint-cool-50', 'rounded-0'); 
373                        renderButton.id = 'category_' + categoryId; 
374 
375                        let icon = document.createElement('i'); 
376                        icon.classList.add('fas', 'fa-chevron-down', 'mr-2', 'text-pure-0', 'text-weight-bold', 'text-center', 'text-uppercase'); 
377                        renderButton.appendChild(icon); 
378 
379                        renderButton.appendChild(document.createTextNode('Ver todos os artigos')); // Add text node 
380 
381                        categoryDiv.appendChild(renderButton); 
382
383 
384 
385 
386                    infinityWrapper.appendChild(categoryDiv); 
387                }); 
388            } else { 
389                console.error('Invalid response format: "items" array not found or not an array.'); 
390
391
392 
393 
394        function renderArticles(categoryId, classPKs, classPKsLength) { 
395            let divId = 'div_' + categoryId; 
396            let maxRender = 3; 
397 
398            let fetchPromises = classPKs.map(classPK => { 
399                let url = "${themeDisplay.getURLPortal()}/o/headless-delivery/v1.0/structured-contents/" + classPK; 
400                return Liferay.Util.fetch(url, {}).then(response => response.json()); 
401            }); 
402 
403            Promise.all(fetchPromises) 
404                    .then(articles => { 
405                        // Sort articles by datePublished 
406                        articles.sort((a, b) => new Date(b.datePublished) - new Date(a.datePublished)); 
407 
408                        // Render up to maxRender articles 
409                        for (let j = 0; j < Math.min(maxRender, articles.length); j++) { 
410                            renderArticle(articles[j], divId); 
411
412 
413                        let categoryButton = document.getElementById('category_' + categoryId); 
414                        if (categoryButton) { 
415                            categoryButton.addEventListener('click', function(event) { 
416                                for (let j = maxRender; j < articles.length; j++) { 
417                                    renderArticle(articles[j], divId); 
418
419                                categoryButton.remove(); 
420                            }); 
421                        } else { 
422 
423
424                        document.getElementById('infinity-wrapper').classList.remove('d-none'); 
425                        document.getElementById('pg-load').classList.add('d-none'); 
426                    }) 
427                    .catch(error => { 
428                        console.error('Error fetching articles:', error); 
429 
430                    }); 
431 
432 
433
434 
435        function renderArticle(article, divId) { 
436            let resourcePrimKey = article.id; 
437            let urlTitle = article.friendlyUrlPath; 
438            let title = article.title; 
439            let summary = article.description ? '<h5 class="c-ml-32 text-primary-50 h5 m-0 post-summary-content">' + article.description + '</h5>' : ''; 
440            let displayDate = new Date(article.datePublished).toLocaleDateString("pt-BR", { 
441                weekday: 'long', 
442                year: 'numeric', 
443                month: 'long', 
444                day: 'numeric' 
445            }); 
446            let commentsCount = article.numberOfComments; 
447            let viewCount = getViewCountByPK(resourcePrimKey); 
448 
449            let listItem = document.createElement('li'); 
450            listItem.className = "bg-pure-0 py-3 px-3"; 
451            listItem.innerHTML = '<div class="mb-0">' + 
452                    '<div class="">' + 
453                    '<div class="d-flex text-primary-50 mx-0 mt-0">' + 
454                    '<span class="btn-monospaced p-3"><i class="fas fa-file-alt"></i></span>' + 
455                    '<a data-senna-off="true" href="/w/' + urlTitle + '" class="h3 text-primary-50 text-weight-bold m-0">' + title + '</a>' + 
456                    '</div>' + 
457                    summary + 
458                    '<div class="autofit-row mt-2">' + 
459                    '<h6 class="text-primary-80 text-weight-bold text-uppercase m-0">' + 
460                    '<span class="btn-monospaced"><i class="fas fa-clock"></i></span>' + 
461                    '<span class="my-auto">' + displayDate + '</span>' + 
462                    '</h6>' + 
463                    //'<h6 class="text-mint-cool-80 text-weight-bold my-auto">' + 
464                    //'<span class="btn-monospaced"><i class="fas fa-eye"></i></span>' + 
465                    //'<span class="my-auto">' + viewCount + ' VISUALIZAÇÕES</span>' + // Include view count here 
466                    //'</h6>' + 
467                    '<div class="d-flex flex-wrap">' + 
468                    '<h6 class="text-mint-cool-80 text-weight-bold my-auto">' + 
469                    '<span class="btn-monospaced"><i class="fas fa-comment"></i></span>' + 
470                    '<span class="my-auto text-uppercase" >' + commentsCount + ' COMENTÁRIOS</span>' + 
471                    '</h6>' + 
472                    '</div>' + 
473                    '</div>' + 
474                    '</div>'; 
475 
476            document.getElementById(divId).appendChild(listItem); 
477 
478            if (document.getElementById(divId).children.length > 1) { 
479                let dividerSpan = document.createElement('span'); 
480                dividerSpan.className = 'br-divider'; 
481                listItem.parentNode.insertBefore(dividerSpan, listItem); 
482
483
484 
485        function getViewCountByPK(resourcePrimKey) { 
486            return viewCountArray[resourcePrimKey] || 0; 
487
488 
489 
490        function fetchTaxonomyVocabulary(vocabularyId) { 
491            const url = '${themeDisplay.getURLPortal()}/o/headless-admin-taxonomy/v1.0/taxonomy-vocabularies/' + vocabularyId; 
492 
493            return Liferay.Util.fetch(url, {}) 
494                    .then(function(response) { 
495                        return response.json(); 
496                    }) 
497                    .catch(function(error) { 
498                        console.error('Ocorreu um erro na requisição do vocabulario', error); 
499                    }); 
500
501 
502        function renderPagination(vocabularyId) { 
503            const authorFilterDiv = document.getElementById('authorFilter'); 
504            const urlParams = new URLSearchParams(window.location.search); 
505            if (urlParams.has('cFilter') || authorFilterDiv) { 
506                return; 
507
508 
509 
510            fetchTaxonomyVocabulary(vocabularyId) 
511                    .then(function(vocabulary) { 
512                        var total = Math.ceil(vocabulary.numberOfTaxonomyCategories /  10); 
513                        var nav = $('<nav>').addClass('br-pagination d-none d-sm-flex large').attr('aria-label', 'Paginação de resultados').attr('data-total', total).attr('data-current', 1); 
514                        var ul = $('<ul>'); 
515                        nav.append(ul); 
516                        $('#infinity-pagination').append(nav); 
517                        updatePagination(); 
518                    }) 
519                    .catch(function(error) { 
520                        console.error('Ocorreu um erro na requisição do vocabulario', error); 
521                    }); 
522
523 
524        function createPageElement(page, isActive = false) { 
525            var li = $('<li>'); 
526            var a = $('<a>').addClass('page').attr('href', 'javascript:void(0)').text(page); 
527            if (isActive) { 
528                a.addClass('active'); 
529
530            li.append(a); 
531            return li; 
532
533 
534        function createEllipsisElement(pages) { 
535            var li = $('<li>').addClass('pagination-ellipsis dropdown'); 
536            var button = $('<button>').addClass('br-button circle').attr('type', 'button').attr('data-toggle', 'dropdown').attr('aria-label', 'Abrir listagem'); 
537            var i = $('<i>').addClass('fas fa-ellipsis-h').attr('aria-hidden', 'true'); 
538            button.append(i); 
539            li.append(button); 
540 
541            var div = $('<div>').addClass('br-list dropdown-menu').hide(); 
542            pages.forEach(function(page) { 
543                var a = $('<a>').addClass('br-item').attr('href', 'javascript:void(0)').text(page); 
544                div.append(a); 
545            }); 
546            li.append(div); 
547            button.click(function() { 
548                div.toggle(); 
549            }); 
550 
551            return li; 
552
553 
554        function updatePagination() { 
555            var total = parseInt($('.br-pagination').attr('data-total')); 
556            var current = parseInt($('.br-pagination').attr('data-current')); 
557            var ul = $('.br-pagination ul'); 
558            ul.empty(); 
559 
560            ul.append(createPageElement(1)); 
561 
562            if (current > 4) { 
563                var pagesBefore = Array.from({length: current - 4}, (_, i) => i + 2); 
564                ul.append(createEllipsisElement(pagesBefore)); 
565
566 
567            for (var i = Math.max(2, current - 2); i <= Math.min(total - 1, current + 2); i++) { 
568                var isActive = i === current; 
569                ul.append(createPageElement(i, isActive)); 
570
571 
572            if (current < total - 3) { 
573                var pagesAfter = Array.from({length: total - current - 3}, (_, i) => i + current + 3); 
574                ul.append(createEllipsisElement(pagesAfter)); 
575
576 
577            if (total > 1) { 
578                ul.append(createPageElement(total)); 
579
580 
581            $('.br-pagination .page').click(function() { 
582                var pageNumber = parseInt($(this).text()); 
583                $('.br-pagination').attr('data-current', pageNumber); 
584                fetchAndRenderCategoriesAndArticles(); 
585                document.getElementById('infinity-wrapper').classList.add('d-none'); 
586                document.getElementById('pg-load').classList.remove('d-none'); 
587            }); 
588 
589            try { 
590                $('.br-pagination .pagination-ellipsis .br-item').click(function() { 
591                    var pageNumber = parseInt($(this).text()); 
592                    $('.br-pagination').attr('data-current', pageNumber); 
593                    fetchAndRenderCategoriesAndArticles(); 
594                    document.getElementById('infinity-wrapper').classList.add('d-none'); 
595                    document.getElementById('pg-load').classList.remove('d-none'); 
596                }); 
597            } catch (error) { 
598 
599
600 
601            $('.br-pagination .page').each(function() { 
602                if ($(this).text() === current.toString()) { 
603                    $(this).addClass(''); 
604                    $(this).off('click'); 
605
606            }); 
607
608 
609        setupAutocomplete(vocabularyId) 
610    }); 
611</script> 
612 
613 
614<#function getCategoryImageSrc category> 
615    <#local CPAttachmentFileEntryLocalService =  serviceLocator.findService("com.liferay.commerce.product.service.CPAttachmentFileEntryLocalService") /> 
616    <#local ClassNameLocalService= serviceLocator.findService("com.liferay.portal.kernel.service.ClassNameLocalService") /> 
617    <#local AssetCategoryClassNameId = ClassNameLocalService.getClassNameId("com.liferay.asset.kernel.model.AssetCategory") /> 
618    <#local categoryId = category.getCategoryId() /> 
619    <#local categoryImages = CPAttachmentFileEntryLocalService.getCPAttachmentFileEntries(AssetCategoryClassNameId,categoryId,0,0,0,1) /> 
620    <#local categoryImageId = ""> 
621 
622    <#list categoryImages as curImage> 
623        <#local categoryImageId = curImage.getCPAttachmentFileEntryId() /> 
624    </#list> 
625 
626    <#if categoryImageId?has_content> 
627        <#return "${themeDisplay.getURLPortal()}/o/commerce-media/accounts/-1/images/${categoryImageId}?download=false"> 
628    <#else> 
629        <#local userLogoURL = themeDisplay.getUser().getPortraitURL(themeDisplay) /> 
630        <#return "${htmlUtil.escape(userLogoURL)}"> 
631    </#if> 
632</#function> 
633 
634<#function getCategoryCount category> 
635    <#local AssetEntryAssetCategoryRelLocalService = serviceLocator.findService("com.liferay.asset.entry.rel.service.AssetEntryAssetCategoryRelLocalService") /> 
636    <#local hasCategories = false /> 
637    <#local AssetEntryLocalService = serviceLocator.findService("com.liferay.asset.kernel.service.AssetEntryLocalService") /> 
638    <#local ClassNameLocalService= serviceLocator.findService("com.liferay.portal.kernel.service.ClassNameLocalService") /> 
639    <#local journalClassNameId = ClassNameLocalService.getClassNameId("com.liferay.journal.model.JournalArticle") /> 
640    <#local categoryCount = 0 /> 
641    <#local assetEntryAssetCategoryRels = AssetEntryAssetCategoryRelLocalService.getAssetEntryAssetCategoryRelsByAssetCategoryId(category.getCategoryId())> 
642    <#list assetEntryAssetCategoryRels as assetEntryAssetCategoryRel> 
643        <#local assetEntry = AssetEntryLocalService.getAssetEntry(assetEntryAssetCategoryRel.getAssetEntryId())> 
644        <#if assetEntry.getClassNameId() == journalClassNameId > 
645            <#local categoryCount = categoryCount + 1 /> 
646        </#if> 
647    </#list> 
648    <#if categoryCount == 1> 
649        <#return "${categoryCount}"> 
650    <#else> 
651 
652        <#return "${categoryCount}"> 
653    </#if> 
654</#function>