// Emoji Library for P4RS3LT0NGV3 // Create namespace for emoji library window.emojiLibrary = {}; // Polyfill for Intl.Segmenter if not available if (!Intl.Segmenter) { console.warn('Intl.Segmenter not available, falling back to basic character splitting'); } // Helper function to properly split text into grapheme clusters (emojis) window.emojiLibrary.splitEmojis = function(text) { if (Intl.Segmenter) { const segmenter = new Intl.Segmenter('en', { granularity: 'grapheme' }); return Array.from(segmenter.segment(text), ({ segment }) => segment); } return Array.from(text); }; // Helper function to properly join emojis window.emojiLibrary.joinEmojis = function(emojis) { return emojis.join(''); }; // Define emoji categories with specific emojis for each category window.emojiLibrary.EMOJIS = { nature: ["๐", "๐", "๐", "๐", "๐", "๐", "๐", "๐", "๐", "๐", "๐ฆ", "๐ฆ", "๐ฏ", "๐ฎ", "๐ท", "๐ธ", "๐ต", "๐", "๐ง", "๐ฆ", "๐ค", "๐ฆ", "๐ฆ ", "๐ฆ", "๐ฆ", "๐บ", "๐", "๐ด", "๐ฆ", "๐", "๐", "๐ฆ", "๐", "๐", "๐", "๐ท๏ธ", "๐ฆ", "๐ฆ", "๐ฆ ", "๐ชฑ"], mystical: ["๐ง", "๐งโโ๏ธ", "๐งโโ๏ธ", "๐ง", "๐งโโ๏ธ", "๐งโโ๏ธ", "๐ง", "๐งโโ๏ธ", "๐งโโ๏ธ", "๐ง", "๐งโโ๏ธ", "๐งโโ๏ธ", "๐น", "๐บ", "๐ป", "๐ฝ", "๐พ", "๐ฒ", "๐ฎ", "๐", "๐", "๐ฆ", "โ๏ธ", "๐ฏ", "๐ฑ", "โ๏ธ", "โจ", "๐ ", "๐", "๐", "๐ฉธ"], faces_people: ["๐", "๐", "๐", "๐คฃ", "๐", "๐", "๐ ", "๐", "๐", "๐", "๐", "๐", "๐", "๐", "๐ฅฐ", "๐", "๐", "๐", "๐", "๐ค", "๐คฉ", "๐ค", "๐คจ", "๐", "๐", "๐ถ", "๐", "๐", "๐ฃ", "๐ฅ", "๐ฎ", "๐ค", "๐ฏ", "๐ช", "๐ซ", "๐ด", "๐", "๐", "๐", "๐", "๐คค", "๐", "๐", "๐", "๐", "๐", "๐ค", "๐ฒ", "๐", "๐", "๐", "๐", "๐ค", "๐ข", "๐ญ", "๐ง", "๐จ", "๐ฉ", "๐คฏ", "๐ฑ", "๐ณ", "๐ฅต", "๐ฅถ", "๐ก", "๐ ", "๐คฌ", "๐ท", "๐ค", "๐ค", "๐คข", "๐คฎ", "๐คง", "๐", "๐ฅณ", "๐ฅด", "๐ฅบ", "๐ง", "๐ฅฑ", "๐ง "], gestures: ["๐", "๐", "๐", "โ๏ธ", "๐ค", "๐ค", "๐ค", "๐ค", "๐", "๐", "๐", "๐", "๐", "โ๏ธ", "โ", "๐ค", "๐๏ธ", "๐", "๐", "๐ค", "๐", "๐", "๐", "๐ค", "๐"], animals_nature: ["๐", "๐ฆ", "๐ฆ", "๐ฏ", "๐ฎ", "๐ท", "๐ธ", "๐ต", "๐", "๐ง", "๐ฆ", "๐ค", "๐ฆ", "๐ฆ ", "๐ฆ", "๐ฆ", "๐บ", "๐", "๐ด", "๐", "๐", "๐ฆ", "๐", "๐", "๐", "๐ท๏ธ", "๐ฆ", "๐", "๐ฆจ", "๐ฆฉ", "๐ฆซ", "๐ฆฌ", "๐ปโโ๏ธ", "๐ผ", "๐จ", "๐", "๐ถ", "๐ฉ", "๐", "๐ฑ"], activities_sports: ["โฝ", "๐", "๐", "๐", "๐", "๐พ", "๐ณ", "๐", "๐", "๐", "๐ธ", "๐ฅ", "๐ฅ", "๐ฅ ", "๐คพ", "๐ฟ", "๐", "๐", "๐", "๐๏ธ", "๐คผ", "๐คธ", "๐คบ", "๐คฝ", "๐คน", "๐ฏ", "๐ฑ", "๐ฝ", "๐ด", "๐ต"], technology_objects: ["๐ป", "โจ๏ธ", "๐ฅ๏ธ", "๐ฑ๏ธ", "๐จ๏ธ", "๐ฑ", "โ๏ธ", "๐", "๐", "๐ ", "๐บ", "๐ป", "๐๏ธ", "๐๏ธ", "๐๏ธ", "๐งญ", "๐ก", "๐", "๐", "๐ก", "๐ข๏ธ", "๐ธ", "๐ต", "๐ณ", "๐", "๐", "๐"], mystical_fantasy: ["๐ง", "๐ง", "๐ง", "๐ง", "๐น", "๐บ", "๐ป", "๐ฝ", "๐พ", "๐ฎ", "๐ช", "๐", "๐ฒ", "๐ฆ"], nature_weather: ["๐", "๐", "๐", "โญ", "๐", "โก", "โ๏ธ", "๐ฅ", "๐ง", "๐", "๐ช๏ธ", "๐"], symbols: ["โค๏ธ", "๐", "๐", "๐", "๐", "๐", "๐", "๐", "๐", "๐", "๐", "๐", "๐", "๐", "๐ข", "๐ฃ", "๐ฅ", "๐ฆ", "๐จ", "๐ฉ", "๐ซ", "๐ฌ", "๐ ", "๐ฎ"], flags: ["๐", "๐ฉ", "๐", "๐ด", "๐ณ๏ธ", "๐ณ๏ธโ๐", "๐ณ๏ธโโง๏ธ", "๐ดโโ ๏ธ", "๐บ๐ธ", "๐จ๐ฆ", "๐ฌ๐ง", "๐ฉ๐ช", "๐ซ๐ท", "๐ฎ๐น", "๐ฏ๐ต", "๐ฐ๐ท", "๐ท๐บ", "๐จ๐ณ", "๐ฎ๐ณ", "๐ง๐ท", "๐ฆ๐บ", "๐ช๐ธ", "๐ณ๐ฑ", "๐ธ๐ช"] }; // Define standard emoji categories window.emojiLibrary.CATEGORIES = [ { id: 'all', name: 'All Emojis', icon: '๐' }, { id: 'faces_people', name: 'Faces & People', icon: '๐' }, { id: 'gestures', name: 'Gestures', icon: '๐' }, { id: 'animals_nature', name: 'Animals & Nature', icon: '๐ฆ' }, { id: 'activities_sports', name: 'Activities & Sports', icon: 'โฝ' }, { id: 'technology_objects', name: 'Tech & Objects', icon: '๐ป' }, { id: 'mystical_fantasy', name: 'Mystical & Fantasy', icon: '๐ง' }, { id: 'nature_weather', name: 'Nature & Weather', icon: '๐' }, { id: 'symbols', name: 'Symbols', icon: 'โค๏ธ' }, { id: 'flags', name: 'Flags', icon: '๐' } ]; // Auto-generate EMOJI_LIST from the categorized EMOJIS object // This ensures a single source of truth for all emojis window.emojiLibrary.EMOJI_LIST = (() => { const allEmojis = []; // Combine all emojis from all categories Object.values(window.emojiLibrary.EMOJIS).forEach(categoryEmojis => { allEmojis.push(...categoryEmojis); }); // Remove duplicates using Set and return as array return Array.from(new Set(allEmojis)); })(); // Function to render emoji grid with categories window.emojiLibrary.renderEmojiGrid = function(containerId, onEmojiSelect, filteredList) { console.log('Rendering emoji grid to:', containerId); // Get container by ID const container = document.getElementById(containerId); if (!container) { console.error('Container not found:', containerId); return; } // Clear container container.innerHTML = ''; // Add header with instruction message const emojiHeader = document.createElement('div'); emojiHeader.className = 'emoji-header'; emojiHeader.innerHTML = '
Click any emoji to copy your hidden message
'; container.appendChild(emojiHeader); // Create category tabs const categoryTabs = document.createElement('div'); categoryTabs.className = 'emoji-category-tabs'; // Add category tabs window.emojiLibrary.CATEGORIES.forEach(category => { const tab = document.createElement('button'); tab.className = 'emoji-category-tab'; if (category.id === 'all') { tab.classList.add('active'); } tab.setAttribute('data-category', category.id); tab.innerHTML = `${category.icon} ${category.name}`; categoryTabs.appendChild(tab); }); container.appendChild(categoryTabs); // Create emoji grid with enforced styling const gridContainer = document.createElement('div'); gridContainer.className = 'emoji-grid'; // Get the active category let activeCategory = 'all'; const activeCategoryTab = container.querySelector('.emoji-category-tab.active'); if (activeCategoryTab) { activeCategory = activeCategoryTab.getAttribute('data-category'); } // Determine which emojis to show based on category and filter let emojisToShow = []; if (filteredList && filteredList.length > 0) { // If we have a filtered list (from search), use that emojisToShow = filteredList; } else if (activeCategory === 'all') { // For 'all' category, combine all emojis from the categories and deduplicate Object.values(window.emojiLibrary.EMOJIS).forEach(categoryEmojis => { emojisToShow = [...emojisToShow, ...categoryEmojis]; }); // Remove duplicates using Set emojisToShow = Array.from(new Set(emojisToShow)); } else if (window.emojiLibrary.EMOJIS[activeCategory]) { // For specific category, use emojis from that category emojisToShow = window.emojiLibrary.EMOJIS[activeCategory]; } console.log(`Adding ${emojisToShow.length} emojis to grid for category: ${activeCategory}`); // Add emojis to grid with enforced styling emojisToShow.forEach(emoji => { const emojiButton = document.createElement('button'); emojiButton.className = 'emoji-button'; emojiButton.textContent = emoji; // Use textContent for better emoji handling emojiButton.title = 'Click to encode with this emoji'; emojiButton.addEventListener('click', () => { if (typeof onEmojiSelect === 'function') { onEmojiSelect(emoji); // Add visual feedback when clicked emojiButton.style.backgroundColor = '#e6f7ff'; setTimeout(() => { emojiButton.style.backgroundColor = ''; }, 300); } }); gridContainer.appendChild(emojiButton); }); container.appendChild(gridContainer); console.log('Emoji grid rendering complete'); // Add event listeners to category tabs const categoryTabButtons = container.querySelectorAll('.emoji-category-tab'); categoryTabButtons.forEach(tab => { tab.addEventListener('click', () => { // Update active tab categoryTabButtons.forEach(t => t.classList.remove('active')); tab.classList.add('active'); // Re-render the emoji grid with the selected category const selectedCategory = tab.getAttribute('data-category'); console.log('Category selected:', selectedCategory); // Determine which emojis to show let emojisToShow = []; if (selectedCategory === 'all') { // For 'all' category, combine all emojis from the categories and deduplicate Object.values(window.emojiLibrary.EMOJIS).forEach(categoryEmojis => { emojisToShow = [...emojisToShow, ...categoryEmojis]; }); // Remove duplicates using Set emojisToShow = Array.from(new Set(emojisToShow)); } else if (window.emojiLibrary.EMOJIS[selectedCategory]) { // For specific category, use emojis from that category emojisToShow = window.emojiLibrary.EMOJIS[selectedCategory]; } console.log(`Updating grid with ${emojisToShow.length} emojis for category: ${selectedCategory}`); // Clear only the grid and rebuild it gridContainer.innerHTML = ''; // Add emojis to grid emojisToShow.forEach(emoji => { const emojiButton = document.createElement('button'); emojiButton.className = 'emoji-button'; emojiButton.textContent = emoji; emojiButton.title = 'Click to encode with this emoji'; emojiButton.addEventListener('click', () => { if (typeof onEmojiSelect === 'function') { onEmojiSelect(emoji); // Add visual feedback when clicked emojiButton.style.backgroundColor = '#e6f7ff'; setTimeout(() => { emojiButton.style.backgroundColor = ''; }, 300); } }); gridContainer.appendChild(emojiButton); }); // Update the count display const countDisplay = container.querySelector('.emoji-count'); if (countDisplay) { countDisplay.textContent = `${emojisToShow.length} emojis available`; } }); }); // Debug info - add count display const countDisplay = document.createElement('div'); countDisplay.className = 'emoji-count'; countDisplay.textContent = `${emojisToShow.length} emojis available`; container.appendChild(countDisplay); };