// ==UserScript== // @name dmhy tree view // @namespace https://greasyfork.org/zh-CN/scripts/26430-dmhy-tree-view // @license GPL version 3 // @encoding utf-8 // @version 0.32 // @date 2017/01/11 // @modified 2017/05/31 // @description convert plain file list into a tree view for 动漫花园 (share.dmhy.org) // @author TautCony // @require http://cdn.bootcss.com/jquery/3.2.1/jquery.min.js // @require http://cdn.bootcss.com/jstree/3.3.4/jstree.min.js // @resource customCSS http://cdn.bootcss.com/jstree/3.3.3/themes/default/style.min.css // @match *://share.dmhy.org/topics/view/* // @grant GM_addStyle // @grant GM_getResourceText // @grant GM_setClipboard // @run-at document-end // @downloadURL none // ==/UserScript== var icons = { audio: "https://share.dmhy.org/images/icon/mp3.gif", bmp: "https://share.dmhy.org/images/icon/bmp.gif", image: "https://share.dmhy.org/images/icon/jpg.gif", png: "https://share.dmhy.org/images/icon/png.gif", rar: "https://share.dmhy.org/images/icon/rar.gif", text: "https://share.dmhy.org/images/icon/txt.gif", unknown: "https://share.dmhy.org/images/icon/unknown.gif", video: "https://share.dmhy.org/images/icon/mp4.gif", }; var type2Icon = { audio: ["flac", "aac", "wav", "mp3"], bmp: ["bmp"], image: ["jpg", "jpeg", "webp"], png: ["png"], rar: ["rar", "zip", "7z"], text: ["txt", "log", "cue", "ass"], video: ["mkv", "mka", "mp4"], }; var Dictionary = (function () { function Dictionary() { this.data = {}; } Dictionary.prototype.add = function (key, value) { if (!(key in this.data)) { this.data[key] = value; } }; Dictionary.prototype.clear = function () { this.data = {}; }; Dictionary.prototype.containsKey = function (key) { return key in this.data; }; Dictionary.prototype.get = function (key) { return this.data[key]; }; Dictionary.prototype.size = function () { return Object.keys(this.data).length; }; Dictionary.prototype.values = function () { return this.data; }; return Dictionary; }()); var FileSize = (function () { function FileSize() { } FileSize.toLength = function (size) { if (size === undefined) { return -1; } size = size.toLowerCase(); var head = ""; var tail = ""; var isNumber = function (c) { return (c >= "0" && c <= "9") || c === "." || c === "-"; }; for (var _i = 0, size_1 = size; _i < size_1.length; _i++) { var c = size_1[_i]; if (isNumber(c)) { head += c; } else { tail += c; } } var value = parseFloat(head); switch (tail) { case "byte": return value; case "bytes": return value; case "kb": return value * Math.pow(2, 10); case "mb": return value * Math.pow(2, 20); case "gb": return value * Math.pow(2, 30); case "tb": return value * Math.pow(2, 40); } return -1; }; FileSize.toSize = function (length) { if (length >= Math.pow(2, 40)) { return this.format(length, 40, "TiB"); } else if (length >= Math.pow(2, 30)) { return this.format(length, 30, "GiB"); } else if (length >= Math.pow(2, 20)) { return this.format(length, 20, "MiB"); } else if (length >= Math.pow(2, 10)) { return this.format(length, 10, "KiB"); } else { return this.format(length, 0, "Bytes"); } }; FileSize.format = function (length, factor, tail) { return (length / Math.pow(2, factor)).toFixed(3).toString() + tail; }; return FileSize; }()); var TreeNode = (function () { function TreeNode(node) { this.getIcon = function (ext) { for (var type in type2Icon) { if (type2Icon[type].indexOf(ext) >= 0) { return icons[type]; } } return icons.unknown; }; this.name = node; this.length = 0; this.childNode = new Dictionary(); } TreeNode.prototype.insert = function (path, size) { var currentNode = this; for (var _i = 0, path_1 = path; _i < path_1.length; _i++) { var node = path_1[_i]; var next = currentNode.childNode.get(node); if (!currentNode.childNode.containsKey(node)) { next = currentNode.add(node, new TreeNode(node)); next.pareneNode = currentNode; } currentNode = next; } currentNode.length = FileSize.toLength(size); return currentNode; }; TreeNode.prototype.toString = function () { return "" + this.name + "" + FileSize.toSize(this.length) + ""; }; TreeNode.prototype.toObject = function () { var ret = { children: [], length: 0, state: { opened: true, }, text: this.toString(), }; var childNodeValues = this.childNode.values(); for (var key in childNodeValues) { if (!childNodeValues.hasOwnProperty(key)) { continue; } var files = []; var value = this.childNode.get(key); if (value.childNode.size() === 0) { files.push(value); } else { var inner = value.toObject(); value.length = inner.children.reduce(function (aac, val) { return aac += val.length; }, 0); ret.length += value.length; ret.children.push({ children: inner.children, length: value.length, state: { opened: false, }, text: value.toString(), }); } for (var _i = 0, files_1 = files; _i < files_1.length; _i++) { var file = files_1[_i]; var ext = ""; var dotIndex = file.name.lastIndexOf("."); if (dotIndex > 0) { ext = file.name.substr(dotIndex + 1).toLowerCase(); } ret.length += file.length; ret.children.push({ icon: this.getIcon(ext), length: file.length, state: { opened: false, }, text: file.toString(), }); } } return ret; }; TreeNode.prototype.add = function (key, value) { this.childNode.add(key, value); return this.childNode.get(key); }; return TreeNode; }()); var GM_getResourceText = GM_getResourceText; var GM_addStyle = GM_addStyle; var GM_setClipboard = GM_setClipboard; var setUpCSS = function () { if (typeof GM_getResourceText !== "undefined") { GM_addStyle(GM_getResourceText("customCSS")); } else { console.info("%cYou may need to install http://tampermonkey.net/ to use this script.", "color:#e55d67;font-size:1.5em"); return; } $("head").append(""); $("#tabs-1").append(''); $("#tabs-1").append(''); $("#tabs-1").append(''); $(".file_list").css("width", "100%"); $(".file_list").css("max-height", "600px"); }; (function () { setUpCSS(); //const data = new TreeNode(`${$(".topic-title > h3").text()}`); var data = new TreeNode($(".topic-title > h3").text()); $(".file_list > ul li").each(function (index, value) { var text = $(value).text(); var line = text.trim().replace(/\t+/i, "\t").split("\t"); switch (line.length) { case 2: var nodes = line[0].split("/"); var size = line[1]; data.insert(nodes, size); break; case 1: //the text should be "More Than 1000 Files" data.insert(line[0].split("/"), ""); break; } }); var getSelectedRow = function (reference) { return $.jstree.reference(reference).get_node(reference, true); }; var options = { contextmenu: { items: { getText: { action: function (selected) { return GM_setClipboard(selected.reference.find(".filename").text(), "text"); }, label: "Copy", }, remove: { action: function (selected) { return getSelectedRow(selected.reference).remove(); }, label: "死ね!", }, }, show_at_node: false, }, core: { data: data.toObject(), }, plugins: ["search", "wholerow", "contextmenu"], }; $($(".file_list").jstree(options)).bind("loaded.jstree", function (loadedEventData) { var isExpended = false; $("#switch").click(function (clickEventData) { if (isExpended) { clickEventData.target.innerHTML = "Expand All"; $(loadedEventData.target).jstree("close_all"); } else { clickEventData.target.innerHTML = "Toggle All"; $(loadedEventData.target).jstree("open_all"); } isExpended = !isExpended; }); var lastVal = ""; $("#search_input").keyup(function (keyupEventData) { var val = keyupEventData.target.value; if (val !== lastVal) { $(loadedEventData.target).jstree(true).search(val); lastVal = val; } }); }); })();