1/******/ (() => { // webpackBootstrap 2/** 3 * Docist Search JavaScript 4 * Handles AJAX search functionality with real-time results 5 */ 6(function ($) { 7 'use strict'; 8 9 // WordPress-style hook system for JavaScript 10 var DocistHooks = { 11 actions: {}, 12 filters: {}, 13 14 // Add an action hook 15 addAction: function (hook, callback, priority) { 16 priority = priority || 10; 17 if (!this.actions[hook]) { 18 this.actions[hook] = []; 19 } 20 this.actions[hook].push({ 21 callback: callback, 22 priority: priority, 23 }); 24 // Sort by priority (lower numbers = higher priority) 25 this.actions[hook].sort(function (a, b) { 26 return a.priority - b.priority; 27 }); 28 }, 29 30 // Add a filter hook 31 addFilter: function (hook, callback, priority) { 32 priority = priority || 10; 33 if (!this.filters[hook]) { 34 this.filters[hook] = []; 35 } 36 this.filters[hook].push({ 37 callback: callback, 38 priority: priority, 39 }); 40 // Sort by priority (lower numbers = higher priority) 41 this.filters[hook].sort(function (a, b) { 42 return a.priority - b.priority; 43 }); 44 }, 45 46 // Do an action 47 doAction: function (hook, data) { 48 if (this.actions[hook]) { 49 for (var i = 0; i < this.actions[hook].length; i++) { 50 this.actions[hook][i].callback(data); 51 } 52 } 53 // Also fire jQuery event for backward compatibility 54 $(document).trigger('docist:' + hook, data); 55 }, 56 57 // Apply a filter 58 applyFilters: function (hook, value, data) { 59 if (this.filters[hook]) { 60 for (var i = 0; i < this.filters[hook].length; i++) { 61 value = this.filters[hook][i].callback(value, data); 62 } 63 } 64 return value; 65 }, 66 67 // Remove an action 68 removeAction: function (hook, callback) { 69 if (this.actions[hook]) { 70 for (var i = this.actions[hook].length - 1; i >= 0; i--) { 71 if (this.actions[hook][i].callback === callback) { 72 this.actions[hook].splice(i, 1); 73 } 74 } 75 } 76 }, 77 78 // Remove a filter 79 removeFilter: function (hook, callback) { 80 if (this.filters[hook]) { 81 for (var i = this.filters[hook].length - 1; i >= 0; i--) { 82 if (this.filters[hook][i].callback === callback) { 83 this.filters[hook].splice(i, 1); 84 } 85 } 86 } 87 }, 88 }; 89 90 // Make hooks available globally 91 window.DocistHooks = DocistHooks; 92 93 // Search functionality 94 var DocistSearch = { 95 searchTimeout: null, 96 isSearching: false, 97 98 init: function () { 99 this.bindEvents(); 100 }, 101 102 bindEvents: function () { 103 var self = this; 104 105 // Search input events 106 $(document).on('input', '.docist-search__input', function () { 107 var query = $(this).val().trim(); 108 self.handleSearchInput(query); 109 }); 110 111 // Form submission (prevent default and use AJAX) 112 $(document).on('submit', '.docist-search-form', function (e) { 113 e.preventDefault(); 114 var $input = $(this).find('.docist-search__input'); 115 var query = $input.val().trim(); 116 if (query) { 117 self.performSearch(query); 118 } 119 }); 120 121 // Search button click (fallback) 122 $(document).on('click', '.docist-search__button', function (e) { 123 e.preventDefault(); 124 var $input = $(this).closest('.docist-search').find('.docist-search__input'); 125 var query = $input.val().trim(); 126 if (query) { 127 self.performSearch(query); 128 } else { 129 // Focus the input if empty 130 $input.focus(); 131 } 132 }); 133 134 // Close button click 135 $(document).on('click', '.docist-search__close', function () { 136 self.closeSearch(); 137 }); 138 139 // ESC key to close search 140 $(document).on('keydown', function (e) { 141 if (e.key === 'Escape') { 142 self.closeSearch(); 143 } 144 }); 145 146 // Click outside to close (optional, but good UX) 147 $(document).on('click', function (e) { 148 if (!$(e.target).closest('.docist-search').length) { 149 self.closeSearch(); 150 } 151 }); 152 }, 153 154 handleSearchInput: function (query) { 155 var self = this; 156 var $searchContainer = $('.docist-search__input').closest('.docist-search'); 157 var $searchButton = $searchContainer.find('.docist-search__button'); 158 var $closeButton = $searchContainer.find('.docist-search__close'); 159 160 // Clear previous timeout 161 if (this.searchTimeout) { 162 clearTimeout(this.searchTimeout); 163 } 164 165 // Show/hide buttons based on input content 166 if (query) { 167 $searchButton.hide(); 168 $closeButton.show(); 169 } else { 170 $searchButton.show(); 171 $closeButton.hide(); 172 this.closeSearch(); 173 return; 174 } 175 176 // Debounce search (wait 300ms after user stops typing) 177 this.searchTimeout = setTimeout(function () { 178 if (query.length >= 2) { 179 self.performSearch(query); 180 } 181 }, 300); 182 }, 183 184 performSearch: function (query) { 185 var self = this; 186 var $searchContainer = $('.docist-search__input').closest('.docist-search'); 187 var $results = $searchContainer.find('.docist-search__results'); 188 var $resultsContent = $searchContainer.find('.docist-search__results-content'); 189 var $closeButton = $searchContainer.find('.docist-search__close'); 190 191 // Show loading state 192 $resultsContent.html('<div class="docist-search__loading">Searching...</div>'); 193 $results.show(); 194 $closeButton.show(); 195 196 // Prevent multiple simultaneous requests 197 if (this.isSearching) { 198 return; 199 } 200 201 this.isSearching = true; 202 203 // Fire action before search 204 DocistHooks.doAction('before_search', { 205 query: query, 206 searchContainer: $searchContainer[0], 207 }); 208 209 $.ajax({ 210 url: docist_search.ajax_url, 211 type: 'POST', 212 data: { 213 action: 'docist_search', 214 search_term: query, 215 nonce: docist_search.nonce, 216 }, 217 success: function (response) { 218 self.isSearching = false;
219 220 if (response.success) { 221 self.displayResults(response.data, query); 222 } else { 223 $resultsContent.html('<div class="docist-search__no-results">' + response.data + '</div>'); 224 } 225 }, 226 error: function () { 227 self.isSearching = false; 228 $resultsContent.html( 229 '<div class="docist-search__no-results">' + docist_search.strings.error + '</div>' 230 ); 231 }, 232 }); 233 }, 234 235 displayResults: function (data, query) { 236 var $searchContainer = $('.docist-search__input').closest('.docist-search'); 237 var $resultsContent = $searchContainer.find('.docist-search__results-content'); 238 var $results = $searchContainer.find('.docist-search__results'); 239 var $input = $searchContainer.find('.docist-search__input'); 240 241 // Prepare action data 242 var actionData = { 243 query: query, 244 results: data.results, 245 totalResults: data.total, 246 timestamp: new Date().toISOString(), 247 searchContainer: $searchContainer[0], 248 }; 249 250 if (data.results && data.results.length > 0) { 251 var html = ''; 252 253 $.each(data.results, function (index, result) { 254 html += 255 '<a href="' + result.url + '" class="docist-search__result-item" role="option" tabindex="0">'; 256 html += '<div class="docist-search__result-title">' + result.title + '</div>'; 257 html += '</a>'; 258 }); 259 260 $resultsContent.html(html); 261 $results.show(); 262 263 // Announce results to screen reader 264 this.announceResults(data.results.length); 265 266 // Fire action for search results found 267 DocistHooks.doAction('search_results_found', actionData); 268 } else { 269 $resultsContent.html( 270 '<div class="docist-search__no-results">No results found for "' + query + '"</div>' 271 ); 272 $results.show(); 273 274 // Announce no results to screen reader 275 this.announceResults(0); 276 277 // Fire action for no search results 278 DocistHooks.doAction('search_no_results', actionData); 279 } 280 }, 281 282 closeSearch: function () { 283 var $searchContainer = $('.docist-search__input').closest('.docist-search'); 284 var $results = $searchContainer.find('.docist-search__results'); 285 var $closeButton = $searchContainer.find('.docist-search__close'); 286 var $searchButton = $searchContainer.find('.docist-search__button'); 287 var $input = $searchContainer.find('.docist-search__input'); 288 289 $results.hide(); 290 $closeButton.hide(); 291 $searchButton.show(); 292 $input.val(''); 293 294 // Fire action when search is closed 295 DocistHooks.doAction('search_closed', { 296 searchContainer: $searchContainer[0], 297 }); 298 }, 299 300 announceResults: function (count) { 301 var message = count > 0 ? count + ' search results found' : 'No search results found'; 302 303 // Create announcement for screen readers 304 var announcement = $('<div>') 305 .attr('aria-live', 'assertive') 306 .attr('aria-atomic', 'true') 307 .addClass('sr-only') 308 .text(message); 309 310 $('body').append(announcement); 311 312 // Remove after announcement 313 setTimeout(function () { 314 announcement.remove(); 315 }, 2000); 316 }, 317 }; 318 319 // Initialize when document is ready 320 $(document).ready(function () { 321 DocistSearch.init(); 322 323 // Keyboard navigation 324 $(document).on('keydown', '.docist-search__input', function (e) { 325 var $searchContainer = $(this).closest('.docist-search'); 326 var $results = $searchContainer.find('.docist-search__results'); 327 var $resultItems = $results.find('.docist-search__result-item'); 328 329 if (e.key === 'ArrowDown' && $results.is(':visible')) { 330 e.preventDefault(); 331 $resultItems.first().focus(); 332 } else if (e.key === 'Escape') { 333 DocistSearch.closeSearch(); 334 } 335 }); 336 337 // Keyboard navigation for result items 338 $(document).on('keydown', '.docist-search__result-item', function (e) { 339 var $currentItem = $(this); 340 var $nextItem = $currentItem.next('.docist-search__result-item'); 341 var $prevItem = $currentItem.prev('.docist-search__result-item'); 342 var $searchInput = $currentItem.closest('.docist-search').find('.docist-search__input'); 343 344 if (e.key === 'ArrowDown') { 345 e.preventDefault(); 346 if ($nextItem.length) { 347 $nextItem.focus(); 348 } 349 } else if (e.key === 'ArrowUp') { 350 e.preventDefault(); 351 if ($prevItem.length) { 352 $prevItem.focus(); 353 } else { 354 $searchInput.focus(); 355 } 356 } else if (e.key === 'Escape') { 357 DocistSearch.closeSearch(); 358 } 359 }); 360 }); 361})(jQuery); 362 363/******/ })() 364; 365//# sourceMappingURL=docist-search.js.map
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.