dmhy tree view

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

As of 2017-05-30. See the latest version.

// ==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.31
// @date         2017/01/11
// @modified     2017/05/30
// @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
// ==/UserScript==
var icons = {
    audio: "https://share.dmhy.org/images/icon/mp3.gif",
    image: "https://share.dmhy.org/images/icon/jpg.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"],
    image: ["jpg", "bmp", "jpeg", "webp"],
    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 "<span class=\"filename\">" + this.name + "</span><span class=\"filesize\">" + FileSize.toSize(this.length) + "</span>";
    };
    TreeNode.prototype.toObject = function () {
        var ret = {};
        ret.children = [];
        ret.length = 0;
        ret.text = this.name;
        ret.state = { opened: true };
        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 tmp = {};
                tmp.children = [];
                tmp.length = 0;
                var inner = value.toObject();
                for (var _i = 0, _a = inner.children; _i < _a.length; _i++) {
                    var innerNode = _a[_i];
                    tmp.children.push(innerNode);
                    tmp.length += innerNode.length;
                }
                ret.length += tmp.length;
                value.length = tmp.length;
                tmp.text = value.toString();
                ret.children.push(tmp);
            }
            for (var _b = 0, files_1 = files; _b < files_1.length; _b++) {
                var file = files_1[_b];
                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,
                    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;
(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("<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>");
    $("#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" />');
    $("#tabs-1").append('<button id="switch" style="border:0;border-radius:2px;padding:8px;margin-left:10px;">Expand All</button>');
    $("#tabs-1").append('<input id="hidden_text" style="display:none;"/>');
    $(".file_list").css("width", "100%");
    $(".file_list").css("max-height", "600px");
    var data = new TreeNode("<span class=\"filename\">" + $(".topic-title > h3").text() + "</span>");
    $(".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 options = {
        contextmenu: {
            items: {
                getText: {
                    action: function (selected) {
                        var row = $.jstree.reference(selected.reference).get_node(selected.reference, true);
                        var node = $("#hidden_text");
                        node.css("display", "block");
                        node.val(row.find(".filename").text());
                        node.focus();
                        document.execCommand("selectAll");
                        document.execCommand("copy");
                        document.execCommand("unselect");
                        node.css("display", "none");
                    },
                    label: "Copy",
                },
                remove: {
                    action: function (selected) {
                        var row = $.jstree.reference(selected.reference).get_node(selected.reference, true);
                        row.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;
            }
        });
    });
})();