PageSourceSearch

https://firewallx.ai/assets/js/explore-our-case-studies-carousel.js

js firewallx.ai collected 2026-10-05 15:15:08 UTC 6,252 bytes, 216 lines download raw bytes

1class Carousel {
2  element = null;
3  buttonDisabled = false;
4  nav = null;
5  items = [];
6  indicators = []; // for storing indicators
7  size = 2.5; // number of items to show
8  gap = 16; // in px
9  activeClass = true;
10  itemProps = {
11    width: 0,
12    left: 0
13  };
14  currentIndex = 0; // To track the currently visible item
15
16  constructor(element) {
17    if (!element) throw new Error("Carousel element not found");
18    this.element = element;
19    
20    // Initialize collections
21    this.refreshItems();
22    this.nav = element.parentElement.querySelectorAll(".carousel__nav__item");
23    this.indicators = element.parentElement.querySelectorAll(".carousel__indicator");
24
25    // Initialize first indicator if they exist
26    if (this.indicators.length > 0) {
27      this.indicators[0]?.classList.add("carousel__indicator--active");
28    }
29
30    this.init();
31  }
32
33  // Refresh the items collection
34  refreshItems() {
35    this.items = this.element.querySelectorAll(".carousel__item");
36  }
37
38  async init() {
39    await this.setMinItems();
40    this.itemProps.width = await this.getSize();
41    
42    // Build the carousel first to ensure items are properly sized
43    await this.build();
44
45    // Set carousel height based on first item
46    if (this.items.length > 0) {
47      this.element.style.height = this.items[0].clientHeight + "px";
48    }
49
50    // Add event listeners to navigation buttons
51    for (let i = 0; i < this.nav.length; i++) {
52      let currentNavElement = this.nav[i];
53      this.nav[i].addEventListener("click", () =>
54        this.moveHandler(currentNavElement)
55      );
56    }
57
58    // Add event listeners to indicators
59    for (let i = 0; i < this.indicators.length; i++) {
60      this.indicators[i].addEventListener("click", () =>
61        this.indicatorClickHandler(i)
62      );
63    }
64
65    // Add mouse move event listener for scrolling
66    this.element.addEventListener("mousemove", (event) => this.onMouseMove(event));
67
68    this.updateIndicators(0); // Initialize indicators
69    this.nav = this.element.parentElement.querySelector(".carousel__nav");
70  }
71
72  async setMinItems() {
73    const minItems = this.size + 2;
74    if (this.items.length < minItems) {
75      let currentLength = this.items.length;
76      for (let i = 0; i < currentLength; i++) {
77        let clonedItem = this.items[i].cloneNode(true);
78        this.element.append(clonedItem);
79      }
80      this.refreshItems();
81    }
82  }
83
84  async getSize() {
85    let totalSpacing = this.gap * (this.size - 1);
86    let width = this.element.clientWidth - totalSpacing;
87    width = width / this.size;
88    return width;
89  }
90
91  async build() {
92    for (let i = 0; i < this.items.length; i++) {
93      this.items[i].style.width = this.itemProps.width + "px";
94      this.items[i].style.left =
95        (this.itemProps.width + this.gap) * (i - 1) + "px";
96    }
97    if (this.activeClass) {
98      this.setActiveClass();
99    }
100  }
101
102  async move(pos) {
103    this.refreshItems(); // Ensure we have current items
104    
105    if (this.items.length === 0) return; // Exit if no items
106    
107    let item = pos === "next" ? this.items[0] : this.items[this.items.length - 1];
108    if (!item) return; // Exit if item not found
109    
110    let clonedItem = item.cloneNode(true);
111
112    if (pos === "next") {
113      this.element.append(clonedItem);
114      this.updateIndicators(1); // Move to next indicator
115      this.currentIndex = (this.currentIndex + 1) % this.indicators.length;
116    } else {
117      this.element.prepend(clonedItem);
118      this.updateIndicators(-1); // Move to previous indicator
119      this.currentIndex = (this.currentIndex - 1 + this.indicators.length) % this.indicators.length;
120    }
121
122    item.remove();
123    this.refreshItems();
124    this.build();
125  }
126
127  async next() {
128    await this.move("next");
129  }
130
131  async prev() {
132    await this.move("prev");
133  }
134
135  async setActiveClass() {
136    let mean = Math.round(this.size / 2);
137    for (let i = 0; i < this.items.length; i++) {
138      this.items[i].classList.remove("carousel__item--active");
139      if (i === mean) {
140        this.items[i].classList.add("carousel__item--active");
141      }
142    }
143  }
144
145  async moveHandler(element) {
146    if (!this.buttonDisabled) {
147      this.buttonDisabled = true;
148      let direction = element.getAttribute("data-direction");
149      if (direction === "next") {
150        await this.next();
151      } else {
152        await this.prev();
153      }
154      setTimeout(() => (this.buttonDisabled = false), 800);
155    }
156  }
157
158  updateIndicators(direction) {
159    if (!this.indicators || this.indicators.length === 0) return;
160
161    // Remove active class from all indicators
162    this.indicators.forEach(indicator => {
163      indicator.classList.remove("carousel__indicator--active");
164    });
165
166    // Calculate new active index
167    let activeIndex;
168    if (direction === 1) {
169      activeIndex = (this.currentIndex + 1) % this.indicators.length;
170    } else if (direction === -1) {
171      activeIndex = (this.currentIndex - 1 + this.indicators.length) % this.indicators.length;
172    } else {
173      activeIndex = this.currentIndex;
174    }
175
176    // Set new active indicator
177    this.indicators[activeIndex]?.classList.add("carousel__indicator--active");
178    this.currentIndex = activeIndex;
179  }
180
181  indicatorClickHandler(index) {
182    const steps = index - this.currentIndex;
183    if (steps === 0) return;
184
185    const direction = steps > 0 ? "next" : "prev";
186    const count = Math.abs(steps);
187
188    const moveSteps = async () => {
189      for (let i = 0; i < count; i++) {
190        await this.move(direction);
191      }
192    };
193
194    moveSteps();
195  }
196
197  onMouseMove(event) {
198    const rect = this.element.getBoundingClientRect();
199    const mouseX = event.clientX - rect.left;
200    const threshold = rect.width * 0.1;
201
202    if (mouseX < threshold) {
203      this.move("prev");
204    } else if (mouseX > rect.width - threshold) {
205      this.move("next");
206    }
207  }
208}
209
210// Initialize carousel if element exists
211document.addEventListener("DOMContentLoaded", () => {
212  const element = document.querySelector(".carousel");
213  if (element) {
214    new Carousel(element);
215  }
216});

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.