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-22 提交的版本,查看 最新版本

  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.8.13
  5. // @description Enables select, right-click, copy and drag on pages that disable them. Enhanced Feature: Alt Key HyperLink Text Selection
  6. // @description:zh-TW 解除禁止復制、剪切、選擇文本、右鍵菜單的限制。破解鎖右鍵、文字複製、文字選取。增強功能:Alt鍵超連結文字選取。
  7. // @include /^https?\:\/\//
  8. // @grant none
  9. // @run-at document-start
  10. // @namespace https://greasyfork.org/users/371179
  11. // ==/UserScript==
  12. (function $$() {
  13. 'use strict';
  14. if (document == null || !document.documentElement) return window.requestAnimationFrame($$); // this is tampermonkey bug?? not sure
  15. //console.log('script at', location)
  16.  
  17. function $nil() {}
  18.  
  19. function isSupportAdvancedEventListener() {
  20. if ('_b1750' in $) return $._b1750
  21. var prop = 0;
  22. document.createAttribute('z').addEventListener('nil', $nil, {
  23. get passive() {
  24. prop++;
  25. },
  26. get once() {
  27. prop++;
  28. }
  29. });
  30. return ($._b1750 = prop == 2);
  31. }
  32.  
  33. function isSupportPassiveEventListener() {
  34. if ('_b1650' in $) return $._b1650
  35. var prop = 0;
  36. document.createAttribute('z').addEventListener('nil', $nil, {
  37. get passive() {
  38. prop++;
  39. }
  40. });
  41. return ($._b1650 = prop == 1);
  42. }
  43.  
  44. var getSelection = window.getSelection || Error()(),
  45. requestAnimationFrame = window.requestAnimationFrame || Error()(),
  46. getComputedStyle = window.getComputedStyle || Error()();
  47.  
  48. const $ = {
  49. utSelectionColorHack: 'msmtwejkzrqa',
  50. utTapHighlight: 'xfcklblvkjsj',
  51. utLpSelection: 'gykqyzwufxpz',
  52. utHoverBlock: 'meefgeibrtqx', //scc_emptyblock
  53. ksFuncReplacerNonFalse: '___dqzadwpujtct___',
  54. ksEventReturnValue: ' ___ndjfujndrlsx___',
  55. ksSetData: '___rgqclrdllmhr___',
  56.  
  57. eh_capture_passive: () => isSupportPassiveEventListener() ? ($._eh_capture_passive = ($._eh_capture_passive || {
  58. capture: true,
  59. passive: true
  60. })) : true,
  61.  
  62. mAlert_DOWN: function() {}, //dummy function in case alert replacement is not valid
  63. mAlert_UP: function() {}, //dummy function in case alert replacement is not valid
  64.  
  65. isAnySelection: function() {
  66. var sel = getSelection();
  67. return !sel ? null : (typeof sel.isCollapsed == 'boolean') ? !sel.isCollapsed : (sel.toString().length > 0);
  68. },
  69.  
  70. createCSSElement: function(cssStyle, container) {
  71. var css = document.createElement('style'); //slope: DOM throughout
  72. css.type = 'text/css';
  73. css.innerHTML = cssStyle;
  74. if (container) container.appendChild(css);
  75. return css;
  76. },
  77.  
  78. createFakeAlert: function(_alert) {
  79. if (typeof _alert != 'function') return null;
  80.  
  81. function alert(msg) {
  82. alert.__isDisabled__() ? console.log("alert msg disabled: ", msg) : _alert.apply(this, arguments);
  83. };
  84. alert.toString = () => "function alert() { [native code] }";
  85. return alert;
  86. },
  87.  
  88. createFuncReplacer: function(originalFunc, pName, resFX) {
  89. resFX = function(ev) {
  90. var res = originalFunc.apply(this, arguments);
  91. if (!this || this[pName] != resFX) return res; // if this is null or undefined, or this.onXXX is not this function
  92. if (res === false) return; // return undefined when "return false;"
  93. originalFunc[$.ksFuncReplacerNonFalse] = true;
  94. this[pName] = originalFunc; // restore original
  95. return res;
  96. }
  97. resFX.toString = () => originalFunc.toString();
  98. return resFX;
  99. },
  100.  
  101. listenerDisableAll: function(evt) {
  102. var elmNode = evt.target;
  103. var pName = 'on' + evt.type;
  104. evt = null;
  105. Promise.resolve().then(() => {
  106. while (elmNode && elmNode.nodeType > 0) { //i.e. HTMLDocument or HTMLElement
  107. var f = elmNode[pName];
  108. if (typeof f == 'function' && f[$.ksFuncReplacerNonFalse] !== true) {
  109. var nf = $.createFuncReplacer(f, pName);
  110. nf[$.ksFuncReplacerNonFalse] = true;
  111. elmNode[pName] = nf;
  112. }
  113. elmNode = elmNode.parentNode;
  114. }
  115. })
  116. },
  117.  
  118. onceCssHighlightSelection: () => {
  119. if (document.documentElement.hasAttribute($.utLpSelection)) return;
  120. $.onceCssHighlightSelection = null
  121. Promise.resolve().then(() => {
  122. 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
  123. var elm = !s.length ? document.body : s[s.length >> 1];
  124. return elm
  125. }).then(elm => {
  126. var selectionStyle = getComputedStyle(elm, ':selection');
  127. if (/^rgba\(\d+,\s*\d+,\s*\d+,\s*0\)$/.test(selectionStyle.getPropertyValue('background-color'))) document.documentElement.setAttribute($.utSelectionColorHack, "");
  128. return elm;
  129. }).then(elm => {
  130. var elmStyle = getComputedStyle(elm)
  131. if (/^rgba\(\d+,\s*\d+,\s*\d+,\s*0\)$/.test(elmStyle.getPropertyValue('-webkit-tap-highlight-color'))) document.documentElement.setAttribute($.utTapHighlight, "");
  132. })
  133. },
  134.  
  135. isCurrentClipboardDataReplaced: function(clipboardData) {
  136. var items = clipboardData ? clipboardData.items : null;
  137. if (items && items.length > 0) {
  138. for (var i = 0, l = items.length; i < l; i++) {
  139. if (items[i].type == 'text/plain') return true;
  140. }
  141. }
  142. return false;
  143. },
  144.  
  145. replacementSetData: function(_setData, evt) {
  146. if (typeof _setData != 'function') return;
  147.  
  148. function setData() {
  149. var res = _setData.apply(this, arguments);
  150. try {
  151. if (evt.clipboardData === this && this.setData === setData && evt.cancelable && evt.defaultPrevented === false) {
  152. if ($.isCurrentClipboardDataReplaced(evt.clipboardData)) {
  153. evt.preventDefault();
  154. if (evt.defaultPrevented === true) {
  155. this.setData = _setData;
  156. delete this[$.ksSetData];
  157. }
  158. }
  159. }
  160. } catch (e) {}
  161. return res;
  162. }
  163. setData.toString = () => _setData.toString();
  164. evt.clipboardData.setData = setData;
  165. evt.clipboardData[$.ksSetData] = _setData;
  166. },
  167.  
  168.  
  169. enableSelectClickCopy: function() {
  170.  
  171. $.eyEvts = ['keydown', 'keyup', 'copy', 'contextmenu', 'select', 'selectstart', 'dragstart', 'beforecopy']; //slope: throughout
  172.  
  173. function isDeactivePreventDefault(evt) {
  174. if ($.bypass) return false;
  175. var j = $.eyEvts.indexOf(evt.type);
  176. switch (j) {
  177. case 6:
  178. if ($.enableDragging) return false;
  179. if (evt.target.hasAttribute('draggable')) {
  180. $.enableDragging = true;
  181. return false;
  182. }
  183. //if(evt.target.hasAttribute('draggable')&&evt.target!=window.getSelection().anchorNode)return false;
  184. return true;
  185. case 3:
  186. if (evt.target instanceof Element && (evt.target.textContent || "").trim().length === 0) return false; //exclude elements like video
  187. return true;
  188. case -1:
  189. return false;
  190. case 0:
  191. case 1:
  192. return (evt.keyCode == 67 && (evt.ctrlKey || evt.metaKey) && !evt.altKey && !evt.shiftKey && $.isAnySelection() === true);
  193. case 2:
  194.  
  195. if (!('clipboardData' in evt && 'setData' in DataTransfer.prototype)) return true; // Event oncopy not supporting clipboardData
  196. // see the richtext hack in https://www.cleancss.com/css-beautify/
  197. // see https://developer.mozilla.org/zh-CN/docs/Web/API/Element/copy_event
  198. // see https://w3c.github.io/clipboard-apis/#widl-ClipboardEvent-clipboardData
  199.  
  200. if ($.isCurrentClipboardDataReplaced(evt.clipboardData) == false) { //no replacement data
  201. if (!evt.clipboardData[$.ksSetData] && evt.cancelable && evt.defaultPrevented === false) $.replacementSetData(evt.clipboardData.setData, evt);
  202. return true;
  203. }
  204. var trimedSelectionText = getSelection().toString().trim()
  205. if (trimedSelectionText) {
  206. //there is replacement data and the selection is not empty
  207. console.log("copy event - clipboardData replacement is allowed and the selection is not empty", trimedSelectionText)
  208. return false;
  209. } else {
  210. //there is replacement data and the selection is empty
  211. return false;
  212. }
  213. break; // for js formatting only
  214.  
  215. default:
  216. return true;
  217. }
  218. }
  219.  
  220. Event.prototype.preventDefault = (function(f) {
  221. function preventDefault() {
  222. if (!isDeactivePreventDefault(this)) f.call(this);
  223. }
  224. preventDefault.toString = () => f.toString();
  225. return preventDefault;
  226. })(Event.prototype.preventDefault);
  227.  
  228. Object.defineProperty(Event.prototype, "returnValue", {
  229. get() {
  230. return $.ksEventReturnValue in this ? this[$.ksEventReturnValue] : true;
  231. },
  232. set(newValue) {
  233. if (newValue === false && !isDeactivePreventDefault(this)) this.preventDefault();
  234. this[$.ksEventReturnValue] = newValue;
  235. },
  236. enumerable: true,
  237. configurable: true
  238. });
  239.  
  240. for (var i = 2, eventsCount = $.eyEvts.length; i < eventsCount; i++) {
  241. document.addEventListener($.eyEvts[i], $.listenerDisableAll, true); // Capture Event; passive:false; expected occurrence COMPLETELY before Target Capture and Target Bubble
  242. }
  243.  
  244. var _alert = window.alert; //slope: temporary
  245. if (typeof _alert == 'function') {
  246. var _mAlert = $.createFakeAlert(_alert);
  247. if (_mAlert) {
  248. var clickBlockingTo = 0;
  249. _mAlert.__isDisabled__ = () => clickBlockingTo > +new Date;
  250. $.mAlert_DOWN = () => (clickBlockingTo = +new Date + 50);
  251. $.mAlert_UP = () => (clickBlockingTo = +new Date + 20);
  252. window.alert = _mAlert
  253. }
  254. }
  255.  
  256. },
  257.  
  258. lpCheckPointer: function(targetElm) {
  259. if (targetElm && targetElm.nodeType == 1 && targetElm.matches('*:hover')) {
  260. if (getComputedStyle(targetElm).getPropertyValue('cursor') == 'pointer' && targetElm.textContent) return true;
  261. }
  262. return false;
  263. },
  264.  
  265. lpFullCancel: function(evt, toPreventDefault) {
  266. $.bypass = true;
  267. !toPreventDefault || evt.preventDefault()
  268. evt.stopPropagation();
  269. evt.stopImmediatePropagation();
  270. $.bypass = false;
  271. },
  272.  
  273. lpMouseDown: function(evt) {
  274. $.lpMouseActive = 0;
  275. if (evt.altKey && !evt.ctrlKey && !evt.metaKey && !evt.shiftKey && evt.button === 0 && $.lpCheckPointer(evt.target)) {
  276. $.lpMouseActive = 1;
  277. $.lpFullCancel(evt, false);
  278. document.documentElement.setAttribute($.utLpSelection, '');
  279. }
  280. },
  281.  
  282. lpMouseUp: function(evt) {
  283. if ($.lpMouseActive == 1) {
  284. $.lpMouseActive = 2;
  285. document.documentElement.removeAttribute($.utLpSelection);
  286. $.lpFullCancel(evt, false);
  287. if ($.onceCssHighlightSelection) window.requestAnimationFrame($.onceCssHighlightSelection);
  288. }
  289. },
  290.  
  291. lpClick: function(evt) {
  292. if ($.lpMouseActive == 2) {
  293. $.lpFullCancel(evt, false);
  294. }
  295. },
  296.  
  297. lpEnable: function() { // this is an optional feature for modern browser
  298. // the built-in browser feature has already disabled the default event behavior, the coding is just to ensure no "tailor-made behavior" occuring.
  299. document.addEventListener('mousedown', $.lpMouseDown, {
  300. capture: true,
  301. passive: true
  302. })
  303. document.addEventListener('mouseup', $.lpMouseUp, {
  304. capture: true,
  305. passive: true
  306. })
  307. document.addEventListener('click', $.lpClick, {
  308. capture: true,
  309. passive: true
  310. })
  311. },
  312.  
  313. mainEnableScript: () => {
  314. var cssStyleOnReady = `
  315. html, html *,
  316. html *::before, html *::after,
  317. html *:hover, html *:link, html *:visited, html *:active,
  318. html *[style], html *[class]{
  319. -khtml-user-select: auto !important; -moz-user-select: auto !important; -ms-user-select: auto !important;
  320. -webkit-touch-callout: default !important; -webkit-user-select: auto !important; user-select: auto !important;
  321. }
  322. *:hover>img[src]{pointer-events:auto !important;}
  323.  
  324. [${$.utSelectionColorHack}] :not(input):not(textarea)::selection{ background-color: Highlight !important; color: HighlightText !important;}
  325. [${$.utSelectionColorHack}] :not(input):not(textarea)::-moz-selection{ background-color: Highlight !important; color: HighlightText !important;}
  326. [${$.utTapHighlight}] *{ -webkit-tap-highlight-color: rgba(0, 0, 0, 0.18) !important;}
  327.  
  328. html[${$.utLpSelection}] *:hover, html[${$.utLpSelection}] *:hover * { cursor:text !important;}
  329. html[${$.utLpSelection}] :not(input):not(textarea)::selection {background-color: rgba(255, 156, 179,0.5) !important;}
  330. html[${$.utLpSelection}] :not(input):not(textarea)::-moz-selection {background-color: rgba(255, 156, 179,0.5) !important;}
  331.  
  332. [${$.utHoverBlock}="2"]{pointer-events:none !important;user-select:none !important;}
  333. img[${$.utHoverBlock}="4"]{
  334. display:none !important;
  335. }
  336. [${$.utHoverBlock}="7"]{
  337. padding:0 !important;
  338. }
  339. [${$.utHoverBlock}="7"]>img[${$.utHoverBlock}="4"]:first-child{
  340. display:inline-block !important;
  341. opacity: 0 !important;
  342. padding: 0 !important;
  343. margin: 0 !important;
  344. position: relative !important;
  345. z-index:1 !important;
  346. width: 100% !important;
  347. height: 100% !important;
  348. left: 0 !important;
  349. top: 0 !important;
  350. outline: 0 !important;
  351. border: 0 !important;
  352. box-sizing: border-box !important;
  353. transform: initial !important;
  354. float: left !important;
  355. pointer-events:auto !important; user-select:none !important;cursor:inherit !important;}
  356.  
  357. `.trim();
  358.  
  359. $.enableSelectClickCopy()
  360. $.createCSSElement(cssStyleOnReady, document.documentElement);
  361.  
  362. },
  363.  
  364. mainEvents: (listenerPress, listenerRelease) => {
  365. document.addEventListener("mousedown", listenerPress, true); // Capture Event; (desktop)
  366. document.addEventListener("contextmenu", listenerPress, true); // Capture Event; (desktop&mobile)
  367. document.addEventListener("mouseup", listenerRelease, false); // Bubble Event;
  368. },
  369.  
  370. disableHoverBlock: () => {
  371.  
  372. var nMap = new WeakMap()
  373.  
  374. function elmParam(elm) {
  375.  
  376. var mElm = nMap.get(elm);
  377. if (!mElm) nMap.set(elm, mElm = {});
  378. return mElm;
  379. }
  380.  
  381. function overlapArea(rect1, rect2) {
  382.  
  383. let l1 = {
  384. x: rect1.left,
  385. y: rect1.top
  386. }
  387.  
  388. let r1 = {
  389. x: rect1.right,
  390. y: rect1.bottom
  391. }
  392. let l2 = {
  393. x: rect2.left,
  394. y: rect2.top
  395. }
  396.  
  397. let r2 = {
  398. x: rect2.right,
  399. y: rect2.bottom
  400. }
  401.  
  402. // Area of 1st Rectangle
  403. let area1 = Math.abs(l1.x - r1.x) * Math.abs(l1.y - r1.y);
  404.  
  405. // Area of 2nd Rectangle
  406. let area2 = Math.abs(l2.x - r2.x) * Math.abs(l2.y - r2.y);
  407.  
  408. // Length of intersecting part i.e
  409. // start from max(l1.x, l2.x) of
  410. // x-coordinate and end at min(r1.x,
  411. // r2.x) x-coordinate by subtracting
  412. // start from end we get required
  413. // lengths
  414. let x_dist = Math.min(r1.x, r2.x) - Math.max(l1.x, l2.x);
  415. let y_dist = (Math.min(r1.y, r2.y) - Math.max(l1.y, l2.y));
  416. let areaI = 0;
  417. if (x_dist > 0 && y_dist > 0) {
  418. areaI = x_dist * y_dist;
  419. }
  420.  
  421. return {
  422. area1,
  423. area2,
  424. areaI
  425. };
  426.  
  427.  
  428. }
  429.  
  430. function redirectEvent(event, toElement) {
  431.  
  432. toElement.dispatchEvent(new event.constructor(event.type, event));
  433. if (event.type != 'wheel') event.preventDefault();
  434. event.stopPropagation();
  435. }
  436.  
  437. const floatingBlockHover = new WeakMap();
  438.  
  439. let _nImgs = [];
  440.  
  441. function nImgFunc() {
  442.  
  443. for (const s of _nImgs) {
  444. if (s.lastTime + 800 < +new Date) {
  445. s.lastTime = +new Date;
  446. return s.elm
  447. }
  448. }
  449.  
  450. let nImg = document.createElement('img');
  451. nImg.onerror = function() {
  452. if (this.parentNode != null) this.parentNode.removeChild(this)
  453. }
  454. nImg.setAttribute($.utHoverBlock, '4');
  455. const handle = function(event) {
  456. if (this === event.target) {
  457. if (event.button != 2) redirectEvent(event, this.parentNode)
  458. Promise.resolve().then(() => {
  459. for (const s of _nImgs) {
  460. if (s.elm === this) {
  461. s.lastTime = +new Date
  462. }
  463. }
  464. })
  465. }
  466. }
  467. nImg.addEventListener('click', handle, true);
  468. nImg.addEventListener('mousedown', handle, true);
  469. nImg.addEventListener('mouseup', handle, true);
  470. nImg.addEventListener('mousemove', handle, true);
  471. nImg.addEventListener('mouseover', handle, true);
  472. nImg.addEventListener('mouseout', handle, true);
  473. nImg.addEventListener('mouseenter', handle, true);
  474. nImg.addEventListener('mouseleave', handle, true);
  475. //nImg.addEventListener('wheel', handle, $.eh_capture_passive());
  476. _nImgs.push({
  477. elm: nImg,
  478. lastTime: +new Date
  479. })
  480.  
  481. return nImg;
  482.  
  483. }
  484.  
  485. const wmHoverUrl=new WeakMap();
  486.  
  487. document.addEventListener('mouseenter', function(evt) {
  488.  
  489. Promise.resolve()
  490. .then(() => {
  491.  
  492. if (evt && evt.target && evt.target.parentNode) {} else {
  493. return;
  494. }
  495.  
  496.  
  497.  
  498. if(floatingBlockHover.get(evt.target)){
  499.  
  500. let url = null
  501. if(evt.target.getAttribute($.utHoverBlock)=='7' && (url=wmHoverUrl.get(evt.target)) && evt.target.querySelector(`[${$.utHoverBlock}]`)==null ){
  502.  
  503. let _nImg = nImgFunc();
  504.  
  505. if (_nImg.parentNode !== evt.target) {
  506. _nImg.setAttribute('src', url);
  507. evt.target.insertBefore(_nImg, evt.target.firstChild);
  508. }
  509.  
  510.  
  511. }
  512.  
  513.  
  514.  
  515. return;
  516. }
  517.  
  518.  
  519. function setNULL() {
  520. floatingBlockHover.set(evt.target, 1)
  521. }
  522.  
  523. setNULL();
  524.  
  525. return 1;
  526.  
  527. }).then((ayRes) => {
  528.  
  529. if (!ayRes) return;
  530.  
  531. if (evt.target.nodeType != 1) return;
  532. if ("|SVG|IMG|HTML|BODY|VIDEO|AUDIO|BR|HEAD|NOSCRIPT|SCRIPT|STYLE|TEXTAREA|AREA|INPUT|FORM|BUTTON|".indexOf(`|${evt.target.nodeName}|`) >= 0) return;
  533.  
  534.  
  535.  
  536. const targetArea = evt.target.clientWidth * evt.target.clientHeight
  537.  
  538. if (targetArea > 0) {} else {
  539. return;
  540. }
  541.  
  542. const targetCSS = getComputedStyle(evt.target)
  543. const targetBgImage = targetCSS.getPropertyValue('background-image');
  544. let exec1 = null
  545.  
  546. if (targetBgImage != 'none' && (exec1 = /^\s*url\s*\("?([^"\)]+\b(\.gif|\.png|\.jpeg|\.jpg|\.webp)\b[^"\)]*)"?\)\s*$/i.exec(targetBgImage))) {
  547. if ((evt.target.textContent || "").trim().length > 0) return;
  548. const url = exec1[1];
  549. return url
  550.  
  551. // console.log(targetBgImage,[...exec1])
  552. }
  553.  
  554.  
  555.  
  556. if (targetCSS.getPropertyValue('position') == 'absolute' && +targetCSS.getPropertyValue('z-index') > 0) {} else {
  557. return;
  558. }
  559. if ((evt.target.textContent || "").trim().length > 0) return;
  560.  
  561. let possibleResults = [];
  562.  
  563. for (const imgElm of document.querySelectorAll('img[src]')) {
  564. const param = elmParam(imgElm)
  565. if (!param.area) {
  566. const area = imgElm.clientWidth * imgElm.clientHeight
  567. if (area > 0) param.area = area;
  568. }
  569. if (param.area > 0) {
  570. if (targetArea > param.area * 0.9) possibleResults.push(imgElm)
  571. }
  572. }
  573.  
  574. let i = 0;
  575. let j = 0;
  576. for (const imgElm of possibleResults) {
  577.  
  578. const cmpVal = evt.target.compareDocumentPosition(imgElm)
  579.  
  580. /*
  581.  
  582.  
  583. 1: The two nodes do not belong to the same document.
  584. 2: p1 is positioned after p2.
  585. 4: p1 is positioned before p2.
  586. 8: p1 is positioned inside p2.
  587. 16: p2 is positioned inside p1.
  588. 32: The two nodes has no relationship, or they are two attributes on the same element.
  589.  
  590. */
  591.  
  592. if (cmpVal & 8 || cmpVal & 16) return;
  593. if (cmpVal & 2) j++; // I<p
  594. else if (cmpVal & 4) break; // I>p
  595.  
  596.  
  597. i++;
  598.  
  599. }
  600.  
  601. // before: j-1 after: j
  602.  
  603. let indexBefore = j - 1;
  604. let indexAfter = j;
  605. if (indexBefore < 0) indexBefore = 0;
  606. if (indexAfter > possibleResults.length - 1) indexAfter = possibleResults.length - 1;
  607.  
  608. // setTimeout(function(){
  609. for (let i = indexBefore; i <= indexAfter; i++) {
  610. const s = possibleResults[i];
  611. const {
  612. area1,
  613. area2,
  614. areaI
  615. } = overlapArea(evt.target.getBoundingClientRect(), s.getBoundingClientRect())
  616. const criteria = area1 * 0.7
  617. if (areaI > 0.9 * area2) {
  618.  
  619.  
  620. return s.getAttribute('src')
  621.  
  622.  
  623. }
  624. }
  625. // },1000);
  626.  
  627. }).then((sUrl) => {
  628.  
  629. if (typeof sUrl != 'string') return;
  630.  
  631.  
  632. // console.log(313, evt.target, s)
  633. let _nImg = nImgFunc();
  634.  
  635.  
  636. if (_nImg.parentNode !== evt.target) {
  637. _nImg.setAttribute('src', sUrl);
  638. evt.target.insertBefore(_nImg, evt.target.firstChild);
  639. wmHoverUrl.set(evt.target, sUrl);
  640. evt.target.setAttribute($.utHoverBlock, '7');
  641. }
  642.  
  643.  
  644.  
  645. })
  646.  
  647. }, $.eh_capture_passive())
  648.  
  649. }
  650.  
  651. }
  652.  
  653. document.addEventListener('selectstart', function(e) {
  654. if ($.enableDragging || (e.target.textContent||"").trim().length===0) {
  655. $.bypass = true;
  656. e.preventDefault();
  657. $.bypass = false;
  658. }
  659. }, true)
  660.  
  661. $.mainEnableScript();
  662.  
  663. if (isSupportAdvancedEventListener()) $.lpEnable(); // top capture event for alt-click
  664.  
  665. $.mainEvents(
  666. function(evt) {
  667. if ($.onceCssHighlightSelection) window.requestAnimationFrame($.onceCssHighlightSelection);
  668. if (evt.button == 2 || evt.type == "contextmenu") $.mAlert_DOWN();
  669. },
  670. function(evt) {
  671. if (evt.button == 2) $.mAlert_UP();
  672. if ($.enableDragging) {
  673. $.enableDragging = false;
  674. }
  675. }
  676. );
  677.  
  678. $.disableHoverBlock();
  679.  
  680. console.log('userscript running - To Re-Enable Selection & Copying');
  681.  
  682.  
  683.  
  684.  
  685. })();