Hierarchy Layout

by WizKid81

HTML

<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.3.0/knockout-debug.js"></script>
<script src="http://code.jquery.com/jquery-2.1.4.min.js"></script>
<script src="http://code.jquery.com/ui/1.11.4/jquery-ui.min.js"></script>
<nav class="navbar navbar-inverse navbar-fixed-top">
    <div class="container-fluid">
        <div class="navbar-header">
            <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse"> <span class="icon-bar"></span>
 <span class="icon-bar"></span>
 <span class="icon-bar"></span>

            </button> <a href="./" class="navbar-brand">IA Hierarchy</a>

        </div>
        <div class="navbar-collapse collapse">
            <ul class="nav navbar-nav">
                <li><a href="./">Home</a>

                </li>
                <li><a href="Hierarchy">Hierarchy</a>

                </li>
                <li><a href="SAHierarchy">Subject Areas</a>

                </li>
                <li><a href="About">About</a>

                </li>
                <li><a href="Contact">Contact</a>

                </li>
            </ul>
            <ul class="nav navbar-nav navbar-right">
                <li><a href="Account/Register">Register</a>

                </li>
                <li><a href="Account/Login">Log in</a>

                </li>
            </ul>
        </div>
    </div>
</nav>
<div class="container body-content">
    <div class="container" data-bind="visible: mode() =='move'">
        <div class="col-xs-12 text-center" role="alert"> <span data-bind="visible: destinationItem() === undefined">Select a destination node</span>

            <div data-bind="visible: destinationItem() !== undefined"> <span>Move to <mark data-bind="text: destinationItem() === undefined ? '' :...

CSS

/* Move down content because we have a fixed navbar that is 50px tall */
 body {
    padding-top: 50px;
    padding-bottom: 20px;
}
/* Wrapping element */

/* Set some basic padding to keep content from hitting the edges */
 .body-content {
    padding-left: 15px;
    padding-right: 15px;
}
/* Set widths on the form inputs since otherwise they're 100% wide */
 input[type="text"], input[type="password"] {
    max-width: 280px;
}
/* Responsive: Portrait tablets and up */
 @media screen and (min-width: 768px) {
    .jumbotron {
        margin-top: 20px;
    }
    .body-content {
        padding: 0;
    }
}
/* unordered list for TreeView*/
 ul.tree.root {
    width:500px;
}
ul.tree {
    list-style-type: none;
    padding-left: 10px;
}
/* tree spacing to keep nodes without children lined up with those that do have children*/
 .tree-spacer {
    display:inline-block;
    width:12px;
    text-decoration:none;
    text-align:center;
}
/* tree toggle buttons*/
 .tree-toggle {
    cursor: pointer;
}
.selectedNode {
    background-color: lightgray;
}
.destinationNode {
    background-color: darkgray;
    color: white;
}
.deletedNode span {
    text-decoration: line-through;
    opacity: .3;
}
.newNode span {
    background-color: green;
}
.hierarchyNode {
    cursor: pointer;
}
.clickableText {
    cursor: pointer;
}
.clickableIcon {
    cursor: pointer;
}
/*These were styles for inline editing of nodes.  Functions correctly but just doesn't seem like a good user experience
    
div.tree_edit {
    display: none;
}

div.selectedNode {
    display: inline;
}

    span.selectedNode {
    display: none;
}


div.selectedNode input {
    display: inline;
}

    */
 .changedValue {
    border: solid 1px red;
}
.sidebar {
    position: fixed;
}
span.no_selection {
    -webkit-user-select: none;
    /* webkit (safari, chrome) browsers */
    -moz-user-select: none;
    /* mozilla browsers */
    -khtml-user-select: none;
    /* webkit (konqueror) browsers */
    -ms-user-select:...

JavaScript

console.clear();

$.fn.scrollView = function () {
    return this.each(function () {
        $('html, body').animate({
            scrollTop: $(this).position().top
        }, 500);
    });
};

//////////////
//////
//////  knockout extensions
//////
//////////////

//Extending Knockout so that a trackDirtyFlag can be added to observables to allow for tracking changes and reverting to orignal values
ko.subscribable.fn.trackDirtyFlag = function () {
    var original = ko.observable(this());

    this.isDirty = ko.computed(function () {
        return this() !== original();
    }, this);

    this.revert = function () {
        this(original());
    };

    this.update = function () {
        original(this());
    };

    this.startingValue = ko.pureComputed(function () {
        return original();
    });

    return this;
};

//Used to add a change event to an observable so that the old selected item can be "unselected" when a new item is selected
ko.subscribable.fn.subscribeChanged = function (callback) {
    var oldValue;
    this.subscribe(function (_oldValue) {
        oldValue = _oldValue;
    }, this, 'beforeChange');

    this.subscribe(function (newValue) {
        callback(newValue, oldValue);
    });
};

//Setting up an autoComplete binding for knockout.  This requires jquery and jquery ui
ko.bindingHandlers.autoComplete = {
    findSelectedItem: function (dataSource, binding, selectedValue) {
        if (selectedValue !== undefined) {
            var unwrap = ko.utils.unwrapObservable;
            //Go through the source and find the id, and use its label to set the autocomplete
            var source = unwrap(dataSource);
            var valueProp = unwrap(binding.optionsValue);

            var selectedItem = ko.utils.arrayFirst(source, function (item) {
                if (unwrap(item[valueProp]) === selectedValue) return true;
            }, this);
        }

        return selectedItem;
    },
    buildDataSource: function (dataSource, labelProp,...