1jQuery(window).on('load', function () { 2 3 4 // Aqui eu consigo alternar entre a versao paginada ou normal, apenas pela classe 5 // vamos deixar tudo no mesmo arquivo, e mudar a classe atraves do elementor. 6 // vamos precisar passar algumas informacoes para o JS (numero de paginas, etc) 7 8 // Talvez de para deixar o packery etc tudo na mesma variavel, porque o packery tambem pode ser paginado 9 10 if ( jQuery( ".elpt-portfolio-content" ).length ) { 11 // IMPORTANT: Use :not(.elpt-portfolio-content-isotope-pro) to prevent double Isotope 12 // initialization. Elements with -pro class are handled by $container for pagination. 13 14 //Isotope Layout (masonry without pagination) 15 var $grid = jQuery('.elpt-portfolio-content-isotope:not(.elpt-portfolio-content-isotope-pro)').isotope({ 16 //layoutMode: 'packery', 17 layoutMode: 'masonry', 18 itemSelector: '.portfolio-item-wrapper' 19 }); 20 21 $grid.imagesLoaded().progress( function() { 22 $grid.isotope('layout'); 23 }); 24 25 //Packery Layout (without pagination) 26 var $packery = jQuery('.elpt-portfolio-content-packery:not(.elpt-portfolio-content-isotope-pro)').isotope({ 27 layoutMode: 'packery', 28 itemSelector: '.portfolio-item-wrapper' 29 }); 30 31 $packery.imagesLoaded().progress( function() { 32 $packery.isotope('layout'); 33 }); 34 35 //fitRows Layout (for Special Grid 7 and similar, without pagination) 36 var $fitrows = jQuery('.elpt-portfolio-content-fitrows:not(.elpt-portfolio-content-isotope-pro)').isotope({ 37 layoutMode: 'fitRows', 38 itemSelector: '.portfolio-item-wrapper' 39 }); 40 41 $fitrows.imagesLoaded().progress( function() { 42 $fitrows.isotope('layout'); 43 }); 44 45 /* 46 * Paginated Isotope 47 */ 48 //https://codepen.io/TimRizzo/details/ervrRq 49 //https://codepen.io/Igorxp5/pen/ojJLQE 50 51 var itemSelector = ".portfolio-item-wrapper"; 52 53 // Support masonry, packery, and fitRows pagination 54 var $container = jQuery('.elpt-portfolio-content-isotope-pro').isotope({ 55 layoutMode: 'masonry', 56 itemSelector: itemSelector 57 }); 58 59 // Check layout mode based on additional classes 60 if ($container.hasClass('elpt-portfolio-content-fitrows')) { 61 // Special Grid 7 - Alternate Rows 1 62 $container.isotope('option', { layoutMode: 'fitRows' }); 63 } else if ($container.hasClass('elpt-portfolio-content-packery')) { 64 // Grid Builder 65 $container.isotope('option', { layoutMode: 'packery' }); 66 } 67 68 $container.imagesLoaded().progress( function() { 69 $container.isotope('layout'); 70 }); 71 72 // Pagination Variables 73 var itemsPerPageDefault = 10; // Default value 74 75 // Prefer the per-container data attribute (works in shortcode + Elementor 76 // contexts and per container). Fall back to the legacy gridSettings global. 77 var containerPerPage = parseInt($container.attr('data-items-per-page'), 10); 78 if (!isNaN(containerPerPage) && containerPerPage > 0) { 79 itemsPerPageDefault = containerPerPage; 80 } else if (typeof gridSettings !== 'undefined' && gridSettings.itemsPerPageDefault !== undefined) { 81 itemsPerPageDefault = gridSettings.itemsPerPageDefault; 82 } 83 84 var itemsPerPage = defineItemsPerPage(); 85 var currentNumberPages = 1; 86 var currentPage = 1; 87 var currentFilter = '*'; 88 var filterAtribute = 'data-filter'; 89 var pageAtribute = 'data-page'; 90 var pagerClass = 'isotope-pager'; 91 92 // =========================================== 93 // Fixed Layout Mode: Race Condition Prevention 94 // =========================================== 95 // These variables prevent race conditions when users click rapidly on 96 // pagination or filters. Only affects Fixed Layout Mode (Grid Builder). 97 // 98 // KEY CONCEPT: Each filter/pagination click should be treated as a fresh start. 99 // We cache the original DOM order and sizes, then restore from cache on each operation. 100 var fixedLayoutEventId = 0; // Invalidates outdated event handlers 101 var fixedLayoutOriginalOrder = []; // Cache of items in original DOM order 102 var fixedLayoutInitialized = false; // Whether cache has been built 103 var fixedLayoutCompletedEventId = 0; // Tracks which eventId has completed (prevents infinite loop) 104 105 // Debug logging for Fixed Layout Mode (disabled in production) 106 var FIXED_LAYOUT_DEBUG = false;
107 function fixedLayoutLog(message, data) { 108 if (FIXED_LAYOUT_DEBUG) { 109 if (data !== undefined) { 110 console.log('[FixedLayout]', message, data); 111 } else { 112 console.log('[FixedLayout]', message); 113 } 114 } 115 } 116 117 /** 118 * Initialize Fixed Layout cache - stores original DOM order of all items. 119 * This should be called once on page load for Fixed Layout grids. 120 */ 121 function initFixedLayoutCache($containerElement) { 122 if (fixedLayoutInitialized) { 123 return; 124 } 125 126 var $allItems = $containerElement.children('.portfolio-item-wrapper'); 127 fixedLayoutOriginalOrder = []; 128 129 $allItems.each(function(index) { 130 fixedLayoutOriginalOrder.push({ 131 element: this, 132 originalIndex: index 133 }); 134 }); 135 136 fixedLayoutInitialized = true; 137 fixedLayoutLog('Cache initialized with ' + fixedLayoutOriginalOrder.length + ' items'); 138 } 139 140 /** 141 * Reset all position classes to original state based on cached order. 142 * This ensures each operation starts from a clean, consistent state. 143 */ 144 function resetFixedLayoutPositions($containerElement) { 145 var $allItems = $containerElement.children('.portfolio-item-wrapper'); 146 147 // Remove ALL position classes from ALL items 148 $allItems.removeClass(function(index, className) { 149 return (className.match(/\belpt-grid-pos-\d+\b/g) || []).join(' '); 150 }); 151 152 fixedLayoutLog('All position classes removed'); 153 } 154 155 /** 156 * Scroll to the top of the grid container INSTANTLY (no animation). 157 * This must happen BEFORE any layout processing so user sees the grid top first. 158 * Only applies to Fixed Layout Mode (Grid Builder). 159 * 160 * Addons can modify the scroll target via 'powerfolio:scroll_target' filter. 161 * 162 * @param {jQuery} $containerElement - The grid container element 163 */ 164 function scrollToGridTop($containerElement) { 165 if (!$containerElement || $containerElement.length === 0) { 166 return; 167 } 168 169 var gridTop = $containerElement.offset().top; 170 var margin = 100; // pixels above the grid to account for headers/nav 171 var targetScroll = Math.max(0, gridTop - margin); 172 var currentScroll = jQuery(window).scrollTop(); 173 174 // Allow addons to modify scroll target (e.g., scroll to page top instead of grid top) 175 var scrollData = { 176 target: targetScroll, 177 gridTop: gridTop, 178 margin: margin, 179 container: $containerElement 180 }; 181 jQuery(document).trigger('powerfolio:scroll_target', [scrollData]); 182 targetScroll = scrollData.target; 183 184 // Only scroll if user is below the target (don't scroll up if already above) 185 if (currentScroll > targetScroll) { 186 // INSTANT scroll - no animation, must complete before layout processing 187 window.scrollTo(0, targetScroll); 188 189 fixedLayoutLog('Scrolled to: ' + targetScroll + 'px'); 190 } 191 } 192 193 // Resize handler with debounce 194 // Fixed Layout Mode requires special handling to maintain positions 195 var resizeTimeout; 196 jQuery(window).resize(function(){ 197 clearTimeout(resizeTimeout); 198 resizeTimeout = setTimeout(function() { 199 // Check if Fixed Layout Mode is enabled (only for Grid Builder) 200 var $gridBuilder = jQuery('.elpt-portfolio-content-packery.elpt-portfolio-grid-builder'); 201 var isFixedLayout = $gridBuilder.length > 0 && $gridBuilder.hasClass('elpt-fixed-layout-mode'); 202 203 if ($container.length > 0) { 204 // Has pagination: maintain current page and filter 205 if (isFixedLayout) { 206 fixedLayoutLog('Resize detected - triggering fresh layout'); 207 // For Fixed Layout, just trigger goToPage which will handle everything 208 } 209 goToPage(currentPage); 210 } else { 211 // No pagination: just refresh layout 212 if (isFixedLayout) { 213 // Fixed Layout without pagination: re-apply positions 214 applyFixedLayoutPositionsFromIsotope($packery); 215 $packery.isotope('layout'); 216 } else { 217 // Normal grids: just trigger layout refresh 218 $grid.isotope('layout'); 219 $packery.isotope('layout'); 220 $fitrows.isotope('layout'); 221 } 222 } 223 }, 150); // 150ms debounce 224 }); 225 226 227 228 // update items based on current filters 229 function changeFilter(selector) { 230 $container.isotope({ filter: selector } 231 ); } 232 233 234 function getFilterSelector() { 235 var selector = itemSelector;
236 if (currentFilter != '*') { 237 selector += currentFilter; 238 } 239 return selector; 240 } 241 242 function goToPage(n) { 243 // Check if Fixed Layout Mode is enabled (only for Grid Builder) 244 var $gridBuilder = jQuery('.elpt-portfolio-content-packery.elpt-portfolio-grid-builder'); 245 var isFixedLayout = $gridBuilder.length > 0 && $gridBuilder.hasClass('elpt-fixed-layout-mode'); 246 247 currentPage = n; 248 249 var selector = getFilterSelector(); 250 selector += '[' + pageAtribute + '="' + currentPage + '"]'; 251 252 if (isFixedLayout) { 253 // FIXED LAYOUT MODE - Fresh Start Approach 254 // ======================================== 255 // Each call to goToPage CANCELS any previous operation and starts fresh. 256 // No queuing, no waiting - just invalidate old handlers and restart. 257 // 258 // Strategy: 259 // 1. Increment eventId to invalidate ALL previous handlers 260 // 2. Initialize cache if needed (first run only) 261 // 3. Reset all position classes to clean state 262 // 4. Apply position classes for visible items (DOM order) 263 // 5. Apply Isotope filter and layout 264 // 6. On arrangeComplete, correct positions based on visual order 265 // 7. Final layout pass (deferred with setTimeout to break recursion) 266 267 // Step 1: Invalidate ALL previous operations 268 fixedLayoutEventId++; 269 var currentEventId = fixedLayoutEventId; 270 271 fixedLayoutLog('goToPage(' + n + ') START - eventId: ' + currentEventId + ' (previous operations cancelled)'); 272 273 // Step 2: Initialize cache on first run 274 initFixedLayoutCache($container); 275 276 // Step 3: Reset ALL position classes to clean state 277 resetFixedLayoutPositions($container); 278 279 // Step 4: Apply position classes for items matching selector (DOM order) 280 applyFixedLayoutPositionsForCurrentFilter($container, selector); 281 282 // Step 5: Apply Isotope filter 283 changeFilter(selector); 284 285 // Step 6: Force layout - Isotope will calculate positions 286 $container.isotope('layout'); 287 288 fixedLayoutLog('goToPage(' + n + ') - layout triggered, eventId: ' + currentEventId); 289 290 // Step 7: After Isotope arranges, correct positions and do final layout 291 $container.isotope('once', 'arrangeComplete', function() { 292 // CRITICAL: Ignore if this event is outdated (user clicked something else) 293 if (currentEventId !== fixedLayoutEventId) { 294 fixedLayoutLog('arrangeComplete SKIPPED - outdated eventId: ' + currentEventId + ', current: ' + fixedLayoutEventId); 295 return; 296 } 297 298 // Prevent infinite loop: if this eventId already completed its final pass, ignore 299 // This happens when layout() triggers another arrangeComplete 300 if (currentEventId <= fixedLayoutCompletedEventId) { 301 fixedLayoutLog('arrangeComplete SKIPPED - eventId ' + currentEventId + ' already completed'); 302 return; 303 } 304 305 fixedLayoutLog('goToPage(' + n + ') arrangeComplete - eventId: ' + currentEventId); 306 307 // Correct position classes based on Isotope's visual order 308 applyFixedLayoutPositionsFromIsotope($container); 309 310 // Mark this event as completed BEFORE the final layout 311 // This prevents the arrangeComplete triggered by layout() from running again 312 fixedLayoutCompletedEventId = currentEventId; 313 314 // Final layout pass - deferred to break synchronous chain 315 setTimeout(function() { 316 // Check again - user might have clicked during the timeout 317 if (currentEventId !== fixedLayoutEventId) { 318 fixedLayoutLog('setTimeout SKIPPED - outdated eventId: ' + currentEventId); 319 return; 320 } 321 322 fixedLayoutLog('goToPage(' + n + ') FINAL layout - eventId: ' + currentEventId); 323 $container.isotope('layout'); 324 325 fixedLayoutLog('goToPage(' + n + ') COMPLETE - eventId: ' + currentEventId); 326 327 // Trigger event for addons (e.g., parallax effects) 328 jQuery(document).trigger('powerfolio:layout_complete', [$container, currentPage, currentFilter]); 329 }, 0); 330 }); 331 } else { 332 // Normal mode: just apply filter (unchanged behavior) 333 changeFilter(selector); 334 335 // Trigger event for addons after layout settles 336 setTimeout(function() { 337 jQuery(document).trigger('powerfolio:layout_complete', [$container, currentPage, currentFilter]); 338 }, 100); 339 } 340 } 341 342 /** 343 * Fixed Layout Mode: Apply position classes based on current filter selector 344 * This applies classes BEFORE Isotope calculates positions, using DOM order. 345 * Used to ensure items have correct sizes during initial layout calculation. 346 */ 347 function applyFixedLayoutPositionsForCurrentFilter($containerElement, filterSelector) { 348 var $allItems = $containerElement.children('.portfolio-item-wrapper'); 349 350 // Remove all position classes from all items 351 $allItems.removeClass(function(index, className) { 352 return (className.match(/\belpt-grid-pos-\d+\b/g) || []).join(' '); 353 }); 354 355 // Get items matching the filter selector 356 var $matchingItems = $allItems.filter(filterSelector); 357 358 // Apply position classes in DOM order 359 $matchingItems.each(function(index) { 360 jQuery(this).addClass('elpt-grid-pos-' + (index + 1)); 361 }); 362 } 363 364 function defineItemsPerPage() { 365 var pages = itemsPerPageDefault; 366 367 return pages; 368 } 369 370 function setPagination() { 371 372 var SettingsPagesOnItems = function(){ 373 374 var item = 1; 375 var page = 1; 376 var selector = getFilterSelector(); 377 378 $container.children(selector).each(function(){ 379 if( item > itemsPerPage ) { 380 page++; 381 item = 1; 382 } 383 jQuery(this).attr(pageAtribute, page); 384 item++; 385 }); 386 387 currentNumberPages = page; 388 389 }(); 390 391 var CreatePagers = function() { 392 393 var $isotopePager = ( jQuery('.'+pagerClass).length == 0 ) ? jQuery('<div class="'+pagerClass+'"></div>') : jQuery('.'+pagerClass); 394 395 $isotopePager.html(''); 396 397 for( var i = 0; i < currentNumberPages; i++ ) { 398 var $pager = jQuery('<a href="javascript:void(0);" class="pager" '+pageAtribute+'="'+(i+1)+'"></a>'); 399 $pager.html(i+1); 400 401 $pager.click(function(){ 402 jQuery('.isotope-pager .active').removeClass('active'); 403 jQuery(this).addClass('active'); 404 var page = jQuery(this).eq(0).attr(pageAtribute); 405 406 // Scroll to grid top BEFORE processing (Fixed Layout Mode) 407 var $gridBuilder = jQuery('.elpt-portfolio-content-packery.elpt-portfolio-gri
407d-builder'); 408 if ($gridBuilder.length > 0 && $gridBuilder.hasClass('elpt-fixed-layout-mode')) { 409 scrollToGridTop($container); 410 } 411 412 goToPage(page); 413 }); 414 415 $pager.appendTo($isotopePager); 416 } 417 418 $container.after($isotopePager); 419 420 }(); 421 422 } 423 424 setPagination(); 425 goToPage(1); 426 427 /** 428 * Fixed Layout Mode: Apply position classes based on Isotope's visual order 429 * 430 * This function uses Isotope's internal filteredItems array to get elements 431 * in their correct visual order (not DOM order). This is essential for 432 * Fixed Layout Mode where CSS classes determine element sizes/positions. 433 * 434 * @param {jQuery} $isotopeContainer - The Isotope container element 435 */ 436 function applyFixedLayoutPositionsFromIsotope($isotopeContainer) { 437 var isotope = $isotopeContainer.data('isotope'); 438 if (!isotope) { 439 return; 440 } 441 442 var $allItems = $isotopeContainer.children('.portfolio-item-wrapper'); 443 444 // Remove all position classes from all items 445 $allItems.removeClass(function(index, className) { 446 return (className.match(/\belpt-grid-pos-\d+\b/g) || []).join(' '); 447 }); 448 449 // Get filtered items in Isotope's visual order 450 var filteredItems = isotope.filteredItems; 451 if (!filteredItems || filteredItems.length === 0) { 452 return; 453 } 454 455 // Apply position classes based on visual order 456 filteredItems.forEach(function(item, index) { 457 jQuery(item.element).addClass('elpt-grid-pos-' + (index + 1)); 458 }); 459 } 460 461 /** 462 * Fixed Layout Mode: Apply position classes for filtered items (pre-pagination) 463 * 464 * This function is called BEFORE pagination is applied. It assigns position 465 * classes to ALL items matching the filter, which will then be narrowed down 466 * by pagination. Uses DOM order since Isotope hasn't filtered yet. 467 * 468 * Note: This is kept for the initial filter application. After Isotope 469 * arranges items, applyFixedLayoutPositionsFromIsotope() corrects the order. 470 * 471 * @param {jQuery} $containerElement - The grid container element 472 * @param {string} filterValue - The filter selector (e.g., '.category-1' or '*') 473 */ 474 function applyFixedLayoutFilter($containerElement, filterValue) { 475 var $allItems = $containerElement.children('.portfolio-item-wrapper'); 476 477 // Determine which items match the filter 478 var $matchingItems; 479 if (filterValue === '*') { 480 $matchingItems = $allItems; 481 } else { 482 $matchingItems = $allItems.filter(filterValue); 483 } 484 485 // Remove all position classes from all items 486 $allItems.removeClass(function(index, className) { 487 return (className.match(/\belpt-grid-pos-\d+\b/g) || []).join(' '); 488 }); 489 490 // Apply initial position classes (will be corrected after Isotope arranges) 491 $matchingItems.each(function(visualIndex) { 492 jQuery(this).addClass('elpt-grid-pos-' + (visualIndex + 1)); 493 }); 494 } 495 496 // On Click Actions 497 jQuery('.elpt-portfolio-filter').on('click', 'button', function () { 498 jQuery('.elpt-portfolio-filter button').removeClass('item-active'); 499 jQuery(this).addClass('item-active'); 500 501 var filterValue = jQuery(this).attr(filterAtribute); 502 var filter = filterValue; 503 currentFilter = filter; 504 505 // Check if Fixed Layout Mode is enabled (only for Grid Builder) 506 var $gridBuilder = jQuery('.elpt-portfolio-content-packery.elpt-portfolio-grid-builder'); 507 var isFixedLayout = $gridBuilder.hasClass('elpt-fixed-layout-mode'); 508 509 if (isFixedLayout) { 510 fixedLayoutLog('Filter clicked: ' + filterValue); 511 512 // If pagination is NOT enabled, apply Isotope filter directly 513 if ($container.length === 0) { 514 // No pagination: apply Isotope filter on $packery 515 // Use Fresh Start Approach - cancel previous and restart 516 517 fixedLayoutEventId++; 518 var currentEventId = fixedLayoutEventId; 519 520 fixedLayoutLog('Filter START (no pagination) - eventId: ' + currentEventId); 521 522 // Reset and apply position classes 523 resetFixedLayoutPositions($packery); 524 applyFixedLayoutFilter($gridBuilder, filterValue); 525 526 $packery.isotope({ filter: filterValue }); 527 $packery.isotope('layout'); 528 529 // After Isotope arranges, correct the position classes and do final layout 530 $packery.isotope('once', 'arrangeComplete', function() { 531 if (currentEventId !== fixedLayoutEventId) { 532 fixedLayoutLog('Filter arrangeComplete SKIPPED - outdated eventId: ' + currentEventId); 533 return; 534 } 535 536 // Prevent infinite loop: if this eventId already completed, ignore 537 if (currentEventId <= fixedLayoutCompletedEventId) { 538 fixedLayoutLog('Filter arrangeComplete SKIPPED - eventId ' + currentEventId + ' already completed'); 539 return; 540 } 541 542 fixedLayoutLog('Filter arrangeComplete (no pagination) - eventId: ' + currentEventId); 543 544 applyFixedLayoutPositionsFromIsotope($packery); 545 546 // Mark as completed before final layout 547 fixedLayoutCompletedEventId = currentEventId; 548 549 setTimeout(function() { 550 if (currentEventId !== fixedLayoutEventId) { 551 return; 552 } 553 554 fixedLayoutLog('Filter FINAL layout (no pagination) - eventId: ' + currentEventId); 555 $packery.isotope('layout'); 556 557 fixedLayoutLog('Filter COMPLETE (no pagination) - eventId: ' + currentEventId); 558 559 // Trigger event for addons (Fixed Layout without pagination) 560 jQuery(document).trigger('powerfolio:layout_complete', [$packery, 1, filterValue]); 561 }, 0); 562 }); 563 } 564 // If pagination IS enabled, we'll handle it below with setPagination + goToPage 565 } else { 566 // Normal: Use Isotope filter (current behavior for non-Fixed Layout) 567 $grid.isotope({ 568 filter: filterValue 569 }); 570 $packery.isotope({ 571 filter: filterValue 572 }); 573 $fitrows.isotope({ 574 filter: filterValue 575 }); 576 577 // Trigger event for addons after filter layout settles (non-paginated grids) 578 if ($container.length === 0) { 579 setTimeout(function() { 580 // Pass whichever grid is active 581 var $activeGrid = $packery.length > 0 ? $packery : ($grid.length > 0 ? $grid : $fitrows); 582 jQuery(document).trigger('powerfolio:layout_complete', [$activeGrid, 1, filterValue]); 583 }, 100); 584 } 585 } 586 587 // Only call pagination functions if pagination is enabled 588 // ($container targets .elpt-portfolio-content-isotope-pro which only exists with pagination) 589 if ($container.length > 0) { 590 if (isFixedLayout) { 591 // Scroll to grid top BEFORE processing 592 scrollToGridTop($container); 593 fixedLayoutLog('Filter with pagination - calling setPagination + goToPage(1)'); 594 } 595 setPagination(); 596 goToPage(1); 597 } 598 }); 599 600 // Trigger initialization event for addons 601 // Provides references to all grid containers for addon initialization 602 jQuery(document).trigger('powerfolio:grid_initialized', [{ 603 grid: $grid, 604 packery: $packery, 605 fitrows: $fitrows, 606 container: $container 607 }]); 608 609 } 610 611});
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.