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,...