improve prevent copy paste with rapid typing

master
a2nr 2026-01-16 20:15:03 +07:00
parent 4939c4edd5
commit 7c1bfdc35a
2 changed files with 189 additions and 8 deletions

View File

@ -4,8 +4,6 @@ services:
elemes: elemes:
build: . build: .
container_name: elemes container_name: elemes
ports:
- "5000:5000" # Expose port 5000 to the host
volumes: volumes:
- ../content:/app/content - ../content:/app/content
- ./static:/app/static - ./static:/app/static

View File

@ -299,26 +299,209 @@
// Update line numbers on other events that might change content // Update line numbers on other events that might change content
codeEditor.addEventListener('keyup', updateLineNumbers); codeEditor.addEventListener('keyup', updateLineNumbers);
codeEditor.addEventListener('paste', function() { // Proper ClipboardEvent API implementation
setTimeout(updateLineNumbers, 0); // Store initial code to restore when paste is detected
let originalCodeOnFocus = initialCode;
// Store the code when editor gains focus
codeEditor.addEventListener('focus', function() {
originalCodeOnFocus = codeEditor.value;
}); });
// Prevent copy-paste in the code editor with multiple methods // Prevent paste event using ClipboardEvent API
codeEditor.addEventListener('paste', function(e) { codeEditor.addEventListener('paste', function(e) {
// Store the current value before paste
const currentValue = codeEditor.value;
// Prevent the default paste behavior
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
alert('Copy-paste is not allowed in the code editor. Please type your code manually.');
// Use a timeout to ensure the paste operation is fully processed
setTimeout(() => {
// Check if the content has changed (some paste operations might bypass preventDefault)
if (codeEditor.value !== currentValue) {
// Restore the original code before the paste attempt
codeEditor.value = currentValue;
updateLineNumbers();
}
// Show warning notification
showCopyPasteNotification('Paste detected and blocked. Copy-paste is not allowed in the code editor. Please type your code manually.');
}, 10); // Small delay to allow paste to potentially occur
return false; return false;
}); });
// Additional paste prevention using onpaste attribute // Prevent copy event using ClipboardEvent API
codeEditor.addEventListener('copy', function(e) {
e.preventDefault();
e.stopPropagation();
// Access the selected text to provide feedback
const selectedText = window.getSelection ? window.getSelection().toString() : '';
if (selectedText && selectedText.trim() !== '') {
showCopyPasteNotification('Copy detected. Copy is not allowed in the code editor. Please type your code manually.');
}
});
// Prevent cut event using ClipboardEvent API
codeEditor.addEventListener('cut', function(e) {
e.preventDefault();
e.stopPropagation();
// Access the selected text to provide feedback
const selectedText = window.getSelection ? window.getSelection().toString() : '';
if (selectedText && selectedText.trim() !== '') {
showCopyPasteNotification('Cut detected. Cut is not allowed in the code editor. Please type your code manually.');
}
});
// Additional prevention using onpaste attribute
codeEditor.onpaste = function(e) { codeEditor.onpaste = function(e) {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
alert('Copy-paste is not allowed in the code editor. Please type your code manually.'); showCopyPasteNotification('Paste detected. Copy-paste is not allowed in the code editor. Please type your code manually.');
return false; return false;
}; };
// Additional prevention using oncopy attribute
codeEditor.oncopy = function(e) {
e.preventDefault();
e.stopPropagation();
showCopyPasteNotification('Copy detected. Copy is not allowed in the code editor. Please type your code manually.');
};
// Additional prevention using oncut attribute
codeEditor.oncut = function(e) {
e.preventDefault();
e.stopPropagation();
showCopyPasteNotification('Cut detected. Cut is not allowed in the code editor. Please type your code manually.');
};
// Allow context menu (right-click) but prevent paste actions
// We'll handle paste operations separately in the paste event
// Device detection function
function isMobileDevice() {
return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
}
// Function to show a temporary notification about copy-paste detection
function showCopyPasteNotification(message) {
// Create or update notification element
let notificationEl = document.getElementById('copy-paste-notification');
if (!notificationEl) {
notificationEl = document.createElement('div');
notificationEl.id = 'copy-paste-notification';
notificationEl.style.position = 'fixed';
notificationEl.style.top = '20px';
notificationEl.style.right = '20px';
notificationEl.style.padding = '10px 15px';
notificationEl.style.backgroundColor = '#dc3545';
notificationEl.style.color = 'white';
notificationEl.style.borderRadius = '4px';
notificationEl.style.zIndex = '10000';
notificationEl.style.boxShadow = '0 2px 10px rgba(0,0,0,0.2)';
document.body.appendChild(notificationEl);
}
notificationEl.textContent = message;
notificationEl.style.display = 'block';
// Hide notification after 3 seconds
setTimeout(() => {
notificationEl.style.display = 'none';
}, 3000);
}
// Enhanced mobile clipboard detection algorithm
// Reference: Based on techniques to detect rapid input to distinguish typing from paste
// Technique: Track timestamps of input events to detect rapid pasting (especially from GBoard)
let lastInputTime = Date.now();
const inputTimeThreshold = 100; // milliseconds - inputs faster than this threshold suggest paste
let rapidInputs = 0;
const rapidInputThreshold = 3; // number of rapid inputs to trigger detection
// Track previous value to detect large changes that might indicate pasting
let previousValue = codeEditor.value;
// Listen for input events to detect rapid typing that might indicate pasting
let lastValidValue = initialCode; // Track the last valid (non-pasted) value
codeEditor.addEventListener('input', function(e) {
const currentTime = Date.now();
const timeDiff = currentTime - lastInputTime;
// Calculate how much text was added
const currentValue = codeEditor.value;
const addedText = currentValue.length - previousValue.length;
// On mobile devices, check for both rapid inputs and large text additions
if (isMobileDevice()) {
// Check for rapid inputs - technique to detect paste events via typing speed
if (timeDiff < inputTimeThreshold) {
rapidInputs++;
if (rapidInputs >= rapidInputThreshold) {
// Likely a paste or snippet insertion on mobile (reference: detect rapid input to distinguish typing from paste)
// Restore to the last known valid value
codeEditor.value = lastValidValue;
updateLineNumbers();
showCopyPasteNotification('Rapid input detected and removed. Please type your code manually.');
rapidInputs = 0; // Reset counter
}
} else {
// Reset counter if normal typing pace resumes
rapidInputs = 0;
}
// Check for large text additions (another sign of paste)
if (addedText > 10) { // If more than 10 characters were added at once
// Restore to the last known valid value
codeEditor.value = lastValidValue;
updateLineNumbers();
showCopyPasteNotification('Large text addition detected and removed. Please type your code manually.');
}
}
// Update tracking variables
lastInputTime = currentTime;
previousValue = currentValue;
// Update the last valid value (but only if we're not in a rapid input situation)
if (isMobileDevice() && rapidInputs === 0 && addedText <= 10) {
// Consider it a valid manual input if it's not rapid or large
lastValidValue = currentValue;
} else if (!isMobileDevice()) {
// For non-mobile, always update the valid value
lastValidValue = currentValue;
}
});
// Additional detection for composition events (used by many mobile keyboards)
// Reference: Some mobile keyboards use composition events when inserting text from suggestions/clipboard
codeEditor.addEventListener('compositionstart', function(e) {
if (isMobileDevice()) {
// Store the current value before composition starts
const beforeCompositionValue = codeEditor.value;
// Some keyboards use composition events when inserting text from suggestions/clipboard
setTimeout(() => {
const currentValue = codeEditor.value;
const addedText = currentValue.length - beforeCompositionValue.length;
if (addedText > 5) { // If more than 5 characters were added via composition
// Restore to the last known valid value
codeEditor.value = lastValidValue;
updateLineNumbers();
showCopyPasteNotification('Text composition detected and removed. Please type your code manually.');
}
previousValue = codeEditor.value; // Update with current value after potential restoration
}, 10); // Small delay to allow composition to complete
}
});
codeEditor.addEventListener('copy', function(e) { codeEditor.addEventListener('copy', function(e) {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();