// ==UserScript==
// @name A Universal Script to Re-Enable the Selection and Copying
// @name:zh-TW A Universal Script to Re-Enable the Selection and Copying
// @version 1.6.0a2
// @description Enables select, right-click, copy and drag on pages that disable them.
// @description:zh-TW 解除禁止復制、剪切、選擇文本、右鍵菜單的限制。
// @include /^https?\:\/\//
// @grant none
// @run-at document-start
// @namespace https://gf.qytechs.cn/users/371179
// ==/UserScript==
'use strict';
(function $$($) {
console.log('script at', location + " #1")
if (document == null || !document.documentElement) return window.requestAnimationFrame($$); // this is tampermonkey bug?? not sure
console.log('script at', location + " #2")
function isPassiveEventListenerSupporting() {
if ('_bPassive' in $) return $._bPassive
var supportsPassive = false;
document.createAttribute('z').addEventListener('', null, {
get passive() {
supportsPassive = true;
}
});
return ($._bPassive = supportsPassive);
}
var mKey = 'dqzadwpujtct';
var _ksNonFalseFunc = '___nff_' + mKey + '___',
_ksReturnValue = '___returnValue_' + mKey + '___';
$ = {
utSelectionColorHack: 'msmtwejkzrqa',
utTapHighlight: 'xfcklblvkjsj',
mAlert_DOWN: function() {}, //dummy function in case alert replacement is not valid
mAlert_UP: function() {}, //dummy function in case alert replacement is not valid
isAnySelection: function() {
var sel = (window.getSelection || function() {})();
return !sel ? null : (typeof sel.isCollapsed == 'boolean') ? !sel.isCollapsed : (sel.toString().length > 0);
},
createCSSElement: function(cssStyle, container) {
var css = document.createElement('style'); //slope: DOM throughout
css.type = 'text/css';
css.innerHTML = cssStyle;
if (container) container.appendChild(css);
return css;
},
createFakeAlert: function(_alert) {
if (typeof _alert != 'function') return null;
function alert(msg) {
setTimeout(() => (alert.__isDisabled__() ? console.log("alert msg disabled: ", msg) : _alert.apply(this, arguments)), 9);
};
alert.toString = () => "function alert() { [native code] }";
return alert;
},
createFuncReplacer: function(originalFunc, pName, resFX) {
resFX = function(ev) {
var res = originalFunc.apply(this, arguments);
if (!this || this[pName] != resFX) return res; // if this is null or undefined, or this.onXXX is not this function
if (res === false) return; // return undefined when "return false;"
originalFunc[_ksNonFalseFunc] = true;
this[pName] = originalFunc; // restore original
return res;
}
resFX.toString = () => originalFunc.toString();
return resFX;
},
listenerDisableAll: function(evt) {
var elmNode = evt.target;
while (elmNode && elmNode.nodeType > 0) { //i.e. HTMLDocument or HTMLElement
var pName = 'on' + evt.type
var f = elmNode[pName];
if (typeof f == 'function' && f[_ksNonFalseFunc] !== true) {
var nf = $.createFuncReplacer(f, pName);
nf[_ksNonFalseFunc] = true;
elmNode[pName] = nf;
}
elmNode = elmNode.parentNode;
}
},
onceCssHighlightSelection: () => {
$.onceCssHighlightSelection = null
var s = [...document.querySelectorAll('a,p,div,span,b,i,strong,li')].filter(elm => elm.childElementCount === 0); // randomly pick an element containing text only to avoid css style bug
var elm = !s.length ? document.body : s[s.length >> 1];
var selectionStyle = window.getComputedStyle(elm, ':selection');
if (/^rgba\(\d+,\s*\d+,\s*\d+,\s*0\)$/.test(selectionStyle.backgroundColor)) document.documentElement.setAttribute($.utSelectionColorHack, "");
var elmStyle = window.getComputedStyle(elm)
if (/^rgba\(\d+,\s*\d+,\s*\d+,\s*0\)$/.test(elmStyle.webkitTapHighlightColor)) document.documentElement.setAttribute($.utTapHighlight, "");
},
enableSelectClickCopy: function() {
$.eyEvts = ['keydown', 'keyup', 'copy', 'contextmenu', 'select', 'selectstart', 'dragstart', 'beforecopy']; //slope: throughout
function getClipText(evt) {
var text;
var clip = (evt.originalEvent || evt).clipboardData;
if (clip) {
text = clip.getData('text/plain') || clip.getData('text/html');
} else {
text = window.clipboardData.getData("text") || null;
}
return text;
}
function isDeactivePreventDefault(evt) {
if ($.bypass) return false;
var j = $.eyEvts.indexOf(evt.type);
switch (j) {
case -1:
return false;
case 0:
case 1:
return (evt.keyCode == 67 && (evt.ctrlKey || evt.metaKey) && !evt.altKey && !evt.shiftKey && $.isAnySelection() === true);
case 2:
var dataTypeString = ((evt.clipboardData || {}).types || [])[0];
// see the richtext hack in https://www.cleancss.com/css-beautify/
// see https://developer.mozilla.org/zh-CN/docs/Web/API/Element/copy_event
// see https://w3c.github.io/clipboard-apis/#widl-ClipboardEvent-clipboardData
if (!dataTypeString) {
//no replacement data
return true;
} else if (window.getSelection().toString().trim()) {
//there is replacement data and the selection is not empty
console.log("copy event - clipboardData replacement is allowed and the selection is not empty", window.getSelection().toString().trim())
return false;
} else {
//there is replacement data and the selection is empty
return false;
}
default:
return true;
}
}
Event.prototype.preventDefault = (function(f) {
return function preventDefault() {
console.log(this.type, 'preventDefault')
if (!isDeactivePreventDefault(this)) f.apply(this);
}
})(Event.prototype.preventDefault);
Event.prototype.preventDefault.toString = () => "function preventDefault() { [native code] }"
Object.defineProperty(Event.prototype, "returnValue", {
get() {
return _ksReturnValue in this ? this[_ksReturnValue] : true;
},
set(newValue) {
if (!isDeactivePreventDefault(this) && newValue === false) this.preventDefault();
this[_ksReturnValue] = newValue;
},
enumerable: true,
configurable: true
});
for (var i = 2, eventsCount = $.eyEvts.length; i < eventsCount; i++) {
document.addEventListener($.eyEvts[i], $.listenerDisableAll, true); // Capture Event; passive:false; expected occurrence COMPLETELY before Target Capture and Target Bubble
}
var _alert = window.alert; //slope: temporary
if (typeof _alert == 'function') {
var _mAlert = $.createFakeAlert(_alert);
if (_mAlert) {
var lastClickAt = 0;
_mAlert.__isDisabled__ = () => lastClickAt + 50 > +new Date;
$.mAlert_DOWN = () => (lastClickAt = +new Date);
$.mAlert_UP = () => (lastClickAt = 0);
window.alert = _mAlert
}
}
},
mainEnableScript: () => {
var vv = 'gykqyzwufxpz';
var cssStyleOnReady = `
*, body *, div, span, body *::before, body *::after, *:hover, *:link, *:visited, *:active , *[style], *[class]{
-webkit-touch-callout: default !important; -webkit-user-select: auto !important;
-khtml-user-select: auto !important; -moz-user-select: auto !important;
-ms-user-select: auto !important; user-select: auto !important;}
` + (1 ? `
a:link, a:visited, a:active, a:link *, a:visited *, a:active *{
-webkit-user-select: text !important;
-khtml-user-select: text !important; -moz-user-select: text !important;
-ms-user-select: text !important; user-select: text !important;
}` : '') +
/*
`
a[${vv}]:link, a[${vv}]:visited, a[${vv}]:active, a[${vv}]:link *, a[${vv}]:visited *, a[${vv}]:active *{
cursor:text !important; }` +*/
`html[${vv}] *:hover { cursor:text !important;}`
+
`
html body *:hover>img[src]{pointer-events:auto;}
[${$.utSelectionColorHack}] :not(input):not(textarea)::selection{ background-color: Highlight !important; color: HighlightText !important;}
[${$.utSelectionColorHack}] :not(input):not(textarea)::-moz-selection{ background-color: Highlight !important; color: HighlightText !important;}
[${$.utTapHighlight}] *{ -webkit-tap-highlight-color: rgba(0, 0, 0, 0.18) !important;}
`.trim();
$.enableSelectClickCopy()
$.createCSSElement(cssStyleOnReady, document.documentElement);
},
mainEvents: (listenerPress, listenerRelease) => {
(["mousedown", "click", "dblclick", "contextmenu"]).forEach(function(event) {
document.addEventListener(event, listenerPress, true); // Capture Event; passive:false; ensure the occurrence of 1st capture event COMPLETELY before executing other listeners
});
document.addEventListener("mouseup", listenerRelease, false); // Bubble Event; passive: true/false; order for releasing is insignificant
}
}
$.mainEnableScript();
$.mainEvents(function(evt) {
if ($.onceCssHighlightSelection) window.requestAnimationFrame($.onceCssHighlightSelection);
if (evt.type != "contextmenu" && evt.which != 3) return;
if ($.cid_mouseup > 0) $.cid_mouseup = clearTimeout($.cid_mouseup);
$.mAlert_DOWN();
},
function(evt) {
if (evt.which != 3) return;
$.cid_mouseup = setTimeout($.mAlert_UP, 17);
});
console.log('userscript running - To Re-Enable Selection & Copying');
/*
function anchorElm(targetElm) {
var p = targetElm;
while (p && p.nodeType > 0) {
if (p.tagName == 'A') {
return p
}
p = p.parentNode
}
return null;
}
var disableLinkingAt = 0;
var mouseDownHold = 0;
var timeoutEndOfLinking = 0;
var sequential = 0,
fDragStart = 1<<3,
fDelaySelectable = 1 << 4,
fPressAnchor = 1 << 8,
fSelectionChange=1<<5
var initVariables = () => {
timeoutEndOfLinking = 0;
mouseDownHold = mouseDownHold ? clearTimeout(mouseDownHold) : 0;
disableLinkingAt = 0;
if ((sequential & fDelaySelectable) ? 1 : 0) {
// remove selectable
window.requestAnimationFrame(removeLinkSelectionAttr)
}
sequential = 0;
}
var removeLinkSelectionAttr = () => [...document.querySelectorAll(`[${'gykqyzwufxpz'}]`)].forEach(elm => elm.removeAttribute('gykqyzwufxpz'));
var endOfLinkSelection = (evt) => {
switch (true) {
case !!(sequential & fDelaySelectable): // target might not be the same one as mousedown
disableLinkingAt = +new Date;
/\* fall through *\/
case disableLinkingAt + 50 > +new Date:
$.bypass = true;
evt.preventDefault()
evt.stopPropagation();
evt.stopImmediatePropagation();
$.bypass = false;
}
}
document.addEventListener('mousedown', function(evt) { // both draggable and non-draggable
console.log('MOUSEDOWN', evt.target)
initVariables()
var targetElement = evt.target
if(mouseDownHold>0)mouseDownHold=clearTimeout(mouseDownHold)
mouseDownHold=setTimeout(function() {
mouseDownHold=0
var mAnchor = anchorElm(targetElement)
if (mAnchor) {
sequential |= fPressAnchor;
if ($.isAnySelection() || mAnchor.hasAttribute('gykqyzwufxpz')) return; // pressing the selected text
mAnchor.setAttribute('gykqyzwufxpz', 1);
sequential |= fDelaySelectable;
}
},300);
}, isPassiveEventListenerSupporting() ? {
passive: true,
capture: true
} : true)
document.addEventListener('dragstart', function(evt) { //draggable link only, always after mousedown
sequential |= fDragStart;
console.log('dragstart', evt.target)
if (mouseDownHold > 0) mouseDownHold = clearTimeout(mouseDownHold);
if (!!(sequential & fDelaySelectable)) {
$.bypass = true;
evt.preventDefault()
evt.stopPropagation();
evt.stopImmediatePropagation();
$.bypass = false;
}
}, true);
document.addEventListener('mouseup', function(evt) {
console.log('onmouseup', evt.target)
if (mouseDownHold > 0) mouseDownHold = clearTimeout(mouseDownHold);
if(!!(sequential&fSelectionChange) && !(sequential & fDelaySelectable) && $.isAnySelection() ){
$.bypass = true;
evt.preventDefault()
evt.stopPropagation();
evt.stopImmediatePropagation();
$.bypass = false;
disableLinkingAt = +new Date
return
}
if (!!(sequential & fPressAnchor) && $.isAnySelection()) {
if (!(sequential & fDelaySelectable)) disableLinkingAt = +new Date; // in case this is a selectable link
endOfLinkSelection(evt)
}
if (!!(sequential & fPressAnchor) && !!(sequential & fDelaySelectable)) {
timeoutEndOfLinking = clearTimeout(timeoutEndOfLinking)
timeoutEndOfLinking = setTimeout(initVariables, 50)
}
}, true);
document.addEventListener('click', function(evt) { // after mouseup - preventDefault to avoid linking
console.log('onclick', evt.target)
if (disableLinkingAt + 50 > +new Date) {
$.bypass = true;
evt.preventDefault()
evt.stopPropagation();
evt.stopImmediatePropagation();
$.bypass = false;
}
}, true);
document.addEventListener('dragend', function(evt) { //mouseup might be not triggered for draggable link
console.log('dragend', evt.target)
if (mouseDownHold > 0) mouseDownHold = clearTimeout(mouseDownHold);
if (!!(sequential & fPressAnchor)) {
endOfLinkSelection(evt)
timeoutEndOfLinking = clearTimeout(timeoutEndOfLinking)
timeoutEndOfLinking = setTimeout(initVariables, 50)
}
}, true)
document.addEventListener('selectionchange', function(evt) { //mouseup might be not triggered for draggable link
sequential|=fSelectionChange;
},true)
*/
/*
var lastMouseDownAt=0, selectionActive=0, selectionChange=false
document.addEventListener('mousedown', function(evt) {
selectionActive=0;
lastMouseDownAt=1;
selectionChange=false;
if(evt.target&&evt.target.nodeType>0){
if(lastMouseDownAt==1)
lastMouseDownAt=+new Date+370;
}
},false)
document.addEventListener('mouseup', function(evt) {
console.log('up', selectionActive)
lastMouseDownAt=0
if(selectionChange){
selectionChange=false;
document.documentElement.removeAttribute('gykqyzwufxpz')
}
if(selectionActive==1||selectionActive==2){
$.bypass=true;
evt.preventDefault()
evt.stopPropagation();
evt.stopImmediatePropagation();
$.bypass=false;
selectionActive=2;
}
},true)
document.addEventListener('click', function(evt) {
if(selectionActive==2){
$.bypass=true;
evt.preventDefault()
evt.stopPropagation();
evt.stopImmediatePropagation();
$.bypass=false;
}
},true)
document.addEventListener('dragstart', function(evt) {
if(lastMouseDownAt>1000 && lastMouseDownAt< +new Date && !$.isAnySelection()){
console.log(1212)
$.bypass=true;
evt.preventDefault()
evt.stopPropagation();
evt.stopImmediatePropagation();
$.bypass=false;
//selectionActive=1;
}else{
document.documentElement.removeAttribute('gykqyzwufxpz')
}
})
document.addEventListener('selectionchange', function(evt) {
selectionChange=true;
lastMouseDownAt=+new Date +180
document.documentElement.setAttribute('gykqyzwufxpz','')
if(selectionActive==0 && $.isAnySelection()) selectionActive=1;
})
*/
})();