// Implements behavior of a tree view generated by TreeViewRenderer.
// Depends on jquery.
class TreeView {
    constructor(elemID) {
        this.treeViewElem = $("#" + elemID);
        this.nodeClickCallbacks = $.Callbacks();
    }

    _expandSelectedNodes(allNodes) {
        let selectedNodes = allNodes.filter(".selected");
        let parentElems = selectedNodes.parentsUntil(".tree-view", ".child-nodes");

        parentElems.prev(".node").find(".expander").addClass("expanded");
        parentElems.removeClass("hidden");
    }

    _selectNode(node, allNodes) {
        allNodes.removeClass("selected");
        node.addClass("selected");
    }

    _clickNode(node, allNodes) {
        let callbackResult = { handled: false };
        this.nodeClickCallbacks.fire(node, callbackResult);

        if (callbackResult.handled) {
            this._selectNode(node, allNodes);
        } else {
            let expander = node.find(".expander");
            let href = node.attr("href");

            if ($(event.target).is(".expander")) {
                this._toggleNode(node, expander);
            } else if (href.length > 0) {
                this._selectNode(node, allNodes);
                location.href = href; // navigate link
            } else if (!expander.hasClass("empty")) {
                this._toggleNode(node, expander);
            }
        }
    }

    _toggleNode(node, expander) {
        let childNodes = node.next(".child-nodes");

        if (expander.hasClass("expanded")) {
            expander.removeClass("expanded");
            childNodes.addClass("hidden");
        } else {
            expander.addClass("expanded");
            childNodes.removeClass("hidden");
        }
    }

    prepare() {
        let allNodes = this.treeViewElem.find(".node");

        // set node indents according to their levels
        let indentStep = this.treeViewElem.find(".indent:first").width();

        allNodes.each(function () {
            let level = $(this).data("level");
            let indentWidth = indentStep * level;
            $(this).find(".indent")
                .css({
                    "width": indentWidth,
                    "min-width": indentWidth
                });
        });

        // expand selected nodes
        this._expandSelectedNodes(allNodes);

        // do action or toggle on node click
        const thisObj = this;
        allNodes
            .off()
            .on("click", function (event) {
                if (event.ctrlKey || event.button == 1 /*middle*/) {
                    return true; // allow the default link behavior
                } else {
                    thisObj._clickNode($(this), allNodes);
                    return false;
                }
            });
    }

    selectNode(node) {
        let allNodes = this.treeViewElem.find(".node");
        this._selectNode(node, allNodes);
        this._expandSelectedNodes(node);
    }
}
