Greasy Fork镜像 还支持 简体中文。

MEGA Print Working Directory

Show current location in title when using MEGA cloud storage.

  1. // ==UserScript==
  2. // @name MEGA Print Working Directory
  3. // @namespace net.myitian.js.mega.pwd
  4. // @description Show current location in title when using MEGA cloud storage.
  5. // @source https://github.com/Myitian/MEGA-PWD
  6. // @author Myitian
  7. // @license MIT
  8. // @version 0.3
  9. // @match https://mega.nz/*
  10. // @grant GM_registerMenuCommand
  11. // ==/UserScript==
  12.  
  13. /** @param {string[]} selectors */
  14. function simpleCallback(...selectors) {
  15. const title = document.title.trim();
  16. if (title.length === 0) {
  17. return;
  18. }
  19. const dlkey = document.querySelector(".dlkey-dialog:not(.hidden)");
  20. if (dlkey) {
  21. /** @type {HTMLElement} */
  22. const element = dlkey.querySelector("#dlkey-dialog-title");
  23. mName = element?.innerText.replace("\n", "") ?? "";
  24. } else {
  25. mName = "";
  26. for (const selector of selectors) {
  27. /** @type {HTMLElement} */
  28. const element = document.querySelector(selector);
  29. if (element) {
  30. if (mName) {
  31. mName += "/";
  32. }
  33. mName += element.innerText.trim().replace("\n", "");
  34. }
  35. }
  36. }
  37. if (!mName) {
  38. return;
  39. }
  40. if (baseTitle === null || /^[^\|]+MEGA[^\|]+$/.test(title)) {
  41. baseTitle = title;
  42. }
  43. const newTitle = `${mName} | ${baseTitle}`.trim();
  44. if (title !== newTitle) {
  45. document.title = newTitle;
  46. }
  47. }
  48. function folderCallback() {
  49. const title = document.title.trim();
  50. if (title.length === 0) {
  51. return;
  52. }
  53. const dlkey = document.querySelector(".dlkey-dialog:not(.hidden)");
  54. if (dlkey) {
  55. /** @type {HTMLElement} */
  56. const dlkeyTitle = dlkey.querySelector("#dlkey-dialog-title");
  57. mName = dlkeyTitle?.innerText.replace("\n", "") ?? "";
  58. } else {
  59. mName = "";
  60. /** @type {NodeListOf<HTMLElement>} */
  61. const es = document.querySelectorAll(".fm-right-files-block .breadcrumb-dropdown span");
  62. if (es.length > 0) {
  63. for (const e of es) {
  64. mName = `${e.innerText.trim()}/${mName}`;
  65. }
  66. /** @type {HTMLElement} */
  67. const element = document.querySelector(".fm-right-files-block .fm-breadcrumbs-block a:last-child .simpletip-tc");
  68. mName += element?.innerText;
  69. } else {
  70. /** @type {NodeListOf<HTMLElement>} */
  71. const es2 = document.querySelectorAll(".fm-right-files-block .fm-breadcrumbs-block .simpletip-tc");
  72. for (let i = 0; i < es2.length; i++) {
  73. mName += es2[i].innerText.trim();
  74. if (i < es2.length - 1) {
  75. mName += "/";
  76. }
  77. }
  78. }
  79. }
  80. if (!mName) {
  81. return;
  82. }
  83. if (baseTitle === null || /^[^\|]+MEGA[^\|]+$/.test(title)) {
  84. baseTitle = title;
  85. }
  86. const newTitle = `${mName} | ${baseTitle}`.trim();
  87. if (title !== newTitle) {
  88. document.title = newTitle;
  89. }
  90. }
  91. /** @param {number} t */
  92. function delay(t) {
  93. return new Promise((rs, _) => setTimeout(rs, t));
  94. }
  95.  
  96. /** @typedef {"password"|"file"|"folder"|"fm"|"fm-chat"|"fm-contacts"|"fm-pwm"|"fm-account"|"fm-shares"|"fm-media"|"fm-dashboard"|null} Mode */
  97.  
  98. /** @type {MutationObserverInit} */
  99. const OBSERVER_CONFIG = { attributes: true, childList: true, subtree: true };
  100. GM_registerMenuCommand("PWD", () => prompt("Value", mName));
  101. let forceRefresh = false;
  102. let mName = null;
  103. let baseTitle = null;
  104. /** @type {MutationObserver|null} */
  105. let observer = null;
  106. /** @type {Mode} */
  107. let prevMode = null;
  108.  
  109. window.addEventListener("hashchange", e => main(new URL(e.newURL)));
  110. const originalPushState = history.pushState;
  111. history.pushState = (data, unused, url) => {
  112. main(new URL(url, window.location.href));
  113. return originalPushState.call(history, data, unused, url);
  114. }
  115. main(window.location);
  116.  
  117. /** @param {URL|Location} url */
  118. async function main(url) {
  119. /** @type {Mode} */
  120. let mode = null;
  121. if (url.hash.startsWith("#P!")) {
  122. mode = "password";
  123. } else if (url.pathname.startsWith("/file/")) {
  124. mode = "file";
  125. } else if (url.pathname.startsWith("/folder/")
  126. || url.pathname.match(/^\/fm(?:\/(?:[0-9A-Za-z]{8})?)?$/)) {
  127. mode = "folder";
  128. } else if (url.pathname.startsWith("/fm/chat/contacts")) {
  129. mode = "fm-contacts";
  130. } else if (url.pathname.startsWith("/fm/chat")) {
  131. mode = "fm-chat";
  132. } else if (url.pathname.startsWith("/fm/pwm")) {
  133. mode = "fm-pwm";
  134. } else if (url.pathname.startsWith("/fm/account")) {
  135. mode = "fm-account";
  136. } else if (url.pathname.startsWith("/fm/shares")
  137. || url.pathname.startsWith("/fm/out-shares")
  138. || url.pathname.startsWith("/fm/public-links")
  139. || url.pathname.startsWith("/fm/file-requests")) {
  140. mode = "fm-shares";
  141. } else if (url.pathname.startsWith("/fm/photos")
  142. || url.pathname.startsWith("/fm/albums")) {
  143. mode = "fm-media";
  144. } else if (url.pathname.startsWith("/fm/dashboard")) {
  145. mode = "fm-dashboard";
  146. } else if (url.pathname.startsWith("/fm/")) {
  147. mode = "fm";
  148. }
  149. if (mode !== prevMode || forceRefresh) {
  150. forceRefresh = false;
  151. observer?.disconnect();
  152. console.debug("[MEGA-PWD]", "SwitchMode:", prevMode, "->", mode);
  153. prevMode = mode;
  154. if (mode !== null) {
  155. /** @type {{callback:()=>void,selector:string}|null} */
  156. let config = null;
  157. switch (mode) {
  158. case "password":
  159. config = {
  160. selector: "#password-dialog-title",
  161. callback: () => simpleCallback("#password-dialog-title")
  162. };
  163. break;
  164. case "file":
  165. config = {
  166. selector: ".title-block",
  167. callback: () => simpleCallback(".title-block")
  168. };
  169. break;
  170. case "folder":
  171. config = {
  172. selector: ".fm-right-files-block .fm-breadcrumbs-wrapper",
  173. callback: folderCallback
  174. };
  175. break;
  176. case "fm-contacts":
  177. config = {
  178. selector: ".contacts-navigation",
  179. callback: () => simpleCallback(".contacts-navigation .active")
  180. };
  181. break;
  182. case "fm-chat":
  183. config = {
  184. selector: ".lhp-nav",
  185. callback: () => simpleCallback(".lhp-nav-container.active")
  186. };
  187. break;
  188. case "fm-pwm":
  189. config = {
  190. selector: ".top-nav .primary-text",
  191. callback: () => simpleCallback(".top-nav .primary-text")
  192. };
  193. break;
  194. case "fm-account":
  195. config = {
  196. selector: ".account .lp-content-wrap",
  197. callback: () => simpleCallback(".account .lp-header", ".account .settings-button.active .head-title")
  198. };
  199. break;
  200. case "fm-shares":
  201. config = {
  202. selector: ".shares-tabs-bl",
  203. callback: () => simpleCallback(".mega-component.active .primary-text", ".shares-tab-lnk.active")
  204. };
  205. break;
  206. case "fm-media":
  207. config = {
  208. selector: "#media-tabs",
  209. callback: () => simpleCallback(".mega-component.active .primary-text", "#media-tabs .active")
  210. };
  211. break;
  212. case "fm-dashboard":
  213. config = {
  214. selector: ".to-my-profile .primary-text",
  215. callback: () => simpleCallback(".to-my-profile .primary-text")
  216. };
  217. break;
  218. case "fm":
  219. config = {
  220. selector: ".menu.ps",
  221. callback: () => simpleCallback(".mega-component.active .primary-text")
  222. };
  223. break;
  224. }
  225. let element = null;
  226. observer = new MutationObserver(config.callback);
  227. if (mode === "file" || mode === "folder") {
  228. while (!(element = document.querySelector(".dlkey-dialog"))) {
  229. await delay(50);
  230. }
  231. config.callback();
  232. observer.observe(element, OBSERVER_CONFIG);
  233. if (mode === "file") {
  234. const largeChangeObserver = new MutationObserver(records => {
  235. if (records.find(
  236. it => Array.from(it.removedNodes).find(
  237. iit => iit instanceof Element && iit.classList.contains("bottom-page")))) {
  238. largeChangeObserver.disconnect();
  239. forceRefresh = true;
  240. }
  241. });
  242. largeChangeObserver.observe(document.querySelector("#startholder"), OBSERVER_CONFIG);
  243. }
  244. }
  245. while (!(element = document.querySelector(config.selector))) {
  246. await delay(50);
  247. }
  248. config.callback();
  249. observer.observe(document.querySelector("title"), OBSERVER_CONFIG);
  250. observer.observe(element, OBSERVER_CONFIG);
  251. }
  252. }
  253. }

QingJ © 2025

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