GreasyFork: User Control Panel Button

To add User Control Panel Button into navigation bar

  1. // ==UserScript==
  2. // @name GreasyFork: User Control Panel Button
  3. // @namespace UserScripts
  4. // @match https://gf.qytechs.cn/*
  5. // @grant none
  6. // @version 0.2.3
  7. // @license MIT
  8. // @author CY Fung
  9. // @description To add User Control Panel Button into navigation bar
  10. // ==/UserScript==
  11.  
  12. (async () => {
  13.  
  14. const USE_IN_PAGE_CONTROL_PANEL = true;
  15.  
  16. function preSetup() {
  17. // for styling
  18.  
  19. let pos = document.querySelectorAll('#site-nav>nav>li.with-submenu');
  20. pos = pos.length >= 1 ? pos[pos.length - 1] : null;
  21.  
  22. if (!pos) return;
  23.  
  24. pos.parentNode.style.minHeight = '2.8rem';
  25.  
  26.  
  27.  
  28.  
  29. return { pos };
  30.  
  31. }
  32.  
  33. function setup(m) {
  34.  
  35. const { cpmRoot } = m;
  36.  
  37.  
  38. let h = (cpmRoot.querySelector('h3') || cpmRoot.querySelector('header'));
  39. if (!h) return;
  40.  
  41. let nav = document.createElement('nav');
  42.  
  43. for (const anchor of cpmRoot.querySelectorAll('li a[href]')) {
  44. let li = nav.appendChild(document.createElement('li'));
  45. li.appendChild(anchor);
  46. }
  47.  
  48.  
  49. let tm = document.createElement('template');
  50. tm.innerHTML = `
  51. <li class="with-submenu" style="display: block;">
  52. <a href="#" onclick="return false">${h.textContent}</a>
  53. <nav style="min-width: initial;">
  54. ${nav.innerHTML}
  55. </nav>
  56. </li>
  57. `.trim();
  58.  
  59. return tm.content;
  60.  
  61.  
  62. }
  63.  
  64.  
  65. function bufferToHex(buffer) {
  66. const byteArray = new Uint8Array(buffer);
  67. const len = byteArray.length;
  68. const hexCodes = new Array(len * 2);
  69. const chars = '0123456789abcdef';
  70. for (let i = 0, j = 0; i < len; i++) {
  71. const byte = byteArray[i];
  72. hexCodes[j++] = chars[byte >> 4];
  73. hexCodes[j++] = chars[byte & 0x0F];
  74. }
  75. return hexCodes.join('');
  76. }
  77.  
  78. async function digestMessage(message) {
  79. const encoder = new TextEncoder("utf-8");
  80. const msgUint8 = encoder.encode(message);
  81. const hashBuffer = await crypto.subtle.digest('SHA-1', msgUint8);
  82. return bufferToHex(hashBuffer);
  83. }
  84.  
  85. async function fetchCPHTML(o) {
  86.  
  87. const { stKey, href } = o;
  88.  
  89. if (USE_IN_PAGE_CONTROL_PANEL) {
  90. const fetchHrefT = `${href}`.replace(/(\/users\/\d+)\-([^\/\s]+)/, '$1').replace(/\?[^\?\s]*$/, '');
  91. const locationHrefT = `${location.href}`.replace(/(\/users\/\d+)\-([^\/\s]+)/, '$1').replace(/\?[^\?\s]*$/, '');
  92. if (fetchHrefT === locationHrefT) {
  93. const cp = document.querySelector('#control-panel');
  94. if (cp && `${cp.textContent}`.trim().length > 0) {
  95. const html = cp.innerHTML.trim();
  96. sessionStorage.setItem(stKey, html);
  97. return html;
  98. }
  99. }
  100. }
  101.  
  102. const isSameOrigin = `${location.protocol}`.startsWith('http') && `${location.hostname}`.length > 4 && `${href}`.startsWith(`${location.protocol}//${location.hostname}/`);
  103. if (!isSameOrigin) return null;
  104.  
  105. for (let trialN = 4; trialN--;) {
  106.  
  107.  
  108. let r = await new Promise(resolve => {
  109.  
  110.  
  111. let t = sessionStorage.getItem(stKey)
  112.  
  113. if (`${t}`.length > 32) resolve(t);
  114.  
  115.  
  116. sessionStorage.setItem(stKey, `${Date.now()}`);
  117.  
  118.  
  119. fetch(href, {
  120. method: "GET",
  121. mode: "same-origin",
  122. cache: "force-cache",
  123. credentials: "same-origin",
  124. redirect: "follow",
  125. referrerPolicy: "no-referrer",
  126.  
  127.  
  128. }).then(res => res.text()).then(async res => {
  129. sessionStorage.removeItem(stKey);
  130.  
  131. const tag = 'section';
  132. let idx1 = res.indexOf(`<${tag} id="control-panel">`);
  133. let idx2 = idx1 >= 0 ? res.indexOf(`</${tag}>`, idx1) : -1;
  134. if (idx1 >= 0 && idx2 > idx1) {
  135. res = res.substring(idx1, idx2 + `</${tag}>`.length);
  136. }
  137.  
  138. let template = document.createElement('template');
  139. template.innerHTML = res;
  140. let w = template.content;
  141.  
  142. let cp = w.querySelector('#control-panel');
  143.  
  144. if (cp) {
  145.  
  146. const html = cp.innerHTML.trim();
  147.  
  148. sessionStorage.setItem(stKey, html);
  149.  
  150. resolve(html);
  151. } else {
  152.  
  153.  
  154. await fetch(href, {
  155. method: "GET",
  156. mode: "same-origin",
  157. cache: "reload",
  158. credentials: "same-origin",
  159. redirect: "follow",
  160. referrerPolicy: "no-referrer",
  161.  
  162. }).catch((e)=>{
  163. console.debug(e);
  164. });
  165.  
  166. resolve();
  167.  
  168. }
  169.  
  170.  
  171.  
  172. }).catch(e => {
  173.  
  174. sessionStorage.removeItem(stKey);
  175. console.warn(e);
  176. });
  177.  
  178.  
  179.  
  180. });
  181.  
  182. if (r && typeof r === 'string' && r.length > 32) return r;
  183.  
  184. await new Promise(r => setTimeout(r, 100));
  185.  
  186. }
  187.  
  188.  
  189.  
  190. }
  191.  
  192. if (!document.querySelector('.sign-out-link') || document.querySelector('.sign-in-link')) return;
  193.  
  194. let plink = document.querySelector('.user-profile-link');
  195. if (!plink) return;
  196.  
  197. let href = plink.querySelector('a[href*="/users/"]').href;
  198.  
  199. let mi = href.indexOf('/users/');
  200. if (mi < 0) return;
  201.  
  202. if (href.includes('/users/sign')) return;
  203.  
  204.  
  205. let presetup = preSetup();
  206.  
  207. if (!presetup) return;
  208.  
  209. const { pos } = presetup;
  210.  
  211.  
  212. let dm = await digestMessage(href);
  213.  
  214. const stKey = `gf_control_panel_${dm}`
  215.  
  216.  
  217. for (let trialN = 8; trialN--;) {
  218. let s = sessionStorage.getItem(stKey);
  219. let d = typeof s === 'string' ? parseInt(s) : 0;
  220. if (d > 9 && Date.now() - d < 8000) await new Promise(r => setTimeout(r, 320));
  221. else break;
  222. }
  223.  
  224.  
  225. const cpHTML = await fetchCPHTML({ stKey, href });
  226.  
  227. if (!cpHTML || typeof cpHTML !== 'string') return;
  228.  
  229. let cpm = document.createElement('template');
  230.  
  231. cpm.innerHTML = cpHTML;
  232.  
  233. const kc = setup({ cpmRoot: cpm.content, pos });
  234. if (kc) {
  235.  
  236. function headerClickHanlder(evt){
  237. const target = (evt||0).target||0;
  238. if(!target)return;
  239. const section = target.closest('section#control-panel');
  240. if(!section)return;
  241. if(section.getAttribute('gf-user-cp-section')==='show'){
  242. section.setAttribute('gf-user-cp-section', 'greyout');
  243. } else{
  244. section.setAttribute('gf-user-cp-section', 'show');
  245. }
  246.  
  247. }
  248.  
  249. pos.parentNode.insertBefore(kc, pos.nextSibling);
  250. let headerTitleCP = document.querySelector('section#control-panel #Control-panel');
  251. if(headerTitleCP){
  252. const sectionCP = headerTitleCP.closest('section#control-panel');
  253. const ul = sectionCP.querySelector('ul#user-control-panel');
  254. if(ul){
  255. sectionCP.setAttribute('gf-user-cp-section', 'greyout');
  256. document.head.appendChild(document.createElement('style')).textContent=`
  257. [gf-user-cp-section="greyout"] ul {
  258. display: none;
  259. }
  260. [gf-user-cp-section="greyout"] #Control-panel {
  261. cursor: pointer;
  262. opacity: 0.2;
  263. }
  264. [gf-user-cp-section="greyout"] #Control-panel:hover {
  265. opacity: 0.6;
  266. }
  267. [gf-user-cp-section="show"] #Control-panel {
  268. cursor: pointer;
  269. }
  270. `;
  271. headerTitleCP.addEventListener('click', headerClickHanlder, true);
  272. }
  273.  
  274. }
  275. }
  276.  
  277.  
  278.  
  279. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址