GreasyFork: User Control Panel Button

To add User Control Panel Button into navigation bar

当前为 2023-09-23 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name GreasyFork: User Control Panel Button
  3. // @namespace UserScripts
  4. // @match https://greasyfork.org/*
  5. // @grant none
  6. // @version 0.1.8
  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. function preSetup() {
  15. // for styling
  16.  
  17. let pos = document.querySelectorAll('#site-nav>nav>li.with-submenu');
  18. pos = pos.length >= 1 ? pos[pos.length - 1] : null;
  19.  
  20. if (!pos) return;
  21.  
  22. pos.parentNode.style.minHeight = '2.8rem';
  23.  
  24.  
  25.  
  26.  
  27. return { pos };
  28.  
  29. }
  30.  
  31. function setup(m) {
  32.  
  33. const { cpmRoot } = m;
  34.  
  35.  
  36. let h = (cpmRoot.querySelector('h3') || cpmRoot.querySelector('header'));
  37. if (!h) return;
  38.  
  39. let nav = document.createElement('nav');
  40.  
  41. for (const anchor of cpmRoot.querySelectorAll('li a[href]')) {
  42. let li = nav.appendChild(document.createElement('li'));
  43. li.appendChild(anchor);
  44. }
  45.  
  46.  
  47. let tm = document.createElement('template');
  48. tm.innerHTML = `
  49. <li class="with-submenu" style="display: block;">
  50. <a href="#" onclick="return false">${h.textContent}</a>
  51. <nav style="min-width: initial;">
  52. ${nav.innerHTML}
  53. </nav>
  54. </li>
  55. `.trim();
  56.  
  57. return tm.content;
  58.  
  59.  
  60. }
  61.  
  62.  
  63. function bufferToHex(buffer) {
  64. const byteArray = new Uint8Array(buffer);
  65. const len = byteArray.length;
  66. const hexCodes = new Array(len * 2);
  67. const chars = '0123456789abcdef';
  68. for (let i = 0, j = 0; i < len; i++) {
  69. const byte = byteArray[i];
  70. hexCodes[j++] = chars[byte >> 4];
  71. hexCodes[j++] = chars[byte & 0x0F];
  72. }
  73. return hexCodes.join('');
  74. }
  75.  
  76. async function digestMessage(message) {
  77. const encoder = new TextEncoder("utf-8");
  78. const msgUint8 = encoder.encode(message);
  79. const hashBuffer = await crypto.subtle.digest('SHA-1', msgUint8);
  80. return bufferToHex(hashBuffer);
  81. }
  82.  
  83. async function fetchCPHTML(o) {
  84.  
  85. const { stKey, href } = o;
  86.  
  87. for (let trialN = 4; trialN--;) {
  88.  
  89.  
  90. let r = await new Promise(resolve => {
  91.  
  92.  
  93. let t = sessionStorage.getItem(stKey)
  94.  
  95. if (`${t}`.length > 32) resolve(t);
  96.  
  97.  
  98. sessionStorage.setItem(stKey, `${Date.now()}`);
  99.  
  100.  
  101. fetch(href, {
  102. method: "GET",
  103. mode: "same-origin",
  104. cache: "force-cache",
  105. credentials: "same-origin",
  106. redirect: "follow",
  107. referrerPolicy: "no-referrer",
  108.  
  109.  
  110. }).then(res => res.text()).then(async res => {
  111. sessionStorage.removeItem(stKey);
  112.  
  113.  
  114. let template = document.createElement('template');
  115. template.innerHTML = res;
  116. let w = template.content;
  117.  
  118. let cp = w.querySelector('#control-panel');
  119.  
  120. if (cp) {
  121.  
  122. const html = cp.innerHTML.trim();
  123.  
  124. sessionStorage.setItem(stKey, html);
  125.  
  126. resolve(html);
  127. } else {
  128.  
  129.  
  130. await fetch(href, {
  131. method: "GET",
  132. mode: "same-origin",
  133. cache: "reload",
  134. credentials: "same-origin",
  135. redirect: "follow",
  136. referrerPolicy: "no-referrer",
  137.  
  138. });
  139.  
  140. resolve();
  141.  
  142. }
  143.  
  144.  
  145.  
  146. }).catch(e => {
  147.  
  148. sessionStorage.removeItem(stKey);
  149. });
  150.  
  151.  
  152.  
  153. });
  154.  
  155. if (r && typeof r === 'string' && r.length > 32) return r;
  156.  
  157. }
  158.  
  159.  
  160.  
  161. }
  162.  
  163. if (!document.querySelector('.sign-out-link') || document.querySelector('.sign-in-link')) return;
  164.  
  165. let plink = document.querySelector('.user-profile-link');
  166. if (!plink) return;
  167.  
  168. let href = plink.querySelector('a[href*="/users/"]').href;
  169.  
  170. let mi = href.indexOf('/users/');
  171. if (mi < 0) return;
  172.  
  173. if (href.includes('/users/sign')) return;
  174.  
  175.  
  176. let presetup = preSetup();
  177.  
  178. if (!presetup) return;
  179.  
  180. const { pos } = presetup;
  181.  
  182.  
  183. let dm = await digestMessage(href);
  184.  
  185. const stKey = `gf_control_panel_${dm}`
  186.  
  187.  
  188. for (let trialN = 8; trialN--;) {
  189. let s = sessionStorage.getItem(stKey);
  190. let d = typeof s === 'string' ? parseInt(s) : 0;
  191. if (d > 9 && Date.now() - d < 8000) await new Promise(r => setTimeout(r, 320));
  192. else break;
  193. }
  194.  
  195.  
  196. const cpHTML = await fetchCPHTML({ stKey, href });
  197.  
  198. if (!cpHTML || typeof cpHTML !== 'string') return;
  199.  
  200. let cpm = document.createElement('template');
  201.  
  202. cpm.innerHTML = cpHTML;
  203.  
  204. const kc = setup({ cpmRoot: cpm.content, pos });
  205. if (kc) {
  206.  
  207. pos.parentNode.insertBefore(kc, pos.nextSibling);
  208. }
  209.  
  210.  
  211.  
  212. })();