通用脚本重开启选取复制

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

当前为 2022-12-14 提交的版本,查看 最新版本

  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.8.1.6
  6. // @icon https://cdn-icons-png.flaticon.com/512/3388/3388671.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. // @match http://*/*
  11. // @match https://*/*
  12. // @exclude /^https?://\S+\.(txt|png|jpg|jpeg|gif|xml|svg|manifest|log|ini)[^\/]*$/
  13. // @exclude https://github.dev/*/*
  14. // @grant GM_registerMenuCommand
  15. // @grant GM_unregisterMenuCommand
  16. // @grant GM_setValue
  17. // @grant GM_getValue
  18. // @grant GM_addValueChangeListener
  19. // @grant GM_listValues
  20. // @run-at document-start
  21. // @namespace https://greasyfork.org/users/371179
  22. // ==/UserScript==
  23. (function $$() {
  24. 'use strict';
  25. if (document == null || !document.documentElement) return window.requestAnimationFrame($$); // this is tampermonkey bug?? not sure
  26. //console.log('script at', location)
  27.  
  28. function $nil() {}
  29.  
  30. function isLatestBrowser(){
  31.  
  32. let res;
  33. try{
  34. let o ={$nil};
  35. o?.$nil();
  36. o=null;
  37. o?.$nil();
  38. res = true;
  39. }catch(e){}
  40. return !!res;
  41. }
  42. if(!isLatestBrowser()) throw 'Browser version before 2020-01-01 is not supported. Please update to the latest version.';
  43.  
  44. function isSupportAdvancedEventListener() {
  45. if ('_b1750' in $) return $._b1750
  46. var prop = 0;
  47. document.createAttribute('z').addEventListener('nil', $nil, {
  48. get passive() {
  49. prop++;
  50. },
  51. get once() {
  52. prop++;
  53. }
  54. });
  55. return ($._b1750 = prop == 2);
  56. }
  57.  
  58. function isSupportPassiveEventListener() {
  59. if ('_b1650' in $) return $._b1650
  60. var prop = 0;
  61. document.createAttribute('z').addEventListener('nil', $nil, {
  62. get passive() {
  63. prop++;
  64. }
  65. });
  66. return ($._b1650 = prop == 1);
  67. }
  68.  
  69. function inIframe () {
  70. try {
  71. return window.self !== window.top;
  72. } catch (e) {
  73. return true;
  74. }
  75. }
  76.  
  77. var getSelection = window.getSelection || Error()(),
  78. requestAnimationFrame = window.requestAnimationFrame || Error()(),
  79. getComputedStyle = window.getComputedStyle.bind(window) || Error()();
  80.  
  81. const $ = {
  82. utSelectionColorHack: 'msmtwejkzrqa',
  83. utTapHighlight: 'xfcklblvkjsj',
  84. utLpSelection: 'gykqyzwufxpz',
  85. utHoverBlock: 'meefgeibrtqx', //scc_emptyblock
  86. //utNonEmptyElm: 'ilkpvtsnwmjb',
  87. utNonEmptyElmPrevElm: 'jttkfplemwzo',
  88. utHoverTextWrap: 'oseksntfvucn',
  89. ksFuncReplacerNonFalse: '___dqzadwpujtct___',
  90. ksEventReturnValue: ' ___ndjfujndrlsx___',
  91. ksSetData: '___rgqclrdllmhr___',
  92. ksNonEmptyPlainText: '___grpvyosdjhuk___',
  93.  
  94. eh_capture_passive: () => isSupportPassiveEventListener() ? ($._eh_capture_passive = ($._eh_capture_passive || {
  95. capture: true,
  96. passive: true
  97. })) : true,
  98.  
  99. mAlert_DOWN: function() {}, //dummy function in case alert replacement is not valid
  100. mAlert_UP: function() {}, //dummy function in case alert replacement is not valid
  101.  
  102.  
  103. lpKeyPressing: false,
  104. lpKeyPressingPromise: Promise.resolve(),
  105.  
  106. isNum: (d) => (d > 0 || d < 0 || d === 0),
  107.  
  108. isAnySelection: function() {
  109. var sel = getSelection();
  110. return !sel ? null : (typeof sel.isCollapsed == 'boolean') ? !sel.isCollapsed : (sel.toString().length > 0);
  111. },
  112.  
  113. createCSSElement: function(cssStyle, container) {
  114. var css = document.createElement('style'); //slope: DOM throughout
  115. css.type = 'text/css';
  116. css.textContent = cssStyle;
  117. if (container) container.appendChild(css);
  118. return css;
  119. },
  120.  
  121. createFakeAlert: function(_alert) {
  122. if (typeof _alert != 'function') return null;
  123.  
  124. function alert(msg) {
  125. alert.__isDisabled__() ? console.log("alert msg disabled: ", msg) : _alert.apply(this, arguments);
  126. };
  127. alert.toString = () => "function alert() { [native code] }";
  128. return alert;
  129. },
  130.  
  131. createFuncReplacer: function(originalFunc, pName, resFX) {
  132. resFX = function(ev) {
  133. var res = originalFunc.apply(this, arguments);
  134. if (!this || this[pName] != resFX) return res; // if this is null or undefined, or this.onXXX is not this function
  135. if (res === false) return; // return undefined when "return false;"
  136. originalFunc[$.ksFuncReplacerNonFalse] = true;
  137. this[pName] = originalFunc; // restore original
  138. return res;
  139. }
  140. resFX.toString = () => originalFunc.toString();
  141. return resFX;
  142. },
  143.  
  144. listenerDisableAll: function(evt) {
  145. var elmNode = evt.target;
  146. var pName = 'on' + evt.type;
  147. evt = null;
  148. Promise.resolve().then(() => {
  149. while (elmNode && elmNode.nodeType > 0) { //i.e. HTMLDocument or HTMLElement
  150. var f = elmNode[pName];
  151. if (typeof f == 'function' && f[$.ksFuncReplacerNonFalse] !== true) {
  152. var nf = $.createFuncReplacer(f, pName);
  153. nf[$.ksFuncReplacerNonFalse] = true;
  154. elmNode[pName] = nf;
  155. }
  156. elmNode = elmNode.parentNode;
  157. }
  158. })
  159. },
  160.  
  161. onceCssHighlightSelection: () => {
  162. if (document.documentElement.hasAttribute($.utLpSelection)) return;
  163. $.onceCssHighlightSelection = null
  164. Promise.resolve().then(() => {
  165. 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
  166. var elm = !s.length ? document.body : s[s.length >> 1];
  167. return elm
  168. }).then(elm => {
  169. var selectionStyle = getComputedStyle(elm, ':selection');
  170. if (/^rgba\(\d+,\s*\d+,\s*\d+,\s*0\)$/.test(selectionStyle.getPropertyValue('background-color'))) document.documentElement.setAttribute($.utSelectionColorHack, "");
  171. return elm;
  172. }).then(elm => {
  173. var elmStyle = getComputedStyle(elm)
  174. if (/^rgba\(\d+,\s*\d+,\s*\d+,\s*0\)$/.test(elmStyle.getPropertyValue('-webkit-tap-highlight-color'))) document.documentElement.setAttribute($.utTapHighlight, "");
  175. })
  176. },
  177.  
  178. clipDataProcess: function(clipboardData) {
  179.  
  180. if (!clipboardData) return;
  181. const evt = clipboardData[$.ksSetData]; //NOT NULL when preventDefault is called
  182. if (!evt || evt.clipboardData !== clipboardData) return;
  183. const plainText = clipboardData[$.ksNonEmptyPlainText]; //NOT NULL when setData is called with non empty input
  184. if (!plainText) return;
  185.  
  186. //BOTH preventDefault and setData are called.
  187.  
  188. if (evt.cancelable !== true || evt.defaultPrevented !== false) return;
  189. $.bypass = true;
  190. evt.preventDefault();
  191. $.bypass = false;
  192.  
  193.  
  194. var trimedSelectionText = getSelection().toString().trim()
  195.  
  196. if (trimedSelectionText) {
  197. //there is replacement data and the selection is not empty
  198. console.log({
  199. msg: "copy event - clipboardData replacement is allowed and the selection is not empty",
  200. oldText: trimedSelectionText,
  201. newText: plainText,
  202. })
  203. } else {
  204. //there is replacement data and the selection is empty
  205. console.log({
  206. msg: "copy event - clipboardData replacement is allowed and the selection is empty",
  207. oldText: trimedSelectionText,
  208. newText: plainText,
  209. })
  210. }
  211.  
  212. },
  213.  
  214. enableSelectClickCopy: function() {
  215. $.eyEvts = ['keydown', 'keyup', 'copy', 'contextmenu', 'select', 'selectstart', 'dragstart', 'beforecopy']; //slope: throughout
  216.  
  217. function isDeactivePreventDefault(evt) {
  218. if ($.bypass) return false;
  219. var j = $.eyEvts.indexOf(evt.type);
  220. switch (j) {
  221. case 6:
  222. if ($.enableDragging) return false;
  223. if (evt&&evt.target && evt.target.nodeType==1 && evt.target.hasAttribute('draggable')) {
  224. $.enableDragging = true;
  225. return false;
  226. }
  227. //if(evt.target.hasAttribute('draggable')&&evt.target!=window.getSelection().anchorNode)return false;
  228. return true;
  229. case 3:
  230. if (evt.target instanceof Element && (evt.target.textContent || "").trim().length === 0) return false; //exclude elements like video
  231. return true;
  232. case -1:
  233. return false;
  234. case 0:
  235. case 1:
  236. return (evt.keyCode == 67 && (evt.ctrlKey || evt.metaKey) && !evt.altKey && !evt.shiftKey && $.isAnySelection() === true);
  237. case 2:
  238. if (!('clipboardData' in evt && 'setData' in DataTransfer.prototype)) return true; // Event oncopy not supporting clipboardData
  239. if (evt.cancelable && evt.defaultPrevented === false) {} else return true;
  240.  
  241. if (evt.clipboardData[$.ksSetData] && evt.clipboardData[$.ksSetData] != evt) return true; //in case there is a bug
  242. evt.clipboardData[$.ksSetData] = evt;
  243.  
  244. $.clipDataProcess(evt.clipboardData);
  245.  
  246. return true; //preventDefault in clipDataProcess
  247.  
  248.  
  249. default:
  250. return true;
  251. }
  252. }
  253.  
  254. !(function($setData) {
  255. DataTransfer.prototype.setData = (function setData() {
  256.  
  257. if (arguments[0] == 'text/plain' && typeof arguments[1] == 'string') {
  258. if (arguments[1].trim().length > 0) {
  259. this[$.ksNonEmptyPlainText] = arguments[1]
  260. } else if (this[$.ksNonEmptyPlainText]) {
  261. arguments[1] = this[$.ksNonEmptyPlainText]
  262. }
  263. }
  264.  
  265. $.clipDataProcess(this)
  266.  
  267. let res = $setData.apply(this, arguments)
  268.  
  269. return res;
  270.  
  271. })
  272. })(DataTransfer.prototype.setData);
  273.  
  274. Object.defineProperties(DataTransfer.prototype, {
  275. [$.ksSetData]: { //store the event
  276. value: null,
  277. writable: true,
  278. enumerable: false,
  279. configurable: true
  280. },
  281. [$.ksNonEmptyPlainText]: { //store the text
  282. value: null,
  283. writable: true,
  284. enumerable: false,
  285. configurable: true
  286. }
  287. })
  288.  
  289.  
  290. Event.prototype.preventDefault = (function(f) {
  291. function preventDefault() {
  292. if (!isDeactivePreventDefault(this)) f.call(this);
  293. }
  294. preventDefault.toString = () => f.toString();
  295. return preventDefault;
  296. })(Event.prototype.preventDefault);
  297.  
  298. Object.defineProperty(Event.prototype, "returnValue", {
  299. get() {
  300. return $.ksEventReturnValue in this ? this[$.ksEventReturnValue] : true;
  301. },
  302. set(newValue) {
  303. if (newValue === false && !isDeactivePreventDefault(this)) this.preventDefault();
  304. this[$.ksEventReturnValue] = newValue;
  305. },
  306. enumerable: true,
  307. configurable: true
  308. });
  309.  
  310. for (var i = 2, eventsCount = $.eyEvts.length; i < eventsCount; i++) {
  311. document.addEventListener($.eyEvts[i], $.listenerDisableAll, true); // Capture Event; passive:false; expected occurrence COMPLETELY before Target Capture and Target Bubble
  312. }
  313.  
  314. // userscript bug ? window.alert not working
  315. let window_ = null;
  316. try{
  317. window_ = new Function('return window')();
  318. }catch(e){}
  319. if(window_){
  320. let _alert = window_.alert; //slope: temporary
  321. if (typeof _alert == 'function') {
  322. let _mAlert = $.createFakeAlert(_alert);
  323. if (_mAlert) {
  324. let clickBlockingTo = 0;
  325. _mAlert.__isDisabled__ = () => clickBlockingTo > +new Date;
  326. $.mAlert_DOWN = () => (clickBlockingTo = +new Date + 50);
  327. $.mAlert_UP = () => (clickBlockingTo = +new Date + 20);
  328. window_.alert = _mAlert
  329. }
  330. _mAlert=null;
  331. }
  332. _alert=null;
  333. }
  334. window_=null;
  335.  
  336. },
  337.  
  338. lpCheckPointer: function(targetElm) {
  339. if (targetElm && targetElm.nodeType == 1 && targetElm.matches('*:hover')) {
  340. if (getComputedStyle(targetElm).getPropertyValue('cursor') == 'pointer' && targetElm.textContent) return true;
  341. }
  342. return false;
  343. },
  344.  
  345. eventCancel: function(evt, toPreventDefault) {
  346. $.bypass = true;
  347. !toPreventDefault || evt.preventDefault()
  348. evt.stopPropagation();
  349. evt.stopImmediatePropagation();
  350. $.bypass = false;
  351. },
  352.  
  353. lpHoverBlocks:[],
  354. lpKeyAltLastPressAt:0,
  355. lpKeyAltPressInterval:0,
  356.  
  357. noPlayingVideo:function(){
  358.  
  359. // prevent poor video preformance
  360.  
  361. let noPlaying =true;
  362. for(const video of document.querySelectorAll('video[src]')){
  363.  
  364. if(video.paused===false) noPlaying=false;
  365.  
  366. }
  367. return noPlaying;
  368.  
  369.  
  370. },
  371.  
  372.  
  373. lpKeyDown: function(evt) {
  374.  
  375. if(!$.gm_lp_enable)return;
  376.  
  377. const isAltPress= (evt.key == "Alt" && evt.altKey && !evt.ctrlKey && !evt.metaKey && !evt.shiftKey)
  378.  
  379.  
  380.  
  381.  
  382. if (isAltPress) {
  383.  
  384. $.lpKeyAltLastPressAt=+new Date;
  385.  
  386. let element=evt.target
  387.  
  388.  
  389. if($.lpKeyPressing == false && element && element.parentNode && !evt.repeat && $.noPlayingVideo()){
  390.  
  391. $.lpKeyPressing = true;
  392.  
  393. $.cid_lpKeyPressing=setInterval(()=>{
  394. if($.lpKeyAltLastPressAt+500<+new Date){
  395. $.lpCancelKeyPressAlt();
  396. }
  397. },137);
  398.  
  399. const rootNode = $.rootHTML(element);
  400. if (rootNode) {
  401. let tmp_wmEty = null;
  402.  
  403.  
  404. let wmTextWrap = new WeakMap();
  405.  
  406. $.lpKeyPressingPromise = $.lpKeyPressingPromise.then(() => {
  407. for (const elm of $.lpHoverBlocks) {
  408. elm.removeAttribute($.utNonEmptyElmPrevElm)
  409. elm.removeAttribute($.utHoverTextWrap)
  410. }
  411. $.lpHoverBlocks.length=0;
  412. }).then(() => {
  413. tmp_wmEty = new WeakMap(); // 1,2,3.....: non-empty elm, -1:empty elm
  414. const s = [...rootNode.querySelectorAll('*:not(button, textarea, input, script, noscript, style, link, img, br)')].filter((elm) => elm.childElementCount === 0 && (elm.textContent || '').trim().length > 0)
  415. for (const elm of s) tmp_wmEty.set(elm, 1);
  416. return s;
  417. }).then((s) => {
  418. let laterArr = [];
  419. let promises = [];
  420.  
  421. let promiseCallback = parentNode => {
  422. if (wmTextWrap.get(parentNode) !== null) return;
  423. const m = [...parentNode.children].some(elm => {
  424. const value = getComputedStyle(elm).getPropertyValue('z-index');
  425. if (typeof value == 'string' && value.length > 0) return $.isNum(+value)
  426. return false
  427. })
  428. wmTextWrap.set(parentNode, m)
  429. if (m) {
  430. $.lpHoverBlocks.push(parentNode);
  431. parentNode.setAttribute($.utHoverTextWrap, '')
  432. }
  433.  
  434. };
  435.  
  436. for (const elm of s) {
  437. let qElm = elm;
  438. let qi = 1;
  439. while (true) {
  440. let pElm = qElm.previousElementSibling;
  441. let anyEmptyHover = false;
  442. while (pElm) {
  443. if (tmp_wmEty.get(pElm) > 0) break;
  444. if (!pElm.matches(`button, textarea, input, script, noscript, style, link, img, br`) && (pElm.textContent || '').length === 0 && pElm.clientWidth * pElm.clientHeight > 0) {
  445. laterArr.push(pElm);
  446. anyEmptyHover = true;
  447. }
  448. pElm = pElm.previousElementSibling;
  449. }
  450. if (anyEmptyHover && !wmTextWrap.has(qElm.parentNode)) {
  451. wmTextWrap.set(qElm.parentNode, null)
  452. promises.push(Promise.resolve(qElm.parentNode).then(promiseCallback))
  453. }
  454. qElm = qElm.parentNode;
  455. if (!qElm || qElm === rootNode) break;
  456. qi++
  457. if (tmp_wmEty.get(qElm) > 0) break;
  458. tmp_wmEty.set(qElm, qi)
  459. }
  460. }
  461.  
  462. tmp_wmEty = null;
  463.  
  464. Promise.all(promises).then(() => {
  465. promises.length = 0;
  466. promises = null;
  467. promiseCallback = null;
  468. for (const pElm of laterArr) {
  469. let parentNode = pElm.parentNode
  470. if (wmTextWrap.get(parentNode) === true) {
  471. $.lpHoverBlocks.push(pElm);
  472. pElm.setAttribute($.utNonEmptyElmPrevElm, '');
  473. }
  474. }
  475. laterArr.length = 0;
  476. laterArr = null;
  477. wmTextWrap = null;
  478. })
  479. })
  480.  
  481. }
  482.  
  483. }
  484.  
  485.  
  486. }else if($.lpKeyPressing==true){
  487.  
  488. $.lpCancelKeyPressAlt();
  489.  
  490. }
  491.  
  492. },
  493. lpCancelKeyPressAlt:()=>{
  494. $.lpKeyPressing = false;
  495. if($.cid_lpKeyPressing>0) $.cid_lpKeyPressing=clearInterval($.cid_lpKeyPressing)
  496.  
  497. $.lpKeyPressingPromise = $.lpKeyPressingPromise.then(() => {
  498. for (const elm of $.lpHoverBlocks) {
  499. elm.removeAttribute($.utNonEmptyElmPrevElm)
  500. elm.removeAttribute($.utHoverTextWrap)
  501. }
  502. $.lpHoverBlocks.length=0;
  503. })
  504.  
  505.  
  506. setTimeout(function(){
  507. if($.lpMouseActive == 1){
  508. $.lpMouseUpClear();
  509. $.lpMouseActive=0;
  510. }
  511. },32);
  512.  
  513. },
  514. lpKeyUp: function(evt) {
  515.  
  516. if(!$.gm_lp_enable)return;
  517.  
  518. if ($.lpKeyPressing == true) {
  519. $.lpCancelKeyPressAlt();
  520. }
  521.  
  522.  
  523. },
  524.  
  525. lpAltRoots:[],
  526.  
  527. lpMouseDown: function(evt) {
  528.  
  529. if(!$.gm_lp_enable)return;
  530.  
  531. $.lpMouseActive = 0;
  532. if (evt.altKey && !evt.ctrlKey && !evt.metaKey && !evt.shiftKey && evt.button === 0 && $.noPlayingVideo()) {
  533. $.lpMouseActive = 1;
  534. $.eventCancel(evt, false);
  535. const rootNode = $.rootHTML(evt.target);
  536. $.lpAltRoots.push(rootNode);
  537. rootNode.setAttribute($.utLpSelection, '');
  538. }
  539. },
  540.  
  541. lpMouseUpClear:function(){
  542. for(const rootNode of $.lpAltRoots) rootNode.removeAttribute($.utLpSelection);
  543. $.lpAltRoots.length=0;
  544. if ($.onceCssHighlightSelection) window.requestAnimationFrame($.onceCssHighlightSelection);
  545. },
  546.  
  547. lpMouseUp: function(evt) {
  548.  
  549. if(!$.gm_lp_enable)return;
  550.  
  551. if ($.lpMouseActive == 1) {
  552. $.lpMouseActive = 2;
  553. $.eventCancel(evt, false);
  554. $.lpMouseUpClear();
  555. }
  556. },
  557.  
  558. lpClick: function(evt) {
  559.  
  560. if(!$.gm_lp_enable)return;
  561.  
  562. if ($.lpMouseActive == 2) {
  563. $.eventCancel(evt, false);
  564. }
  565. },
  566.  
  567. lpEnable: function() { // this is an optional feature for modern browser
  568. // the built-in browser feature has already disabled the default event behavior, the coding is just to ensure no "tailor-made behavior" occuring.
  569. document.addEventListener('keydown', $.lpKeyDown, {
  570. capture: true,
  571. passive: true
  572. })
  573. document.addEventListener('keyup', $.lpKeyUp, {
  574. capture: true,
  575. passive: true
  576. })
  577. document.addEventListener('mousedown', $.lpMouseDown, {
  578. capture: true,
  579. passive: true
  580. })
  581. document.addEventListener('mouseup', $.lpMouseUp, {
  582. capture: true,
  583. passive: true
  584. })
  585. document.addEventListener('click', $.lpClick, {
  586. capture: true,
  587. passive: true
  588. })
  589. },
  590.  
  591. rootHTML: (node) => {
  592.  
  593. if (!node || !(node.nodeType > 0)) return null;
  594. if (!node.ownerDocument) return node;
  595. let rootNode = node.getRootNode ? node.getRootNode() : null
  596. if (!rootNode) {
  597. let pElm = node;
  598. while (pElm) {
  599.  
  600. if (pElm.parentNode) pElm = pElm.parentNode;
  601. else break;
  602.  
  603. }
  604. rootNode = pElm;
  605. }
  606.  
  607.  
  608. rootNode = rootNode.querySelector('html') || node.ownerDocument.documentElement || null;
  609. return rootNode
  610.  
  611. },
  612.  
  613. mainEnableScript: () => {
  614. var cssStyleOnReady = `
  615. html, html *,
  616. html *::before, html *::after,
  617. html *:hover, html *:link, html *:visited, html *:active,
  618. html *[style], html *[class]{
  619. -khtml-user-select: auto !important; -moz-user-select: auto !important; -ms-user-select: auto !important;
  620. -webkit-touch-callout: default !important; -webkit-user-select: auto !important; user-select: auto !important;
  621. }
  622. *:hover>img[src]{pointer-events:auto !important;}
  623.  
  624. [${$.utSelectionColorHack}] :not(input):not(textarea)::selection{ background-color: Highlight !important; color: HighlightText !important;}
  625. [${$.utSelectionColorHack}] :not(input):not(textarea)::-moz-selection{ background-color: Highlight !important; color: HighlightText !important;}
  626. [${$.utTapHighlight}] *{ -webkit-tap-highlight-color: rgba(0, 0, 0, 0.18) !important;}
  627.  
  628. [${$.utHoverTextWrap}]>[${$.utNonEmptyElmPrevElm}]{pointer-events:none !important;}
  629. [${$.utHoverTextWrap}]>*{z-index:inherit !important;}
  630.  
  631. html[${$.utLpSelection}] *:hover, html[${$.utLpSelection}] *:hover * { cursor:text !important;}
  632. html[${$.utLpSelection}] :not(input):not(textarea)::selection {background-color: rgba(255, 156, 179,0.5) !important;}
  633. html[${$.utLpSelection}] :not(input):not(textarea)::-moz-selection {background-color: rgba(255, 156, 179,0.5) !important;}
  634.  
  635. img[${$.utHoverBlock}="4"]{display:none !important;}
  636. [${$.utHoverBlock}="7"]{padding:0 !important;overflow:hidden !important;}
  637. [${$.utHoverBlock}="7"]>img[${$.utHoverBlock}="4"]:first-child{
  638. display:inline-block !important;
  639. position: relative !important;
  640. top: auto !important;
  641. left: auto !important;
  642. bottom: auto !important;
  643. right: auto !important;
  644. opacity: 0 !important;
  645. padding: 0 !important;
  646. margin: 0 !important;
  647. width: 100% !important;
  648. height: 100% !important;
  649. outline: 0 !important;
  650. border: 0 !important;
  651. box-sizing: border-box !important;
  652. transform: initial !important;
  653. user-select:none !important;
  654. z-index:1 !important;
  655. float: left !important;
  656. cursor:inherit !important;
  657. pointer-events:inherit !important;
  658. border-radius: inherit !important;
  659. background:none !important;
  660. }
  661.  
  662. `.trim();
  663.  
  664. $.enableSelectClickCopy()
  665. $.createCSSElement(cssStyleOnReady, document.documentElement);
  666.  
  667. },
  668.  
  669. mainEvents: (listenerPress, listenerRelease) => {
  670. document.addEventListener("mousedown", listenerPress, true); // Capture Event; (desktop)
  671. document.addEventListener("contextmenu", listenerPress, true); // Capture Event; (desktop&mobile)
  672. document.addEventListener("mouseup", listenerRelease, false); // Bubble Event;
  673. },
  674.  
  675. disableHoverBlock: () => {
  676.  
  677. var nMap = new WeakMap()
  678.  
  679. function elmParam(elm) {
  680.  
  681. var mElm = nMap.get(elm);
  682. if (!mElm) nMap.set(elm, mElm = {});
  683. return mElm;
  684. }
  685.  
  686. function overlapArea(rect1, rect2) {
  687.  
  688. let l1 = {
  689. x: rect1.left,
  690. y: rect1.top
  691. }
  692.  
  693. let r1 = {
  694. x: rect1.right,
  695. y: rect1.bottom
  696. }
  697. let l2 = {
  698. x: rect2.left,
  699. y: rect2.top
  700. }
  701.  
  702. let r2 = {
  703. x: rect2.right,
  704. y: rect2.bottom
  705. }
  706.  
  707. // Area of 1st Rectangle
  708. let area1 = Math.abs(l1.x - r1.x) * Math.abs(l1.y - r1.y);
  709.  
  710. // Area of 2nd Rectangle
  711. let area2 = Math.abs(l2.x - r2.x) * Math.abs(l2.y - r2.y);
  712.  
  713. // Length of intersecting part i.e
  714. // start from max(l1.x, l2.x) of
  715. // x-coordinate and end at min(r1.x,
  716. // r2.x) x-coordinate by subtracting
  717. // start from end we get required
  718. // lengths
  719. let x_dist = Math.min(r1.x, r2.x) - Math.max(l1.x, l2.x);
  720. let y_dist = Math.min(r1.y, r2.y) - Math.max(l1.y, l2.y);
  721. let areaI = 0;
  722. if (x_dist > 0 && y_dist > 0) {
  723. areaI = x_dist * y_dist;
  724. }
  725.  
  726. return {
  727. area1,
  728. area2,
  729. areaI
  730. };
  731.  
  732.  
  733. }
  734.  
  735. function redirectEvent(event, toElement) {
  736.  
  737. toElement.dispatchEvent(new event.constructor(event.type, event));
  738. if (event.type != 'wheel') event.preventDefault();
  739. event.stopPropagation();
  740. }
  741.  
  742. const floatingBlockHover = new WeakMap();
  743.  
  744. let _nImgs = [];
  745.  
  746.  
  747. function nImgFunc() {
  748.  
  749. for (const s of _nImgs) {
  750. if (s.lastTime + 800 < +new Date) {
  751. s.lastTime = +new Date;
  752. return s.elm
  753. }
  754. }
  755.  
  756. let nImg = document.createElement('img');
  757. nImg.setAttribute('title', ' ');
  758. nImg.setAttribute('alt', ' ');
  759. nImg.onerror = function() {
  760. if (this.parentNode != null) this.parentNode.removeChild(this)
  761. }
  762. nImg.setAttribute($.utHoverBlock, '4');
  763. const handle = function(event) {
  764. if (this === event.target) {
  765. if (event.button != 2) redirectEvent(event, this.parentNode)
  766. Promise.resolve().then(() => {
  767. for (const s of _nImgs) {
  768. if (s.elm === this) {
  769. s.lastTime = +new Date
  770. }
  771. }
  772. })
  773. }
  774. }
  775. nImg.addEventListener('click', handle, true);
  776. nImg.addEventListener('mousedown', handle, true);
  777. nImg.addEventListener('mouseup', handle, true);
  778. nImg.addEventListener('mousemove', handle, true);
  779. nImg.addEventListener('mouseover', handle, true);
  780. nImg.addEventListener('mouseout', handle, true);
  781. nImg.addEventListener('mouseenter', handle, true);
  782. nImg.addEventListener('mouseleave', handle, true);
  783. //nImg.addEventListener('wheel', handle, $.eh_capture_passive());
  784. let resObj = {
  785. elm: nImg,
  786. lastTime: +new Date,
  787. cid_fade: 0
  788. }
  789. _nImgs.push(resObj)
  790.  
  791. return nImg;
  792.  
  793. }
  794.  
  795. const wmHoverUrl = new WeakMap();
  796. let lastMouseEnterElm = null;
  797. let lastMouseEnterAt = 0;
  798. let lastMouseEnterCid = 0;
  799.  
  800. function mouseEnter() {
  801. lastMouseEnterCid = 0;
  802.  
  803. if (+new Date - lastMouseEnterAt < 30) {
  804. lastMouseEnterCid = setTimeout(mouseEnter, 82)
  805. return;
  806. }
  807.  
  808. //if($.lpKeyPressing)return;
  809.  
  810. const targetElm = lastMouseEnterElm
  811.  
  812. Promise.resolve()
  813. .then(() => {
  814. if (targetElm && targetElm.parentNode) {} else {
  815. return;
  816. }
  817. if (floatingBlockHover.get(targetElm)) {
  818. let url = null
  819. if (targetElm.getAttribute($.utHoverBlock) == '7' && (url = wmHoverUrl.get(targetElm)) && targetElm.querySelector(`[${$.utHoverBlock}]`) == null) {
  820. let _nImg = nImgFunc();
  821. if (_nImg.parentNode !== targetElm) {
  822. _nImg.setAttribute('src', url);
  823. targetElm.insertBefore(_nImg, targetElm.firstChild);
  824. }
  825. }
  826. return;
  827. }
  828. floatingBlockHover.set(targetElm, 1);
  829. return 1;
  830. }).then((ayRes) => {
  831. if (!ayRes) return;
  832.  
  833. if (targetElm.nodeType != 1) return;
  834. if ("|SVG|IMG|HTML|BODY|VIDEO|AUDIO|BR|HEAD|NOSCRIPT|SCRIPT|STYLE|TEXTAREA|AREA|INPUT|FORM|BUTTON|".indexOf(`|${targetElm.nodeName}|`) >= 0) return;
  835.  
  836. const targetArea = targetElm.clientWidth * targetElm.clientHeight
  837.  
  838. if (targetArea > 0) {} else {
  839. return;
  840. }
  841.  
  842. const targetCSS = getComputedStyle(targetElm)
  843. const targetBgImage = targetCSS.getPropertyValue('background-image');
  844. let exec1 = null
  845.  
  846. if (targetBgImage != 'none' && (exec1 = /^\s*url\s*\("?([^"\)]+\b(\.gif|\.png|\.jpeg|\.jpg|\.webp)\b[^"\)]*)"?\)\s*$/i.exec(targetBgImage))) {
  847. if ((targetElm.textContent || "").trim().length > 0) return;
  848. const url = exec1[1];
  849. return url
  850.  
  851. // console.log(targetBgImage,[...exec1])
  852. }
  853.  
  854.  
  855.  
  856. if (targetCSS.getPropertyValue('position') == 'absolute' && +targetCSS.getPropertyValue('z-index') > 0) {} else {
  857. return;
  858. }
  859. if ((targetElm.textContent || "").trim().length > 0) return;
  860.  
  861. let possibleResults = [];
  862.  
  863. for (const imgElm of document.querySelectorAll('img[src]')) {
  864. const param = elmParam(imgElm)
  865. if (!param.area) {
  866. const area = imgElm.clientWidth * imgElm.clientHeight
  867. if (area > 0) param.area = area;
  868. }
  869. if (param.area > 0) {
  870. if (targetArea > param.area * 0.9) possibleResults.push(imgElm)
  871. }
  872. }
  873.  
  874. let i = 0;
  875. let j = 0;
  876. for (const imgElm of possibleResults) {
  877.  
  878. const cmpVal = targetElm.compareDocumentPosition(imgElm)
  879.  
  880. /*
  881.  
  882.  
  883. 1: The two nodes do not belong to the same document.
  884. 2: p1 is positioned after p2.
  885. 4: p1 is positioned before p2.
  886. 8: p1 is positioned inside p2.
  887. 16: p2 is positioned inside p1.
  888. 32: The two nodes has no relationship, or they are two attributes on the same element.
  889.  
  890. */
  891.  
  892. if (cmpVal & 8 || cmpVal & 16) return;
  893. if (cmpVal & 2) j++; // I<p
  894. else if (cmpVal & 4) break; // I>p
  895.  
  896.  
  897. i++;
  898.  
  899. }
  900.  
  901. // before: j-1 after: j
  902.  
  903. let indexBefore = j - 1;
  904. let indexAfter = j;
  905. if (indexBefore < 0) indexBefore = 0;
  906. if (indexAfter > possibleResults.length - 1) indexAfter = possibleResults.length - 1;
  907.  
  908. // setTimeout(function(){
  909. for (let i = indexBefore; i <= indexAfter; i++) {
  910. const s = possibleResults[i];
  911. const {
  912. area1,
  913. area2,
  914. areaI
  915. } = overlapArea(targetElm.getBoundingClientRect(), s.getBoundingClientRect())
  916. const criteria = area1 * 0.7
  917. if (areaI > 0.9 * area2) {
  918.  
  919.  
  920. return s.getAttribute('src')
  921.  
  922.  
  923. }
  924. }
  925. // },1000);
  926.  
  927. }).then((sUrl) => {
  928.  
  929. if (typeof sUrl != 'string') return;
  930.  
  931. //console.log(targetElm, targetElm.querySelectorAll('img').length)
  932.  
  933. // console.log(313, evt.target, s)
  934. let _nImg = nImgFunc();
  935.  
  936.  
  937. if (_nImg.parentNode !== targetElm) {
  938. _nImg.setAttribute('src', sUrl);
  939. targetElm.insertBefore(_nImg, targetElm.firstChild);
  940. wmHoverUrl.set(targetElm, sUrl);
  941. targetElm.setAttribute($.utHoverBlock, '7');
  942. }
  943.  
  944.  
  945.  
  946. })
  947.  
  948. }
  949.  
  950. document.addEventListener('mouseenter', function(evt) {
  951. if(!$.gm_disablehover_enable) return;
  952. lastMouseEnterElm = evt.target
  953. lastMouseEnterAt = +new Date;
  954. if (!lastMouseEnterCid) lastMouseEnterCid = setTimeout(mouseEnter, 82)
  955. }, $.eh_capture_passive())
  956.  
  957.  
  958.  
  959. },
  960.  
  961. acrAuxDown: function(evt) {
  962.  
  963. if(!$.gm_prevent_aux_click_enable) return;
  964.  
  965. if (evt.button === 1 ){
  966. let check = $.dmmMouseUpLast > $.dmmMouseDownLast && evt.timeStamp - $.dmmMouseUpLast < 40
  967. $.dmmMouseDownLast = evt.timeStamp;
  968. if( check ) {
  969. $.eventCancel(evt, true);
  970. }
  971. }
  972.  
  973. },
  974.  
  975. acrAuxUp: function(evt) {
  976. if(!$.gm_prevent_aux_click_enable) return;
  977.  
  978. if (evt.button === 1 ){
  979. let check = $.dmmMouseDownLast > $.dmmMouseUpLast && evt.timeStamp - $.dmmMouseDownLast < 40;
  980. $.dmmMouseUpLast = evt.timeStamp;
  981. if( check ) {
  982. $.dmmMouseUpCancel = evt.timeStamp;
  983. $.eventCancel(evt, true);
  984. }
  985. }
  986.  
  987. },
  988.  
  989.  
  990. acrAuxClick: function(evt) {
  991. if(!$.gm_prevent_aux_click_enable) return;
  992.  
  993. if (evt.button === 1 ){
  994. if( evt.timeStamp - $.dmmMouseUpCancel < 40 ) {
  995. $.eventCancel(evt, true);
  996. }
  997. }
  998.  
  999.  
  1000. },
  1001.  
  1002. preventAuxClickRepeat:function(){
  1003.  
  1004. document.addEventListener('mousedown', $.acrAuxDown, {
  1005. capture: true,
  1006. passive: false
  1007. })
  1008. document.addEventListener('mouseup', $.acrAuxUp, {
  1009. capture: true,
  1010. passive: false
  1011. })
  1012. document.addEventListener('auxclick', $.acrAuxClick, {
  1013. capture: true,
  1014. passive: false
  1015. })
  1016.  
  1017.  
  1018. },
  1019.  
  1020. MenuEnable: (
  1021. class MenuEnable{
  1022.  
  1023. constructor(textToEnable, textToDisable, callback, initalEnable){
  1024. this.textToEnable=textToEnable;
  1025. this.textToDisable=textToDisable;
  1026. this.callback=callback;
  1027. this.gx=this.gx.bind(this);
  1028. }
  1029.  
  1030. unregister(){
  1031. (this.h>=0)?(GM_unregisterMenuCommand(this.h), (this.h=0)):0;
  1032. }
  1033.  
  1034. register(text){
  1035. if(typeof text == 'string') this.showText = text;
  1036. text = this.showText;
  1037. if(typeof text != 'string') return;
  1038. this.h=GM_registerMenuCommand(text, this.gx);
  1039. }
  1040.  
  1041. a(o){
  1042.  
  1043. if(this.enabled===o.bEnable) return;
  1044. this.enabled = o.bEnable;
  1045. this.unregister();
  1046.  
  1047.  
  1048. let pr= 0 ;
  1049.  
  1050. if($.gm_status_fn_store && $.gm_status_fn_store.indexOf(this)>=0){
  1051.  
  1052. let store = $.gm_status_fn_store
  1053. let idx = store.indexOf(this)
  1054. let count = store.length;
  1055.  
  1056.  
  1057. if(idx>=0&&idx<=count-2){
  1058.  
  1059. console.log(idx, count)
  1060.  
  1061. for(let jdx=idx+1;jdx<count;jdx++){
  1062.  
  1063. store[jdx].unregister();
  1064. }
  1065.  
  1066. this.register (o.bText);
  1067.  
  1068. for(let jdx=idx+1;jdx<count;jdx++){
  1069.  
  1070. store[jdx].register();
  1071. }
  1072.  
  1073. pr=1;
  1074.  
  1075. }
  1076.  
  1077.  
  1078. }
  1079.  
  1080. if(!pr) this.register (o.bText);
  1081.  
  1082. this.callback(this.enabled, o.byUserInput);
  1083.  
  1084.  
  1085. }
  1086.  
  1087. enableNow(byUserInput){
  1088. this.a({
  1089. bEnable: true,
  1090. bText: this.textToDisable,
  1091. byUserInput
  1092. });
  1093.  
  1094. }
  1095.  
  1096. gx(){
  1097. if(this.enabled) this.disableNow(true);
  1098. else this.enableNow(true);
  1099.  
  1100. }
  1101.  
  1102. disableNow(byUserInput){
  1103. this.a({
  1104. bEnable: false,
  1105. bText: this.textToEnable,
  1106. byUserInput
  1107. });
  1108.  
  1109. }
  1110.  
  1111. toggle(enable, byUserInput){
  1112. enable?this.enableNow(byUserInput):this.disableNow(byUserInput);
  1113. }
  1114.  
  1115. }
  1116. ),
  1117.  
  1118. gm_status_fn: function(gm_name, textToEnable, textToDisable, callback){
  1119.  
  1120. let menuEnableName = gm_name + "$menuEnable";
  1121.  
  1122. function set_gm(enabled){
  1123. $[gm_name]=enabled;
  1124. let menuEnable = $[menuEnableName];
  1125. if( menuEnable ){
  1126. menuEnable.toggle(enabled)
  1127. };
  1128. callback(enabled)
  1129. }
  1130.  
  1131. set_gm(!!GM_getValue(gm_name));
  1132.  
  1133. GM_addValueChangeListener(gm_name,function(name, old_value, new_value, remote){
  1134.  
  1135. if(old_value === new_value) return;
  1136. if(new_value === $[gm_name]) return;
  1137. set_gm(new_value);
  1138.  
  1139. });
  1140.  
  1141. if(!inIframe()){
  1142.  
  1143. $.gm_status_fn_store=$.gm_status_fn_store||[];
  1144.  
  1145. $[menuEnableName]=new $.MenuEnable(textToEnable, textToDisable, (enabled) => {
  1146. GM_setValue(gm_name, !!enabled);
  1147. });
  1148.  
  1149. $.gm_status_fn_store.push($[menuEnableName]);
  1150.  
  1151. $[menuEnableName].toggle(!!GM_getValue(gm_name));
  1152.  
  1153. }
  1154.  
  1155.  
  1156.  
  1157. }
  1158.  
  1159. }
  1160.  
  1161. // $.holdingElm=null;
  1162.  
  1163. $.mainEnableScript();
  1164.  
  1165. if (isSupportAdvancedEventListener()) $.lpEnable(); // top capture event for alt-click
  1166.  
  1167. $.mainEvents(
  1168. function(evt) {
  1169. // $.holdingElm=evt.target;
  1170. // console.log('down',evt.target)
  1171. if ($.onceCssHighlightSelection) window.requestAnimationFrame($.onceCssHighlightSelection);
  1172. if (evt.button == 2 || evt.type == "contextmenu") $.mAlert_DOWN();
  1173. },
  1174. function(evt) {
  1175. // $.holdingElm=null;
  1176. // console.log('up',evt.target)
  1177. if (evt.button == 2) $.mAlert_UP();
  1178. if ($.enableDragging) {
  1179. $.enableDragging = false;
  1180. }
  1181. }
  1182. );
  1183.  
  1184. $.disableHoverBlock();
  1185. $.preventAuxClickRepeat();
  1186.  
  1187. console.log('userscript running - To Re-Enable Selection & Copying');
  1188.  
  1189.  
  1190. $.gm_status_fn("gm_lp_enable", "To Enable `Enhanced build-in Alt Text Selection`", "To Disable `Enhanced build-in Alt Text Selection`", ()=>{
  1191. // callback
  1192. });
  1193. $.gm_status_fn("gm_disablehover_enable", "To Enable `Hover on Image`", "To Disable `Hover on Image`", ()=>{
  1194. // callback
  1195. });
  1196. $.gm_status_fn("gm_prevent_aux_click_enable", "To Enable `Repetitive AuxClick Prevention`", "To Disable `Repetitive AuxClick Prevention`", ()=>{
  1197. // callback
  1198. });
  1199.  
  1200.  
  1201.  
  1202.  
  1203. })();