nowcoder extend class

展开多少届。直观看到用户是多少届的

Verze ze dne 30. 03. 2025. Zobrazit nejnovější verzi.

  1. // ==UserScript==
  2. // @name nowcoder extend class
  3. // @description 展开多少届。直观看到用户是多少届的
  4. // @version 1.1
  5. // @include https://www.nowcoder.com/search/*
  6. // @include https://www.nowcoder.com/feed/main/detail/*
  7. // @namespace baojie.nowcoder
  8. // @license MIT
  9. // @grant GM_xmlhttpRequest
  10. // ==/UserScript==
  11. // prompt archive https://markdownpastebin.com/?id=44bc3d7d7a8345968817f865646c1f91
  12. (function() {
  13. 'use strict';
  14.  
  15. // Cache to store user IDs and their graduation years
  16. const userCache = new Map();
  17. // Set to track user IDs that have been processed
  18. const processedUserIds = new Set();
  19.  
  20. // Add CSS styles for graduation year display
  21. function addStyles() {
  22. // Check if styles already added
  23. if (document.getElementById('nowcoder-grad-year-styles')) {
  24. return;
  25. }
  26. const styleElement = document.createElement('style');
  27. styleElement.id = 'nowcoder-grad-year-styles';
  28. styleElement.textContent = `
  29. .grad-year-display {
  30. color: #ff7830;
  31. margin-left: 5px;
  32. font-size: 12px;
  33. font-weight: bold;
  34. }
  35. `;
  36. document.head.appendChild(styleElement);
  37. }
  38.  
  39. // Extract user ID from element
  40. function getUserIdFromElement(element) {
  41. // For links with href attribute
  42. if (element.tagName === 'A' && element.href) {
  43. const match = element.href.match(/\/users\/(\d+)/);
  44. if (match && match[1]) {
  45. return match[1];
  46. }
  47. }
  48. // For other elements, check parent or child links
  49. const link = element.closest('a[href*="/users/"]') ||
  50. element.querySelector('a[href*="/users/"]');
  51. if (link) {
  52. const match = link.href.match(/\/users\/(\d+)/);
  53. if (match && match[1]) {
  54. return match[1];
  55. }
  56. }
  57. return null;
  58. }
  59.  
  60. // Display the graduation year next to the user name
  61. function displayGradYear(element, gradYear) {
  62. // Find the appropriate container
  63. const nameSpan = element.querySelector('.name-text');
  64. // Check if we already added to this specific element
  65. if (nameSpan.querySelector('.grad-year-display') || element.hasAttribute('data-grad-displayed')) {
  66. return;
  67. }
  68. // Create the graduation year display
  69. const gradYearElement = document.createElement('span');
  70. gradYearElement.className = 'grad-year-display';
  71. gradYearElement.textContent = `(${gradYear})`;
  72. gradYearElement.setAttribute('data-userid', element.getAttribute('data-userid'));
  73. // Add to the page
  74. nameSpan.appendChild(gradYearElement);
  75. // Mark this element as having the graduation year displayed
  76. element.setAttribute('data-grad-displayed', 'true');
  77. }
  78.  
  79. // Fetch user info using GM_xmlhttpRequest
  80. function fetchUserInfo(userId) {
  81. return new Promise((resolve) => {
  82. const url = `https://gw-c.nowcoder.com/api/sparta/user/info/card/${userId}?_=${Date.now()}`;
  83. // Use GM_xmlhttpRequest directly, avoiding the unsafe header issue
  84. GM_xmlhttpRequest({
  85. method: 'GET',
  86. url: url,
  87. headers: {
  88. 'Accept': 'application/json',
  89. 'X-Requested-With': 'XMLHttpRequest'
  90. // Removed Referer header to avoid the 'unsafe header' error
  91. },
  92. onload: function(response) {
  93. try {
  94. const data = JSON.parse(response.responseText);
  95. if (data.success && data.data && data.data.workTime) {
  96. resolve(data.data.workTime);
  97. } else {
  98. resolve(null);
  99. }
  100. } catch (e) {
  101. console.error('Failed to parse response', e);
  102. resolve(null);
  103. }
  104. },
  105. onerror: function() {
  106. resolve(null);
  107. }
  108. });
  109. });
  110. }
  111.  
  112. // Process a single user element
  113. async function processUserElement(element) {
  114. // Skip if already fully processed
  115. if (element.hasAttribute('data-grad-displayed')) {
  116. return;
  117. }
  118. const userId = getUserIdFromElement(element);
  119. if (!userId) {
  120. return;
  121. }
  122. // Store userId as data attribute to help with duplicate detection
  123. element.setAttribute('data-userid', userId);
  124. // Skip if already in progress for this user ID
  125. if (processedUserIds.has(userId)) {
  126. // If we already know this user's info, display it
  127. if (userCache.has(userId)) {
  128. displayGradYear(element, userCache.get(userId));
  129. }
  130. return;
  131. }
  132. // Mark as being processed
  133. processedUserIds.add(userId);
  134. // Check cache first
  135. if (userCache.has(userId)) {
  136. displayGradYear(element, userCache.get(userId));
  137. return;
  138. }
  139. // Fetch user info
  140. const gradYear = await fetchUserInfo(userId);
  141. if (gradYear) {
  142. userCache.set(userId, gradYear);
  143. // Update all instances of this user on the page
  144. document.querySelectorAll(`[data-userid="${userId}"]:not([data-grad-displayed])`).forEach(el => {
  145. displayGradYear(el, gradYear);
  146. });
  147. }
  148. }
  149.  
  150. // Process all user elements visible on the page
  151. function processVisibleElements() {
  152. // Find all user name links
  153. document.querySelectorAll('a[href*="/users/"]').forEach(link => {
  154. if (link.closest('.user-nickname') || link.querySelector('.name-text')) {
  155. processUserElement(link);
  156. }
  157. });
  158. }
  159.  
  160. // Simple debounce function
  161. function debounce(func, wait) {
  162. let timeout;
  163. return function() {
  164. const context = this, args = arguments;
  165. clearTimeout(timeout);
  166. timeout = setTimeout(() => func.apply(context, args), wait);
  167. };
  168. }
  169.  
  170. // Initialize
  171. function init() {
  172. console.log('Nowcoder graduation year display script initialized');
  173. addStyles();
  174. // Initial processing with a short delay
  175. setTimeout(processVisibleElements, 500);
  176. // Process on scroll (debounced)
  177. window.addEventListener('scroll', debounce(processVisibleElements, 300));
  178. // Process on content changes
  179. const observer = new MutationObserver(debounce(mutations => {
  180. let hasNewNodes = false;
  181. mutations.forEach(mutation => {
  182. if (mutation.addedNodes.length > 0) {
  183. hasNewNodes = true;
  184. }
  185. });
  186. if (hasNewNodes) {
  187. processVisibleElements();
  188. }
  189. }, 300));
  190. observer.observe(document.body, { childList: true, subtree: true });
  191. }
  192.  
  193. // Run after page has loaded
  194. if (document.readyState === 'loading') {
  195. document.addEventListener('DOMContentLoaded', init);
  196. } else {
  197. init();
  198. }
  199. })();