1(function () { 2 const initializedAttr = "data-carrossel-initialized"; 3 const automaticInterval = 3000; 4 5 function rafThrottle(callback) { 6 let ticking = false; 7 8 return (...args) => { 9 if (ticking) { 10 return; 11 } 12 13 ticking = true; 14 15 window.requestAnimationFrame(() => { 16 callback(...args); 17 ticking = false; 18 }); 19 }; 20 } 21 22 function initCarousel(carrossel) { 23 if (!carrossel || carrossel.hasAttribute(initializedAttr)) { 24 return; 25 } 26 27 const mobileMedia = window.matchMedia("(max-width: 61.9375em)"); 28 const track = carrossel.querySelector(".carrossel__track"); 29 30 if (!track || !track.children.length) { 31 return; 32 } 33 34 const controls = carrossel.querySelector(".carrossel__controles"); 35 const arrows = controls?.querySelectorAll(".carrossel__seta") || []; 36 const allowMobileAuto = carrossel.classList.contains( 37 "depoimentos__carrossel-wrapper" 38 ); 39 let metrics = null; 40 let autoScrollId = null; 41 42 const getMetrics = () => { 43 const firstChild = track.children[0]; 44 45 if (!firstChild) { 46 return null; 47 } 48 49 const styles = window.getComputedStyle(track); 50 const gap = Number.parseFloat(styles.gap) || 0; 51 const paddingLeft = Number.parseFloat(styles.paddingLeft) || 0; 52 const paddingRight = Number.parseFloat(styles.paddingRight) || 0; 53 const childWidth = firstChild.getBoundingClientRect().width; 54 const trackWidth = track.getBoundingClientRect().width; 55 const trackFullWidth = 56 track.children.length * childWidth + 57 (track.children.length - 1) * gap + 58 paddingLeft + 59 paddingRight; 60 61 return { 62 childWidth, 63 gap, 64 paddingLeft, 65 trackWidth, 66 trackFullWidth, 67 }; 68 }; 69 70 const updateControls = () => { 71 metrics = getMetrics(); 72 73 if (!metrics) { 74 return; 75 } 76 77 const scrollLeft = track.scrollLeft; 78 const maxScrollLeft = Math.max( 79 0, 80 metrics.trackFullWidth - metrics.trackWidth - metrics.gap 81 ); 82 const canScrollRight = scrollLeft < maxScrollLeft; 83 const canScrollLeft = scrollLeft > metrics.paddingLeft; 84 85 track.classList.toggle("js-scrollable-right", canScrollRight); 86 track.classList.toggle("js-scrollable-left", canScrollLeft); 87 track.toggleAttribute("scrolled", scrollLeft > 0); 88 89 if (!controls || arrows.length < 2) { 90 return; 91 } 92 93 if (mobileMedia.matches && !allowMobileAuto) { 94 controls.classList.add("carrossel__controles--mobile"); 95 controls.classList.add("carrossel__controles--semscroll"); 96 arrows.forEach((arrow) => { 97 arrow.classList.remove("carrossel__seta--habilitada"); 98 99 if ("disabled" in arrow) { 100 arrow.disabled = true; 101 } 102 }); 103 return; 104 } 105 106 controls.classList.remove("carrossel__controles--mobile"); 107 108 controls.classList.toggle( 109 "carrossel__controles--semscroll", 110 !canScrollLeft && !canScrollRight 111 ); 112 arrows[0].classList.toggle("carrossel__seta--habilitada", canScrollLeft); 113 arrows[1].classList.toggle("carrossel__seta--habilitada", canScrollRight); 114 115 if ("disabled" in arrows[0]) { 116 arrows[0].disabled = !canScrollLeft; 117 } 118 119 if ("disabled" in arrows[1]) { 120 arrows[1].disabled = !canScrollRight; 121 } 122 }; 123 124 const scrollByAmount = (direction, isUserInteraction) => { 125 if (!metrics) { 126 metrics = getMetrics(); 127 } 128 129 if (!metrics) { 130 return; 131 } 132 133 if (isUserInteraction) { 134 track.classList.add("js-user-interaction"); 135 } 136 137 track.scrollTo({ 138 left: Math.max(0, track.scrollLeft + metrics.childWidth * direction), 139 behavior: "smooth", 140 }); 141 }; 142 143 const stopAutoScroll = () => { 144 if (autoScrollId) { 145 window.clearInterval(autoScrollId); 146 autoScrollId = null; 147 } 148 }; 149 150 const isVisibleEnough = (minimumRatio = 0.2) => { 151 const rect = carrossel.getBoundingClientRect();
152 const viewportHeight = 153 window.innerHeight || document.documentElement.clientHeight; 154 155 if (rect.height <= 0 || viewportHeight <= 0) { 156 return false; 157 } 158 159 const visibleHeight = 160 Math.min(rect.bottom, viewportHeight) - Math.max(rect.top, 0); 161 162 return visibleHeight / rect.height >= minimumRatio; 163 }; 164 165 const startAutoScroll = () => { 166 if ( 167 !carrossel.classList.contains("carrossel--automatico") || 168 autoScrollId || 169 (mobileMedia.matches && !allowMobileAuto) 170 ) { 171 return; 172 } 173 174 autoScrollId = window.setInterval(() => { 175 if (track.classList.contains("js-user-interaction")) { 176 stopAutoScroll(); 177 return; 178 } 179 180 updateControls(); 181 182 if (track.classList.contains("js-scrollable-right")) { 183 scrollByAmount(1, false); 184 } else { 185 track.scrollTo({ 186 left: 0, 187 behavior: "smooth", 188 }); 189 } 190 }, automaticInterval); 191 }; 192 193 track.addEventListener("scroll", rafThrottle(updateControls), { 194 passive: true, 195 }); 196 track.addEventListener( 197 "pointerdown", 198 () => { 199 track.classList.add("js-user-interaction"); 200 stopAutoScroll(); 201 }, 202 { passive: true } 203 ); 204 205 if (arrows.length >= 2) { 206 arrows[0].addEventListener("click", () => scrollByAmount(-1, true)); 207 arrows[1].addEventListener("click", () => scrollByAmount(1, true)); 208 } 209 210 window.addEventListener( 211 "resize", 212 rafThrottle(() => { 213 if (mobileMedia.matches && !allowMobileAuto) { 214 stopAutoScroll(); 215 } 216 217 updateControls(); 218 }), 219 { 220 passive: true, 221 } 222 ); 223 224 if ( 225 "IntersectionObserver" in window && 226 carrossel.classList.contains("carrossel--automatico") 227 ) { 228 const visibilityObserver = new IntersectionObserver( 229 (entries) => { 230 entries.forEach((entry) => { 231 if (entry.isIntersecting) { 232 startAutoScroll(); 233 } else { 234 stopAutoScroll(); 235 } 236 }); 237 }, 238 { 239 threshold: 0.2, 240 } 241 ); 242 243 visibilityObserver.observe(carrossel); 244 245 if (isVisibleEnough()) { 246 startAutoScroll(); 247 } 248 } else { 249 startAutoScroll(); 250 } 251 252 updateControls(); 253 carrossel.setAttribute(initializedAttr, "true"); 254 } 255 256 function initVisibleCarousels() { 257 const carousels = document.querySelectorAll(".carrossel"); 258 259 if (!carousels.length) { 260 return; 261 } 262 263 const priorityCarousels = Array.from(carousels).filter((carrossel) => { 264 if (carrossel.hasAttribute(initializedAttr)) { 265 return false; 266 } 267 268 // Na home, o carrossel de Cursos e Formacoes precisa nascer pronto 269 // visualmente, mesmo antes de entrar no viewport. 270 return Boolean( 271 document.body.classList.contains("pg-home") && 272 carrossel.closest(".cursos") 273 ); 274 }); 275 276 priorityCarousels.forEach(initCarousel); 277 278 if (!("IntersectionObserver" in window)) { 279 carousels.forEach(initCarousel); 280 return; 281 } 282 283 const observer = new IntersectionObserver( 284 (entries) => { 285 entries.forEach((entry) => { 286 if (!entry.isIntersecting) { 287 return; 288 } 289 290 initCarousel(entry.target); 291 observer.unobserve(entry.target); 292 }); 293 }, 294 { 295 rootMargin: "300px 0px", 296 threshold: 0.01, 297 } 298 ); 299 300 carousels.forEach((carrossel) => { 301 if (carrossel.hasAttribute(initializedAttr)) { 302 return; 303 } 304 305 observer.observe(carrossel); 306 }); 307 } 308 309 window.checarCarrosselAutomatico = (carrossel) => { 310 if (carrossel instanceof Element) { 311 initCarousel(carrossel); 312 return; 313 } 314 315 initVisibleCarousels(); 316 }; 317 318 document.addEventListener("carrossel:contentReady", (event) => { 319 initCarousel(event.detail?.carrossel); 320 }); 321 322 if (document.readyState === "loading") { 323 document.addEventListener("DOMContentLoaded", initVisibleCarousels, { 324 once: true, 325 }); 326 } else { 327 initVisibleCarousels(); 328 } 329})(); 330 331(function () { 332 const initializedAttr = "data-course-cards-carousel-initialized"; 333 const automaticInterval = 3000; 334 const mobileMedia = window.matchMedia 335 ? window.matchMedia("(max-width: 61.9375em)") 336 : { matches: false }; 337 const reducedMotionMedia = window.matchMedia 338 ? window.matchMedia("(prefers-reduced-motion: reduce)") 339 : { matches: false }; 340 341 function initCourseCardsCarousel(wrapper) { 342 if (!wrapper || wrapper.hasAttribute(initializedAttr)) { 343 return; 344 } 345 346 const track = wrapper.querySelector(".cards-track"); 347 const nav = wrapper.querySelector(".carrossel-nav"); 348 349 if (!track || !track.children.length) { 350 return; 351 } 352 353 const arrows = nav ? nav.querySelectorAll(".carrossel-nav__btn") : []; 354 const noAuto = wrapper.hasAttribute("data-cards-no-auto"); 355 let autoId = null; 356 let userInteracted = false;
357 358 function updateArrows() { 359 if (arrows.length < 2) { 360 return; 361 } 362 363 const max = getMaxScroll(); 364 const left = track.scrollLeft; 365 const canPrev = left > 1; 366 const canNext = left < max - 1; 367 368 arrows[0].toggleAttribute("hidden", !canPrev); 369 arrows[1].toggleAttribute("hidden", !canNext); 370 } 371 372 function getStep() { 373 const first = track.children[0]; 374 const second = track.children[1]; 375 376 if (!first) { 377 return 0; 378 } 379 380 const cardWidth = first.getBoundingClientRect().width; 381 const gap = second 382 ? second.getBoundingClientRect().left - first.getBoundingClientRect().right 383 : Number.parseFloat(window.getComputedStyle(track).gap) || 0; 384 385 return cardWidth + gap; 386 } 387 388 function getMaxScroll() { 389 return track.scrollWidth - track.clientWidth; 390 } 391 392 function scrollByStep(direction) { 393 const step = getStep(); 394 395 track.scrollTo({ 396 left: track.scrollLeft + step * direction, 397 behavior: "smooth", 398 }); 399 } 400 401 function pauseAuto() { 402 if (autoId) { 403 window.clearInterval(autoId); 404 autoId = null; 405 } 406 } 407 408 function stopAuto() { 409 userInteracted = true; 410 pauseAuto(); 411 } 412 413 // Com "reduzir movimento" ligado no sistema o carrossel NÃO anda sozinho, e 414 // isso é deliberado: conteúdo que se move sozinho é exatamente o que a 415 // preferência pede para desligar (e a WCAG 2.2.2 exige um jeito de parar 416 // qualquer movimento acima de 5s). Parece "travado" para quem não sabe que 417 // tem a opção ligada â por isso a seta continua visÃvel e o track continua 418 // arrastável: quem quiser ver o resto navega, ninguém fica sem os cards. 419 function startAuto() { 420 if ( 421 noAuto || 422 autoId || 423 userInteracted || 424 mobileMedia.matches || 425 reducedMotionMedia.matches 426 ) { 427 return; 428 } 429 430 autoId = window.setInterval(() => { 431 if (userInteracted) { 432 stopAuto(); 433 return; 434 } 435 436 if (track.scrollLeft >= getMaxScroll() - 2) { 437 track.scrollTo({ left: 0, behavior: "smooth" }); 438 } else { 439 scrollByStep(1); 440 } 441 }, automaticInterval); 442 } 443 444 if (arrows.length >= 2) { 445 arrows[0].addEventListener("click", () => { 446 stopAuto(); 447 scrollByStep(-1); 448 }); 449 arrows[1].addEventListener("click", () => { 450 stopAuto(); 451 scrollByStep(1); 452 }); 453 } 454 455 track.addEventListener("pointerdown", stopAuto, { passive: true }); 456 track.addEventListener("wheel", stopAuto, { passive: true }); 457 track.addEventListener("scroll", updateArrows, { passive: true }); 458 window.addEventListener( 459 "resize", 460 () => { 461 if (mobileMedia.matches) { 462 pauseAuto(); 463 } 464 updateArrows(); 465 }, 466 { passive: true } 467 ); 468 469 updateArrows(); 470 471 // O primeiro updateArrows() roda no DOMContentLoaded, e o CSS que dá largura 472 // aos cards chega DEPOIS (media="print" + onload). Nesse instante o track ainda 473 // não transborda, updateArrows conclui "não há para onde rolar" e esconde as 474 // DUAS setas â em produção o carrossel nascia sem seta nenhuma até o primeiro 475 // scroll. No DDEV não aparecia: sem CDN, o CSS chega antes de o JS medir. 476 // Remedir quando o layout assentar resolve nos dois casos. 477 window.addEventListener("load", updateArrows, { passive: true, once: true }); 478 479 if ("ResizeObserver" in window) { 480 const layoutObserver = new ResizeObserver(() => updateArrows()); 481 layoutObserver.observe(track); 482 if (track.children[0]) { 483 layoutObserver.observe(track.children[0]); 484 } 485 } 486 487 if ("IntersectionObserver" in window) { 488 const observer = new IntersectionObserver( 489 (entries) => { 490 entries.forEach((entry) => { 491 if (entry.isIntersecting) { 492 startAuto(); 493 } else { 494 pauseAuto(); 495 } 496 }); 497 }, 498 { threshold: 0.2 } 499 ); 500 501 observer.observe(wrapper); 502 } else { 503 startAuto(); 504 } 505 506 // A preferência do sistema muda com a página aberta (o Windows desliga 507 // animação sozinho ao entrar em economia de bateria, e o usuário pode 508 // trocar no painel). Sem ouvir a mudança, o valor lido no boot valeria 509 // para sempre â e ligar de volta só surtiria efeito no próximo reload. 510 if (typeof reducedMotionMedia.addEventListener === "function") { 511 reducedMotionMedia.addEventListener("change", (event) => { 512 if (event.matches) { 513 pauseAuto(); 514 return; 515 } 516 517 const rect = wrapper.getBoundingClientRect();
518 const viewportHeight = 519 window.innerHeight || document.documentElement.clientHeight; 520 521 if (rect.bottom > 0 && rect.top < viewportHeight) { 522 startAuto(); 523 } 524 }); 525 } 526 527 wrapper.setAttribute(initializedAttr, "true"); 528 } 529 530 function initCourseCardsCarousels() { 531 document 532 .querySelectorAll(".carrossel-wrapper") 533 .forEach(initCourseCardsCarousel); 534 } 535 536 if (document.readyState === "loading") { 537 document.addEventListener("DOMContentLoaded", initCourseCardsCarousels, { 538 once: true, 539 }); 540 } else { 541 initCourseCardsCarousels(); 542 } 543})();
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.