mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2024-11-25 11:16:31 -05:00
Support show last change user with profile and support YAML config inside the note with robots, lang, dir, breaks options
This commit is contained in:
parent
1672df3dce
commit
2ecec3b59a
18 changed files with 546 additions and 167 deletions
|
@ -29,6 +29,7 @@
|
||||||
"handlebars": "~4.0.5",
|
"handlebars": "~4.0.5",
|
||||||
"js-url": "~2.0.2",
|
"js-url": "~2.0.2",
|
||||||
"socket.io-client": "~1.3.7",
|
"socket.io-client": "~1.3.7",
|
||||||
"viz.js": "~1.3.0"
|
"viz.js": "~1.3.0",
|
||||||
|
"js-yaml": "~3.4.6"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
21
lib/note.js
21
lib/note.js
|
@ -26,6 +26,10 @@ var model = mongoose.model('note', {
|
||||||
type: String,
|
type: String,
|
||||||
enum: permissionTypes
|
enum: permissionTypes
|
||||||
},
|
},
|
||||||
|
lastchangeuser: {
|
||||||
|
type: Schema.Types.ObjectId,
|
||||||
|
ref: 'user'
|
||||||
|
},
|
||||||
viewcount: {
|
viewcount: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 0
|
default: 0
|
||||||
|
@ -45,7 +49,8 @@ var note = {
|
||||||
getNoteTitle: getNoteTitle,
|
getNoteTitle: getNoteTitle,
|
||||||
generateWebTitle: generateWebTitle,
|
generateWebTitle: generateWebTitle,
|
||||||
increaseViewCount: increaseViewCount,
|
increaseViewCount: increaseViewCount,
|
||||||
updatePermission: updatePermission
|
updatePermission: updatePermission,
|
||||||
|
updateLastChangeUser: updateLastChangeUser
|
||||||
};
|
};
|
||||||
|
|
||||||
function checkNoteIdValid(noteId) {
|
function checkNoteIdValid(noteId) {
|
||||||
|
@ -198,4 +203,18 @@ function updatePermission(note, permission, callback) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateLastChangeUser(note, lastchangeuser, callback) {
|
||||||
|
note.lastchangeuser = lastchangeuser;
|
||||||
|
note.updated = Date.now();
|
||||||
|
note.save(function (err) {
|
||||||
|
if (err) {
|
||||||
|
logger.error('update note lastchangeuser failed: ' + err);
|
||||||
|
callback(err, null);
|
||||||
|
} else {
|
||||||
|
logger.info("update note lastchangeuser success: " + note.id);
|
||||||
|
callback(null, note);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = note;
|
module.exports = note;
|
124
lib/realtime.js
124
lib/realtime.js
|
@ -9,7 +9,6 @@ var shortId = require('shortid');
|
||||||
var randomcolor = require("randomcolor");
|
var randomcolor = require("randomcolor");
|
||||||
var Chance = require('chance'),
|
var Chance = require('chance'),
|
||||||
chance = new Chance();
|
chance = new Chance();
|
||||||
var md5 = require("blueimp-md5").md5;
|
|
||||||
var moment = require('moment');
|
var moment = require('moment');
|
||||||
|
|
||||||
//core
|
//core
|
||||||
|
@ -68,7 +67,9 @@ function secure(socket, next) {
|
||||||
|
|
||||||
function emitCheck(note) {
|
function emitCheck(note) {
|
||||||
var out = {
|
var out = {
|
||||||
updatetime: note.updatetime
|
updatetime: note.updatetime,
|
||||||
|
lastchangeuser: note.lastchangeuser,
|
||||||
|
lastchangeuserprofile: note.lastchangeuserprofile
|
||||||
};
|
};
|
||||||
realtime.io.to(note.id).emit('check', out);
|
realtime.io.to(note.id).emit('check', out);
|
||||||
/*
|
/*
|
||||||
|
@ -89,18 +90,52 @@ var updater = setInterval(function () {
|
||||||
if (note.server.isDirty) {
|
if (note.server.isDirty) {
|
||||||
if (config.debug)
|
if (config.debug)
|
||||||
logger.info("updater found dirty note: " + key);
|
logger.info("updater found dirty note: " + key);
|
||||||
|
Note.findNote(note.id, function (err, _note) {
|
||||||
|
if (err || !_note) return callback(err, null);
|
||||||
|
//mongo update
|
||||||
|
if (note.lastchangeuser && _note.lastchangeuser != note.lastchangeuser) {
|
||||||
|
var lastchangeuser = note.lastchangeuser;
|
||||||
|
var lastchangeuserprofile = null;
|
||||||
|
User.findUser(lastchangeuser, function (err, user) {
|
||||||
|
if (err) return callback(err, null);
|
||||||
|
if (user && user.profile) {
|
||||||
|
var profile = JSON.parse(user.profile);
|
||||||
|
if (profile) {
|
||||||
|
lastchangeuserprofile = {
|
||||||
|
name: profile.displayName || profile.username,
|
||||||
|
photo: User.parsePhotoByProfile(profile)
|
||||||
|
}
|
||||||
|
note.lastchangeuser = lastchangeuser;
|
||||||
|
note.lastchangeuserprofile = lastchangeuserprofile;
|
||||||
|
Note.updateLastChangeUser(_note, lastchangeuser, function (err, result) {
|
||||||
|
if (err) return callback(err, null);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
note.lastchangeuser = null;
|
||||||
|
note.lastchangeuserprofile = null;
|
||||||
|
Note.updateLastChangeUser(_note, null, function (err, result) {
|
||||||
|
if (err) return callback(err, null);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//postgres update
|
||||||
var body = note.server.document;
|
var body = note.server.document;
|
||||||
var title = Note.getNoteTitle(body);
|
var title = Note.getNoteTitle(body);
|
||||||
title = LZString.compressToBase64(title);
|
title = LZString.compressToBase64(title);
|
||||||
body = LZString.compressToBase64(body);
|
body = LZString.compressToBase64(body);
|
||||||
db.saveToDB(key, title, body, function (err, result) {
|
db.saveToDB(key, title, body, function (err, result) {
|
||||||
if (err) return;
|
if (err) return callback(err, null);
|
||||||
note.server.isDirty = false;
|
note.server.isDirty = false;
|
||||||
note.updatetime = Date.now();
|
note.updatetime = Date.now();
|
||||||
emitCheck(note);
|
emitCheck(note);
|
||||||
|
callback(null, null);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
callback(null, null);
|
||||||
}
|
}
|
||||||
callback();
|
|
||||||
}, function (err) {
|
}, function (err) {
|
||||||
if (err) return logger.error('updater error', err);
|
if (err) return logger.error('updater error', err);
|
||||||
});
|
});
|
||||||
|
@ -121,7 +156,7 @@ var cleaner = setInterval(function () {
|
||||||
disconnectSocketQueue.push(socket);
|
disconnectSocketQueue.push(socket);
|
||||||
disconnect(socket);
|
disconnect(socket);
|
||||||
}
|
}
|
||||||
callback();
|
callback(null, null);
|
||||||
}, function (err) {
|
}, function (err) {
|
||||||
if (err) return logger.error('cleaner error', err);
|
if (err) return logger.error('cleaner error', err);
|
||||||
});
|
});
|
||||||
|
@ -250,7 +285,11 @@ function emitRefresh(socket) {
|
||||||
socket.emit('refresh', {
|
socket.emit('refresh', {
|
||||||
docmaxlength: config.documentmaxlength,
|
docmaxlength: config.documentmaxlength,
|
||||||
owner: note.owner,
|
owner: note.owner,
|
||||||
|
ownerprofile: note.ownerprofile,
|
||||||
|
lastchangeuser: note.lastchangeuser,
|
||||||
|
lastchangeuserprofile: note.lastchangeuserprofile,
|
||||||
permission: note.permission,
|
permission: note.permission,
|
||||||
|
createtime: note.createtime,
|
||||||
updatetime: note.updatetime
|
updatetime: note.updatetime
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -321,11 +360,15 @@ function startConnection(socket) {
|
||||||
isConnectionBusy = false;
|
isConnectionBusy = false;
|
||||||
return logger.error(err);
|
return logger.error(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
var owner = data.rows[0].owner;
|
var owner = data.rows[0].owner;
|
||||||
|
var ownerprofile = null;
|
||||||
var permission = "freely";
|
var permission = "freely";
|
||||||
if (owner && owner != "null") {
|
if (owner && owner != "null") {
|
||||||
permission = "editable";
|
permission = "editable";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//find or new note
|
||||||
Note.findOrNewNote(notename, permission, function (err, note) {
|
Note.findOrNewNote(notename, permission, function (err, note) {
|
||||||
if (err) {
|
if (err) {
|
||||||
responseError(res, "404", "Not Found", "oops.");
|
responseError(res, "404", "Not Found", "oops.");
|
||||||
|
@ -333,21 +376,65 @@ function startConnection(socket) {
|
||||||
isConnectionBusy = false;
|
isConnectionBusy = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var body = LZString.decompressFromBase64(data.rows[0].content);
|
var body = LZString.decompressFromBase64(data.rows[0].content);
|
||||||
//body = LZString.compressToUTF16(body);
|
//body = LZString.compressToUTF16(body);
|
||||||
|
var createtime = data.rows[0].create_time;
|
||||||
var updatetime = data.rows[0].update_time;
|
var updatetime = data.rows[0].update_time;
|
||||||
var server = new ot.EditorSocketIOServer(body, [], notename, ifMayEdit);
|
var server = new ot.EditorSocketIOServer(body, [], notename, ifMayEdit);
|
||||||
|
|
||||||
|
var lastchangeuser = note.lastchangeuser || null;
|
||||||
|
var lastchangeuserprofile = null;
|
||||||
|
|
||||||
notes[notename] = {
|
notes[notename] = {
|
||||||
id: notename,
|
id: notename,
|
||||||
owner: owner,
|
owner: owner,
|
||||||
|
ownerprofile: ownerprofile,
|
||||||
permission: note.permission,
|
permission: note.permission,
|
||||||
|
lastchangeuser: lastchangeuser,
|
||||||
|
lastchangeuserprofile: lastchangeuserprofile,
|
||||||
socks: [],
|
socks: [],
|
||||||
users: {},
|
users: {},
|
||||||
|
createtime: moment(createtime).valueOf(),
|
||||||
updatetime: moment(updatetime).valueOf(),
|
updatetime: moment(updatetime).valueOf(),
|
||||||
server: server
|
server: server
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (lastchangeuser) {
|
||||||
|
//find last change user profile if lastchangeuser exists
|
||||||
|
User.findUser(lastchangeuser, function (err, user) {
|
||||||
|
if (!err && user && user.profile) {
|
||||||
|
var profile = JSON.parse(user.profile);
|
||||||
|
if (profile) {
|
||||||
|
lastchangeuserprofile = {
|
||||||
|
name: profile.displayName || profile.username,
|
||||||
|
photo: User.parsePhotoByProfile(profile)
|
||||||
|
}
|
||||||
|
notes[notename].lastchangeuserprofile = lastchangeuserprofile;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (owner && owner != "null") {
|
||||||
|
//find owner profile if owner exists
|
||||||
|
User.findUser(owner, function (err, user) {
|
||||||
|
if (!err && user && user.profile) {
|
||||||
|
var profile = JSON.parse(user.profile);
|
||||||
|
if (profile) {
|
||||||
|
ownerprofile = {
|
||||||
|
name: profile.displayName || profile.username,
|
||||||
|
photo: User.parsePhotoByProfile(profile)
|
||||||
|
}
|
||||||
|
notes[notename].ownerprofile = ownerprofile;
|
||||||
|
}
|
||||||
|
}
|
||||||
finishConnection(socket, notes[notename], users[socket.id]);
|
finishConnection(socket, notes[notename], users[socket.id]);
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
finishConnection(socket, notes[notename], users[socket.id]);
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
finishConnection(socket, notes[notename], users[socket.id]);
|
finishConnection(socket, notes[notename], users[socket.id]);
|
||||||
|
@ -433,23 +520,7 @@ function updateUserData(socket, user) {
|
||||||
//retrieve user data from passport
|
//retrieve user data from passport
|
||||||
if (socket.request.user && socket.request.user.logged_in) {
|
if (socket.request.user && socket.request.user.logged_in) {
|
||||||
var profile = JSON.parse(socket.request.user.profile);
|
var profile = JSON.parse(socket.request.user.profile);
|
||||||
var photo = null;
|
user.photo = User.parsePhotoByProfile(profile);
|
||||||
switch (profile.provider) {
|
|
||||||
case "facebook":
|
|
||||||
photo = 'https://graph.facebook.com/' + profile.id + '/picture';
|
|
||||||
break;
|
|
||||||
case "twitter":
|
|
||||||
photo = profile.photos[0].value;
|
|
||||||
break;
|
|
||||||
case "github":
|
|
||||||
photo = 'https://avatars.githubusercontent.com/u/' + profile.id + '?s=48';
|
|
||||||
break;
|
|
||||||
case "dropbox":
|
|
||||||
//no image api provided, use gravatar
|
|
||||||
photo = 'https://www.gravatar.com/avatar/' + md5(profile.emails[0].value);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
user.photo = photo;
|
|
||||||
user.name = profile.displayName || profile.username;
|
user.name = profile.displayName || profile.username;
|
||||||
user.userid = socket.request.user._id;
|
user.userid = socket.request.user._id;
|
||||||
user.login = true;
|
user.login = true;
|
||||||
|
@ -480,6 +551,15 @@ function ifMayEdit(socket, callback) {
|
||||||
mayEdit = false;
|
mayEdit = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
//if user may edit and this note have owner (not anonymous usage)
|
||||||
|
if (mayEdit && note.owner && note.owner != "null") {
|
||||||
|
//save for the last change user id
|
||||||
|
if (socket.request.user && socket.request.user.logged_in) {
|
||||||
|
note.lastchangeuser = socket.request.user._id;
|
||||||
|
} else {
|
||||||
|
note.lastchangeuser = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
callback(mayEdit);
|
callback(mayEdit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
112
lib/response.js
112
lib/response.js
|
@ -8,6 +8,7 @@ var markdownpdf = require("markdown-pdf");
|
||||||
var LZString = require('lz-string');
|
var LZString = require('lz-string');
|
||||||
var S = require('string');
|
var S = require('string');
|
||||||
var shortId = require('shortid');
|
var shortId = require('shortid');
|
||||||
|
var metaMarked = require('meta-marked');
|
||||||
|
|
||||||
//core
|
//core
|
||||||
var config = require("../config.js");
|
var config = require("../config.js");
|
||||||
|
@ -15,6 +16,7 @@ var config = require("../config.js");
|
||||||
//others
|
//others
|
||||||
var db = require("./db.js");
|
var db = require("./db.js");
|
||||||
var Note = require("./note.js");
|
var Note = require("./note.js");
|
||||||
|
var User = require("./user.js");
|
||||||
|
|
||||||
//slides
|
//slides
|
||||||
var md = require('reveal.js/plugin/markdown/markdown');
|
var md = require('reveal.js/plugin/markdown/markdown');
|
||||||
|
@ -104,6 +106,13 @@ function responseHackMD(res, noteId) {
|
||||||
responseError(res, "404", "Not Found", "oops.");
|
responseError(res, "404", "Not Found", "oops.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
var body = LZString.decompressFromBase64(data.rows[0].content);
|
||||||
|
var meta = null;
|
||||||
|
try {
|
||||||
|
meta = metaMarked(body).meta;
|
||||||
|
} catch(err) {
|
||||||
|
//na
|
||||||
|
}
|
||||||
var title = data.rows[0].title;
|
var title = data.rows[0].title;
|
||||||
var decodedTitle = LZString.decompressFromBase64(title);
|
var decodedTitle = LZString.decompressFromBase64(title);
|
||||||
if (decodedTitle) title = decodedTitle;
|
if (decodedTitle) title = decodedTitle;
|
||||||
|
@ -116,7 +125,8 @@ function responseHackMD(res, noteId) {
|
||||||
var compiled = ejs.compile(fs.readFileSync(template, 'utf8'), options);
|
var compiled = ejs.compile(fs.readFileSync(template, 'utf8'), options);
|
||||||
var html = compiled({
|
var html = compiled({
|
||||||
title: title,
|
title: title,
|
||||||
useCDN: config.usecdn
|
useCDN: config.usecdn,
|
||||||
|
robots: (meta && meta.robots) || false //default allow robots
|
||||||
});
|
});
|
||||||
var buf = html;
|
var buf = html;
|
||||||
res.writeHead(200, {
|
res.writeHead(200, {
|
||||||
|
@ -192,27 +202,63 @@ function showPublishNote(req, res, next) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var body = LZString.decompressFromBase64(data.rows[0].content);
|
var body = LZString.decompressFromBase64(data.rows[0].content);
|
||||||
|
var meta = null;
|
||||||
|
try {
|
||||||
|
meta = metaMarked(body).meta;
|
||||||
|
} catch(err) {
|
||||||
|
//na
|
||||||
|
}
|
||||||
var updatetime = data.rows[0].update_time;
|
var updatetime = data.rows[0].update_time;
|
||||||
var text = S(body).escapeHTML().s;
|
var text = S(body).escapeHTML().s;
|
||||||
var title = data.rows[0].title;
|
var title = data.rows[0].title;
|
||||||
var decodedTitle = LZString.decompressFromBase64(title);
|
var decodedTitle = LZString.decompressFromBase64(title);
|
||||||
if (decodedTitle) title = decodedTitle;
|
if (decodedTitle) title = decodedTitle;
|
||||||
title = Note.generateWebTitle(title);
|
title = Note.generateWebTitle(title);
|
||||||
|
var origin = config.getserverurl();
|
||||||
|
var data = {
|
||||||
|
title: title,
|
||||||
|
viewcount: note.viewcount,
|
||||||
|
updatetime: updatetime,
|
||||||
|
url: origin,
|
||||||
|
body: text,
|
||||||
|
useCDN: config.usecdn,
|
||||||
|
lastchangeuserprofile: null,
|
||||||
|
robots: (meta && meta.robots) || false //default allow robots
|
||||||
|
};
|
||||||
|
if (note.lastchangeuser) {
|
||||||
|
//find last change user profile if lastchangeuser exists
|
||||||
|
User.findUser(note.lastchangeuser, function (err, user) {
|
||||||
|
if (!err && user && user.profile) {
|
||||||
|
var profile = JSON.parse(user.profile);
|
||||||
|
if (profile) {
|
||||||
|
data.lastchangeuserprofile = {
|
||||||
|
name: profile.displayName || profile.username,
|
||||||
|
photo: User.parsePhotoByProfile(profile)
|
||||||
|
}
|
||||||
|
renderPublish(data, res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
renderPublish(data, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
responseError(res, "404", "Not Found", "oops.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderPublish(data, res) {
|
||||||
var template = config.prettypath;
|
var template = config.prettypath;
|
||||||
var options = {
|
var options = {
|
||||||
cache: !config.debug,
|
cache: !config.debug,
|
||||||
filename: template
|
filename: template
|
||||||
};
|
};
|
||||||
var compiled = ejs.compile(fs.readFileSync(template, 'utf8'), options);
|
var compiled = ejs.compile(fs.readFileSync(template, 'utf8'), options);
|
||||||
var origin = config.getserverurl();
|
var html = compiled(data);
|
||||||
var html = compiled({
|
|
||||||
title: title,
|
|
||||||
viewcount: note.viewcount,
|
|
||||||
updatetime: updatetime,
|
|
||||||
url: origin,
|
|
||||||
body: text,
|
|
||||||
useCDN: config.usecdn
|
|
||||||
});
|
|
||||||
var buf = html;
|
var buf = html;
|
||||||
res.writeHead(200, {
|
res.writeHead(200, {
|
||||||
'Content-Type': 'text/html; charset=UTF-8',
|
'Content-Type': 'text/html; charset=UTF-8',
|
||||||
|
@ -220,12 +266,6 @@ function showPublishNote(req, res, next) {
|
||||||
'Content-Length': buf.length
|
'Content-Length': buf.length
|
||||||
});
|
});
|
||||||
res.end(buf);
|
res.end(buf);
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
responseError(res, "404", "Not Found", "oops.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function actionPublish(req, res, noteId) {
|
function actionPublish(req, res, noteId) {
|
||||||
|
@ -269,36 +309,6 @@ function actionSlide(req, res, noteId) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
//pretty api is deprecated
|
|
||||||
function actionPretty(req, res, noteId) {
|
|
||||||
db.readFromDB(noteId, function (err, data) {
|
|
||||||
if (err) {
|
|
||||||
responseError(res, "404", "Not Found", "oops.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var body = LZString.decompressFromBase64(data.rows[0].content);
|
|
||||||
var text = S(body).escapeHTML().s;
|
|
||||||
var title = data.rows[0].title;
|
|
||||||
var decodedTitle = LZString.decompressFromBase64(title);
|
|
||||||
if (decodedTitle) title = decodedTitle;
|
|
||||||
title = Note.generateWebTitle(title);
|
|
||||||
var template = config.prettypath;
|
|
||||||
var compiled = ejs.compile(fs.readFileSync(template, 'utf8'));
|
|
||||||
var origin = config.getserverurl();
|
|
||||||
var html = compiled({
|
|
||||||
title: title,
|
|
||||||
url: origin,
|
|
||||||
body: text
|
|
||||||
});
|
|
||||||
var buf = html;
|
|
||||||
res.writeHead(200, {
|
|
||||||
'Content-Type': 'text/html; charset=UTF-8',
|
|
||||||
'Cache-Control': 'private',
|
|
||||||
'Content-Length': buf.length
|
|
||||||
});
|
|
||||||
res.end(buf);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function actionDownload(req, res, noteId) {
|
function actionDownload(req, res, noteId) {
|
||||||
db.readFromDB(noteId, function (err, data) {
|
db.readFromDB(noteId, function (err, data) {
|
||||||
|
@ -325,6 +335,11 @@ function actionPDF(req, res, noteId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var body = LZString.decompressFromBase64(data.rows[0].content);
|
var body = LZString.decompressFromBase64(data.rows[0].content);
|
||||||
|
try {
|
||||||
|
body = metaMarked(body).markdown;
|
||||||
|
} catch(err) {
|
||||||
|
//na
|
||||||
|
}
|
||||||
var title = Note.getNoteTitle(body);
|
var title = Note.getNoteTitle(body);
|
||||||
|
|
||||||
if (!fs.existsSync(config.tmppath)) {
|
if (!fs.existsSync(config.tmppath)) {
|
||||||
|
@ -424,6 +439,11 @@ function showPublishSlide(req, res, next) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var body = LZString.decompressFromBase64(data.rows[0].content);
|
var body = LZString.decompressFromBase64(data.rows[0].content);
|
||||||
|
try {
|
||||||
|
body = metaMarked(body).markdown;
|
||||||
|
} catch(err) {
|
||||||
|
//na
|
||||||
|
}
|
||||||
var title = data.rows[0].title;
|
var title = data.rows[0].title;
|
||||||
var decodedTitle = LZString.decompressFromBase64(title);
|
var decodedTitle = LZString.decompressFromBase64(title);
|
||||||
if (decodedTitle) title = decodedTitle;
|
if (decodedTitle) title = decodedTitle;
|
||||||
|
|
34
lib/user.js
34
lib/user.js
|
@ -1,6 +1,7 @@
|
||||||
//user
|
//user
|
||||||
//external modules
|
//external modules
|
||||||
var mongoose = require('mongoose');
|
var mongoose = require('mongoose');
|
||||||
|
var md5 = require("blueimp-md5").md5;
|
||||||
|
|
||||||
//core
|
//core
|
||||||
var config = require("../config.js");
|
var config = require("../config.js");
|
||||||
|
@ -20,9 +21,30 @@ var user = {
|
||||||
findUser: findUser,
|
findUser: findUser,
|
||||||
newUser: newUser,
|
newUser: newUser,
|
||||||
findOrNewUser: findOrNewUser,
|
findOrNewUser: findOrNewUser,
|
||||||
getUserCount: getUserCount
|
getUserCount: getUserCount,
|
||||||
|
parsePhotoByProfile: parsePhotoByProfile
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function parsePhotoByProfile(profile) {
|
||||||
|
var photo = null;
|
||||||
|
switch (profile.provider) {
|
||||||
|
case "facebook":
|
||||||
|
photo = 'https://graph.facebook.com/' + profile.id + '/picture';
|
||||||
|
break;
|
||||||
|
case "twitter":
|
||||||
|
photo = profile.photos[0].value;
|
||||||
|
break;
|
||||||
|
case "github":
|
||||||
|
photo = 'https://avatars.githubusercontent.com/u/' + profile.id + '?s=48';
|
||||||
|
break;
|
||||||
|
case "dropbox":
|
||||||
|
//no image api provided, use gravatar
|
||||||
|
photo = 'https://www.gravatar.com/avatar/' + md5(profile.emails[0].value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return photo;
|
||||||
|
}
|
||||||
|
|
||||||
function getUserCount(callback) {
|
function getUserCount(callback) {
|
||||||
model.count(function(err, count){
|
model.count(function(err, count){
|
||||||
if(err) callback(err, null);
|
if(err) callback(err, null);
|
||||||
|
@ -31,9 +53,13 @@ function getUserCount(callback) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function findUser(id, callback) {
|
function findUser(id, callback) {
|
||||||
model.findOne({
|
var rule = {};
|
||||||
id: id
|
var checkForHexRegExp = new RegExp("^[0-9a-fA-F]{24}$");
|
||||||
}, function (err, user) {
|
if (checkForHexRegExp.test(id))
|
||||||
|
rule._id = id;
|
||||||
|
else
|
||||||
|
rule.id = id;
|
||||||
|
model.findOne(rule, function (err, user) {
|
||||||
if (err) {
|
if (err) {
|
||||||
logger.error('find user failed: ' + err);
|
logger.error('find user failed: ' + err);
|
||||||
callback(err, null);
|
callback(err, null);
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
"lz-string": "1.4.4",
|
"lz-string": "1.4.4",
|
||||||
"markdown-pdf": "^6.0.0",
|
"markdown-pdf": "^6.0.0",
|
||||||
"marked": "^0.3.5",
|
"marked": "^0.3.5",
|
||||||
|
"meta-marked": "^0.4.0",
|
||||||
"method-override": "^2.3.5",
|
"method-override": "^2.3.5",
|
||||||
"moment": "^2.10.6",
|
"moment": "^2.10.6",
|
||||||
"mongoose": "^4.3.1",
|
"mongoose": "^4.3.1",
|
||||||
|
|
|
@ -56,6 +56,7 @@ h6:hover .header-link {
|
||||||
.header-link {
|
.header-link {
|
||||||
position: relative;
|
position: relative;
|
||||||
left: 0.5em;
|
left: 0.5em;
|
||||||
|
right: 0.5em;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
font-size: 0.8em;
|
font-size: 0.8em;
|
||||||
-webkit-transition: opacity 0.2s ease-in-out 0.1s;
|
-webkit-transition: opacity 0.2s ease-in-out 0.1s;
|
||||||
|
@ -114,6 +115,12 @@ h6:hover .header-link {
|
||||||
width: 25vw;
|
width: 25vw;
|
||||||
max-height: 65vh;
|
max-height: 65vh;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
text-align: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-toc-dropdown[dir='rtl'] .nav {
|
||||||
|
padding-right: 0;
|
||||||
|
letter-spacing: 0.0029em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui-toc-dropdown a {
|
.ui-toc-dropdown a {
|
||||||
|
@ -138,6 +145,12 @@ h6:hover .header-link {
|
||||||
border-left: 1px solid black;
|
border-left: 1px solid black;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ui-toc-dropdown[dir='rtl'] .nav>li>a:focus,.ui-toc-dropdown[dir='rtl'] .nav>li>a:hover {
|
||||||
|
padding-right: 19px;
|
||||||
|
border-left: none;
|
||||||
|
border-right: 1px solid black;
|
||||||
|
}
|
||||||
|
|
||||||
.ui-toc-dropdown .nav>.active:focus>a,.ui-toc-dropdown .nav>.active:hover>a,.ui-toc-dropdown .nav>.active>a {
|
.ui-toc-dropdown .nav>.active:focus>a,.ui-toc-dropdown .nav>.active:hover>a,.ui-toc-dropdown .nav>.active>a {
|
||||||
padding-left: 18px;
|
padding-left: 18px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
|
@ -146,6 +159,12 @@ h6:hover .header-link {
|
||||||
border-left: 2px solid black;
|
border-left: 2px solid black;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ui-toc-dropdown[dir='rtl'] .nav>.active:focus>a,.ui-toc-dropdown[dir='rtl'] .nav>.active:hover>a,.ui-toc-dropdown[dir='rtl'] .nav>.active>a {
|
||||||
|
padding-right: 18px;
|
||||||
|
border-left: none;
|
||||||
|
border-right: 2px solid black;
|
||||||
|
}
|
||||||
|
|
||||||
.ui-toc-dropdown .nav .nav {
|
.ui-toc-dropdown .nav .nav {
|
||||||
display: none;
|
display: none;
|
||||||
padding-bottom: 10px;
|
padding-bottom: 10px;
|
||||||
|
@ -163,6 +182,10 @@ h6:hover .header-link {
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ui-toc-dropdown[dir='rtl'] .nav .nav>li>a {
|
||||||
|
padding-right: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
.ui-toc-dropdown .nav .nav>li>ul>li>a {
|
.ui-toc-dropdown .nav .nav>li>ul>li>a {
|
||||||
padding-top: 1px;
|
padding-top: 1px;
|
||||||
padding-bottom: 1px;
|
padding-bottom: 1px;
|
||||||
|
@ -171,24 +194,44 @@ h6:hover .header-link {
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ui-toc-dropdown[dir='rtl'] .nav .nav>li>ul>li>a {
|
||||||
|
padding-right: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
.ui-toc-dropdown .nav .nav>li>a:focus,.ui-toc-dropdown .nav .nav>li>a:hover {
|
.ui-toc-dropdown .nav .nav>li>a:focus,.ui-toc-dropdown .nav .nav>li>a:hover {
|
||||||
padding-left: 29px;
|
padding-left: 29px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ui-toc-dropdown[dir='rtl'] .nav .nav>li>a:focus,.ui-toc-dropdown[dir='rtl'] .nav .nav>li>a:hover {
|
||||||
|
padding-right: 29px;
|
||||||
|
}
|
||||||
|
|
||||||
.ui-toc-dropdown .nav .nav>li>ul>li>a:focus,.ui-toc-dropdown .nav .nav>li>ul>li>a:hover {
|
.ui-toc-dropdown .nav .nav>li>ul>li>a:focus,.ui-toc-dropdown .nav .nav>li>ul>li>a:hover {
|
||||||
padding-left: 39px;
|
padding-left: 39px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ui-toc-dropdown[dir='rtl'] .nav .nav>li>ul>li>a:focus,.ui-toc-dropdown[dir='rtl'] .nav .nav>li>ul>li>a:hover {
|
||||||
|
padding-right: 39px;
|
||||||
|
}
|
||||||
|
|
||||||
.ui-toc-dropdown .nav .nav>.active:focus>a,.ui-toc-dropdown .nav .nav>.active:hover>a,.ui-toc-dropdown .nav .nav>.active>a {
|
.ui-toc-dropdown .nav .nav>.active:focus>a,.ui-toc-dropdown .nav .nav>.active:hover>a,.ui-toc-dropdown .nav .nav>.active>a {
|
||||||
padding-left: 28px;
|
padding-left: 28px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ui-toc-dropdown[dir='rtl'] .nav .nav>.active:focus>a,.ui-toc-dropdown[dir='rtl'] .nav .nav>.active:hover>a,.ui-toc-dropdown[dir='rtl'] .nav .nav>.active>a {
|
||||||
|
padding-right: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
.ui-toc-dropdown .nav .nav>.active>.nav>.active:focus>a,.ui-toc-dropdown .nav .nav>.active>.nav>.active:hover>a,.ui-toc-dropdown .nav .nav>.active>.nav>.active>a {
|
.ui-toc-dropdown .nav .nav>.active>.nav>.active:focus>a,.ui-toc-dropdown .nav .nav>.active>.nav>.active:hover>a,.ui-toc-dropdown .nav .nav>.active>.nav>.active>a {
|
||||||
padding-left: 38px;
|
padding-left: 38px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ui-toc-dropdown[dir='rtl'] .nav .nav>.active>.nav>.active:focus>a,.ui-toc-dropdown[dir='rtl'] .nav .nav>.active>.nav>.active:hover>a,.ui-toc-dropdown[dir='rtl'] .nav .nav>.active>.nav>.active>a {
|
||||||
|
padding-right: 38px;
|
||||||
|
}
|
||||||
|
|
||||||
.ui-affix-toc {
|
.ui-affix-toc {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
@ -216,6 +259,26 @@ h6:hover .header-link {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ui-user-icon {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
display: block;
|
||||||
|
border-radius: 3px;
|
||||||
|
margin-top: 2px;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
margin-right: 5px;
|
||||||
|
background-position: center center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: contain;
|
||||||
|
}
|
||||||
|
.ui-user-icon.small {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
margin: 0 0 0.2em 0;
|
||||||
|
}
|
||||||
|
|
||||||
small span {
|
small span {
|
||||||
line-height: 22px;
|
line-height: 22px;
|
||||||
}
|
}
|
||||||
|
|
2
public/css/html.min.css
vendored
2
public/css/html.min.css
vendored
File diff suppressed because one or more lines are too long
|
@ -156,18 +156,7 @@ body {
|
||||||
.ui-user-name {
|
.ui-user-name {
|
||||||
margin-top: 2px;
|
margin-top: 2px;
|
||||||
}
|
}
|
||||||
.ui-user-icon {
|
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
display: block;
|
|
||||||
border-radius: 3px;
|
|
||||||
margin-top: 2px;
|
|
||||||
margin-bottom: 2px;
|
|
||||||
margin-right: 5px;
|
|
||||||
background-position: center center;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-size: contain;
|
|
||||||
}
|
|
||||||
.ui-user-status {
|
.ui-user-status {
|
||||||
margin-top: 5px;
|
margin-top: 5px;
|
||||||
}
|
}
|
||||||
|
@ -213,7 +202,6 @@ body {
|
||||||
width: 0px;
|
width: 0px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
border-right: none;
|
border-right: none;
|
||||||
transition: left 0.1s, top 0.1s;
|
|
||||||
}
|
}
|
||||||
.dropdown-menu.other-cursor {
|
.dropdown-menu.other-cursor {
|
||||||
transition: none;
|
transition: none;
|
||||||
|
@ -243,6 +231,10 @@ div[contenteditable]:empty:not(:focus):before{
|
||||||
max-height: 80vh;
|
max-height: 80vh;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
.dropdown-menu.list.small {
|
||||||
|
max-height: 40vh;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
.dropdown-menu.list::-webkit-scrollbar {
|
.dropdown-menu.list::-webkit-scrollbar {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
@ -279,6 +271,25 @@ div[contenteditable]:empty:not(:focus):before{
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.info-label {
|
||||||
|
width: 36%;
|
||||||
|
text-align: right;
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: 6px;
|
||||||
|
}
|
||||||
|
.popover {
|
||||||
|
width: 100%;
|
||||||
|
font-family: inherit !important;
|
||||||
|
line-height: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-ellipsis {
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
.cm-trailing-space-a:before,
|
.cm-trailing-space-a:before,
|
||||||
.cm-trailing-space-b:before,
|
.cm-trailing-space-b:before,
|
||||||
.cm-trailing-space-new-line:before {
|
.cm-trailing-space-new-line:before {
|
||||||
|
|
|
@ -36,7 +36,6 @@
|
||||||
text-align: right;
|
text-align: right;
|
||||||
position: relative;
|
position: relative;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
float: right;
|
|
||||||
cursor: default;
|
cursor: default;
|
||||||
z-index: 4;
|
z-index: 4;
|
||||||
padding: 0 8px 0 0;
|
padding: 0 8px 0 0;
|
||||||
|
@ -89,6 +88,18 @@
|
||||||
.markdown-body .flow-chart {
|
.markdown-body .flow-chart {
|
||||||
margin-bottom: 40px;
|
margin-bottom: 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*fixed style for rtl in pre and code*/
|
||||||
|
|
||||||
|
.markdown-body[dir='rtl'] pre {
|
||||||
|
direction: ltr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body[dir='rtl'] code {
|
||||||
|
direction: ltr;
|
||||||
|
unicode-bidi: embed;
|
||||||
|
}
|
||||||
|
|
||||||
svg {
|
svg {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-height: 70vh;
|
max-height: 70vh;
|
||||||
|
|
|
@ -1,15 +1,32 @@
|
||||||
//auto update last change
|
//auto update last change
|
||||||
var lastchangetime = null;
|
var lastchangetime = null;
|
||||||
var lastchangeui = null;
|
var lastchangeui = {
|
||||||
|
time: $(".ui-lastchange"),
|
||||||
|
user: $(".ui-lastchangeuser"),
|
||||||
|
nouser: $(".ui-no-lastchangeuser")
|
||||||
|
}
|
||||||
|
|
||||||
function updateLastChange() {
|
function updateLastChange() {
|
||||||
if (lastchangetime && lastchangeui) {
|
if (lastchangetime && lastchangeui) {
|
||||||
lastchangeui.html(' <i class="fa fa-clock-o"></i> change ' + moment(lastchangetime).fromNow());
|
lastchangeui.time.html(moment(lastchangetime).fromNow());
|
||||||
lastchangeui.attr('title', moment(lastchangetime).format('llll'));
|
lastchangeui.time.attr('title', moment(lastchangetime).format('llll'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setInterval(updateLastChange, 60000);
|
setInterval(updateLastChange, 60000);
|
||||||
|
|
||||||
|
function updateLastChangeUser(data) {
|
||||||
|
if (data.lastchangeuserprofile) {
|
||||||
|
var icon = lastchangeui.user.children('i');
|
||||||
|
icon.attr('title', data.lastchangeuserprofile.name).tooltip('fixTitle');
|
||||||
|
icon.attr('style', 'background-image:url(' + data.lastchangeuserprofile.photo + ')');
|
||||||
|
lastchangeui.user.show();
|
||||||
|
lastchangeui.nouser.hide();
|
||||||
|
} else {
|
||||||
|
lastchangeui.user.hide();
|
||||||
|
lastchangeui.nouser.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//get title
|
//get title
|
||||||
function getTitle(view) {
|
function getTitle(view) {
|
||||||
var h1s = view.find("h1");
|
var h1s = view.find("h1");
|
||||||
|
@ -48,6 +65,57 @@ function slugifyWithUTF8(text) {
|
||||||
return newText;
|
return newText;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//parse meta
|
||||||
|
function parseMeta(md, view, toc, tocAffix) {
|
||||||
|
var robots = null;
|
||||||
|
var lang = null;
|
||||||
|
var dir = null;
|
||||||
|
var breaks = true;
|
||||||
|
if (md && md.meta) {
|
||||||
|
var meta = md.meta;
|
||||||
|
robots = meta.robots;
|
||||||
|
lang = meta.lang;
|
||||||
|
dir = meta.dir;
|
||||||
|
breaks = meta.breaks;
|
||||||
|
}
|
||||||
|
//robots meta
|
||||||
|
var robotsMeta = $('meta[name=robots]');
|
||||||
|
if (robots) {
|
||||||
|
if (robotsMeta.length > 0)
|
||||||
|
robotsMeta.attr('content', robots);
|
||||||
|
else
|
||||||
|
$('head').prepend('<meta name="robots" content="' + robots + '">')
|
||||||
|
}
|
||||||
|
else
|
||||||
|
robotsMeta.remove();
|
||||||
|
//text language
|
||||||
|
if (lang) {
|
||||||
|
view.attr('lang', lang);
|
||||||
|
toc.attr('lang', lang);
|
||||||
|
tocAffix.attr('lang', lang);
|
||||||
|
} else {
|
||||||
|
view.removeAttr('lang');
|
||||||
|
toc.removeAttr('lang');
|
||||||
|
tocAffix.removeAttr('lang');
|
||||||
|
}
|
||||||
|
//text direction
|
||||||
|
if (dir) {
|
||||||
|
view.attr('dir', dir);
|
||||||
|
toc.attr('dir', dir);
|
||||||
|
tocAffix.attr('dir', dir);
|
||||||
|
} else {
|
||||||
|
view.removeAttr('dir');
|
||||||
|
toc.removeAttr('dir');
|
||||||
|
tocAffix.removeAttr('dir');
|
||||||
|
}
|
||||||
|
//breaks
|
||||||
|
if (typeof breaks === 'boolean' && !breaks) {
|
||||||
|
md.options.breaks = false;
|
||||||
|
} else {
|
||||||
|
md.options.breaks = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var viewAjaxCallback = null;
|
var viewAjaxCallback = null;
|
||||||
|
|
||||||
//regex for extra tags
|
//regex for extra tags
|
||||||
|
@ -329,7 +397,10 @@ function exportToHTML(view) {
|
||||||
css: css,
|
css: css,
|
||||||
html: src[0].outerHTML,
|
html: src[0].outerHTML,
|
||||||
toc: toc.html(),
|
toc: toc.html(),
|
||||||
'toc-affix': tocAffix.html()
|
'toc-affix': tocAffix.html(),
|
||||||
|
robots: (md && md.meta && md.meta.robots) ? '<meta name="robots" content="' + md.meta.robots + '">' : null,
|
||||||
|
lang: (md && md.meta && md.meta.lang) ? 'lang="' + md.meta.lang + '"' : null,
|
||||||
|
dir: (md && md.meta && md.meta.dir) ? 'dir="' + md.meta.dir + '"' : null
|
||||||
};
|
};
|
||||||
var html = template(context);
|
var html = template(context);
|
||||||
// console.log(html);
|
// console.log(html);
|
||||||
|
@ -737,6 +808,49 @@ var speakerdeckPlugin = new Plugin(
|
||||||
return div[0].outerHTML;
|
return div[0].outerHTML;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
//yaml meta, from https://github.com/eugeneware/remarkable-meta
|
||||||
|
function get(state, line) {
|
||||||
|
var pos = state.bMarks[line];
|
||||||
|
var max = state.eMarks[line];
|
||||||
|
return state.src.substr(pos, max - pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
function meta(state, start, end, silent) {
|
||||||
|
if (start !== 0 || state.blkIndent !== 0) return false;
|
||||||
|
if (state.tShift[start] < 0) return false;
|
||||||
|
if (!get(state, start).match(/^---$/)) return false;
|
||||||
|
|
||||||
|
var data = [];
|
||||||
|
for (var line = start + 1; line < end; line++) {
|
||||||
|
var str = get(state, line);
|
||||||
|
if (str.match(/^(\.{3}|-{3})$/)) break;
|
||||||
|
if (state.tShift[line] < 0) break;
|
||||||
|
data.push(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line >= end) return false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
md.meta = jsyaml.safeLoad(data.join('\n')) || {};
|
||||||
|
} catch(err) {
|
||||||
|
console.error(err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
state.line = line + 1;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function metaPlugin(md) {
|
||||||
|
md.meta = md.meta || {};
|
||||||
|
md.block.ruler.before('code', 'meta', meta, {
|
||||||
|
alt: []
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
md.use(metaPlugin);
|
||||||
md.use(youtubePlugin);
|
md.use(youtubePlugin);
|
||||||
md.use(vimeoPlugin);
|
md.use(vimeoPlugin);
|
||||||
md.use(gistPlugin);
|
md.use(gistPlugin);
|
||||||
|
|
|
@ -320,6 +320,8 @@ var ui = {
|
||||||
},
|
},
|
||||||
infobar: {
|
infobar: {
|
||||||
lastchange: $(".ui-lastchange"),
|
lastchange: $(".ui-lastchange"),
|
||||||
|
lastchangeuser: $(".ui-lastchangeuser"),
|
||||||
|
nolastchangeuser: $(".ui-no-lastchangeuser"),
|
||||||
permission: {
|
permission: {
|
||||||
permission: $(".ui-permission"),
|
permission: $(".ui-permission"),
|
||||||
label: $(".ui-permission-label"),
|
label: $(".ui-permission-label"),
|
||||||
|
@ -387,9 +389,9 @@ function setHaveUnreadChanges(bool) {
|
||||||
function updateTitleReminder() {
|
function updateTitleReminder() {
|
||||||
if (!loaded) return;
|
if (!loaded) return;
|
||||||
if (haveUnreadChanges) {
|
if (haveUnreadChanges) {
|
||||||
document.title = '• ' + renderTitle(ui.area.view);
|
document.title = '• ' + renderTitle(ui.area.markdown);
|
||||||
} else {
|
} else {
|
||||||
document.title = renderTitle(ui.area.view);
|
document.title = renderTitle(ui.area.markdown);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -465,6 +467,8 @@ $(document).ready(function () {
|
||||||
upClass: 'navbar-hide',
|
upClass: 'navbar-hide',
|
||||||
downClass: 'navbar-show'
|
downClass: 'navbar-show'
|
||||||
});
|
});
|
||||||
|
//tooltip
|
||||||
|
$('[data-toggle="tooltip"]').tooltip();
|
||||||
});
|
});
|
||||||
//when page resize
|
//when page resize
|
||||||
$(window).resize(function () {
|
$(window).resize(function () {
|
||||||
|
@ -1165,8 +1169,8 @@ socket.on('version', function (data) {
|
||||||
});
|
});
|
||||||
socket.on('check', function (data) {
|
socket.on('check', function (data) {
|
||||||
lastchangetime = data.updatetime;
|
lastchangetime = data.updatetime;
|
||||||
lastchangeui = ui.infobar.lastchange;
|
|
||||||
updateLastChange();
|
updateLastChange();
|
||||||
|
updateLastChangeUser(data);
|
||||||
});
|
});
|
||||||
socket.on('permission', function (data) {
|
socket.on('permission', function (data) {
|
||||||
updatePermission(data.permission);
|
updatePermission(data.permission);
|
||||||
|
@ -1182,8 +1186,8 @@ socket.on('refresh', function (data) {
|
||||||
owner = data.owner;
|
owner = data.owner;
|
||||||
updatePermission(data.permission);
|
updatePermission(data.permission);
|
||||||
lastchangetime = data.updatetime;
|
lastchangetime = data.updatetime;
|
||||||
lastchangeui = ui.infobar.lastchange;
|
|
||||||
updateLastChange();
|
updateLastChange();
|
||||||
|
updateLastChangeUser(data);
|
||||||
if (!loaded) {
|
if (!loaded) {
|
||||||
changeMode(currentMode);
|
changeMode(currentMode);
|
||||||
loaded = true;
|
loaded = true;
|
||||||
|
@ -1884,15 +1888,18 @@ var lastResult = null;
|
||||||
function updateViewInner() {
|
function updateViewInner() {
|
||||||
if (currentMode == modeType.edit || !isDirty) return;
|
if (currentMode == modeType.edit || !isDirty) return;
|
||||||
var value = editor.getValue();
|
var value = editor.getValue();
|
||||||
|
md.meta = {};
|
||||||
|
md.render(value); //only for get meta
|
||||||
|
parseMeta(md, ui.area.markdown, $('#toc'), $('#toc-affix'));
|
||||||
var result = postProcess(md.render(value)).children().toArray();
|
var result = postProcess(md.render(value)).children().toArray();
|
||||||
partialUpdate(result, lastResult, ui.area.markdown.children().toArray());
|
partialUpdate(result, lastResult, ui.area.markdown.children().toArray());
|
||||||
if (result && lastResult && result.length != lastResult.length)
|
if (result && lastResult && result.length != lastResult.length)
|
||||||
updateDataAttrs(result, ui.area.markdown.children().toArray());
|
updateDataAttrs(result, ui.area.markdown.children().toArray());
|
||||||
lastResult = $(result).clone();
|
lastResult = $(result).clone();
|
||||||
finishView(ui.area.view);
|
finishView(ui.area.markdown);
|
||||||
autoLinkify(ui.area.view);
|
autoLinkify(ui.area.markdown);
|
||||||
deduplicatedHeaderId(ui.area.view);
|
deduplicatedHeaderId(ui.area.markdown);
|
||||||
renderTOC(ui.area.view);
|
renderTOC(ui.area.markdown);
|
||||||
generateToc('toc');
|
generateToc('toc');
|
||||||
generateToc('toc-affix');
|
generateToc('toc-affix');
|
||||||
generateScrollspy();
|
generateScrollspy();
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
var markdown = $(".markdown-body");
|
var markdown = $(".markdown-body");
|
||||||
var text = $('<textarea/>').html(markdown.html()).text();
|
var text = $('<textarea/>').html(markdown.html()).text();
|
||||||
|
md.meta = {};
|
||||||
|
md.render(text); //only for get meta
|
||||||
|
parseMeta(md, markdown, $('#toc'), $('#toc-affix'));
|
||||||
var result = postProcess(md.render(text));
|
var result = postProcess(md.render(text));
|
||||||
markdown.html(result.html());
|
markdown.html(result.html());
|
||||||
$(document.body).show();
|
$(document.body).show();
|
||||||
|
@ -10,8 +13,7 @@ renderTOC(markdown);
|
||||||
generateToc('toc');
|
generateToc('toc');
|
||||||
generateToc('toc-affix');
|
generateToc('toc-affix');
|
||||||
smoothHashScroll();
|
smoothHashScroll();
|
||||||
lastchangetime = $('.ui-lastchange').text();
|
lastchangetime = lastchangeui.time.text();
|
||||||
lastchangeui = $('.ui-lastchange');
|
|
||||||
updateLastChange();
|
updateLastChange();
|
||||||
var url = window.location.pathname;
|
var url = window.location.pathname;
|
||||||
$('.ui-edit').attr('href', url + '/edit');
|
$('.ui-edit').attr('href', url + '/edit');
|
||||||
|
@ -68,6 +70,8 @@ $(window).resize(function () {
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
windowResize();
|
windowResize();
|
||||||
generateScrollspy();
|
generateScrollspy();
|
||||||
|
//tooltip
|
||||||
|
$('[data-toggle="tooltip"]').tooltip();
|
||||||
});
|
});
|
||||||
|
|
||||||
function scrollToTop() {
|
function scrollToTop() {
|
||||||
|
|
|
@ -5,7 +5,12 @@
|
||||||
<div class="ui-view-area">
|
<div class="ui-view-area">
|
||||||
<div class="ui-infobar container-fluid unselectable hidden-print">
|
<div class="ui-infobar container-fluid unselectable hidden-print">
|
||||||
<small>
|
<small>
|
||||||
|
<span>
|
||||||
|
<span class="ui-lastchangeuser"> <i class="ui-user-icon small" data-toggle="tooltip" data-placement="right"></i></span>
|
||||||
|
<span class="ui-no-lastchangeuser"> <i class="fa fa-clock-o"></i></span>
|
||||||
|
<span class="text-uppercase">changed</span>
|
||||||
<span class="ui-lastchange text-uppercase"></span>
|
<span class="ui-lastchange text-uppercase"></span>
|
||||||
|
</span>
|
||||||
<span class="ui-permission dropdown pull-right">
|
<span class="ui-permission dropdown pull-right">
|
||||||
<a id="permissionLabel" class="ui-permission-label text-uppercase" data-target="#" data-toggle="dropdown" href="#" role="button" aria-haspopup="true" aria-expanded="false">
|
<a id="permissionLabel" class="ui-permission-label text-uppercase" data-target="#" data-toggle="dropdown" href="#" role="button" aria-haspopup="true" aria-expanded="false">
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
<script src="//cdnjs.cloudflare.com/ajax/libs/gsap/1.18.0/TweenMax.min.js" defer></script>
|
<script src="//cdnjs.cloudflare.com/ajax/libs/gsap/1.18.0/TweenMax.min.js" defer></script>
|
||||||
<script src="//cdnjs.cloudflare.com/ajax/libs/gsap/1.18.0/jquery.gsap.min.js" defer></script>
|
<script src="//cdnjs.cloudflare.com/ajax/libs/gsap/1.18.0/jquery.gsap.min.js" defer></script>
|
||||||
<script src="//cdnjs.cloudflare.com/ajax/libs/socket.io/1.3.7/socket.io.min.js" defer></script>
|
<script src="//cdnjs.cloudflare.com/ajax/libs/socket.io/1.3.7/socket.io.min.js" defer></script>
|
||||||
|
<script src="//cdnjs.cloudflare.com/ajax/libs/js-yaml/3.4.6/js-yaml.min.js" defer></script>
|
||||||
<% } else { %>
|
<% } else { %>
|
||||||
<script src="/vendor/spin.js/spin.min.js" defer></script>
|
<script src="/vendor/spin.js/spin.min.js" defer></script>
|
||||||
<script src="/vendor/jquery/dist/jquery.min.js"></script>
|
<script src="/vendor/jquery/dist/jquery.min.js"></script>
|
||||||
|
@ -14,6 +15,7 @@
|
||||||
<script src="/vendor/gsap/src/minified/TweenMax.min.js" defer></script>
|
<script src="/vendor/gsap/src/minified/TweenMax.min.js" defer></script>
|
||||||
<script src="/vendor/gsap/src/minified/jquery.gsap.min.js" defer></script>
|
<script src="/vendor/gsap/src/minified/jquery.gsap.min.js" defer></script>
|
||||||
<script src="/vendor/socket.io-client/socket.io.js" defer></script>
|
<script src="/vendor/socket.io-client/socket.io.js" defer></script>
|
||||||
|
<script src="/vendor/js-yaml/dist/js-yaml.min.js" defer></script>
|
||||||
<% } %>
|
<% } %>
|
||||||
<script src="/vendor/jquery-ui/jquery-ui.min.js" defer></script>
|
<script src="/vendor/jquery-ui/jquery-ui.min.js" defer></script>
|
||||||
<!--codemirror-->
|
<!--codemirror-->
|
||||||
|
|
|
@ -4,6 +4,9 @@
|
||||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||||
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
||||||
<meta name="mobile-web-app-capable" content="yes">
|
<meta name="mobile-web-app-capable" content="yes">
|
||||||
|
<% if(typeof robots !== 'undefined' && robots) { %>
|
||||||
|
<meta name="robots" content="<%- robots %>">
|
||||||
|
<% } %>
|
||||||
<title><%- title %></title>
|
<title><%- title %></title>
|
||||||
<link rel="icon" type="image/png" href="/favicon.png">
|
<link rel="icon" type="image/png" href="/favicon.png">
|
||||||
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
|
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||||
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
||||||
<meta name="mobile-web-app-capable" content="yes">
|
<meta name="mobile-web-app-capable" content="yes">
|
||||||
|
{{{robots}}}
|
||||||
<title>
|
<title>
|
||||||
{{title}}
|
{{title}}
|
||||||
</title>
|
</title>
|
||||||
|
@ -42,7 +43,7 @@
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="toc-affix" class="ui-affix-toc ui-toc-dropdown unselectable hidden-print" data-spy="affix" style="top:17px;display:none;">
|
<div id="toc-affix" class="ui-affix-toc ui-toc-dropdown unselectable hidden-print" data-spy="affix" style="top:17px;display:none;" {{{lang}}} {{{dir}}}>
|
||||||
{{{toc-affix}}}
|
{{{toc-affix}}}
|
||||||
</div>
|
</div>
|
||||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
|
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
|
||||||
|
|
|
@ -8,6 +8,9 @@
|
||||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||||
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
||||||
<meta name="mobile-web-app-capable" content="yes">
|
<meta name="mobile-web-app-capable" content="yes">
|
||||||
|
<% if(typeof robots !== 'undefined' && robots) { %>
|
||||||
|
<meta name="robots" content="<%- robots %>">
|
||||||
|
<% } %>
|
||||||
<title><%- title %></title>
|
<title><%- title %></title>
|
||||||
<link rel="icon" type="image/png" href="<%- url %>/favicon.png">
|
<link rel="icon" type="image/png" href="<%- url %>/favicon.png">
|
||||||
<link rel="apple-touch-icon" href="<%- url %>/apple-touch-icon.png">
|
<link rel="apple-touch-icon" href="<%- url %>/apple-touch-icon.png">
|
||||||
|
@ -37,13 +40,19 @@
|
||||||
<body style="display:none;">
|
<body style="display:none;">
|
||||||
<div class="ui-infobar container-fluid unselectable hidden-print">
|
<div class="ui-infobar container-fluid unselectable hidden-print">
|
||||||
<small>
|
<small>
|
||||||
|
<span>
|
||||||
|
<% if(lastchangeuserprofile) { %>
|
||||||
|
<span class="ui-lastchangeuser"> <i class="ui-user-icon small" style="background-image: url(<%- lastchangeuserprofile.photo %>);" data-toggle="tooltip" data-placement="right" title="<%- lastchangeuserprofile.name %>"></i></span>
|
||||||
|
<% } else { %>
|
||||||
|
<span class="ui-no-lastchangeuser"> <i class="fa fa-clock-o"></i></span>
|
||||||
|
<% } %>
|
||||||
|
<span class="text-uppercase">changed</span>
|
||||||
<span class="ui-lastchange text-uppercase"><%- updatetime %></span>
|
<span class="ui-lastchange text-uppercase"><%- updatetime %></span>
|
||||||
<span class="pull-right"><%- viewcount %> views <a href="#" class="ui-edit" title="Edit this note"><i class="fa fa-pencil"></i></a></span>
|
</span>
|
||||||
|
<span class="pull-right"><%- viewcount %> views <a href="#" class="ui-edit" title="Edit this note"><i class="fa fa-fw fa-pencil"></i></a></span>
|
||||||
</small>
|
</small>
|
||||||
</div>
|
</div>
|
||||||
<div id="doc" class="container markdown-body">
|
<div id="doc" class="container markdown-body"><%- body %></div>
|
||||||
<%- body %>
|
|
||||||
</div>
|
|
||||||
<div class="ui-toc dropup unselectable hidden-print" style="display:none;">
|
<div class="ui-toc dropup unselectable hidden-print" style="display:none;">
|
||||||
<div class="pull-right dropdown">
|
<div class="pull-right dropdown">
|
||||||
<a id="tocLabel" class="ui-toc-label btn btn-default" data-target="#" data-toggle="dropdown" href="#" role="button" aria-haspopup="true" aria-expanded="false" title="Table of content">
|
<a id="tocLabel" class="ui-toc-label btn btn-default" data-target="#" data-toggle="dropdown" href="#" role="button" aria-haspopup="true" aria-expanded="false" title="Table of content">
|
||||||
|
@ -60,9 +69,11 @@
|
||||||
<% if(useCDN) { %>
|
<% if(useCDN) { %>
|
||||||
<script src="https://code.jquery.com/jquery-1.11.3.min.js"></script>
|
<script src="https://code.jquery.com/jquery-1.11.3.min.js"></script>
|
||||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js" defer></script>
|
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js" defer></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/js-yaml/3.4.6/js-yaml.min.js" defer></script>
|
||||||
<% } else { %>
|
<% } else { %>
|
||||||
<script src="<%- url %>/vendor/jquery/dist/jquery.min.js"></script>
|
<script src="<%- url %>/vendor/jquery/dist/jquery.min.js"></script>
|
||||||
<script src="<%- url %>/vendor/bootstrap/dist/js/bootstrap.min.js" defer></script>
|
<script src="<%- url %>/vendor/bootstrap/dist/js/bootstrap.min.js" defer></script>
|
||||||
|
<script src="<%- url %>/vendor/js-yaml/dist/js-yaml.min.js" defer></script>
|
||||||
<% } %>
|
<% } %>
|
||||||
<script src="<%- url %>/vendor/lz-string.min.js" defer></script>
|
<script src="<%- url %>/vendor/lz-string.min.js" defer></script>
|
||||||
<script src="<%- url %>/vendor/remarkable.min.js" defer></script>
|
<script src="<%- url %>/vendor/remarkable.min.js" defer></script>
|
||||||
|
|
Loading…
Reference in a new issue