PageSourceSearch

https://dressagetoday.com/wp-content/plugins/equine-streaming/assets/js/search.js?ver=0.5.3

js dressagetoday.com collected 2026-10-02 11:25:59 UTC 6,782 bytes, 279 lines download raw bytes

1/**
2 * Search functionality for Equine Streaming plugin.
3 */
4(function($) {
5	'use strict';
6
7	var searchTimeout = null;
8	var isSearching = false;
9
10	/**
11	 * Initialize search functionality.
12	 */
13	function initSearch() {
14		// Open modal when search icon is clicked
15		$(document).on('click', '.equine-search-icon', function(e) {
16			e.preventDefault();
17			openModal();
18		});
19
20		// Close modal when close button is clicked
21		$(document).on('click', '.equine-search-modal-close', function(e) {
22			e.preventDefault();
23			closeModal();
24		});
25
26		// Close modal when overlay is clicked
27		$(document).on('click', '.equine-search-modal-overlay', function(e) {
28			e.preventDefault();
29			closeModal();
30		});
31
32		// Close modal on ESC key
33		$(document).on('keydown', function(e) {
34			if (e.key === 'Escape' || e.keyCode === 27) {
35				if ($('.equine-search-modal').is(':visible')) {
36					closeModal();
37				}
38			}
39		});
40
41		// Handle search input
42		$(document).on('input', '.equine-search-input', function() {
43			var query = $(this).val().trim();
44			
45			// Clear previous timeout
46			if (searchTimeout) {
47				clearTimeout(searchTimeout);
48			}
49
50			// If query is less than 3 characters, clear results
51			if (query.length < 3) {
52				clearResults();
53				return;
54			}
55
56			// Debounce search (wait 300ms after user stops typing)
57			searchTimeout = setTimeout(function() {
58				performSearch(query);
59			}, 300);
60		});
61
62		// Handle Enter key in search input
63		$(document).on('keydown', '.equine-search-input', function(e) {
64			if (e.key === 'Enter' || e.keyCode === 13) {
65				e.preventDefault();
66				var query = $(this).val().trim();
67				if (query.length >= 3) {
68					if (searchTimeout) {
69						clearTimeout(searchTimeout);
70					}
71					performSearch(query);
72				}
73			}
74		});
75
76		// Handle result click
77		$(document).on('click', '.equine-search-result-item', function(e) {
78			e.preventDefault();
79			var url = $(this).data('url');
80			if (url) {
81				window.location.href = url;
82			}
83		});
84	}
85
86	/**
87	 * Open search modal.
88	 */
89	function openModal() {
90		// Hide all elements that might appear above the modal
91		$('body').addClass('equine-search-modal-open');
92		
93		$('.equine-search-modal').css({
94			'display': 'block',
95			'opacity': '1',
96			'z-index': '99999999'
97		});
98		$('.equine-search-modal-overlay').css({
99			'opacity': '1',
100			'background-color': 'rgba(0, 0, 0, 0.75)',
101			'z-index': '99999998'
102		});
103		$('.equine-search-modal-content').css({
104			'z-index': '99999999'
105		});
106		$('.equine-search-input').focus();
107		$('body').css('overflow', 'hidden'); // Prevent body scroll
108	}
109
110	/**
111	 * Close search modal.
112	 */
113	function closeModal() {
114		$('.equine-search-modal').css('display', 'none');
115		$('.equine-search-input').val('');
116		clearResults();
117		$('body').removeClass('equine-search-modal-open');
118		$('body').css('overflow', ''); // Restore body scroll
119	}
120
121	/**
122	 * Perform search via AJAX.
123	 *
124	 * @param {string} query Search query.
125	 */
126	function performSearch(query) {
127		if (isSearching) {
128			return; // Prevent multiple simultaneous requests
129		}
130
131		if (!query || query.length < 3) {
132			clearResults();
133			return;
134		}
135
136		isSearching = true;
137		showLoading();
138
139		$.ajax({
140			url: equineSearch.ajaxUrl,
141			type: 'POST',
142			data: {
143				action: 'equine_streaming_search',
144				nonce: equineSearch.nonce,
145				query: query
146			},
147			success: function(response) {
148				isSearching = false;
149				
150				if (response.success && response.data && response.data.results) {
151					displayResults(response.data.results);
152				} else {
153					displayNoResults();
154				}
155			},
156			error: function() {
157				isSearching = false;
158				displayError();
159			}
160		});
161	}
162
163	/**
164	 * Display search results.
165	 *
166	 * @param {Array} results Array of result objects.
167	 */
168	function displayResults(results) {
169		var $resultsContainer = $('.equine-search-results');
170		$resultsContainer.empty();
171
172		if (results.length === 0) {
173			displayNoResults();
174			return;
175		}
176
177		var html = '<div class="equine-search-results-list">';
178		
179		$.each(results, function(index, result) {
180			var thumbnail = result.thumbnail || '';
181			var duration = result.duration_formatted || '';
182			var taxonomies = result.taxonomies || '';
183
184			html += '<div class="equine-search-result-item" data-url="' + escapeHtml(result.url) + '">';
185			
186			html += '<div class="equine-search-result-thumbnail">';
187			if (thumbnail && thumbnail.trim() !== '') {
188				html += '<img src="' + escapeHtml(thumbnail) + '" alt="' + escapeHtml(result.title) + '" onerror="this.style.display=\'none\'">';
189			} else {
190				html += '<div class="equine-search-thumbnail-placeholder">📹</div>';
191			}
192			if (duration) {
193				html += '<span class="equine-search-result-duration">' + escapeHtml(duration) + '</span>';
194			}
195			html += '</div>';
196			
197			html += '<div class="equine-search-result-content">';
198			html += '<h3 class="equine-search-result-title">' + escapeHtml(result.title) + '</h3>';
199			if (taxonomies) {
200				html += '<p class="equine-search-result-taxonomies">' + escapeHtml(taxonomies) + '</p>';
201			} else {
202				html += '<p class="equine-search-result-taxonomies" style="color: #999; font-style: italic;">No categories</p>';
203			}
204			html += '</div>';
205			html += '</div>';
206		});
207
208		html += '</div>';
209		$resultsContainer.html(html);
210	}
211
212	/**
213	 * Display "no results" message.
214	 */
215	function displayNoResults() {
216		var $resultsContainer = $('.equine-search-results');
217		$resultsContainer.html(
218			'<div class="equine-search-no-results">' +
219			'<p>' + (typeof equineSearch !== 'undefined' && equineSearch.noResultsText ? equineSearch.noResultsText : 'No results found.') + '</p>' +
220			'</div>'
221		);
222	}
223
224	/**
225	 * Display error message.
226	 */
227	function displayError() {
228		var $resultsContainer = $('.equine-search-results');
229		$resultsContainer.html(
230			'<div class="equine-search-error">' +
231			'<p>' + (typeof equineSearch !== 'undefined' && equineSearch.errorText ? equineSearch.errorText : 'An error occurred. Please try again.') + '</p>' +
232			'</div>'
233		);
234	}
235
236	/**
237	 * Show loading state.
238	 */
239	function showLoading() {
240		var $resultsContainer = $('.equine-search-results');
241		$resultsContainer.html(
242			'<div class="equine-search-loading">' +
243			'<div class="equine-search-spinner"></div>' +
244			'<p>Searching...</p>' +
245			'</div>'
246		);
247	}
248
249	/**
250	 * Clear results.
251	 */
252	function clearResults() {
253		var $resultsContainer = $('.equine-search-results');
254		$resultsContainer.empty();
255	}
256
257	/**
258	 * Escape HTML to prevent XSS.
259	 *
260	 * @param {string} text Text to escape.
261	 * @return {string} Escaped text.
262	 */
263	function escapeHtml(text) {
264		var map = {
265			'&': '&amp;',
266			'<': '&lt;',
267			'>': '&gt;',
268			'"': '&quot;',
269			"'": '&#039;'
270		};
271		return text.replace(/[&<>"']/g, function(m) { return map[m]; });
272	}
273
274	// Initialize when DOM is ready
275	$(document).ready(function() {
276		initSearch();
277	});
278
279})(jQuery);

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.