Twitter image viewing enhancement

Make Twitter photo viewing more humane

As of 07/09/2022. See the latest version.

  1. // ==UserScript==
  2. // @name Twitter image viewing enhancement
  3. // @name:zh-CN Twitter 图片查看增强
  4. // @name:zh-TW Twitter 圖像查看增強
  5. // @icon https://twitter.com/favicon.ico
  6. // @namespace https://moe.best/
  7. // @version 1.2.3
  8. // @description Make Twitter photo viewing more humane
  9. // @description:zh-CN 让推特图片浏览更加人性化
  10. // @description:zh-TW 讓 Twitter 照片瀏覽更人性化
  11. // @author Jindai Kirin
  12. // @include https://twitter.com/*
  13. // @license MIT
  14. // @grant GM_addStyle
  15. // @grant GM_getValue
  16. // @grant GM_setValue
  17. // @grant GM_registerMenuCommand
  18. // @run-at document-end
  19. // ==/UserScript==
  20.  
  21. // 注意 NOTICE
  22. // v1.0.0 是一次重大更新,你将不再需要设置 aria-label,并且支持所有语言。如果某一天脚本突然无法正常工作,请于脚本页面反馈,或退回至 v0.6.3。
  23. // v1.0.0 is an major update, you will no longer need to set up aria-labels and it support all languages. If one day the script not work, please feedback on the script homepage or use v0.6.3.
  24.  
  25. (() => {
  26. 'use strict';
  27.  
  28. // 滑动切换图片
  29. let enableDragToSwitch = GM_getValue('enableDragToSwitch', false);
  30. GM_registerMenuCommand('Drag to swtich images', () => {
  31. enableDragToSwitch = confirm(`Do you want to enable drag to swtich images?
  32. Current: ${enableDragToSwitch ? 'Enabled' : 'Disabled'}
  33.  
  34. Please refresh to take effect after modification.`);
  35. GM_setValue('enableDragToSwitch', enableDragToSwitch);
  36. });
  37.  
  38. if (enableDragToSwitch) GM_addStyle('img{-webkit-user-drag:none}');
  39.  
  40. const labels = {};
  41. try {
  42. const kv = {
  43. af8fa2ad: 'close',
  44. af8fa2ae: 'close',
  45. c4d53ba2: 'prev',
  46. d70740d9: 'next',
  47. d70740da: 'next',
  48. };
  49. const i18nModule = webpackChunk_twitter_responsive_web.find(([[name]]) => name.startsWith('i18n'));
  50. Object.values(i18nModule[1]).forEach(fn => {
  51. if (fn.length < 3) return;
  52. try {
  53. fn(undefined, undefined, () => ({
  54. _register: () => (k, v) => {
  55. if (k in kv) labels[kv[k]] = v;
  56. },
  57. }));
  58. } catch (e) {}
  59. });
  60. } catch (error) {
  61. console.error(error);
  62. }
  63.  
  64. const getBtnByLabel = label => document.querySelector(`div[aria-labelledby="modal-header"] div[aria-label="${label}"]`);
  65. const clickBtn = name => {
  66. const $btn = getBtnByLabel(labels[name]);
  67. if ($btn) {
  68. $btn.click();
  69. return true;
  70. }
  71. return false;
  72. };
  73.  
  74. const closeImgView = () => clickBtn('close');
  75. const prevImg = () => clickBtn('prev');
  76. const nextImg = () => clickBtn('next');
  77.  
  78. window.addEventListener('wheel', ({ deltaY, target: { tagName, baseURI } }) => {
  79. if (tagName == 'IMG' && /\/photo\//.test(baseURI)) {
  80. if (deltaY < 0) prevImg();
  81. else if (deltaY > 0) nextImg();
  82. }
  83. });
  84.  
  85. if (enableDragToSwitch) {
  86. let x = 0;
  87. let y = 0;
  88. window.addEventListener('mousedown', ({ clientX, clientY }) => {
  89. x = clientX;
  90. y = clientY;
  91. });
  92. window.addEventListener('mouseup', ({ button, clientX, clientY, target: { tagName, baseURI } }) => {
  93. if (button !== 0 || !(tagName == 'IMG' && /\/photo\//.test(baseURI))) return;
  94. const [sx, sy] = [clientX - x, clientY - y].map(Math.abs);
  95. const mx = clientX - x;
  96. if (sx <= 10 && sy <= 10) closeImgView();
  97. if (sy <= sx) {
  98. if (mx > 0) prevImg();
  99. else if (mx < 0) nextImg();
  100. }
  101. });
  102. } else {
  103. document.addEventListener(
  104. 'click',
  105. e => {
  106. const {
  107. target: { tagName, baseURI },
  108. } = e;
  109. if (!(tagName == 'IMG' && /\/photo\//.test(baseURI))) return;
  110. closeImgView();
  111. e.stopPropagation();
  112. },
  113. { capture: true }
  114. );
  115. }
  116. })();