diff --git a/app.js b/app.js index a07c7da..355ef7f 100644 --- a/app.js +++ b/app.js @@ -8,7 +8,10 @@ var passport = require('passport'); var flash = require('connect-flash'); require('./config/passport')(passport); // pass passport for configuration +require("./models/Node.js"); +//This isn't used in this file, but calling it before helper lib avoids circularDependencies. var helperLib = require('./lib/helperLib.js'); +console.log(helperLib); helperLib.safeConnectToDB(); diff --git a/lib/MessageQueue.js b/lib/MessageQueue.js new file mode 100644 index 0000000..ece23e2 --- /dev/null +++ b/lib/MessageQueue.js @@ -0,0 +1,90 @@ +var Node = require("../models/Node.js"); + +var MessageQueue = []; + +module.exports.iterateQueue = function(){ + // console.log("iteratingQueue"); + MessageQueue.shift(); //remove the finished operation. + if(MessageQueue.length == 0){return;} + + var nextOperation = MessageQueue[0]; + executeOperation(nextOperation); +} + +//queueLength needs to be != 0 if there's an operation occuring. +module.exports.queueOrDo = function(operation){ + if(MessageQueue.length == 0){ + MessageQueue.push(operation); + executeOperation(operation); + } + else{ + // console.log("QUEUE BEING USED!!!"); + MessageQueue.push(operation); + } +} + + + + + + +function executeOperation(operation){ + var type = operation[0]; + var data = operation[1]; + var socket = operation[2]; + + switch(type){ + case "newNode": + addNode(data, socket); break; + case "removeNode": + removeNode(data, socket); break; + case "movedNode": + moveNode(data, socket); break; + default: + break; + } +} + + +function moveNode(data, socket){ + // var ids = [thisModel.get("_id"), oldParModel.get("_id"), newParModel.get("_id")]; + // var arrays = [thisModel.get("parents"), oldParModel.get("children"), newParModel.get("children")]; + // var indices = [dragIndex, dropIndex]; + // var data = [ids, arrays, indices, CurrentUser]; + + Node.moveNode(data[0], data[1],data[3]._id); +} + + +function removeNode(data, socket){ + var thisId = data[0]; + var thisIndex = data[1]; + var parId = data[2]; + var author = data[3]; + // data[3] = null; //improve efficiency. + + Node.removeNode(thisId, thisIndex, parId, author._id); +} + + +//need to broadcast parentArray. +function addNode(data, socket){ + var modelJson = data[1]; //(includes the negative ID to find later); + var parId = data[0][0]; + var newIndex = data[0][1]; + var Author = data[2]; + + var now = Date.now(); + + var callback = function(err, instance, now){ + socket.emit("updateReceived", [modelJson._id ,instance, data[0]]); + socket.broadcast.emit("newNode", [ [parId,newIndex] , instance ]); + + + Node.updateParent(parId, instance._id ,newIndex, now ); + } + + // console.log("newNode"); + Node.addNode(modelJson.text, modelJson.children, modelJson.parents, modelJson.author._id ,callback); + +} diff --git a/lib/helperLib.js b/lib/helperLib.js index a5add79..8578a9a 100644 --- a/lib/helperLib.js +++ b/lib/helperLib.js @@ -14,8 +14,14 @@ var lastExchangeData = {}; var makeCommit = require("./makeCommit.js").makeCommit; var getAndSendRevHistory = require("./revAlgorithm.js").getAndSendRevHistory; +var MessageQueue =require("./MessageQueue.js"); +console.log("MessageQueue"); +console.log(MessageQueue); + module.exports = { + iterateQueue: MessageQueue.iterateQueue, + safeConnectToDB: function(){ var url = require("../config/config.js").DB_URL; try { mongoose.connect(url); } @@ -109,56 +115,21 @@ function attachLogInListeners(socket){ // }); - - socket.on("newNode", function(data){ - var modelJson = data[1]; //(includes the negative ID to find later); - var parId = data[0][0]; - var newIndex = data[0][1]; - var Author = data[2]; - - var now = Date.now(); - - var callback = function(err, instance, now){ - socket.emit("updateReceived", [modelJson._id ,instance, data[0]]); - socket.broadcast.emit("newNode", [ [parId,newIndex] , instance ]); - - - Node.updateParent(parId, instance._id ,newIndex, now ); - } - - console.log("newNode"); - Node.addNode(modelJson.text, modelJson.children, modelJson.parents, modelJson.author._id ,callback); - - }); + //maybe emit sooner... //need to broadcast parentArray. + socket.on("newNode", function(data){ + MessageQueue.queueOrDo(['newNode', data, socket]) + }); + socket.on("removeNode", function(data){ - var thisId = data[0]; - var thisIndex = data[1]; - var parId = data[2]; - var author = data[3]; - // data[3] = null; //improve efficiency. socket.broadcast.emit("removeNode", data); - Node.removeNode(thisId, thisIndex, parId, author._id); - + MessageQueue.queueOrDo(['removeNode', data, socket]) }); socket.on("movedNode", function(data){ - // var ids = [thisModel.get("_id"), oldParModel.get("_id"), newParModel.get("_id")]; - // var arrays = [thisModel.get("parents"), oldParModel.get("children"), newParModel.get("children")]; - // var indices = [dragIndex, dropIndex]; - // var data = [ids, arrays, indices, CurrentUser]; - - Node.moveNode(data[0], data[1],data[3]._id); socket.broadcast.emit("movedNode", [data[0], data[2]]); - - - + MessageQueue.queueOrDo(['movedNode', data, socket]) }); - socket.on("getTimeHash", function(){ - var timeHash = getTimeHash(); - socket.emit("timeHash", timeHash); - }) ; } - diff --git a/models/Node.js b/models/Node.js index e2935bd..0486123 100644 --- a/models/Node.js +++ b/models/Node.js @@ -72,73 +72,6 @@ function setUpDB(){ }); } - - - - - - - - -function moveNode(ids, arrays, authorId){ - var now = Date.now(); - - var thisId = ids[0]; //draggedId - MyNode.findById(thisId, null, function(err, node){ - node.author = authorId; // - 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, authorId){ - 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.author = authorId; - delNode.timestamp = now; - if(delNode.parents.length ==1 ){//(this if statement is technically redundant) - // delNode.remove(); - delNode.parents = []; //(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(delNode.parents, parId); - delNode.save(); - } - }) -} - function updateText(id, newText, authorId){ MyNode.findById(id, null, function(err, node){ if(err || node == null){ @@ -156,6 +89,90 @@ function updateText(id, newText, authorId){ + + + + + + +function moveNode(ids, arrays, authorId){ + var now = Date.now(); + var numCallbacks = 0; + + var thisId = ids[0]; //draggedId + MyNode.findById(thisId, null, function(err, node){ + node.author = authorId; // + node.timestamp = now; + node.parents = arrays[0]; + node.save(parallelExecutionHelper); + }); + + var oldParId = ids[1]; + MyNode.findById(oldParId, null, function(err, node){ + node.timestamp = now; + node.children = arrays[1]; + node.save(parallelExecutionHelper); + }); + + var newParId = ids[2]; + MyNode.findById(newParId, null, function(err, node){ + node.timestamp = now; + node.children = arrays[2]; + node.save(parallelExecutionHelper); + }); + + + function parallelExecutionHelper(){ + numCallbacks++; + if(numCallbacks==3){ + console.log("FINISHED- MoveNode,ParallelExecutionHelper") + helperLib.iterateQueue(); + numCallbacks=0; + } + } +} + +function removeNode(thisId, thisIndex, parId, authorId){ + 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(function(){helperLib.iterateQueue()}); + }); + + MyNode.findById(thisId, null, function(err, delNode){ + if(err || delNode == null){ + return; + } + delNode.author = authorId; + delNode.timestamp = now; + if(delNode.parents.length ==1 ){//(this if statement is technically redundant) + // delNode.remove(); + delNode.parents = []; //(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(delNode.parents, parId); + delNode.save(); + } + + }); + + +} + + + + + + function updateParent(parId, newId ,newIndex,now){ MyNode.findById(parId, null, function(err, parNode){ if(err || parNode == null){ @@ -166,6 +183,7 @@ function updateParent(parId, newId ,newIndex,now){ parNode.save(); }) } + //add Node to the DB. function addNode(text, children, parents, authorId, callback){ var instance = new MyNode(); @@ -181,6 +199,7 @@ function addNode(text, children, parents, authorId, callback){ callback(err); } else { + helperLib.iterateQueue(); callback(null, instance); } }); @@ -206,3 +225,8 @@ Array.prototype.remove = function(from, to) { this.length = from < 0 ? this.length + from : from; return this.push.apply(this, rest); }; + + +var helperLib = require("../lib/helperLib.js"); +console.log("HELPERLIB"); +console.log(helperLib); \ No newline at end of file diff --git a/public/js/app.js b/public/js/app.js index bc14f8b..7c80d2a 100644 --- a/public/js/app.js +++ b/public/js/app.js @@ -1,5 +1,6 @@ $(function(){ //alert("jquery works"); + INPUT_PROCESSED = true; $("#COMMIT").click(function(){ @@ -73,15 +74,15 @@ $(function(){ var that = this; voInitializer(that, event); var id = $(event.target).closest("li").attr("data-id"); + keydownHandler(event); - socket.emit("editing", [id, CurrentUser._id]); + socket.emit("editing", [id, CurrentUser.google.name]); }); $("body").on("blur", "textarea", function(event){ var thisLI = $(event.target).closest("li"); var id = thisLI.attr("data-id"); var text = thisLI.children().children("textarea").val(); $("textarea").textareaAutoExpand(); - socket.emit("blurred", [id, text, CurrentUser]); }); @@ -143,6 +144,18 @@ function hasDuplicates(array) { return false; } +Array.prototype.removeOne = function(parId){ + var parIndex = this.indexOf(parId); + this.remove(parIndex); +} + + + + + + + + voInitializer = function(that, event){ //var that = this; vo = {}; @@ -200,13 +213,33 @@ voInitializer = function(that, event){ } + + + + + + + + + + + + + keydownHandler = function(event){ //the entire body is wrapped in this. var that = this; + console.log("keyDownHandler"); // - if(event.which == undefined){ return; } + if(event.which == undefined){ console.log("ABORTED- event.which==undefined"); return; } if(!CurrentUser){ alert("only logged in users can edit"); return;}//prevent non-logged in users from editing. - + + if(!INPUT_PROCESSED){console.log("ABORT- INPUT_PROCESSED=false"); return;}//hitting-enter too quickly causes bugs. Need to make sure nodes have been added. + if(event.which == 13){event.preventDefault();} + voInitializer(that, event); + console.log("ABOUT TO PROCESS INPUT"); + + INPUT_PROCESSED = false; //event.preventDefault(); //http://stackoverflow.com/questions/20964729/run-keydown-event-handler-after-the-value-of-a-textarea-has-been-changed @@ -218,6 +251,8 @@ keydownHandler = function(event){ //the entire body is wrapped in this. _.each(vo.thisModel.get("views"), function(view){ view.updateText() }); + INPUT_PROCESSED = true; + return; } if(vo.hitEnter){ @@ -266,7 +301,7 @@ keydownHandler = function(event){ //the entire body is wrapped in this. if(vo.hitBack && vo.empty){ event.preventDefault(); removeNode(vo); - + return; }//hitBack. // // START ON HIT TAB @@ -332,14 +367,7 @@ if((vo.hitTab && event.shiftKey) || (event.keyCode == 37 && event.shiftKey)){// -} - - - -Array.prototype.removeOne = function(parId){ - var parIndex = this.indexOf(parId); - this.remove(parIndex); -} +}//keyboardHandler diff --git a/public/js/libs/myLib/KeyboardLogic.js b/public/js/libs/myLib/KeyboardLogic.js index a75e4a4..5a3607b 100644 --- a/public/js/libs/myLib/KeyboardLogic.js +++ b/public/js/libs/myLib/KeyboardLogic.js @@ -1,60 +1,18 @@ -// voInitializer = function(that, event){ - -// vo = {}; -// vo.hitEnter = (event.which == 13); -// vo.hitTab = (event.which ==9); -// vo.atEnd = ( $(that).getSelection().end == $(that).val().length); -// vo.atBeg = ( $(that).getSelection().start == 0); -// //cursor = $(this).getSelection().start; -// vo.hitBack = (event.which ==8); -// vo.empty = ($(that).val().length ==0); - -// vo.rootLevel = $(that).closest("ul").is(".root") -// vo.lastBullet = ( $(that).closest("li").is(":first-child") && vo.rootLevel); -// vo.thisLI = $(event.target).closest("li"); -// vo.thisId = vo.thisLI.attr("data-id"); //data-id. -// vo.thisIndex = vo.thisLI.index(); //returns -1 if there's no match. -// vo.thisModel = nodesCollection.findWhere({_id: vo.thisId}); - -// vo.siblingLI = vo.thisLI.prev(); -// vo.siblingIndex = vo.siblingLI.index(); -// vo.siblingId = vo.siblingLI.attr("data-id"); -// vo.siblingModel = nodesCollection.findWhere({_id: vo.siblingId}); - -// console.log(nodesCollection); -// console.log(vo.thisModel); - - - - -// if(vo.rootLevel){ -// vo.parentLI = undefined; -// vo.parentId = (vo.thisLI.closest("ul").attr("data-id")) -// vo.grandParentId = undefined; // won't matter since outTab prevents it. //unless programattic. -// } -// else{ //not root level. -// vo.parentLI = vo.thisLI.parent().closest("li"); -// vo.parentId = (vo.parentLI.attr("data-id")); -// if(vo.parentLI.attr("data-depth") == 0){ //could test this another way. -// vo.grandParentId = vo.parentLI.closest("ul").attr("data-id"); -// } -// else{ -// vo.grandParentId = (vo.parentLI.parent().closest("li").attr('data-id')); -// } -// } -// vo.grandParentModel = nodesCollection.findWhere({_id: vo.grandParentId}); -// vo.parentModel = nodesCollection.findWhere({_id: vo.parentId}); -// } - - // keydownHandler = function(event){ //the entire body is wrapped in this. // var that = this; +// console.log("keyDownHandler"); // // -// if(event.which == undefined){ return; } - +// if(event.which == undefined){ console.log("ABORTED- event.which==undefined"); return; } +// if(!CurrentUser){ alert("only logged in users can edit"); return;}//prevent non-logged in users from editing. + +// if(!INPUT_PROCESSED){console.log("ABORT- INPUT_PROCESSED=false"); return;}//hitting-enter too quickly causes bugs. Need to make sure nodes have been added. +// if(event.which == 13){event.preventDefault();} + // voInitializer(that, event); +// console.log("ABOUT TO PROCESS INPUT"); + +// INPUT_PROCESSED = false; -// //minor-bug // //event.preventDefault(); // //http://stackoverflow.com/questions/20964729/run-keydown-event-handler-after-the-value-of-a-textarea-has-been-changed // //keyupp fixes this, but causes other problems. @@ -65,6 +23,9 @@ // _.each(vo.thisModel.get("views"), function(view){ // view.updateText() // }); +// socket.emit("blurred", [id, text, CurrentUser]); +// INPUT_PROCESSED = true; +// return; // } // if(vo.hitEnter){ @@ -80,6 +41,7 @@ // if(!event.shiftKey){ // event.preventDefault(); + // if(vo.empty){ // addNode(""); // return; @@ -112,15 +74,42 @@ // if(vo.hitBack && vo.empty){ // event.preventDefault(); // removeNode(vo); - +// return; // }//hitBack. +// // // START ON HIT TAB +// // if (vo.hitTab) { + +// // event.preventDefault(); + +// // if (event.shiftKey) { +// // event.preventDefault(); + + +// // if (($(this).parent().parent().parent().hasClass("root"))) { +// // // do nothing. //alert() //IT USED TO BE ID = 'ROOT' // we use 'root SubList' because it has two classes. +// // } +// // else { // OUTDENT!! +// // var newIndex = $(this).parent().parent().parent().closest("li").index(); +// // debugger; +// // moveNode(vo.thisModel, vo.thisIndex, vo.parentModel, vo.grandParentModel, newIndex+1, true); +// // } +// // } +// // var hasAboveSibling = (vo.thisIndex != 0); +// // if(!event.shiftKey && (hasAboveSibling) ){ +// // var newIndex = vo.siblingModel.get("children").length; // no need for a + 1, because 0 index + insert (duh) +// // moveNode(vo.thisModel, vo.thisIndex, vo.parentModel, vo.siblingModel, newIndex, true); +// // } +// // }// END ON HIT TAB // if((vo.hitTab && !event.shiftKey) || (event.keyCode == 39 && event.shiftKey)){ //INDENT // event.preventDefault(); // var hasAboveSibling = (vo.thisIndex != 0); // if(hasAboveSibling){ // var newIndex = vo.siblingModel.get("children").length; // no need for a + 1, because 0 index + insert (duh) // moveNode(vo.thisModel, vo.thisIndex, vo.parentModel, vo.siblingModel, newIndex, true); +// setTimeout(function(){ + +// }, 100); // } // } // if((vo.hitTab && event.shiftKey) || (event.keyCode == 37 && event.shiftKey)){// OUTDENT!! @@ -128,14 +117,17 @@ // // do nothing. //alert() //IT USED TO BE ID = 'ROOT' // we use 'root SubList' because it has two classes. // } // else { + // var newSiblingUL = $(this).parent().parent().parent(); // var newIndex = newSiblingUL.closest("li").index(); // moveNode(vo.thisModel, vo.thisIndex, vo.parentModel, vo.grandParentModel, newIndex+1, true); // setTimeout(function(){ //(MoveNode is asynchronous, so you need to wait a little bit.). // newSiblingUL.parent().next().children().children("textarea").focus(); // }, 100); -// }//if->else -// }//if + + +// } +// } // if(event.keyCode == 38 && event.shiftKey && !vo.thisLI.is(":first-child")) { // moveNode(vo.thisModel, vo.thisIndex, vo.parentModel, vo.parentModel, vo.thisIndex-1, true); @@ -146,19 +138,6 @@ // return; // } -// }//vo-initializer - -// Array.prototype.removeOne = function(parId){ -// var parIndex = this.indexOf(parId); -// this.remove(parIndex); -// } - - - - - - - - +// } \ No newline at end of file diff --git a/public/js/libs/myLib/addNode.js b/public/js/libs/myLib/addNode.js index c90ae5f..24522c7 100644 --- a/public/js/libs/myLib/addNode.js +++ b/public/js/libs/myLib/addNode.js @@ -1,6 +1,4 @@ addNode = function(botStr, topStr){ - // debugger; - var randomId = ( -1 * Math.floor( Math.random() * 100000000) ) var modelJSON = { _id: randomId @@ -27,7 +25,9 @@ addNode = function(botStr, topStr){ vo.parentModel.get("children").insert(vo.thisIndex + 1, randomId); - socket.emit("newNode", [ [vo.parentId, vo.thisIndex+1] , modelJSON ]); + var data = [ [vo.parentId, vo.thisIndex+1] , modelJSON ]; + console.log("AddNode DATA"); console.log(data); + socket.emit("newNode", data); var parentViews = vo.parentModel.get("views"); @@ -36,6 +36,8 @@ addNode = function(botStr, topStr){ parentView.addNode(newNode, tempIndex, true); }); vo.thisLI.next().children().children("textarea").focus(); + INPUT_PROCESSED=true; + console.log("FINISHED- ADD NODE") } //topStr is to the left. (also, the bottom part will be to the right. ) @@ -79,7 +81,7 @@ transclude = function(){ }); vo.thisLI.next().children().children("textarea").focus(); - + INPUT_PROCESSED=true; } diff --git a/public/js/libs/myLib/moveNode.js b/public/js/libs/myLib/moveNode.js index fe3771b..d69b951 100644 --- a/public/js/libs/myLib/moveNode.js +++ b/public/js/libs/myLib/moveNode.js @@ -32,4 +32,5 @@ var moveNode = function(thisModel, dragIndex, oldParModel, newParModel, dropInde socket.emit("movedNode", data); } + INPUT_PROCESSED=true; } \ No newline at end of file diff --git a/public/js/libs/myLib/removeNode.js b/public/js/libs/myLib/removeNode.js index 6dc7de6..652899e 100644 --- a/public/js/libs/myLib/removeNode.js +++ b/public/js/libs/myLib/removeNode.js @@ -41,4 +41,6 @@ var upDateParentModelViews = function(vo, broadcast){ _.each(vo.parentModel.get("views"), function(parentView){ parentView.removeNode(vo.thisIndex); }); + + INPUT_PROCESSED=true; } \ No newline at end of file