PageSourceSearch

https://themeist.com/wp-content/plugins/docist/assets/dist/js/docist-search.js?ver=1.0.0

js themeist.com collected 2026-10-02 05:14:04 UTC 9,924 bytes, 365 lines download raw bytes

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.