GreasyFork: User Control Panel Button

To add User Control Panel Button into navigation bar

目前為 2023-09-24 提交的版本,檢視 最新版本

  1. // ==UserScript==
  2. // @name GreasyFork: User Control Panel Button
  3. // @namespace UserScripts
  4. // @match https://greasyfork.org/*
  5. // @grant none
  6. // @version 0.2.0
  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. }).catch((e)=>{
  139. console.debug(e);
  140. });
  141.  
  142. resolve();
  143.  
  144. }
  145.  
  146.  
  147.  
  148. }).catch(e => {
  149.  
  150. sessionStorage.removeItem(stKey);
  151. });
  152.  
  153.  
  154.  
  155. });
  156.  
  157. if (r && typeof r === 'string' && r.length > 32) return r;
  158.  
  159. }
  160.  
  161.  
  162.  
  163. }
  164.  
  165. if (!document.querySelector('.sign-out-link') || document.querySelector('.sign-in-link')) return;
  166.  
  167. let plink = document.querySelector('.user-profile-link');
  168. if (!plink) return;
  169.  
  170. let href = plink.querySelector('a[href*="/users/"]').href;
  171.  
  172. let mi = href.indexOf('/users/');
  173. if (mi < 0) return;
  174.  
  175. if (href.includes('/users/sign')) return;
  176.  
  177.  
  178. let presetup = preSetup();
  179.  
  180. if (!presetup) return;
  181.  
  182. const { pos } = presetup;
  183.  
  184.  
  185. let dm = await digestMessage(href);
  186.  
  187. const stKey = `gf_control_panel_${dm}`
  188.  
  189.  
  190. for (let trialN = 8; trialN--;) {
  191. let s = sessionStorage.getItem(stKey);
  192. let d = typeof s === 'string' ? parseInt(s) : 0;
  193. if (d > 9 && Date.now() - d < 8000) await new Promise(r => setTimeout(r, 320));
  194. else break;
  195. }
  196.  
  197.  
  198. const cpHTML = await fetchCPHTML({ stKey, href });
  199.  
  200. if (!cpHTML || typeof cpHTML !== 'string') return;
  201.  
  202. let cpm = document.createElement('template');
  203.  
  204. cpm.innerHTML = cpHTML;
  205.  
  206. const kc = setup({ cpmRoot: cpm.content, pos });
  207. if (kc) {
  208.  
  209. function headerClickHanlder(evt){
  210. const target = (evt||0).target||0;
  211. if(!target)return;
  212. const section = target.closest('section#control-panel');
  213. if(!section)return;
  214. if(section.getAttribute('gf-user-cp-section')==='show'){
  215. section.setAttribute('gf-user-cp-section', 'greyout');
  216. } else{
  217. section.setAttribute('gf-user-cp-section', 'show');
  218. }
  219.  
  220. }
  221.  
  222. pos.parentNode.insertBefore(kc, pos.nextSibling);
  223. let headerTitleCP = document.querySelector('section#control-panel #Control-panel');
  224. if(headerTitleCP){
  225. const sectionCP = headerTitleCP.closest('section#control-panel');
  226. const ul = sectionCP.querySelector('ul#user-control-panel');
  227. if(ul){
  228. sectionCP.setAttribute('gf-user-cp-section', 'greyout');
  229. document.head.appendChild(document.createElement('style')).textContent=`
  230. [gf-user-cp-section="greyout"] ul {
  231. display: none;
  232. }
  233. [gf-user-cp-section="greyout"] #Control-panel {
  234. cursor: pointer;
  235. opacity: 0.2;
  236. }
  237. [gf-user-cp-section="greyout"] #Control-panel:hover {
  238. opacity: 0.6;
  239. }
  240. [gf-user-cp-section="show"] #Control-panel {
  241. cursor: pointer;
  242. }
  243. `;
  244. headerTitleCP.addEventListener('click', headerClickHanlder, true);
  245. }
  246.  
  247. }
  248. }
  249.  
  250.  
  251.  
  252. })();