dmhy tree view

convert plain file list into a tree view for 动漫花园 (share.dmhy.org)

  1. // ==UserScript==
  2. // @name dmhy tree view
  3. // @namespace https://gf.qytechs.cn/zh-CN/scripts/26430-dmhy-tree-view
  4. // @license GPL version 3
  5. // @encoding utf-8
  6. // @version 0.35
  7. // @date 2017/01/11
  8. // @modified 2020/12/20
  9. // @description convert plain file list into a tree view for 动漫花园 (share.dmhy.org)
  10. // @author TautCony
  11. // @require http://cdn.bootcss.com/jquery/3.5.1/jquery.min.js
  12. // @require http://cdn.bootcss.com/jstree/3.3.11/jstree.min.js
  13. // @resource customCSS http://cdn.bootcss.com/jstree/3.3.11/themes/default/style.min.css
  14. // @match *://share.dmhy.org/topics/view/*
  15. // @match *://dmhy.anoneko.com/topics/view/*
  16. // @grant GM_addStyle
  17. // @grant GM_getResourceText
  18. // @run-at document-end
  19. // ==/UserScript==
  20. var iconOrigin = location.origin;
  21. var icons = {
  22. audio: iconOrigin + "/images/icon/mp3.gif",
  23. bmp: iconOrigin + "/images/icon/bmp.gif",
  24. image: iconOrigin + "/images/icon/jpg.gif",
  25. png: iconOrigin + "/images/icon/png.gif",
  26. rar: iconOrigin + "/images/icon/rar.gif",
  27. text: iconOrigin + "/images/icon/txt.gif",
  28. unknown: iconOrigin + "/images/icon/unknown.gif",
  29. video: iconOrigin + "/images/icon/mp4.gif",
  30. };
  31. var type2Icon = {
  32. audio: ["flac", "aac", "wav", "mp3"],
  33. bmp: ["bmp"],
  34. image: ["jpg", "jpeg", "webp"],
  35. png: ["png"],
  36. rar: ["rar", "zip", "7z"],
  37. text: ["txt", "log", "cue", "ass"],
  38. video: ["mkv", "mka", "mp4"],
  39. };
  40. var Dictionary = /** @class */ (function () {
  41. function Dictionary() {
  42. this.data = {};
  43. }
  44. Dictionary.prototype.add = function (key, value) {
  45. if (!(key in this.data)) {
  46. this.data[key] = value;
  47. }
  48. };
  49. Dictionary.prototype.clear = function () {
  50. this.data = {};
  51. };
  52. Dictionary.prototype.containsKey = function (key) {
  53. return key in this.data;
  54. };
  55. Dictionary.prototype.get = function (key) {
  56. return this.data[key];
  57. };
  58. Dictionary.prototype.size = function () {
  59. return Object.keys(this.data).length;
  60. };
  61. Dictionary.prototype.values = function () {
  62. return this.data;
  63. };
  64. return Dictionary;
  65. }());
  66. var FileSize = /** @class */ (function () {
  67. function FileSize() {
  68. }
  69. FileSize.toLength = function (size) {
  70. if (size === undefined) {
  71. return -1;
  72. }
  73. var head = "";
  74. var tail = "";
  75. var isNumber = function (c) { return (c >= "0" && c <= "9") || c === "." || c === "-"; };
  76. for (var _i = 0, _a = size.toLowerCase(); _i < _a.length; _i++) {
  77. var c = _a[_i];
  78. if (isNumber(c)) {
  79. head += c;
  80. }
  81. else {
  82. tail += c;
  83. }
  84. }
  85. var value = parseFloat(head);
  86. switch (tail) {
  87. case "byte": return value * Math.pow(2, 0);
  88. case "bytes": return value * Math.pow(2, 0);
  89. case "kb": return value * Math.pow(2, 10);
  90. case "mb": return value * Math.pow(2, 20);
  91. case "gb": return value * Math.pow(2, 30);
  92. case "tb": return value * Math.pow(2, 40);
  93. }
  94. return -1;
  95. };
  96. FileSize.toSize = function (length) {
  97. if (length >= Math.pow(2, 40)) {
  98. return this.format(length, 40, "TiB");
  99. }
  100. else if (length >= Math.pow(2, 30)) {
  101. return this.format(length, 30, "GiB");
  102. }
  103. else if (length >= Math.pow(2, 20)) {
  104. return this.format(length, 20, "MiB");
  105. }
  106. else if (length >= Math.pow(2, 10)) {
  107. return this.format(length, 10, "KiB");
  108. }
  109. else {
  110. return this.format(length, 0, "Bytes", 0);
  111. }
  112. };
  113. FileSize.format = function (length, factor, tail, digits) {
  114. if (digits === undefined) {
  115. digits = 3;
  116. }
  117. return (length / Math.pow(2, factor)).toFixed(digits).toString() + tail;
  118. };
  119. return FileSize;
  120. }());
  121. var TreeNode = /** @class */ (function () {
  122. function TreeNode(node) {
  123. this._ext = undefined;
  124. this._icon = undefined;
  125. this.name = node;
  126. this.length = 0;
  127. this.childNode = new Dictionary();
  128. }
  129. TreeNode.prototype.insert = function (path, size) {
  130. var currentNode = this;
  131. for (var _i = 0, path_1 = path; _i < path_1.length; _i++) {
  132. var node = path_1[_i];
  133. var next = currentNode.childNode.get(node);
  134. if (!currentNode.childNode.containsKey(node)) {
  135. next = currentNode.add(node, new TreeNode(node));
  136. next.pareneNode = currentNode;
  137. }
  138. currentNode = next;
  139. }
  140. currentNode.length = FileSize.toLength(size);
  141. return currentNode;
  142. };
  143. TreeNode.prototype.toString = function () {
  144. return "<span class=\"filename\">" + this.name + "</span><span class=\"filesize\">" + FileSize.toSize(this.length) + "</span>";
  145. };
  146. TreeNode.prototype.toObject = function () {
  147. var ret = {
  148. children: [],
  149. length: 0,
  150. state: {
  151. opened: true,
  152. },
  153. text: this.toString(),
  154. };
  155. var childNodeValues = this.childNode.values();
  156. for (var key in childNodeValues) {
  157. if (!childNodeValues.hasOwnProperty(key)) {
  158. continue;
  159. }
  160. var files = [];
  161. var value = this.childNode.get(key);
  162. if (value.childNode.size() === 0) {
  163. files.push(value);
  164. }
  165. else {
  166. var inner = value.toObject();
  167. value.length = inner.length = inner.children.reduce(function (aac, val) { return aac + val.length; }, 0);
  168. inner.text = value.toString(); //update text with size info
  169. inner.state.opened = false;
  170. ret.children.push(inner);
  171. }
  172. for (var _i = 0, files_1 = files; _i < files_1.length; _i++) {
  173. var file = files_1[_i];
  174. ret.length += file.length;
  175. ret.children.push({
  176. icon: file.icon,
  177. length: file.length,
  178. text: file.toString(),
  179. });
  180. }
  181. }
  182. return ret;
  183. };
  184. TreeNode.prototype.add = function (key, value) {
  185. this.childNode.add(key, value);
  186. return this.childNode.get(key);
  187. };
  188. Object.defineProperty(TreeNode.prototype, "ext", {
  189. get: function () {
  190. if (this._ext !== undefined) {
  191. return this._ext;
  192. }
  193. this._ext = "";
  194. var dotIndex = this.name.lastIndexOf(".");
  195. if (dotIndex > 0) {
  196. this._ext = this.name.substr(dotIndex + 1).toLowerCase();
  197. }
  198. return this._ext;
  199. },
  200. enumerable: false,
  201. configurable: true
  202. });
  203. Object.defineProperty(TreeNode.prototype, "icon", {
  204. get: function () {
  205. if (this._icon !== undefined) {
  206. return this._icon;
  207. }
  208. this._icon = icons.unknown;
  209. for (var type in type2Icon) {
  210. if (type2Icon[type].indexOf(this.ext) >= 0) {
  211. this._icon = icons[type];
  212. break;
  213. }
  214. }
  215. return this._icon;
  216. },
  217. enumerable: false,
  218. configurable: true
  219. });
  220. return TreeNode;
  221. }());
  222. var GM_getResourceText = GM_getResourceText;
  223. var GM_addStyle = GM_addStyle;
  224. var copyToClipboard = function (text) {
  225. if (document.queryCommandSupported && document.queryCommandSupported("copy")) {
  226. var textarea = document.createElement("textarea");
  227. textarea.textContent = text;
  228. textarea.style.position = "fixed"; // Prevent scrolling to bottom of page in MS Edge.
  229. document.body.appendChild(textarea);
  230. textarea.select();
  231. try {
  232. return document.execCommand("copy"); // Security exception may be thrown by some browsers.
  233. }
  234. catch (ex) {
  235. console.warn("Copy to clipboard failed.", ex);
  236. return false;
  237. }
  238. finally {
  239. document.body.removeChild(textarea);
  240. }
  241. }
  242. };
  243. var setupCSS = function () {
  244. if (typeof GM_getResourceText !== "undefined") {
  245. GM_addStyle(GM_getResourceText("customCSS"));
  246. $("head").append("<style>.jstree-node,.jstree-default .jstree-icon{background-image:url(http://cdn.bootcss.com/jstree/3.3.3/themes/default/32px.png);}.filesize{padding-left:1em;color:grey;}</style>");
  247. $(".file_list").css("width", "100%");
  248. $(".file_list").css("max-height", "600px");
  249. }
  250. else {
  251. console.info("%cTo load style sheet and let script works correctly, http://tampermonkey.net/ is required.", "color:#e55d67;font-size:1.3em");
  252. }
  253. };
  254. var setupOpe = function () {
  255. $("#tabs-1").append('<input type="text" style="width:240px;margin:0;padding:6px 12px;border-radius:4px;border:1px solid silver;font-size:1.1em;" id="search_input" placeholder="Search" />');
  256. $("#tabs-1").append('<button id="switch" style="border:0;border-radius:2px;padding:8px;margin-left:10px;">Expand All</button>');
  257. $("#tabs-1").append('<input id="hidden_text" style="display:none;"/>');
  258. };
  259. (function () {
  260. setupCSS();
  261. setupOpe();
  262. var data = new TreeNode($(".topic-title > h3").text());
  263. var pattern = /^(.+?) (\d+(?:\.\d+)?[TGMK]?B(?:ytes)?)$/;
  264. $(".file_list:first > ul li").each(function (index, value) {
  265. var text = $(value).text().trim();
  266. var line = text.replace(/\t+/i, "\t").split("\t");
  267. switch (line.length) {
  268. case 2:
  269. var nodes = line[0].split("/");
  270. var size = line[1];
  271. data.insert(nodes, size);
  272. break;
  273. case 1:
  274. var ret = pattern.exec(text);
  275. if (ret === null) {
  276. //the text should be "More Than 1000 Files"
  277. data.insert(line[0].split("/"), "");
  278. }
  279. else {
  280. data.insert(ret[1].split("/"), ret[2]);
  281. }
  282. break;
  283. default:
  284. console.log("Unexpected length in \"" + line + "\"");
  285. }
  286. });
  287. var getSelectedRow = function (reference) { return $.jstree.reference(reference).get_node(reference, true); };
  288. var options = {
  289. contextmenu: {
  290. items: {
  291. getText: {
  292. action: function (selected) { return copyToClipboard(selected.reference.find(".filename").text()); },
  293. label: "Copy",
  294. },
  295. remove: {
  296. action: function (selected) { return getSelectedRow(selected.reference).remove(); },
  297. label: "Delete",
  298. },
  299. },
  300. show_at_node: false,
  301. },
  302. core: {
  303. data: data.toObject(),
  304. },
  305. plugins: ["search", "wholerow", "contextmenu"],
  306. };
  307. $($(".file_list:first").jstree(options)).bind("loaded.jstree", function (loadedEventData) {
  308. var isExpended = false;
  309. $("#switch").click(function (clickEventData) {
  310. if (isExpended) {
  311. clickEventData.target.innerHTML = "Expand All";
  312. $(loadedEventData.target).jstree("close_all");
  313. }
  314. else {
  315. clickEventData.target.innerHTML = "Toggle All";
  316. $(loadedEventData.target).jstree("open_all");
  317. }
  318. isExpended = !isExpended;
  319. });
  320. var lastVal = "";
  321. $("#search_input").keyup(function (keyupEventData) {
  322. var val = keyupEventData.target.value;
  323. if (val !== lastVal) {
  324. $(loadedEventData.target).jstree(true).search(val);
  325. lastVal = val;
  326. }
  327. });
  328. });
  329. })();

QingJ © 2025

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