Enhanced universal decoder to support braille, base64, and other encodings; Fixed emoji library to always show all emojis when typing

This commit is contained in:
EP
2025-03-10 13:58:25 -04:00
parent 633b93c092
commit 6fe724b312
2 changed files with 710 additions and 115 deletions
+536 -92
View File
@@ -1,5 +1,5 @@
// Initialize Vue app
new Vue({
window.app = new Vue({
el: '#app',
data: {
// Theme
@@ -24,13 +24,22 @@ new Vue({
decodeInput: '',
decodedMessage: '',
selectedCarrier: null,
// Universal Decoder - works on both tabs
universalDecodeInput: '',
universalDecodeResult: null,
isPasteOperation: false, // Flag to track paste operations
activeSteg: null,
carriers: window.steganography.carriers,
showDecoder: true,
// Emoji Library
emojiSearch: '',
filteredEmojis: [...window.emojiLibrary.EMOJI_LIST],
selectedEmoji: null
selectedEmoji: null,
// History of copied content
copyHistory: [],
maxHistoryItems: 10,
showCopyHistory: false
},
methods: {
// Theme Toggle
@@ -38,19 +47,49 @@ new Vue({
this.isDarkTheme = !this.isDarkTheme;
document.body.classList.toggle('light-theme');
},
// Copy History Toggle
toggleCopyHistory() {
this.showCopyHistory = !this.showCopyHistory;
console.log('Copy history toggled:', this.showCopyHistory);
// If showing history panel, focus the first copy-again button if available
if (this.showCopyHistory && this.copyHistory.length > 0) {
this.$nextTick(() => {
const firstCopyButton = document.querySelector('.copy-again-button');
if (firstCopyButton) {
firstCopyButton.focus();
}
});
}
},
// Transform Methods
applyTransform(transform) {
if (this.transformInput) {
this.activeTransform = transform;
this.transformOutput = transform.func(this.transformInput);
this.copyToClipboard(this.transformOutput);
// Force copy and log to history
this.forceCopyToClipboard(this.transformOutput);
// Add to copy history
this.addToCopyHistory(`Transform: ${transform.name}`, this.transformOutput);
// Enhanced notification for transform and copy
this.showNotification(`<i class="fas fa-check"></i> ${transform.name} applied and copied!`, 'success');
}
},
autoTransform() {
if (this.transformInput && this.activeTransform) {
// Only proceed if we're in the transforms tab and have an active transform
if (this.transformInput && this.activeTransform && this.activeTab === 'transforms') {
this.transformOutput = this.activeTransform.func(this.transformInput);
this.copyToClipboard(this.transformOutput);
// Use forceCopyToClipboard for auto-copy
this.forceCopyToClipboard(this.transformOutput);
// Add to copy history
this.addToCopyHistory(`Transform: ${this.activeTransform.name}`, this.transformOutput);
}
},
@@ -67,34 +106,50 @@ new Vue({
}
},
setStegMode(mode) {
// Toggle mode selection if clicking the same one again
if (this.activeSteg === mode) {
this.activeSteg = null;
this.encodedMessage = '';
} else {
// For invisible text, make it a direct action (not a toggle)
if (mode === 'invisible') {
// Set the mode temporarily to generate the encoded message
this.activeSteg = mode;
// When switching to invisible mode, clear the carrier selection
if (mode === 'invisible') {
this.selectedCarrier = null;
}
// Clear any carrier selection
this.selectedCarrier = null;
// Generate the encoded message
this.autoEncode();
// Auto-copy the encoded message
if (this.encodedMessage) {
this.$nextTick(() => {
this.forceCopyToClipboard(this.encodedMessage);
this.showNotification('<i class="fas fa-check"></i> Invisible text created and copied!', 'success');
this.addToCopyHistory('Invisible Text', this.encodedMessage);
});
}
} else {
// For other modes (like emoji), keep the toggle behavior
if (this.activeSteg === mode) {
this.activeSteg = null;
this.encodedMessage = '';
} else {
this.activeSteg = mode;
this.autoEncode();
}
}
},
autoEncode() {
if (!this.emojiMessage) {
// Only proceed if we're in the steganography tab
if (!this.emojiMessage || this.activeTab !== 'steganography') {
this.encodedMessage = '';
return;
}
if (this.activeSteg === 'invisible') {
this.encodedMessage = window.steganography.encodeInvisible(this.emojiMessage);
// Don't auto-copy to avoid clipboard permission errors
// Auto-copy will be handled in setStegMode method
} else if (this.selectedCarrier) {
this.encodedMessage = window.steganography.encodeEmoji(
this.selectedCarrier.emoji,
this.emojiMessage
);
// Don't auto-copy to avoid clipboard permission errors
// Auto-copy for emoji carrier is handled in selectEmoji method
}
},
autoDecode() {
@@ -108,7 +163,21 @@ new Vue({
if (result) {
this.decodedMessage = `Decoded (${result.method}): ${result.text}`;
// Don't auto-copy to avoid clipboard permission errors
// Auto-copy decoded message to clipboard
this.$nextTick(() => {
// Only copy the actual decoded text, not the formatted message
const decodedText = result.text;
if (decodedText) {
// Force clipboard copy regardless of event source
this.forceCopyToClipboard(decodedText);
this.showNotification(`<i class="fas fa-check"></i> Decoded message copied!`, 'success');
// Add to copy history
this.addToCopyHistory(`Decoded (${result.method})`, decodedText);
}
});
} else {
this.decodedMessage = 'No encoded message detected';
}
@@ -117,27 +186,47 @@ new Vue({
return '[invisible]';
},
// Add to copy history functionality
addToCopyHistory(source, content) {
// Create history item with timestamp
const historyItem = {
source: source,
content: content,
timestamp: new Date().toLocaleTimeString(),
date: new Date().toLocaleDateString()
};
// Add to beginning of array (most recent first)
this.copyHistory.unshift(historyItem);
// Limit history to maxHistoryItems
if (this.copyHistory.length > this.maxHistoryItems) {
this.copyHistory.pop();
}
// Log history item for debugging
console.log('Added to copy history:', historyItem);
},
// Utility Methods
async copyToClipboard(text) {
if (!text) return;
// Don't auto-copy in preview/iframe environments to avoid permission errors
// Only copy when explicitly triggered by a button click
const isExplicitUserAction = event && event.isTrusted;
// Only try to copy if we're not in a restricted environment or it's a direct user action
if (isExplicitUserAction) {
try {
await navigator.clipboard.writeText(text);
// Show a success notification
this.showNotification('<i class="fas fa-check"></i> Copied!', 'success');
} catch (err) {
console.warn('Clipboard access not available:', err);
// Try fallback method for copying (textarea method)
this.fallbackCopy(text);
}
// Always try to copy, regardless of event source
try {
await navigator.clipboard.writeText(text);
// Show a success notification
this.showNotification('<i class="fas fa-check"></i> Copied!', 'success');
// Add to history - determine source from active tab or context
const source = this.activeTab === 'transforms' ? 'Transform' : 'Steganography';
this.addToCopyHistory(source, text);
} catch (err) {
console.warn('Clipboard access not available:', err);
// Try fallback method for copying (textarea method)
this.fallbackCopy(text);
}
},
@@ -156,6 +245,23 @@ new Vue({
// Show appropriate notification
if (successful) {
this.showNotification('<i class="fas fa-check"></i> Copied!', 'success');
// Add to history when successful
// Try to determine a more specific source based on the context
let source = this.activeTab === 'transforms' ? 'Transform' : 'Steganography';
// Add more context if available
if (this.activeTab === 'transforms' && this.activeTransform) {
source = `Transform: ${this.activeTransform.name}`;
} else if (this.activeTab === 'steganography') {
if (this.activeSteg === 'invisible') {
source = 'Invisible Text';
} else if (this.selectedEmoji) {
source = `Emoji: ${this.selectedEmoji}`;
}
}
this.addToCopyHistory(source, text);
} else {
this.showNotification('<i class="fas fa-exclamation-triangle"></i> Copy not supported', 'error');
}
@@ -168,6 +274,73 @@ new Vue({
}
},
// Force copy to clipboard regardless of event context
forceCopyToClipboard(text) {
if (!text) return;
// Skip notifications if this was triggered by a paste operation
if (this.isPasteOperation) {
console.log('Skipping clipboard notification for paste operation');
return;
}
console.log('Force copying to clipboard:', text);
try {
// Try to use the Clipboard API first
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(text)
.then(() => {
console.log('Force copy successful using Clipboard API');
// Show clear notification on success
this.showCopiedPopup();
})
.catch(err => {
console.warn('Force Clipboard API failed:', err);
this.forceFallbackCopy(text);
// Still show notification, as fallback might work
this.showCopiedPopup();
});
} else {
// Fall back to execCommand method
this.forceFallbackCopy(text);
// Show notification for fallback method too
this.showCopiedPopup();
}
} catch (error) {
console.error('Force copy failed:', error);
// Try one last fallback and still show notification
this.forceFallbackCopy(text);
this.showCopiedPopup();
}
},
// Fallback copy method that doesn't rely on user-initiated events
forceFallbackCopy(text) {
try {
const textarea = document.createElement('textarea');
textarea.value = text;
textarea.style.position = 'fixed';
textarea.style.left = '-9999px';
textarea.style.top = '0';
document.body.appendChild(textarea);
textarea.focus();
textarea.select();
try {
document.execCommand('copy');
console.log('Force fallback copy successful');
} catch (err) {
console.error('Force fallback copy command failed:', err);
}
document.body.removeChild(textarea);
} catch (err) {
console.error('Force fallback copy method failed:', err);
}
},
// Notification system
showNotification(message, type = 'success') {
// Create notification element
const notification = document.createElement('div');
@@ -186,6 +359,76 @@ new Vue({
}, 1000);
},
// Special prominent copy notification
showCopiedPopup() {
// Create a more visible popup just for copy operations
const popup = document.createElement('div');
popup.className = 'copy-popup';
popup.innerHTML = '<i class="fas fa-clipboard-check"></i> Copied to clipboard!';
// Add to body
document.body.appendChild(popup);
// Force it to be visible and centered
popup.style.position = 'fixed';
popup.style.top = '50%';
popup.style.left = '50%';
popup.style.transform = 'translate(-50%, -50%)';
popup.style.backgroundColor = 'rgba(0, 0, 0, 0.8)';
popup.style.color = 'white';
popup.style.padding = '15px 25px';
popup.style.borderRadius = '5px';
popup.style.fontSize = '18px';
popup.style.fontWeight = 'bold';
popup.style.zIndex = '10000';
popup.style.boxShadow = '0 0 10px rgba(0, 0, 0, 0.5)';
popup.style.textAlign = 'center';
// Add fade-in animation
popup.style.opacity = '0';
popup.style.transition = 'opacity 0.3s ease-in-out';
// Force reflow to make animation work
void popup.offsetWidth;
// Fade in
popup.style.opacity = '1';
// Remove after a short delay
setTimeout(() => {
popup.style.opacity = '0';
setTimeout(() => {
if (popup.parentNode) {
document.body.removeChild(popup);
}
}, 300);
}, 1500);
},
// Run the universal decoder when input changes
runUniversalDecode() {
console.log('Running universal decoder with input:', this.universalDecodeInput);
// Clear result if input is empty
if (!this.universalDecodeInput) {
this.universalDecodeResult = null;
return;
}
// Try to decode using all available methods
const result = this.universalDecode(this.universalDecodeInput);
// Update the result
this.universalDecodeResult = result;
// Log the result
if (result) {
console.log(`Universal decoder found a match: ${result.method}`);
} else {
console.log('Universal decoder could not decode the input');
}
},
// Universal Decoder - tries all decoding methods
universalDecode(input) {
if (!input) return '';
@@ -196,9 +439,13 @@ new Vue({
// - Check for emoji steganography first
// The emoji encoding uses variation selectors which are hard to see
if (/[\u{1F300}-\u{1F6FF}\u{2600}-\u{26FF}]/u.test(input)) {
console.log('Detected emoji, attempting to decode...');
const decoded = window.steganography.decodeEmoji(input);
if (decoded) {
console.log('Successfully decoded emoji:', decoded);
return { text: decoded, method: 'Emoji Steganography' };
} else {
console.log('Emoji detected but no hidden message found');
}
}
@@ -251,15 +498,127 @@ new Vue({
console.error('Morse decode error:', e);
}
}
// - Braille
const braillePattern = /[-⣿]/;
if (braillePattern.test(input)) {
try {
// Create a reverse mapping for braille
const brailleReverseMap = {};
if (window.transforms.braille && window.transforms.braille.map) {
for (const [key, value] of Object.entries(window.transforms.braille.map)) {
brailleReverseMap[value] = key;
}
// Decode the braille
let result = '';
for (const char of input) {
result += brailleReverseMap[char] || char;
}
if (result !== input && /[a-zA-Z0-9]/.test(result)) {
return { text: result, method: 'Braille' };
}
}
} catch (e) {
console.error('Braille decode error:', e);
}
}
// - Try reverse each transform
// - Base64
if (/^[A-Za-z0-9+/=]+$/.test(input.trim())) {
try {
// Attempt to decode as base64
const result = atob(input.trim());
// Check if result is readable text
if (/[\x20-\x7E]{3,}/.test(result)) { // At least 3 readable ASCII chars
return { text: result, method: 'Base64' };
}
} catch (e) {
// Not valid base64, continue to next decoder
console.error('Base64 decode error:', e);
}
}
// - Upside Down text
if (window.transforms.upside_down && window.transforms.upside_down.reverse) {
try {
const result = window.transforms.upside_down.reverse(input);
// Check if the result is significantly different
if (result !== input && result.length > 3 && /[a-zA-Z0-9\s]{3,}/.test(result)) {
return { text: result, method: 'Upside Down' };
}
} catch (e) {
console.error('Upside Down decode error:', e);
}
}
// - Small Caps (create reverse mapping since there's no built-in decoder)
if (window.transforms.small_caps && window.transforms.small_caps.map) {
try {
// Create reverse mapping
const smallCapsReverseMap = {};
for (const [key, value] of Object.entries(window.transforms.small_caps.map)) {
smallCapsReverseMap[value] = key;
}
// Check if input contains small caps characters
const smallCapsChars = Object.values(window.transforms.small_caps.map);
const hasSmallCaps = smallCapsChars.some(char => input.includes(char));
if (hasSmallCaps) {
// Decode text
let result = '';
for (const char of input) {
result += smallCapsReverseMap[char] || char;
}
if (result !== input && /[a-zA-Z]/.test(result)) {
return { text: result, method: 'Small Caps' };
}
}
} catch (e) {
console.error('Small Caps decode error:', e);
}
}
// - Bubble text (create reverse mapping)
if (window.transforms.bubble && window.transforms.bubble.map) {
try {
// Create reverse mapping
const bubbleReverseMap = {};
for (const [key, value] of Object.entries(window.transforms.bubble.map)) {
bubbleReverseMap[value] = key;
}
// Check if input contains bubble characters
const bubbleChars = Object.values(window.transforms.bubble.map);
const hasBubbleChars = bubbleChars.some(char => input.includes(char));
if (hasBubbleChars) {
// Decode text
let result = '';
for (const char of input) {
result += bubbleReverseMap[char] || char;
}
if (result !== input && /[a-zA-Z]/.test(result)) {
return { text: result, method: 'Bubble' };
}
}
} catch (e) {
console.error('Bubble decode error:', e);
}
}
// - Try reverse each transform that has a built-in reverse function
for (const name in window.transforms) {
const transform = window.transforms[name];
if (transform.reverse) {
try {
const result = transform.reverse(input);
// Only return if the result is different and contains readable characters
if (result !== input && /[a-zA-Z0-9\s]/.test(result)) {
if (result !== input && /[a-zA-Z0-9\s]{3,}/.test(result)) {
return { text: result, method: transform.name };
}
} catch (e) {
@@ -273,86 +632,171 @@ new Vue({
// Emoji Library Methods
filterEmojis() {
if (!this.emojiSearch) {
this.filteredEmojis = [...window.emojiLibrary.EMOJI_LIST];
this.renderEmojiGrid();
return;
}
const searchTerm = this.emojiSearch.toLowerCase();
this.filteredEmojis = window.emojiLibrary.EMOJI_LIST.filter(emoji => {
// Simple search - we could enhance this with emoji names/descriptions later
return emoji.toLowerCase().includes(searchTerm);
});
// Always show all emojis - search functionality removed
this.filteredEmojis = [...window.emojiLibrary.EMOJI_LIST];
this.renderEmojiGrid();
},
selectEmoji(emoji) {
this.selectedEmoji = emoji;
// Insert the emoji at cursor position in the textarea
const textarea = document.getElementById('steg-input');
if (textarea) {
const startPos = textarea.selectionStart;
const endPos = textarea.selectionEnd;
const currentValue = this.emojiMessage;
// Create a temporary carrier for this emoji
const tempCarrier = {
name: `${emoji} Carrier`,
emoji: emoji,
encode: (text) => this.steganography.encode(text, emoji),
decode: (text) => this.steganography.decode(text),
preview: (text) => `${emoji}${text}${emoji}`
};
// Use this emoji as carrier
this.selectedCarrier = tempCarrier;
this.activeSteg = 'emoji';
// Encode the message with this emoji
if (this.emojiMessage) {
this.autoEncode();
// Insert emoji at cursor position
this.emojiMessage = currentValue.substring(0, startPos) + emoji + currentValue.substring(endPos);
// Set cursor position after the inserted emoji
// Wait for encoding to complete, then copy to clipboard
this.$nextTick(() => {
textarea.focus();
textarea.selectionStart = startPos + emoji.length;
textarea.selectionEnd = startPos + emoji.length;
// Trigger encoding
this.autoEncode();
if (this.encodedMessage) {
// Force clipboard copy regardless of event source
this.forceCopyToClipboard(this.encodedMessage);
this.showNotification(`<i class="fas fa-check"></i> Copied hidden message with ${emoji}`, 'success');
// Add to copy history
this.addToCopyHistory(`Emoji: ${emoji}`, this.encodedMessage);
}
});
this.showNotification(`Emoji ${emoji} inserted`);
} else {
this.showNotification(`Select an emoji and enter a message first`, 'info');
}
},
renderEmojiGrid() {
console.log('renderEmojiGrid called with', this.filteredEmojis.length, 'emojis');
// Make sure container exists
const container = document.getElementById('emoji-grid-container');
if (!container) {
console.error('emoji-grid-container not found!');
return;
}
// Force container to be completely visible
container.style.cssText = 'display: block !important; visibility: visible !important; min-height: 300px;';
// Make sure parent containers are visible too
const emojiLibrary = document.querySelector('.emoji-library');
if (emojiLibrary) {
emojiLibrary.style.cssText = 'display: block !important; visibility: visible !important;';
}
// Clear any existing content to avoid duplication
container.innerHTML = '';
// Render the emoji grid
window.emojiLibrary.renderEmojiGrid('emoji-grid-container', this.selectEmoji.bind(this), this.filteredEmojis);
// Add a bold message about copying
const copyNote = document.createElement('div');
copyNote.style.cssText = 'text-align: center; margin-top: 10px; font-weight: bold; padding: 5px; background-color: #f0f0f0; border-radius: 4px;';
copyNote.innerHTML = '<i class="fas fa-info-circle"></i> Clicking an emoji will automatically copy your hidden message';
container.appendChild(copyNote);
// Log success
console.log('Emoji grid rendered successfully');
}
},
// Initialize theme and components
mounted() {
console.log('Vue app mounted');
// Apply theme
if (this.isDarkTheme) {
document.body.classList.add('dark-theme');
}
// Initialize emoji grid
// Initialize emoji grid with all emojis shown by default
this.$nextTick(() => {
this.renderEmojiGrid();
console.log('nextTick: Initializing emoji grid');
// Make sure filtered emojis is populated
this.filteredEmojis = [...window.emojiLibrary.EMOJI_LIST];
// Define a function to properly initialize the emoji grid
const initializeEmojiGrid = () => {
const emojiGridContainer = document.getElementById('emoji-grid-container');
if (emojiGridContainer) {
console.log('Found emoji-grid-container, rendering grid');
// Set inline styles to ensure visibility
emojiGridContainer.setAttribute('style', 'display: block !important; visibility: visible !important; min-height: 300px; padding: 10px; border: 1px solid #ccc;');
// Also make sure the parent container is visible
const emojiLibrary = document.querySelector('.emoji-library');
if (emojiLibrary) {
emojiLibrary.setAttribute('style', 'display: block !important; visibility: visible !important; margin-top: 30px; border: 3px solid var(--accent-color); overflow: visible;');
}
// Now render the grid
this.renderEmojiGrid();
console.log('Emoji grid rendering complete in mounted()');
} else {
console.error('emoji-grid-container not found, will retry');
// Try again after a longer delay if not found
setTimeout(initializeEmojiGrid, 500);
}
};
// Start with a small delay to ensure DOM is ready
setTimeout(initializeEmojiGrid, 100);
// Set up paste event handlers for all textareas to prevent unwanted clipboard notifications
this.setupPasteHandlers();
});
},
// Keyboard shortcuts
created() {
window.addEventListener('keydown', (e) => {
// Theme toggle
if (e.key === 'd' || e.key === 'D') {
this.toggleTheme();
}
// Tab switching
else if (e.key === 't' || e.key === 'T') {
this.activeTab = 'transforms';
}
else if (e.key === 'h' || e.key === 'H') {
this.activeTab = 'steganography';
}
// Transform shortcuts (1-9)
else if (this.activeTab === 'transforms' && e.key >= '1' && e.key <= '9') {
const index = parseInt(e.key) - 1;
if (index < this.transforms.length) {
this.applyTransform(this.transforms[index]);
}
}
// Set up paste event handlers for all textareas
setupPasteHandlers() {
// Get all textareas in the app
const textareas = document.querySelectorAll('textarea');
// Add paste event listener to each textarea
textareas.forEach(textarea => {
textarea.addEventListener('paste', (e) => {
// Mark this as an explicit paste event
this.isPasteOperation = true;
// Reset the flag after a short delay
setTimeout(() => {
this.isPasteOperation = false;
}, 100);
});
});
},
// No keyboard shortcuts - they were removed as requested
created() {
// Initialize any required functionality
// But no keyboard shortcuts/hotkeys for now
},
// Watch for input events and ensure emojis stay visible
watch: {
// Make sure emoji list stays loaded when user types in any input
emojiMessage() {
// Reset the filtered emojis to the full list whenever typing occurs
this.filteredEmojis = [...window.emojiLibrary.EMOJI_LIST];
this.$nextTick(() => {
this.renderEmojiGrid();
});
},
// Also watch the decode input field for typing activity
decodeInput() {
// Always show full emoji list
this.filteredEmojis = [...window.emojiLibrary.EMOJI_LIST];
this.$nextTick(() => {
this.renderEmojiGrid();
});
}
}
});
+174 -23
View File
@@ -7,45 +7,137 @@ window.emojiLibrary = {};
window.emojiLibrary.EMOJI_LIST = [
// Faces and People
"😀", // Grinning Face
"😁", // Beaming Face with Smiling Eyes
"😂", // Face with Tears of Joy
"🥰", // Smiling Face with Hearts
"😎", // Smiling Face with Sunglasses
"🤔", // Thinking Face
"😅", // Smiling Face with Sweat
"😊", // Smiling Face with Smiling Eyes
"😇", // Smiling Face with Halo
"🙃", // Upside-Down Face
"🤣", // Rolling on the Floor Laughing
"😃", // Grinning Face with Big Eyes
"😄", // Grinning Face with Smiling Eyes
"😅", // Grinning Face with Sweat
"😆", // Grinning Squinting Face
"😉", // Winking Face
"🥳", // Partying Face
"😊", // Smiling Face with Smiling Eyes
"😋", // Face Savoring Food
"😎", // Smiling Face with Sunglasses
"😍", // Smiling Face with Heart-Eyes
"😘", // Face Blowing a Kiss
"🥰", // Smiling Face with Hearts
"😗", // Kissing Face
"😙", // Kissing Face with Smiling Eyes
"😚", // Kissing Face with Closed Eyes
"🙂", // Slightly Smiling Face
"🤗", // Hugging Face
"🤩", // Star-Struck
"🤔", // Thinking Face
"🤨", // Face with Raised Eyebrow
"😐", // Neutral Face
"😑", // Expressionless Face
"😶", // Face Without Mouth
"🙄", // Face with Rolling Eyes
"😏", // Smirking Face
"😣", // Persevering Face
"😥", // Sad but Relieved Face
"😮", // Face with Open Mouth
"🤐", // Zipper-Mouth Face
"😯", // Hushed Face
"😪", // Sleepy Face
"😫", // Tired Face
"😴", // Sleeping Face
"🥱", // Yawning Face
"😌", // Relieved Face
"😛", // Face with Tongue
"😜", // Winking Face with Tongue
"😝", // Squinting Face with Tongue
"🤤", // Drooling Face
"😒", // Unamused Face
"😓", // Downcast Face with Sweat
"😔", // Pensive Face
"😕", // Confused Face
"🙃", // Upside-Down Face
"🤑", // Money-Mouth Face
"😲", // Astonished Face
"🙁", // Slightly Frowning Face
"😖", // Confounded Face
"😞", // Disappointed Face
"😟", // Worried Face
"😤", // Face with Steam From Nose
"😢", // Crying Face
"😭", // Loudly Crying Face
"😧", // Anguished Face
"😨", // Fearful Face
"😩", // Weary Face
"🤯", // Exploding Head
"😱", // Face Screaming in Fear
"😳", // Flushed Face
"🥵", // Hot Face
"🥶", // Cold Face
"😡", // Pouting Face
"😠", // Angry Face
"🤬", // Face with Symbols on Mouth
"😷", // Face with Medical Mask
"🤒", // Face with Thermometer
"🤕", // Face with Head-Bandage
"🤢", // Nauseated Face
"🤮", // Face Vomiting
"🤧", // Sneezing Face
"😇", // Smiling Face with Halo
"🥳", // Partying Face
"🥴", // Woozy Face
"🥺", // Pleading Face
"🧐", // Face with Monocle
"🥱", // Yawning Face
"🧠", // Brain
// Gestures and Body Parts
"👍", // Thumbs Up
"👎", // Thumbs Down
"👏", // Clapping Hands
"🙌", // Raising Hands
"🤝", // Handshake
"👋", // Waving Hand
"✌️", // Victory Hand
"🤟", // Love-You Gesture
"🤘", // Sign of the Horns
"👊", // Oncoming Fist
"✊", // Raised Fist
"👆", // Backhand Index Pointing Up
"👇", // Backhand Index Pointing Down
"👈", // Backhand Index Pointing Left
"👉", // Backhand Index Pointing Right
"👌", // OK Hand
"🤌", // Pinched Fingers
"🤏", // Pinching Hand
"✋", // Raised Hand
"🤚", // Raised Back of Hand
"🖐️", // Hand with Fingers Splayed
"🖖", // Vulcan Salute
"👀", // Eyes
"👁️", // Eye
"👄", // Mouth
"🧿", // Nazar Amulet
// Celebration & Objects
"🎉", // Party Popper
"🎊", // Confetti Ball
"🎂", // Birthday Cake
"🎁", // Wrapped Gift
"🎈", // Balloon
"🎄", // Christmas Tree
"🎃", // Jack-O-Lantern
"🏆", // Trophy
"🏅", // Sports Medal
"🥇", // 1st Place Medal
"🥈", // 2nd Place Medal
"🥉", // 3rd Place Medal
"💰", // Money Bag
"💸", // Money with Wings
"💵", // Dollar Banknote
"💴", // Yen Banknote
"💶", // Euro Banknote
"💷", // Pound Banknote
"💯", // Hundred Points
"📱", // Mobile Phone
"💻", // Laptop
"⌨️", // Keyboard
"🖥️", // Desktop Computer
"🔒", // Locked
"🔓", // Unlocked
@@ -186,36 +278,84 @@ window.emojiLibrary.EMOJI_LIST = [
// Function to render emoji grid
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) return;
if (!container) {
console.error('Container not found:', containerId);
return;
}
// Clear container
container.innerHTML = '';
// Create emoji grid
// Create emoji grid with enforced styling
const gridContainer = document.createElement('div');
gridContainer.className = 'emoji-grid';
// Set a heading explaining the full library is shown
if (!filteredList || filteredList.length === window.emojiLibrary.EMOJI_LIST.length) {
const fullLibraryNote = document.createElement('div');
fullLibraryNote.className = 'emoji-grid-note';
fullLibraryNote.innerHTML = '<i class="fas fa-info-circle"></i> Showing all emojis. Use search to filter.';
container.appendChild(fullLibraryNote);
}
// Force grid styling
gridContainer.style.display = 'grid';
gridContainer.style.gridTemplateColumns = 'repeat(auto-fill, minmax(50px, 1fr))';
gridContainer.style.gap = '8px';
gridContainer.style.padding = '15px';
gridContainer.style.maxHeight = '300px';
gridContainer.style.overflowY = 'auto';
gridContainer.style.border = '1px solid #ccc';
gridContainer.style.borderRadius = '4px';
gridContainer.style.margin = '10px 0';
// Determine which list to use (filtered or full)
const emojisToShow = filteredList || window.emojiLibrary.EMOJI_LIST;
// Add a message showing we're displaying all emojis
const fullLibraryNote = document.createElement('div');
fullLibraryNote.className = 'emoji-grid-note';
fullLibraryNote.innerHTML = '<i class="fas fa-magic"></i> Click an emoji to automatically copy your hidden message';
fullLibraryNote.style.padding = '10px';
fullLibraryNote.style.marginBottom = '10px';
fullLibraryNote.style.backgroundColor = 'rgba(0,0,0,0.05)';
fullLibraryNote.style.borderRadius = '4px';
fullLibraryNote.style.textAlign = 'center';
container.appendChild(fullLibraryNote);
// Add emojis to grid
// Always use full emoji list - search removed
// Use the provided filtered list if available, otherwise default to full list
// This ensures we always show ALL emojis regardless of input state
const emojisToShow = filteredList && filteredList.length > 0 ? filteredList : window.emojiLibrary.EMOJI_LIST;
console.log(`Adding ${emojisToShow.length} emojis to grid`);
// Add emojis to grid with enforced styling
emojisToShow.forEach(emoji => {
const emojiButton = document.createElement('button');
emojiButton.className = 'emoji-button';
emojiButton.innerHTML = emoji;
emojiButton.title = 'Click to select this emoji';
emojiButton.textContent = emoji; // Use textContent for better emoji handling
emojiButton.title = 'Click to encode with this emoji';
// Force button styling
emojiButton.style.fontSize = '24px';
emojiButton.style.padding = '8px';
emojiButton.style.border = '1px solid #ddd';
emojiButton.style.borderRadius = '8px';
emojiButton.style.cursor = 'pointer';
emojiButton.style.backgroundColor = '#fff';
emojiButton.style.transition = 'transform 0.1s';
// Add hover effect
emojiButton.onmouseover = function() {
this.style.transform = 'scale(1.1)';
this.style.boxShadow = '0 0 5px rgba(0,0,0,0.2)';
};
emojiButton.onmouseout = function() {
this.style.transform = 'scale(1)';
this.style.boxShadow = 'none';
};
emojiButton.addEventListener('click', () => {
if (typeof onEmojiSelect === 'function') {
onEmojiSelect(emoji);
// Add visual feedback when clicked
emojiButton.style.backgroundColor = '#e6f7ff';
setTimeout(() => {
emojiButton.style.backgroundColor = '#fff';
}, 300);
}
});
@@ -223,4 +363,15 @@ window.emojiLibrary.renderEmojiGrid = function(containerId, onEmojiSelect, filte
});
container.appendChild(gridContainer);
console.log('Emoji grid rendering complete');
// Force container to be visible
container.style.display = 'block !important';
container.style.visibility = 'visible !important';
// Debug info - add count display
const countDisplay = document.createElement('div');
countDisplay.className = 'emoji-count';
countDisplay.textContent = `${emojisToShow.length} emojis available`;
container.appendChild(countDisplay);
};