Skip to content

Instantly share code, notes, and snippets.

@sijie123
Created December 9, 2018 07:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sijie123/26be19e7aba1522b79cef42b75bfed89 to your computer and use it in GitHub Desktop.
Save sijie123/26be19e7aba1522b79cef42b75bfed89 to your computer and use it in GitHub Desktop.
gg=G: an extract
/**
* Attempts to create a comment on the UI.
*
* @param sess Ace editor session to place comment on.
* @param commentID ID of comment, should be unique to each comment, syncs across all clients
* @param comment the comment object.
*
* @return the new comment variable with local marker object. (Note: marker object syncs across clients but is discarded. Good for 1st time clients only)
*/
function addComment(sess, commentID, comment) {
var selectedRange = comment.range;
let marker = {};
marker.update = function(html, markerLayer, session, config) {
// Use the helper method above to get the marker's HTML as a string (how Ace normally does it)
getMarkerHTML(html, markerLayer, session, config, selectedRange, 'myMarker', commentID);
if (canUpdateJSON) {
console.log("Code text has changed trigger 2!");
canUpdateJSON = false;
writeJSON();
}
};
let markerObject = sess.addDynamicMarker(marker);
console.log(markerObject);
console.log("added markerID: " + markerObject.id);
comment.markerID = markerObject.id;
return comment;
}
function pushReply(id, name, reply) {
if (!(id in commentList)) {
console.log("Not found :(");
return;
}
commentList[id].replies.push({name: name, reply: reply});
writeJSON();
}
/**
* Creates and adds a new comment.
* If comment is from server:
* Will ensure comment is not already present.
* Will update UI to place comment.
* Will update commentList to include the new comment.
*
* If comment is from local:
* Will generate a commentID and comment object.
* Will then update UI to place comment.
* Will update commentList to include the new comment.
* This action will cause re-sync.
*
* @param fromServer whether command initiated from server or local side.
* @param commentID ID of comment if comment is from server, unused otherwise.
* @param comment comment object if comment is from server, unused otherwise.
*/
function pushComment(fromServer, commentID, comment) {
let sess = editor.getSession();
console.log(comment);
if (fromServer) {
let localComment = addComment(sess, commentID, comment);
commentList[commentID] = localComment;
}
else {
// console.log("Pusheen comment");
let selectedRange = sess.getSelection().getRange();
if (selectedRange.isEmpty()) {
alert("No text is selected, aborting");
return;
}
selectedRange.start = sess.doc.createAnchor(selectedRange.start); //Update to dynamic
selectedRange.end = sess.doc.createAnchor(selectedRange.end); //Update to dynamic
let commentID = getRandomID();
var newComment = {
'markerID': null,
'range': selectedRange,
'text': $("#commentText").val(),
'username': $('#commentName').val(),
'replies': []
};
var localComment = addComment(sess, commentID, newComment);
commentList[commentID] = localComment;
$("#commentText").val("");
writeJSON();
}
}
/**
* Parses the JSON retrieved from the sync server.
* Reconstructs and draws a comment object containing highlight range, text
* user and the comment's replies.
*
* @param json the JSON to parse.
*/
function parseJSONComments(json) {
if (json === "") {
console.log("Nothing to parse");
return;
}
try {
var testParse = JSON.parse(json);
}
catch(e) {
console.log("JSON parse error!");
return;
}
try {
var newComments = JSON.parse(json);
let sess = editor.getSession();
for (var key in newComments) {
if (key in commentList) {
commentList[key].replies = newComments[key].replies;
continue;
}
//Reconstruct comment object.
var serverComment = newComments[key];
var commentRange = new Range(
serverComment.range.start.row,
serverComment.range.start.column,
serverComment.range.end.row,
serverComment.range.end.column
);
var startAnchor = sess.doc.createAnchor(commentRange.start); //Update to dynamic
var endAnchor = sess.doc.createAnchor(commentRange.end); //Update to dynamic
commentRange.start = startAnchor;
commentRange.end = endAnchor;
var comment = {
'markerID': null,
'range': commentRange,
'text': serverComment.text,
'username': serverComment.username,
'replies': serverComment.replies || []
}
pushComment(true, key, comment);
}
for (let key in commentList) {
if (! (key in newComments)) {
let sess = editor.getSession();
sess.removeMarker(commentList[key].markerID);
delete commentList[key];
}
}
}
catch (e) {
console.log(json);
console.log(e);
console.log("Couldn't parse properly.");
}
drawAllComments();
}
const fs = require('fs');
const express = require('express');
const cookieParser = require('cookie-parser');
const http = require('http');
const crypto = require('crypto');
var ShareDB = require('sharedb');
const db = require('sharedb-mongo')(DATABASE_CONFIG);
const bodyParser = require("body-parser");
var backend = new ShareDB({db});
var mongoose = require('mongoose');
mongoose.Promise = Promise;
var metaDB = mongoose.connect(DATABASE_CONFIG);
var LiveDoc = require("./liveDoc.js"); //The model
const app = express();
app.use(cookieParser(SECRET_KEY));
app.use(bodyParser.urlencoded({
extended: true
}));
/* Routing section */
app.get('/', (req, res) => {
var file = fs.readFileSync('./static/html/index.html', 'utf8');
res.send(file);
})
app.get('/new', (req, res) => {
createNew(req, "", "", function(url, hash) {
serve(res, url, hash);
});
})
app.get('/view/:id/bundle.js', (req, res) => {
let id = req.params.id;
try {
let workspaceHash = req.signedCookies[id];
let hash = crypto.createHash('md5').update(id).update(SECRET_KEY).digest("hex");
if (workspaceHash === String(hash)) {
// Extend cookie expiry
res.cookie(id, hash, cookieOptions);
res.sendFile("static/js/teacher-bundle.js", { root: __dirname });
return;
}
}
catch(e) {
res.sendFile("static/js/student-bundle.js", { root: __dirname });
return;
}
res.sendFile("static/js/student-bundle.js", { root: __dirname });
})
app.get('/meta/:id', (req, res) => {
getMeta(req, res);
})
app.get('/viewLecturer', (req, res) => {
var file = fs.readFileSync('./static/html/manage.html', 'utf8');
res.send(file);
})
app.post('/fork', (req, res) => {
createNew(req, req.body.code, req.body.originID, function(url, hash) {
serve(res, url, hash);
})
})
app.post('/meta/:id', (req, res) => {
updateMeta(req, res);
})
app.use('/static', express.static('static'));
app.use('/view/:id', express.static('.'));
function createNew(req, code, originID, callback) {
var connection = backend.connect();
var docURL = crypto.randomBytes(8).toString('hex');
var doc = connection.get(docURL, 'textarea');
doc.fetch(function(err) {
if (err)
callback(null);
doc.create({code: code || "", comment: ""}, writeMetaDB(req, docURL, originID, callback));
});
}
function getMeta(req, res) {
// console.log("meta for: " + req.params.id);
LiveDoc.findOne({docURL: req.params.id}).then(result => {
if (result == null) res.send(JSON.stringify({removed: true}));
else res.send(JSON.stringify({
filename: result.filename,
language: result.language,
open: result.open,
description: result.description
}))
}).catch(err => {
res.send("Error");
});
}
function updateMeta(req, res) {
let id = req.params.id;
let workspaceHash = req.signedCookies[id];
let hash = crypto.createHash('md5').update(id).update(SECRET_KEY).digest("hex");
if (workspaceHash === String(hash)) {
let key = req.body.key;
let value = req.body.value;
LiveDoc.findOne({docURL: id}).then(result => {
if (null) {
res.send("Error");
return;
}
result[key] = value;
result.save();
res.send("");
}).catch(err => {
res.send("Error");
});
}
}
function writeMetaDB(req, docURL, originID, callback) {
let hash = crypto.createHash('md5').update(docURL).update(SECRET_KEY).digest("hex");
console.log("generated hash: " + hash);
console.log(docURL);
if (originID === "") {
LiveDoc.create({
filename: "Untitled",
description: "",
docURL: docURL,
profCookie: hash,
open: /*req.params.open || */ true, //true = public, false = private
maxStudentCount: 0,
totalViewCount: 0,
language: "java",
}).then(success => {
callback(docURL, hash)
}, err => {
console.log(err);
callback(null);
})
}
else {
LiveDoc.findOne({docURL: originID}).then(result => {
LiveDoc.create({
filename: result.filename || "Untitled",
description: result.description || "",
docURL: docURL,
profCookie: hash,
open: result.open === undefined ? true : result.open,
maxStudentCount: 0,
totalViewCount: 0,
language: result.language || "java",
}).then(success => {
callback(docURL, hash);
}).catch(err => {
console.log(err);
callback(null);
})
}).catch(err => {
console.log(err);
callback(null);
});
}
}
function serve(res, url, hash) {
if (url === null) {
res.send("Something went wrong... :(");
return;
}
let options = {
maxAge: 31 * 24 * 60 * 60 * 1000, // would expire after 1 month
httpOnly: true, // The cookie only accessible by the web server
signed: true // Indicates if the cookie should be signed
}
// Set cookie
res.cookie(url, hash, options) // options is optional
res.send(url);
}
var server = http.createServer(app);
server.listen(3000);
console.log('Serving client on http://localhost:3000');
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment