mirror of
https://github.com/elder-plinius/P4RS3LT0NGV3.git
synced 2026-02-12 16:52:46 +00:00
- Implement tool registry system with individual tool modules - Reorganize transformers into categorized source modules - Remove emojiLibrary.js, consolidate into EmojiUtils and emojiData - Fix mobile close button and tooltip functionality - Add build system for transforms and emoji data - Migrate from Python backend to pure JavaScript - Add comprehensive documentation and testing - Improve code organization and maintainability - Ignore generated files (transforms-bundle.js, emojiData.js)
423 lines
17 KiB
JavaScript
423 lines
17 KiB
JavaScript
const baseData = {
|
|
isDarkTheme: true,
|
|
activeTab: 'transforms',
|
|
registeredTools: [],
|
|
universalDecodeInput: '',
|
|
universalDecodeResult: null,
|
|
isPasteOperation: false,
|
|
lastCopyTime: 0,
|
|
ignoreKeyboardEvents: false,
|
|
isTransformCopy: false,
|
|
keyboardEventsTimeout: null,
|
|
showDecoder: true,
|
|
tbCarrierManual: '',
|
|
copyHistory: [],
|
|
maxHistoryItems: window.CONFIG.MAX_HISTORY_ITEMS,
|
|
showCopyHistory: false,
|
|
showUnicodePanel: false,
|
|
unicodeApplyBusy: false,
|
|
unicodeApplyFlash: false,
|
|
unicodePanelToggleLock: false,
|
|
unicodeApplyFlashTimeout: null,
|
|
showDangerModal: false,
|
|
dangerThresholdTokens: window.CONFIG.DANGER_THRESHOLD_TOKENS
|
|
};
|
|
|
|
const toolData = (window.toolRegistry && typeof window.toolRegistry.mergeVueData === 'function')
|
|
? window.toolRegistry.mergeVueData()
|
|
: {};
|
|
const mergedData = Object.assign({}, baseData, toolData);
|
|
|
|
const toolMethods = (window.toolRegistry && typeof window.toolRegistry.mergeVueMethods === 'function')
|
|
? window.toolRegistry.mergeVueMethods()
|
|
: {};
|
|
|
|
window.app = new Vue({
|
|
el: '#app',
|
|
data: mergedData,
|
|
methods: Object.assign({}, toolMethods || {}, {
|
|
toggleUnicodePanel(event) {
|
|
if (this.unicodePanelToggleLock) return;
|
|
this.unicodePanelToggleLock = true;
|
|
|
|
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');
|
|
}
|
|
|
|
setTimeout(() => {
|
|
this.unicodePanelToggleLock = false;
|
|
}, 300);
|
|
},
|
|
applyUnicodeOptions() {
|
|
if (this.unicodeApplyBusy) return;
|
|
|
|
if (this.unicodeApplyFlashTimeout) {
|
|
clearTimeout(this.unicodeApplyFlashTimeout);
|
|
this.unicodeApplyFlashTimeout = null;
|
|
}
|
|
|
|
this.unicodeApplyFlash = false;
|
|
this.unicodeApplyBusy = true;
|
|
|
|
try {
|
|
const initSel = document.querySelector('.steg-initial-presentation');
|
|
const vs0Sel = document.querySelector('.steg-vs-zero');
|
|
const vs1Sel = document.querySelector('.steg-vs-one');
|
|
const zwSel = document.querySelector('.steg-inter-zw');
|
|
const everyInput = document.querySelector('.steg-inter-every');
|
|
const orderSel = document.querySelector('.steg-bit-order');
|
|
const trailSel = document.querySelector('.steg-trailing-zw');
|
|
|
|
const parseEsc = (s) => window.EscapeParser.parseEscapeSequence(s);
|
|
|
|
if (window.steganography && window.steganography.setStegOptions) {
|
|
window.steganography.setStegOptions({
|
|
initialPresentation: (initSel && initSel.value) || 'none',
|
|
bitZeroVS: parseEsc(vs0Sel && vs0Sel.value) || '\ufe0e',
|
|
bitOneVS: parseEsc(vs1Sel && vs1Sel.value) || '\ufe0f',
|
|
interBitZW: parseEsc(zwSel && zwSel.value) || null,
|
|
interBitEvery: Math.max(1, Math.min(8, Number((everyInput && everyInput.value) || 1))),
|
|
bitOrder: (orderSel && orderSel.value) || 'msb',
|
|
trailingZW: parseEsc(trailSel && trailSel.value) || ''
|
|
});
|
|
this.unicodeApplyFlash = true;
|
|
this.showNotification('Advanced settings applied', 'success', 'fas fa-sliders-h');
|
|
this.unicodeApplyFlashTimeout = setTimeout(() => {
|
|
this.unicodeApplyFlash = false;
|
|
this.unicodeApplyFlashTimeout = null;
|
|
}, 1200);
|
|
} else {
|
|
this.showNotification('Engine missing setStegOptions()', 'warning', 'fas fa-exclamation-triangle');
|
|
}
|
|
} catch (e) {
|
|
console.error('Apply Unicode options error', e);
|
|
this.showNotification('Failed to apply settings', 'error', 'fas fa-exclamation-triangle');
|
|
this.unicodeApplyFlash = false;
|
|
} finally {
|
|
this.unicodeApplyBusy = false;
|
|
}
|
|
},
|
|
focusWithoutScroll(el) {
|
|
window.FocusUtils.focusWithoutScroll(el);
|
|
},
|
|
|
|
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(_) {}
|
|
},
|
|
switchToTab(tabName) {
|
|
if (this.activeTab && window.toolRegistry) {
|
|
window.toolRegistry.deactivateTool(this.activeTab, this);
|
|
}
|
|
|
|
this.activeTab = tabName;
|
|
this.universalDecodeInput = '';
|
|
this.universalDecodeResult = null;
|
|
|
|
if (window.toolRegistry) {
|
|
window.toolRegistry.activateTool(tabName, this);
|
|
}
|
|
},
|
|
|
|
toggleTheme() {
|
|
this.isDarkTheme = window.ThemeUtils.toggleTheme(this.isDarkTheme);
|
|
},
|
|
|
|
toggleCopyHistory() {
|
|
this.showCopyHistory = !this.showCopyHistory;
|
|
|
|
if (this.showCopyHistory && this.copyHistory.length > 0) {
|
|
this.$nextTick(() => {
|
|
const firstCopyButton = document.querySelector('.copy-again-button');
|
|
if (firstCopyButton) {
|
|
firstCopyButton.focus();
|
|
}
|
|
});
|
|
}
|
|
},
|
|
|
|
addToCopyHistory(source, content) {
|
|
window.HistoryUtils.addToHistory(
|
|
this.copyHistory,
|
|
this.maxHistoryItems,
|
|
source,
|
|
content
|
|
);
|
|
},
|
|
|
|
clearCopyHistory() {
|
|
window.HistoryUtils.clearHistory(this.copyHistory);
|
|
this.showNotification('History cleared', 'success', 'fas fa-check');
|
|
},
|
|
|
|
removeFromCopyHistory(id) {
|
|
window.HistoryUtils.removeFromHistory(this.copyHistory, id);
|
|
this.showNotification('Removed from history', 'success', 'fas fa-check');
|
|
},
|
|
|
|
formatHistoryTime(timestamp) {
|
|
if (!timestamp) return '';
|
|
const date = new Date(timestamp);
|
|
return date.toLocaleString();
|
|
},
|
|
|
|
async copyToClipboard(text, skipHistory = false) {
|
|
if (!text || !window.ClipboardUtils) return;
|
|
|
|
// Check if content already exists in history
|
|
const alreadyInHistory = this.copyHistory.some(item => item.content === text);
|
|
|
|
const source = window.HistoryUtils.getHistorySource(this.activeTab, {
|
|
activeTransform: this.activeTransform,
|
|
activeSteg: this.activeSteg,
|
|
selectedEmoji: this.selectedEmoji
|
|
});
|
|
|
|
const success = await window.ClipboardUtils.copy(text, {
|
|
onSuccess: () => {
|
|
// Only add to history if not skipping and not already in history
|
|
if (!skipHistory && !alreadyInHistory) {
|
|
this.addToCopyHistory(source, text);
|
|
}
|
|
window.FocusUtils.clearFocusAndSelection();
|
|
}
|
|
});
|
|
|
|
return success;
|
|
},
|
|
|
|
forceCopyToClipboard(text) {
|
|
if (!text || !window.ClipboardUtils) return;
|
|
|
|
if (this.isPasteOperation) {
|
|
this.isPasteOperation = false;
|
|
return;
|
|
}
|
|
|
|
if (!this.isTransformCopy && this.ignoreKeyboardEvents) {
|
|
return;
|
|
}
|
|
|
|
const source = window.HistoryUtils.getHistorySource(this.activeTab, {
|
|
activeTransform: this.activeTransform,
|
|
activeSteg: this.activeSteg,
|
|
selectedEmoji: this.selectedEmoji
|
|
});
|
|
|
|
window.ClipboardUtils.copy(text, {
|
|
onSuccess: () => {
|
|
this.addToCopyHistory(source, text);
|
|
if (this.isTransformCopy) {
|
|
this.showCopiedPopup();
|
|
this.ignoreKeyboardEvents = true;
|
|
clearTimeout(this.keyboardEventsTimeout);
|
|
this.keyboardEventsTimeout = setTimeout(() => {
|
|
this.ignoreKeyboardEvents = false;
|
|
}, window.CONFIG.KEYBOARD_EVENTS_TIMEOUT_MS);
|
|
}
|
|
this.isTransformCopy = false;
|
|
const inputBox = document.querySelector('#transform-input');
|
|
if (inputBox) {
|
|
window.FocusUtils.focusWithoutScroll(inputBox);
|
|
const len = inputBox.value.length;
|
|
try { inputBox.setSelectionRange(len, len); } catch (_) {}
|
|
}
|
|
}
|
|
});
|
|
},
|
|
|
|
showNotification(message, type = 'success', iconClass = null) {
|
|
window.NotificationUtils.showNotification(message, type, iconClass);
|
|
},
|
|
|
|
showCopiedPopup() {
|
|
window.NotificationUtils.showCopiedPopup();
|
|
},
|
|
|
|
setupPasteHandlers() {
|
|
const textareas = document.querySelectorAll('textarea');
|
|
textareas.forEach(textarea => {
|
|
textarea.addEventListener('paste', (e) => {
|
|
this.isPasteOperation = true;
|
|
setTimeout(() => {
|
|
this.isPasteOperation = false;
|
|
}, window.CONFIG.PASTE_FLAG_RESET_DELAY_MS);
|
|
});
|
|
});
|
|
}
|
|
}),
|
|
mounted() {
|
|
if (window.ThemeUtils && window.ThemeUtils.initializeTheme) {
|
|
this.isDarkTheme = window.ThemeUtils.initializeTheme();
|
|
if (this.isDarkTheme) {
|
|
document.body.classList.add('dark-theme');
|
|
} else {
|
|
document.body.classList.add('light-theme');
|
|
}
|
|
} else if (this.isDarkTheme) {
|
|
document.body.classList.add('dark-theme');
|
|
}
|
|
|
|
if (window.toolRegistry && typeof window.toolRegistry.mergeVueLifecycle === 'function') {
|
|
const lifecycleHooks = window.toolRegistry.mergeVueLifecycle();
|
|
if (lifecycleHooks && lifecycleHooks.mounted) {
|
|
lifecycleHooks.mounted.call(this);
|
|
}
|
|
}
|
|
|
|
if (window.toolRegistry && typeof window.toolRegistry.getAll === 'function') {
|
|
this.registeredTools = window.toolRegistry.getAll();
|
|
}
|
|
|
|
this.$nextTick(() => {
|
|
const closeButton = document.querySelector('#unicode-options-panel .close-button');
|
|
if (closeButton) {
|
|
const handleClose = (e) => {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
this.toggleUnicodePanel(e);
|
|
};
|
|
|
|
closeButton.addEventListener('click', handleClose, { passive: false });
|
|
closeButton.addEventListener('touchend', handleClose, { passive: false });
|
|
}
|
|
});
|
|
|
|
document.addEventListener('click', (e) => {
|
|
if (e.target.closest('.custom-tooltip')) {
|
|
return;
|
|
}
|
|
|
|
const tooltipIcon = e.target.closest('.tooltip-icon');
|
|
|
|
if (tooltipIcon) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
|
|
const tooltipText = tooltipIcon.getAttribute('data-tooltip');
|
|
if (!tooltipText) return;
|
|
|
|
const existingTooltip = document.querySelector('.custom-tooltip.active');
|
|
if (existingTooltip && existingTooltip.textContent === tooltipText) {
|
|
existingTooltip.classList.remove('active');
|
|
setTimeout(() => {
|
|
if (!existingTooltip.classList.contains('active')) {
|
|
existingTooltip.remove();
|
|
}
|
|
}, 200);
|
|
return;
|
|
}
|
|
|
|
document.querySelectorAll('.custom-tooltip.active').forEach(tooltip => {
|
|
tooltip.classList.remove('active');
|
|
setTimeout(() => {
|
|
if (!tooltip.classList.contains('active')) {
|
|
tooltip.remove();
|
|
}
|
|
}, 200);
|
|
});
|
|
|
|
setTimeout(() => {
|
|
const tooltip = document.createElement('div');
|
|
tooltip.className = 'custom-tooltip active';
|
|
tooltip.textContent = tooltipText;
|
|
|
|
document.body.appendChild(tooltip);
|
|
|
|
const rect = tooltipIcon.getBoundingClientRect();
|
|
tooltip.style.left = (rect.left + rect.width / 2) + 'px';
|
|
tooltip.style.top = (rect.top - tooltip.offsetHeight - 8) + 'px';
|
|
tooltip.style.transform = 'translateX(-50%)';
|
|
}, 10);
|
|
|
|
return;
|
|
}
|
|
|
|
if (e.target.closest('#unicode-options-panel')) {
|
|
return;
|
|
}
|
|
|
|
document.querySelectorAll('.custom-tooltip.active').forEach(tooltip => {
|
|
tooltip.classList.remove('active');
|
|
setTimeout(() => {
|
|
if (!tooltip.classList.contains('active')) {
|
|
tooltip.remove();
|
|
}
|
|
}, 200);
|
|
});
|
|
});
|
|
|
|
this.$nextTick(() => {
|
|
const initializeEmojiGrid = () => {
|
|
if (this.activeTab !== 'steganography') {
|
|
return;
|
|
}
|
|
|
|
const emojiGridContainer = document.getElementById('emoji-grid-container');
|
|
|
|
if (emojiGridContainer) {
|
|
emojiGridContainer.setAttribute('style', 'display: block !important; visibility: visible !important; min-height: 300px; padding: 10px;');
|
|
|
|
const emojiLibrary = document.querySelector('.emoji-library');
|
|
if (emojiLibrary) {
|
|
emojiLibrary.setAttribute('style', 'display: block !important; visibility: visible !important; margin-top: 20px; overflow: visible;');
|
|
}
|
|
|
|
this.renderEmojiGrid();
|
|
clearInterval(emojiGridInitializer);
|
|
}
|
|
};
|
|
|
|
const emojiGridInitializer = setInterval(initializeEmojiGrid, window.CONFIG.EMOJI_GRID_INIT_INTERVAL_MS);
|
|
this._emojiGridInitializer = emojiGridInitializer;
|
|
this.setupPasteHandlers();
|
|
});
|
|
},
|
|
|
|
created() {
|
|
if (window.toolRegistry && typeof window.toolRegistry.mergeVueLifecycle === 'function') {
|
|
const lifecycleHooks = window.toolRegistry.mergeVueLifecycle();
|
|
if (lifecycleHooks && lifecycleHooks.created) {
|
|
lifecycleHooks.created.call(this);
|
|
}
|
|
}
|
|
},
|
|
|
|
beforeDestroy() {
|
|
if (this._emojiGridInitializer) {
|
|
clearInterval(this._emojiGridInitializer);
|
|
this._emojiGridInitializer = null;
|
|
}
|
|
|
|
if (window.toolRegistry && typeof window.toolRegistry.mergeVueLifecycle === 'function') {
|
|
const lifecycleHooks = window.toolRegistry.mergeVueLifecycle();
|
|
if (lifecycleHooks && lifecycleHooks.beforeDestroy) {
|
|
lifecycleHooks.beforeDestroy.call(this);
|
|
}
|
|
}
|
|
},
|
|
|
|
watch: (window.toolRegistry && typeof window.toolRegistry.mergeVueWatchers === 'function')
|
|
? window.toolRegistry.mergeVueWatchers()
|
|
: {}
|
|
});
|