您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Change Twitter X Icon
当前为
- // ==UserScript==
- // @name Twitter X Icon
- // @namespace TwitterX
- // @match https://twitter.com/*
- // @grant none
- // @version 0.1.0
- // @author CY Fung
- // @description Change Twitter X Icon
- // @run-at document-start
- // @license MIT
- // ==/UserScript==
- (() => {
- let mIconUrl = '';
- let linkCache = new Map();
- let waa = new WeakSet();
- let mDotUrlMap = new Map();
- const op = {
- radius: (canvas)=>Math.round(canvas.width * 0.14),
- x: (canvas, radius)=> canvas.width - radius * 2 + radius*0.05,
- y: (canvas, radius)=>0 + radius * 2 - radius*0.3,
- };
- function addRedDotToImage(dataUriBase64, op) {
- return new Promise((resolve, reject) => {
- // Create an image element to load the data URI
- const image = new Image();
- image.onload = () => {
- const canvas = document.createElement('canvas');
- canvas.width = image.width;
- canvas.height = image.height;
- const ctx = canvas.getContext('2d');
- ctx.drawImage(image, 0, 0);
- const radius = op.radius(canvas);
- const dotX = op.x(canvas, radius);
- const dotY = op.y(canvas, radius);
- // Draw a red dot on the top right corner
- ctx.beginPath();
- ctx.arc(dotX, dotY, radius, 0, 2 * Math.PI);
- ctx.fillStyle = 'red';
- ctx.fill();
- // Convert the canvas back to a data URI base64 string
- const revisedDataUriBase64 = canvas.toDataURL();
- resolve(revisedDataUriBase64);
- };
- // Set the image source to the provided data URI
- image.src = dataUriBase64;
- });
- }
- function myLink(link, dottable) {
- if (waa.has(link)) return;
- waa.add(link);
- let hrefDtor = Object.getOwnPropertyDescriptor(link.constructor.prototype, 'href');
- if (!hrefDtor.set || !hrefDtor.get) {
- return;
- }
- const getHref = () => {
- return hrefDtor.get.call(link)
- }
- let qq = null;
- async function updateURL(hh) {
- console.log('old href', hh, link.getAttribute('has-dot') === 'true')
- let nurl = mIconUrl;
- if(nurl && hh){
- let href = hh;
- let isDotted = link.getAttribute('has-dot') === 'true'
- if (isDotted && !nurl.startsWith('http') ) {
- nurl = await addRedDotToImage(nurl, op);
- }
- }
- if (hh !== nurl && nurl) link.href = nurl;
- }
- function ckk() {
- const hh = getHref();
- if (qq === hh) return;
- qq = hh;
- updateURL(hh);
- }
- function updateDotState(hh2) {
- if (hh2 && typeof hh2 =='string' && hh2.startsWith('http')) {
- let href = hh2;
- let isDotted = false;
- if (mDotUrlMap.has(href)) isDotted = mDotUrlMap.get(href);
- else {
- if (href.endsWith('/twitter-pip.3.ico')) isDotted = true;
- else {
- let q = /\?[^?.:\/\\]+/.exec(href);
- q = q ? q[0] : '';
- if (q) {
- isDotted = true;
- }
- }
- mDotUrlMap.set(href, isDotted);
- }
- link.setAttribute('has-dot', isDotted ? 'true' : 'false')
- }
- Promise.resolve().then(ckk)
- }
- let hh2 = null;
- hh2 = getHref();
- updateDotState(hh2);
- Object.defineProperty(link, 'href', {
- get() {
- return hh2;
- },
- set(a) {
- if (!a || a.startsWith('http')) {
- hh2 = a;
- updateDotState(hh2);
- }
- return hrefDtor.set.call(this, a);
- }
- });
- document.addEventListener('my-twitter-icon-has-changed',(evt)=>{
- if(!evt) return;
- let detail = evt.detail;
- if(!detail)return;
- let mIconUrl = detail.mIconUrl;
- if(!mIconUrl) return;
- link.href = mIconUrl;
- console.log('icon changed')
- Promise.resolve().then(ckk);
- },true);
- }
- function mIconFn(iconUrl, rel, dottable) {
- const selector = `link[rel~="${rel}"]`;
- let link = document.querySelector(selector);
- if (!link) {
- /** @type {HTMLLinkElement} */
- link = document.createElement("link");
- link.rel = `${rel}`;
- link.href = iconUrl;
- document.head.appendChild(link);
- }
- for (const link of document.querySelectorAll(selector)) {
- if(waa.has(link))continue;
- myLink(link, dottable);
- }
- }
- function replacePageIcon(iconUrl) {
- mIconFn(iconUrl, 'icon', 1)
- }
- function replaceAppIcon(iconUrl) {
- mIconFn(iconUrl, 'apple-touch-icon', 0);
- }
- const addCSS = (href) => {
- let p = document.querySelector('style#j8d4f');
- if (!p) {
- p = document.createElement('style');
- p.id = 'j8d4f';
- document.head.appendChild(p);
- }
- let newTextContent = `
- a[href="/home"][aria-label="Twitter"] * {
- pointer-events: none;
- }
- a[href="/home"][aria-label="Twitter"] > div::before {
- background-image: url("${href}");
- position: absolute;
- left: 0;
- right: 0;
- top: 0;
- bottom: 0;
- content: '';
- color: #fff;
- display: block;
- background-size: cover;
- background-position: center;
- background-repeat: no-repeat;
- border-radius: 50% / 50%;
- }
- a[href="/home"] svg::before {
- display: block;
- position: absolute;
- content: "";
- left: 0;
- right: 0;
- top: 0;
- bottom: 0;
- }
- a[href="/home"] svg path {
- visibility: collapse;
- }
- `;
- newTextContent = newTextContent.trim();
- if (p.textContent !== newTextContent) p.textContent = newTextContent;
- }
- let qdd = 0;
- function sendMessageIconChanged (mIconUrl){
- document.dispatchEvent(new CustomEvent('my-twitter-icon-has-changed', {detail: { mIconUrl }}));
- }
- function changeIconFn(withPageElement) {
- mIconUrl = localStorage.getItem('myCustomTwitterIcon');
- if (!mIconUrl) return;
- let tid = qdd = Date.now();
- if (tid !== qdd) return;
- addCSS(mIconUrl);
- replacePageIcon(mIconUrl);
- replaceAppIcon(mIconUrl);
- sendMessageIconChanged(mIconUrl)
- }
- function onImageLoaded(dataURL) {
- // Save the data URL to localStorage with a specific key
- localStorage.setItem('myCustomTwitterIcon', dataURL);
- console.log('myCustomTwitterIcon - done');
- changeIconFn(1);
- }
- // Function to handle the image drop event
- function handleDrop(event) {
- if (!event) return;
- if (!(event.target instanceof HTMLElement)) return;
- event.preventDefault();
- // Check if the target element is the desired anchor with href="/home"
- const targetElement = event.target.closest('a[href="/home"][aria-label="Twitter"]');
- if (!targetElement) return;
- // Get the dropped file (assuming only one file is dropped)
- const file = event.dataTransfer.files[0];
- // Check if the dropped file is an image
- if (!file || !file.type.startsWith('image/')) return;
- linkCache.clear();
- // Read the image file and convert to base64 data URL
- let reader = new FileReader();
- reader.onload = function () {
- Promise.resolve(reader.result).then(onImageLoaded);
- reader = null;
- };
- reader.readAsDataURL(file);
- }
- // Function to handle the dragover event and allow dropping
- function handleDragOver(event) {
- event.preventDefault();
- }
- if (localStorage.getItem('myCustomTwitterIcon')) {
- changeIconFn(0);
- }
- let observer = null;
- // Function to check if the target element is available and hook the drag and drop functionality
- function hookDragAndDrop() {
- const targetElement = document.querySelector('a[href="/home"]');
- if (targetElement && observer) {
- targetElement.addEventListener('dragover', handleDragOver);
- targetElement.addEventListener('drop', handleDrop);
- console.log('Drag and drop functionality hooked.');
- observer.takeRecords();
- // Stop and disconnect the observer since the targetElement is found
- observer.disconnect();
- observer = null;
- if (localStorage.getItem('myCustomTwitterIcon')) {
- changeIconFn(1);
- }
- }
- }
- // Use MutationObserver to observe changes in the document
- observer = new MutationObserver(function (mutationsList, observer) {
- let p = false;
- for (const mutation of mutationsList) {
- if (mutation.type === 'childList' || mutation.type === 'subtree') {
- p = true;
- }
- }
- if (p) hookDragAndDrop();
- });
- // Start observing the entire document
- observer.observe(document, { childList: true, subtree: true });
- })();