Allows highlighting and copying text on websites where it might be disabled by overriding CSS and removing common disabling attributes.
// ==UserScript==
// @name CopyAnything
// @namespace http://tampermonkey.net/
// @version 1.0
// @description Allows highlighting and copying text on websites where it might be disabled by overriding CSS and removing common disabling attributes.
// @author Aditya Mendiratta
// @match *://*/*
// @license MIT
// @grant none
// ==/UserScript==
(function() {
'use strict';
function applyCssOverrides() {
const style = document.createElement('style');
style.type = 'text/css';
style.innerHTML = `
* {
-webkit-user-select: text !important;
-moz-user-select: text !important;
-ms-user-select: text !important;
user-select: text !important;
}
body {
-webkit-user-select: text !important;
-moz-user-select: text !important;
-ms-user-select: text !important;
user-select: text !important;
}
input, textarea {
/* Ensure input fields are always selectable,
though 'text' should generally cover this too. */
user-select: auto !important;
}
`;
document.head.appendChild(style);
console.log('User select CSS overrides applied.');
}
function removeDisablingAttributes() {
const elements = document.querySelectorAll('*');
let removedCount = 0;
elements.forEach(el => {
if (el.hasAttribute('onselectstart')) {
el.removeAttribute('onselectstart');
removedCount++;
}
if (el.hasAttribute('ondragstart')) {
el.removeAttribute('ondragstart');
removedCount++;
}
if (el.hasAttribute('oncontextmenu')) {
el.removeAttribute('oncontextmenu');
removedCount++;
}
});
if (removedCount > 0) {
console.log(`Removed ${removedCount} disabling attributes.`);
}
}
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.type === 'attributes' &&
(mutation.attributeName === 'onselectstart' ||
mutation.attributeName === 'ondragstart' ||
mutation.attributeName === 'oncontextmenu')) {
if (mutation.target.hasAttribute('onselectstart')) { mutation.target.removeAttribute('onselectstart'); }
if (mutation.target.hasAttribute('ondragstart')) { mutation.target.removeAttribute('ondragstart'); }
if (mutation.target.hasAttribute('oncontextmenu')) { mutation.target.removeAttribute('oncontextmenu'); }
}
else if (mutation.type === 'childList') {
mutation.addedNodes.forEach(node => {
if (node.nodeType === Node.ELEMENT_NODE) {
node.style.setProperty('-webkit-user-select', 'text', 'important');
node.style.setProperty('-moz-user-select', 'text', 'important');
node.style.setProperty('-ms-user-select', 'text', 'important');
node.style.setProperty('user-select', 'text', 'important');
if (node.hasAttribute('onselectstart')) { node.removeAttribute('onselectstart'); }
if (node.hasAttribute('ondragstart')) { node.removeAttribute('ondragstart'); }
if (node.hasAttribute('oncontextmenu')) { node.removeAttribute('oncontextmenu'); }
node.querySelectorAll('*').forEach(descendant => {
if (descendant.hasAttribute('onselectstart')) { descendant.removeAttribute('onselectstart'); }
if (descendant.hasAttribute('ondragstart')) { descendant.removeAttribute('ondragstart'); }
if (descendant.hasAttribute('oncontextmenu')) { descendant.removeAttribute('oncontextmenu'); }
});
}
});
}
});
});
window.addEventListener('DOMContentLoaded', () => {
applyCssOverrides();
removeDisablingAttributes();
observer.observe(document.body, {
attributes: true,
childList: true,
subtree: true
});
console.log('MutationObserver started for dynamic content.');
});
window.addEventListener('load', () => {
applyCssOverrides();
removeDisablingAttributes();
});
})();