mirror of
https://github.com/elder-plinius/P4RS3LT0NGV3.git
synced 2026-07-14 16:27:18 +02:00
Fixed emoji copying and category navigation, added Greek transform
This commit is contained in:
+20
-9
@@ -23,15 +23,12 @@
|
||||
--format-color: #ffb74d; /* Orange for formatting */
|
||||
--unicode-color: #42a5f5; /* Blue for unicode transformations */
|
||||
--special-color: #66bb6a; /* Green for special transformations */
|
||||
scroll-behavior: smooth;
|
||||
scroll-padding-top: 20px;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
body {
|
||||
@@ -885,7 +882,12 @@ button:hover {
|
||||
transition: all 0.2s ease;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.legend-item:hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
|
||||
background-color: var(--button-hover-bg);
|
||||
}
|
||||
|
||||
.legend-item.active-category {
|
||||
@@ -895,11 +897,6 @@ button:hover {
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.legend-item:hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.transform-section:focus-within {
|
||||
border-color: var(--accent-color);
|
||||
box-shadow: var(--focus-shadow);
|
||||
@@ -932,6 +929,20 @@ button:hover {
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.highlight-section {
|
||||
background-color: rgba(var(--accent-color-rgb), 0.1);
|
||||
box-shadow: 0 0 12px rgba(var(--accent-color-rgb), 0.3);
|
||||
animation: pulse 1s ease-in-out;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0% { background-color: rgba(var(--accent-color-rgb), 0.1); }
|
||||
50% { background-color: rgba(var(--accent-color-rgb), 0.2); }
|
||||
100% { background-color: rgba(var(--accent-color-rgb), 0.1); }
|
||||
}
|
||||
|
||||
.category-title {
|
||||
|
||||
+10
@@ -368,6 +368,13 @@
|
||||
</div>
|
||||
|
||||
<div class="output-section" v-if="transformOutput">
|
||||
<div class="output-heading">
|
||||
<h4>
|
||||
<i class="fas fa-check-circle"></i>
|
||||
Transformed Message
|
||||
<small v-if="activeTransform">({{ activeTransform.name }})</small>
|
||||
</h4>
|
||||
</div>
|
||||
<div class="output-container">
|
||||
<textarea
|
||||
readonly
|
||||
@@ -378,6 +385,9 @@
|
||||
<i class="fas fa-copy"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="output-instructions">
|
||||
<small><i class="fas fa-info-circle"></i> Copy this text and share it. The transformation can be reversed using the Universal Decoder below.</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Universal Decoder Section for Transforms Tab -->
|
||||
|
||||
@@ -16,9 +16,9 @@ window.app = new Vue({
|
||||
transformCategories: {
|
||||
encoding: ['Base64', 'Base32', 'Binary', 'Hexadecimal', 'ASCII85', 'URL Encode', 'HTML Entities'],
|
||||
cipher: ['Caesar Cipher', 'ROT13', 'ROT47', 'Morse Code'],
|
||||
visual: ['Rainbow Text', 'Strikethrough', 'Underline', 'Reverse Text', 'Wingdings Style'],
|
||||
format: ['Pig Latin', 'Leetspeak', 'NATO Phonetic', 'Greek Letters'],
|
||||
unicode: ['Invisible Text', 'Upside Down', 'Full Width', 'Small Caps', 'Bubble', 'Braille'],
|
||||
visual: ['Rainbow Text', 'Strikethrough', 'Underline', 'Reverse Text'],
|
||||
format: ['Pig Latin', 'Leetspeak', 'NATO Phonetic'],
|
||||
unicode: ['Invisible Text', 'Upside Down', 'Full Width', 'Small Caps', 'Bubble', 'Braille', 'Greek Letters', 'Wingdings'],
|
||||
special: ['Medieval', 'Cursive', 'Monospace', 'Double-Struck', 'Elder Futhark', 'Mirror Text', 'Zalgo']
|
||||
},
|
||||
transforms: Object.entries(window.transforms).map(([key, transform]) => ({
|
||||
@@ -80,6 +80,13 @@ window.app = new Vue({
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Initialize category navigation when switching to transforms tab
|
||||
if (tabName === 'transforms') {
|
||||
this.$nextTick(() => {
|
||||
this.initializeCategoryNavigation();
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
// Get transforms grouped by category
|
||||
@@ -739,25 +746,11 @@ window.app = new Vue({
|
||||
universalDecode(input) {
|
||||
if (!input) return '';
|
||||
|
||||
// If we're in the steganography tab, only try emoji decoding
|
||||
if (this.activeTab === 'steganography') {
|
||||
if (/[\u{1F300}-\u{1F6FF}\u{2600}-\u{26FF}]/u.test(input)) {
|
||||
console.log('In emoji tab: attempting emoji decode only...');
|
||||
const decoded = window.steganography.decodeEmoji(input);
|
||||
if (decoded) {
|
||||
console.log('Successfully decoded emoji:', decoded);
|
||||
return { text: decoded, method: 'Emoji Steganography' };
|
||||
} else {
|
||||
console.log('No hidden message found in emoji');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
// Try all decoders in order
|
||||
|
||||
// For other tabs, 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);
|
||||
@@ -1161,10 +1154,29 @@ window.app = new Vue({
|
||||
},
|
||||
|
||||
selectEmoji(emoji) {
|
||||
// Directly copy the emoji to clipboard
|
||||
this.forceCopyToClipboard(emoji);
|
||||
this.showNotification(`<i class="fas fa-check"></i> Emoji copied!`, 'success');
|
||||
this.addToCopyHistory('Emoji', 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(`<i class="fas fa-check"></i> 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(`<i class="fas fa-check"></i> Emoji copied!`, 'success');
|
||||
this.addToCopyHistory('Emoji', emojiStr);
|
||||
});
|
||||
} else {
|
||||
// Use our custom method if Clipboard API not available
|
||||
this.forceCopyToClipboard(emojiStr);
|
||||
this.showNotification(`<i class="fas fa-check"></i> Emoji copied!`, 'success');
|
||||
this.addToCopyHistory('Emoji', emojiStr);
|
||||
}
|
||||
|
||||
// Also set up carrier if we're in steganography mode
|
||||
if (this.activeTab === 'steganography') {
|
||||
@@ -1190,9 +1202,28 @@ window.app = new Vue({
|
||||
// Wait for encoding to complete, then copy to clipboard
|
||||
this.$nextTick(() => {
|
||||
if (this.encodedMessage) {
|
||||
this.forceCopyToClipboard(this.encodedMessage);
|
||||
this.showNotification(`<i class="fas fa-check"></i> Hidden message copied with ${emoji}`, 'success');
|
||||
this.addToCopyHistory(`Hidden Message with ${emoji}`, 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(`<i class="fas fa-check"></i> 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(`<i class="fas fa-check"></i> 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(`<i class="fas fa-check"></i> Hidden message copied with ${emoji}`, 'success');
|
||||
this.addToCopyHistory(`Hidden Message with ${emoji}`, encodedStr);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1228,6 +1259,49 @@ window.app = new Vue({
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
// Initialize theme and components
|
||||
@@ -1238,29 +1312,8 @@ window.app = new Vue({
|
||||
document.body.classList.add('dark-theme');
|
||||
}
|
||||
|
||||
// Add smooth scrolling for category navigation
|
||||
this.$nextTick(() => {
|
||||
const legendItems = document.querySelectorAll('.transform-category-legend .legend-item');
|
||||
legendItems.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
|
||||
legendItems.forEach(li => li.classList.remove('active-category'));
|
||||
item.classList.add('active-category');
|
||||
|
||||
// Scroll to the target element with smooth behavior
|
||||
targetElement.scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: 'start'
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
// Initialize category navigation
|
||||
this.initializeCategoryNavigation();
|
||||
|
||||
// Initialize emoji grid with all emojis shown by default
|
||||
this.$nextTick(() => {
|
||||
|
||||
+72
-48
@@ -804,54 +804,6 @@ const transforms = {
|
||||
}
|
||||
},
|
||||
|
||||
greek: {
|
||||
name: 'Greek Letters',
|
||||
map: {
|
||||
'A': 'Α', 'B': 'Β', 'G': 'Γ', 'D': 'Δ', 'E': 'Ε',
|
||||
'Z': 'Ζ', 'H': 'Η', 'TH': 'Θ', 'I': 'Ι', 'K': 'Κ',
|
||||
'L': 'Λ', 'M': 'Μ', 'N': 'Ν', 'X': 'Ξ', 'O': 'Ο',
|
||||
'P': 'Π', 'R': 'Ρ', 'S': 'Σ', 'T': 'Τ', 'Y': 'Υ',
|
||||
'F': 'Φ', 'CH': 'Χ', 'PS': 'Ψ', 'W': 'Ω',
|
||||
'a': 'α', 'b': 'β', 'g': 'γ', 'd': 'δ', 'e': 'ε',
|
||||
'z': 'ζ', 'h': 'η', 'th': 'θ', 'i': 'ι', 'k': 'κ',
|
||||
'l': 'λ', 'm': 'μ', 'n': 'ν', 'x': 'ξ', 'o': 'ο',
|
||||
'p': 'π', 'r': 'ρ', 's': 'σ', 't': 'τ', 'y': 'υ',
|
||||
'f': 'φ', 'ch': 'χ', 'ps': 'ψ', 'w': 'ω'
|
||||
},
|
||||
func: function(text) {
|
||||
return text.replace(/ch|th|ps|[a-zA-Z]/g, match => this.map[match] || match);
|
||||
},
|
||||
preview: function(text) {
|
||||
return this.func(text);
|
||||
},
|
||||
reverse: function(text) {
|
||||
const reverseMap = {};
|
||||
Object.entries(this.map).forEach(([k, v]) => reverseMap[v] = k);
|
||||
return text.replace(/[Α-Ωα-ω]/g, match => reverseMap[match] || match);
|
||||
}
|
||||
},
|
||||
|
||||
wingdings: {
|
||||
name: 'Wingdings Style',
|
||||
map: {
|
||||
'a': '📧', 'b': '📎', 'c': '💿', 'd': '📁', 'e': '✉️',
|
||||
'f': '📂', 'g': '✂️', 'h': '🔨', 'i': '💡', 'j': '🔑',
|
||||
'k': '🔒', 'l': '🔓', 'm': '🖱️', 'n': '💻', 'o': '⭕',
|
||||
'p': '📱', 'q': '❓', 'r': '🌹', 's': '⭐', 't': '📌',
|
||||
'u': '☂️', 'v': '✌️', 'w': '🌊', 'x': '❌', 'y': '☯️',
|
||||
'z': '💤', '1': '☝️', '2': '✌️', '3': '🤟', '4': '4️⃣',
|
||||
'5': '5️⃣', '6': '6️⃣', '7': '7️⃣', '8': '8️⃣', '9': '9️⃣',
|
||||
'0': '0️⃣', '!': '❗', '?': '❓', '.': '•', ',': '،',
|
||||
' ': ' '
|
||||
},
|
||||
func: function(text) {
|
||||
return text.toLowerCase().split('').map(char => this.map[char] || char).join('');
|
||||
},
|
||||
preview: function(text) {
|
||||
return this.func(text);
|
||||
}
|
||||
},
|
||||
|
||||
base32: {
|
||||
name: 'Base32',
|
||||
alphabet: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567',
|
||||
@@ -919,6 +871,78 @@ const transforms = {
|
||||
|
||||
return result;
|
||||
}
|
||||
},
|
||||
|
||||
greek: {
|
||||
name: 'Greek Letters',
|
||||
map: {
|
||||
'a': 'α', 'b': 'β', 'c': 'χ', 'd': 'δ', 'e': 'ε', 'f': 'φ', 'g': 'γ', 'h': 'η',
|
||||
'i': 'ι', 'j': 'ξ', 'k': 'κ', 'l': 'λ', 'm': 'μ', 'n': 'ν', 'o': 'ο', 'p': 'π',
|
||||
'q': 'θ', 'r': 'ρ', 's': 'σ', 't': 'τ', 'u': 'υ', 'v': 'ς', 'w': 'ω', 'x': 'χ',
|
||||
'y': 'ψ', 'z': 'ζ',
|
||||
'A': 'Α', 'B': 'Β', 'C': 'Χ', 'D': 'Δ', 'E': 'Ε', 'F': 'Φ', 'G': 'Γ', 'H': 'Η',
|
||||
'I': 'Ι', 'J': 'Ξ', 'K': 'Κ', 'L': 'Λ', 'M': 'Μ', 'N': 'Ν', 'O': 'Ο', 'P': 'Π',
|
||||
'Q': 'Θ', 'R': 'Ρ', 'S': 'Σ', 'T': 'Τ', 'U': 'Υ', 'V': 'ς', 'W': 'Ω', 'X': 'Χ',
|
||||
'Y': 'Ψ', 'Z': 'Ζ'
|
||||
},
|
||||
func: function(text) {
|
||||
return text.split('').map(char => this.map[char] || char).join('');
|
||||
},
|
||||
preview: function(text) {
|
||||
return text.substring(0, 10) + (text.length > 10 ? '...' : '');
|
||||
},
|
||||
reverseMap: function() {
|
||||
if (!this._reverseMap) {
|
||||
this._reverseMap = {};
|
||||
for (let key in this.map) {
|
||||
this._reverseMap[this.map[key]] = key;
|
||||
}
|
||||
}
|
||||
return this._reverseMap;
|
||||
},
|
||||
reverse: function(text) {
|
||||
const revMap = this.reverseMap();
|
||||
return text.split('').map(char => revMap[char] || char).join('');
|
||||
}
|
||||
},
|
||||
|
||||
wingdings: {
|
||||
name: 'Wingdings',
|
||||
map: {
|
||||
'a': '♋', 'b': '♌', 'c': '♍', 'd': '♎', 'e': '♏', 'f': '♐', 'g': '♑', 'h': '♒',
|
||||
'i': '♓', 'j': '⛎', 'k': '☀', 'l': '☁', 'm': '☂', 'n': '☃', 'o': '☄', 'p': '★',
|
||||
'q': '☆', 'r': '☇', 's': '☈', 't': '☉', 'u': '☊', 'v': '☋', 'w': '☌', 'x': '☍',
|
||||
'y': '☎', 'z': '☏',
|
||||
'A': '♠', 'B': '♡', 'C': '♢', 'D': '♣', 'E': '♤', 'F': '♥', 'G': '♦', 'H': '♧',
|
||||
'I': '♨', 'J': '♩', 'K': '♪', 'L': '♫', 'M': '♬', 'N': '♭', 'O': '♮', 'P': '♯',
|
||||
'Q': '✁', 'R': '✂', 'S': '✃', 'T': '✄', 'U': '✆', 'V': '✇', 'W': '✈', 'X': '✉',
|
||||
'Y': '✌', 'Z': '✍',
|
||||
'0': '✓', '1': '✔', '2': '✕', '3': '✖', '4': '✗', '5': '✘', '6': '✙', '7': '✚',
|
||||
'8': '✛', '9': '✜',
|
||||
'.': '✠', ',': '✡', '?': '✢', '!': '✣', '@': '✤', '#': '✥', '$': '✦', '%': '✧',
|
||||
'^': '✩', '&': '✪', '*': '✫', '(': '✬', ')': '✭', '-': '✮', '_': '✯', '=': '✰',
|
||||
'+': '✱', '[': '✲', ']': '✳', '{': '✴', '}': '✵', '|': '✶', '\\': '✷', ';': '✸',
|
||||
':': '✹', '"': '✺', '\'': '✻', '<': '✼', '>': '✽', '/': '✾', '~': '✿', '`': '❀'
|
||||
},
|
||||
func: function(text) {
|
||||
return text.split('').map(char => this.map[char] || char).join('');
|
||||
},
|
||||
preview: function(text) {
|
||||
return text.substring(0, 10) + (text.length > 10 ? '...' : '');
|
||||
},
|
||||
reverseMap: function() {
|
||||
if (!this._reverseMap) {
|
||||
this._reverseMap = {};
|
||||
for (let key in this.map) {
|
||||
this._reverseMap[this.map[key]] = key;
|
||||
}
|
||||
}
|
||||
return this._reverseMap;
|
||||
},
|
||||
reverse: function(text) {
|
||||
const revMap = this.reverseMap();
|
||||
return text.split('').map(char => revMap[char] || char).join('');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user