通用脚本重开启选取复制

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

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

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