dmhy tree view

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

As of 2018-06-24. 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.34
// @date         2017/01/11
// @modified     2018/06/24
// @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
// @run-at       document-end
// ==/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 = /** @class */ (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 = /** @class */ (function () {
    function FileSize() {
    }
    FileSize.toLength = function (size) {
        if (size === undefined) {
            return -1;
        }
        var head = "";
        var tail = "";
        var isNumber = function (c) { return (c >= "0" && c <= "9") || c === "." || c === "-"; };
        for (var _i = 0, _a = size.toLowerCase(); _i < _a.length; _i++) {
            var c = _a[_i];
            if (isNumber(c)) {
                head += c;
            }
            else {
                tail += c;
            }
        }
        var value = parseFloat(head);
        switch (tail) {
            case "byte": return value * Math.pow(2, 0);
            case "bytes": return value * Math.pow(2, 0);
            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 = /** @class */ (function () {
    function TreeNode(node) {
        this._ext = undefined;
        this._icon = undefined;
        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 = {
            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.length = inner.children.reduce(function (aac, val) { return aac + val.length; }, 0);
                inner.text = value.toString(); //update text with size info
                inner.state.opened = false;
                ret.children.push(inner);
            }
            for (var _i = 0, files_1 = files; _i < files_1.length; _i++) {
                var file = files_1[_i];
                ret.length += file.length;
                ret.children.push({
                    icon: file.icon,
                    length: file.length,
                    text: file.toString(),
                });
            }
        }
        return ret;
    };
    TreeNode.prototype.add = function (key, value) {
        this.childNode.add(key, value);
        return this.childNode.get(key);
    };
    Object.defineProperty(TreeNode.prototype, "ext", {
        get: function () {
            if (this._ext !== undefined) {
                return this._ext;
            }
            this._ext = "";
            var dotIndex = this.name.lastIndexOf(".");
            if (dotIndex > 0) {
                this._ext = this.name.substr(dotIndex + 1).toLowerCase();
            }
            return this._ext;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(TreeNode.prototype, "icon", {
        get: function () {
            if (this._icon !== undefined) {
                return this._icon;
            }
            this._icon = icons.unknown;
            for (var type in type2Icon) {
                if (type2Icon[type].indexOf(this.ext) >= 0) {
                    this._icon = icons[type];
                    break;
                }
            }
            return this._icon;
        },
        enumerable: true,
        configurable: true
    });
    return TreeNode;
}());
var GM_getResourceText = GM_getResourceText;
var GM_addStyle = GM_addStyle;
var copyToClipboard = function (text) {
    if (document.queryCommandSupported && document.queryCommandSupported("copy")) {
        var textarea = document.createElement("textarea");
        textarea.textContent = text;
        textarea.style.position = "fixed"; // Prevent scrolling to bottom of page in MS Edge.
        document.body.appendChild(textarea);
        textarea.select();
        try {
            return document.execCommand("copy"); // Security exception may be thrown by some browsers.
        }
        catch (ex) {
            console.warn("Copy to clipboard failed.", ex);
            return false;
        }
        finally {
            document.body.removeChild(textarea);
        }
    }
};
var setupCSS = function () {
    if (typeof GM_getResourceText !== "undefined") {
        GM_addStyle(GM_getResourceText("customCSS"));
        $("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>");
        $(".file_list").css("width", "100%");
        $(".file_list").css("max-height", "600px");
    }
    else {
        console.info("%cTo load style sheet and let script works correctly, http://tampermonkey.net/ is required.", "color:#e55d67;font-size:1.3em");
    }
};
var setupOpe = function () {
    $("#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;"/>');
};
(function () {
    setupCSS();
    setupOpe();
    var data = new TreeNode($(".topic-title > h3").text());
    $(".file_list:first > 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;
            default:
                console.log("Unexpected length in \"" + line + "\"");
        }
    });
    var getSelectedRow = function (reference) { return $.jstree.reference(reference).get_node(reference, true); };
    var options = {
        contextmenu: {
            items: {
                getText: {
                    action: function (selected) { return copyToClipboard(selected.reference.find(".filename").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:first").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;
            }
        });
    });
})();