1 2/** 3 * 4 * Advanced Search Panel Toggle & Filter Management 5 * 6 * Handles collapsible advanced search functionality and mutual exclusivity 7 * between tag filters and content type selector 8 */ 9 10document.addEventListener("DOMContentLoaded", () => { 11 const toggleButton = document.querySelector(".advanced-search-toggle"); 12 const searchContent = document.getElementById("advanced-search-content"); 13 const contentTypeSelect = document.getElementById("content_type"); 14 const tagCheckboxes = document.querySelectorAll(".tag-filter-checkbox"); 15 16 if (!toggleButton || !searchContent) { 17 return; 18 } 19 20 // ===== Toggle Functionality ===== 21 function toggleSearch() { 22 const isExpanded = toggleButton.getAttribute("aria-expanded") === "true"; 23 24 toggleButton.setAttribute("aria-expanded", !isExpanded); 25 26 if (isExpanded) { 27 searchContent.setAttribute("hidden", ""); 28 } else { 29 searchContent.removeAttribute("hidden"); 30 } 31 } 32 33 toggleButton.addEventListener("click", toggleSearch); 34 35 // ===== Filter Mutual Exclusivity ===== 36 // Tags only apply to posts (News). When "Everything" is selected, tags are locked. 37 // When a tag is checked, content type switches to "News". 38 function updateFilterStates() { 39 const isEverythingSelected = 40 contentTypeSelect && contentTypeSelect.value === "all"; 41 42 // Disable/enable tag checkboxes based on content type 43 const tagFilterSection = document.querySelector(".search-filter-tags"); 44 tagCheckboxes.forEach((checkbox) => { 45 if (isEverythingSelected) { 46 checkbox.disabled = true; 47 checkbox.checked = false; 48 } else { 49 checkbox.disabled = false; 50 } 51 }); 52 53 // Disable/enable search operator radios 54 const searchOperatorRadios = document.querySelectorAll(".search-operator-radio"); 55 searchOperatorRadios.forEach((radio) => { 56 radio.disabled = isEverythingSelected; 57 }); 58 59 // Visual feedback for tag section 60 if (tagFilterSection) { 61 if (isEverythingSelected) { 62 tagFilterSection.style.opacity = "0.6"; 63 tagFilterSection.style.pointerEvents = "none"; 64 } else { 65 tagFilterSection.style.opacity = "1"; 66 tagFilterSection.style.pointerEvents = "auto"; 67 } 68 } 69 70 // Update selected tags display 71 updateSelectedTagsList(); 72 } 73 74 // ===== Update Selected Tags Display ===== 75 function updateSelectedTagsList() { 76 const selectedTagsList = document.getElementById("selected_tags_list"); 77 if (!selectedTagsList) { 78 return; 79 } 80 81 // Get all checked checkboxes 82 const checkedCheckboxes = Array.from(tagCheckboxes).filter((checkbox) => checkbox.checked); 83 84 if (checkedCheckboxes.length === 0) { 85 selectedTagsList.innerHTML = ""; 86 return; 87 } 88 89 // Build comma-separated list 90 const tagLabels = checkedCheckboxes.map((checkbox) => { 91 return checkbox.closest(".tag-checkbox-inline").querySelector(".tag-label").textContent; 92 }); 93 94 selectedTagsList.innerHTML = `<span class="selected-tags-text">Selected: ${tagLabels.join(", ")}</span>`; 95 } 96 97 // Add event listeners to content type select 98 if (contentTypeSelect) { 99 contentTypeSelect.addEventListener("change", updateFilterStates); 100 } 101 102 // Add event listeners to all tag checkboxes 103 tagCheckboxes.forEach((checkbox) => { 104 checkbox.addEventListener("change", () => { 105 // When a tag is checked, switch content type to News (tags only apply to posts) 106 if (checkbox.checked && contentTypeSelect && contentTypeSelect.value === "all") { 107 contentTypeSelect.value = "news"; 108 } 109 updateFilterStates(); 110 }); 111 }); 112 113 // Initial state update 114 updateFilterStates(); 115 116 // ===== Auto-Expand on Active Filters ===== 117 const form = document.querySelector(".advanced-search-form"); 118 const mainSearchForm = document.querySelector(".search-bar-top .search-form, .search-prompt .search-form"); 119 120 // Helper to add a hidden input to a form 121 function addHiddenInput(targetForm, name, value) { 122 const input = document.createElement("input"); 123 input.type = "hidden"; 124 input.name = name; 125 input.value = value; 126 input.className = "synced-filter"; 127 targetForm.appendChild(input); 128 } 129 130 // Helper to get current filter values from the advanced search form 131 function getCurrentFilters() { 132 const filters = {};
133 const dateRange = document.getElementById("date_range"); 134 const orderby = document.getElementById("orderby"); 135 const searchOperator = document.querySelector('input[name="search_operator"]:checked'); 136 137 if (dateRange && dateRange.value !== "all") { 138 filters.date_range = dateRange.value; 139 } 140 if (contentTypeSelect && contentTypeSelect.value !== "all") { 141 filters.content_type = contentTypeSelect.value; 142 } 143 if (orderby && orderby.value !== "date_desc") { 144 filters.orderby = orderby.value; 145 } 146 if (searchOperator && searchOperator.value !== "AND") { 147 filters.search_operator = searchOperator.value; 148 } 149 150 // Get selected tags from checkboxes 151 const selectedTags = Array.from(tagCheckboxes) 152 .filter((cb) => cb.checked) 153 .map((cb) => cb.value); 154 if (selectedTags.length > 0) { 155 filters.tag_filter = selectedTags.join(","); 156 } 157 158 return filters; 159 } 160 161 // ===== Sync: Main search form includes advanced filters ===== 162 if (mainSearchForm) { 163 mainSearchForm.addEventListener("submit", () => { 164 // Remove any previously synced hidden inputs 165 mainSearchForm.querySelectorAll(".synced-filter").forEach((el) => el.remove()); 166 167 // Inject current filter values as hidden inputs 168 const filters = getCurrentFilters(); 169 for (const [name, value] of Object.entries(filters)) { 170 addHiddenInput(mainSearchForm, name, value); 171 } 172 }); 173 } 174 175 // Handle form submission to populate tag_filter hidden field with comma-separated values 176 if (form) { 177 form.addEventListener("submit", () => { 178 // ===== Sync: Advanced form uses current search input value ===== 179 if (mainSearchForm) { 180 const mainSearchInput = mainSearchForm.querySelector('input[name="s"]'); 181 const hiddenSearchInput = form.querySelector('input[name="s"]'); 182 if (mainSearchInput && hiddenSearchInput) { 183 hiddenSearchInput.value = mainSearchInput.value; 184 } 185 } 186 187 const tagFilterHidden = document.getElementById("tag_filter_hidden"); 188 if (tagFilterHidden) { 189 // Clear tags when Everything is selected (tags only apply to posts) 190 const isEverything = contentTypeSelect && contentTypeSelect.value === "all"; 191 if (isEverything) { 192 tagFilterHidden.value = ''; 193 } else { 194 // Collect all checked tag filter checkboxes and create comma-separated list 195 const selectedTags = Array.from(tagCheckboxes) 196 .filter((checkbox) => checkbox.checked) 197 .map((checkbox) => checkbox.value); 198 tagFilterHidden.value = selectedTags.join(','); 199 } 200 } 201 202 }); 203 } 204 if (form) { 205 let hasActiveFilters = false; 206 207 // Check for active filters 208 tagCheckboxes.forEach((checkbox) => { 209 if (checkbox.checked) { 210 hasActiveFilters = true; 211 } 212 }); 213 214 if (contentTypeSelect && contentTypeSelect.value !== "all") { 215 hasActiveFilters = true; 216 } 217 218 const dateRangeSelect = document.getElementById("date_range"); 219 if (dateRangeSelect && dateRangeSelect.value !== "all") { 220 hasActiveFilters = true; 221 } 222 223 const orderbySelect = document.getElementById("orderby"); 224 if (orderbySelect && orderbySelect.value !== "date_desc") { 225 hasActiveFilters = true; 226 } 227 228 const searchOperatorRadios = document.querySelectorAll( 229 'input[name="search_operator"]', 230 ); 231 for (const radio of searchOperatorRadios) { 232 if (radio.checked && radio.value !== "AND") { 233 hasActiveFilters = true; 234 break; 235 } 236 } 237 238 // Expand if filters are active 239 if (hasActiveFilters) { 240 toggleButton.setAttribute("aria-expanded", "true"); 241 searchContent.removeAttribute("hidden"); 242 } 243 } 244});
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.