A Universal Script to Re-Enable the Selection and Copying

Enables select, right-click, copy and drag on pages that disable them. Enhanced Feature: Alt Key HyperLink Text Selection

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

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