// Initialize Vue app
window.app = new Vue({
el: '#app',
data: {
// Theme
isDarkTheme: true,
// Tab Management
activeTab: 'transforms',
// Transform Tab
transformInput: '',
transformOutput: '',
activeTransform: null,
// Transform categories for styling
transformCategories: {
encoding: ['Base64', 'Base64 URL', 'Base32', 'Base58', 'Base62', 'Binary', 'Hexadecimal', 'ASCII85', 'URL Encode', 'HTML Entities'],
cipher: ['Caesar Cipher', 'ROT13', 'ROT47', 'Morse Code', 'Atbash Cipher', 'ROT5', 'Vigenรจre Cipher', 'Rail Fence (3 Rails)'],
visual: ['Rainbow Text', 'Strikethrough', 'Underline', 'Reverse Text', 'Alternating Case', 'Reverse Words', 'Random Case', 'Title Case', 'Sentence Case', 'Emoji Speak'],
format: ['Pig Latin', 'Leetspeak', 'NATO Phonetic', 'camelCase', 'snake_case', 'kebab-case'],
unicode: ['Invisible Text', 'Upside Down', 'Full Width', 'Small Caps', 'Bubble', 'Braille', 'Greek Letters', 'Wingdings', 'Superscript', 'Subscript', 'Regional Indicator Letters', 'Fraktur', 'Cyrillic Stylized', 'Katakana', 'Hiragana', 'Roman Numerals'],
special: ['Medieval', 'Cursive', 'Monospace', 'Double-Struck', 'Elder Futhark', 'Mirror Text', 'Zalgo'],
fantasy: ['Quenya (Tolkien Elvish)', 'Tengwar Script', 'Klingon', 'Aurebesh (Star Wars)', 'Dovahzul (Dragon)'],
ancient: ['Hieroglyphics', 'Ogham (Celtic)', 'Semaphore Flags'],
technical: ['Brainfuck', 'Mathematical Notation', 'Chemical Symbols'],
randomizer: ['Random Mix']
},
transforms: Object.entries(window.transforms).map(([key, transform]) => ({
name: transform.name,
func: transform.func.bind(transform),
preview: transform.preview.bind(transform)
})),
// Steganography Tab
emojiMessage: '',
encodedMessage: '',
decodeInput: '',
decodedMessage: '',
selectedCarrier: null,
// Universal Decoder - works on both tabs
universalDecodeInput: '',
universalDecodeResult: null,
isPasteOperation: false, // Flag to track paste operations
lastCopyTime: 0, // Timestamp of last copy operation for debounce
ignoreKeyboardEvents: false, // Flag to prevent keyboard events from triggering copies
isTransformCopy: false, // Flag to mark transform-initiated copy operations
keyboardEventsTimeout: null, // Timeout for resetting keyboard event flag
activeSteg: null,
carriers: window.steganography.carriers,
showDecoder: true,
// Emoji Library
filteredEmojis: [...window.emojiLibrary.EMOJI_LIST],
selectedEmoji: null,
carrierEmojiList: [...window.emojiLibrary.EMOJI_LIST],
quickCarrierEmojis: ['๐','๐','๐ฒ','๐ฅ','๐ฅ','๐ฟ','โ','โญ','โจ','๐','๐','๐ชจ','๐','๐ชถ','๐ฎ','๐ข','๐','๐ฆ','๐'],
tbCarrierManual: '',
// Token Bomb Generator
tbDepth: 3,
tbBreadth: 4,
tbRepeats: 5,
tbSeparator: 'zwnj',
tbIncludeVS: true,
tbIncludeNoise: true,
tbRandomizeEmojis: true,
tbAutoCopy: false,
tbSingleCarrier: false,
tbCarrier: '',
tbPayloadEmojis: [],
tokenBombOutput: '',
// Text Payload Generator
tpBase: '',
tpRepeat: 100,
tpCombining: true,
tpZW: false,
textPayload: '',
// Tokenizer tab
tokenizerInput: '',
tokenizerEngine: 'byte',
tokenizerTokens: [],
tokenizerCharCount: 0,
tokenizerWordCount: 0,
// History of copied content
copyHistory: [],
maxHistoryItems: 10,
showCopyHistory: false,
showUnicodePanel: false
},
methods: {
toggleUnicodePanel() {
this.showUnicodePanel = !this.showUnicodePanel;
const panel = document.getElementById('unicode-options-panel');
if (panel) {
if (this.showUnicodePanel) panel.classList.add('active');
else panel.classList.remove('active');
}
},
// Focus an element without causing the page to scroll
focusWithoutScroll(el) {
if (!el) return;
const x = window.scrollX, y = window.scrollY;
try {
el.focus({ preventScroll: true });
} catch (e) {
el.focus();
window.scrollTo(x, y);
}
},
// Trigger randomizer chaos animation regardless of input
triggerRandomizerChaos() {
try {
const section = document.getElementById('category-randomizer');
const overlay = section && section.querySelector('.chaos-overlay');
if (!overlay) return;
const emojis = ['โจ','๐','๐ฅ','โก','๐ฅ','๐','๐ฒ','๐ฎ','๐ซ','๐ช๏ธ'];
for (let i=0;i<10;i++) {
const el = document.createElement('div');
el.className = 'chaos-particle';
el.textContent = emojis[Math.floor(Math.random()*emojis.length)];
el.style.left = (10 + Math.random()*80) + '%';
el.style.fontSize = (14 + Math.random()*10) + 'px';
el.style.animationDelay = (Math.random()*0.2) + 's';
overlay.appendChild(el);
setTimeout(()=>{ if (el.parentNode) el.parentNode.removeChild(el); }, 1300);
}
section.classList.add('shake-once','randomizer-glow');
setTimeout(()=>section && section.classList.remove('shake-once','randomizer-glow'), 600);
} catch(_) {}
},
// Switch between tabs with proper initialization
switchToTab(tabName) {
this.activeTab = tabName;
console.log('Switched to tab:', tabName);
// Reset universal decoder input when switching tabs
this.universalDecodeInput = '';
this.universalDecodeResult = null;
// Initialize emoji grid when switching to steganography tab
if (tabName === 'steganography') {
this.$nextTick(() => {
console.log('Tab switch: Initializing emoji grid');
const emojiGridContainer = document.getElementById('emoji-grid-container');
if (emojiGridContainer) {
console.log('Found emoji grid container after tab switch');
// Make sure the container is visible
emojiGridContainer.setAttribute('style', 'display: block !important; visibility: visible !important; min-height: 300px; padding: 10px;');
// Render the emoji grid
this.renderEmojiGrid();
} else {
console.log('Emoji grid container not found after tab switch');
}
});
}
// Initialize category navigation when switching to transforms tab
if (tabName === 'transforms') {
this.$nextTick(() => {
this.initializeCategoryNavigation();
});
}
if (tabName === 'tokenizer') {
this.$nextTick(() => this.runTokenizer());
}
},
// Get transforms grouped by category
getTransformsByCategory(category) {
return this.transforms.filter(transform =>
this.transformCategories[category].includes(transform.name)
);
},
// Theme Toggle
toggleTheme() {
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, event) {
// Prevent default button behavior and scrolling
event && event.preventDefault();
event && event.stopPropagation();
// Always trigger chaos animation for Random Mix, even with empty input
if (transform && transform.name === 'Random Mix') {
this.triggerRandomizerChaos();
}
if (this.transformInput) {
// Update active transform and apply it
this.activeTransform = transform;
if (transform.name === 'Random Mix') {
this.transformOutput = window.transforms.randomizer.func(this.transformInput);
// Show transform mapping info
const transformInfo = window.transforms.randomizer.getLastTransformInfo();
if (transformInfo.length > 0) {
const transformsList = transformInfo.map(t => t.transformName).join(', ');
this.showNotification(` Mixed with: ${transformsList}`, 'success');
console.log('Transform mapping:', transformInfo);
}
} else {
// Handle text with proper Unicode segmentation
const segments = window.emojiLibrary.splitEmojis(this.transformInput);
const transformedSegments = segments.map(segment => {
// Skip transformation for emojis and complex Unicode characters
if (segment.length > 1 || /[\u{1F300}-\u{1F9FF}\u{2600}-\u{26FF}]/u.test(segment)) {
return segment;
}
return transform.func(segment);
});
this.transformOutput = window.emojiLibrary.joinEmojis(transformedSegments);
}
// Set flag to mark this as a transform-initiated copy
this.isTransformCopy = true;
// Force copy the transform output to clipboard
this.forceCopyToClipboard(this.transformOutput);
// Add to copy history
this.addToCopyHistory(`Transform: ${transform.name}`, this.transformOutput);
// Enhanced notification for transform and copy (if not randomizer - it has its own notification)
if (transform.name !== 'Random Mix') {
this.showNotification(` ${transform.name} applied and copied!`, 'success');
}
// Remove active state from transform buttons
document.querySelectorAll('.transform-button').forEach(button => {
button.classList.remove('active');
});
// Keep focus on input and move cursor to end
const inputBox = document.querySelector('#transform-input');
if (inputBox) {
this.focusWithoutScroll(inputBox);
const len = inputBox.value.length;
try { inputBox.setSelectionRange(len, len); } catch (_) {}
}
// Reset flags immediately
this.isTransformCopy = false;
this.ignoreKeyboardEvents = false;
}
},
autoTransform() {
// Only proceed if we're in the transforms tab and have an active transform
if (this.transformInput && this.activeTransform && this.activeTab === 'transforms') {
// Handle text with proper Unicode segmentation
const segments = window.emojiLibrary.splitEmojis(this.transformInput);
const transformedSegments = segments.map(segment => {
// Skip transformation for emojis and complex Unicode characters
if (segment.length > 1 || /[\u{1F300}-\u{1F9FF}\u{2600}-\u{26FF}]/u.test(segment)) {
return segment;
}
return this.activeTransform.func(segment);
});
this.transformOutput = window.emojiLibrary.joinEmojis(transformedSegments);
}
},
// Check if a transform has a reverse function
transformHasReverse(transform) {
return transform && typeof transform.reverse === 'function';
},
// Decode text using the specific transform's reverse function
decodeWithTransform(transform) {
if (!this.transformInput || !transform || !this.transformHasReverse(transform)) {
return;
}
try {
// Handle text with proper Unicode segmentation
const segments = window.emojiLibrary.splitEmojis(this.transformInput);
const decodedSegments = segments.map(segment => {
// Skip decoding for emojis and complex Unicode characters
if (segment.length > 1 || /[\u{1F300}-\u{1F9FF}\u{2600}-\u{26FF}]/u.test(segment)) {
return segment;
}
return transform.reverse(segment);
});
const decodedText = window.emojiLibrary.joinEmojis(decodedSegments);
if (decodedText !== this.transformInput) {
// Update the input with the decoded text
this.transformInput = decodedText;
// Show a notification
this.showNotification(` Decoded using ${transform.name}`, 'success');
// Add to copy history
this.addToCopyHistory(`Decoded (${transform.name})`, decodedText);
} else {
this.showNotification(` Could not decode with ${transform.name}`, 'warning');
}
} catch (error) {
console.error(`Error decoding with ${transform.name}:`, error);
this.showNotification(` Error decoding with ${transform.name}`, 'error');
}
},
// Steganography Methods
selectCarrier(carrier) {
// Toggle carrier selection if clicking the same one again
if (this.selectedCarrier === carrier) {
this.selectedCarrier = null;
this.encodedMessage = '';
} else {
this.selectedCarrier = carrier;
this.activeSteg = 'emoji';
this.autoEncode();
}
},
setStegMode(mode) {
// 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;
// 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() {
// 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);
// Auto-copy will be handled in setStegMode method
} else if (this.selectedCarrier) {
this.encodedMessage = window.steganography.encodeEmoji(
this.selectedCarrier.emoji,
this.emojiMessage
);
// Auto-copy for emoji carrier is handled in selectEmoji method
}
},
autoDecode() {
if (!this.decodeInput) {
this.decodedMessage = '';
return;
}
// Use the universal decoder
const result = this.universalDecode(this.decodeInput);
if (result) {
this.decodedMessage = `Decoded (${result.method}): ${result.text}`;
// 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';
}
},
previewInvisible(text) {
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
// Track last copy operation to prevent rapid repeated copies
lastCopyTime: 0,
async copyToClipboard(text) {
if (!text) return;
// Check clipboard lock - don't proceed if locked
if (this.clipboardLocked) {
console.log('Copy operation prevented by clipboard lock');
return;
}
// Prevent rapid successive copy operations (debounce)
const now = Date.now();
if (now - this.lastCopyTime < 500) {
console.log('Copy operation debounced');
return;
}
this.lastCopyTime = now;
// Set clipboard lock immediately
this.clipboardLocked = true;
console.log('Setting clipboard lock during regular copy');
// 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);
// Aggressively clear focus and selections
if (document.activeElement && document.activeElement.blur) {
document.activeElement.blur();
}
// Clear any text selection
if (window.getSelection) {
window.getSelection().removeAllRanges();
}
// Focus body to avoid any specific interactive elements
document.body.focus();
// Release clipboard lock after a longer delay
setTimeout(() => {
this.clipboardLocked = false;
console.log('Clipboard lock released after regular copy');
}, 500);
} catch (err) {
console.warn('Clipboard access not available:', err);
// Try fallback method for copying (textarea method)
this.fallbackCopy(text);
}
},
fallbackCopy(text) {
try {
// Check if keyboard events should be ignored
if (this.ignoreKeyboardEvents && !this.isTransformCopy) {
console.log('Ignoring fallback copy due to keyboard event flag');
return;
}
// Reset the transform flag if it was set
if (this.isTransformCopy) {
this.isTransformCopy = false;
}
// Debounce check
const now = Date.now();
if (now - this.lastCopyTime < 300) {
console.log('Fallback copy operation debounced');
return;
}
this.lastCopyTime = now;
// Create temporary textarea
const textarea = document.createElement('textarea');
textarea.value = text;
textarea.style.position = 'fixed'; // Avoid scrolling to bottom
textarea.style.left = '-9999px'; // Move offscreen
textarea.style.top = '0';
document.body.appendChild(textarea);
textarea.select();
// Try the copy command
const successful = document.execCommand('copy');
// Show appropriate notification
if (successful) {
this.showNotification(' Copied!', 'success');
// Add to history with context
let source = this.activeTab === 'transforms' ? 'Transform' : 'Steganography';
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');
}
// Clean up
document.body.removeChild(textarea);
// Aggressively clear focus and selection
if (document.activeElement && document.activeElement.blur) {
document.activeElement.blur();
}
// Clear any text selection
if (window.getSelection) {
window.getSelection().removeAllRanges();
}
// Focus on body element
document.body.focus();
} catch (err) {
console.warn('Fallback copy method failed:', err);
this.showNotification(' Copy not supported', 'error');
}
},
// Force copy to clipboard regardless of event context
forceCopyToClipboard(text) {
if (!text) return;
// Skip copy operations during paste
if (this.isPasteOperation) {
this.isPasteOperation = false;
return;
}
// Block keyboard-triggered copies unless it's a transform
if (!this.isTransformCopy && this.ignoreKeyboardEvents) {
return;
}
try {
// Use Clipboard API
if (navigator.clipboard && navigator.clipboard.writeText) {
// For emojis and complex characters, use a more robust approach
const processedText = typeof text === 'string' ? text : String(text);
// Try to use the newer clipboard API methods if available
if (navigator.clipboard.write && processedText.match(/[\u{1F300}-\u{1F9FF}\u{2600}-\u{26FF}]/u)) {
const blob = new Blob([processedText], { type: 'text/plain;charset=utf-8' });
const clipboardItem = new ClipboardItem({ 'text/plain': blob });
navigator.clipboard.write([clipboardItem])
.then(() => {
if (this.isTransformCopy) {
this.showCopiedPopup();
this.ignoreKeyboardEvents = true;
clearTimeout(this.keyboardEventsTimeout);
this.keyboardEventsTimeout = setTimeout(() => {
this.ignoreKeyboardEvents = false;
}, 1000);
}
this.isTransformCopy = false;
const inputBox = document.querySelector('#transform-input');
if (inputBox) {
inputBox.focus();
const len = inputBox.value.length;
inputBox.setSelectionRange(len, len);
}
})
.catch(err => {
console.warn('Advanced Clipboard API failed:', err);
// Fall back to basic writeText
navigator.clipboard.writeText(processedText)
.then(() => {
if (this.isTransformCopy) {
this.showCopiedPopup();
}
this.isTransformCopy = false;
const inputBox = document.querySelector('#transform-input');
if (inputBox) {
inputBox.focus();
const len = inputBox.value.length;
inputBox.setSelectionRange(len, len);
}
})
.catch(err => {
console.warn('Basic Clipboard API failed:', err);
this.forceFallbackCopy(processedText);
});
});
} else {
navigator.clipboard.writeText(processedText)
.then(() => {
if (this.isTransformCopy) {
this.showCopiedPopup();
}
this.isTransformCopy = false;
const inputBox = document.querySelector('#transform-input');
if (inputBox) {
inputBox.focus();
const len = inputBox.value.length;
inputBox.setSelectionRange(len, len);
}
})
.catch(err => {
console.warn('Basic Clipboard API failed:', err);
this.forceFallbackCopy(processedText);
});
}
} else {
this.forceFallbackCopy(text);
}
} catch (error) {
console.error('Force copy failed:', error);
this.forceFallbackCopy(text);
}
},
// Fallback copy method that doesn't rely on user-initiated events
forceFallbackCopy(text) {
try {
// If clipboard is locked, don't proceed
if (this.clipboardLocked) {
console.log('Fallback copy prevented by clipboard lock');
return;
}
// Set clipboard lock immediately
this.clipboardLocked = true;
// Create temporary textarea for copying
const textarea = document.createElement('textarea');
textarea.value = text;
// Ensure proper emoji rendering
textarea.style.fontFamily = "'Segoe UI Emoji', 'Apple Color Emoji', sans-serif";
textarea.style.fontSize = '16px';
// Position offscreen but with proper dimensions
textarea.style.position = 'fixed';
textarea.style.left = '-9999px';
textarea.style.top = '0';
textarea.style.width = '100px';
textarea.style.height = '100px';
document.body.appendChild(textarea);
// Focus and select the text
textarea.focus();
textarea.select();
try {
document.execCommand('copy');
console.log('Force fallback copy successful');
} catch (err) {
console.error('Force fallback copy command failed:', err);
}
// Remove the temporary element
document.body.removeChild(textarea);
// Keep focus on input
const inputBox = document.querySelector('#transform-input');
if (inputBox) {
inputBox.focus();
const len = inputBox.value.length;
inputBox.setSelectionRange(len, len);
}
// Reset flags immediately
this.clipboardLocked = false;
this.isTransformCopy = false;
this.ignoreKeyboardEvents = false;
console.log('Clipboard lock released after fallback copy');
} catch (err) {
console.error('Force fallback copy method failed:', err);
this.clipboardLocked = false; // Make sure we don't leave it locked in case of error
}
},
// Notification system
showNotification(message, type = 'success') {
// Create notification element
const notification = document.createElement('div');
notification.className = `copy-notification ${type}`;
notification.innerHTML = message;
document.body.appendChild(notification);
// Remove after animation
setTimeout(() => {
notification.classList.add('fade-out');
setTimeout(() => {
if (notification.parentNode) {
document.body.removeChild(notification);
}
}, 300);
}, 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 the currently selected transform first, if any
if (this.activeTransform && this.transformHasReverse(this.activeTransform)) {
try {
console.log(`Trying to decode with currently selected transform: ${this.activeTransform.name}`);
const decodedText = this.activeTransform.reverse(this.universalDecodeInput);
// If the decoded text is different from the input and looks like readable text
if (decodedText !== this.universalDecodeInput && /[a-zA-Z0-9\s]{3,}/.test(decodedText)) {
this.universalDecodeResult = {
text: decodedText,
method: this.activeTransform.name
};
console.log(`Successfully decoded with ${this.activeTransform.name}`);
return;
}
} catch (e) {
console.error(`Error decoding with selected transform ${this.activeTransform.name}:`, e);
}
}
// If the selected transform didn't work or there isn't one selected,
// fall back to trying 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 '';
// Try all decoders in order
// 1. Try steganography decoders
// - 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');
}
}
// - Invisible text (only check if the input actually contains invisible characters)
if (/[\uE0000-\uE007F]/.test(input)) {
let decoded = window.steganography.decodeInvisible(input);
if (decoded && decoded.length > 0) {
return { text: decoded, method: 'Invisible Text' };
}
}
// 2. Try transform reversals
// Try to decode using active transform first
if (this.activeTab === 'transforms' && this.activeTransform) {
try {
const transformKey = Object.keys(window.transforms).find(
key => window.transforms[key].name === this.activeTransform.name
);
if (transformKey && window.transforms[transformKey].reverse) {
const result = window.transforms[transformKey].reverse(input);
if (result && result !== input) {
return {
text: result,
method: this.activeTransform.name,
priorityMatch: true
};
}
}
} catch (e) {
console.error('Error decoding with active transform:', e);
}
}
// 3. Smart pattern detection for new transforms
// Check for specific patterns that indicate certain transform types
// - Check for fantasy language patterns
if (/[แชแแฒแแแ แทแบแแแแแพแแแฉแฑแแแขแฉแ]/.test(input)) {
// This looks like Tengwar or Elder Futhark runes
try {
if (window.transforms.tengwar && window.transforms.tengwar.reverse) {
const result = window.transforms.tengwar.reverse(input);
if (result !== input && /[a-zA-Z0-9]/.test(result)) {
return { text: result, method: 'Tengwar Script', priorityMatch: true };
}
}
if (window.transforms.elder_futhark && window.transforms.elder_futhark.reverse) {
const result = window.transforms.elder_futhark.reverse(input);
if (result !== input && /[a-zA-Z0-9]/.test(result)) {
return { text: result, method: 'Elder Futhark', priorityMatch: true };
}
}
} catch (e) {
console.error('Rune decode error:', e);
}
}
// - Check for hieroglyphic patterns
if (/[๐ญ๐ฎ๐ฏ๐ฐ๐ฑ๐ฒ๐ณ๐ด๐ต๐ถ๐ท๐ธ๐น๐บ๐ป๐ผ]/.test(input)) {
try {
if (window.transforms.hieroglyphics && window.transforms.hieroglyphics.reverse) {
const result = window.transforms.hieroglyphics.reverse(input);
if (result !== input && /[a-zA-Z0-9]/.test(result)) {
return { text: result, method: 'Hieroglyphics', priorityMatch: true };
}
}
} catch (e) {
console.error('Hieroglyphics decode error:', e);
}
}
// - Check for Ogham patterns
if (/[แแแแแแแแแแแแแแ
แแแแ]/.test(input)) {
try {
if (window.transforms.ogham && window.transforms.ogham.reverse) {
const result = window.transforms.ogham.reverse(input);
if (result !== input && /[a-zA-Z0-9]/.test(result)) {
return { text: result, method: 'Ogham (Celtic)', priorityMatch: true };
}
}
} catch (e) {
console.error('Ogham decode error:', e);
}
}
// - Check for mathematical notation patterns
if (/[๐ถ๐ท๐ธ๐น๐๐ป๐๐ฝ๐พ๐ฟ๐๐๐๐๐๐
๐๐๐๐๐๐๐๐๐๐๐โฌ๐๐โฐโฑ๐ขโโ๐ฅ๐ฆโโณ๐ฉ๐ช๐ซ๐ฌโ๐ฎ๐ฏ๐ฐ๐ฑ๐ฒ๐ณ๐ด๐ต]/.test(input)) {
try {
if (window.transforms.mathematical && window.transforms.mathematical.reverse) {
const result = window.transforms.mathematical.reverse(input);
if (result !== input && /[a-zA-Z0-9]/.test(result)) {
return { text: result, method: 'Mathematical Notation', priorityMatch: true };
}
}
} catch (e) {
console.error('Mathematical notation decode error:', e);
}
}
// - Check for chemical symbol patterns
if (/^(Ac|B|C|D|Es|F|Ge|H|I|J|K|L|Mn|N|O|P|Q|R|S|Ti|U|V|W|Xe|Y|Zn|AC|ES|GE|MN|TI|XE)\s*$/.test(input.trim())) {
try {
if (window.transforms.chemical && window.transforms.chemical.reverse) {
const result = window.transforms.chemical.reverse(input);
if (result !== input && /[a-zA-Z0-9]/.test(result)) {
return { text: result, method: 'Chemical Symbols', priorityMatch: true };
}
}
} catch (e) {
console.error('Chemical symbols decode error:', e);
}
}
// - Binary (improved with more patterns)
if (/^[01\s]+$/.test(input.trim())) {
try {
// Use binary transform's reverse function if available
if (window.transforms.binary && window.transforms.binary.reverse) {
const result = window.transforms.binary.reverse(input);
if (result && /[\x20-\x7E]{3,}/.test(result)) { // Make sure it's readable ASCII
return { text: result, method: 'Binary' };
}
}
// Try different binary formats (with and without spaces)
const variations = [
input.trim(), // Original input
input.replace(/\s+/g, ''), // No spaces
input.replace(/([01]{8})/g, '$1 ') // Force 8-bit spacing
];
for (const binVariation of variations) {
// Fallback implementation
const binText = binVariation.replace(/\s+/g, '');
let result = '';
// Try standard 8-bit ASCII
for (let i = 0; i < binText.length; i += 8) {
const byte = binText.substr(i, 8);
if (byte.length === 8) {
result += String.fromCharCode(parseInt(byte, 2));
}
}
if (result && /[\x20-\x7E]{3,}/.test(result)) { // Make sure it's readable ASCII
return { text: result, method: 'Binary' };
}
}
} catch (e) {
console.error('Binary decode error:', e);
}
}
// - Morse code
if (/^[.\-\s\/]+$/.test(input.trim())) {
try {
// Use morse transform's reverse function if available
if (window.transforms.morse && window.transforms.morse.reverse) {
const result = window.transforms.morse.reverse(input);
if (result !== input && /[a-zA-Z0-9]/.test(result)) {
return { text: result, method: 'Morse Code' };
}
}
} catch (e) {
console.error('Morse decode error:', e);
}
}
// - Braille
const braillePattern = /[โ -โฃฟ]/;
if (braillePattern.test(input)) {
try {
// Count how many braille characters are in the input
const brailleMatches = [...input.matchAll(/[โ -โฃฟ]/g)];
// Only proceed if there are enough braille characters (to avoid false positives)
if (brailleMatches.length > 2) {
// 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);
}
}
// - 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);
}
}
// - Base58
if (/^[1-9A-HJ-NP-Za-km-z]+$/.test(input.trim())) {
try {
if (window.transforms.base58 && window.transforms.base58.reverse) {
const result = window.transforms.base58.reverse(input.trim());
if (result && /[\x20-\x7E]{3,}/.test(result)) {
return { text: result, method: 'Base58' };
}
}
} catch (e) {
console.error('Base58 decode error:', e);
}
}
// - Base62
if (/^[0-9A-Za-z]+$/.test(input.trim())) {
try {
if (window.transforms.base62 && window.transforms.base62.reverse) {
const result = window.transforms.base62.reverse(input.trim());
if (result && /[\x20-\x7E]{3,}/.test(result)) {
return { text: result, method: 'Base62' };
}
}
} catch (e) {
console.error('Base62 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);
}
}
// Check for specific new transforms before trying the generic approach
// - Hexadecimal
if (/^[0-9A-Fa-f\s]+$/.test(input.trim())) {
try {
if (window.transforms.hex && window.transforms.hex.reverse) {
const result = window.transforms.hex.reverse(input);
if (result && /[\x20-\x7E]{3,}/.test(result)) {
return { text: result, method: 'Hexadecimal' };
}
}
} catch (e) {
console.error('Hex decode error:', e);
}
}
// - URL Encoded
if (/%[0-9A-Fa-f]{2}/.test(input)) {
try {
if (window.transforms.url && window.transforms.url.reverse) {
const result = window.transforms.url.reverse(input);
if (result !== input && /[\x20-\x7E]{3,}/.test(result)) {
return { text: result, method: 'URL Encoded' };
}
} else {
// Fallback implementation
try {
const result = decodeURIComponent(input);
if (result !== input && /[\x20-\x7E]{3,}/.test(result)) {
return { text: result, method: 'URL Encoded' };
}
} catch (e) {
console.error('URL decode fallback error:', e);
}
}
} catch (e) {
console.error('URL decode error:', e);
}
}
// - HTML Entities
if (/&[#a-zA-Z0-9]+;/.test(input)) {
try {
if (window.transforms.html && window.transforms.html.reverse) {
const result = window.transforms.html.reverse(input);
if (result !== input && /[\x20-\x7E]{3,}/.test(result)) {
return { text: result, method: 'HTML Entities' };
}
}
} catch (e) {
console.error('HTML entities decode error:', e);
}
}
// - ROT13/Caesar Cipher (check if decoding produces more common English words)
if (/^[a-zA-Z\s.,!?]+$/.test(input)) {
try {
// Try ROT13 first as it's more common
if (window.transforms.rot13 && window.transforms.rot13.reverse) {
const result = window.transforms.rot13.reverse(input);
if (result !== input) {
return { text: result, method: 'ROT13' };
}
}
// Then try Caesar cipher
if (window.transforms.caesar && window.transforms.caesar.reverse) {
const result = window.transforms.caesar.reverse(input);
if (result !== input) {
return { text: result, method: 'Caesar Cipher' };
}
}
} catch (e) {
console.error('Cipher decode error:', e);
}
}
// - Base32
if (/^[A-Z2-7=]+$/.test(input.trim())) {
try {
if (window.transforms.base32 && window.transforms.base32.reverse) {
const result = window.transforms.base32.reverse(input);
if (result && /[\x20-\x7E]{3,}/.test(result)) {
return { text: result, method: 'Base32' };
}
}
} catch (e) {
console.error('Base32 decode error:', e);
}
}
// - ASCII85
if (/^<~.*~>$/.test(input.trim())) {
try {
if (window.transforms.ascii85 && window.transforms.ascii85.reverse) {
const result = window.transforms.ascii85.reverse(input);
if (result && /[\x20-\x7E]{3,}/.test(result)) {
return { text: result, method: 'ASCII85' };
}
}
} catch (e) {
console.error('ASCII85 decode error:', e);
}
}
// - Check for Zalgo text (text with combining marks)
const combiningMarksRegex = /[\u0300-\u036f\u1ab0-\u1aff\u1dc0-\u1dff\u20d0-\u20ff\ufe20-\ufe2f]/;
if (combiningMarksRegex.test(input)) {
try {
// Count the number of combining marks to ensure it's actually Zalgo text
// and not just text with a few accents
const matches = input.match(combiningMarksRegex) || [];
if (matches.length > 3) { // Threshold to distinguish Zalgo from normal accented text
// Fallback implementation to remove combining marks
const result = input.replace(/[\u0300-\u036f\u1ab0-\u1aff\u1dc0-\u1dff\u20d0-\u20ff\ufe20-\ufe2f]/g, '');
if (result !== input && result.length > 0) {
return { text: result, method: 'Zalgo' };
}
}
} catch (e) {
console.error('Zalgo decode error:', e);
}
}
// - Check for various Unicode text styles (medieval, cursive, monospace, double-struck)
const unicodeStyleChecks = [
{ name: 'Medieval', transform: 'medieval' },
{ name: 'Cursive', transform: 'cursive' },
{ name: 'Monospace', transform: 'monospace' },
{ name: 'Double-Struck', transform: 'doubleStruck' }
];
for (const style of unicodeStyleChecks) {
if (window.transforms[style.transform] && window.transforms[style.transform].map) {
try {
// Create reverse mapping
const reverseMap = {};
for (const [key, value] of Object.entries(window.transforms[style.transform].map)) {
reverseMap[value] = key;
}
// Check if input contains characters from this style
const styleChars = Object.values(window.transforms[style.transform].map);
const hasStyleChars = styleChars.some(char => input.includes(char));
if (hasStyleChars) {
// Decode text
let result = '';
for (const char of input) {
result += reverseMap[char] || char;
}
if (result !== input && /[a-zA-Z0-9]/.test(result)) {
return { text: result, method: style.name };
}
}
} catch (e) {
console.error(`${style.name} decode error:`, e);
}
}
}
// - Check for Fantasy Languages
const fantasyLanguageChecks = [
{ name: 'Quenya (Tolkien Elvish)', transform: 'quenya' },
{ name: 'Tengwar Script', transform: 'tengwar' },
{ name: 'Klingon', transform: 'klingon' },
{ name: 'Dovahzul (Dragon)', transform: 'dovahzul' }
];
for (const language of fantasyLanguageChecks) {
if (window.transforms[language.transform] && window.transforms[language.transform].map) {
try {
// Create reverse mapping
const reverseMap = {};
for (const [key, value] of Object.entries(window.transforms[language.transform].map)) {
reverseMap[value] = key;
}
// Check if input contains characters from this language
const languageChars = Object.values(window.transforms[language.transform].map);
const hasLanguageChars = languageChars.some(char => input.includes(char));
if (hasLanguageChars) {
// Decode text
let result = '';
for (const char of input) {
result += reverseMap[char] || char;
}
if (result !== input && /[a-zA-Z0-9]/.test(result)) {
return { text: result, method: language.name };
}
}
} catch (e) {
console.error(`${language.name} decode error:`, e);
}
}
}
// - Check for Aurebesh (Star Wars) - special case due to word-based mapping
if (window.transforms.aurebesh && window.transforms.aurebesh.map) {
try {
// Check if input contains Aurebesh words
const aurebeshWords = Object.values(window.transforms.aurebesh.map);
const hasAurebeshWords = aurebeshWords.some(word =>
input.toLowerCase().includes(word.toLowerCase())
);
if (hasAurebeshWords) {
const result = window.transforms.aurebesh.reverse(input);
if (result !== input && /[a-zA-Z0-9]/.test(result)) {
return { text: result, method: 'Aurebesh (Star Wars)' };
}
}
} catch (e) {
console.error('Aurebesh decode error:', e);
}
}
// - Check for Ancient Scripts
const ancientScriptChecks = [
{ name: 'Hieroglyphics', transform: 'hieroglyphics' },
{ name: 'Ogham (Celtic)', transform: 'ogham' },
{ name: 'Elder Futhark', transform: 'elder_futhark' }
];
for (const script of ancientScriptChecks) {
if (window.transforms[script.transform] && window.transforms[script.transform].map) {
try {
// Create reverse mapping
const reverseMap = {};
for (const [key, value] of Object.entries(window.transforms[script.transform].map)) {
reverseMap[value] = key;
}
// Check if input contains characters from this script
const scriptChars = Object.values(window.transforms[script.transform].map);
const hasScriptChars = scriptChars.some(char => input.includes(char));
if (hasScriptChars) {
// Decode text
let result = '';
for (const char of input) {
result += reverseMap[char] || char;
}
if (result !== input && /[a-zA-Z0-9]/.test(result)) {
return { text: result, method: script.name };
}
}
} catch (e) {
console.error(`${script.name} decode error:`, e);
}
}
}
// - Check for Technical Codes
const technicalCodeChecks = [
{ name: 'Mathematical Notation', transform: 'mathematical' },
{ name: 'Chemical Symbols', transform: 'chemical' }
];
for (const code of technicalCodeChecks) {
if (window.transforms[code.transform] && window.transforms[code.transform].map) {
try {
// Create reverse mapping
const reverseMap = {};
for (const [key, value] of Object.entries(window.transforms[code.transform].map)) {
reverseMap[value] = key;
}
// Check if input contains characters from this code
const codeChars = Object.values(window.transforms[code.transform].map);
const hasCodeChars = codeChars.some(char => input.includes(char));
if (hasCodeChars) {
// Decode text
let result = '';
for (const char of input) {
result += reverseMap[char] || char;
}
if (result !== input && /[a-zA-Z0-9]/.test(result)) {
return { text: result, method: code.name };
}
}
} catch (e) {
console.error(`${code.name} decode error:`, e);
}
}
}
// - Check for Brainfuck (special case - look for brainfuck patterns)
if (window.transforms.brainfuck) {
try {
// Brainfuck typically contains lots of +, -, <, >, [, ], ., and ,
const brainfuckPattern = /^[+\-<>\[\].,\s]+$/;
if (brainfuckPattern.test(input.trim()) && input.length > 20) {
// This looks like brainfuck code, but we can't easily reverse it
// Just indicate that it was detected
return { text: '[Brainfuck code detected - cannot decode]', method: 'Brainfuck' };
}
} catch (e) {
console.error('Brainfuck detection error:', e);
}
}
// - Check for Semaphore Flags (special case - look for flag emojis)
if (window.transforms.semaphore) {
try {
// Look for flag-like characters or emojis
const flagPattern = /[๐๐ฉ๐๐ด๐ณ๏ธ]/;
if (flagPattern.test(input)) {
return { text: '[Semaphore flags detected]', method: 'Semaphore Flags' };
}
} catch (e) {
console.error('Semaphore detection 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]{3,}/.test(result)) {
return { text: result, method: transform.name };
}
} catch (e) {
console.error(`Error decoding with ${name}:`, e);
}
}
}
// 4. Mixed/Randomized text decoding (token-wise decoding)
// Split on whitespace and common punctuation, keep separators
const tokens = input.split(/(\s+|[\.,!?:;()\[\]{}])/);
if (tokens.length > 1) {
const decodedTokens = tokens.map(tok => {
// Skip separators
if (!tok || /^(\s+|[\.,!?:;()\[\]{}])$/.test(tok)) return tok;
// Try specific pattern checks first for token
const quick = this.universalDecode(tok);
if (quick && quick.text) return quick.text;
// Fallback: try all reverses for token
for (const name in window.transforms) {
const transform = window.transforms[name];
if (transform.reverse) {
try {
const r = transform.reverse(tok);
if (r && r !== tok && /[a-zA-Z0-9\s]{1,}/.test(r)) return r;
} catch (_) {}
}
}
return tok;
});
const joined = decodedTokens.join('');
if (joined !== input && /[a-zA-Z0-9\s]{3,}/.test(joined)) {
return { text: joined, method: 'Mixed (token-wise)' };
}
}
return null;
},
// Emoji Library Methods
filterEmojis() {
// Always show all emojis - search functionality removed
this.filteredEmojis = [...window.emojiLibrary.EMOJI_LIST];
this.renderEmojiGrid();
},
selectEmoji(emoji) {
// Directly copy the emoji to clipboard - ensure it's a string
const emojiStr = String(emoji);
// Special handling for emoji characters
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(emojiStr)
.then(() => {
this.showNotification(` Emoji copied!`, 'success');
this.addToCopyHistory('Emoji', emojiStr);
})
.catch(err => {
console.warn('Emoji clipboard API failed:', err);
// Fallback to our custom method
this.forceCopyToClipboard(emojiStr);
this.showNotification(` Emoji copied!`, 'success');
this.addToCopyHistory('Emoji', emojiStr);
});
} else {
// Use our custom method if Clipboard API not available
this.forceCopyToClipboard(emojiStr);
this.showNotification(` Emoji copied!`, 'success');
this.addToCopyHistory('Emoji', emojiStr);
}
// Also set up carrier if we're in steganography mode
if (this.activeTab === 'steganography') {
this.selectedEmoji = emoji;
// 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 we have one
if (this.emojiMessage) {
this.autoEncode();
// Wait for encoding to complete, then copy to clipboard
this.$nextTick(() => {
if (this.encodedMessage) {
const encodedStr = String(this.encodedMessage);
// Use native clipboard API first for better emoji support
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(encodedStr)
.then(() => {
this.showNotification(` Hidden message copied with ${emoji}`, 'success');
this.addToCopyHistory(`Hidden Message with ${emoji}`, encodedStr);
})
.catch(err => {
console.warn('Encoded emoji clipboard API failed:', err);
// Fall back to our custom method
this.forceCopyToClipboard(encodedStr);
this.showNotification(` Hidden message copied with ${emoji}`, 'success');
this.addToCopyHistory(`Hidden Message with ${emoji}`, encodedStr);
});
} else {
// Use our custom method if Clipboard API not available
this.forceCopyToClipboard(encodedStr);
this.showNotification(` Hidden message copied with ${emoji}`, 'success');
this.addToCopyHistory(`Hidden Message with ${emoji}`, encodedStr);
}
}
});
}
}
},
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);
// Message about copying has been removed as requested
// Log success
console.log('Emoji grid rendered successfully');
},
// Initialize category navigation for transform sections
initializeCategoryNavigation() {
this.$nextTick(() => {
console.log('Initializing category navigation');
const legendItems = document.querySelectorAll('.transform-category-legend .legend-item');
// First, remove any existing event listeners to prevent duplicates
legendItems.forEach(item => {
const newItem = item.cloneNode(true);
item.parentNode.replaceChild(newItem, item);
});
// Now add event listeners to the fresh elements
document.querySelectorAll('.transform-category-legend .legend-item').forEach(item => {
item.addEventListener('click', () => {
const targetId = item.getAttribute('data-target');
if (targetId) {
const targetElement = document.getElementById(targetId);
if (targetElement) {
// Add active class to the clicked legend item
document.querySelectorAll('.transform-category-legend .legend-item').forEach(li => {
li.classList.remove('active-category');
});
item.classList.add('active-category');
// Jump directly to the target element
targetElement.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
// Highlight the section briefly to draw attention
targetElement.classList.add('highlight-section');
setTimeout(() => {
targetElement.classList.remove('highlight-section');
}, 1000);
}
}
});
});
});
}
,
// Token Bomb Generator Logic
generateTokenBomb() {
const depth = Math.max(1, Math.min(8, Number(this.tbDepth) || 1));
const breadth = Math.max(1, Math.min(12, Number(this.tbBreadth) || 1));
const repeats = Math.max(1, Math.min(100, Number(this.tbRepeats) || 1));
const sep = this.tbSeparator === 'zwj' ? '\u200D' : this.tbSeparator === 'zwnj' ? '\u200C' : this.tbSeparator === 'zwsp' ? '\u200B' : '';
const includeVS = !!this.tbIncludeVS;
const includeNoise = !!this.tbIncludeNoise;
const randomize = !!this.tbRandomizeEmojis;
const emojiList = this.filteredEmojis && this.filteredEmojis.length ? this.filteredEmojis : window.emojiLibrary.EMOJI_LIST;
function pickEmojis(count) {
const out = [];
for (let i = 0; i < count; i++) {
const idx = randomize ? Math.floor(Math.random() * emojiList.length) : (i % emojiList.length);
out.push(String(emojiList[idx]));
}
return out;
}
function addVS(str) {
if (!includeVS) return str;
// Alternate VS16/VS15 to maximize tokenization churn
const vs16 = '\uFE0F';
const vs15 = '\uFE0E';
let out = '';
for (let i = 0; i < str.length; i++) {
const ch = str[i];
out += ch + (i % 2 === 0 ? vs16 : vs15);
}
return out;
}
function noise() {
if (!includeNoise) return '';
const parts = ['\u200B','\u200C','\u200D','\u2060','\u2062','\u2063'];
let s = '';
const n = 1 + Math.floor(Math.random() * 3);
for (let i = 0; i < n; i++) s += parts[Math.floor(Math.random() * parts.length)];
return s;
}
function buildLevel(level) {
if (level === 0) {
const base = pickEmojis(breadth).join('');
return addVS(base);
}
const items = [];
for (let i = 0; i < breadth; i++) {
const inner = buildLevel(level - 1);
items.push(inner + noise());
}
return items.join(sep);
}
if (this.tbSingleCarrier) {
const manual = (this.tbCarrierManual || '').trim();
const carrier = manual || (this.tbCarrier && String(this.tbCarrier)) || (this.selectedEmoji ? String(this.selectedEmoji) : '๐ฅ');
function countUnits(level) {
if (level === 0) return breadth;
return breadth * countUnits(level - 1);
}
const unitsPerBlock = countUnits(depth - 1);
const totalUnits = Math.max(1, repeats * unitsPerBlock);
let payload = [];
payload = pickEmojis(totalUnits);
function toTagSeqForEmojiChar(ch) {
const cp = ch.codePointAt(0);
const hex = cp.toString(16);
let seq = '';
for (const d of hex) {
if (d >= '0' && d <= '9') {
const base = 0xE0030 + (d.charCodeAt(0) - '0'.charCodeAt(0));
seq += String.fromCodePoint(base);
} else {
const base = 0xE0061 + (d.charCodeAt(0) - 'a'.charCodeAt(0));
seq += String.fromCodePoint(base);
}
}
seq += String.fromCodePoint(0xE007F);
return seq;
}
const vs16 = includeVS ? '\uFE0F' : '';
let out = carrier + vs16;
for (let i = 0; i < payload.length; i++) {
out += sep + toTagSeqForEmojiChar(payload[i]) + noise();
}
this.tokenBombOutput = out;
} else {
let block = buildLevel(depth - 1);
// Repeat the block to increase token length
const blocks = [];
for (let i = 0; i < repeats; i++) {
blocks.push(block + noise());
}
this.tokenBombOutput = blocks.join(sep);
}
// Provide a quick visual confirmation
this.showNotification(' Tokenade generated', 'success');
if (this.tbAutoCopy && this.tokenBombOutput) {
this.$nextTick(() => this.copyToClipboard(this.tokenBombOutput));
}
},
applyTokenadePreset(preset) {
if (preset === 'feather') {
this.tbDepth = 1; this.tbBreadth = 3; this.tbRepeats = 2; this.tbSeparator = 'zwnj';
this.tbIncludeVS = false; this.tbIncludeNoise = false; this.tbRandomizeEmojis = true;
} else if (preset === 'light') {
this.tbDepth = 2; this.tbBreadth = 3; this.tbRepeats = 3; this.tbSeparator = 'zwnj';
this.tbIncludeVS = false; this.tbIncludeNoise = true; this.tbRandomizeEmojis = true;
} else if (preset === 'middle') {
this.tbDepth = 3; this.tbBreadth = 4; this.tbRepeats = 6; this.tbSeparator = 'zwnj';
this.tbIncludeVS = true; this.tbIncludeNoise = true; this.tbRandomizeEmojis = true;
} else if (preset === 'heavy') {
this.tbDepth = 4; this.tbBreadth = 6; this.tbRepeats = 12; this.tbSeparator = 'zwnj';
this.tbIncludeVS = true; this.tbIncludeNoise = true; this.tbRandomizeEmojis = true;
} else if (preset === 'super') {
this.tbDepth = 5; this.tbBreadth = 8; this.tbRepeats = 18; this.tbSeparator = 'zwnj';
this.tbIncludeVS = true; this.tbIncludeNoise = true; this.tbRandomizeEmojis = true;
}
this.showNotification(' Preset applied', 'success');
},
// Live estimator for pre-generation length
estimateTokenadeLength() {
const depth = Math.max(1, Math.min(8, Number(this.tbDepth) || 1));
const breadth = Math.max(1, Math.min(12, Number(this.tbBreadth) || 1));
const repeats = Math.max(1, Math.min(100, Number(this.tbRepeats) || 1));
const sepLen = this.tbSeparator === 'none' ? 0 : 1;
const vsPerEmoji = this.tbIncludeVS ? 1 : 0;
const noiseAvg = this.tbIncludeNoise ? 2 : 0;
function lenLevel(level) {
if (level === 0) {
return breadth * (1 + vsPerEmoji);
}
const inner = lenLevel(level - 1);
return breadth * (inner + noiseAvg) + Math.max(0, breadth - 1) * sepLen;
}
if (this.tbSingleCarrier) {
function countUnits(level) { return level === 0 ? breadth : breadth * countUnits(level - 1); }
const unitsPerBlock = countUnits(depth - 1);
const totalUnits = Math.max(1, repeats * unitsPerBlock);
const avgDigits = 5; // avg hex digits in tag sequence
const perUnit = avgDigits + 1 + sepLen + (this.tbIncludeNoise ? 2 : 0); // tags+term + sep + noise
const carrierLen = 1 + (this.tbIncludeVS ? 1 : 0);
return carrierLen + totalUnits * perUnit;
} else {
const blockLen = lenLevel(depth - 1);
return repeats * (blockLen + noiseAvg) + Math.max(0, repeats - 1) * sepLen;
}
},
setCarrierFromSelected() {
if (this.selectedEmoji) this.tbCarrier = String(this.selectedEmoji);
},
clearTokenadePayload() { this.tbPayloadEmojis = []; },
removeTokenadePayloadAt(idx) { this.tbPayloadEmojis.splice(idx, 1); },
onCarrierInput() {
const q = (this.tbCarrier || '').trim();
if (!q) {
this.carrierEmojiList = [...window.emojiLibrary.EMOJI_LIST];
return;
}
// Filter emoji list by simple name guess or by including the character; if q contains an emoji, keep it
const list = window.emojiLibrary.EMOJI_LIST;
const byChar = list.filter(e => e.includes(q));
// Also support colon-like query (e.g., ':heart') by rough keywords
const keywords = {
heart: ['โค๏ธ','๐','๐','๐','๐','๐','๐','๐','๐'],
star: ['โญ','๐'],
fire: ['๐ฅ'],
bomb: ['๐ฃ'],
snake: ['๐'],
dragon: ['๐','๐ฒ'],
skull: ['๐'],
sparkles: ['โจ'],
moon: ['๐','๐','๐','๐','๐','๐','๐','๐','๐']
};
let byKey = [];
const qk = q.replace(/[:_\s]/g,'').toLowerCase();
Object.keys(keywords).forEach(k => {
if (k.indexOf(qk) !== -1) byKey = byKey.concat(keywords[k]);
});
const merged = Array.from(new Set([...byChar, ...byKey]));
this.carrierEmojiList = merged.length ? merged : [...window.emojiLibrary.EMOJI_LIST];
},
handleTokenadeDrop(e) {
e.preventDefault();
const text = e.dataTransfer && (e.dataTransfer.getData('text/plain') || e.dataTransfer.getData('text'));
if (!text) return;
const parts = window.emojiLibrary.splitEmojis(text);
const onlyEmojis = parts.filter(p => /\p{Extended_Pictographic}/u.test(p));
this.tbPayloadEmojis.push(...onlyEmojis);
},
handleTokenadePaste(e) {
const text = (e.clipboardData && e.clipboardData.getData('text')) || '';
if (!text) return;
const parts = window.emojiLibrary.splitEmojis(text);
const onlyEmojis = parts.filter(p => /\p{Extended_Pictographic}/u.test(p));
this.tbPayloadEmojis.push(...onlyEmojis);
}
,
// Tokenizer visualization
runTokenizer() {
const text = this.tokenizerInput || '';
const engine = this.tokenizerEngine;
const tokens = [];
if (!text) { this.tokenizerTokens = []; this.tokenizerCharCount = 0; this.tokenizerWordCount = 0; return; }
if (engine === 'byte') {
// Split into UTF-8 bytes, display hex and glyphs
const encoder = new TextEncoder();
const bytes = encoder.encode(text);
for (let i=0;i Text payload generated', 'success');
}
},
// Initialize theme and components
mounted() {
console.log('Vue app mounted');
// Apply theme
if (this.isDarkTheme) {
document.body.classList.add('dark-theme');
}
// Initialize category navigation
this.initializeCategoryNavigation();
// Initialize emoji grid with all emojis shown by default
this.$nextTick(() => {
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 = () => {
// Only try to initialize when steganography tab is active
if (this.activeTab !== 'steganography') {
return;
}
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;');
// 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: 20px; overflow: visible;');
}
// Now render the grid
this.renderEmojiGrid();
console.log('Emoji grid rendering complete in mounted()');
// Stop retrying once we've successfully found and rendered the grid
clearInterval(emojiGridInitializer);
} else {
console.log('emoji-grid-container not found, will retry when steganography tab is active');
}
};
// Use an interval instead of recursive setTimeout for more reliable initialization
// This will try every 500ms until it succeeds or the page is navigated away from
const emojiGridInitializer = setInterval(initializeEmojiGrid, 500);
// Set up paste event handlers for all textareas to prevent unwanted clipboard notifications
this.setupPasteHandlers();
});
},
// 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 proper focus handling
watch: {
// Watch transform input to update transforms
transformInput() {
// Only auto-transform if we have an active transform
if (this.activeTransform && this.activeTab === 'transforms') {
this.transformOutput = this.activeTransform.func(this.transformInput);
}
},
// Make sure emoji list stays loaded when user types in any input
emojiMessage() {
this.filteredEmojis = [...window.emojiLibrary.EMOJI_LIST];
this.$nextTick(() => {
this.renderEmojiGrid();
});
},
// Also watch the decode input field for typing activity
decodeInput() {
this.filteredEmojis = [...window.emojiLibrary.EMOJI_LIST];
this.$nextTick(() => {
this.renderEmojiGrid();
});
}
}
});