-
-
Save sijie123/26be19e7aba1522b79cef42b75bfed89 to your computer and use it in GitHub Desktop.
gg=G: an extract
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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