通用脚本重开启选取复制

破解锁右键,解除禁止复制、剪切、选择文本、右键菜单、文字复制、文字选取、图片右键等限制。增强功能:Alt键超连结文字选取。

目前为 2023-01-04 提交的版本。查看 最新版本

  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. // @name:zh-CN 通用脚本重开启选取复制
  5. // @version 1.8.1.8
  6. // @description Enables select, right-click, copy and drag on pages that disable them. Enhanced Feature: Alt Key HyperLink Text Selection
  7. // @description:zh-TW 破解鎖右鍵,解除禁止復制、剪切、選擇文本、右鍵菜單、文字複製、文字選取、圖片右鍵等限制。增強功能:Alt鍵超連結文字選取。
  8. // @description:zh-CN 破解锁右键,解除禁止复制、剪切、选择文本、右键菜单、文字复制、文字选取、图片右键等限制。增强功能:Alt键超连结文字选取。
  9. // @namespace https://greasyfork.org/users/371179
  10. // @author CY Fung
  11. // @supportURL https://github.com/cyfung1031/userscript-supports
  12. // @run-at document-start
  13. // @match http://*/*
  14. // @match https://*/*
  15. // @exclude /^https?://\S+\.(txt|png|jpg|jpeg|gif|xml|svg|manifest|log|ini)[^\/]*$/
  16. // @exclude https://github.dev/*/*
  17. // @exclude https://www.photopea.com/*
  18. // @icon https://cdn-icons-png.flaticon.com/512/3388/3388671.png
  19. // @grant GM_registerMenuCommand
  20. // @grant GM_unregisterMenuCommand
  21. // @grant GM_setValue
  22. // @grant GM_getValue
  23. // @grant GM_addValueChangeListener
  24. // ==/UserScript==
  25. (function $$() {
  26. 'use strict';
  27. if (document == null || !document.documentElement) return window.requestAnimationFrame($$); // this is tampermonkey bug?? not sure
  28. //console.log('script at', location)
  29. function $nil() {}
  30. function isLatestBrowser(){
  31. let res;
  32. try{
  33. let o ={$nil};
  34. o?.$nil();
  35. o=null;
  36. o?.$nil();
  37. res = true;
  38. }catch(e){}
  39. return !!res;
  40. }
  41. if(!isLatestBrowser()) throw 'Browser version before 2020-01-01 is not supported. Please update to the latest version.';
  42. function isSupportAdvancedEventListener() {
  43. if ('_b1750' in $) return $._b1750
  44. var prop = 0;
  45. document.createAttribute('z').addEventListener('nil', $nil, {
  46. get passive() {
  47. prop++;
  48. },
  49. get once() {
  50. prop++;
  51. }
  52. });
  53. return ($._b1750 = prop == 2);
  54. }
  55. function isSupportPassiveEventListener() {
  56. if ('_b1650' in $) return $._b1650
  57. var prop = 0;
  58. document.createAttribute('z').addEventListener('nil', $nil, {
  59. get passive() {
  60. prop++;
  61. }
  62. });
  63. return ($._b1650 = prop == 1);
  64. }
  65. function inIframe () {
  66. try {
  67. return window.self !== window.top;
  68. } catch (e) {
  69. return true;
  70. }
  71. }
  72. var getSelection = window.getSelection || Error()(),
  73. requestAnimationFrame = window.requestAnimationFrame || Error()(),
  74. getComputedStyle = window.getComputedStyle.bind(window) || Error()();
  75. const $ = {
  76. utSelectionColorHack: 'msmtwejkzrqa',
  77. utTapHighlight: 'xfcklblvkjsj',
  78. utLpSelection: 'gykqyzwufxpz',
  79. utHoverBlock: 'meefgeibrtqx', //scc_emptyblock
  80. //utNonEmptyElm: 'ilkpvtsnwmjb',
  81. utNonEmptyElmPrevElm: 'jttkfplemwzo',
  82. utHoverTextWrap: 'oseksntfvucn',
  83. ksFuncReplacerNonFalse: '___dqzadwpujtct___',
  84. ksEventReturnValue: ' ___ndjfujndrlsx___',
  85. ksSetData: '___rgqclrdllmhr___',
  86. ksNonEmptyPlainText: '___grpvyosdjhuk___',
  87. eh_capture_passive: () => isSupportPassiveEventListener() ? ($._eh_capture_passive = ($._eh_capture_passive || {
  88. capture: true,
  89. passive: true
  90. })) : true,
  91. mAlert_DOWN: function() {}, //dummy function in case alert replacement is not valid
  92. mAlert_UP: function() {}, //dummy function in case alert replacement is not valid
  93. lpKeyPressing: false,
  94. lpKeyPressingPromise: Promise.resolve(),
  95. isNum: (d) => (d > 0 || d < 0 || d === 0),
  96. isAnySelection: function() {
  97. var sel = getSelection();
  98. return !sel ? null : (typeof sel.isCollapsed == 'boolean') ? !sel.isCollapsed : (sel.toString().length > 0);
  99. },
  100. createCSSElement: function(cssStyle, container) {
  101. var css = document.createElement('style'); //slope: DOM throughout
  102. css.type = 'text/css';
  103. css.textContent = cssStyle;
  104. if (container) container.appendChild(css);
  105. return css;
  106. },
  107. createFakeAlert: function(_alert) {
  108. if (typeof _alert != 'function') return null;
  109. function alert(msg) {
  110. alert.__isDisabled__() ? console.log("alert msg disabled: ", msg) : _alert.apply(this, arguments);
  111. };
  112. alert.toString = () => "function alert() { [native code] }";
  113. return alert;
  114. },
  115. createFuncReplacer: function(originalFunc, pName, resFX) {
  116. resFX = function(ev) {
  117. var res = originalFunc.apply(this, arguments);
  118. if (!this || this[pName] != resFX) return res; // if this is null or undefined, or this.onXXX is not this function
  119. if (res === false) return; // return undefined when "return false;"
  120. originalFunc[$.ksFuncReplacerNonFalse] = true;
  121. this[pName] = originalFunc; // restore original
  122. return res;
  123. }
  124. resFX.toString = () => originalFunc.toString();
  125. return resFX;
  126. },
  127. listenerDisableAll: function(evt) {
  128. var elmNode = evt.target;
  129. var pName = 'on' + evt.type;
  130. evt = null;
  131. Promise.resolve().then(() => {
  132. while (elmNode && elmNode.nodeType > 0) { //i.e. HTMLDocument or HTMLElement
  133. var f = elmNode[pName];
  134. if (typeof f == 'function' && f[$.ksFuncReplacerNonFalse] !== true) {
  135. var nf = $.createFuncReplacer(f, pName);
  136. nf[$.ksFuncReplacerNonFalse] = true;
  137. elmNode[pName] = nf;
  138. }
  139. elmNode = elmNode.parentNode;
  140. }
  141. })
  142. },
  143. onceCssHighlightSelection: () => {
  144. if (document.documentElement.hasAttribute($.utLpSelection)) return;
  145. $.onceCssHighlightSelection = null
  146. Promise.resolve().then(() => {
  147. 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
  148. var elm = !s.length ? document.body : s[s.length >> 1];
  149. return elm
  150. }).then(elm => {
  151. var selectionStyle = getComputedStyle(elm, ':selection');
  152. if (/^rgba\(\d+,\s*\d+,\s*\d+,\s*0\)$/.test(selectionStyle.getPropertyValue('background-color'))) document.documentElement.setAttribute($.utSelectionColorHack, "");
  153. return elm;
  154. }).then(elm => {
  155. var elmStyle = getComputedStyle(elm)
  156. if (/^rgba\(\d+,\s*\d+,\s*\d+,\s*0\)$/.test(elmStyle.getPropertyValue('-webkit-tap-highlight-color'))) document.documentElement.setAttribute($.utTapHighlight, "");
  157. })
  158. },
  159. clipDataProcess: function(clipboardData) {
  160. if (!clipboardData) return;
  161. const evt = clipboardData[$.ksSetData]; //NOT NULL when preventDefault is called
  162. if (!evt || evt.clipboardData !== clipboardData) return;
  163. const plainText = clipboardData[$.ksNonEmptyPlainText]; //NOT NULL when setData is called with non empty input
  164. if (!plainText) return;
  165. //BOTH preventDefault and setData are called.
  166. if (evt.cancelable !== true || evt.defaultPrevented !== false) return;
  167. $.bypass = true;
  168. evt.preventDefault();
  169. $.bypass = false;
  170. var trimedSelectionText = getSelection().toString().trim()
  171. if (trimedSelectionText) {
  172. //there is replacement data and the selection is not empty
  173. console.log({
  174. msg: "copy event - clipboardData replacement is allowed and the selection is not empty",
  175. oldText: trimedSelectionText,
  176. newText: plainText,
  177. })
  178. } else {
  179. //there is replacement data and the selection is empty
  180. console.log({
  181. msg: "copy event - clipboardData replacement is allowed and the selection is empty",
  182. oldText: trimedSelectionText,
  183. newText: plainText,
  184. })
  185. }
  186. },
  187. enableSelectClickCopy: function() {
  188. $.eyEvts = ['keydown', 'keyup', 'copy', 'contextmenu', 'select', 'selectstart', 'dragstart', 'beforecopy']; //slope: throughout
  189. function isDeactivePreventDefault(evt) {
  190. if ($.bypass) return false;
  191. var j = $.eyEvts.indexOf(evt.type);
  192. switch (j) {
  193. case 6:
  194. if ($.enableDragging) return false;
  195. if (evt&&evt.target && evt.target.nodeType==1 && evt.target.hasAttribute('draggable')) {
  196. $.enableDragging = true;
  197. return false;
  198. }
  199. //if(evt.target.hasAttribute('draggable')&&evt.target!=window.getSelection().anchorNode)return false;
  200. return true;
  201. case 3:
  202. if (evt.target instanceof Element && (evt.target.textContent || "").trim().length === 0) return false; //exclude elements like video
  203. return true;
  204. case -1:
  205. return false;
  206. case 0:
  207. case 1:
  208. return (evt.keyCode == 67 && (evt.ctrlKey || evt.metaKey) && !evt.altKey && !evt.shiftKey && $.isAnySelection() === true);
  209. case 2:
  210. if (!('clipboardData' in evt && 'setData' in DataTransfer.prototype)) return true; // Event oncopy not supporting clipboardData
  211. if (evt.cancelable && evt.defaultPrevented === false) {} else return true;
  212. if (evt.clipboardData[$.ksSetData] && evt.clipboardData[$.ksSetData] != evt) return true; //in case there is a bug
  213. evt.clipboardData[$.ksSetData] = evt;
  214. $.clipDataProcess(evt.clipboardData);
  215. return true; //preventDefault in clipDataProcess
  216. default:
  217. return true;
  218. }
  219. }
  220. !(function($setData) {
  221. DataTransfer.prototype.setData = (function setData() {
  222. if (arguments[0] == 'text/plain' && typeof arguments[1] == 'string') {
  223. if (arguments[1].trim().length > 0) {
  224. this[$.ksNonEmptyPlainText] = arguments[1]
  225. } else if (this[$.ksNonEmptyPlainText]) {
  226. arguments[1] = this[$.ksNonEmptyPlainText]
  227. }
  228. }
  229. $.clipDataProcess(this)
  230. let res = $setData.apply(this, arguments)
  231. return res;
  232. })
  233. })(DataTransfer.prototype.setData);
  234. Object.defineProperties(DataTransfer.prototype, {
  235. [$.ksSetData]: { //store the event
  236. value: null,
  237. writable: true,
  238. enumerable: false,
  239. configurable: true
  240. },
  241. [$.ksNonEmptyPlainText]: { //store the text
  242. value: null,
  243. writable: true,
  244. enumerable: false,
  245. configurable: true
  246. }
  247. })
  248. Event.prototype.preventDefault = (function(f) {
  249. function preventDefault() {
  250. if (!isDeactivePreventDefault(this)) f.call(this);
  251. }
  252. preventDefault.toString = () => f.toString();
  253. return preventDefault;
  254. })(Event.prototype.preventDefault);
  255. Object.defineProperty(Event.prototype, "returnValue", {
  256. get() {
  257. return $.ksEventReturnValue in this ? this[$.ksEventReturnValue] : true;
  258. },
  259. set(newValue) {
  260. if (newValue === false && !isDeactivePreventDefault(this)) this.preventDefault();
  261. this[$.ksEventReturnValue] = newValue;
  262. },
  263. enumerable: true,
  264. configurable: true
  265. });
  266. for (var i = 2, eventsCount = $.eyEvts.length; i < eventsCount; i++) {
  267. document.addEventListener($.eyEvts[i], $.listenerDisableAll, true); // Capture Event; passive:false; expected occurrence COMPLETELY before Target Capture and Target Bubble
  268. }
  269. // userscript bug ? window.alert not working
  270. let window_ = null;
  271. try{
  272. window_ = new Function('return window')();
  273. }catch(e){}
  274. if(window_){
  275. let _alert = window_.alert; //slope: temporary
  276. if (typeof _alert == 'function') {
  277. let _mAlert = $.createFakeAlert(_alert);
  278. if (_mAlert) {
  279. let clickBlockingTo = 0;
  280. _mAlert.__isDisabled__ = () => clickBlockingTo > +new Date;
  281. $.mAlert_DOWN = () => (clickBlockingTo = +new Date + 50);
  282. $.mAlert_UP = () => (clickBlockingTo = +new Date + 20);
  283. window_.alert = _mAlert
  284. }
  285. _mAlert=null;
  286. }
  287. _alert=null;
  288. }
  289. window_=null;
  290. },
  291. lpCheckPointer: function(targetElm) {
  292. if (targetElm && targetElm.nodeType == 1 && targetElm.matches('*:hover')) {
  293. if (getComputedStyle(targetElm).getPropertyValue('cursor') == 'pointer' && targetElm.textContent) return true;
  294. }
  295. return false;
  296. },
  297. eventCancel: function(evt, toPreventDefault) {
  298. $.bypass = true;
  299. !toPreventDefault || evt.preventDefault()
  300. evt.stopPropagation();
  301. evt.stopImmediatePropagation();
  302. $.bypass = false;
  303. },
  304. lpHoverBlocks:[],
  305. lpKeyAltLastPressAt:0,
  306. lpKeyAltPressInterval:0,
  307. noPlayingVideo:function(){
  308. // prevent poor video preformance
  309. let noPlaying =true;
  310. for(const video of document.querySelectorAll('video[src]')){
  311. if(video.paused===false) noPlaying=false;
  312. }
  313. return noPlaying;
  314. },
  315. lpKeyDown: function(evt) {
  316. if(!$.gm_lp_enable)return;
  317. const isAltPress= (evt.key == "Alt" && evt.altKey && !evt.ctrlKey && !evt.metaKey && !evt.shiftKey)
  318. if (isAltPress) {
  319. $.lpKeyAltLastPressAt=+new Date;
  320. let element=evt.target
  321. if($.lpKeyPressing == false && element && element.parentNode && !evt.repeat && $.noPlayingVideo()){
  322. $.lpKeyPressing = true;
  323. $.cid_lpKeyPressing=setInterval(()=>{
  324. if($.lpKeyAltLastPressAt+500<+new Date){
  325. $.lpCancelKeyPressAlt();
  326. }
  327. },137);
  328. const rootNode = $.rootHTML(element);
  329. if (rootNode) {
  330. let tmp_wmEty = null;
  331. let wmTextWrap = new WeakMap();
  332. $.lpKeyPressingPromise = $.lpKeyPressingPromise.then(() => {
  333. for (const elm of $.lpHoverBlocks) {
  334. elm.removeAttribute($.utNonEmptyElmPrevElm)
  335. elm.removeAttribute($.utHoverTextWrap)
  336. }
  337. $.lpHoverBlocks.length=0;
  338. }).then(() => {
  339. tmp_wmEty = new WeakMap(); // 1,2,3.....: non-empty elm, -1:empty elm
  340. const s = [...rootNode.querySelectorAll('*:not(button, textarea, input, script, noscript, style, link, img, br)')].filter((elm) => elm.childElementCount === 0 && (elm.textContent || '').trim().length > 0)
  341. for (const elm of s) tmp_wmEty.set(elm, 1);
  342. return s;
  343. }).then((s) => {
  344. let laterArr = [];
  345. let promises = [];
  346. let promiseCallback = parentNode => {
  347. if (wmTextWrap.get(parentNode) !== null) return;
  348. const m = [...parentNode.children].some(elm => {
  349. const value = getComputedStyle(elm).getPropertyValue('z-index');
  350. if (typeof value == 'string' && value.length > 0) return $.isNum(+value)
  351. return false
  352. })
  353. wmTextWrap.set(parentNode, m)
  354. if (m) {
  355. $.lpHoverBlocks.push(parentNode);
  356. parentNode.setAttribute($.utHoverTextWrap, '')
  357. }
  358. };
  359. for (const elm of s) {
  360. let qElm = elm;
  361. let qi = 1;
  362. while (true) {
  363. let pElm = qElm.previousElementSibling;
  364. let anyEmptyHover = false;
  365. while (pElm) {
  366. if (tmp_wmEty.get(pElm) > 0) break;
  367. if (!pElm.matches(`button, textarea, input, script, noscript, style, link, img, br`) && (pElm.textContent || '').length === 0 && pElm.clientWidth * pElm.clientHeight > 0) {
  368. laterArr.push(pElm);
  369. anyEmptyHover = true;
  370. }
  371. pElm = pElm.previousElementSibling;
  372. }
  373. if (anyEmptyHover && !wmTextWrap.has(qElm.parentNode)) {
  374. wmTextWrap.set(qElm.parentNode, null)
  375. promises.push(Promise.resolve(qElm.parentNode).then(promiseCallback))
  376. }
  377. qElm = qElm.parentNode;
  378. if (!qElm || qElm === rootNode) break;
  379. qi++
  380. if (tmp_wmEty.get(qElm) > 0) break;
  381. tmp_wmEty.set(qElm, qi)
  382. }
  383. }
  384. tmp_wmEty = null;
  385. Promise.all(promises).then(() => {
  386. promises.length = 0;
  387. promises = null;
  388. promiseCallback = null;
  389. for (const pElm of laterArr) {
  390. let parentNode = pElm.parentNode
  391. if (wmTextWrap.get(parentNode) === true) {
  392. $.lpHoverBlocks.push(pElm);
  393. pElm.setAttribute($.utNonEmptyElmPrevElm, '');
  394. }
  395. }
  396. laterArr.length = 0;
  397. laterArr = null;
  398. wmTextWrap = null;
  399. })
  400. })
  401. }
  402. }
  403. }else if($.lpKeyPressing==true){
  404. $.lpCancelKeyPressAlt();
  405. }
  406. },
  407. lpCancelKeyPressAlt:()=>{
  408. $.lpKeyPressing = false;
  409. if($.cid_lpKeyPressing>0) $.cid_lpKeyPressing=clearInterval($.cid_lpKeyPressing)
  410. $.lpKeyPressingPromise = $.lpKeyPressingPromise.then(() => {
  411. for (const elm of $.lpHoverBlocks) {
  412. elm.removeAttribute($.utNonEmptyElmPrevElm)
  413. elm.removeAttribute($.utHoverTextWrap)
  414. }
  415. $.lpHoverBlocks.length=0;
  416. })
  417. setTimeout(function(){
  418. if($.lpMouseActive == 1){
  419. $.lpMouseUpClear();
  420. $.lpMouseActive=0;
  421. }
  422. },32);
  423. },
  424. lpKeyUp: function(evt) {
  425. if(!$.gm_lp_enable)return;
  426. if ($.lpKeyPressing == true) {
  427. $.lpCancelKeyPressAlt();
  428. }
  429. },
  430. lpAltRoots:[],
  431. lpMouseDown: function(evt) {
  432. if(!$.gm_lp_enable)return;
  433. $.lpMouseActive = 0;
  434. if (evt.altKey && !evt.ctrlKey && !evt.metaKey && !evt.shiftKey && evt.button === 0 && $.noPlayingVideo()) {
  435. $.lpMouseActive = 1;
  436. $.eventCancel(evt, false);
  437. const rootNode = $.rootHTML(evt.target);
  438. $.lpAltRoots.push(rootNode);
  439. rootNode.setAttribute($.utLpSelection, '');
  440. }
  441. },
  442. lpMouseUpClear:function(){
  443. for(const rootNode of $.lpAltRoots) rootNode.removeAttribute($.utLpSelection);
  444. $.lpAltRoots.length=0;
  445. if ($.onceCssHighlightSelection) window.requestAnimationFrame($.onceCssHighlightSelection);
  446. },
  447. lpMouseUp: function(evt) {
  448. if(!$.gm_lp_enable)return;
  449. if ($.lpMouseActive == 1) {
  450. $.lpMouseActive = 2;
  451. $.eventCancel(evt, false);
  452. $.lpMouseUpClear();
  453. }
  454. },
  455. lpClick: function(evt) {
  456. if(!$.gm_lp_enable)return;
  457. if ($.lpMouseActive == 2) {
  458. $.eventCancel(evt, false);
  459. }
  460. },
  461. lpEnable: function() { // this is an optional feature for modern browser
  462. // the built-in browser feature has already disabled the default event behavior, the coding is just to ensure no "tailor-made behavior" occuring.
  463. document.addEventListener('keydown', $.lpKeyDown, {
  464. capture: true,
  465. passive: true
  466. })
  467. document.addEventListener('keyup', $.lpKeyUp, {
  468. capture: true,
  469. passive: true
  470. })
  471. document.addEventListener('mousedown', $.lpMouseDown, {
  472. capture: true,
  473. passive: true
  474. })
  475. document.addEventListener('mouseup', $.lpMouseUp, {
  476. capture: true,
  477. passive: true
  478. })
  479. document.addEventListener('click', $.lpClick, {
  480. capture: true,
  481. passive: true
  482. })
  483. },
  484. rootHTML: (node) => {
  485. if (!node || !(node.nodeType > 0)) return null;
  486. if (!node.ownerDocument) return node;
  487. let rootNode = node.getRootNode ? node.getRootNode() : null
  488. if (!rootNode) {
  489. let pElm = node;
  490. while (pElm) {
  491. if (pElm.parentNode) pElm = pElm.parentNode;
  492. else break;
  493. }
  494. rootNode = pElm;
  495. }
  496. rootNode = rootNode.querySelector('html') || node.ownerDocument.documentElement || null;
  497. return rootNode
  498. },
  499. mainEnableScript: () => {
  500. var cssStyleOnReady = `
  501. html, html *,
  502. html *::before, html *::after,
  503. html *:hover, html *:link, html *:visited, html *:active,
  504. html *[style], html *[class]{
  505. -khtml-user-select: auto !important; -moz-user-select: auto !important; -ms-user-select: auto !important;
  506. -webkit-touch-callout: default !important; -webkit-user-select: auto !important; user-select: auto !important;
  507. }
  508. *:hover>img[src]{pointer-events:auto !important;}
  509. [${$.utSelectionColorHack}] :not(input):not(textarea)::selection{ background-color: Highlight !important; color: HighlightText !important;}
  510. [${$.utSelectionColorHack}] :not(input):not(textarea)::-moz-selection{ background-color: Highlight !important; color: HighlightText !important;}
  511. [${$.utTapHighlight}] *{ -webkit-tap-highlight-color: rgba(0, 0, 0, 0.18) !important;}
  512. [${$.utHoverTextWrap}]>[${$.utNonEmptyElmPrevElm}]{pointer-events:none !important;}
  513. [${$.utHoverTextWrap}]>*{z-index:inherit !important;}
  514. html[${$.utLpSelection}] *:hover, html[${$.utLpSelection}] *:hover * { cursor:text !important;}
  515. html[${$.utLpSelection}] :not(input):not(textarea)::selection {background-color: rgba(255, 156, 179,0.5) !important;}
  516. html[${$.utLpSelection}] :not(input):not(textarea)::-moz-selection {background-color: rgba(255, 156, 179,0.5) !important;}
  517. img[${$.utHoverBlock}="4"]{display:none !important;}
  518. [${$.utHoverBlock}="7"]{padding:0 !important;overflow:hidden !important;}
  519. [${$.utHoverBlock}="7"]>img[${$.utHoverBlock}="4"]:first-child{
  520. display:inline-block !important;
  521. position: relative !important;
  522. top: auto !important;
  523. left: auto !important;
  524. bottom: auto !important;
  525. right: auto !important;
  526. opacity: 0 !important;
  527. padding: 0 !important;
  528. margin: 0 !important;
  529. width: 100% !important;
  530. height: 100% !important;
  531. outline: 0 !important;
  532. border: 0 !important;
  533. box-sizing: border-box !important;
  534. transform: initial !important;
  535. user-select:none !important;
  536. z-index:1 !important;
  537. float: left !important;
  538. cursor:inherit !important;
  539. pointer-events:inherit !important;
  540. border-radius: inherit !important;
  541. background:none !important;
  542. }
  543. `.trim();
  544. $.enableSelectClickCopy()
  545. $.createCSSElement(cssStyleOnReady, document.documentElement);
  546. },
  547. mainEvents: (listenerPress, listenerRelease) => {
  548. document.addEventListener("mousedown", listenerPress, true); // Capture Event; (desktop)
  549. document.addEventListener("contextmenu", listenerPress, true); // Capture Event; (desktop&mobile)
  550. document.addEventListener("mouseup", listenerRelease, false); // Bubble Event;
  551. },
  552. disableHoverBlock: () => {
  553. var nMap = new WeakMap()
  554. function elmParam(elm) {
  555. var mElm = nMap.get(elm);
  556. if (!mElm) nMap.set(elm, mElm = {});
  557. return mElm;
  558. }
  559. function overlapArea(rect1, rect2) {
  560. let l1 = {
  561. x: rect1.left,
  562. y: rect1.top
  563. }
  564. let r1 = {
  565. x: rect1.right,
  566. y: rect1.bottom
  567. }
  568. let l2 = {
  569. x: rect2.left,
  570. y: rect2.top
  571. }
  572. let r2 = {
  573. x: rect2.right,
  574. y: rect2.bottom
  575. }
  576. // Area of 1st Rectangle
  577. let area1 = Math.abs(l1.x - r1.x) * Math.abs(l1.y - r1.y);
  578. // Area of 2nd Rectangle
  579. let area2 = Math.abs(l2.x - r2.x) * Math.abs(l2.y - r2.y);
  580. // Length of intersecting part i.e
  581. // start from max(l1.x, l2.x) of
  582. // x-coordinate and end at min(r1.x,
  583. // r2.x) x-coordinate by subtracting
  584. // start from end we get required
  585. // lengths
  586. let x_dist = Math.min(r1.x, r2.x) - Math.max(l1.x, l2.x);
  587. let y_dist = Math.min(r1.y, r2.y) - Math.max(l1.y, l2.y);
  588. let areaI = 0;
  589. if (x_dist > 0 && y_dist > 0) {
  590. areaI = x_dist * y_dist;
  591. }
  592. return {
  593. area1,
  594. area2,
  595. areaI
  596. };
  597. }
  598. function redirectEvent(event, toElement) {
  599. toElement.dispatchEvent(new event.constructor(event.type, event));
  600. if (event.type != 'wheel') event.preventDefault();
  601. event.stopPropagation();
  602. }
  603. const floatingBlockHover = new WeakMap();
  604. let _nImgs = [];
  605. function nImgFunc() {
  606. for (const s of _nImgs) {
  607. if (s.lastTime + 800 < +new Date) {
  608. s.lastTime = +new Date;
  609. return s.elm
  610. }
  611. }
  612. let nImg = document.createElement('img');
  613. nImg.setAttribute('title', ' ');
  614. nImg.setAttribute('alt', ' ');
  615. nImg.onerror = function() {
  616. if (this.parentNode != null) this.parentNode.removeChild(this)
  617. }
  618. nImg.setAttribute($.utHoverBlock, '4');
  619. const handle = function(event) {
  620. if (this === event.target) {
  621. if (event.button != 2) redirectEvent(event, this.parentNode)
  622. Promise.resolve().then(() => {
  623. for (const s of _nImgs) {
  624. if (s.elm === this) {
  625. s.lastTime = +new Date
  626. }
  627. }
  628. })
  629. }
  630. }
  631. nImg.addEventListener('click', handle, true);
  632. nImg.addEventListener('mousedown', handle, true);
  633. nImg.addEventListener('mouseup', handle, true);
  634. nImg.addEventListener('mousemove', handle, true);
  635. nImg.addEventListener('mouseover', handle, true);
  636. nImg.addEventListener('mouseout', handle, true);
  637. nImg.addEventListener('mouseenter', handle, true);
  638. nImg.addEventListener('mouseleave', handle, true);
  639. //nImg.addEventListener('wheel', handle, $.eh_capture_passive());
  640. let resObj = {
  641. elm: nImg,
  642. lastTime: +new Date,
  643. cid_fade: 0
  644. }
  645. _nImgs.push(resObj)
  646. return nImg;
  647. }
  648. const wmHoverUrl = new WeakMap();
  649. let lastMouseEnterElm = null;
  650. let lastMouseEnterAt = 0;
  651. let lastMouseEnterCid = 0;
  652. function mouseEnter() {
  653. lastMouseEnterCid = 0;
  654. if (+new Date - lastMouseEnterAt < 30) {
  655. lastMouseEnterCid = setTimeout(mouseEnter, 82)
  656. return;
  657. }
  658. //if($.lpKeyPressing)return;
  659. const targetElm = lastMouseEnterElm
  660. Promise.resolve()
  661. .then(() => {
  662. if (targetElm && targetElm.parentNode) {} else {
  663. return;
  664. }
  665. if (floatingBlockHover.get(targetElm)) {
  666. let url = null
  667. if (targetElm.getAttribute($.utHoverBlock) == '7' && (url = wmHoverUrl.get(targetElm)) && targetElm.querySelector(`[${$.utHoverBlock}]`) == null) {
  668. let _nImg = nImgFunc();
  669. if (_nImg.parentNode !== targetElm) {
  670. _nImg.setAttribute('src', url);
  671. targetElm.insertBefore(_nImg, targetElm.firstChild);
  672. }
  673. }
  674. return;
  675. }
  676. floatingBlockHover.set(targetElm, 1);
  677. return 1;
  678. }).then((ayRes) => {
  679. if (!ayRes) return;
  680. if (targetElm.nodeType != 1) return;
  681. if ("|SVG|IMG|HTML|BODY|VIDEO|AUDIO|BR|HEAD|NOSCRIPT|SCRIPT|STYLE|TEXTAREA|AREA|INPUT|FORM|BUTTON|".indexOf(`|${targetElm.nodeName}|`) >= 0) return;
  682. const targetArea = targetElm.clientWidth * targetElm.clientHeight
  683. if (targetArea > 0) {} else {
  684. return;
  685. }
  686. const targetCSS = getComputedStyle(targetElm)
  687. const targetBgImage = targetCSS.getPropertyValue('background-image');
  688. let exec1 = null
  689. if (targetBgImage != 'none' && (exec1 = /^\s*url\s*\("?([^"\)]+\b(\.gif|\.png|\.jpeg|\.jpg|\.webp)\b[^"\)]*)"?\)\s*$/i.exec(targetBgImage))) {
  690. if ((targetElm.textContent || "").trim().length > 0) return;
  691. const url = exec1[1];
  692. return url
  693. // console.log(targetBgImage,[...exec1])
  694. }
  695. if (targetCSS.getPropertyValue('position') == 'absolute' && +targetCSS.getPropertyValue('z-index') > 0) {} else {
  696. return;
  697. }
  698. if ((targetElm.textContent || "").trim().length > 0) return;
  699. let possibleResults = [];
  700. for (const imgElm of document.querySelectorAll('img[src]')) {
  701. const param = elmParam(imgElm)
  702. if (!param.area) {
  703. const area = imgElm.clientWidth * imgElm.clientHeight
  704. if (area > 0) param.area = area;
  705. }
  706. if (param.area > 0) {
  707. if (targetArea > param.area * 0.9) possibleResults.push(imgElm)
  708. }
  709. }
  710. let i = 0;
  711. let j = 0;
  712. for (const imgElm of possibleResults) {
  713. const cmpVal = targetElm.compareDocumentPosition(imgElm)
  714. /*
  715. 1: The two nodes do not belong to the same document.
  716. 2: p1 is positioned after p2.
  717. 4: p1 is positioned before p2.
  718. 8: p1 is positioned inside p2.
  719. 16: p2 is positioned inside p1.
  720. 32: The two nodes has no relationship, or they are two attributes on the same element.
  721. */
  722. if (cmpVal & 8 || cmpVal & 16) return;
  723. if (cmpVal & 2) j++; // I<p
  724. else if (cmpVal & 4) break; // I>p
  725. i++;
  726. }
  727. // before: j-1 after: j
  728. let indexBefore = j - 1;
  729. let indexAfter = j;
  730. if (indexBefore < 0) indexBefore = 0;
  731. if (indexAfter > possibleResults.length - 1) indexAfter = possibleResults.length - 1;
  732. // setTimeout(function(){
  733. for (let i = indexBefore; i <= indexAfter; i++) {
  734. const s = possibleResults[i];
  735. const {
  736. area1,
  737. area2,
  738. areaI
  739. } = overlapArea(targetElm.getBoundingClientRect(), s.getBoundingClientRect())
  740. const criteria = area1 * 0.7
  741. if (areaI > 0.9 * area2) {
  742. return s.getAttribute('src')
  743. }
  744. }
  745. // },1000);
  746. }).then((sUrl) => {
  747. if (typeof sUrl != 'string') return;
  748. //console.log(targetElm, targetElm.querySelectorAll('img').length)
  749. // console.log(313, evt.target, s)
  750. let _nImg = nImgFunc();
  751. if (_nImg.parentNode !== targetElm) {
  752. _nImg.setAttribute('src', sUrl);
  753. targetElm.insertBefore(_nImg, targetElm.firstChild);
  754. wmHoverUrl.set(targetElm, sUrl);
  755. targetElm.setAttribute($.utHoverBlock, '7');
  756. }
  757. })
  758. }
  759. document.addEventListener('mouseenter', function(evt) {
  760. if(!$.gm_disablehover_enable) return;
  761. lastMouseEnterElm = evt.target
  762. lastMouseEnterAt = +new Date;
  763. if (!lastMouseEnterCid) lastMouseEnterCid = setTimeout(mouseEnter, 82)
  764. }, $.eh_capture_passive())
  765. },
  766. acrAuxDown: function(evt) {
  767. if(!$.gm_prevent_aux_click_enable) return;
  768. if (evt.button === 1 ){
  769. let check = $.dmmMouseUpLast > $.dmmMouseDownLast && evt.timeStamp - $.dmmMouseUpLast < 40
  770. $.dmmMouseDownLast = evt.timeStamp;
  771. if( check ) {
  772. $.eventCancel(evt, true);
  773. }
  774. }
  775. },
  776. acrAuxUp: function(evt) {
  777. if(!$.gm_prevent_aux_click_enable) return;
  778. if (evt.button === 1 ){
  779. let check = $.dmmMouseDownLast > $.dmmMouseUpLast && evt.timeStamp - $.dmmMouseDownLast < 40;
  780. $.dmmMouseUpLast = evt.timeStamp;
  781. if( check ) {
  782. $.dmmMouseUpCancel = evt.timeStamp;
  783. $.eventCancel(evt, true);
  784. }
  785. }
  786. },
  787. acrAuxClick: function(evt) {
  788. if(!$.gm_prevent_aux_click_enable) return;
  789. if (evt.button === 1 ){
  790. if( evt.timeStamp - $.dmmMouseUpCancel < 40 ) {
  791. $.eventCancel(evt, true);
  792. }
  793. }
  794. },
  795. preventAuxClickRepeat:function(){
  796. document.addEventListener('mousedown', $.acrAuxDown, {
  797. capture: true,
  798. passive: false
  799. })
  800. document.addEventListener('mouseup', $.acrAuxUp, {
  801. capture: true,
  802. passive: false
  803. })
  804. document.addEventListener('auxclick', $.acrAuxClick, {
  805. capture: true,
  806. passive: false
  807. })
  808. },
  809. MenuEnable: (
  810. class MenuEnable{
  811. constructor(textToEnable, textToDisable, callback, initalEnable){
  812. this.textToEnable=textToEnable;
  813. this.textToDisable=textToDisable;
  814. this.callback=callback;
  815. this.gx=this.gx.bind(this);
  816. }
  817. unregister(){
  818. (this.h>=0)?(GM_unregisterMenuCommand(this.h), (this.h=0)):0;
  819. }
  820. register(text){
  821. if(typeof text == 'string') this.showText = text;
  822. text = this.showText;
  823. if(typeof text != 'string') return;
  824. this.h=GM_registerMenuCommand(text, this.gx);
  825. }
  826. a(o){
  827. if(this.enabled===o.bEnable) return;
  828. this.enabled = o.bEnable;
  829. this.unregister();
  830. let pr= 0 ;
  831. if($.gm_status_fn_store && $.gm_status_fn_store.indexOf(this)>=0){
  832. let store = $.gm_status_fn_store
  833. let idx = store.indexOf(this)
  834. let count = store.length;
  835. if(idx>=0&&idx<=count-2){
  836. console.log(idx, count)
  837. for(let jdx=idx+1;jdx<count;jdx++){
  838. store[jdx].unregister();
  839. }
  840. this.register (o.bText);
  841. for(let jdx=idx+1;jdx<count;jdx++){
  842. store[jdx].register();
  843. }
  844. pr=1;
  845. }
  846. }
  847. if(!pr) this.register (o.bText);
  848. this.callback(this.enabled, o.byUserInput);
  849. }
  850. enableNow(byUserInput){
  851. this.a({
  852. bEnable: true,
  853. bText: this.textToDisable,
  854. byUserInput
  855. });
  856. }
  857. gx(){
  858. if(this.enabled) this.disableNow(true);
  859. else this.enableNow(true);
  860. }
  861. disableNow(byUserInput){
  862. this.a({
  863. bEnable: false,
  864. bText: this.textToEnable,
  865. byUserInput
  866. });
  867. }
  868. toggle(enable, byUserInput){
  869. enable?this.enableNow(byUserInput):this.disableNow(byUserInput);
  870. }
  871. }
  872. ),
  873. gm_status_fn: function(gm_name, textToEnable, textToDisable, callback){
  874. let menuEnableName = gm_name + "$menuEnable";
  875. function set_gm(enabled){
  876. $[gm_name]=enabled;
  877. let menuEnable = $[menuEnableName];
  878. if( menuEnable ){
  879. menuEnable.toggle(enabled)
  880. };
  881. callback(enabled)
  882. }
  883. set_gm(!!GM_getValue(gm_name));
  884. GM_addValueChangeListener(gm_name,function(name, old_value, new_value, remote){
  885. if(old_value === new_value) return;
  886. if(new_value === $[gm_name]) return;
  887. set_gm(new_value);
  888. });
  889. if(!inIframe()){
  890. $.gm_status_fn_store=$.gm_status_fn_store||[];
  891. $[menuEnableName]=new $.MenuEnable(textToEnable, textToDisable, (enabled) => {
  892. GM_setValue(gm_name, !!enabled);
  893. });
  894. $.gm_status_fn_store.push($[menuEnableName]);
  895. $[menuEnableName].toggle(!!GM_getValue(gm_name));
  896. }
  897. }
  898. }
  899. // $.holdingElm=null;
  900. $.mainEnableScript();
  901. if (isSupportAdvancedEventListener()) $.lpEnable(); // top capture event for alt-click
  902. $.mainEvents(
  903. function(evt) {
  904. // $.holdingElm=evt.target;
  905. // console.log('down',evt.target)
  906. if ($.onceCssHighlightSelection) window.requestAnimationFrame($.onceCssHighlightSelection);
  907. if (evt.button == 2 || evt.type == "contextmenu") $.mAlert_DOWN();
  908. },
  909. function(evt) {
  910. // $.holdingElm=null;
  911. // console.log('up',evt.target)
  912. if (evt.button == 2) $.mAlert_UP();
  913. if ($.enableDragging) {
  914. $.enableDragging = false;
  915. }
  916. }
  917. );
  918. $.disableHoverBlock();
  919. $.preventAuxClickRepeat();
  920. console.log('userscript running - To Re-Enable Selection & Copying');
  921. $.gm_status_fn("gm_lp_enable", "To Enable `Enhanced build-in Alt Text Selection`", "To Disable `Enhanced build-in Alt Text Selection`", ()=>{
  922. // callback
  923. });
  924. $.gm_status_fn("gm_disablehover_enable", "To Enable `Hover on Image`", "To Disable `Hover on Image`", ()=>{
  925. // callback
  926. });
  927. $.gm_status_fn("gm_prevent_aux_click_enable", "To Enable `Repetitive AuxClick Prevention`", "To Disable `Repetitive AuxClick Prevention`", ()=>{
  928. // callback
  929. });
  930. })();