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