Hierarchy Tree

by WizKid81

HTML

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
<ul data-bind="template: { name: 'itemTmpl', foreach: $data.hierarchyNodes }"></ul>
<script id="itemTmpl" type="text/html">
    <li><button class = "tree-toggle"
    data-bind = "click: getChildNodes, text: expandText, visible: hasChildren" > + </button>
        <div data-bind="visible: hasChildren == false" class = "tree-spacer"></div>
        <span data-bind = "text: name"> </span > <ul data-bind = "template: { name: 'itemTmpl', foreach: $data.childNodes }, visible: expanded" > </ul> </li>
</script>

CSS

ul {
    list-style-type: none;

}
.tree-spacer {
    display:inline-block;
    height:19px;
    line-height:19px;
    width:21px;
    text-decoration:none;
    text-align:center;
}
.tree-toggle {
    -moz-box-shadow:inset 0px 1px 0px 0px #ffffff;
    -webkit-box-shadow:inset 0px 1px 0px 0px #ffffff;
    box-shadow:inset 0px 1px 0px 0px #ffffff;
    background:-webkit-gradient(linear, left top, left bottom, color-stop(0.05, #f9f9f9), color-stop(1, #e9e9e9));
    background:-moz-linear-gradient(center top, #f9f9f9 5%, #e9e9e9 100%);
    filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#f9f9f9', endColorstr='#e9e9e9');
    background-color:#f9f9f9;
    -webkit-border-top-left-radius:0px;
    -moz-border-radius-topleft:0px;
    border-top-left-radius:0px;
    -webkit-border-top-right-radius:0px;
    -moz-border-radius-topright:0px;
    border-top-right-radius:0px;
    -webkit-border-bottom-right-radius:0px;
    -moz-border-radius-bottomright:0px;
    border-bottom-right-radius:0px;
    -webkit-border-bottom-left-radius:0px;
    -moz-border-radius-bottomleft:0px;
    border-bottom-left-radius:0px;
    text-indent:0px;
    border:1px solid #dcdcdc;
    display:inline-block;
    color:#666666;
    font-family:Arial;
    font-size:15px;
    font-weight:bold;
    font-style:normal;
    height:19px;
    line-height:19px;
    width:21px;
    text-decoration:none;
    text-align:center;
    text-shadow:1px 1px 0px #ffffff;
}
.tree-toggle:hover {
    background:-webkit-gradient(linear, left top, left bottom, color-stop(0.05, #e9e9e9), color-stop(1, #f9f9f9));
    background:-moz-linear-gradient(center top, #e9e9e9 5%, #f9f9f9 100%);
    filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#e9e9e9', endColorstr='#f9f9f9');
    background-color:#e9e9e9;
}
.tree-toggle:active {
    position:relative;
    top:1px;
}

JavaScript

console.clear();
var id = 20;
//Testing function just to create dummy test child
function getTestChild(parent) {
    var child = new hierarchyNode();
    child.parent_id = parent.id_field;
    child.id_field = id;
    id = id + 1;
    child.name = "Child number " + child.id_field;
    child.hasChildren = false;

    return child;
}

var hierarchyNode = function (data) {
    getObservableArray = function (childNodes) {
        var returnArray = ko.observableArray();

        $(childNodes).each(function (index, element) {
            var hierarchyItem = new hierarchyNode(element);
            returnArray.push(hierarchyItem);
        })

        return returnArray;
    }

    this.id_field = (data === null || data === undefined || typeof data.ID_Field === "undefined") ? undefined : data.ID_Field;
    this.parent_id = (data === null || data === undefined || typeof data.Parent_ID === "undefined") ? undefined : data.Parent_ID;
    this.name = (data === null || data === undefined || typeof data.Name === "undefined") ? undefined : data.Name;
    this.hasChildren = (data === null || data === undefined || typeof data.HasChildren === "undefined") ? false : data.HasChildren == 1 ? true : false;
    this.childNodes = (data === null || data === undefined || typeof data.childNodes === "undefined") ? ko.observableArray() : getObservableArray(data.childNodes);

    this.expanded = ko.observable(false);
    this.expandText = ko.computed(function () {
        if (this.expanded() == true) {
            return "-";
        } else {
            return "+"
        }
    }, this)
}

hierarchyNode.prototype = {
    id_field: null,
    parent_id: null,
    name: null,
    hasChildren: null,
    childNodes: null,
    populateChildren: function (nodeArray) {
        if (this.hasChildren == true && this.childNodes().length == 0) {
            var self = this;

            $(nodeArray).each(function (index, element) {
                var hierarchyItem = new hierarchyNode(element);
           ...