REVISION CONTROL

This commit is contained in:
Curtis SerVaas
2014-08-12 18:25:51 -04:00
parent 9d84461422
commit 16719fe2cc
13 changed files with 621 additions and 295 deletions
+14
View File
@@ -51,6 +51,20 @@ require('./lib/routes.js')(app, passport);
// console.log("ABOUT TO RECURSE");
// require('./lib/revAlgorithm').recurse();
// var MySnap = require('./models/Snap.js').MySnap;
// console.log("TESTING Mongo");
// MySnap.find({},'timestamp')
// .sort({timestamp: -1}).limit(1).exec(function(err,timeStamp){
// timeStamp = timeStamp[0].timestamp;
// console.log("timeStamp=");
// console.log(timeStamp);
// });
if(process.argv[2] == "restart"){
+15 -7
View File
@@ -14,6 +14,8 @@ var sessionStore = new MemoryStore();
var io;
var online = [];
var lastExchangeData = {};
var makeCommit = require("./makeCommit.js").makeCommit;
var getAndSendRevHistory = require("./revAlgorithm.js").getAndSendRevHistory;
console.log("\n\nLOOK HERE!!")
@@ -32,11 +34,7 @@ module.exports = {
// },
createUser: function(username, password, callback){
User.addUser(username, password, callback);
},
getNodes: function(){
return Node.findNodes();
},
},
getSessionStore: function(){
return sessionStore;
@@ -46,9 +44,18 @@ module.exports = {
io = require('socket.io').listen(server);
io.sockets.on('connection', function(socket){
socket.on("COMMIT", function(){
makeCommit();
socket.emit("commitReceived");
});
socket.on("revHistoryRequest", function(){
getAndSendRevHistory(null, socket);
});
//socket.emit('news', {hello: "world"});
socket.on('nodeRequest', function(data){
var nodes = Node.findNodes(socket); //finds, then sends through socket.
var nodes = Node.findAndSendSocket(socket); //finds, then sends through socket.
//var nodes = {'keep': 'calm'};
//socket.emit('nodeData', nodes); (emit is in findNodes);
})
@@ -69,7 +76,7 @@ module.exports = {
socket.on("blurred", function(data){
console.log('\nBLURRED\n')
// console.log('\nBLURRED\n')
socket.broadcast.emit("blurred", data);
var id = data[0];
var text = data[1];
@@ -111,6 +118,7 @@ module.exports = {
});
socket.on("removeNode", function(data){
// console.log("removeNode!!!");
var thisId = data[0];
var thisIndex = data[1];
var parId = data[2];
+48
View File
@@ -0,0 +1,48 @@
var MyNode = require('../models/Node.js').MyNode;
var snapMoudule = require('../models/Snap.js');
var MySnap = snapMoudule.MySnap;
var addSnap = snapMoudule.addSnap;
var _ = require("underscore");
module.exports.makeCommit = makeCommit;
//http://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example
function createCallback(node, now){
return function(err,snapTimestamp){
console.log("\t\tSNAPTIMESTAMP");
console.log(snapTimestamp);
console.log("\t\tsnapNode");
console.log(node);
if(snapTimestamp.length == 0){//noSnaps in DB means MyNode is newNode.
if(node.parents.length == 0){node.remove();}//(node was created and deleted before being committed)
else{addSnap(node, now);}
return; //just returns from callback, not from the _.each function.
}
snapTimestamp = snapTimestamp[0].timestamp;
if(snapTimestamp == node.timestamp){} //no edits have happened for this node.
else{//edits have occurred, and we need to save them.
if(node.parents.length == 0){
addSnap(node,now,1); //remove the node with no parents (from NodeCollection).
}
else{
addSnap(node, now);
}
}//else
}//findSnapTimeStamp => saveSnap.
}
function makeCommit(){
var now = Date.now();
MyNode.find(function(err,nodes){
_.each(nodes, function(node){
console.log("NODE");
console.log(node)
MySnap.find({cur_id: node._id}, 'timestamp')
.sort({timestamp: -1}).limit(1).exec(createCallback(node, now));
});//.each
});
}//function
+122
View File
@@ -0,0 +1,122 @@
var _ = require('underscore');
var MySnap = require('../models/Snap.js').MySnap
module.exports.getAndSendRevHistory = getAndSendRevHistory;
function getAndSendRevHistory(rootId, sock){
globalList = [];
snapHash = {};
timeHash = {};
depth = 0;
socket = sock;
var rootId = "53ea3f506dc8d39342bf4f9f";
// FetchSelfAndChildrenBack(rootId, asyncLoopGetChildren);
asyncLoopGetChildren([rootId]);
}
//This is all the backSnaps for ONE rootId.
function updateGlobals(rootSnaps){
var rootId = rootSnaps[0].cur_id;
snapHash[rootId] = snapHash[rootId] || rootSnaps;
//snapHash[rootId] = rootSnaps; //should be equivalent.
_.each(rootSnaps, function(snap){
(timeHash[snap.timestamp] = timeHash[snap.timestamp] || []).push(snap);
});
globalList.push(rootId);
}
//This should only be called once per ID.
function FetchSelfAndChildrenBack(rootId, callback){
MySnap.find({cur_id: rootId}).sort({timestamp: 1}).exec(function(err, rootSnaps){
if(err){console.log("ERROR HERE")}
updateGlobals(rootSnaps);
var metaArray = [];
_.each(rootSnaps,function(snap){metaArray.push(snap.children)})
mergeArrays(metaArray, callback); //(mergeArrays, then get children)
});
}
function mergeArrays(metaArray, callback){
// console.log("MERGE_SNAPs_CHILDREN");
// console.log("metaArray")
// console.log(metaArray);
mergedArray = _.union(_.flatten(metaArray));
mergedArray = mergedArray.filter( function(a){if (!this[a]) {this[a] = 1; return a;}},{});
// console.log("mergedArray");
// console.log(mergedArray);
callback(mergedArray); //d2temp.push(merged); loopStep();
}
//d2=nodes in the next depth.//(maybe been FetchSelfAndChildrenBack'd)
//globalList=nodes in this depth. //(have bene fetch'd)
//temp = nodes to fetch next.
function MergeAndDiffArraysSync(d2){
var temp = [];
_.each(d2, function(el){
if(globalList.indexOf(el) == -1){
temp.push(el);
}
});
console.log("d2");
console.log(d2);
globalList = _.union(globalList, temp);
return temp
}
//BFS from one node to all of its adjacents.
//d1Union has all the unique nodes that have ever been a child of root.
function asyncLoopGetChildren(d1Union){
var i = 0;
var numTimes = d1Union.length;
var nextDepthList = [];
if(typeof d1Union == 'undefined' || d1Union.length == 0){//bottomed out recursion = end of bfs
console.log("finished!!!!")
console.log("snapHash");
console.log(snapHash);
console.log("timeHash");
console.log(timeHash);
console.log("globalList");
console.log(globalList);
socket.emit("revHistory", [snapHash, timeHash]);
return;
}
function loopStep(){
if(i == numTimes){//call completion function
d2Union = _.union(_.flatten(nextDepthList)); //array of arrays.
//these can easily be done using callbacks if this causes a bug.
var temp = MergeAndDiffArraysSync(d2Union);
asyncLoopGetChildren(temp);
return;
}
// fn(loopStep);
FetchSelfAndChildrenBack(d1Union[i], function(mergedArray){
nextDepthList.push(mergedArray);
loopStep();
});
i+=1;
}
loopStep();
}
+152 -181
View File
@@ -4,199 +4,41 @@ var NodeSchema = new mongoose.Schema({
text: {type: String},
children: {type: Array},
parents: {type: Array},
markdown: {type: Boolean}
})
markdown: {type: Boolean},
timestamp: {type: Number}
});
var SnapSchema = new mongoose.Schema({
text: {type: String},
children: {type: Array},
parents: {type: Array},
timestamp: {type: Number},
cur_id: {type: String}
});
var rootID;
var MySnap = mongoose.model('snaps', SnapSchema);
var MyNode = mongoose.model('nodes', NodeSchema);
var snapModule = require('./Snap.js');
var MySnap = snapModule.MySnap;
var addSnap = snapModule.addSnap;
// Exports
module.exports.addNode = addNode;
module.exports.findNodes = findNodes;
module.exports.MyNode = MyNode;
module.exports.findAndSendSocket = findAndSendSocket
module.exports.setUpDB = setUpDB;
module.exports.moveNode = moveNode;
module.exports.removeNode = removeNode;
module.exports.updateParent = updateParent;
module.exports.updateText = updateText;
module.exports.setUpDB = setUpDB;
module.exports.removeNode = removeNode;
module.exports.moveNode = moveNode;
module.exports.rootNodeId = rootNodeId;
module.exports.MySnap = MySnap;
function rootNodeId(){
return rootID;
}
function moveNode(ids, arrays){
var thisId = ids[0]; //draggedId
var now = Date.now();
MyNode.findById(thisId, null, function(err, node){
addSnap(node, now);
node.parents = arrays[0];
node.save();
});
var oldParId = ids[1];
MyNode.findById(oldParId, null, function(err, node){
addSnap(node, now);
node.children = arrays[1];
node.save();
});
var newParId = ids[2];
MyNode.findById(newParId, null, function(err, node){
addSnap(node, now);
node.children = arrays[2];
node.save();
});
}
function addSnap(node, now, callback){
var instance = new MySnap();
instance.text = node.text;
instance.children = node.children;
instance.parents = node.parents;
instance.cur_id = node._id;
instance.timestamp = now;
instance.save(function(err){
if(err){
//callback(err);
}
else{
//callback(null, instance);
}
});
}
module.exports.addNode = addNode;
// module.exports.rootNodeId = rootNodeId;
// var rootID;
// function rootNodeId(){
// return rootID;
// }
//this is technically not good asynchronous code.
//this coulde be made into a recursive function which takes an array of todo-strings.
function addListOfNodes(){
addNode("0root", [], [], function(err, rootNode){
rootID = rootNodeId._id;
var parId = rootNode._id;
addNode("Todos", [], [parId], function(err, vadar){ //called vadar because it is the parent/father
var vadarId = vadar._id;
var childArray = [];
addNode("Revision Control", [], [vadarId], function(err, child){
childArray.push(child._id);
});
addNode("LatexEditor", [], [vadarId], function(err, child){
childArray.push(child._id);
});
addNode("Lots of other stuff", [], [vadarId], function(err, child){
childArray.push(child._id);
vadar.children = childArray;
vadar.save();
});
rootNode.children = [vadarId];
rootNode.save();
});
});
}
function setUpDB(){
MyNode.remove({}, function(err) { console.log('collection removed') });
MySnap.remove({}, function(err) { console.log('collection removed') });
addListOfNodes();
}
function removeNode(thisId, thisIndex, parId){
var now = Date.now();
MyNode.findById(parId, null, function(err, parNode){
if(err || parNode == null){
return;
}
addSnap(parNode, now);
var temp = parNode.children;
console.log(temp.splice(thisIndex, 1));
console.log(temp);
parNode.children = temp;
parNode.save();
});
MyNode.findById(thisId, null, function(err, delNode){
if(err || delNode == null){
return;
}
addSnap(delNode, now);
if(delNode.parents.length ==1 ){
delNode.remove();
}
else{ //this is the condition that we'll have to take care of if there are dups.
delNode.parents = _.without(parents, parId);
delNode.save();
}
})
}
function updateText(id, newText){
console.log("\nUPDATE TEXT\n" + id + newText);
var now = Date.now();
MyNode.findById(id, null, function(err, node){
if(err || node == null){
return;
}
addSnap(node, now);
node.text = newText;
node.save();
});
}
function updateParent(parId, newId ,newIndex, now){
MyNode.findById(parId, null, function(err, parNode){
if(err || parNode == null){
return;
}
addSnap(parNode, now);
parNode.children.insert(newIndex, newId);
parNode.save();
})
}
//add Node to the DB.
function addNode(text, children, parents, callback){
var now = Date.now();
var instance = new MyNode();
instance.text = text;
instance.children = children;
instance.parents = parents;
instance.markdown = 0;
addSnap(instance, now);
instance.save(function (err) {
if (err) {
callback(err);
}
else {
callback(null, instance, now);
}
});
}
function findNodes(socket){
function findAndSendSocket(socket){
var nodes = {'keeping': 'calm'}
MyNode.find(function(err, nodes){
if(!err){
@@ -211,6 +53,135 @@ function findNodes(socket){
});
}
function setUpDB(){
MyNode.remove({}, function(err) { console.log('collection removed') });
MySnap.remove({}, function(err) { console.log('collection removed') });
// addNode("0root", [], ["123456"]);
// addNode("Welcolme!", [], [rootNode._id]);
addNode("0root", [], ["123456"], function(err, rootNode){
addNode("Welcolme!", [], [rootNode._id], function(err, firstBullet){
rootNode.children = [firstBullet._id]
var now = rootNode.timestamp = firstBullet.timestamp
addSnap(rootNode, now);
addSnap(firstBullet, now);
});
});
}
function moveNode(ids, arrays){
var thisId = ids[0]; //draggedId
var now = Date.now();
MyNode.findById(thisId, null, function(err, node){
node.timestamp = now;
node.parents = arrays[0];
node.save();
});
var oldParId = ids[1];
MyNode.findById(oldParId, null, function(err, node){
node.timestamp = now;
node.children = arrays[1];
node.save();
});
var newParId = ids[2];
MyNode.findById(newParId, null, function(err, node){
node.timestamp = now;
node.children = arrays[2];
node.save();
});
}
function removeNode(thisId, thisIndex, parId){
var now = Date.now();
MyNode.findById(parId, null, function(err, parNode){
if(err || parNode == null){
return;
}
var temp = parNode.children;
temp.splice(thisIndex, 1); //remove thisIndex.
parNode.children = temp;
parNode.timestamp = now;
parNode.save();
});
MyNode.findById(thisId, null, function(err, delNode){
if(err || delNode == null){
return;
}
delNode.timestamp = now;
if(delNode.parents.length ==1 ){//(this if statement is technically redundant)
// delNode.remove();
delNode.parnets = []; //(this is how deletion is represented).
delNode.save();
}
else{ //this is the condition that we'll have to take care of if there are dups.
delNode.parents = _.without(parents, parId);
delNode.save();
}
})
}
function updateText(id, newText){
MyNode.findById(id, null, function(err, node){
if(err || node == null){
return;
}
node.timestamp = Date.now();
node.text = newText;
node.save();
});
}
function updateParent(parId, newId ,newIndex, now){
MyNode.findById(parId, null, function(err, parNode){
if(err || parNode == null){
return;
}
parNode.children.insert(newIndex, newId);
parNode.timestamp = Date.now();
parNode.save();
})
}
//add Node to the DB.
function addNode(text, children, parents, callback){
var instance = new MyNode();
instance.text = text;
instance.children = children;
instance.parents = parents;
instance.markdown = 0;
instance.timestamp = Date.now();
instance.save(function (err) {
if (err) {
callback(err);
}
else {
callback(null, instance);
}
});
}
Array.prototype.insert = function (index, item) {
this.splice(index, 0, item);
};
+52
View File
@@ -0,0 +1,52 @@
var mongoose = require('mongoose');
var SnapSchema = new mongoose.Schema({
text: {type: String},
children: {type: Array},
parents: {type: Array},
markdown: {type: Boolean},
timestamp: {type: Number},
cur_id: {type: String}
});
var MySnap = mongoose.model('snaps', SnapSchema);
module.exports.MySnap = MySnap;
// module.exports.fetchMostRecent = fetchMostRecent;
module.exports.addSnap = addSnap;
// function fetchMostRecent(){
// MySnap
// }
//helper function for setUpDB.
function addSnap(node, now, remove, callback){
var instance = new MySnap();
instance.text = node.text;
instance.children = node.children;
instance.parents = node.parents;
instance.markdown = node.markdown;
instance.cur_id = node._id;
instance.timestamp = now;
if(remove){
console.log("removingNode!!!");
node.remove();
}
else{
console.log("saving Node!!!");
node.timestamp = now;
node.save();
}
instance.save(function(err){
if(err){
//callback(err);
}
else{
//callback(null, instance);
}
});
}
+14
View File
@@ -1,6 +1,20 @@
$(function(){
//alert("jquery works");
$("#COMMIT").click(function(){
socket.emit("COMMIT");
});
$(".getRevHistory").click(function(){
socket.emit("revHistoryRequest");
});
$(".toggleSidebar").click(function(){
$("#mainPanel").toggleClass("floatRight centering");
$("#metaSidebar").toggleClass("hideBar");
});
$("#markdownButton").click(function(){
vo.thisView.createMarkDownBullet();
});
+1 -1
View File
@@ -1,4 +1,4 @@
var nodesCollection = Backbone.Collection.extend({
var NodesCollection = Backbone.Collection.extend({
model: NodeModel
+38
View File
@@ -0,0 +1,38 @@
renderRevControl = function(rootNode, timeStamp){
var finalCollection = [];
rootSnap = fetchLTE(rootNode, timeStamp );
var queue = [rootSnap];
while(queue.length > 0){
snap = queue.shift();
finalCollection.push(snap);
childrenSnaps = fetchChildren(snap);
_.each(childrenSnaps, function(childSnap){queue.push(childSnap)});
}
console.log("FINALCOLLECTION");
console.log(finalCollection);
nodesCollection = new NodesCollection(finalCollection);
}
//we want the first snap that has a timestamp lessThan or Equal to this.
//(if none exists, then this call is made in error!).
function fetchLTE(nodeId, timeStamp){
var snapList = snapHash[nodeId]; //sorted, [0] is smallest
for(var i = snapList.length-1 ; i >= 0; i--){ //start with biggest/mostRecent.
if(snapList[i].timestamp <= timeStamp){
return snapList[i];
}
}
alert("error!!");
}
function fetchChildren(subRootSnap){
childrenSnaps = [];
var timeStamp = snap.timestamp;
_.each(subRootSnap.children, function(childId){
childrenSnaps.push( fetchLTE(childId, timeStamp) );
});
return childrenSnaps;
}
+57 -3
View File
@@ -29,7 +29,7 @@
socket.on('nodeData', function(data){
//alert("data");
console.log(data);
nodesCollection = new nodesCollection(data);
nodesCollection = new NodesCollection(data);
var id = nodesCollection.findWhere({text: "0root"}).get("_id");
if(otherID){
id = otherID
@@ -39,6 +39,42 @@
that.viewRoot(id);
});
socket.on("commitReceived", function(){
alert("commitReceived");
});
socket.on("revHistory", function(data){
alert("revHistory!!");
console.log("revHistory!!");
snapHash = data[0];
timeHash = data[1];
console.log("snapHash");
console.log(snapHash);
console.log("timeHash");
console.log(timeHash);
var list = "";
_.each(Object.keys(timeHash), function(timestamp){
list += "<li><a class='timestamp'>"+timestamp+"</a></li>";
})
$("#revTimestamps").html(list);
})
$("#revTimestamps").on("click", "a.timestamp", function(event){
// debugger;
var a = event.target;
var timestamp = parseInt($(a).html());
renderRevControl("53ea3f506dc8d39342bf4f9f", timestamp);
setTimeout(function(){
// debugger;
console.log("nodesCollection")
console.log(nodesCollection);
that.viewRoot("53ea3f506dc8d39342bf4f9f")
}, 1000);
});
socket.on('edit', function(data){
var id = data[0];
var newText = data[1];
@@ -130,9 +166,24 @@
var thisModel = nodesCollection.findWhere({_id: ids[0]});
var oldParModel = nodesCollection.findWhere({_id: ids[1]});
var newParModel = nodesCollection.findWhere({_id: ids[2]});
debugger;
// debugger;
moveNode(thisModel, indices[0], oldParModel, newParModel, indices[1]);
})
});
socket.on("revControl", function(data){
//(store all of this...).
var timeHash = data[0];
var snapHash = data[1];
var timeStamps = timeHash.keys;
//To be continued...
});
@@ -140,6 +191,9 @@
viewRoot: function(id){
var rootNode = nodesCollection.findWhere({_id: id});
if(!rootNode){
rootNode = nodesCollection.findWhere({cur_id: id});
}
// alert("viewNode");
console.log("ViewRoot + rootNode");
console.log(rootNode);
+1
View File
@@ -55,6 +55,7 @@ var listView = Backbone.View.extend({
var childrenIds = that.model.get("children");
_.each(childrenIds, function(childId, index){
var childModel = nodesCollection.findWhere({_id: childId});
if(!childModel){childModel= nodesCollection.findWhere({cur_id: childId});}
console.log("childModelId = " + childId);
console.log(childModel);
var tempView = new showView({
+75 -68
View File
@@ -8,80 +8,44 @@ body {
}
/************** Navbar *****************/
.navbar-primary .navbar {
border-bottom:none;
}
.navbar-primary .navbar .navbar-brand {
color:#fff;
}
.navbar-nav.navbar-right:last-child {
margin-right: 0;
.myNav{
position: fixed;
top: 0px;
width: 100%;
height: 26px;
background-color: black;
z-index: 9999;
}
/* Navbar-xs */
.navbar-xs .navbar-primary .navbar {
min-height:36px;
.btn{
height: 20px;
width: 10px;
}
/* Sidebar Link */
.navbar-xs a.navbar-left {
margin: 7px;
margin-left: 10px;
ul.navbar-right {
list-style-type: none;
}
.navbar-xs a i {
margin: 0;
.navbar-right > li {
position: relative;
float: left;
/*border*/
}
/* Navbar-Brand */
.navbar-xs .navbar-primary .navbar .navbar-brand {
padding: 8px 8px;
font-size: 16px;
line-height: 18px;
.navbar-right > li > a{
display: block;
padding: 2px 20px;
}
.navbar-xs .navbar-brand {
margin-top: 2px;
}
/* Search Form */
.navbar-xs .navbar-form {
height: 28px;
margin-top: 4px;
margin-bottom: 0;
padding-right: 6px;
}
.navbar-xs .navbar-form .form-control {
height: 28px;
/*padding: 0 12px;*/
font-size: 14px;
line-height: 1.42857;
border-radius: 3px;
}
/* Star Button */
.navbar-xs .btn {
min-width: 27px;
width: 27px;
height: 28px;
margin-top: 4px;
}
.navbar-xs .btn i {
margin-left: -7px;
margin-top: -2px;
}
/* Navbar Nav */
.navbar-xs ul {
margin: 2px
}
.navbar-xs .navbar-primary .navbar .navbar-nav > li > a {
padding-top: 7px;
padding-bottom: 7px;
line-height: 18px;
}
.navbar-xs a img {
height: 16px;
margin-top: 0px;
.navbar-right > li:hover > a {
background: grey;
}
/************* End Navbar **************/
/*************** Sidebar ***************/
.sidebar .list-group-item {
/*.sidebar .list-group-item {
position: relative;
display: block;
padding-top: 6px;
@@ -102,18 +66,61 @@ body {
.sidebar .list-group-item-text {
margin-left: 4px;
}*/
#metaSidebar{
position: fixed;
height: 100%;
top: 60px;
left: 15px;
width: 200px;
background-color: beige;
z-index: 999999;
}
#metaSidebar.hideBar{
display: none;
}
.nodeAttributes{
background-color: rgb(147, 213, 202);
width: 100%;
height: 100px;
}
.nodeAttributes > ul {
list-style-type: none;
}
.revHistory{
background-color: rgb(48, 127, 189);
width: 100%;
height: 100px;
}
.revHistory > a{
color: white
}
#revTimestamps > li > a{
color: white;
}
/************* End Sidebar *************/
/**************** Panel ****************/
/*.main-container {
margin-top: 60px;
margin-bottom: 30px;
}*/
.floatRight{
float: right;
margin-top: 40px;
width: 760px;
}
.centering{
width: 800px;
width: 760px;
margin: auto;
margin-top: 40px;
}
.panel {
+32 -35
View File
@@ -16,6 +16,7 @@
<script src="js/libs/myLib/removeNode.js"></script>
<script src="js/libs/myLib/KeyBoardLogic.js"></script>
<script src="js/libs/myLib/moveNode.js"></script>
<script src="js/libs/myLib/revControl.js"></script>
<script src="js/app.js"></script>
@@ -51,44 +52,40 @@
<body>
<p><br /><span class="math"><em>x</em><sup>2</sup></span><br /></p>
<div class='myNav'>
<a class='logoLink'>CoNote</a>
<input type='text'>
<a class='btn btn-default'><i class='icon-star'></i></a>
<div class="navbar-xs">
<div class="navbar-primary">
<nav class="navbar navbar-inverse navbar-fixed-top" role="navigation">
<div class="navbar-header">
<a class="navbar-left" href="sidebar.html"><i class="icon-list"></i></a>
<a class="navbar-brand" href="index.html">Metablock</a>
</div>
<form class="navbar-form navbar-left" role="search">
<div class="form-group">
<input type="text" class="form-control" placeholder="Search">
</div>
</form>
<a class="btn btn-default" href=""><i class="icon-star"></i></a>
<ul class="nav navbar-nav navbar-right">
<li><a class='transclude'>Transclude/alias</a></li>
<!-- <li><a href="index.html"><img src="img/splitscreen.png"></a></li> -->
<li><a class='splitScreen'><img src="img/splitscreen.png"></a></li>
<li><a href=""><img src="img/undo.png"></a></li>
<li><a href=""><img src="img/redo.png"></a></li>
<li><a id="markdownButton">x<sup>2</sup></a></li>
<li class="dropdown">
<a href="" class="dropdown-toggle" data-toggle="dropdown">Dropdown <b class="caret"></b></a>
<ul class="dropdown-menu">
<li><a href="">Action</a></li>
<li><a href="">Another action</a></li>
<li class="divider"></li>
<li><a href="">Separated link</a></li>
<li><a href="/auth/google">Sign In with Google</a></li>
</ul>
</li>
</ul>
</nav>
</div>
<ul class='navbar-right'>
<li><a class='toggleSidebar'>Toggle sideBar</a></li>
<li><a href='/auth/google'>Sign In With Google</a></li>
<li><a id='COMMIT'>Make Commit!</a></li>
<!-- <li><a><img src='img/undo.png'></a></li>
<li><a><img src='img/redo.png'></a></li> -->
<li><a id='markdownButton'>x<sup>2</sup></a></li>
</ul>
</div>
<div id='metaSidebar'>
<div class='nodeAttributes'>
Node Attributes
<ul>
<li>author: you</li>
<li>timeStamp: now</li>
<li>etc</li>
</ul>
</div>
<div class='revHistory'>
<a class='getRevHistory'>Get revHistory</a>
<ul id='revTimestamps'>
</ul>
</div>
</div>
<div class='centering'>
<div id='mainPanel' class='floatRight'>
<div id="pathDiv">PathDIV</div>
<div class='main main1 panel'>
<ul id="0" class='root subList dd-list' ></ul>