Alternative Investments Forum | Preqin Presentation Slides
Thank you for watching ourGlobal Alternatives Outlookpresentation during CFA Quebec's Alternative Investments Forum.
Access the presentation slides by filling out the form today!
Access the slides
'); // Wrap in a container
$inputField.after('' +
decodeURI('%3Csvg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="none" viewBox="0 0 20 20" class="search-icon">'))
// Find the container (assuming it's the direct parent with the class)
const $container = $inputField.closest('#formField--companyName');
// Demo display element
const $idDisplaySpan = $('#selected-id-display span');
let $resultsContainer = null;
let $resultsList = null;
let valueWasSelected = false; // Flag to track if the current value is from selection
let responseData = null; // Store the response data
// --- Helper Functions ---
function ensureResultsListExists() {
if(!$resultsContainer || $resultsContainer.length === 0) {
$resultsContainer = $container.find('.autocomplete-results');
if($resultsContainer.length === 0) {
$resultsContainer = $('
');
$resultsContainer.show();
}
currentRequest = null;
}
});
}
// Function to match company type from API to dropdown option
function setCompanyTypeFromAPI(apiCompanyType) {
if (!apiCompanyType || !$companyTypeField.length) {
return;
}
// First try exact match (most API types match dropdown options exactly)
let matchedOption = $companyTypeField.find('option').filter(function() {
return $(this).text().trim() === apiCompanyType;
});
// If no exact match, check for specific mappings for the differences
if (matchedOption.length === 0) {
const specificMappings = {
'Non-sponsor backed': 'Other', // API type not in dropdown
'Professional Services': 'Other', // API type not in dropdown
'Infrastructure - Other': 'Infrastructure - Other', // Check if this exact match works
'Natural Resources - Other': 'Natural Resources - Other', // Check if this exact match works
'Real Estate - Other': 'Real Estate - Other' // Check if this exact match works
};
if (specificMappings[apiCompanyType]) {
const mappedType = specificMappings[apiCompanyType];
matchedOption = $companyTypeField.find('option').filter(function() {
return $(this).text().trim() === mappedType;
});
}
}
// Set the matched option or clear if no match found
if (matchedOption.length > 0) {
$companyTypeField.val(matchedOption.first().val());
} else {
$companyTypeField.val('');
console.log(`No matching dropdown option found for API company type: "${apiCompanyType}"`);
}
}
// Function to handle selection (from click or Enter key)
function handleSelection($selectedItem) {
if(!$selectedItem || $selectedItem.length === 0 || $selectedItem.hasClass('autocomplete-message')) {
return; // Do nothing if no valid item or it's a message
}
const selectedText = $selectedItem.text();
const selectedId = $selectedItem.data('company-id'); // Retrieve stored ID
const selectedCompanyData = $selectedItem.data('company-data'); // Retrieve complete company data
if(selectedId) {
$inputField.val(selectedText); // Update visible input
$hiddenFirmIdInput.val(selectedId); // Update hidden input
// Update company type field if type exists in company data
if(selectedCompanyData && selectedCompanyData.typeRaw) {
setCompanyTypeFromAPI(selectedCompanyData.typeRaw);
} else {
$companyTypeField.val(''); // Clear if no type available
}
valueWasSelected = true; // Mark that selection occurred
$idDisplaySpan?.text(selectedId); // Update demo display
// console.log("Selected:", selectedText, "ID:", selectedId);
hideResults();
$inputField.trigger('change'); // Optional: Trigger change event for other listeners
}
else {
console.warn('Selected item missing company ID data:', $selectedItem);
hideResults(); // Hide results even if ID is missing
}
}
// --- Event Handlers ---
$inputField.on('input', function() {
const currentInputValue = $(this).val();
const searchTerm = currentInputValue.trim();
// CRITICAL: If the user types anything, clear the previously selected ID
// Only clear if the flag 'valueWasSelected' is true, meaning the last change
// wasn't already a manual input modification.
if(valueWasSelected) {
clearSelectedId();
}
// We always set valueWasSelected to false here, because *any* input
// event means the current value is potentially manual, until a selection happens again.
valueWasSelected = false;
clearTimeout(debounceTimer);
if(currentRequest) {
currentRequest.abort();
currentRequest = null;
}
if(searchTerm.length === 0) {
hideResults();
clearSelectedId(); // Also clear ID if input is emptied
return;
}
debounceTimer = setTimeout(() => {
performQuickSearch(searchTerm);
}, debounceDelay);
});
// Handle click on a suggestion item (delegated to container)
$container.on('click', '.autocomplete-results li', function(e) {
handleSelection($(this)); // Pass the clicked LI to the handler
});
// Keyboard navigation and selection/dismissal
$inputField.on('keydown', function(e) {
// Handle arrow-down when results are not visible - trigger search
if (e.key === 'ArrowDown' && (!$resultsContainer || !$resultsContainer.is(':visible'))) {
e.preventDefault();
const searchTerm = $(this).val().trim();
// If there's text in the input, trigger a search
if (searchTerm.length > 0) {
performQuickSearch(searchTerm, { autoSelectFirst: true });
}
return;
}
// Handle TAB key when results are not visible - allow normal focus movement
if (e.key === 'Tab' && (!$resultsContainer || !$resultsContainer.is(':visible'))) {
// Don't prevent default - allow normal tab navigation to next field
return;
}
if(!$resultsContainer || !$resultsContainer.is(':visible')) {
// If results not visible, ESC shouldn't do anything special here
return;
}
const $items = $resultsList.find('li:not(.autocomplete-message)');
if(!$items.length && e.key !== 'Escape') {
return;
} // No items to navigate (but allow ESC)
let $currentActive = $items.filter('.active');
let currentIndex = $items.index($currentActive);
switch(e.key) {
case 'ArrowDown':
e.preventDefault();
currentIndex = ($currentActive.length === 0) ? 0: (currentIndex + 1) % $items.length;
$items.removeClass('active');
$items.eq(currentIndex).addClass('active');
break;
case 'ArrowUp':
e.preventDefault();
currentIndex = ($currentActive.length === 0) ? $items.length - 1: (currentIndex - 1 + $items.length) % $items.length;
$items.removeClass('active');
$items.eq(currentIndex).addClass('active');
break;
case 'Enter':
e.preventDefault(); // IMPORTANT: Prevent form submission
if($currentActive.length) {
handleSelection($currentActive); // Use the selection handler
}
else {
// Optional: Hide results if Enter is pressed without a selection
hideResults();
}
break;
case 'Escape': // ESC key dismisses
e.preventDefault();
hideResults();
break;
case 'Tab': // Tab key dismisses and allows normal focus movement
hideResults();
// Don't prevent default - allow normal tab navigation to next field
break;
}
});
// Hide results when clicking outside
$(document).on('click', function(e) {
// Check if the click is outside the input AND outside the results container
if(!$inputField.is(e.target) && !$container.is(e.target) && $container.has(e.target).length === 0) {
if($resultsContainer && $resultsContainer.is(':visible')) {
hideResults();
}
}
});
// Initial state check (optional)
if($hiddenFirmIdInput.val()) {
$idDisplaySpan?.text($hiddenFirmIdInput.val());
valueWasSelected = true; // Assume pre-filled value was selected
}
});