| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135 |
- /**
- * Drop-in replacement for Twitter's widgets.js
- * Include this script to automatically convert twitter-tweet blockquotes to Nitter embeds
- */
- (function () {
- "use strict";
- // Determine the Nitter instance URL from the script src, or fall back to current origin
- var widgetScripts = document.querySelectorAll('script[src*="widgets.js"]');
- var NITTER_URL = widgetScripts.length
- ? new URL(widgetScripts[widgetScripts.length - 1].src).origin
- : location.origin;
- var TWEET_URL_PATTERN =
- /^https?:\/\/(?:twitter\.com|x\.com)\/([^\/]+)\/status\/(\d+)/i;
- // Track iframes by URL for resize messages
- var iframesByUrl = {};
- /**
- * Extract tweet info (username and ID) from a blockquote's links
- */
- function findTweetInfo(blockquote) {
- var links = blockquote.querySelectorAll("a");
- for (var i = 0; i < links.length; i++) {
- var match = TWEET_URL_PATTERN.exec(links[i].href);
- if (match) {
- return { username: match[1], tweetId: match[2] };
- }
- }
- return null;
- }
- /**
- * Transform all twitter-tweet blockquotes into Nitter embed iframes
- */
- function transformBlockquotes() {
- var blockquotes = document.querySelectorAll("blockquote.twitter-tweet");
- for (var i = 0; i < blockquotes.length; i++) {
- var blockquote = blockquotes[i];
- var tweetInfo = findTweetInfo(blockquote);
- if (!tweetInfo) continue;
- var embedUrl =
- NITTER_URL +
- "/" +
- tweetInfo.username +
- "/status/" +
- tweetInfo.tweetId +
- "/embed";
- var iframe = document.createElement("iframe");
- iframe.src = embedUrl;
- iframe.style.cssText =
- "width: 100%; max-width: 550px; height: 250px; border: none; display: block;";
- iframe.loading = "lazy";
- // Track iframe for resize messages
- if (!iframesByUrl[embedUrl]) {
- iframesByUrl[embedUrl] = [];
- }
- iframesByUrl[embedUrl].push(iframe);
- blockquote.parentNode.replaceChild(iframe, blockquote);
- }
- }
- /**
- * Handle resize messages from Nitter embeds
- */
- function handleResizeMessage(event) {
- if (!Array.isArray(event.data) || event.data[0] !== "resizeIframe") return;
- var data = event.data[1];
- if (!data.h || data.h <= 0) return;
- var iframes = iframesByUrl[data.url];
- if (iframes) {
- for (var i = 0; i < iframes.length; i++) {
- iframes[i].style.height = data.h + "px";
- }
- }
- }
- // Remove any Twitter widget scripts that might have been loaded
- var twitterScripts = document.querySelectorAll(
- 'script[src*="platform.twitter.com/widgets.js"], script[src*="platform.x.com/widgets.js"]',
- );
- for (var i = 0; i < twitterScripts.length; i++) {
- twitterScripts[i].remove();
- }
- // Listen for resize messages from embeds
- window.addEventListener("message", handleResizeMessage);
- // Transform existing blockquotes
- if (document.readyState === "loading") {
- document.addEventListener("DOMContentLoaded", transformBlockquotes);
- } else {
- transformBlockquotes();
- }
- // Watch for dynamically added blockquotes
- var observer = new MutationObserver(function (mutations) {
- for (var i = 0; i < mutations.length; i++) {
- var addedNodes = mutations[i].addedNodes;
- for (var j = 0; j < addedNodes.length; j++) {
- var node = addedNodes[j];
- if (node.nodeType !== 1) continue;
- var isTwitterBlockquote =
- node.matches && node.matches("blockquote.twitter-tweet");
- var containsTwitterBlockquote =
- node.querySelector && node.querySelector("blockquote.twitter-tweet");
- if (isTwitterBlockquote || containsTwitterBlockquote) {
- transformBlockquotes();
- return;
- }
- }
- }
- });
- if (document.body) {
- observer.observe(document.body, { childList: true, subtree: true });
- }
- // Provide a fake twttr object for compatibility with sites that check for it
- window.twttr = window.twttr || {};
- window.twttr.widgets = {
- load: transformBlockquotes,
- loaded: true,
- };
- })();
|