1/******************************************************************** 2 * OWN CAROUSEL VERSÃO ROBUSTA v3 3 * ------------------------------------------------------------- 4 * - Corrige itemPerRow=1 (gapWidth=0) 5 * - Corrige poucos itens (sem branco / sem travar) 6 * - Loop infinito robusto (clones corretos + teleporte no transitionend) 7 * - Evita width=0 na inicialização (retry com rAF) 8 * - useVisiblePerRow robusto (considera largura real + gap/margin) 9 * - Dots com dois modos: 10 * dotsMode: "page" (páginas/offsets) | "item" (1 dot por card) 11 * - responsive(): ignora containers não inicializados (sem crash) 12 ********************************************************************/ 13 14HTMLElement.prototype.ownCarousel = function (options) { 15 const { 16 itemPerRow = 4, 17 itemWidth = 24, 18 loop = true, 19 responsive = {}, 20 draggable = true, 21 mouseWheel = false, 22 autoplay = 0, 23 stopAutoplayWhenHover = true, 24 nav = false, 25 isRTL = false, 26 dots = false, 27 dotsMode = "page", // "page" | "item" 28 useVisiblePerRow = false 29 } = options; 30 31 /* ------------------------------------------------------- 32 * ELEMENTOS BASE 33 * ------------------------------------------------------- */ 34 this.carousel = this.querySelector(".own-carousel"); 35 this.carouselOuter = 36 this.querySelector(".own-carousel__outer") || 37 this.querySelector("[class*='own-carousel__outer']"); 38 39 if (!this.carousel || !this.carouselOuter) return; 40 41 // live collection 42 this.carouselItem = this.carousel.children; 43 44 this.index = 0; 45 this.numberOfItem = this.carouselItem.length; 46 47 // prevent double init (importante em Orchard com partial render) 48 if (this._ownCarouselInitialized) return; 49 this._ownCarouselInitialized = true; 50 51 this.useVisiblePerRow = !!useVisiblePerRow; 52 this.dotsMode = (dotsMode === "item") ? "item" : "page"; 53 54 // Controles de navegação (setas) 55 this.controlsEl = this.querySelector(".own-carousel__control") || null; 56 if (!this.controlsEl) { 57 const wrapper = this.closest(".widget-servicos") || this.closest("[id^='carousel-']"); 58 if (wrapper) this.controlsEl = wrapper.querySelector(".own-carousel__control"); 59 } 60 61 const dotsEnabled = !!dots; 62 const dotsContainer = this.querySelector(".own-carousel__dots"); 63 this.dots = []; 64 65 /* ------------------------------------------------------- 66 * CONFIG BASE 67 * ------------------------------------------------------- */ 68 this.itemPerRowBig = (this.itemPerRow = itemPerRow); 69 this.itemWidthBig = (this.itemWidth = itemWidth); 70 this.responsive = responsive; 71 72 this.gapWidth = (this.itemPerRow > 1) 73 ? (100 - this.itemPerRow * this.itemWidth) / (this.itemPerRow - 1) 74 : 0; 75 76 this.style.setProperty("--width", `${this.itemWidth}%`); 77 this.style.setProperty("--margin", `${this.gapWidth}%`); 78 79 /* ------------------------------------------------------- 80 * helper: quantos por linha devemos considerar 81 * ------------------------------------------------------- */ 82 this.visiblePerRow = null; 83 84 this.getPerRow = () => { 85 if (!this.useVisiblePerRow) return this.itemPerRow; 86 return this.visiblePerRow || this.itemPerRow; 87 }; 88 89 /* ------------------------------------------------------- 90 * FUNÇÕES DE VISIBILIDADE 91 * ------------------------------------------------------- */ 92 this.hasScroll = () => this.numberOfItem > this.getPerRow(); 93 94 this.hasNavigation = () => { 95 return !!nav || !!dotsEnabled; 96 }; 97 98 this.isLoopEnabled = () => { 99 return !!loop && this.hasNavigation() && this.hasScroll(); 100 }; 101 102 /* ------------------------------------------------------- 103 * VISÍVEIS POR LINHA (modo opcional) ROBUSTO 104 * - usa carouselOuter (área útil real) 105 * - considera largura real do item + gap/margin 106 * ------------------------------------------------------- */ 107 this.updateVisiblePerRowAndOuterWidth = () => { 108 if (!this.useVisiblePerRow) return; 109 if (!this.carouselOuter || !this.carouselItem || !this.carouselItem[0]) return; 110 111 // MEDIR A LARGURA DISPONÍVEL REAL (não o outer que você trava) 112 // O viewport do carrossel é o PAI do outer (normalmente o container com overflow hidden) 113 const host = this.carouselOuter.parentElement || this; 114 const hostWidth = host.getBoundingClientRect().width; 115 if (!hostWidth) return; 116 117 // mede o item raiz correto (inclui padding) 118 const firstItem = this.carousel.querySelector(".own-carousel__item") || this.carouselItem[0]; 119 const itemWidthPx = firstItem.getBoundingClientRect().width; 120 if (!itemWidthPx) return; 121 122 // spacing real: preferir STEP - itemWidth 123 // (margin-left no :not(:first-child) faz o firstItem ter margin=0, então não use isso) 124 let spacing = 0; 125 126 // garante recomputeStep rodado 127 if (!this.step || !Number.isFinite(this.step) || this.step <= 0) { 128 this.recomputeStep?.(); 129 } 130 if (this.step && Number.isFinite(this.step) && this.step > 0) { 131 spacing = Math.max(0, this.step - itemWidthPx); 132 } 133 134 // fallback: mede distância entre item[0] e item[1] quando existe 135 if (!spacing && this.carouselItem.length > 1) { 136 const a = this.carouselItem[0].getBoundingClientRect(); 137 const b = this.carouselItem[1].getBoundingClientRect(); 138 spacing = Math.max(0, (b.left - a.left) - a.width); 139 } 140 141 const footprint = itemWidthPx + spacing; 142 if (!footprint) return; 143 144 // +spacing no numerador ajuda quando encaixa "no limite" 145 const maxBySpace = Math.max(1, Math.floor((hostWidth + spacing) / footprint)); 146 147 // respeita o limite configurado (itemPerRow) 148 this.visiblePerRow = Math.min(this.itemPerRow, maxBySpace); 149 150 // trava outer pra centralizar (ok), mas não mede outer depois 151 const usefulWidth = this.visiblePerRow * footprint - spacing; 152 this.carouselOuter.style.width = usefulWidth + "px"; 153 this.carouselOuter.style.marginLeft = "auto"; 154 this.carouselOuter.style.marginRight = "auto"; 155 }; 156 157 158 /* ------------------------------------------------------- 159 * ESTADO VISUAL ATIVO 160 * ------------------------------------------------------- */ 161 this.setActiveItem = () => { 162 if (!this.carouselItem || !this.carouselItem.length) return; 163 for (let i = 0; i < this.carouselItem.length; i++) { 164 this.carouselItem[i].classList.toggle( 165 "own-carousel__item--active", 166 i === this.index 167 ); 168 } 169 }; 170 171 /* ------------------------------------------------------- 172 * STEP / MEDIÇÃO (com retry p/ width=0) 173 * ------------------------------------------------------- */ 174 this.recomputeStep = (attempt = 0) => { 175 const MAX_ATTEMPTS = 12; 176 177 const cs = getComputedStyle(this.carousel); 178 this.gapPx = Number.parseFloat(cs.columnGap || cs.gap || "0") || 0; 179 this.usesGap = this.gapPx > 0; 180 181 const perRow = this.getPerRow(); 182 183 let imgWidth = 0; 184 if (perRow === 1) { 185 imgWidth = this.carouselOuter.getBoundingClientRect().width; 186 } else { 187 // mede o item raiz (inclui padding) 188 const firstItem = this.carousel.querySelector(".own-carousel__item") || this.carouselItem[0]; 189 imgWidth = firstItem?.getBoundingClientRect().width || 0; 190 } 191 192 if (!imgWidth && attempt < MAX_ATTEMPTS) { 193 requestAnimationFrame(() => this.recomputeStep(attempt + 1)); 194 return false;
195 } 196 197 this.imgWidth = imgWidth; 198 199 const marginOffsetPx = 200 (this.itemPerRow > 1 && this.itemWidth) 201 ? (this.gapWidth / this.itemWidth) * this.imgWidth 202 : 0; 203 204 this.step = this.usesGap 205 ? this.imgWidth + this.gapPx 206 : this.imgWidth + marginOffsetPx; 207 208 return true; 209 }; 210 211 this.stepToMoveAutoplay = isRTL ? -1 : 1; 212 213 /* ------------------------------------------------------- 214 * MOVIMENTAÇÃO 215 * ------------------------------------------------------- */ 216 this.translateSlide = () => { 217 const dir = isRTL ? 1 : -1; 218 this.carousel.style.transform = `translate3d(${dir * this.index * this.step}px,0,0)`; 219 }; 220 221 this.teleportTo = (newIndex) => { 222 this.carousel.style.transition = "none"; 223 this.index = newIndex; 224 this.translateSlide(); 225 void this.carousel.offsetHeight; // reflow 226 this.carousel.style.transition = "transform .25s"; 227 }; 228 229 /* ------------------------------------------------------- 230 * DOTS (page ou item) 231 * ------------------------------------------------------- */ 232 this.generateDots = () => { 233 if (!dotsEnabled || !dotsContainer) return; 234 235 dotsContainer.innerHTML = ""; 236 this.dots = []; 237 238 const perRow = this.getPerRow(); 239 const loopDots = this.isLoopEnabled(); // loop ativo => 1 dot por item 240 const total = (loopDots || this.dotsMode === "item") 241 ? this.numberOfItem 242 : Math.max(this.numberOfItem - perRow + 1, 1); 243 244 for (let i = 0; i < total; i++) { 245 const dot = document.createElement("span"); 246 dot.classList.add("carousel-dot"); 247 if (i === 0) dot.classList.add("active"); 248 249 dot.addEventListener("click", () => { 250 const cc = this.cloneCount || this.getPerRow(); 251 const maxStart = Math.max(this.numberOfItem - perRow, 0); 252 253 if (loopDots || this.dotsMode === "item") { 254 // dot i = item i (índice real) 255 if (this.isLoopEnabled()) { 256 this.index = i + cc; 257 } else { 258 this.index = Math.min(i, maxStart); 259 } 260 } else { 261 const offset = this.isLoopEnabled() ? cc : 0; 262 this.index = this.isLoopEnabled() ? (i + offset) : i; 263 } 264 265 266 this.translateSlide(); 267 this.updateDots(); 268 }); 269 270 dotsContainer.appendChild(dot); 271 this.dots.push(dot); 272 } 273 274 dotsContainer.style.display = (total > 1) ? "flex" : "none"; 275 }; 276 277 this.updateDots = () => { 278 if (!dotsEnabled || !dotsContainer) return; 279 280 this.dots.forEach((d) => d.classList.remove("active")); 281 282 const perRow = this.getPerRow(); 283 const maxStart = Math.max(this.numberOfItem - perRow, 0); 284 285 let realIndex = this.index; 286 287 if (this.isLoopEnabled()) { 288 const cc = this.cloneCount || this.getPerRow(); 289 const n = this.numberOfItem; 290 291 realIndex = (this.index - cc) % n; 292 if (realIndex < 0) realIndex += n; 293 } 294 295 const loopDots = this.isLoopEnabled(); 296 297 if (loopDots || this.dotsMode === "item") { 298 const idx = Math.min(realIndex, this.numberOfItem - 1); 299 if (this.dots[idx]) this.dots[idx].classList.add("active"); 300 } else { 301 const idx = Math.min(realIndex, maxStart); 302 if (this.dots[idx]) this.dots[idx].classList.add("active"); 303 } 304 }; 305 306 /* ------------------------------------------------------- 307 * LOOP (CLONES) sempre baseado em perRow efetivo 308 * ------------------------------------------------------- */ 309 this.cloneCount = 0; 310 311 this._createClonesIfNeeded = () => { 312 if (!this.isLoopEnabled()) { 313 this.cloneCount = 0; 314 this.index = 0; 315 return; 316 } 317 318 const perRow = this.getPerRow(); 319 this.cloneCount = Math.min(perRow, this.numberOfItem); 320 321 const originals = Array.from(this.carousel.children); // itens reais 322 323 // APPEND: primeiros N 324 for (let i = 0; i < this.cloneCount; i++) { 325 this.carousel.appendChild(originals[i].cloneNode(true)); 326 } 327 328 // PREPEND: últimos N (na ordem correta) 329 for (let i = this.cloneCount - 1; i >= 0; i--) { 330 const srcIndex = this.numberOfItem - 1 - (this.cloneCount - 1 - i); 331 this.carousel.insertBefore(originals[srcIndex].cloneNode(true), this.carousel.firstChild); 332 } 333 334 this.index = this.cloneCount; 335 }; 336 337 this._normalizeIfLoopEdge = () => { 338 if (!this.isLoopEnabled()) return; 339 340 const cc = this.cloneCount || this.getPerRow(); 341 const n = this.numberOfItem; 342 343 if (this.index >= cc + n) { 344 this.teleportTo(cc); 345 } else if (this.index < cc) { 346 this.teleportTo(cc + n - 1); 347 } 348 }; 349 350 this.moveSlide = (delta) => { 351 const perRow = this.getPerRow(); 352 const maxStart = Math.max(this.numberOfItem - perRow, 0); 353 const loopEnabled = this.isLoopEnabled(); 354 355 if (!loopEnabled) { 356 const next = this.index + delta; 357 if (next < 0 || next > maxStart) return; 358 359 this.index = next; 360 this.translateSlide(); 361 this.updateDots(); 362 this.setActiveItem && this.setActiveItem(); 363 return; 364 } 365 366 this.index += delta; 367 this.translateSlide(); 368 this.updateDots(); 369 this.setActiveItem && this.setActiveItem(); 370 }; 371 372 if (!this._loopBound) { 373 this.carousel.addEventListener("transitionend", (e) => { 374 if (e.propertyName !== "transform") return; 375 this._normalizeIfLoopEdge(); 376 this.updateDots(); 377 }); 378 this._loopBound = true; 379 } 380 381 /* ------------------------------------------------------- 382 * NAV VISIBILITY 383 * ------------------------------------------------------- */ 384 this.updateNavVisibility = () => { 385 const canScroll = this.hasScroll(); 386 const showNavArea = canScroll && this.hasNavigation(); 387 388 if (this.controlsEl) this.controlsEl.style.display = (canScroll && !!nav) ? "flex" : "none"; 389 390 if (dotsEnabled && dotsContainer) { 391 const perRow = this.getPerRow(); 392 const loopDots = this.isLoopEnabled(); 393 const totalDots = (loopDots || this.dotsMode === "item") 394 ? this.numberOfItem 395 : Math.max(this.numberOfItem - perRow + 1, 1); 396 397 dotsContainer.style.display = (canScroll && totalDots > 1) ? "flex" : "none"; 398 } 399 400 this.classList.toggle("own-no-scroll", !canScroll); 401 402 if (!showNavArea) { 403 this.index = 0; 404 this.carousel.style.transition = "none"; 405 this.carousel.style.transform = ""; 406 this.carousel.style.justifyContent = "center"; 407 this.carousel.style.marginBottom = "20px"; 408 } else { 409 this.carousel.style.justifyContent = "flex-start"; 410 this.carousel.style.transition = "transform .25s"; 411 this.translateSlide(); 412 } 413 }; 414 415 /* ------------------------------------------------------- 416 * NAV (SETAS) 417 * ------------------------------------------------------- */ 418 if (nav) { 419 const navScope = this.controlsEl || this; 420 421 navScope.querySelector(".control__prev") 422 ?.addEventListener("click", () => this.moveSlide(-1)); 423 424 navScope.querySelector(".control__next") 425 ?.addEventListener("click", () => this.moveSlide(1)); 426 } 427 428 /* ------------------------------------------------------- 429 * DRAG (Pointer) com cálculo RTL correto 430 * ------------------------------------------------------- */ 431 if (draggable && this.carouselOuter) { 432 const THRESHOLD_PX = 12; 433 const CLICK_BLOCK_MS = 350; 434 435 this._drag = this._drag || { 436 bound: false, 437 dragging: false, 438 pointerId: null, 439 firstX: 0, 440 startTransform: 0, 441 moved: false, 442 blockClickUntil: 0 443 }; 444 445 const getCurrentTranslateX = () => { 446 const tr = getComputedStyle(this.carousel).transform; 447 if (tr && tr !== "none") { 448 const m = new DOMMatrixReadOnly(tr); 449 return m.m41; 450 } 451 return 0; 452 }; 453 454 const finishByIndex = () => { 455 this.carousel.style.transition = "transform .25s"; 456 457 const currentMove = getCurrentTranslateX(); 458 const dir = isRTL ? 1 : -1; 459 460 // transform = dir * index * step => index = transform / (dir*step) 461 const rawIndex = currentMove / (dir * this.step); 462 this.index = Math.round(rawIndex); 463 464 const perRow = this.getPerRow(); 465 const maxStart = Math.max(this.numberOfItem - perRow, 0); 466 467 if (!this.isLoopEnabled()) { 468 if (this.index > maxStart) this.index = maxStart; 469 if (this.index < 0) this.index = 0; 470 } 471 472 this.translateSlide(); 473 this.updateDots(); 474 }; 475 476 const cleanupState = () => { 477 this._drag.dragging = false;
478 this._drag.pointerId = null; 479 this.carouselOuter.style.cursor = ""; 480 this.carousel.style.transition = "transform .25s"; 481 this.classList.remove("is-dragging"); 482 this._drag.moved = false; 483 }; 484 485 if (!this._drag._clickBlockerBound) { 486 this.carouselOuter.addEventListener("click", (e) => { 487 const a = e.target?.closest?.("a, button, [role='button']"); 488 if (!a) return; 489 490 if (Date.now() < (this._drag.blockClickUntil || 0)) { 491 e.preventDefault(); 492 e.stopPropagation(); 493 } 494 }, true); 495 this._drag._clickBlockerBound = true; 496 } 497 498 const bindPointer = () => { 499 const dragStart = (e) => { 500 if (e.pointerType === "mouse" && e.button !== 0) return; 501 502 this._drag.dragging = true; 503 this._drag.pointerId = e.pointerId; 504 this._drag.firstX = e.clientX; 505 this._drag.startTransform = getCurrentTranslateX(); 506 this._drag.moved = false; 507 508 this.carousel.style.transition = "none"; 509 this.carouselOuter.style.cursor = "grab"; 510 this.classList.add("is-dragging"); 511 }; 512 513 const dragMove = (e) => { 514 if (!this._drag.dragging) return; 515 if (e.pointerId !== this._drag.pointerId) return; 516 517 const totalDx = e.clientX - this._drag.firstX; 518 if (Math.abs(totalDx) > THRESHOLD_PX) { 519 this._drag.moved = true; 520 if (!this.carouselOuter.hasPointerCapture(e.pointerId)) { 521 try { this.carouselOuter.setPointerCapture(e.pointerId); } catch { } 522 } 523 } 524 525 const next = this._drag.startTransform + totalDx; 526 this.carousel.style.transform = `translate3d(${next}px,0,0)`; 527 }; 528 529 const dragEnd = (e) => { 530 if (!this._drag.dragging) return; 531 if (e.pointerId !== this._drag.pointerId) return; 532 533 try { this.carouselOuter.releasePointerCapture(e.pointerId); } catch { } 534 535 this._drag.blockClickUntil = this._drag.moved ? (Date.now() + CLICK_BLOCK_MS) : 0; 536 537 finishByIndex(); 538 cleanupState(); 539 }; 540 541 const dragCancel = () => { 542 this.translateSlide(); 543 this.updateDots(); 544 cleanupState(); 545 }; 546 547 this.carouselOuter.addEventListener("pointerdown", dragStart, { passive: true }); 548 this.carouselOuter.addEventListener("pointermove", dragMove, { passive: true }); 549 this.carouselOuter.addEventListener("pointerup", dragEnd, { passive: true }); 550 this.carouselOuter.addEventListener("pointercancel", dragCancel, { passive: true }); 551 }; 552 553 if (!this._drag.bound) { 554 if (window.PointerEvent) bindPointer(); 555 this._drag.bound = true; 556 } 557 } 558 559 /* ------------------------------------------------------- 560 * INIT ORDEM CORRETA 561 * ------------------------------------------------------- */ 562 applyAdaptiveGap(this); 563 564 // 1) mede gap/step primeiro (pra updateVisible usar gapPx) 565 this.recomputeStep(); 566 567 // 2) calcula visiblePerRow com footprint real (item + gap/margin) 568 if (this.useVisiblePerRow) { 569 this.updateVisiblePerRowAndOuterWidth(); 570 // como outer mudou, recalcula step 571 this.recomputeStep(); 572 } 573 574 // 3) cria clones (depende de perRow final) 575 this._createClonesIfNeeded(); 576 577 // 4) pinta posição inicial sem cortar 578 this.carousel.style.transition = "none"; 579 this.translateSlide(); 580 void this.carousel.offsetHeight; 581 this.carousel.style.transition = "transform .25s"; 582 583 // 5) dots/nav 584 this.generateDots(); 585 this.updateDots(); 586 this.setActiveItem(); 587 this.updateNavVisibility(); 588 589 // blindagem extra: recalcular em resize do outer 590 if (window.ResizeObserver && !this._ro) { 591 this._ro = new ResizeObserver(() => { 592 applyAdaptiveGap(this); 593 this.recomputeStep(); 594 595 if (this.useVisiblePerRow) { 596 this.updateVisiblePerRowAndOuterWidth(); 597 this.recomputeStep(); 598 } 599 600 this.updateNavVisibility && this.updateNavVisibility(); 601 this.updateDots && this.updateDots(); 602 }); 603 const host = this.carouselOuter.parentElement || this.carouselOuter; 604 this._ro.observe(host); 605 } 606}; 607 608/***************************************************************** 609 * GAP ADAPTATIVO 610 *****************************************************************/ 611function calcAdaptiveGap(container) { 612 const ww = window.innerWidth; 613 const n = container.numberOfItem || 0; 614 615 const inBandA = ww <= 1399 && ww >= 1272; 616 const inBandB = ww <= 1199 && ww >= 1060; 617 618 if (!inBandA && !inBandB) return 0; 619 620 if (n >= 6) return 0; 621 if (n === 5) return 10; 622 return 20; 623} 624 625function applyAdaptiveGap(container) { 626 const px = calcAdaptiveGap(container); 627 container.style.setProperty("--gap", `${px}px`); 628 629 if (px > 0) container.classList.add("use-gap"); 630 else container.classList.remove("use-gap"); 631} 632 633/***************************************************************** 634 * RESPONSIVE LISTENER (SAFE) 635 *****************************************************************/ 636function debounce(fn, delay) { 637 let id = null; 638 return function () { 639 clearTimeout(id); 640 id = setTimeout(() => fn.apply(this, arguments), delay); 641 }; 642} 643 644function safeResponsiveUpdate() { 645 requestAnimationFrame(() => setTimeout(responsive, 100)); 646} 647 648function responsive() { 649 const ww = window.innerWidth; 650 const containers = document.querySelectorAll(".own-carousel__container"); 651 652 const pickBreakpoint = (map, width) => { 653 if (!map || typeof map !== "object") return null; 654
655 const keys = Object.keys(map).map(Number).filter(n => !Number.isNaN(n)).sort((a, b) => a - b); 656 if (!keys.length) return null; 657 658 const minBased = keys[0] === 0; 659 if (minBased) { 660 let chosen = keys[0]; 661 for (let bp of keys) if (width >= bp) chosen = bp; 662 return chosen; 663 } else { 664 for (let bp of keys) if (width <= bp) return bp; 665 return null; 666 } 667 }; 668 669 containers.forEach((c) => { 670 if (!c._ownCarouselInitialized) return; 671 672 const chosen = pickBreakpoint(c.responsive, ww); 673 674 if (chosen !== null) { 675 c.itemPerRow = c.responsive[chosen][0]; 676 c.itemWidth = c.responsive[chosen][1]; 677 } else { 678 c.itemPerRow = c.itemPerRowBig; 679 c.itemWidth = c.itemWidthBig; 680 } 681 682 c.gapWidth = (c.itemPerRow > 1) 683 ? (100 - c.itemPerRow * c.itemWidth) / (c.itemPerRow - 1) 684 : 0; 685 686 c.style.setProperty("--width", `${c.itemWidth}%`); 687 c.style.setProperty("--margin", `${c.gapWidth}%`); 688 689 applyAdaptiveGap(c); 690 691 // recalcula step e visible (se necessário) 692 c.recomputeStep && c.recomputeStep(); 693 694 if (c.useVisiblePerRow && c.updateVisiblePerRowAndOuterWidth) { 695 c.updateVisiblePerRowAndOuterWidth(); 696 c.recomputeStep && c.recomputeStep(); 697 } 698 699 // reposiciona com segurança (sem recriar clones aqui) 700 const perRow = c.getPerRow ? c.getPerRow() : c.itemPerRow; 701 const maxStart = Math.max(c.numberOfItem - perRow, 0); 702 703 if (!c.hasScroll || !c.hasScroll()) { 704 c.index = 0; 705 } else if (!c.isLoopEnabled || !c.isLoopEnabled()) { 706 if (c.index > maxStart) c.index = maxStart; 707 if (c.index < 0) c.index = 0; 708 } else { 709 const cc = c.cloneCount || perRow; 710 if (c.index < cc) c.index = cc; 711 if (c.index >= cc + c.numberOfItem) c.index = cc; 712 } 713 714 c.carousel.style.transition = "none"; 715 c.translateSlide && c.translateSlide(); 716 void c.carousel.offsetHeight; 717 c.carousel.style.transition = "transform .25s"; 718 719 c.generateDots && c.generateDots(); 720 c.updateDots && c.updateDots(); 721 c.updateNavVisibility && c.updateNavVisibility(); 722 }); 723} 724 725window.addEventListener("resize", debounce(safeResponsiveUpdate, 300));
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.