/** * WC Licensed Product - Admin Licenses Live Search * * @package Jeremias\WcLicensedProduct */ (function($) { 'use strict'; var searchTimeout = null; var $searchInput = $('#license-search-input'); var $resultsDropdown = null; var isSearching = false; /** * Initialize live search */ function init() { if (!$searchInput.length) { return; } // Create results dropdown $resultsDropdown = $('
'); $searchInput.parent().css('position', 'relative').append($resultsDropdown); // Bind events $searchInput.on('input', handleSearchInput); $searchInput.on('keydown', handleKeydown); $searchInput.on('focus', function() { if ($resultsDropdown.children().length > 0) { $resultsDropdown.show(); } }); // Close dropdown when clicking outside $(document).on('click', function(e) { if (!$(e.target).closest('.search-box').length) { $resultsDropdown.hide(); } }); } /** * Handle search input with debouncing */ function handleSearchInput() { var query = $searchInput.val().trim(); // Clear previous timeout if (searchTimeout) { clearTimeout(searchTimeout); } // Hide results if query is too short if (query.length < 2) { $resultsDropdown.hide().empty(); return; } // Show loading state showLoading(); // Debounce search searchTimeout = setTimeout(function() { performSearch(query); }, 300); } /** * Handle keyboard navigation */ function handleKeydown(e) { var $items = $resultsDropdown.find('.wclp-search-result-item'); var $active = $items.filter('.active'); var index = $items.index($active); switch (e.keyCode) { case 40: // Down arrow e.preventDefault(); if (index < $items.length - 1) { $items.removeClass('active'); $items.eq(index + 1).addClass('active'); } else if (index === -1 && $items.length > 0) { $items.eq(0).addClass('active'); } break; case 38: // Up arrow e.preventDefault(); if (index > 0) { $items.removeClass('active'); $items.eq(index - 1).addClass('active'); } break; case 13: // Enter if ($active.length) { e.preventDefault(); window.location.href = $active.data('url'); } break; case 27: // Escape $resultsDropdown.hide(); break; } } /** * Show loading state */ function showLoading() { $resultsDropdown.html( '
' + ' ' + wclpAdmin.strings.searching + '
' ).show(); } /** * Perform AJAX search */ function performSearch(query) { if (isSearching) { return; } isSearching = true; $.ajax({ url: wclpAdmin.ajaxUrl, type: 'GET', data: { action: 'wclp_live_search', nonce: wclpAdmin.nonce, search: query }, success: function(response) { if (response.success && response.data.results) { renderResults(response.data.results, query); } else { showNoResults(); } }, error: function() { $resultsDropdown.html( '
' + wclpAdmin.strings.error + '
' ).show(); }, complete: function() { isSearching = false; } }); } /** * Render search results */ function renderResults(results, query) { if (results.length === 0) { showNoResults(); return; } var html = ''; results.forEach(function(item) { var statusClass = 'license-status-' + item.status; var highlightedKey = highlightMatch(item.license_key, query); var highlightedDomain = highlightMatch(item.domain, query); html += '
' + '
' + '' + highlightedKey + '' + '' + escapeHtml(item.status) + '' + '
' + '
' + '' + highlightedDomain + '' + '' + escapeHtml(item.product_name) + '' + '
' + '
' + escapeHtml(item.customer_name) + (item.customer_email ? ' (' + escapeHtml(item.customer_email) + ')' : '') + '
' + '
'; }); $resultsDropdown.html(html).show(); // Make items clickable $resultsDropdown.find('.wclp-search-result-item').on('click', function() { window.location.href = $(this).data('url'); }).on('mouseenter', function() { $resultsDropdown.find('.wclp-search-result-item').removeClass('active'); $(this).addClass('active'); }); } /** * Show no results message */ function showNoResults() { $resultsDropdown.html( '
' + wclpAdmin.strings.noResults + '
' ).show(); } /** * Highlight matching text */ function highlightMatch(text, query) { if (!text || !query) { return escapeHtml(text || ''); } var escaped = escapeHtml(text); var queryEscaped = escapeHtml(query); var regex = new RegExp('(' + queryEscaped.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + ')', 'gi'); return escaped.replace(regex, '$1'); } /** * Escape HTML entities */ function escapeHtml(text) { if (!text) return ''; var div = document.createElement('div'); div.textContent = text; return div.innerHTML; } // Initialize when document is ready $(document).ready(init); })(jQuery);