Skip to content

Commit 2f06045

Browse files
committed
refactor: consolidate conference filtering and remove lazy loading complexity
- Remove lazy-load.js script from layouts (home.html, year.html) - Consolidate all badge click handlers into conference-filter.js - Fix filter toggle behavior when clicking same badge twice - Improve multiselect synchronization to prevent feedback loops - Add proper event namespacing to prevent duplicate handlers - Remove conferenceLoaded event listeners from action-bar.js and favorites.js - Add test files for debugging filtering and countdown functionality This simplifies the codebase by removing the complex lazy loading system and consolidating all filtering logic in one place. Badge clicks now properly toggle between single filter and show-all states.
1 parent 5efd0e9 commit 2f06045

File tree

12 files changed

+411
-121
lines changed

12 files changed

+411
-121
lines changed

_layouts/home.html

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,4 @@
1818
{{ content }}
1919

2020
<!-- Consolidated Conference Filtering -->
21-
<script type="text/javascript" src="{{ "/static/js/conference-filter.js" | prepend:site.baseurl_root }}?t={{site.time | date: '%s'}}"></script>
22-
23-
<!-- Lazy Loading for Performance -->
24-
<script type="text/javascript" src="{{ "/static/js/lazy-load.js" | prepend:site.baseurl_root }}?t={{site.time | date: '%s'}}"></script>
21+
<script type="text/javascript" src="{{ "/static/js/conference-filter.js" | prepend:site.baseurl_root }}?t={{site.time | date: '%s'}}"></script>

_layouts/year.html

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -108,14 +108,7 @@ <h1 id="past-events-title">{% t titles.past_events %}</h1>
108108

109109
{% include handle_url_retrieval.js %}
110110

111-
// Event handler on sub badge click
112-
$('.conf-sub').click(function (e) {
113-
var csub = $(this).data('sub');
114-
subs = [csub];
115-
$("#subject-select").multiselect('deselect', all_subs);
116-
$("#subject-select").multiselect('select', subs);
117-
update_filtering({ subs: subs, all_subs: all_subs});
118-
});
111+
// Legacy click handler removed - now handled by conference-filter.js
119112

120113
});
121114
</script>

_pages/archive.html

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -95,14 +95,7 @@ <h1 id="past-events-title">{% t titles.past_events %}</h1>
9595

9696
{% include handle_url_retrieval.js %}
9797

98-
// Event handler on sub badge click
99-
$('.conf-sub').click(function (e) {
100-
var csub = $(this).data('sub');
101-
subs = [csub];
102-
$("#subject-select").multiselect('deselect', all_subs);
103-
$("#subject-select").multiselect('select', subs);
104-
update_filtering({ subs: subs, all_subs: all_subs});
105-
});
98+
// Legacy click handler removed - now handled by conference-filter.js
10699

107100
});
108101
</script>

index.html

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -168,14 +168,7 @@ <h1 id="archive-link"><a href="{% tl archive %}">{% t titles.visit_archive %}</a
168168

169169
{% include handle_url_retrieval.js %}
170170

171-
// Event handler on sub badge click
172-
$('.conf-sub').click(function (e) {
173-
var csub = $(this).data('sub');
174-
subs = [csub];
175-
$("#subject-select").multiselect('deselect', all_subs);
176-
$("#subject-select").multiselect('select', subs);
177-
update_filtering({ subs: subs, all_subs: all_subs});
178-
});
171+
// Legacy click handler removed - now handled by conference-filter.js
179172

180173
});
181174
</script>

static/js/action-bar.js

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -536,17 +536,6 @@ END:VCALENDAR`;
536536
initializeIndicators();
537537
}
538538

539-
// Handle lazy-loaded conferences
540-
document.addEventListener('conferenceLoaded', function(e) {
541-
if (e.detail && e.detail.element) {
542-
const indicator = e.detail.element.querySelector('.action-indicator');
543-
const mobileBtn = e.detail.element.querySelector('.mobile-action-bookmark');
544-
if (indicator || mobileBtn) {
545-
initializeIndicators();
546-
}
547-
}
548-
});
549-
550539
// Export API for other components
551540
window.minimalActionAPI = {
552541
getPrefs: getPrefs,

static/js/conference-filter.js

Lines changed: 160 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -16,22 +16,35 @@
1616
},
1717

1818
allSubs: [],
19+
isUpdatingMultiselect: false,
20+
isInitialized: false,
1921

2022
// Initialize the filter manager
2123
init: function() {
24+
// Prevent multiple initializations
25+
if (this.isInitialized) {
26+
return;
27+
}
28+
this.isInitialized = true;
29+
2230
this.loadState();
2331
this.bindEvents();
2432
this.applyInitialFilters();
2533
},
2634

2735
// Load filter state from localStorage and URL
2836
loadState: function() {
29-
// Get all available subcategories
37+
// Get all available subcategories from multiselect or fallback to known categories
3038
this.allSubs = [];
3139
$('#subject-select option').each((i, opt) => {
3240
if (opt.value) this.allSubs.push(opt.value);
3341
});
3442

43+
// If multiselect doesn't exist or has no options, use default categories
44+
if (this.allSubs.length === 0) {
45+
this.allSubs = ['PY', 'SCIPY', 'DATA', 'WEB', 'BIZ', 'GEO', 'CAMP', 'DAY'];
46+
}
47+
3548
// Check URL parameters first (highest priority)
3649
const urlParams = new URLSearchParams(window.location.search);
3750
const urlSubs = urlParams.get('sub');
@@ -42,10 +55,15 @@
4255
// Fall back to localStorage
4356
const stored = store.get(this.STORAGE_DOMAIN + '-subs');
4457
if (stored && !this.isDataExpired(stored)) {
45-
this.currentFilters.subs = stored.subs || [];
58+
// If stored filters are all categories, treat as empty (show all)
59+
if (stored.subs && stored.subs.length === this.allSubs.length) {
60+
this.currentFilters.subs = [];
61+
} else {
62+
this.currentFilters.subs = stored.subs || [];
63+
}
4664
} else {
47-
// Default to all categories
48-
this.currentFilters.subs = [...this.allSubs];
65+
// Default to empty (show all) instead of listing all categories
66+
this.currentFilters.subs = [];
4967
}
5068
}
5169
},
@@ -77,15 +95,16 @@
7795

7896
// Apply filters to conference cards
7997
applyFilters: function() {
80-
// Hide all conferences first
81-
$('.ConfItem').hide();
82-
83-
// Show filtered conferences
84-
if (this.currentFilters.subs.length === 0 ||
85-
this.currentFilters.subs.length === this.allSubs.length) {
98+
// If no filters or all filters selected, show all conferences
99+
if (!this.currentFilters.subs ||
100+
this.currentFilters.subs.length === 0 ||
101+
(this.allSubs.length > 0 && this.currentFilters.subs.length === this.allSubs.length)) {
86102
// Show all
87103
$('.ConfItem').show();
88104
} else {
105+
// Hide all conferences first
106+
$('.ConfItem').hide();
107+
89108
// Show only selected subcategories
90109
this.currentFilters.subs.forEach(sub => {
91110
$('.' + sub + '-conf').show();
@@ -128,31 +147,60 @@
128147

129148
// Update filters from multiselect dropdown
130149
updateFromMultiselect: function(selectedValues) {
131-
this.currentFilters.subs = selectedValues || [];
150+
// Skip if we're programmatically updating the multiselect
151+
if (this.isUpdatingMultiselect) {
152+
return;
153+
}
154+
155+
// Ensure selectedValues is always an array
156+
if (typeof selectedValues === 'string') {
157+
this.currentFilters.subs = [selectedValues];
158+
} else if (Array.isArray(selectedValues)) {
159+
this.currentFilters.subs = selectedValues;
160+
} else {
161+
this.currentFilters.subs = [];
162+
}
163+
132164
this.saveState();
133165
this.applyFilters();
134166
},
135167

136168
// Filter by single subcategory (from badge click)
137169
filterBySub: function(sub) {
138-
// Check if this is the only selected item - if so, select all (toggle behavior)
139-
const $select = $('#subject-select');
140-
const currentlySelected = $select.val() || [];
170+
// Check if this is currently the only selected filter
171+
const isOnlyFilter = this.currentFilters.subs.length === 1 &&
172+
this.currentFilters.subs[0] === sub;
141173

142-
if (currentlySelected.length === 1 && currentlySelected[0] === sub) {
143-
// If clicking the same single selected item, select all
144-
this.clearFilters();
174+
if (isOnlyFilter) {
175+
// Toggle behavior: clear filters to show all
176+
this.currentFilters.subs = [];
145177
} else {
146-
// Otherwise filter by this subcategory only
178+
// Otherwise filter to just this category
147179
this.currentFilters.subs = [sub];
180+
}
181+
182+
// Save state and apply filters
183+
this.saveState();
184+
this.applyFilters();
185+
186+
// Update multiselect UI if it exists
187+
const $select = $('#subject-select');
188+
if ($select.length && $select.data('multiselect')) {
189+
// Set flag to prevent feedback loop
190+
this.isUpdatingMultiselect = true;
148191

149-
// Update multiselect UI
150-
$select.multiselect('deselectAll', false);
151-
$select.multiselect('select', sub);
192+
if (this.currentFilters.subs.length === 0) {
193+
// Select all
194+
$select.val(this.allSubs);
195+
} else {
196+
$select.val(this.currentFilters.subs);
197+
}
152198
$select.multiselect('refresh');
153199

154-
this.saveState();
155-
this.applyFilters();
200+
// Reset flag after a short delay to ensure change event has fired
201+
setTimeout(() => {
202+
this.isUpdatingMultiselect = false;
203+
}, 100);
156204
}
157205
},
158206

@@ -187,28 +235,44 @@
187235
bindEvents: function() {
188236
const self = this;
189237

238+
// Unbind any existing handlers first to prevent duplicates
239+
$(document).off('change.conferenceFilter');
240+
$(document).off('click.conferenceFilter');
241+
$(document).off('mouseenter.conferenceFilter');
242+
$(document).off('mouseleave.conferenceFilter');
243+
190244
// Handle multiselect changes
191-
$(document).on('change', '#subject-select', function() {
245+
$(document).on('change.conferenceFilter', '#subject-select', function() {
246+
// Skip if we're programmatically updating
247+
if (self.isUpdatingMultiselect) {
248+
return;
249+
}
192250
const selected = $(this).val() || [];
193251
self.updateFromMultiselect(selected);
194252
});
195253

196-
// Handle badge clicks
197-
$(document).on('click', '.conf-sub', function(e) {
198-
e.preventDefault();
199-
e.stopPropagation();
200-
const sub = $(this).data('sub');
201-
if (sub) {
202-
self.filterBySub(sub);
203-
}
204-
});
254+
// Handle badge clicks - use document delegation for dynamically loaded elements
255+
// First remove any legacy direct click handlers
256+
$('.conf-sub').off('click');
257+
258+
// Then bind our delegated handler with specific selector
259+
$(document).off('click.conferenceFilter', '.conf-sub')
260+
.on('click.conferenceFilter', '.conf-sub', function(e) {
261+
e.preventDefault();
262+
e.stopImmediatePropagation(); // Stop all other handlers
263+
const sub = $(this).data('sub');
264+
if (sub) {
265+
// Call FilterManager directly to maintain context
266+
self.filterBySub(sub);
267+
}
268+
});
205269

206270
// Add hover effects to indicate clickability
207-
$(document).on('mouseenter', '.conf-sub', function() {
271+
$(document).on('mouseenter.conferenceFilter', '.conf-sub', function() {
208272
$(this).css('opacity', '0.8');
209273
});
210274

211-
$(document).on('mouseleave', '.conf-sub', function() {
275+
$(document).on('mouseleave.conferenceFilter', '.conf-sub', function() {
212276
$(this).css('opacity', '1');
213277
});
214278

@@ -221,35 +285,78 @@
221285

222286
// Apply initial filters on page load
223287
applyInitialFilters: function() {
224-
// Update multiselect to match current state
225-
const $select = $('#subject-select');
226-
if ($select.length) {
227-
$select.multiselect('deselectAll', false);
228-
this.currentFilters.subs.forEach(sub => {
229-
$select.multiselect('select', sub);
230-
});
231-
$select.multiselect('refresh');
288+
// If filters contain all categories, treat as "show all" (empty filters)
289+
if (this.currentFilters.subs.length === this.allSubs.length) {
290+
this.currentFilters.subs = [];
232291
}
233292

234-
// Apply filters
293+
// Apply filters first
235294
this.applyFilters();
295+
296+
// Update multiselect to match current state (without triggering change events)
297+
const $select = $('#subject-select');
298+
if ($select.length && $select.data('multiselect')) {
299+
// Set flag to prevent feedback loop during initialization
300+
this.isUpdatingMultiselect = true;
301+
302+
// Update multiselect without triggering change events
303+
if (this.currentFilters.subs.length === 0) {
304+
// Show all selected in multiselect
305+
$select.val(this.allSubs);
306+
} else {
307+
// Show only filtered categories selected
308+
$select.val(this.currentFilters.subs);
309+
}
310+
$select.multiselect('refresh');
311+
312+
// Reset flag after update
313+
setTimeout(() => {
314+
this.isUpdatingMultiselect = false;
315+
}, 100);
316+
}
236317
}
237318
};
238319

239320
// Public API
240321
window.ConferenceFilter = {
241-
init: () => FilterManager.init(),
242-
filterBySub: (sub) => FilterManager.filterBySub(sub),
243-
search: (query) => FilterManager.search(query),
244-
clearFilters: () => FilterManager.clearFilters(),
245-
getCurrentFilters: () => FilterManager.currentFilters,
246-
updateFromMultiselect: (values) => FilterManager.updateFromMultiselect(values)
322+
init: function() {
323+
return FilterManager.init();
324+
},
325+
filterBySub: function(sub) {
326+
return FilterManager.filterBySub(sub);
327+
},
328+
search: function(query) {
329+
return FilterManager.search(query);
330+
},
331+
clearFilters: function() {
332+
return FilterManager.clearFilters();
333+
},
334+
getCurrentFilters: function() {
335+
return FilterManager.currentFilters;
336+
},
337+
updateFromMultiselect: function(values) {
338+
return FilterManager.updateFromMultiselect(values);
339+
}
247340
};
248341

342+
// Global alias for backward compatibility
343+
window.filterBySub = (sub) => FilterManager.filterBySub(sub);
344+
249345
// Auto-initialize when DOM is ready
250-
if (document.readyState === 'loading') {
251-
document.addEventListener('DOMContentLoaded', () => FilterManager.init());
346+
// Wait for jQuery to be ready and for page setup to complete
347+
if (typeof jQuery !== 'undefined') {
348+
// Use jQuery's ready handler which ensures DOM is ready
349+
jQuery(document).ready(function() {
350+
// Add a small delay to ensure other scripts have finished setup
351+
setTimeout(() => FilterManager.init(), 100);
352+
});
353+
} else if (document.readyState === 'loading') {
354+
document.addEventListener('DOMContentLoaded', () => {
355+
// Add delay for other scripts
356+
setTimeout(() => FilterManager.init(), 100);
357+
});
252358
} else {
253-
FilterManager.init();
359+
// Already loaded, but still wait for other scripts
360+
setTimeout(() => FilterManager.init(), 100);
254361
}
255362
})();

0 commit comments

Comments
 (0)