diff --git a/app.js b/app.js index d5e97c7..50d8886 100644 --- a/app.js +++ b/app.js @@ -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"){ diff --git a/lib/helperLib.js b/lib/helperLib.js index 4cf0bc3..2afc871 100644 --- a/lib/helperLib.js +++ b/lib/helperLib.js @@ -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]; diff --git a/lib/makeCommit.js b/lib/makeCommit.js new file mode 100644 index 0000000..ff85244 --- /dev/null +++ b/lib/makeCommit.js @@ -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 \ No newline at end of file diff --git a/lib/revAlgorithm.js b/lib/revAlgorithm.js new file mode 100644 index 0000000..559c07a --- /dev/null +++ b/lib/revAlgorithm.js @@ -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(); +} + + + diff --git a/models/Node.js b/models/Node.js index 74476bf..1497895 100644 --- a/models/Node.js +++ b/models/Node.js @@ -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); }; diff --git a/models/Snap.js b/models/Snap.js new file mode 100644 index 0000000..f1a5158 --- /dev/null +++ b/models/Snap.js @@ -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); + } + }); +} \ No newline at end of file diff --git a/public/js/app.js b/public/js/app.js index dbc00c6..8f43d92 100644 --- a/public/js/app.js +++ b/public/js/app.js @@ -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(); }); diff --git a/public/js/collections/nodesCollection.js b/public/js/collections/nodesCollection.js index 52674b8..fd82101 100644 --- a/public/js/collections/nodesCollection.js +++ b/public/js/collections/nodesCollection.js @@ -1,4 +1,4 @@ -var nodesCollection = Backbone.Collection.extend({ +var NodesCollection = Backbone.Collection.extend({ model: NodeModel diff --git a/public/js/libs/myLib/revControl.js b/public/js/libs/myLib/revControl.js new file mode 100644 index 0000000..9337920 --- /dev/null +++ b/public/js/libs/myLib/revControl.js @@ -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; +} \ No newline at end of file diff --git a/public/js/router.js b/public/js/router.js index 5ba226d..b04d370 100644 --- a/public/js/router.js +++ b/public/js/router.js @@ -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 += "
x2