diff --git a/js/app.js b/js/app.js
index 07293ba..00021f9 100644
--- a/js/app.js
+++ b/js/app.js
@@ -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(` ${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(' 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(` 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(' 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(' 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(' 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(' 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 = ' 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(` 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 = ' 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();
+ });
+ }
}
});
diff --git a/js/emojiLibrary.js b/js/emojiLibrary.js
index ff9bc41..fe8fa92 100644
--- a/js/emojiLibrary.js
+++ b/js/emojiLibrary.js
@@ -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 = ' 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 = ' 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);
};