widgets.js 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. /**
  2. * Drop-in replacement for Twitter's widgets.js
  3. * Include this script to automatically convert twitter-tweet blockquotes to Nitter embeds
  4. */
  5. (function () {
  6. "use strict";
  7. // Determine the Nitter instance URL from the script src, or fall back to current origin
  8. var widgetScripts = document.querySelectorAll('script[src*="widgets.js"]');
  9. var NITTER_URL = widgetScripts.length
  10. ? new URL(widgetScripts[widgetScripts.length - 1].src).origin
  11. : location.origin;
  12. var TWEET_URL_PATTERN =
  13. /^https?:\/\/(?:twitter\.com|x\.com)\/([^\/]+)\/status\/(\d+)/i;
  14. // Track iframes by URL for resize messages
  15. var iframesByUrl = {};
  16. /**
  17. * Extract tweet info (username and ID) from a blockquote's links
  18. */
  19. function findTweetInfo(blockquote) {
  20. var links = blockquote.querySelectorAll("a");
  21. for (var i = 0; i < links.length; i++) {
  22. var match = TWEET_URL_PATTERN.exec(links[i].href);
  23. if (match) {
  24. return { username: match[1], tweetId: match[2] };
  25. }
  26. }
  27. return null;
  28. }
  29. /**
  30. * Transform all twitter-tweet blockquotes into Nitter embed iframes
  31. */
  32. function transformBlockquotes() {
  33. var blockquotes = document.querySelectorAll("blockquote.twitter-tweet");
  34. for (var i = 0; i < blockquotes.length; i++) {
  35. var blockquote = blockquotes[i];
  36. var tweetInfo = findTweetInfo(blockquote);
  37. if (!tweetInfo) continue;
  38. var embedUrl =
  39. NITTER_URL +
  40. "/" +
  41. tweetInfo.username +
  42. "/status/" +
  43. tweetInfo.tweetId +
  44. "/embed";
  45. var iframe = document.createElement("iframe");
  46. iframe.src = embedUrl;
  47. iframe.style.cssText =
  48. "width: 100%; max-width: 550px; height: 250px; border: none; display: block;";
  49. iframe.loading = "lazy";
  50. // Track iframe for resize messages
  51. if (!iframesByUrl[embedUrl]) {
  52. iframesByUrl[embedUrl] = [];
  53. }
  54. iframesByUrl[embedUrl].push(iframe);
  55. blockquote.parentNode.replaceChild(iframe, blockquote);
  56. }
  57. }
  58. /**
  59. * Handle resize messages from Nitter embeds
  60. */
  61. function handleResizeMessage(event) {
  62. if (!Array.isArray(event.data) || event.data[0] !== "resizeIframe") return;
  63. var data = event.data[1];
  64. if (!data.h || data.h <= 0) return;
  65. var iframes = iframesByUrl[data.url];
  66. if (iframes) {
  67. for (var i = 0; i < iframes.length; i++) {
  68. iframes[i].style.height = data.h + "px";
  69. }
  70. }
  71. }
  72. // Remove any Twitter widget scripts that might have been loaded
  73. var twitterScripts = document.querySelectorAll(
  74. 'script[src*="platform.twitter.com/widgets.js"], script[src*="platform.x.com/widgets.js"]',
  75. );
  76. for (var i = 0; i < twitterScripts.length; i++) {
  77. twitterScripts[i].remove();
  78. }
  79. // Listen for resize messages from embeds
  80. window.addEventListener("message", handleResizeMessage);
  81. // Transform existing blockquotes
  82. if (document.readyState === "loading") {
  83. document.addEventListener("DOMContentLoaded", transformBlockquotes);
  84. } else {
  85. transformBlockquotes();
  86. }
  87. // Watch for dynamically added blockquotes
  88. var observer = new MutationObserver(function (mutations) {
  89. for (var i = 0; i < mutations.length; i++) {
  90. var addedNodes = mutations[i].addedNodes;
  91. for (var j = 0; j < addedNodes.length; j++) {
  92. var node = addedNodes[j];
  93. if (node.nodeType !== 1) continue;
  94. var isTwitterBlockquote =
  95. node.matches && node.matches("blockquote.twitter-tweet");
  96. var containsTwitterBlockquote =
  97. node.querySelector && node.querySelector("blockquote.twitter-tweet");
  98. if (isTwitterBlockquote || containsTwitterBlockquote) {
  99. transformBlockquotes();
  100. return;
  101. }
  102. }
  103. }
  104. });
  105. if (document.body) {
  106. observer.observe(document.body, { childList: true, subtree: true });
  107. }
  108. // Provide a fake twttr object for compatibility with sites that check for it
  109. window.twttr = window.twttr || {};
  110. window.twttr.widgets = {
  111. load: transformBlockquotes,
  112. loaded: true,
  113. };
  114. })();