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