document.addEventListener('DOMContentLoaded', function() { // Define product type keywords and their standardized forms const productTypes = { 'mug': ['mug', 'cup'], 'sticker': ['sticker'], 'shirt': ['shirt', 't-shirt', 'tee'], 'hoodie': ['hoodie', 'jacket'], 'blanket': ['blanket'], // Add more product types and synonyms as needed }; const MAX_RESULTS = 20; // Limit the number of search results displayed let searchTimeout; // Timeout for delayed search // Function to convert plurals to singular function normalizeKeyword(keyword) { if (keyword.endsWith('s')) { return keyword.slice(0, -1); } return keyword; } // Function to standardize synonyms to a common keyword function standardizeKeyword(keyword) { const normalizedKeyword = normalizeKeyword(keyword); for (let standard in productTypes) { if (productTypes[standard].includes(normalizedKeyword)) { return standard; } } return normalizedKeyword; } // Function to extract keywords from the description function extractKeywords(description) { const keywordPrefix = "keywords: "; const keywordStart = description.toLowerCase().indexOf(keywordPrefix); if (keywordStart !== -1) { return description.slice(keywordStart + keywordPrefix.length).trim().toLowerCase(); } return ''; } // Fetch and parse the RSS feed fetch('https://merch.ookamikun.tv/.well-known/merchant-center/rss.xml') .then(response => response.text()) .then(data => { const parser = new DOMParser(); const xml = parser.parseFromString(data, "application/xml"); const items = xml.getElementsByTagName("item"); const products = {}; for (let i = 0; i < items.length; i++) { const item = items[i]; const idElement = item.getElementsByTagName("g:id")[0]; const titleElement = item.getElementsByTagName("g:title")[0]; const descriptionElement = item.getElementsByTagName("g:description")[0]; const linkElement = item.getElementsByTagName("g:link")[0]; const imageElement = item.getElementsByTagName("g:image_link")[0]; const priceElement = item.getElementsByTagName("g:price")[0]; const sizeElement = item.getElementsByTagName("g:size")[0]; const colorElement = item.getElementsByTagName("g:color")[0]; if (idElement && titleElement && descriptionElement && linkElement && imageElement && priceElement) { const id = idElement.textContent || ''; const title = titleElement.textContent.trim() || 'Untitled Product'; const description = descriptionElement.textContent.trim() || 'No description available.'; const link = linkElement.textContent || '#'; const image = imageElement.textContent || 'default-image-url.jpg'; const price = parseFloat(priceElement.textContent.replace(/[^\d.]/g, '')) || 0; const size = sizeElement ? sizeElement.textContent : 'One Size'; const color = colorElement ? colorElement.textContent.trim().toLowerCase() : ''; const keywords = extractKeywords(description); // Extracting keywords using the new function if (!products[title]) { products[title] = { title, description, link, image, prices: [], sizes: new Set(), color, keywords, searchText: title.toLowerCase() + ' ' + description.toLowerCase() + ' ' + color + ' ' + keywords, }; } products[title].prices.push(price); if (size) products[title].sizes.add(size); } } function calculateRelevanceScore(product, keywords, query) { let score = 0; let matchesAnyKeyword = false; // Standardize and normalize keywords const standardizedKeywords = keywords.map(standardizeKeyword); // Keywords match - highest weight (75%) const keywordMatches = standardizedKeywords.filter(keyword => product.keywords.includes(keyword)); if (keywordMatches.length > 0) { score += keywordMatches.length * 75; // 75% weight for keywords matchesAnyKeyword = true; } // Exact match - significant weight if (standardizedKeywords.includes(standardizeKeyword(product.title.toLowerCase())) || standardizedKeywords.includes(standardizeKeyword(product.description.toLowerCase()))) { score += 30; matchesAnyKeyword = true; } // Product type match - significant weight let productTypeMatched = false; for (let type in productTypes) { const standardType = standardizeKeyword(type); if (standardizedKeywords.includes(standardType)) { score += 20; matchesAnyKeyword = true; productTypeMatched = true; break; } } if (!productTypeMatched && standardizedKeywords.some(keyword => productTypes.hasOwnProperty(keyword))) { score -= 20; // Stronger penalty for missing product type match if the user provided a product type keyword } // Color match - medium weight const colorMatches = standardizedKeywords.filter(keyword => product.color.includes(keyword)); if (colorMatches.length > 0) { score += colorMatches.length * 10; matchesAnyKeyword = true; } // Title match - lower weight const titleMatches = standardizedKeywords.filter(keyword => product.title.toLowerCase().includes(keyword)); if (titleMatches.length > 0) { score += titleMatches.length * 5; matchesAnyKeyword = true; } // Description match - lower weight const descriptionMatches = standardizedKeywords.filter(keyword => product.description.toLowerCase().includes(keyword)); if (descriptionMatches.length > 0) { score += descriptionMatches.length * 3; matchesAnyKeyword = true; } // Ensure the product is only considered if it contains at least one of the keywords if (!matchesAnyKeyword) { score = 0; } return score; } const searchInput = document.querySelector('#search-input'); const resultsContainer = document.querySelector('#search-results'); searchInput.addEventListener('input', function() { clearTimeout(searchTimeout); searchTimeout = setTimeout(() => { const query = this.value.toLowerCase().trim(); const keywords = query.split(/\s+/); const results = []; for (let key in products) { const product = products[key]; const relevanceScore = calculateRelevanceScore(product, keywords, query); if (relevanceScore > 0) { results.push({ product, relevanceScore }); } } results.sort((a, b) => b.relevanceScore - a.relevanceScore); // Render the top X results resultsContainer.innerHTML = ''; results.slice(0, MAX_RESULTS).forEach(({ product }) => { const minPrice = Math.min(...product.prices).toFixed(2); const maxPrice = Math.max(...product.prices).toFixed(2); const priceRange = minPrice === maxPrice ? `$${minPrice}` : `$${minPrice} - $${maxPrice}`; const productElement = document.createElement('div'); productElement.className = 'search-result-item'; productElement.innerHTML = ` ${product.title}

${product.title}

${product.description.substring(0, 100)}...

Sizes: ${[...product.sizes].join(', ')} Price: ${priceRange}
`; resultsContainer.appendChild(productElement); }); }, 500); // 0.5-second delay before performing the search }); }) .catch(error => console.error('Error fetching or parsing the RSS feed:', error)); const searchIcon = document.querySelector('.header__icon--search'); const searchBar = document.querySelector('#search-bar'); searchIcon.addEventListener('click', function() { searchBar.classList.toggle('active'); searchBar.querySelector('input').focus(); }); });