PageSourceSearch

https://sboc.org.br/wp-content/plugins/breakdance/subplugins/break…/custom-tabs@1/tabs.js?bd_ver=2.8.3

js sboc.org.br collected 2026-10-02 05:33:13 UTC 10,308 bytes, 372 lines download raw bytes

1(function () {
2  const { mergeObjects, matchMedia } = BreakdanceFrontend.utils;
3
4  const verticalTabsSelector = "is-vertical";
5
6  class BreakdanceTabs {
7    defaultOptions = {
8      activeTab: 1,
9      horizontalAt: "breakpoint_phone_landscape",
10      isVertical: false,
11      openOnHover: false
12    };
13
14    activateTabEventTypes = ["click"];
15
16    keys = {
17      end: 35,
18      home: 36,
19      left: 37,
20      up: 38,
21      right: 39,
22      down: 40,
23      delete: 46
24    };
25
26    // Add or substract depending on key pressed
27    direction = {
28      37: -1,
29      38: -1,
30      39: 1,
31      40: 1
32    };
33
34    constructor(selector, options = {}) {
35      this.selector = selector;
36      this.element = document.querySelector(`${this.selector}`);
37      this.isBuilder = !!window?.BreakdanceFrontend.utils.isBuilder();
38      this.options = mergeObjects(this.defaultOptions, options);
39
40      this.init();
41    }
42
43    bindListeners() {
44      if (this.tabs) {
45        this.tabs.forEach((tab, index) => {
46          this.addListeners(tab, index);
47        });
48        if (this.tabSelect) {
49          this.tabSelect.addEventListener("change", event => {
50            this.changeEventListener(event);
51          });
52        }
53      }
54    }
55
56    onResizeVertical() {
57      if (!this.options.isVertical) return;
58      const breakpoint = this.options.horizontalAt;
59      const { BASE_BREAKPOINT_ID } = window.BreakdanceFrontend.data;
60
61      if (
62        (matchMedia(breakpoint) && this.options.isVertical) ||
63        (breakpoint === BASE_BREAKPOINT_ID && this.options.isVertical)
64      ) {
65        this.tabsContainer.classList.remove(verticalTabsSelector);
66        this.tabList.setAttribute("aria-orientation", "horizontal");
67      } else {
68        if (!this.tabsContainer.classList.contains(verticalTabsSelector)) {
69          this.tabsContainer.classList.add(verticalTabsSelector);
70          this.tabList.setAttribute("aria-orientation", "vertical");
71        }
72      }
73    }
74
75    addListeners(tab, index) {
76      this.tabs[index].index = index;
77      this.onClick = this.clickEventListener.bind(this);
78      this.onKeydown = this.keydownEventListener.bind(this);
79
80      this.activateTabEventTypes.forEach((eventType) => {
81        tab.addEventListener(eventType, this.onClick);
82      });
83
84      if (this.isBuilder) return;
85      tab.addEventListener("keydown", this.onKeydown);
86    }
87
88    changeEventListener(event) {
89      const tab = document.querySelector(`#${event.target.value}`);
90      this.activateTab(tab, false);
91    }
92
93    clickEventListener(event) {
94      const tab = event.target.closest(".js-tab");
95      this.activateTab(tab, false);
96    }
97
98    keydownEventListener(event) {
99      const key = event.keyCode;
100
101      switch (key) {
102        case this.keys.end:
103          event.preventDefault();
104          // Activate last tab
105          this.activateTab(this.tabs[this.tabs.length - 1], true);
106          break;
107        case this.keys.home:
108          event.preventDefault();
109          // Activate first tab
110          this.activateTab(this.tabs[0], true);
111          break;
112
113        // Up and down are in keydown
114        // because we need to prevent page scroll >:)
115        case this.keys.up:
116        case this.keys.down:
117        case this.keys.left:
118        case this.keys.right:
119          this.determineOrientation(event);
120          break;
121      }
122    }
123
124    determineOrientation(event) {
125      const key = event.keyCode;
126
127      const vertical =
128        this.tabList.getAttribute("aria-orientation") == "vertical";
129      let proceed = false;
130
131      if (vertical) {
132        if (key === this.keys.up || key === this.keys.down) {
133          event.preventDefault();
134          proceed = true;
135        }
136      } else {
137        if (key === this.keys.left || key === this.keys.right) {
138          proceed = true;
139        }
140      }
141
142      if (proceed) {
143        this.switchTabOnArrowPress(event);
144      }
145    }
146
147    switchTabOnArrowPress(event) {
148      const pressed = event.keyCode;
149
150      this.onFocus = this.focusEventHandler.bind(this);
151
152      this.tabs.forEach(tab => {
153        tab.addEventListener("focus", event => this.onFocus(event));
154      });
155
156      if (this.direction[pressed]) {
157        const target = event.target;
158        if (target.index !== undefined) {
159          if (this.tabs[target.index + this.direction[pressed]]) {
160            this.tabs[target.index + this.direction[pressed]].focus();
161          } else if (pressed === this.keys.left || pressed === this.keys.up) {
162            this.focusLastTab();
163          } else if (pressed === this.keys.right || pressed == this.keys.down) {
164            this.focusFirstTab();
165          }
166        }
167      }
168    }
169
170    activateTab(tab, setFocus) {
171      if (tab) {
172        setFocus = setFocus || false;
173        // Deactivate all other tabs
174        this.deactivateTabs();
175
176        // Remove tabindex attribute
177        tab.removeAttribute("tabindex");
178
179        // Set the tab as selected
180        tab.setAttribute("aria-selected", "true");
181
182        tab.classList.add("is-active");
183
184        // Get the value of aria-controls (which is an ID)
185        const controls = tab.getAttribute("aria-controls");
186
187        if (this.tabSelect) {
188          this.tabSelect.value = tab.getAttribute("id");
189          this.tabSelect[this.tabSelect.selectedIndex].setAttribute(
190            "selected",
191            "selected"
192          );
193        }
194
195        const panel = document.getElementById(controls);
196
197        if (!panel) return;
198
199        panel.classList.add("is-active");
200
201        // Set focus when required
202        if (setFocus && !this.isBuilder) {
203          tab.focus();
204        }
205
206        // Force-refresh animations to allow the ones inside hidden tabs to work.
207        const event = new Event("breakdance_play_animations", { bubbles: true });
208        panel.parentElement.dispatchEvent(event);
209      }
210    }
211
212    refreshPanelAttributes() {
213      const firstPanel = document.querySelector(`${this.selector} .js-panel`);
214      const panelsContainer = firstPanel.closest(".bde-tabs-content-container");
215
216      this.panels = panelsContainer.querySelectorAll(
217        ":scope > .js-panel, :scope > .bde-advanced-tabs-content > .js-panel"
218      );
219
220      this.panels.forEach((panel, index) => {
221        panel.setAttribute("id", `tab-panel-${this.tabsId}-${index + 1}`);
222        panel.setAttribute(
223          "aria-labelledby",
224          `tab-${this.tabsId}-${index + 1}`
225        );
226      });
227    }
228
229    deactivateTabs() {
230      if (this.tabs) {
231        this.tabs.forEach(tab => {
232          tab.setAttribute("tabindex", "-1");
233          tab.setAttribute("aria-selected", "false");
234          tab.removeEventListener("focus", event => this.onFocus(event));
235          this.activateTabEventTypes.forEach((eventType) => {
236            tab.removeEventListener(eventType, event => this.onClick(event));
237          });
238          tab.removeEventListener("keydown", event => this.onKeydown(event));
239          tab.classList.remove("is-active");
240        });
241      }
242
243      if (this.tabSelect) {
244        [...this.tabSelect.options].forEach(option =>
245          option.removeAttribute("selected")
246        );
247      }
248
249      if (this.tabSelect) {
250        this.tabSelect.removeEventListener("change", event =>
251          this.changeEventListener(event)
252        );
253      }
254
255      if (this.panels) {
256        this.panels.forEach(panel => {
257          panel.classList.remove("is-active");
258        });
259      }
260    }
261
262    focusFirstTab() {
263      this.tabs[0].focus();
264    }
265
266    focusLastTab() {
267      this.tabs[this.tabs.length - 1].focus();
268    }
269
270    determineDelay() {
271      const hasDelay = this.tabList.hasAttribute("data-delay");
272      const delay = 0;
273
274      if (hasDelay) {
275        const delayValue = this.tabList.getAttribute("data-delay");
276        if (delayValue) {
277          this.delay = delayValue;
278        } else {
279          // If no value is specified, default to 300ms
280          this.delay = 300;
281        }
282      }
283
284      return delay;
285    }
286
287    focusEventHandler(event) {
288      const target = event.target;
289      setTimeout(() => this.checkTabFocus(target), this.delay);
290    }
291
292    checkTabFocus(target) {
293      const focused = document.activeElement;
294
295      if (target === focused) {
296        this.activateTab(target, false);
297      }
298    }
299
300    destroy() {
301      if (!this.tabs) return;
302      this.deactivateTabs();
303      this.resetTabstoVerticalLayout();
304      this.destroyResizeObserver();
305    }
306
307    destroyResizeObserver() {
308      if (!this.resizeObserver) return;
309      this.resizeObserver.unobserve(this.element);
310    }
311
312    resetTabstoVerticalLayout() {
313      this.tabsContainer.classList.remove(verticalTabsSelector);
314      this.tabList.setAttribute("aria-orientation", "vertical");
315    }
316
317    onResize(callback) {
318      this.resizeObserver = new ResizeObserver(callback);
319      this.resizeObserver.observe(this.element);
320
321      return () => {
322        this.resizeObserver.disconnect();
323      };
324    }
325
326    update(options) {
327      if (options) {
328        this.options = mergeObjects(this.defaultOptions, options);
329      }
330      this.destroy();
331      this.init();
332    }
333
334    updateAndActivateTabFromSelector(selector) {
335      this.refreshPanelAttributes();
336      const panel = selector.querySelector(".js-panel");
337      const tabId = panel.getAttribute("aria-labelledby");
338      if (!tabId) return;
339      const tab = document.getElementById(tabId);
340      this.activateTab(tab);
341    }
342
343    init() {
344      if (!this.element) return;
345
346      if (this.options.openOnHover && !this.isBuilder) {
347        this.activateTabEventTypes.push("mouseover");
348      }
349
350      this.tabList = this.element.querySelector(".js-tablist");
351      if (!this.tabList) return;
352
353      this.tabs = this.tabList.querySelectorAll(".js-tab");
354      this.tabsContainer = this.tabList.closest(".js-tabs-container");
355      this.tabsId =
356        this.tabList && this.tabList.dataset.tabsId
357          ? this.tabList.dataset.tabsId
358          : this.selector.match(/\d+$/)[0];
359      this.tabSelect = this.element.querySelector(".js-tab-select");
360
361      this.refreshPanelAttributes();
362      this.bindListeners();
363      this.activateTab(this.tabs[this.options.activeTab - 1]);
364
365      if (this.options.isVertical) {
366        this.onResize(() => this.onResizeVertical());
367      }
368    }
369  }
370
371  window.BreakdanceTabs = BreakdanceTabs;
372})();

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.