您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Show your Twitter Lists in sidebar
// ==UserScript== // @name Twitter Lists Sidebar // @description Show your Twitter Lists in sidebar // @version 0.3 // @grant GM.xmlHttpRequest // @include https://twitter.com/* // @namespace https://greasyfork.runtimutd.eu.org/users/173161 // ==/UserScript== // Note: Avoid using jQuery as it may not be avialble on page load yet (function(){ // Cache data using localStorage or sessionStorage // class definion have to be placed before its usage class Cache { constructor(){ // Key name prefix. Avoid clashes with Twitter itself this.prefix = 'lists-sidebar:'; // Use sessionStorage or localStorage this.storage = window.sessionStorage; } get(name) { return this.storage.getItem(this.prefix + name); } set(name, value) { return this.storage.setItem(this.prefix + name, value); } clear() { for (let key in this.storage) { if (key.indexOf(this.prefix) === 0) { this.storage.removeItem(key); } } } } let usernameTag = document.querySelector('.DashUserDropdown-userInfo .username b'); const username = usernameTag && usernameTag.innerText; // Not login if (!username) { return; } const cache = new Cache(); // Clear cache if user changed if (cache.get('username') && cache.get('username') !== username) { cache.clear(); } let sidebar; // Execute once on page load pageChanged(); // Poll to detect page navigation. // Twitter.com is an SPA and thus document ready event is only fired once. // history.onpopstate event is not reliable as history.pushState does not trigger it let currentPathname = location.pathname; setInterval(function(){ if (location.pathname !== currentPathname) { currentPathname = location.pathname; pageChanged(); } }, 300); function ajaxRequest(url, callback) { // "Referer" header is required for the request to succeed, // but native XMLHttpRequest does not alllow to set this header // https://wiki.greasespot.net/GM.xmlHttpRequest GM.xmlHttpRequest({ method: 'GET', url: url, timeout: 3000, headers: { 'Accept': 'application/json, text/javascript, */*; q=0.01', 'Referer': 'https://twitter.com/', 'X-Push-State-Request': 'true', 'X-Twitter-Active-User': 'yes', 'X-Requested-With': 'XMLHttpRequest', }, onload: function(details) { callback(JSON.parse(details.responseText).page.replace(/\\n|\\/g, '')); }, onerror: function() { callback(null); }, ontimeout: function() { callback(null); } }); } // Retrive lists by requesting lists page and parsing returned HTML function retriveLists(username, callback) { const pattern = /<a class="ProfileListItem-name[^>]*href="([^"]+)">([^<]*)<\/a>/g; ajaxRequest( 'https://twitter.com/' + username + '/lists', function(html) { if (!html) { return; } let lists = []; let match = null; while (match = pattern.exec(html)) { lists.push({ url: match[1], name: match[2], }); } // Sort by name lists.sort(function(a, b){ return a.name > b.name ? 1 : (a.name < b.name ? -1 : 0); }); callback && callback(lists.length > 0 ? lists : null); } ); } function createSidebar(lists) { if (!lists || !lists.length) { return; } sidebar = document.createElement('sidebar'); sidebar.id = 'lists-sidebar'; // Reuse existing style for consistent color and backgroud-color sidebar.className = 'DashboardProfileCard' let title = document.createElement('h3'); title.innerHTML = 'Lists'; sidebar.appendChild(title); let ul = document.createElement('ul'); ul.id = 'sidebar-lists'; sidebar.appendChild(ul); for (let i = 0; i < lists.length; i++) { let li = document.createElement('li'); let a = document.createElement('a'); a.className = 'js-nav u-textUserColor'; a.href = lists[i].url; a.innerHTML = lists[i].name; li.appendChild(a); ul.appendChild(li); } document.body.appendChild(sidebar); addSidebarStyle(); } function addSidebarStyle() { if (document.getElementById('lists-sidebar-style')) { return; } let style = document.createElement('style'); style.id = 'lists-sidebar-style'; style.innerHTML = ` #lists-sidebar { position: fixed; left: 20px; top: 30%; padding: 1em 1.5em; line-height: 1.5; z-index: 1000000; } #lists-sidebar h3 { margin-bottom: 0.5em; text-align: center; } #lists-sidebar ul { margin: 0; padding: 0; list-style-type: disc; list-style-position: inside; } `; document.head.appendChild(style); } function updateSidebar() { // Cache only valid for 10 minutes let lastUpdate = cache.get('lastUpdate'); if (lastUpdate && new Date() - new Date(lastUpdate) > 600000) { cache.clear(); removeSidebar(); } // Already exists and do not need to update if (sidebar) { return; } let lists = JSON.parse(cache.get('lists')); if (lists) { createSidebar(lists); } else { retriveLists(username, function(lists) { if (lists && lists.length > 0) { cache.set('lists', JSON.stringify(lists)); cache.set('username', username); cache.set('lastUpdate', new Date().toString()); createSidebar(lists); } }); } } function removeSidebar() { if (sidebar) { sidebar.remove(); sidebar = null; } } // Create or remove sidebar when page changed // Only show sidebar on homepage and lists related pages function pageChanged() { if (location.pathname === '/' || /^\/[^/]+\/lists/.test(location.pathname)) { updateSidebar(); } else { removeSidebar(); } } }());