A Universal Script to Re-Enable the Selection and Copying

解除禁止復制、剪切、選擇文本、右鍵菜單的限制。

目前為 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.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;


        })
        */


})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址