INIclaw / docs /_ext /search_assets /modules /EventHandler.js
Nitish kumar
Upload folder using huggingface_hub
0722e92 verified
Raw
History Blame Contribute Delete
9.56 kB
/**
* EventHandler Module
* Handles keyboard shortcuts and event management for the search interface
*/
class EventHandler {
constructor(enhancedSearch) {
this.enhancedSearch = enhancedSearch;
this.searchInterface = enhancedSearch.searchInterface;
this.resultRenderer = enhancedSearch.resultRenderer;
this.searchEngine = enhancedSearch.searchEngine;
this.utils = enhancedSearch.utils;
// Track bound event listeners for cleanup
this.boundListeners = new Map();
// Debounced search function
this.debouncedSearch = this.utils.debounce(this.handleSearch.bind(this), 200);
}
/**
* Bind all event listeners
*/
bindEvents() {
this.bindInputEvents();
this.bindModalEvents();
this.bindGlobalEvents();
console.log('✅ Event handlers bound');
}
/**
* Bind input-related events
*/
bindInputEvents() {
const input = this.searchInterface.getInput();
if (!input) return;
// Search input
const inputHandler = (e) => this.debouncedSearch(e);
input.addEventListener('input', inputHandler);
this.boundListeners.set('input', inputHandler);
// Keyboard navigation
const keydownHandler = (e) => this.handleKeyDown(e);
input.addEventListener('keydown', keydownHandler);
this.boundListeners.set('keydown', keydownHandler);
}
/**
* Bind page-specific events (replaces modal events)
*/
bindModalEvents() {
// Check if we're on the search page
if (!this.searchInterface.isSearchPage()) {
return;
}
// Get query parameter if we're on search page
const urlParams = new URLSearchParams(window.location.search);
const query = urlParams.get('q');
if (query) {
// Perform search immediately with the query from URL
setTimeout(() => {
const input = this.searchInterface.getInput();
if (input) {
input.value = query;
this.handleSearch({ target: input });
}
}, 100);
}
}
/**
* Bind global keyboard shortcuts
*/
bindGlobalEvents() {
const globalKeyHandler = (e) => {
// Ctrl+K or Cmd+K to focus search input
if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
e.preventDefault();
// Focus the search input if we're on the search page
const searchInput = this.searchInterface.getInput();
if (searchInput) {
searchInput.focus();
} else {
// If not on search page, redirect to search page
window.location.href = 'search.html';
}
return;
}
};
document.addEventListener('keydown', globalKeyHandler);
this.boundListeners.set('global', globalKeyHandler);
}
/**
* Handle search input
*/
async handleSearch(event) {
const query = event.target.value.trim();
const resultsContainer = this.searchInterface.getResultsContainer();
if (query.length < this.enhancedSearch.options.minQueryLength) {
this.searchInterface.showEmptyState();
this.searchInterface.clearStats();
return;
}
try {
// Show loading state
this.resultRenderer.renderLoading(resultsContainer);
// Perform search
const results = this.searchEngine.search(query, this.enhancedSearch.options.maxResults);
const count = results.length;
// Render results
this.resultRenderer.render(results, query, resultsContainer);
// Update stats
this.searchInterface.updateStats(query, count);
// Emit search event for AI Assistant extension if available
this.emitSearchEvent(query, results, count);
} catch (error) {
console.error('Search error:', error);
this.resultRenderer.renderError(resultsContainer, 'Search temporarily unavailable');
this.searchInterface.clearStats();
}
}
/**
* Handle keyboard navigation
*/
handleKeyDown(event) {
const resultsContainer = this.searchInterface.getResultsContainer();
switch (event.key) {
case 'ArrowDown':
event.preventDefault();
this.resultRenderer.selectNext(resultsContainer);
break;
case 'ArrowUp':
event.preventDefault();
this.resultRenderer.selectPrevious(resultsContainer);
break;
case 'Enter':
event.preventDefault();
this.resultRenderer.activateSelected(resultsContainer);
break;
case 'Escape':
event.preventDefault();
this.enhancedSearch.hide();
break;
}
}
/**
* Emit search event for other extensions
*/
emitSearchEvent(query, results, count) {
if (window.AIAssistant && window.aiAssistantInstance) {
const searchEvent = new CustomEvent('enhanced-search-results', {
detail: { query, results, count }
});
document.dispatchEvent(searchEvent);
}
}
/**
* Handle window resize
*/
handleResize() {
// Adjust modal positioning if needed
const modal = this.searchInterface.getModal();
if (modal && this.searchInterface.isModalVisible()) {
// Could add responsive adjustments here
}
}
/**
* Handle focus management
*/
handleFocus(event) {
// Trap focus within modal when visible
if (this.searchInterface.isModalVisible()) {
const modal = this.searchInterface.getModal();
const focusableElements = modal.querySelectorAll(
'button, input, select, textarea, [tabindex]:not([tabindex="-1"])'
);
const firstFocusable = focusableElements[0];
const lastFocusable = focusableElements[focusableElements.length - 1];
if (event.key === 'Tab') {
if (event.shiftKey) {
// Shift + Tab
if (document.activeElement === firstFocusable) {
event.preventDefault();
lastFocusable.focus();
}
} else {
// Tab
if (document.activeElement === lastFocusable) {
event.preventDefault();
firstFocusable.focus();
}
}
}
}
}
/**
* Bind additional event listeners
*/
bindAdditionalEvents() {
// Window resize
const resizeHandler = this.utils.debounce(() => this.handleResize(), 100);
window.addEventListener('resize', resizeHandler);
this.boundListeners.set('resize', resizeHandler);
// Focus trap
const focusHandler = (e) => this.handleFocus(e);
document.addEventListener('keydown', focusHandler);
this.boundListeners.set('focus', focusHandler);
}
/**
* Unbind all event listeners
*/
unbindEvents() {
// Remove input events
const input = this.searchInterface.getInput();
if (input && this.boundListeners.has('input')) {
input.removeEventListener('input', this.boundListeners.get('input'));
input.removeEventListener('keydown', this.boundListeners.get('keydown'));
}
// Remove modal events
const closeBtn = this.searchInterface.getCloseButton();
if (closeBtn && this.boundListeners.has('close')) {
closeBtn.removeEventListener('click', this.boundListeners.get('close'));
}
const backdrop = this.searchInterface.getBackdrop();
if (backdrop && this.boundListeners.has('backdrop')) {
backdrop.removeEventListener('click', this.boundListeners.get('backdrop'));
}
// Remove global events
if (this.boundListeners.has('global')) {
document.removeEventListener('keydown', this.boundListeners.get('global'));
}
if (this.boundListeners.has('resize')) {
window.removeEventListener('resize', this.boundListeners.get('resize'));
}
if (this.boundListeners.has('focus')) {
document.removeEventListener('keydown', this.boundListeners.get('focus'));
}
// Clear listeners map
this.boundListeners.clear();
console.log('✅ Event handlers unbound');
}
/**
* Get event handler statistics
*/
getStatistics() {
return {
boundListeners: this.boundListeners.size,
modalVisible: this.searchInterface.isModalVisible(),
hasInput: !!this.searchInterface.getInput(),
hasModal: !!this.searchInterface.getModal()
};
}
/**
* Check if events are properly bound
*/
isReady() {
return this.boundListeners.size > 0 &&
this.searchInterface.getInput() !== null &&
this.searchInterface.getModal() !== null;
}
}
// Make EventHandler available globally
window.EventHandler = EventHandler;