PageSourceSearch

https://firstpagelife.com/wp-content/plugins/breakdance/subplugins…/breakdance-filter@1/filter.js?bd_ver=2.8.3

js firstpagelife.com collected 2026-10-06 11:08:54 UTC 8,841 bytes, 316 lines download raw bytes

1(function () {
2  const { mergeObjects, matchMedia, onResize } = BreakdanceFrontend.utils;
3
4  class BreakdanceFilter {
5    defaultOptions = {
6      wrapperSelector: ".ee-posts",
7      itemSelector: ".ee-post",
8      sizerSelector: ".ee-post-sizer",
9      gutterSelector: ".ee-post-gutter",
10      defaultActiveIndex: 0,
11      layout: "grid",
12      horizontalAt: "breakpoint_phone_landscape",
13      isVertical: false,
14      equalHeight: false
15    };
16    items = [];
17    filterActiveClass = "is-active";
18
19    constructor(selector, options) {
20      this.onResize = this.onResize.bind(this);
21      this.onImagesLoaded = this.onImagesLoaded.bind(this);
22      this.onFilterChange = this.onFilterChange.bind(this);
23      this.selector = selector;
24      this.options = mergeObjects(this.defaultOptions, options);
25
26      this.init();
27    }
28
29    isEqualHeight() {
30      const isMasonry = this.options.layout === "masonry";
31      return this.options.equalHeight && !isMasonry && !this.isSlider();
32    }
33
34    isSlider() {
35      return this.options.layout === "slider";
36    }
37
38    /*
39     * Turn DOM Elements into Isotope-ready Items
40     * This is required in order to make Isotope work in the Builder.
41     */
42    itemize(items) {
43      return items.map((elem) => new Isotope.Item(elem, this.iso));
44    }
45
46    /*
47     * Force Isotope to accept our DOM Elements.
48     * This is required due to a bug in the library.
49     * "elem instanceof HTMLCollection" always return false inside same-origin iframes
50     */
51    appendItems(elems) {
52      const items = this.itemize(elems);
53
54      if (!items.length) return;
55
56      this.iso.items = items;
57      this.iso.filteredItems = items;
58    }
59
60    relayout() {
61      if (!this.iso) return;
62
63      if (this.isEqualHeight()) {
64        this.equalizeHeights();
65      }
66
67      this.iso.layout();
68    }
69
70    getItemElems() {
71      return Array.from(this.wrapperEl.querySelectorAll(this.options.itemSelector));
72    }
73
74    onImagesLoaded() {
75      this.relayout();
76    }
77
78    setActiveButton(activeButton) {
79      this.filterButtons.forEach((button) => {
80        button.classList.remove(this.filterActiveClass);
81        button.setAttribute("aria-selected", "false");
82      });
83
84      activeButton.classList.add(this.filterActiveClass);
85      activeButton.setAttribute("aria-selected", "true");
86    }
87
88    // Filters
89    getFilterValue(node) {
90      if (node.tagName === "SELECT") {
91        const selected = node.options[node.selectedIndex];
92        return selected.dataset.value;
93      }
94
95      return node.dataset.value;
96    }
97
98    filterBy(node) {
99      const filter = this.getFilterValue(node);
100      const button = this.filterButtons.find((button) => button.id === node.id);
101
102      const filterByTerms = (item) => {
103        const curFilters = (item.dataset.filters ?? '').split(",");
104        return curFilters.includes(filter);
105      };
106
107      if (this.isSlider()) {
108        this.filterBySlider(filter, filterByTerms);
109      } else {
110        this.filterByIsotope(filter, filterByTerms);
111      }
112
113      if (button) {
114        this.setActiveButton(button);
115      }
116    }
117
118    filterBySlider(filter, filterFn) {
119      const swiperEl = this.element.querySelector('.swiper');
120      const itemsToShow = filter === "all" ? this.items : this.items.filter(filterFn);
121      const itemsToHide = this.items.filter(x => !itemsToShow.includes(x));
122
123      itemsToShow.forEach(item => item.style.display = null);
124      itemsToHide.forEach(item => item.style.display = 'none');
125
126      swiperEl.swiper.update();
127      swiperEl.swiper.slideTo(0, 0);
128    }
129
130    filterByIsotope(filter, filterFn) {
131      if (filter === "all") {
132        this.iso.arrange({
133          layoutInstant: false,
134          filter: "*"
135        });
136      } else {
137        this.iso.arrange({
138          layoutInstant: false,
139          filter(element) {
140            return filterFn(element);
141          },
142        });
143      }
144    }
145
146    onFilterChange(event) {
147      event.preventDefault();
148      this.filterBy(event.currentTarget);
149    }
150
151    initFilters() {
152      this.filterButtons = Array.from(this.element.querySelectorAll(".js-tab"));
153      const mobileFilter = this.element.querySelector(".js-tab-select");
154
155      this.filterButtons.forEach((button, index) => {
156        if (index === this.options.defaultActiveIndex) {
157          this.setActiveButton(button);
158        }
159
160        button.addEventListener("click", this.onFilterChange, { signal: this.ac.signal });
161      });
162
163      if (mobileFilter) {
164        mobileFilter.addEventListener("change", this.onFilterChange, { signal: this.ac.signal });
165      }
166    }
167
168    equalizeHeights() {
169      const grid = this.wrapperEl;
170      const items = this.getItemElems()
171        .filter(item => item.style.display !== "none");
172
173      const itemsPerRow = Math.floor(grid.clientWidth / items[0].offsetWidth);
174      let rowHeight = 0;
175
176      // Reset heights
177      items.forEach(item => {
178        item.style.height = "auto";
179      });
180
181      for (let i = 0; i < items.length; i += itemsPerRow) {
182        const rowItems = items.slice(i, i + itemsPerRow);
183        rowHeight = Math.max(...rowItems.map(item => item.offsetHeight));
184        rowItems.forEach(item => item.style.height = `${rowHeight}px`);
185      }
186    }
187
188    resizeVertical() {
189      if (!this.options.isVertical) return;
190      const breakpoint = this.options.horizontalAt;
191      const { BASE_BREAKPOINT_ID } = window.BreakdanceFrontend.data;
192
193      const tabList = this.element.querySelector(".js-tablist");
194      const tabsContainer = tabList.closest(".js-tabs-container");
195
196      if (
197        matchMedia(breakpoint) ||
198        breakpoint === BASE_BREAKPOINT_ID
199      ) {
200        tabsContainer.classList.remove('is-vertical');
201        tabList.setAttribute("aria-orientation", "horizontal");
202      } else {
203        tabsContainer.classList.add('is-vertical');
204        tabList.setAttribute("aria-orientation", "vertical");
205      }
206    }
207
208    onResize() {
209      this.relayout();
210      this.resizeVertical();
211    }
212
213    destroy() {
214      if (this.iso) {
215        this.iso.destroy();
216        this.iso = null;
217      }
218
219      if (this.ac) {
220        this.ac.abort();
221        this.ac = null;
222      }
223
224      this.disconnect();
225
226      // Reset heights
227      this.getItemElems()
228        .filter(item => item.style.display !== "none")
229        .forEach(item => {
230          item.style.removeProperty("height");
231        });
232
233      imagesLoaded(this.element).off("progress", this.onImagesLoaded);
234    }
235
236    update(options) {
237      this.options = mergeObjects(this.defaultOptions, options);
238      this.destroy();
239      this.init();
240    }
241
242    initIsotope() {
243      const layoutMode = this.isEqualHeight() ? "fitRows" : "masonry";
244
245      // Remove display: none from initially hidden items
246      this.items.forEach(item => item.style.display = null);
247
248      this.iso = new Isotope(this.wrapperEl, {
249        itemSelector: this.options.itemSelector,
250        percentPosition: true,
251        isJQueryFiltering: false,
252        layoutMode,
253        fitRows: {
254          columnWidth: this.options.sizerSelector,
255          gutter: this.options.gutterSelector,
256        },
257        masonry: {
258          columnWidth: this.options.sizerSelector,
259          gutter: this.options.gutterSelector,
260        }
261      });
262
263      this.appendItems(this.items);
264
265      // Hide initially hidden items by default
266      this.iso.arrange({
267        layoutInstant: true,
268        filter: ":not(.initially-hidden)"
269      });
270
271      this.iso.on("arrangeComplete", () => {
272        if (this.isEqualHeight()) {
273          this.relayout();
274        }
275      });
276
277      this.element.addEventListener("breakdance_infinite_scroll_loaded", (event) => {
278        const appendedElems = Array.from(event.detail || []);
279        if (!appendedElems.length) return;
280
281        this.iso.appended(appendedElems);
282
283        imagesLoaded(appendedElems)
284          .on("always", () => this.relayout());
285      }, { signal: this.ac.signal });
286
287      // Force Isotope to relayout when clicking on a link or button.
288      // This is required if the link or button causes the element to change size.
289      this.wrapperEl.querySelectorAll("a, button").forEach((el) => {
290        el.addEventListener("click", () => this.iso.layout(), { signal: this.ac.signal });
291      });
292
293      imagesLoaded(this.element).on("progress", this.onImagesLoaded);
294    }
295
296    init() {
297      this.element = document.querySelector(this.selector);
298      this.wrapperEl = this.element.querySelector(this.options.wrapperSelector);
299      this.items = this.getItemElems();
300
301      if (this.ac) this.ac.abort();
302      this.ac = new AbortController();
303
304      if (!this.isSlider()) {
305        this.initIsotope();
306      }
307
308      this.initFilters();
309
310      this.disconnect = onResize(this.onResize);
311      this.resizeVertical();
312    }
313  }
314
315  window.BreakdanceFilter = BreakdanceFilter;
316})();

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.