A Universal Script to Re-Enable the Selection and Copying

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

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

  1. // ==UserScript==
  2. // @name A Universal Script to Re-Enable the Selection and Copying
  3. // @name:zh-TW A Universal Script to Re-Enable the Selection and Copying
  4. // @version 1.5.3
  5. // @description Enables select, right-click, copy and drag on pages that disable them.
  6. // @description:zh-TW 解除禁止復制、剪切、選擇文本、右鍵菜單的限制。
  7. // @include /^https?\:\/\//
  8. // @grant none
  9. // @run-at document-start
  10. // @namespace https://greasyfork.org/users/371179
  11. // ==/UserScript==
  12. 'use strict';
  13. (function($) {
  14.  
  15. // console.log('script at', location+"")
  16. if (document == null || !document.documentElement) return;
  17.  
  18. var mKey = 'dqzadwpujtct';
  19. var _ksNonFalseFunc = '___nff_' + mKey + '___',
  20. _ksReturnValue = '___returnValue_' + mKey + '___';
  21.  
  22. $ = {
  23. utSelectionColorHack: 'msmtwejkzrqa',
  24.  
  25. mAlert_DOWN: function() {}, //dummy function in case alert replacement is not valid
  26. mAlert_UP: function() {}, //dummy function in case alert replacement is not valid
  27.  
  28. isAnySelection: function() {
  29. var sel = (window.getSelection || function() {})();
  30. return !sel ? null : (typeof sel.isCollapsed == 'boolean') ? !sel.isCollapsed : (sel.toString().length > 0);
  31. },
  32.  
  33. createCSSElement: function(cssStyle, container) {
  34. var css = document.createElement('style'); //slope: DOM throughout
  35. css.type = 'text/css';
  36. css.innerHTML = cssStyle;
  37. if (container) container.appendChild(css);
  38. return css;
  39. },
  40.  
  41. createFakeAlert: function(_alert) {
  42. if (typeof _alert != 'function') return null;
  43.  
  44. function alert(msg) {
  45. setTimeout(() => (alert.__isDisabled__() ? console.log("alert msg disabled: ", msg) : _alert.apply(this, arguments)), 9);
  46. };
  47. alert.toString = () => "function alert() { [native code] }";
  48. return alert;
  49. },
  50.  
  51. createFuncReplacer: function(originalFunc, pName, resFX) {
  52. resFX = function(ev) {
  53. var res = originalFunc.apply(this, arguments);
  54. if (!this || this[pName] != resFX) return res; // if this is null or undefined, or this.onXXX is not this function
  55. if (res === false) return; // return undefined when "return false;"
  56. originalFunc[_ksNonFalseFunc] = true;
  57. this[pName] = originalFunc; // restore original
  58. return res;
  59. }
  60. resFX.toString = () => originalFunc.toString();
  61. return resFX;
  62. },
  63.  
  64. listenerDisableAll: function(evt) {
  65. var elmNode = evt.target;
  66. while (elmNode && elmNode.nodeType > 0) { //i.e. HTMLDocument or HTMLElement
  67. var pName = 'on' + evt.type
  68. var f = elmNode[pName];
  69. if (typeof f == 'function' && f[_ksNonFalseFunc] !== true) {
  70. var nf = $.createFuncReplacer(f, pName);
  71. nf[_ksNonFalseFunc] = true;
  72. elmNode[pName] = nf;
  73. }
  74. elmNode = elmNode.parentNode;
  75. }
  76. },
  77.  
  78. onceCssHighlightSelection: () => {
  79. $.onceCssHighlightSelection = null
  80. 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
  81. var elm = !s.length ? document.body : s[s.length >> 1];
  82. var bgColor = window.getComputedStyle(elm, ':selection').backgroundColor;
  83. if (/^rgba\(\d+,\s*\d+,\s*\d+,\s*0\)$/.test(bgColor)) document.documentElement.setAttribute($.utSelectionColorHack, "");
  84. },
  85.  
  86.  
  87. enableSelectClickCopy: function() {
  88.  
  89. $.eyEvts = ['keydown', 'keyup', 'copy', 'contextmenu', 'select', 'selectstart', 'dragstart', 'beforecopy']; //slope: throughout
  90.  
  91.  
  92. function isDeactivePreventDefault(evt) {
  93. var j = $.eyEvts.indexOf(evt.type);
  94. return (j >= 2) || ((j == 0 || j == 1) && evt.ctrlKey && evt.keyCode == 67 && !evt.altKey && !evt.shiftKey && $.isAnySelection() === true); //j<0 return false
  95. }
  96.  
  97. Event.prototype.preventDefault = (function(f) {
  98. return function preventDefault() {
  99. if (!isDeactivePreventDefault(this)) f.apply(this);
  100. }
  101. })(Event.prototype.preventDefault);
  102. Event.prototype.preventDefault.toString = () => "function preventDefault() { [native code] }"
  103.  
  104. Object.defineProperty(Event.prototype, "returnValue", {
  105. get() {
  106. return _ksReturnValue in this ? this[_ksReturnValue] : true;
  107. },
  108. set(newValue) {
  109. if (!isDeactivePreventDefault(this) && newValue === false) this.preventDefault();
  110. this[_ksReturnValue] = newValue;
  111. },
  112. enumerable: true,
  113. configurable: true
  114. });
  115.  
  116. for (var i = 2, eventsCount = $.eyEvts.length; i < eventsCount; i++) {
  117. document.addEventListener($.eyEvts[i], $.listenerDisableAll, {
  118. capture: true, // Capture Event
  119. passive: false // expected occurrence COMPLETELY before Target Capture and Target Bubble
  120. }, true);
  121. }
  122.  
  123. var _alert = window.alert; //slope: temporary
  124. if (typeof _alert == 'function') {
  125. var _mAlert = $.createFakeAlert(_alert);
  126. if (_mAlert) {
  127. var lastClickAt = 0;
  128. _mAlert.__isDisabled__ = () => lastClickAt + 50 > +new Date;
  129. $.mAlert_DOWN = () => (lastClickAt = +new Date);
  130. $.mAlert_UP = () => (lastClickAt = 0);
  131. window.alert = _mAlert
  132. }
  133. }
  134.  
  135. },
  136.  
  137. mainEnableScript: () => {
  138.  
  139. var cssStyleOnReady = '*, body *, div, span, body *::before, body *::after, *:hover, *:link, *:visited, *:active , *[style], *[class]{' +
  140. '-webkit-touch-callout: default !important; -webkit-user-select: auto !important; ' +
  141. '-khtml-user-select: auto !important; -moz-user-select: auto !important; ' +
  142. '-ms-user-select: auto !important; user-select: auto !important;}' +
  143. 'html body *:hover>img[src]{pointer-events:auto;}' +
  144. '[' + $.utSelectionColorHack + '] :not(input):not(textarea)::selection{ background-color: Highlight !important; color: HighlightText !important;}' +
  145. '[' + $.utSelectionColorHack + '] :not(input):not(textarea)::-moz-selection{ background-color: Highlight !important; color: HighlightText !important;}';
  146.  
  147. $.enableSelectClickCopy()
  148. $.createCSSElement(cssStyleOnReady, document.documentElement);
  149.  
  150. },
  151.  
  152. mainEvents: (listenerPress, listenerRelease) => {
  153. (["mousedown", "click", "dblclick", "contextmenu"]).forEach(function(event) {
  154. document.addEventListener(event, listenerPress, {
  155. capture: true, // Capture Event
  156. passive: false // ensure the occurrence of 1st capture event COMPLETELY before executing other listeners
  157. }, true);
  158. });
  159. document.addEventListener("mouseup", listenerRelease, {
  160. capture: false, // Bubble Event
  161. passive: true // order for releasing is insignificant
  162. });
  163. }
  164.  
  165. }
  166.  
  167. $.mainEnableScript();
  168.  
  169. $.mainEvents(function(evt) {
  170. if ($.onceCssHighlightSelection) window.requestAnimationFrame($.onceCssHighlightSelection);
  171. if (evt.type != "contextmenu" && evt.which != 3) return;
  172. if ($.cid_mouseup > 0) $.cid_mouseup = clearTimeout($.cid_mouseup);
  173. $.mAlert_DOWN();
  174. },
  175. function(evt) {
  176. if (evt.which != 3) return;
  177. $.cid_mouseup = setTimeout($.mAlert_UP, 17);
  178. });
  179. console.log('userscript running - To Re-Enable Selection & Copying');
  180.  
  181. })();