PageSourceSearch

https://www.beezwax.net/assets/controllers/filtered_content_contro…9d850568c995c77172ee207df2e534c808f04d84.js

js beezwax.net collected 2026-10-02 06:54:12 UTC 5,143 bytes, 177 lines download raw bytes

1import ApplicationController from 'controllers/application_controller';
2import URLHashStore from 'utils/url_hash_store'
3import debounce from 'utils/debounce'
4
5export default class extends ApplicationController {
6  static targets = ['allCategories', 'category', 'term', 'range', 'item', 'joinUsCard']
7
8  initialize () {
9    this.filter = debounce(this.filter, 25)
10    this.hashStore = new URLHashStore()
11  }
12
13  connect() {
14    this.termTarget.addEventListener('keyup', () => this.filter())
15    this.element.filteredContentController = this
16
17    if (this.hasRangeTarget) {
18      this.updateRangeGradient()
19      this.rangeTarget.addEventListener('input', () => {
20        this.updateRangeGradient()
21      })
22      this.rangeTarget.addEventListener('change', () => this.filter())
23    }
24
25    this.allCategoriesTarget.addEventListener('change', () => {
26      this.categoryTargets.forEach((categoryTarget) => {
27        categoryTarget.checked = false;
28      });
29
30      this.filter();
31    });
32
33    this.categoryTargets.forEach((categoryTarget) => {
34      categoryTarget.addEventListener('change', () => {
35        this.allCategoriesTarget.checked = false;
36        if (this.categories.length === 0) {
37          this.allCategoriesTarget.checked = true;
38        }
39
40        this.filter();
41      });
42    });
43
44    this.loadFilters()
45    this.filter()
46  }
47
48  // private
49
50  get categories() {
51    if (this.allCategoriesTarget.checked) {
52      return [];
53    } else {
54      return this.categoryTargets
55        .filter((categoryTarget) => categoryTarget.checked)
56        .map((categoryTarget) => categoryTarget.value);
57    }
58  }
59
60  filter() {
61    this.itemTargets.forEach((itemTarget) => itemTarget.classList.remove('display-none'));
62    this.filterByName();
63    this.filterByRange();
64    this.filterByCategory();
65    this.positionJoinUsCard();
66    this.persistFilters()
67  }
68
69  positionJoinUsCard() {
70    if (!this.hasJoinUsCardTarget) return;
71
72    const MAX_INDEX = 5
73    const visibleAmount = this.itemTargets.filter((itemTarget) => {
74      return !itemTarget.classList.contains('display-none')
75    }).length
76    const newPosition = Math.floor(Math.random() * Math.min(visibleAmount, MAX_INDEX))
77    $(this.itemTargets[newPosition]).after(this.joinUsCardTarget)
78  }
79
80  filterByName() {
81    this.itemTargets.forEach((itemTarget) => {
82      const name = itemTarget.dataset.name;
83      const matches = name.toLowerCase().includes(this.termTarget.value.toLowerCase());
84
85      if (!matches) {
86        itemTarget.classList.add('display-none');
87      }
88    });
89  }
90
91  filterByRange() {
92    if (!this.hasRangeTarget) return;
93
94    this.itemTargets.forEach((itemTarget) => {
95      const since = itemTarget.dataset.joined;
96      const matches = +since <= +this.rangeTarget.value
97
98      if (!matches) {
99        itemTarget.classList.add('display-none');
100      }
101    });
102  }
103
104  filterByCategory() {
105    const categories = this.categories;
106    if (categories.length === 0) return;
107
108    this.itemTargets.forEach((itemTarget) => {
109      const itemCategories = itemTarget.dataset.categories.split(' ');
110      const intersection = categories.filter((category) => itemCategories.includes(category));
111      const matches = intersection.length > 0
112
113      if (!matches) {
114        itemTarget.classList.add('display-none');
115      }
116    });
117  }
118
119  persistFilters () {
120    this.hashStore.setState((state) => {
121      if (this.termTarget.value) state.term = this.termTarget.value.toLowerCase();
122
123      // Always persist category state - either specific categories or 'all'
124      if (this.allCategoriesTarget.checked) {
125        state.categories = 'all';
126      } else if (this.categories.length) {
127        state.categories = this.categories;
128      } else {
129        // If no categories selected and not "all", default to "all"
130        state.categories = 'all';
131      }
132
133      if (this.hasRangeTarget) state.range = +this.rangeTarget.value;
134    })
135  }
136
137  loadFilters () {
138    const state = this.hashStore.getState()
139
140    // Term
141    if (state.term) this.termTarget.value = state.term
142
143    // Categories
144    if (state.categories) {
145      if (state.categories === 'all') {
146        // Set "All Beez" checkbox to checked
147        this.allCategoriesTarget.checked = true;
148      } else if (state.categories.length > 0) {
149        // Uncheck "All Beez" and check specific categories
150        this.allCategoriesTarget.checked = false;
151        this.categoryTargets.forEach((categoryTarget) => {
152          if (state.categories.includes(categoryTarget.value)) {
153            categoryTarget.checked = true
154            categoryTarget.dispatchEvent(new Event('change'))
155          }
156        })
157      }
158    }
159
160    // Range
161    if (this.hasRangeTarget && state.range) {
162      this.rangeTarget.value = state.range
163      this.updateRangeGradient()
164    }
165  }
166
167  updateRangeGradient() {
168    if (!this.hasRangeTarget) return;
169
170    const min = parseInt(this.rangeTarget.min);
171    const max = parseInt(this.rangeTarget.max);
172    const value = parseInt(this.rangeTarget.value);
173    const percentage = ((value - min) / (max - min)) * 100;
174
175    this.rangeTarget.style.setProperty('--value-percent', `${percentage}%`);
176  }
177};

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.