Greasy Fork is available in English.

Twitter X Icon

Change Twitter X Icon

  1. // ==UserScript==
  2. // @name Twitter X Icon
  3. // @namespace TwitterX
  4. // @match https://twitter.com/*
  5. // @grant none
  6. // @unwrap
  7. // @inject-into page
  8. // @version 0.1.13
  9. // @author CY Fung
  10. // @description Change Twitter X Icon
  11. // @run-at document-start
  12. // @license MIT
  13.  
  14. // @description:ja Twitterアイコンをカスタマイズして個性を表現しましょう。
  15. // @description:zh-TW 自訂 Twitter 圖示,展現獨特風格。
  16. // @description:zh-CN 自定义 Twitter 图标,展现独特风格。
  17.  
  18. // @description:ko 트위터 아이콘을 원하는 이미지로 변경하여 개성을 표현하세요.
  19. // @description:ru Замените иконку Twitter на свое изображение и выразите свою индивидуальность.
  20. // @description:af Vervang die Twitter-ikoon met 'n persoonlike prent om jou individualiteit uit te druk.
  21. // @description:az Twitter nişanını şəxsi şəkil ilə dəyişdirin və özünüzü ifadə edin.
  22. // @description:id Ganti ikon Twitter dengan gambar pilihan Anda dan tunjukkan gaya pribadi.
  23. // @description:ms Tukar ikon Twitter dengan imej pilihan anda dan tunjukkan gaya peribadi.
  24. // @description:bs Zamijenite Twitter ikonu sa odabranom slikom i izrazite svoj stil.
  25. // @description:ca Canvieu la icona de Twitter amb una imatge personalitzada i mostreu el vostre estil.
  26. // @description:cs Změňte ikonu Twitteru na vlastní obrázek a vyjádřete svůj osobní styl.
  27. // @description:da Skift Twitter-ikonet med et personligt billede og vis din stil.
  28. // @description:de Ersetzen Sie das Twitter-Icon durch ein persönliches Bild und zeigen Sie Ihren Stil.
  29. // @description:et Asendage Twitteri ikoon isikliku pildiga ja näidake oma stiili.
  30. // @description:es Cambia el ícono de Twitter por una imagen personalizada y muestra tu estilo.
  31. // @description:eu Aldatu Twitter ikurria zure irudi pertsonal batekin eta erakutsi zure estiloa.
  32. // @description:fr Remplacez l'icône Twitter par une image personnalisée et montrez votre style.
  33. // @description:gl Cambia o icona de Twitter cunha imaxe personalizada e mostra o teu estilo.
  34. // @description:hr Zamijenite Twitter ikonu osobnom slikom i pokažite svoj stil.
  35. // @description:zu Thayela uhlobo lweTwitter ngezithombe ozokhetha ukuze uphazamise izibonelo zakho.
  36. // @description:is Skiptu út Twitter tákn með persónulegu myndi og sýndu stílinn þinn.
  37. // @description:it Sostituisci l'icona di Twitter con un'immagine personalizzata e mostra il tuo stile.
  38. // @description:sw Badilisha ishara ya Twitter na picha yako ya kibinafsi na onyesha mtindo wako.
  39. // @description:lv Mainiet Twitter ikonu ar personīgu attēlu un parādiet savu stilu.
  40. // @description:lt Pakeiskite „Twitter“ piktogramą asmeniniu vaizdu ir parodykite savo stilų.
  41. // @description:hu Cserélje le a Twitter ikont egyéni képre, és mutassa meg stílusát.
  42. // @description:nl Vervang het Twitter-pictogram door een aangepaste afbeelding en toon uw stijl.
  43. // @description:uz Twitter niqobini o'zgartiring va shaxsiy tasvir bilan o'zingizni ifodalang.
  44. // @description:pl Zmień ikonę Twittera na wybrany obraz i pokaż swój styl.
  45. // @description:pt Substitua o ícone do Twitter por uma imagem personalizada e mostre o seu estilo.
  46. // @description:pt-BR Substitua o ícone do Twitter por uma imagem personalizada e mostre o seu estilo.
  47. // @description:ro Înlocuiți iconița Twitter cu o imagine personalizată și arătați-vă stilul.
  48. // @description:sq Zëvendësoni ikonën e Twitter me një imazh të personalizuar dhe tregoni stilin tuaj.
  49. // @description:sk Nahraďte ikonu Twitteru vlastným obrázkom a ukážte svoj štýl.
  50. // @description:sl Zamenjajte ikono Twitter z izbrano sliko in izrazite svoj slog.
  51. // @description:sr Zamenite Twitter ikonu ličnom slikom i pokažite svoj stil.
  52. // @description:fi Vaihda Twitter-kuvake omalla kuvalla ja näytä oma tyyli.
  53. // @description:sv Byt ut Twitter-ikonen med en anpassad bild och visa din stil.
  54. // @description:vi Thay đổi biểu tượng Twitter bằng hình ảnh tùy chỉnh và thể hiện phong cách của bạn.
  55. // @description:tr Twitter simgesini istediğiniz bir görüntüyle değiştirin ve tarzınızı gösterin.
  56. // @description:be Замяніце іконку Twitter на свой малюнак і выразіце свой стыль.
  57. // @description:bg Заменете иконата на Twitter с изображение по ваш избор и покажете своя стил.
  58. // @description:ky Twitter иконкасын карата муркунун издесеңиз жана стилиңизди көрсөтүңүз.
  59. // @description:kk Twitter белгісін таңдап жатқан суретпен ауыстырып, стильіңізді көрсетіңіз.
  60. // @description:mk Заменете ја иконата на Twitter со слика по ваш избор и прикажете го вашиот стил.
  61. // @description:mn Twitter хэрэглэгчийн дүрсийг өөрчил, таны ихэнх стилийг харуулна уу.
  62. // @description:uk Замініть іконку Twitter на свій малюнок і виразіть свій стиль.
  63. // @description:el Αντικαταστήστε το εικονίδιο του Twitter με εικόνα της επιλογής σας και εμφανίστε το στυλ σας.
  64. // @description:hy Փոխարինեք Twitter-ի պատկերն անձնանշանով և ցուցադրեք ձեր ոլորտը:
  65. // @description:ur ٹوئٹر آئیکن کو آپ کی منتخب تصویر سے تبدیل کریں اور اپنی شہرت کو ظاہر کریں۔
  66. // @description:ar قم بتغيير أيقونة Twitter إلى صورة اختيارك وعرض أسلوبك الشخصي.
  67. // @description:fa آیکن Twitter را با تصویر انتخابی تغییر داده و سبک خود را نشان دهید.
  68. // @description:ne आफ्नो छवि छान्ने गरी ट्विटर चिन्ह परिवर्तन गर्नुहोस् र आफ्नो शैली प्रदर्शन गर्नुहोस्।
  69. // @description:mr Twitter चिन्हाची प्रतिनिधित्व करण्यासाठी आपली पसंतीची चित्रे वापरा.
  70. // @description:hi ट्विटर आइकन को अपनी पसंदीदा तस्वीर से बदलें और अपनी शैली दिखाएं।
  71. // @description:as আপোনাৰ বৰ্ণনাৰ সৈতে Twitter চিনত পৰিষ্কৰণ কৰক।
  72. // @description:bn আপনার পছন্দের ছবি দিয়ে Twitter চিহ্নিকা পরিবর্তন করুন এবং আপনার শৈলী প্রদর্শন করুন।
  73. // @description:pa ਆਪਣੀ ਚੋਣ ਦੀ ਤਸਵੀਰ ਨਾਲ Twitter ਚਿੰਨਕ ਨੂੰ ਬਦਲੋ ਅਤੇ ਆਪਣੀ ਸ਼ੈਲੀ ਦਿਖਾਓ।
  74. // @description:gu આપની પસંદની ચિત્રો સાથે Twitter ચિન્હ બદલો અને તમારી શૈલી બતાવો.
  75. // @description:or ଆପଣଙ୍କ ପସନ୍ଦର ଚିତ୍ରରେ Twitter ପ୍ରତିକା ବଦଳାନ୍ତୁ ଏବଂ ଆପଣଙ୍କ ସ୍ଟାଇଲ ଦର୍ଶନ ଦିଅନ୍ତୁ।
  76. // @description:ta உங்கள் ஆர்வம் உள்ள படத்தைப் பயன்படுத்தி Twitter ஐகானை மாற்றவும் உங்கள் பார்வையைக் காட்டுங்கள்.
  77. // @description:te మీ ఇష్టమైన చిత్రంతో Twitter గురించిన చిహ్నాన్ని మార్చండి మరియు మీ శైలిని చూపించండి.
  78. // @description:kn ನಿಮ್ಮ ಆಸಕ್ತಿಗೆ ಅನುಗುನವಾಗಿ Twitter ಚಿಹ್ನೆಯನ್ನು ಬದಲಾಯಿಸಿ ಮತ್ತು ನಿಮ್ಮ ಶೈಲಿಯನ್ನು ತೋರಿಸಿ.
  79. // @description:ml നിങ്ങളുടെ ഇഷ്ട ചിത്രം ഉപയോഗിച്ച് Twitter ചിഹ്നം മാറ്റുകയും നിങ്ങളുടെ ശൈലി കാണിക്കുകയും ചെയ്യുക.
  80. // @description:si ඔබේ ආදරයෙන් Twitter ලකුණ වෙනස් කිරීමේදී ඔබේ ප්‍රධාන වෙළඳ ආකර්ෂණය පෙන්වන්ද සඳහා ඉඩදීම.
  81. // @description:th เปลี่ยนไอคอน Twitter ด้วยภาพที่คุณเลือกและแสดงสไตล์ของคุณ
  82. // @description:lo ປ່ຽນເວັບໄຊ Twitter ໂດຍຮູບພາບທີ່ເຈົ້າເລືອກແລະສະແດງສະຖານທີ່ຂ້ອຍ
  83. // @description:my Twitter အိုင်ကွန်းရွေးကို သင်ရွေးသောပုံမျှ နှင့်သင်၏အပြင်ကိုပြပေးပါ။
  84. // @description:ka Twitter ხატულა შეიცვალეთ თქვენი რასიელი სურათით და გამოაჩინეთ თქვენი სტილი.
  85. // @description:am የጥንቃቄዎን ምስል በ Twitter አይኮን መቀየር እና ስብስቦችዎን ተማሪዎች እንዴት ያቀየረው እንደሚፈልግ ትስጋላችሁ።
  86. // @description:km ប្តូររូបក្នុង Twitter ជារូបសម្រាប់អ្នកដែលអ្នកស្រលាញ់យ៉ាងមាននងពីរបៀបផ្សេងៗ
  87.  
  88. // ==/UserScript==
  89.  
  90.  
  91. (() => {
  92.  
  93. let mIconUrl = '';
  94. let linkCache = new Map();
  95.  
  96. let waa = new WeakSet();
  97.  
  98. let mDotUrlMap = new Map();
  99.  
  100. const op = {
  101. radius: (canvasSize) => Math.round(canvasSize.width * 0.14),
  102.  
  103. x: (canvasSize, radius) => canvasSize.width - radius * 2 + radius * 0.05,
  104.  
  105. y: (canvasSize, radius) => 0 + radius * 2 - radius * 0.3,
  106.  
  107. };
  108.  
  109. function addRedDotToImage(dataUriBase64, op) {
  110. return new Promise((resolve, reject) => {
  111. // Create an image element to load the data URI
  112. const image = new Image();
  113. image.onload = () => {
  114.  
  115. const { width, height } = image;
  116. const canvasSize = {
  117. width, height
  118. }
  119.  
  120. const radius = op.radius(canvasSize);
  121. const dotX = op.x(canvasSize, radius);
  122. const dotY = op.y(canvasSize, radius);
  123.  
  124. // Convert the canvas back to a data URI base64 string
  125. let revisedDataUriBase64;
  126. if (dataUriBase64.startsWith('data:image/svg+xml')) {
  127. // For SVG, create a new SVG element and add the circle element
  128. const svgElement = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
  129. svgElement.setAttribute('width', width);
  130. svgElement.setAttribute('height', height);
  131.  
  132. // Create a new image element within the SVG
  133. const svgImageElement = document.createElementNS('http://www.w3.org/2000/svg', 'image');
  134. svgImageElement.setAttribute('width', width);
  135. svgImageElement.setAttribute('height', height);
  136. svgImageElement.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', dataUriBase64);
  137. svgElement.appendChild(svgImageElement);
  138.  
  139. // Create a red dot circle element
  140. const circleElement = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
  141. circleElement.setAttribute('cx', dotX);
  142. circleElement.setAttribute('cy', dotY);
  143. circleElement.setAttribute('r', radius);
  144. circleElement.setAttribute('fill', 'red');
  145. svgElement.appendChild(circleElement);
  146.  
  147. if (typeof btoa !== 'function') return reject();
  148. try {
  149.  
  150. // Convert the modified SVG element back to a data URI base64 string
  151. const serializer = new XMLSerializer();
  152. const svgString = serializer.serializeToString(svgElement);
  153. revisedDataUriBase64 = 'data:image/svg+xml;base64,' + btoa(svgString);
  154. } catch (e) { }
  155. } else {
  156.  
  157. const canvas = document.createElement('canvas');
  158. canvas.width = width;
  159. canvas.height = height;
  160.  
  161. const ctx = canvas.getContext('2d');
  162. ctx.drawImage(image, 0, 0);
  163.  
  164. // Draw a red dot on the top right corner
  165. ctx.beginPath();
  166. ctx.arc(dotX, dotY, radius, 0, 2 * Math.PI);
  167. ctx.fillStyle = 'red';
  168. ctx.fill();
  169. try {
  170. revisedDataUriBase64 = canvas.toDataURL();
  171. } catch (e) { }
  172. }
  173.  
  174.  
  175. if (!revisedDataUriBase64) {
  176. return reject();
  177. }
  178.  
  179. // Convert the canvas back to a data URI base64 string
  180. // const revisedDataUriBase64 = canvas.toDataURL();
  181. resolve(revisedDataUriBase64);
  182. };
  183.  
  184. // Set the image source to the provided data URI
  185. image.src = dataUriBase64;
  186. });
  187. }
  188.  
  189.  
  190. function myLink(link, dottable) {
  191.  
  192. if (waa.has(link)) return;
  193. waa.add(link);
  194.  
  195.  
  196. let hrefDtor = Object.getOwnPropertyDescriptor(link.constructor.prototype, 'href');
  197.  
  198. if (!hrefDtor.set || !hrefDtor.get) {
  199. return;
  200. }
  201.  
  202. const getHref = () => {
  203. return hrefDtor.get.call(link)
  204. }
  205.  
  206. let qq = null;
  207.  
  208.  
  209. async function updateURL(hh) {
  210.  
  211.  
  212. // console.log('old href', hh, link.getAttribute('has-dot') === 'true')
  213.  
  214. let nurl = mIconUrl;
  215.  
  216. if (nurl && hh) {
  217.  
  218. let href = hh;
  219. let isDotted = link.getAttribute('has-dot') === 'true'
  220.  
  221. if (isDotted && !nurl.startsWith('http')) {
  222. nurl = await addRedDotToImage(nurl, op);
  223. }
  224.  
  225.  
  226.  
  227. if (hh !== nurl && nurl) {
  228.  
  229.  
  230. let rel = link.getAttribute('rel');
  231. if (rel === 'icon' || rel === 'shortcut icon') {
  232. const link1 = document.querySelector('link[rel="icon"]');
  233. const link2 = document.querySelector('link[rel="shortcut icon"]');
  234.  
  235. if(link1) link1.setAttribute('has-dot', isDotted ? 'true' : 'false');
  236. if(link2) link2.setAttribute('has-dot', isDotted ? 'true' : 'false');
  237.  
  238. if(link1) link1.href = nurl;
  239. if(link2) link2.href = nurl;
  240. } else {
  241. link.href = nurl;
  242. }
  243.  
  244.  
  245.  
  246. }
  247.  
  248. }
  249.  
  250.  
  251.  
  252.  
  253.  
  254. }
  255.  
  256. function ckk() {
  257. const hh = getHref();
  258. if (qq === hh) return;
  259. qq = hh;
  260. updateURL(hh);
  261. }
  262.  
  263.  
  264. function updateDotState(hh2) {
  265.  
  266. if (hh2 && typeof hh2 == 'string' && hh2.startsWith('http')) {
  267. let href = hh2;
  268. let isDotted = false;
  269.  
  270. if (mDotUrlMap.has(href)) isDotted = mDotUrlMap.get(href);
  271. else {
  272.  
  273. if (href.endsWith('/twitter-pip.3.ico')) isDotted = true;
  274. else {
  275.  
  276. let q = /\?[^?.:\/\\]+/.exec(href);
  277. q = q ? q[0] : '';
  278.  
  279. if (q) {
  280. isDotted = true;
  281. }
  282.  
  283. }
  284.  
  285. mDotUrlMap.set(href, isDotted);
  286.  
  287.  
  288. }
  289.  
  290.  
  291. link.setAttribute('has-dot', isDotted ? 'true' : 'false')
  292. }
  293.  
  294. Promise.resolve().then(ckk)
  295.  
  296.  
  297.  
  298. }
  299.  
  300. let hh2 = null;
  301.  
  302. hh2 = getHref();
  303. updateDotState(hh2);
  304.  
  305. Object.defineProperty(link, 'href', {
  306. get() {
  307. return hh2;
  308. },
  309. set(a) {
  310. if (!a || a.startsWith('http')) {
  311. hh2 = a;
  312. updateDotState(hh2);
  313. }
  314. return hrefDtor.set.call(this, a);
  315. }
  316.  
  317. });
  318.  
  319.  
  320.  
  321. document.addEventListener('my-twitter-icon-has-changed', (evt) => {
  322.  
  323. if (!evt) return;
  324. let detail = evt.detail;
  325.  
  326. if (!detail) return;
  327. let mIconUrl = detail.mIconUrl;
  328. if (!mIconUrl) return;
  329.  
  330.  
  331. link.href = mIconUrl;
  332. // console.log('icon changed')
  333.  
  334. Promise.resolve().then(ckk);
  335.  
  336.  
  337.  
  338. }, true);
  339.  
  340. }
  341.  
  342. function mIconFn(iconUrl, rel, dottable) {
  343.  
  344.  
  345.  
  346. const selector = `link[rel~="${rel}"]`;
  347. let link = document.querySelector(selector);
  348. if (!link) {
  349.  
  350. /** @type {HTMLLinkElement} */
  351. link = document.createElement("link");
  352. link.rel = `${rel}`;
  353. link.href = iconUrl;
  354. document.head.appendChild(link);
  355. }
  356.  
  357. for (const link of document.querySelectorAll(selector)) {
  358. if (waa.has(link)) continue;
  359. myLink(link, dottable);
  360. }
  361.  
  362.  
  363. }
  364.  
  365. function replacePageIcon(iconUrl) {
  366. mIconFn(iconUrl, 'icon', 1)
  367. }
  368.  
  369. function replaceAppIcon(iconUrl) {
  370.  
  371. mIconFn(iconUrl, 'apple-touch-icon', 0);
  372. }
  373.  
  374.  
  375. const addCSS = (href) => {
  376. let p = document.querySelector('style#j8d4f');
  377. if (!p) {
  378. p = document.createElement('style');
  379. p.id = 'j8d4f';
  380. document.head.appendChild(p);
  381. }
  382.  
  383. let newTextContent = `
  384. a[href][my-custom-icon] > div::before {
  385.  
  386. background-image: url("${href}");
  387. --my-custom-icon-padding: 6px;
  388. position: absolute;
  389. left: var(--my-custom-icon-padding);
  390. right: var(--my-custom-icon-padding);
  391. top: var(--my-custom-icon-padding);
  392. bottom: var(--my-custom-icon-padding);
  393. content: '';
  394. color: #fff;
  395. display: block;
  396. background-size: cover;
  397. background-position: center;
  398. background-repeat: no-repeat;
  399. border-radius: 46%;
  400. }
  401. a[href][my-custom-icon] svg::before {
  402. display: block;
  403. position: absolute;
  404. content: "";
  405. left: 0;
  406. right: 0;
  407. top: 0;
  408. bottom: 0;
  409. }
  410.  
  411.  
  412. a[href][my-custom-icon] svg path {
  413. visibility: collapse;
  414. }
  415.  
  416. `;
  417. newTextContent = newTextContent.trim();
  418.  
  419. if (p.textContent !== newTextContent) p.textContent = newTextContent;
  420. }
  421.  
  422. let qdd = 0;
  423.  
  424. function sendMessageIconChanged(mIconUrl) {
  425. document.dispatchEvent(new CustomEvent('my-twitter-icon-has-changed', { detail: { mIconUrl } }));
  426. }
  427.  
  428. function changeIconFn() {
  429. mIconUrl = localStorage.getItem('myCustomTwitterIcon');
  430. if (!mIconUrl) return;
  431.  
  432. let tid = qdd = Date.now();
  433.  
  434. if (tid !== qdd) return;
  435.  
  436. addCSS(mIconUrl);
  437. replacePageIcon(mIconUrl);
  438. replaceAppIcon(mIconUrl);
  439.  
  440. sendMessageIconChanged(mIconUrl)
  441.  
  442.  
  443. }
  444.  
  445.  
  446. function onImageLoaded(dataURL) {
  447.  
  448.  
  449. // Save the data URL to localStorage with a specific key
  450. localStorage.setItem('myCustomTwitterIcon', dataURL);
  451. // console.log('myCustomTwitterIcon - done');
  452. changeIconFn();
  453.  
  454.  
  455.  
  456. }
  457.  
  458.  
  459. // Function to handle the image drop event
  460. function handleDrop(event) {
  461. if (!event) return;
  462.  
  463. if (!(event.target instanceof HTMLElement)) return;
  464.  
  465. event.preventDefault();
  466. // Check if the target element is the desired anchor with href="/home"
  467. const targetElement = event.target.closest('a[href][my-custom-icon]');
  468. if (!targetElement) return;
  469.  
  470. // Get the dropped file (assuming only one file is dropped)
  471. const file = event.dataTransfer.files[0];
  472.  
  473. // Check if the dropped file is an image
  474. if (!file || !file.type.startsWith('image/')) return;
  475.  
  476. linkCache.clear();
  477.  
  478. // Read the image file and convert to base64 data URL
  479. let reader = new FileReader();
  480. reader.onload = function () {
  481. Promise.resolve(reader.result).then(onImageLoaded);
  482. reader = null;
  483. };
  484. reader.readAsDataURL(file);
  485. }
  486.  
  487. // Function to handle the dragover event and allow dropping
  488. function handleDragOver(event) {
  489. event.preventDefault();
  490. }
  491.  
  492.  
  493. if (localStorage.getItem('myCustomTwitterIcon')) {
  494.  
  495. changeIconFn();
  496. }
  497.  
  498. let observer = null;
  499.  
  500. // Function to check if the target element is available and hook the drag and drop functionality
  501. function hookDragAndDrop() {
  502. const targetElement = document.querySelector('a[href="/home"][aria-label="Twitter"], a[href="/home"][aria-label="X"]');
  503. if (targetElement && observer) {
  504. targetElement.setAttribute('my-custom-icon', '');
  505. targetElement.addEventListener('dragover', handleDragOver);
  506. targetElement.addEventListener('drop', handleDrop);
  507. // console.log('Drag and drop functionality hooked.');
  508.  
  509. document.head.appendChild(document.createElement('style')).textContent = `
  510. a[href="/home"][aria-label="Twitter"][my-custom-icon] *,
  511. a[href="/home"][aria-label="X"][my-custom-icon] * {
  512. pointer-events: none;
  513. }
  514. `;
  515.  
  516.  
  517. observer.takeRecords();
  518. // Stop and disconnect the observer since the targetElement is found
  519. observer.disconnect();
  520. observer = null;
  521.  
  522. if (localStorage.getItem('myCustomTwitterIcon')) {
  523.  
  524. changeIconFn();
  525. }
  526.  
  527.  
  528. }
  529. }
  530.  
  531. // Use MutationObserver to observe changes in the document
  532. observer = new MutationObserver(function (mutationsList, observer) {
  533. let p = false;
  534. for (const mutation of mutationsList) {
  535. if (mutation.type === 'childList' || mutation.type === 'subtree') {
  536. p = true;
  537.  
  538. }
  539. }
  540. if (p) hookDragAndDrop();
  541. });
  542.  
  543. // Start observing the entire document
  544. observer.observe(document, { childList: true, subtree: true });
  545.  
  546. document.addEventListener('change-my-twitter-icon', () => {
  547. changeIconFn();
  548. }, true);
  549.  
  550. })();