通用脚本重开启选取复制

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

当前为 2023-05-11 提交的版本,查看 最新版本

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