improve prevent copy paste with rapid typing
parent
4939c4edd5
commit
7c1bfdc35a
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue