A Universal Script to Re-Enable the Selection and Copying

Enables select, right-click, copy and drag on pages that disable them.

当前为 2021-06-13 提交的版本,查看 最新版本

// ==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.0a3
// @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://greasyfork.org/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() {
                    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;}

            a:hover, a:hover *{
            -webkit-user-select: text !important;
            -khtml-user-select: text !important; -moz-user-select: text !important;
            -ms-user-select: text !important; user-select: text !important;
             }

                html[${vv}] *:hover, html[${vv}] *:hover * { cursor:text !important;}

               html[${vv}] :not(input):not(textarea)::selection {background-color: rgba(255, 156, 179,0.5) !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');
    /*
        var longPressForcedCopy = false;
        var selectionChange = null;
        var cid_timer = 0;
        document.addEventListener('mousedown', function(evt) {

            selectionChange = null;
            longPressForcedCopy= false;
            console.log('mousedown')
            cid_timer = setTimeout(function() {
                cid_timer=0;
                if($.isAnySelection()&&selectionChange==null){} // pressing the selection area
                else{
                    document.documentElement.setAttribute('gykqyzwufxpz', '')
                    longPressForcedCopy = true;
                }
            }, 300)

        }, true)
        document.addEventListener('mouseup', function(evt) {
            if(cid_timer) cid_timer = clearTimeout(cid_timer)
            console.log('mouseup')
            if (longPressForcedCopy) document.documentElement.removeAttribute('gykqyzwufxpz')

            if (longPressForcedCopy && selectionChange === true) {

                $.bypass = true;
                evt.preventDefault()
                evt.stopPropagation();
                evt.stopImmediatePropagation();
                $.bypass = false;
            }

            setTimeout(function() {
                longPressForcedCopy = false;
                selectionChange = null;
            }, 50);
        }, true)

        document.addEventListener('click', function(evt) {


            if (longPressForcedCopy && selectionChange === true) {

                $.bypass = true;
                evt.preventDefault()
                evt.stopPropagation();
                evt.stopImmediatePropagation();
                $.bypass = false;
            }

            longPressForcedCopy = false;
            selectionChange = null;
        }, true)

        document.addEventListener('dragstart', function(evt) {
            if(cid_timer) cid_timer = clearTimeout(cid_timer)

            console.log('dragstart')

            if (longPressForcedCopy && $.isAnySelection()) {

                document.documentElement.removeAttribute('gykqyzwufxpz')
                longPressForcedCopy = false;
            }

            if (longPressForcedCopy) {

                $.bypass = true;
                evt.preventDefault()
                // evt.stopPropagation();
                // evt.stopImmediatePropagation();
                $.bypass = false;
            }

        }, true)
        document.addEventListener('dragend', function(evt) {
            if(cid_timer) cid_timer = clearTimeout(cid_timer)
            console.log('dragend')
            if (longPressForcedCopy) document.documentElement.removeAttribute('gykqyzwufxpz')
            longPressForcedCopy = false;
        }, true)

        document.addEventListener('selectionchange', function(evt) {
            selectionChange = $.isAnySelection() //first selection change shall be to empty
            if (selectionChange === true) {
                cid_timer = clearTimeout(cid_timer)
            }
        })
    */

})();