Last active
March 22, 2025 05:14
-
-
Save olragon/39b6682b5c600a064caa1b956b51a599 to your computer and use it in GitHub Desktop.
TamperMonkey Script to force text selectable
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name Blagu - Text Selectable | |
// @namespace http://tampermonkey.net/ | |
// @version 1.1 | |
// @description Enables text selection, right-click and copy/paste on website by removing protections | |
// @author You | |
// @match *://*.5office.vn/* | |
// @match *://*.officesaigon.vn/* | |
// @grant none | |
// @run-at document-start | |
// ==/UserScript== | |
(function() { | |
'use strict'; | |
// Function to create and inject CSS to force text selection | |
function injectCSS() { | |
const style = document.createElement('style'); | |
style.textContent = ` | |
* { | |
-webkit-user-select: text !important; | |
-moz-user-select: text !important; | |
-ms-user-select: text !important; | |
user-select: text !important; | |
cursor: auto !important; | |
} | |
[unselectable="on"], | |
[onselectstart="return false;"], | |
[ondragstart="return false;"], | |
[onmousedown="return false;"], | |
[style*="user-select: none"], | |
[style*="-webkit-user-select: none"], | |
[style*="-moz-user-select: none"], | |
[style*="-ms-user-select: none"] { | |
-webkit-user-select: text !important; | |
-moz-user-select: text !important; | |
-ms-user-select: text !important; | |
user-select: text !important; | |
} | |
/* Force highlighting styles for text selection */ | |
::selection { | |
background-color: #3390ff !important; | |
color: white !important; | |
text-shadow: none !important; | |
opacity: 1 !important; | |
visibility: visible !important; | |
} | |
::-moz-selection { | |
background-color: #3390ff !important; | |
color: white !important; | |
text-shadow: none !important; | |
opacity: 1 !important; | |
visibility: visible !important; | |
} | |
`; | |
document.head.appendChild(style); | |
} | |
// Call immediately to ensure CSS is applied early | |
injectCSS(); | |
// Run immediately | |
disableCopyInterception(); | |
// Also run when DOM is ready (some sites add protection later) | |
document.addEventListener('DOMContentLoaded', disableCopyInterception); | |
// Run whenever the page changes (for single-page applications) | |
const observer = new MutationObserver(function() { | |
disableCopyInterception(); | |
}); | |
// Start observing once the body is available | |
window.addEventListener('load', function() { | |
if (document.body) { | |
observer.observe(document.body, { | |
childList: true, | |
subtree: true | |
}); | |
} | |
}); | |
// Function to remove event listeners that prevent selection | |
function removeProtections() { | |
// Override specific functions to make text selectable | |
window.disableSelection = function() { return true; }; | |
window.nocontext = function() { return true; }; | |
window.wccp_pro_iscontenteditable = function() { return true; }; | |
window.wccp_pro_iscontenteditable_flag = true; | |
window.disable_copy = function() { return true; }; | |
window.isDraggable = true; | |
window.disable_hot_keys = function() { return true; }; | |
window.call_disable_copy_WithDelay = function() { return true; }; | |
window.disable_copy_ie = function() { return true; }; | |
window.disable_drag_text = function() { return true; }; | |
window.wccp_pro_clear_any_selection = function() { return true; }; | |
window.preventStopSelection = function() { return true; }; | |
// Prevent any selection clearing functions | |
window.getSelection().removeAllRanges = function() {}; | |
window.getSelection().empty = function() {}; | |
// Remove the masks used to block selection | |
const masks = document.querySelectorAll('#wccp_pro_mask'); | |
masks.forEach(mask => { | |
if (mask) mask.remove(); | |
}); | |
// Remove the error message shown when trying to select text | |
const errorMsgs = document.querySelectorAll('#wpcp-error-message'); | |
errorMsgs.forEach(msg => { | |
if (msg) msg.remove(); | |
}); | |
// Clean up any context menu prevention | |
document.oncontextmenu = null; | |
document.onmousedown = null; | |
document.onmouseup = null; | |
document.onselectionchange = null; | |
document.onselectstart = null; | |
document.ondragstart = null; | |
} | |
// Function to disable copy event interception | |
function disableCopyInterception() { | |
// Store original functions | |
const originalAddEventListener = EventTarget.prototype.addEventListener; | |
const originalRemoveEventListener = EventTarget.prototype.removeEventListener; | |
// Override addEventListener to prevent copy event blocking | |
EventTarget.prototype.addEventListener = function(type, listener, options) { | |
// Skip intercepting 'copy', 'cut', 'contextmenu' event listeners | |
if (type === 'copy' || type === 'cut' || type === 'contextmenu') { | |
console.log('Blocked attempt to intercept:', type); | |
return; | |
} | |
// Call original method for other events | |
return originalAddEventListener.call(this, type, listener, options); | |
}; | |
// Override removeEventListener to maintain consistency | |
EventTarget.prototype.removeEventListener = function(type, listener, options) { | |
// Skip for copy-related events | |
if (type === 'copy' || type === 'cut' || type === 'contextmenu') { | |
return; | |
} | |
// Call original method for other events | |
return originalRemoveEventListener.call(this, type, listener, options); | |
}; | |
// Remove any existing copy protection | |
document.oncopy = null; | |
document.oncut = null; | |
document.oncontextmenu = null; | |
// Enable context menu | |
document.addEventListener('contextmenu', function(e) { | |
e.stopImmediatePropagation(); | |
}, true); | |
// Enable copy | |
document.addEventListener('copy', function(e) { | |
e.stopImmediatePropagation(); | |
}, true); | |
// Enable cut | |
document.addEventListener('cut', function(e) { | |
e.stopImmediatePropagation(); | |
}, true); | |
// Disable user-select: none CSS property | |
const style = document.createElement('style'); | |
style.textContent = ` | |
* { | |
-webkit-user-select: auto !important; | |
-moz-user-select: auto !important; | |
-ms-user-select: auto !important; | |
user-select: auto !important; | |
} | |
`; | |
document.head.appendChild(style); | |
console.log('Copy Protection Bypass: Enabled'); | |
} | |
// Observe and disable any dynamically added protections or scripts | |
function createObserver() { | |
const observer = new MutationObserver(function(mutations) { | |
mutations.forEach(function(mutation) { | |
if (mutation.addedNodes && mutation.addedNodes.length > 0) { | |
for (let i = 0; i < mutation.addedNodes.length; i++) { | |
const node = mutation.addedNodes[i]; | |
// Remove any script blocks that might be reinserting protections | |
if (node.tagName === 'SCRIPT' && | |
(node.innerHTML.includes('wccp_pro') || | |
node.innerHTML.includes('disable_copy') || | |
node.innerHTML.includes('nocontext'))) { | |
node.remove(); | |
} | |
// Remove any added protection masks | |
if (node.id === 'wccp_pro_mask' || node.id === 'wpcp-error-message') { | |
node.remove(); | |
} | |
} | |
} | |
}); | |
// Reapply our changes to ensure they stick | |
removeProtections(); | |
}); | |
// Start observing the document with the configured parameters | |
observer.observe(document.documentElement, { | |
childList: true, | |
subtree: true | |
}); | |
} | |
// Clean up any existing inline event handlers that block selection | |
function cleanupInlineHandlers() { | |
const elements = document.querySelectorAll('*'); | |
elements.forEach(el => { | |
if (el.hasAttribute('oncontextmenu')) el.removeAttribute('oncontextmenu'); | |
if (el.hasAttribute('onselectstart')) el.removeAttribute('onselectstart'); | |
if (el.hasAttribute('ondragstart')) el.removeAttribute('ondragstart'); | |
if (el.hasAttribute('onmousedown')) el.removeAttribute('onmousedown'); | |
if (el.hasAttribute('onmouseup')) el.removeAttribute('onmouseup'); | |
if (el.hasAttribute('oncopy')) el.removeAttribute('oncopy'); | |
if (el.hasAttribute('oncut')) el.removeAttribute('oncut'); | |
}); | |
} | |
// Function to disable all related protections | |
function disableAllProtections() { | |
// Add toggle button with close feature | |
const button = document.createElement('div'); | |
button.id = 'text-selection-toggle'; | |
button.innerHTML = 'T'; | |
button.title = 'Text selection enabled (click to toggle)'; | |
button.style.position = 'fixed'; | |
button.style.bottom = '20px'; | |
button.style.right = '20px'; | |
button.style.zIndex = '9999'; | |
button.style.background = '#4CAF50'; | |
button.style.color = 'white'; | |
button.style.border = 'none'; | |
button.style.borderRadius = '50%'; | |
button.style.width = '48px'; | |
button.style.height = '48px'; | |
button.style.fontSize = '20px'; | |
button.style.cursor = 'pointer'; | |
button.style.display = 'flex'; | |
button.style.alignItems = 'center'; | |
button.style.justifyContent = 'center'; | |
button.style.boxShadow = '0 2px 5px rgba(0,0,0,0.3)'; | |
// Add close button | |
const closeBtn = document.createElement('div'); | |
closeBtn.className = 'close-btn'; | |
closeBtn.innerHTML = '×'; | |
closeBtn.title = 'Hide button'; | |
closeBtn.style.position = 'absolute'; | |
closeBtn.style.top = '-8px'; | |
closeBtn.style.right = '-8px'; | |
closeBtn.style.backgroundColor = '#333'; | |
closeBtn.style.color = 'white'; | |
closeBtn.style.border = '2px solid white'; | |
closeBtn.style.borderRadius = '50%'; | |
closeBtn.style.width = '20px'; | |
closeBtn.style.height = '20px'; | |
closeBtn.style.fontSize = '12px'; | |
closeBtn.style.display = 'flex'; | |
closeBtn.style.alignItems = 'center'; | |
closeBtn.style.justifyContent = 'center'; | |
closeBtn.style.cursor = 'pointer'; | |
closeBtn.onclick = function(e) { | |
e.stopPropagation(); | |
button.style.display = 'none'; | |
}; | |
button.appendChild(closeBtn); | |
let isEnabled = true; | |
button.addEventListener('click', function() { | |
isEnabled = !isEnabled; | |
if (isEnabled) { | |
button.style.backgroundColor = '#4CAF50'; | |
injectCSS(); | |
removeProtections(); | |
cleanupInlineHandlers(); | |
} else { | |
button.style.backgroundColor = '#f44336'; | |
} | |
}); | |
// Wait for the body to be available | |
if (document.body) { | |
document.body.appendChild(button); | |
} else { | |
window.addEventListener('DOMContentLoaded', function() { | |
document.body.appendChild(button); | |
}); | |
} | |
// Apply all protections | |
injectCSS(); | |
removeProtections(); | |
cleanupInlineHandlers(); | |
createObserver(); | |
// Intercept and prevent protection functions | |
window.addEventListener('DOMContentLoaded', function() { | |
removeProtections(); | |
cleanupInlineHandlers(); | |
}); | |
} | |
// Execute immediately | |
disableAllProtections(); | |
// Also run when page is fully loaded | |
window.addEventListener('load', function() { | |
disableAllProtections(); | |
}); | |
// Prevent any selection clearing attempts by overriding related functions | |
const originalGetSelection = window.getSelection; | |
window.getSelection = function() { | |
const selection = originalGetSelection.apply(this, arguments); | |
// Override selection clearing methods | |
if (selection) { | |
const originalEmpty = selection.empty; | |
const originalRemoveAllRanges = selection.removeAllRanges; | |
selection.empty = function() { | |
// Do nothing to prevent clearing selection | |
return selection; | |
}; | |
selection.removeAllRanges = function() { | |
// Do nothing to prevent clearing selection | |
return selection; | |
}; | |
} | |
return selection; | |
}; | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment