mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2025-05-14 23:24:46 -04:00
Linter: Fix all lint errors
Signed-off-by: Philip Molares <philip.molares@udo.edu>
This commit is contained in:
parent
b0a45bdf9c
commit
136d895d15
51 changed files with 2245 additions and 1539 deletions
72
app.js
72
app.js
|
@ -1,44 +1,44 @@
|
||||||
'use strict'
|
'use strict'
|
||||||
// app
|
// app
|
||||||
// external modules
|
// external modules
|
||||||
var express = require('express')
|
const express = require('express')
|
||||||
|
|
||||||
var ejs = require('ejs')
|
const ejs = require('ejs')
|
||||||
var passport = require('passport')
|
const passport = require('passport')
|
||||||
var methodOverride = require('method-override')
|
const methodOverride = require('method-override')
|
||||||
var cookieParser = require('cookie-parser')
|
const cookieParser = require('cookie-parser')
|
||||||
var compression = require('compression')
|
const compression = require('compression')
|
||||||
var session = require('express-session')
|
const session = require('express-session')
|
||||||
var SequelizeStore = require('connect-session-sequelize')(session.Store)
|
const SequelizeStore = require('connect-session-sequelize')(session.Store)
|
||||||
var fs = require('fs')
|
const fs = require('fs')
|
||||||
var path = require('path')
|
const path = require('path')
|
||||||
|
|
||||||
var morgan = require('morgan')
|
const morgan = require('morgan')
|
||||||
var passportSocketIo = require('passport.socketio')
|
const passportSocketIo = require('passport.socketio')
|
||||||
var helmet = require('helmet')
|
const helmet = require('helmet')
|
||||||
var i18n = require('i18n')
|
const i18n = require('i18n')
|
||||||
var flash = require('connect-flash')
|
const flash = require('connect-flash')
|
||||||
|
|
||||||
// core
|
// core
|
||||||
var config = require('./lib/config')
|
const config = require('./lib/config')
|
||||||
var logger = require('./lib/logger')
|
const logger = require('./lib/logger')
|
||||||
var errors = require('./lib/errors')
|
const errors = require('./lib/errors')
|
||||||
var models = require('./lib/models')
|
const models = require('./lib/models')
|
||||||
var csp = require('./lib/csp')
|
const csp = require('./lib/csp')
|
||||||
|
|
||||||
// server setup
|
// server setup
|
||||||
var app = express()
|
const app = express()
|
||||||
var server = null
|
let server = null
|
||||||
if (config.useSSL) {
|
if (config.useSSL) {
|
||||||
var ca = (function () {
|
const ca = (function () {
|
||||||
var i, len, results
|
let i, len
|
||||||
results = []
|
const results = []
|
||||||
for (i = 0, len = config.sslCAPath.length; i < len; i++) {
|
for (i = 0, len = config.sslCAPath.length; i < len; i++) {
|
||||||
results.push(fs.readFileSync(config.sslCAPath[i], 'utf8'))
|
results.push(fs.readFileSync(config.sslCAPath[i], 'utf8'))
|
||||||
}
|
}
|
||||||
return results
|
return results
|
||||||
})()
|
})()
|
||||||
var options = {
|
const options = {
|
||||||
key: fs.readFileSync(config.sslKeyPath, 'utf8'),
|
key: fs.readFileSync(config.sslKeyPath, 'utf8'),
|
||||||
cert: fs.readFileSync(config.sslCertPath, 'utf8'),
|
cert: fs.readFileSync(config.sslCertPath, 'utf8'),
|
||||||
ca: ca,
|
ca: ca,
|
||||||
|
@ -60,18 +60,18 @@ if (!config.useSSL && config.protocolUseSSL) {
|
||||||
|
|
||||||
// logger
|
// logger
|
||||||
app.use(morgan('combined', {
|
app.use(morgan('combined', {
|
||||||
'stream': logger.stream
|
stream: logger.stream
|
||||||
}))
|
}))
|
||||||
|
|
||||||
// socket io
|
// socket io
|
||||||
var io = require('socket.io')(server, { cookie: false })
|
const io = require('socket.io')(server, { cookie: false })
|
||||||
io.engine.ws = new (require('ws').Server)({
|
io.engine.ws = new (require('ws').Server)({
|
||||||
noServer: true,
|
noServer: true,
|
||||||
perMessageDeflate: false
|
perMessageDeflate: false
|
||||||
})
|
})
|
||||||
|
|
||||||
// others
|
// others
|
||||||
var realtime = require('./lib/realtime.js')
|
const realtime = require('./lib/realtime.js')
|
||||||
|
|
||||||
// assign socket io to realtime
|
// assign socket io to realtime
|
||||||
realtime.io = io
|
realtime.io = io
|
||||||
|
@ -80,7 +80,7 @@ realtime.io = io
|
||||||
app.use(methodOverride('_method'))
|
app.use(methodOverride('_method'))
|
||||||
|
|
||||||
// session store
|
// session store
|
||||||
var sessionStore = new SequelizeStore({
|
const sessionStore = new SequelizeStore({
|
||||||
db: models.sequelize
|
db: models.sequelize
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -154,7 +154,7 @@ app.use(session({
|
||||||
}))
|
}))
|
||||||
|
|
||||||
// session resumption
|
// session resumption
|
||||||
var tlsSessionStore = {}
|
const tlsSessionStore = {}
|
||||||
server.on('newSession', function (id, data, cb) {
|
server.on('newSession', function (id, data, cb) {
|
||||||
tlsSessionStore[id.toString('hex')] = data
|
tlsSessionStore[id.toString('hex')] = data
|
||||||
cb()
|
cb()
|
||||||
|
@ -246,9 +246,9 @@ io.sockets.on('connection', realtime.connection)
|
||||||
|
|
||||||
// listen
|
// listen
|
||||||
function startListen () {
|
function startListen () {
|
||||||
var address
|
let address
|
||||||
var listenCallback = function () {
|
const listenCallback = function () {
|
||||||
var schema = config.useSSL ? 'HTTPS' : 'HTTP'
|
const schema = config.useSSL ? 'HTTPS' : 'HTTP'
|
||||||
logger.info('%s Server listening at %s', schema, address)
|
logger.info('%s Server listening at %s', schema, address)
|
||||||
realtime.maintenance = false
|
realtime.maintenance = false
|
||||||
}
|
}
|
||||||
|
@ -290,7 +290,7 @@ function handleTermSignals () {
|
||||||
realtime.maintenance = true
|
realtime.maintenance = true
|
||||||
// disconnect all socket.io clients
|
// disconnect all socket.io clients
|
||||||
Object.keys(io.sockets.sockets).forEach(function (key) {
|
Object.keys(io.sockets.sockets).forEach(function (key) {
|
||||||
var socket = io.sockets.sockets[key]
|
const socket = io.sockets.sockets[key]
|
||||||
// notify client server going into maintenance status
|
// notify client server going into maintenance status
|
||||||
socket.emit('maintenance')
|
socket.emit('maintenance')
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
|
@ -300,7 +300,7 @@ function handleTermSignals () {
|
||||||
if (config.path) {
|
if (config.path) {
|
||||||
fs.unlink(config.path)
|
fs.unlink(config.path)
|
||||||
}
|
}
|
||||||
var checkCleanTimer = setInterval(function () {
|
const checkCleanTimer = setInterval(function () {
|
||||||
if (realtime.isReady()) {
|
if (realtime.isReady()) {
|
||||||
models.Revision.checkAllNotesRevision(function (err, notes) {
|
models.Revision.checkAllNotesRevision(function (err, notes) {
|
||||||
if (err) return logger.error(err)
|
if (err) return logger.error(err)
|
||||||
|
|
|
@ -58,14 +58,14 @@ if (!['strict', 'lax', 'none'].includes(config.cookiePolicy)) {
|
||||||
|
|
||||||
// load LDAP CA
|
// load LDAP CA
|
||||||
if (config.ldap.tlsca) {
|
if (config.ldap.tlsca) {
|
||||||
let ca = config.ldap.tlsca.split(',')
|
const ca = config.ldap.tlsca.split(',')
|
||||||
let caContent = []
|
const caContent = []
|
||||||
for (let i of ca) {
|
for (const i of ca) {
|
||||||
if (fs.existsSync(i)) {
|
if (fs.existsSync(i)) {
|
||||||
caContent.push(fs.readFileSync(i, 'utf8'))
|
caContent.push(fs.readFileSync(i, 'utf8'))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let tlsOptions = {
|
const tlsOptions = {
|
||||||
ca: caContent
|
ca: caContent
|
||||||
}
|
}
|
||||||
config.ldap.tlsOptions = config.ldap.tlsOptions ? Object.assign(config.ldap.tlsOptions, tlsOptions) : tlsOptions
|
config.ldap.tlsOptions = config.ldap.tlsOptions ? Object.assign(config.ldap.tlsOptions, tlsOptions) : tlsOptions
|
||||||
|
@ -90,9 +90,9 @@ config.isStandardHTTPPort = (function isStandardHTTPPort () {
|
||||||
|
|
||||||
// cache serverURL
|
// cache serverURL
|
||||||
config.serverURL = (function getserverurl () {
|
config.serverURL = (function getserverurl () {
|
||||||
var url = ''
|
let url = ''
|
||||||
if (config.domain) {
|
if (config.domain) {
|
||||||
var protocol = config.protocolUseSSL ? 'https://' : 'http://'
|
const protocol = config.protocolUseSSL ? 'https://' : 'http://'
|
||||||
url = protocol + config.domain
|
url = protocol + config.domain
|
||||||
if (config.urlAddPort) {
|
if (config.urlAddPort) {
|
||||||
if (!config.isStandardHTTPPort || !config.isStandardHTTPsPort) {
|
if (!config.isStandardHTTPPort || !config.isStandardHTTPsPort) {
|
||||||
|
@ -138,10 +138,10 @@ config.isGitlabSnippetsEnable = (!config.gitlab.scope || config.gitlab.scope ===
|
||||||
config.updateI18nFiles = (env === Environment.development)
|
config.updateI18nFiles = (env === Environment.development)
|
||||||
|
|
||||||
// merge legacy values
|
// merge legacy values
|
||||||
let keys = Object.keys(config)
|
const keys = Object.keys(config)
|
||||||
const uppercase = /[A-Z]/
|
const uppercase = /[A-Z]/
|
||||||
for (let i = keys.length; i--;) {
|
for (let i = keys.length; i--;) {
|
||||||
let lowercaseKey = keys[i].toLowerCase()
|
const lowercaseKey = keys[i].toLowerCase()
|
||||||
// if the config contains uppercase letters
|
// if the config contains uppercase letters
|
||||||
// and a lowercase version of this setting exists
|
// and a lowercase version of this setting exists
|
||||||
// and the config with uppercase is not set
|
// and the config with uppercase is not set
|
||||||
|
|
24
lib/csp.js
24
lib/csp.js
|
@ -1,9 +1,9 @@
|
||||||
var config = require('./config')
|
const config = require('./config')
|
||||||
var uuid = require('uuid')
|
const uuid = require('uuid')
|
||||||
|
|
||||||
var CspStrategy = {}
|
const CspStrategy = {}
|
||||||
|
|
||||||
var defaultDirectives = {
|
const defaultDirectives = {
|
||||||
defaultSrc: ['\'self\''],
|
defaultSrc: ['\'self\''],
|
||||||
scriptSrc: ['\'self\'', 'vimeo.com', 'https://gist.github.com', 'www.slideshare.net', 'https://query.yahooapis.com', '\'unsafe-eval\''],
|
scriptSrc: ['\'self\'', 'vimeo.com', 'https://gist.github.com', 'www.slideshare.net', 'https://query.yahooapis.com', '\'unsafe-eval\''],
|
||||||
// ^ TODO: Remove unsafe-eval - webpack script-loader issues https://github.com/hackmdio/codimd/issues/594
|
// ^ TODO: Remove unsafe-eval - webpack script-loader issues https://github.com/hackmdio/codimd/issues/594
|
||||||
|
@ -16,28 +16,28 @@ var defaultDirectives = {
|
||||||
connectSrc: ['*']
|
connectSrc: ['*']
|
||||||
}
|
}
|
||||||
|
|
||||||
var cdnDirectives = {
|
const cdnDirectives = {
|
||||||
scriptSrc: ['https://cdnjs.cloudflare.com', 'https://cdn.mathjax.org'],
|
scriptSrc: ['https://cdnjs.cloudflare.com', 'https://cdn.mathjax.org'],
|
||||||
styleSrc: ['https://cdnjs.cloudflare.com', 'https://fonts.googleapis.com'],
|
styleSrc: ['https://cdnjs.cloudflare.com', 'https://fonts.googleapis.com'],
|
||||||
fontSrc: ['https://cdnjs.cloudflare.com', 'https://fonts.gstatic.com']
|
fontSrc: ['https://cdnjs.cloudflare.com', 'https://fonts.gstatic.com']
|
||||||
}
|
}
|
||||||
|
|
||||||
var disqusDirectives = {
|
const disqusDirectives = {
|
||||||
scriptSrc: ['https://disqus.com', 'https://*.disqus.com', 'https://*.disquscdn.com'],
|
scriptSrc: ['https://disqus.com', 'https://*.disqus.com', 'https://*.disquscdn.com'],
|
||||||
styleSrc: ['https://*.disquscdn.com'],
|
styleSrc: ['https://*.disquscdn.com'],
|
||||||
fontSrc: ['https://*.disquscdn.com']
|
fontSrc: ['https://*.disquscdn.com']
|
||||||
}
|
}
|
||||||
|
|
||||||
var googleAnalyticsDirectives = {
|
const googleAnalyticsDirectives = {
|
||||||
scriptSrc: ['https://www.google-analytics.com']
|
scriptSrc: ['https://www.google-analytics.com']
|
||||||
}
|
}
|
||||||
|
|
||||||
var dropboxDirectives = {
|
const dropboxDirectives = {
|
||||||
scriptSrc: ['https://www.dropbox.com', '\'unsafe-inline\'']
|
scriptSrc: ['https://www.dropbox.com', '\'unsafe-inline\'']
|
||||||
}
|
}
|
||||||
|
|
||||||
CspStrategy.computeDirectives = function () {
|
CspStrategy.computeDirectives = function () {
|
||||||
var directives = {}
|
const directives = {}
|
||||||
mergeDirectives(directives, config.csp.directives)
|
mergeDirectives(directives, config.csp.directives)
|
||||||
mergeDirectivesIf(config.csp.addDefaults, directives, defaultDirectives)
|
mergeDirectivesIf(config.csp.addDefaults, directives, defaultDirectives)
|
||||||
mergeDirectivesIf(config.useCDN, directives, cdnDirectives)
|
mergeDirectivesIf(config.useCDN, directives, cdnDirectives)
|
||||||
|
@ -53,10 +53,10 @@ CspStrategy.computeDirectives = function () {
|
||||||
}
|
}
|
||||||
|
|
||||||
function mergeDirectives (existingDirectives, newDirectives) {
|
function mergeDirectives (existingDirectives, newDirectives) {
|
||||||
for (var propertyName in newDirectives) {
|
for (const propertyName in newDirectives) {
|
||||||
var newDirective = newDirectives[propertyName]
|
const newDirective = newDirectives[propertyName]
|
||||||
if (newDirective) {
|
if (newDirective) {
|
||||||
var existingDirective = existingDirectives[propertyName] || []
|
const existingDirective = existingDirectives[propertyName] || []
|
||||||
existingDirectives[propertyName] = existingDirective.concat(newDirective)
|
existingDirectives[propertyName] = existingDirective.concat(newDirective)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
'use strict'
|
'use strict'
|
||||||
// history
|
// history
|
||||||
// external modules
|
// external modules
|
||||||
var LZString = require('lz-string')
|
const LZString = require('lz-string')
|
||||||
|
|
||||||
// core
|
// core
|
||||||
var logger = require('./logger')
|
const logger = require('./logger')
|
||||||
var models = require('./models')
|
const models = require('./models')
|
||||||
const errors = require('./errors')
|
const errors = require('./errors')
|
||||||
|
|
||||||
// public
|
// public
|
||||||
var History = {
|
const History = {
|
||||||
historyGet: historyGet,
|
historyGet: historyGet,
|
||||||
historyPost: historyPost,
|
historyPost: historyPost,
|
||||||
historyDelete: historyDelete,
|
historyDelete: historyDelete,
|
||||||
|
@ -25,7 +25,7 @@ function getHistory (userid, callback) {
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return callback(null, null)
|
return callback(null, null)
|
||||||
}
|
}
|
||||||
var history = {}
|
let history = {}
|
||||||
if (user.history) {
|
if (user.history) {
|
||||||
history = JSON.parse(user.history)
|
history = JSON.parse(user.history)
|
||||||
// migrate LZString encoded note id to base64url encoded note id
|
// migrate LZString encoded note id to base64url encoded note id
|
||||||
|
@ -40,7 +40,7 @@ function getHistory (userid, callback) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
let id = LZString.decompressFromBase64(history[i].id)
|
const id = LZString.decompressFromBase64(history[i].id)
|
||||||
if (id && models.Note.checkNoteIdValid(id)) {
|
if (id && models.Note.checkNoteIdValid(id)) {
|
||||||
history[i].id = models.Note.encodeNoteId(id)
|
history[i].id = models.Note.encodeNoteId(id)
|
||||||
}
|
}
|
||||||
|
@ -85,8 +85,8 @@ function updateHistory (userid, noteId, document, time) {
|
||||||
if (!history[noteId]) {
|
if (!history[noteId]) {
|
||||||
history[noteId] = {}
|
history[noteId] = {}
|
||||||
}
|
}
|
||||||
var noteHistory = history[noteId]
|
const noteHistory = history[noteId]
|
||||||
var noteInfo = models.Note.parseNoteInfo(document)
|
const noteInfo = models.Note.parseNoteInfo(document)
|
||||||
noteHistory.id = noteId
|
noteHistory.id = noteId
|
||||||
noteHistory.text = noteInfo.title
|
noteHistory.text = noteInfo.title
|
||||||
noteHistory.time = time || Date.now()
|
noteHistory.time = time || Date.now()
|
||||||
|
@ -101,18 +101,18 @@ function updateHistory (userid, noteId, document, time) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseHistoryToArray (history) {
|
function parseHistoryToArray (history) {
|
||||||
var _history = []
|
const _history = []
|
||||||
Object.keys(history).forEach(function (key) {
|
Object.keys(history).forEach(function (key) {
|
||||||
var item = history[key]
|
const item = history[key]
|
||||||
_history.push(item)
|
_history.push(item)
|
||||||
})
|
})
|
||||||
return _history
|
return _history
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseHistoryToObject (history) {
|
function parseHistoryToObject (history) {
|
||||||
var _history = {}
|
const _history = {}
|
||||||
for (var i = 0, l = history.length; i < l; i++) {
|
for (let i = 0, l = history.length; i < l; i++) {
|
||||||
var item = history[i]
|
const item = history[i]
|
||||||
_history[item.id] = item
|
_history[item.id] = item
|
||||||
}
|
}
|
||||||
return _history
|
return _history
|
||||||
|
@ -134,25 +134,25 @@ function historyGet (req, res) {
|
||||||
|
|
||||||
function historyPost (req, res) {
|
function historyPost (req, res) {
|
||||||
if (req.isAuthenticated()) {
|
if (req.isAuthenticated()) {
|
||||||
var noteId = req.params.noteId
|
const noteId = req.params.noteId
|
||||||
if (!noteId) {
|
if (!noteId) {
|
||||||
if (typeof req.body['history'] === 'undefined') return errors.errorBadRequest(res)
|
if (typeof req.body.history === 'undefined') return errors.errorBadRequest(res)
|
||||||
logger.debug(`SERVER received history from [${req.user.id}]: ${req.body.history}`)
|
logger.debug(`SERVER received history from [${req.user.id}]: ${req.body.history}`)
|
||||||
try {
|
try {
|
||||||
var history = JSON.parse(req.body.history)
|
const history = JSON.parse(req.body.history)
|
||||||
|
if (Array.isArray(history)) {
|
||||||
|
setHistory(req.user.id, history, function (err, count) {
|
||||||
|
if (err) return errors.errorInternalError(res)
|
||||||
|
res.end()
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
return errors.errorBadRequest(res)
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return errors.errorBadRequest(res)
|
return errors.errorBadRequest(res)
|
||||||
}
|
}
|
||||||
if (Array.isArray(history)) {
|
|
||||||
setHistory(req.user.id, history, function (err, count) {
|
|
||||||
if (err) return errors.errorInternalError(res)
|
|
||||||
res.end()
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
return errors.errorBadRequest(res)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if (typeof req.body['pinned'] === 'undefined') return errors.errorBadRequest(res)
|
if (typeof req.body.pinned === 'undefined') return errors.errorBadRequest(res)
|
||||||
getHistory(req.user.id, function (err, history) {
|
getHistory(req.user.id, function (err, history) {
|
||||||
if (err) return errors.errorInternalError(res)
|
if (err) return errors.errorInternalError(res)
|
||||||
if (!history) return errors.errorNotFound(res)
|
if (!history) return errors.errorNotFound(res)
|
||||||
|
@ -175,7 +175,7 @@ function historyPost (req, res) {
|
||||||
|
|
||||||
function historyDelete (req, res) {
|
function historyDelete (req, res) {
|
||||||
if (req.isAuthenticated()) {
|
if (req.isAuthenticated()) {
|
||||||
var noteId = req.params.noteId
|
const noteId = req.params.noteId
|
||||||
if (!noteId) {
|
if (!noteId) {
|
||||||
setHistory(req.user.id, [], function (err, count) {
|
setHistory(req.user.id, [], function (err, count) {
|
||||||
if (err) return errors.errorInternalError(res)
|
if (err) return errors.errorInternalError(res)
|
||||||
|
|
|
@ -32,9 +32,9 @@ exports.generateAvatarURL = function (name, email = '', big = true) {
|
||||||
}
|
}
|
||||||
name = encodeURIComponent(name)
|
name = encodeURIComponent(name)
|
||||||
|
|
||||||
let hash = crypto.createHash('md5')
|
const hash = crypto.createHash('md5')
|
||||||
hash.update(email.toLowerCase())
|
hash.update(email.toLowerCase())
|
||||||
let hexDigest = hash.digest('hex')
|
const hexDigest = hash.digest('hex')
|
||||||
|
|
||||||
if (email !== '' && config.allowGravatar) {
|
if (email !== '' && config.allowGravatar) {
|
||||||
photo = 'https://cdn.libravatar.org/avatar/' + hexDigest
|
photo = 'https://cdn.libravatar.org/avatar/' + hexDigest
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
'use strict'
|
'use strict'
|
||||||
// external modules
|
// external modules
|
||||||
var Sequelize = require('sequelize')
|
const Sequelize = require('sequelize')
|
||||||
|
|
||||||
module.exports = function (sequelize, DataTypes) {
|
module.exports = function (sequelize, DataTypes) {
|
||||||
var Author = sequelize.define('Author', {
|
const Author = sequelize.define('Author', {
|
||||||
id: {
|
id: {
|
||||||
type: Sequelize.INTEGER,
|
type: Sequelize.INTEGER,
|
||||||
primaryKey: true,
|
primaryKey: true,
|
||||||
|
|
|
@ -1,20 +1,22 @@
|
||||||
'use strict'
|
'use strict'
|
||||||
// external modules
|
// external modules
|
||||||
var fs = require('fs')
|
const fs = require('fs')
|
||||||
var path = require('path')
|
const path = require('path')
|
||||||
var Sequelize = require('sequelize')
|
const Sequelize = require('sequelize')
|
||||||
const { cloneDeep } = require('lodash')
|
const { cloneDeep } = require('lodash')
|
||||||
|
|
||||||
// core
|
// core
|
||||||
var config = require('../config')
|
const config = require('../config')
|
||||||
var logger = require('../logger')
|
const logger = require('../logger')
|
||||||
|
|
||||||
var dbconfig = cloneDeep(config.db)
|
const dbconfig = cloneDeep(config.db)
|
||||||
dbconfig.logging = config.debug ? (data) => {
|
dbconfig.logging = config.debug
|
||||||
logger.info(data)
|
? (data) => {
|
||||||
} : false
|
logger.info(data)
|
||||||
|
}
|
||||||
|
: false
|
||||||
|
|
||||||
var sequelize = null
|
let sequelize = null
|
||||||
|
|
||||||
// Heroku specific
|
// Heroku specific
|
||||||
if (config.dbURL) {
|
if (config.dbURL) {
|
||||||
|
@ -38,14 +40,14 @@ function processData (data, _default, process) {
|
||||||
}
|
}
|
||||||
sequelize.processData = processData
|
sequelize.processData = processData
|
||||||
|
|
||||||
var db = {}
|
const db = {}
|
||||||
|
|
||||||
fs.readdirSync(__dirname)
|
fs.readdirSync(__dirname)
|
||||||
.filter(function (file) {
|
.filter(function (file) {
|
||||||
return (file.indexOf('.') !== 0) && (file !== 'index.js')
|
return (file.indexOf('.') !== 0) && (file !== 'index.js')
|
||||||
})
|
})
|
||||||
.forEach(function (file) {
|
.forEach(function (file) {
|
||||||
var model = sequelize.import(path.join(__dirname, file))
|
const model = sequelize.import(path.join(__dirname, file))
|
||||||
db[model.name] = model
|
db[model.name] = model
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -1,32 +1,32 @@
|
||||||
'use strict'
|
'use strict'
|
||||||
// external modules
|
// external modules
|
||||||
var fs = require('fs')
|
const fs = require('fs')
|
||||||
var path = require('path')
|
const path = require('path')
|
||||||
var LZString = require('lz-string')
|
const LZString = require('lz-string')
|
||||||
var base64url = require('base64url')
|
const base64url = require('base64url')
|
||||||
var md = require('markdown-it')()
|
const md = require('markdown-it')()
|
||||||
var metaMarked = require('meta-marked')
|
const metaMarked = require('meta-marked')
|
||||||
var cheerio = require('cheerio')
|
const cheerio = require('cheerio')
|
||||||
var shortId = require('shortid')
|
const shortId = require('shortid')
|
||||||
var Sequelize = require('sequelize')
|
const Sequelize = require('sequelize')
|
||||||
var async = require('async')
|
const async = require('async')
|
||||||
var moment = require('moment')
|
const moment = require('moment')
|
||||||
var DiffMatchPatch = require('diff-match-patch')
|
const DiffMatchPatch = require('diff-match-patch')
|
||||||
var dmp = new DiffMatchPatch()
|
const dmp = new DiffMatchPatch()
|
||||||
var S = require('string')
|
const S = require('string')
|
||||||
|
|
||||||
// core
|
// core
|
||||||
var config = require('../config')
|
const config = require('../config')
|
||||||
var logger = require('../logger')
|
const logger = require('../logger')
|
||||||
|
|
||||||
// ot
|
// ot
|
||||||
var ot = require('../ot')
|
const ot = require('../ot')
|
||||||
|
|
||||||
// permission types
|
// permission types
|
||||||
var permissionTypes = ['freely', 'editable', 'limited', 'locked', 'protected', 'private']
|
const permissionTypes = ['freely', 'editable', 'limited', 'locked', 'protected', 'private']
|
||||||
|
|
||||||
module.exports = function (sequelize, DataTypes) {
|
module.exports = function (sequelize, DataTypes) {
|
||||||
var Note = sequelize.define('Note', {
|
const Note = sequelize.define('Note', {
|
||||||
id: {
|
id: {
|
||||||
type: DataTypes.UUID,
|
type: DataTypes.UUID,
|
||||||
primaryKey: true,
|
primaryKey: true,
|
||||||
|
@ -91,7 +91,7 @@ module.exports = function (sequelize, DataTypes) {
|
||||||
return new Promise(function (resolve, reject) {
|
return new Promise(function (resolve, reject) {
|
||||||
// if no content specified then use default note
|
// if no content specified then use default note
|
||||||
if (!note.content) {
|
if (!note.content) {
|
||||||
var body = null
|
let body = null
|
||||||
let filePath = null
|
let filePath = null
|
||||||
if (note.alias) {
|
if (note.alias) {
|
||||||
filePath = path.join(config.docsPath, note.alias + '.md')
|
filePath = path.join(config.docsPath, note.alias + '.md')
|
||||||
|
@ -100,7 +100,7 @@ module.exports = function (sequelize, DataTypes) {
|
||||||
filePath = config.defaultNotePath
|
filePath = config.defaultNotePath
|
||||||
}
|
}
|
||||||
if (Note.checkFileExist(filePath)) {
|
if (Note.checkFileExist(filePath)) {
|
||||||
var fsCreatedTime = moment(fs.statSync(filePath).ctime)
|
const fsCreatedTime = moment(fs.statSync(filePath).ctime)
|
||||||
body = fs.readFileSync(filePath, 'utf8')
|
body = fs.readFileSync(filePath, 'utf8')
|
||||||
note.title = Note.parseNoteTitle(body)
|
note.title = Note.parseNoteTitle(body)
|
||||||
note.content = body
|
note.content = body
|
||||||
|
@ -165,15 +165,15 @@ module.exports = function (sequelize, DataTypes) {
|
||||||
}
|
}
|
||||||
Note.encodeNoteId = function (id) {
|
Note.encodeNoteId = function (id) {
|
||||||
// remove dashes in UUID and encode in url-safe base64
|
// remove dashes in UUID and encode in url-safe base64
|
||||||
let str = id.replace(/-/g, '')
|
const str = id.replace(/-/g, '')
|
||||||
let hexStr = Buffer.from(str, 'hex')
|
const hexStr = Buffer.from(str, 'hex')
|
||||||
return base64url.encode(hexStr)
|
return base64url.encode(hexStr)
|
||||||
}
|
}
|
||||||
Note.decodeNoteId = function (encodedId) {
|
Note.decodeNoteId = function (encodedId) {
|
||||||
// decode from url-safe base64
|
// decode from url-safe base64
|
||||||
let id = base64url.toBuffer(encodedId).toString('hex')
|
const id = base64url.toBuffer(encodedId).toString('hex')
|
||||||
// add dashes between the UUID string parts
|
// add dashes between the UUID string parts
|
||||||
let idParts = []
|
const idParts = []
|
||||||
idParts.push(id.substr(0, 8))
|
idParts.push(id.substr(0, 8))
|
||||||
idParts.push(id.substr(8, 4))
|
idParts.push(id.substr(8, 4))
|
||||||
idParts.push(id.substr(12, 4))
|
idParts.push(id.substr(12, 4))
|
||||||
|
@ -182,8 +182,8 @@ module.exports = function (sequelize, DataTypes) {
|
||||||
return idParts.join('-')
|
return idParts.join('-')
|
||||||
}
|
}
|
||||||
Note.checkNoteIdValid = function (id) {
|
Note.checkNoteIdValid = function (id) {
|
||||||
var uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i
|
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i
|
||||||
var result = id.match(uuidRegex)
|
const result = id.match(uuidRegex)
|
||||||
if (result && result.length === 1) { return true } else { return false }
|
if (result && result.length === 1) { return true } else { return false }
|
||||||
}
|
}
|
||||||
Note.parseNoteId = function (noteId, callback) {
|
Note.parseNoteId = function (noteId, callback) {
|
||||||
|
@ -196,15 +196,15 @@ module.exports = function (sequelize, DataTypes) {
|
||||||
}
|
}
|
||||||
}).then(function (note) {
|
}).then(function (note) {
|
||||||
if (note) {
|
if (note) {
|
||||||
let filePath = path.join(config.docsPath, noteId + '.md')
|
const filePath = path.join(config.docsPath, noteId + '.md')
|
||||||
if (Note.checkFileExist(filePath)) {
|
if (Note.checkFileExist(filePath)) {
|
||||||
// if doc in filesystem have newer modified time than last change time
|
// if doc in filesystem have newer modified time than last change time
|
||||||
// then will update the doc in db
|
// then will update the doc in db
|
||||||
var fsModifiedTime = moment(fs.statSync(filePath).mtime)
|
const fsModifiedTime = moment(fs.statSync(filePath).mtime)
|
||||||
var dbModifiedTime = moment(note.lastchangeAt || note.createdAt)
|
const dbModifiedTime = moment(note.lastchangeAt || note.createdAt)
|
||||||
var body = fs.readFileSync(filePath, 'utf8')
|
const body = fs.readFileSync(filePath, 'utf8')
|
||||||
var contentLength = body.length
|
const contentLength = body.length
|
||||||
var title = Note.parseNoteTitle(body)
|
const title = Note.parseNoteTitle(body)
|
||||||
if (fsModifiedTime.isAfter(dbModifiedTime) && note.content !== body) {
|
if (fsModifiedTime.isAfter(dbModifiedTime) && note.content !== body) {
|
||||||
note.update({
|
note.update({
|
||||||
title: title,
|
title: title,
|
||||||
|
@ -214,9 +214,9 @@ module.exports = function (sequelize, DataTypes) {
|
||||||
sequelize.models.Revision.saveNoteRevision(note, function (err, revision) {
|
sequelize.models.Revision.saveNoteRevision(note, function (err, revision) {
|
||||||
if (err) return _callback(err, null)
|
if (err) return _callback(err, null)
|
||||||
// update authorship on after making revision of docs
|
// update authorship on after making revision of docs
|
||||||
var patch = dmp.patch_fromText(revision.patch)
|
const patch = dmp.patch_fromText(revision.patch)
|
||||||
var operations = Note.transformPatchToOperations(patch, contentLength)
|
const operations = Note.transformPatchToOperations(patch, contentLength)
|
||||||
var authorship = note.authorship
|
let authorship = note.authorship
|
||||||
for (let i = 0; i < operations.length; i++) {
|
for (let i = 0; i < operations.length; i++) {
|
||||||
authorship = Note.updateAuthorshipByOperation(operations[i], null, authorship)
|
authorship = Note.updateAuthorshipByOperation(operations[i], null, authorship)
|
||||||
}
|
}
|
||||||
|
@ -238,7 +238,7 @@ module.exports = function (sequelize, DataTypes) {
|
||||||
return callback(null, note.id)
|
return callback(null, note.id)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var filePath = path.join(config.docsPath, noteId + '.md')
|
const filePath = path.join(config.docsPath, noteId + '.md')
|
||||||
if (Note.checkFileExist(filePath)) {
|
if (Note.checkFileExist(filePath)) {
|
||||||
Note.create({
|
Note.create({
|
||||||
alias: noteId,
|
alias: noteId,
|
||||||
|
@ -270,7 +270,7 @@ module.exports = function (sequelize, DataTypes) {
|
||||||
}
|
}
|
||||||
// try to parse note id by LZString Base64
|
// try to parse note id by LZString Base64
|
||||||
try {
|
try {
|
||||||
var id = LZString.decompressFromBase64(noteId)
|
const id = LZString.decompressFromBase64(noteId)
|
||||||
if (id && Note.checkNoteIdValid(id)) { return callback(null, id) } else { return _callback(null, null) }
|
if (id && Note.checkNoteIdValid(id)) { return callback(null, id) } else { return _callback(null, null) }
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err.message === 'Cannot read property \'charAt\' of undefined') {
|
if (err.message === 'Cannot read property \'charAt\' of undefined') {
|
||||||
|
@ -284,7 +284,7 @@ module.exports = function (sequelize, DataTypes) {
|
||||||
parseNoteIdByBase64Url: function (_callback) {
|
parseNoteIdByBase64Url: function (_callback) {
|
||||||
// try to parse note id by base64url
|
// try to parse note id by base64url
|
||||||
try {
|
try {
|
||||||
var id = Note.decodeNoteId(noteId)
|
const id = Note.decodeNoteId(noteId)
|
||||||
if (id && Note.checkNoteIdValid(id)) { return callback(null, id) } else { return _callback(null, null) }
|
if (id && Note.checkNoteIdValid(id)) { return callback(null, id) } else { return _callback(null, null) }
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error(err)
|
logger.error(err)
|
||||||
|
@ -321,24 +321,24 @@ module.exports = function (sequelize, DataTypes) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Note.parseNoteInfo = function (body) {
|
Note.parseNoteInfo = function (body) {
|
||||||
var parsed = Note.extractMeta(body)
|
const parsed = Note.extractMeta(body)
|
||||||
var $ = cheerio.load(md.render(parsed.markdown))
|
const $ = cheerio.load(md.render(parsed.markdown))
|
||||||
return {
|
return {
|
||||||
title: Note.extractNoteTitle(parsed.meta, $),
|
title: Note.extractNoteTitle(parsed.meta, $),
|
||||||
tags: Note.extractNoteTags(parsed.meta, $)
|
tags: Note.extractNoteTags(parsed.meta, $)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Note.parseNoteTitle = function (body) {
|
Note.parseNoteTitle = function (body) {
|
||||||
var parsed = Note.extractMeta(body)
|
const parsed = Note.extractMeta(body)
|
||||||
var $ = cheerio.load(md.render(parsed.markdown))
|
const $ = cheerio.load(md.render(parsed.markdown))
|
||||||
return Note.extractNoteTitle(parsed.meta, $)
|
return Note.extractNoteTitle(parsed.meta, $)
|
||||||
}
|
}
|
||||||
Note.extractNoteTitle = function (meta, $) {
|
Note.extractNoteTitle = function (meta, $) {
|
||||||
var title = ''
|
let title = ''
|
||||||
if (meta.title && (typeof meta.title === 'string' || typeof meta.title === 'number')) {
|
if (meta.title && (typeof meta.title === 'string' || typeof meta.title === 'number')) {
|
||||||
title = meta.title
|
title = meta.title
|
||||||
} else {
|
} else {
|
||||||
var h1s = $('h1')
|
const h1s = $('h1')
|
||||||
if (h1s.length > 0 && h1s.first().text().split('\n').length === 1) { title = S(h1s.first().text()).stripTags().s }
|
if (h1s.length > 0 && h1s.first().text().split('\n').length === 1) { title = S(h1s.first().text()).stripTags().s }
|
||||||
}
|
}
|
||||||
if (!title) title = 'Untitled'
|
if (!title) title = 'Untitled'
|
||||||
|
@ -355,28 +355,28 @@ module.exports = function (sequelize, DataTypes) {
|
||||||
return title
|
return title
|
||||||
}
|
}
|
||||||
Note.extractNoteTags = function (meta, $) {
|
Note.extractNoteTags = function (meta, $) {
|
||||||
var tags = []
|
const tags = []
|
||||||
var rawtags = []
|
const rawtags = []
|
||||||
if (meta.tags && (typeof meta.tags === 'string' || typeof meta.tags === 'number')) {
|
if (meta.tags && (typeof meta.tags === 'string' || typeof meta.tags === 'number')) {
|
||||||
var metaTags = ('' + meta.tags).split(',')
|
const metaTags = ('' + meta.tags).split(',')
|
||||||
for (let i = 0; i < metaTags.length; i++) {
|
for (let i = 0; i < metaTags.length; i++) {
|
||||||
var text = metaTags[i].trim()
|
const text = metaTags[i].trim()
|
||||||
if (text) rawtags.push(text)
|
if (text) rawtags.push(text)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var h6s = $('h6')
|
const h6s = $('h6')
|
||||||
h6s.each(function (key, value) {
|
h6s.each(function (key, value) {
|
||||||
if (/^tags/gmi.test($(value).text())) {
|
if (/^tags/gmi.test($(value).text())) {
|
||||||
var codes = $(value).find('code')
|
const codes = $(value).find('code')
|
||||||
for (let i = 0; i < codes.length; i++) {
|
for (let i = 0; i < codes.length; i++) {
|
||||||
var text = S($(codes[i]).text().trim()).stripTags().s
|
const text = S($(codes[i]).text().trim()).stripTags().s
|
||||||
if (text) rawtags.push(text)
|
if (text) rawtags.push(text)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
for (let i = 0; i < rawtags.length; i++) {
|
for (let i = 0; i < rawtags.length; i++) {
|
||||||
var found = false
|
let found = false
|
||||||
for (let j = 0; j < tags.length; j++) {
|
for (let j = 0; j < tags.length; j++) {
|
||||||
if (tags[j] === rawtags[i]) {
|
if (tags[j] === rawtags[i]) {
|
||||||
found = true
|
found = true
|
||||||
|
@ -388,7 +388,7 @@ module.exports = function (sequelize, DataTypes) {
|
||||||
return tags
|
return tags
|
||||||
}
|
}
|
||||||
Note.extractMeta = function (content) {
|
Note.extractMeta = function (content) {
|
||||||
var obj = null
|
let obj = null
|
||||||
try {
|
try {
|
||||||
obj = metaMarked(content)
|
obj = metaMarked(content)
|
||||||
if (!obj.markdown) obj.markdown = ''
|
if (!obj.markdown) obj.markdown = ''
|
||||||
|
@ -402,7 +402,7 @@ module.exports = function (sequelize, DataTypes) {
|
||||||
return obj
|
return obj
|
||||||
}
|
}
|
||||||
Note.parseMeta = function (meta) {
|
Note.parseMeta = function (meta) {
|
||||||
var _meta = {}
|
const _meta = {}
|
||||||
if (meta) {
|
if (meta) {
|
||||||
if (meta.title && (typeof meta.title === 'string' || typeof meta.title === 'number')) { _meta.title = meta.title }
|
if (meta.title && (typeof meta.title === 'string' || typeof meta.title === 'number')) { _meta.title = meta.title }
|
||||||
if (meta.description && (typeof meta.description === 'string' || typeof meta.description === 'number')) { _meta.description = meta.description }
|
if (meta.description && (typeof meta.description === 'string' || typeof meta.description === 'number')) { _meta.description = meta.description }
|
||||||
|
@ -416,7 +416,7 @@ module.exports = function (sequelize, DataTypes) {
|
||||||
return _meta
|
return _meta
|
||||||
}
|
}
|
||||||
Note.parseOpengraph = function (meta, title) {
|
Note.parseOpengraph = function (meta, title) {
|
||||||
var _ogdata = {}
|
let _ogdata = {}
|
||||||
if (meta.opengraph) { _ogdata = meta.opengraph }
|
if (meta.opengraph) { _ogdata = meta.opengraph }
|
||||||
if (!(_ogdata.title && (typeof _ogdata.title === 'string' || typeof _ogdata.title === 'number'))) { _ogdata.title = title }
|
if (!(_ogdata.title && (typeof _ogdata.title === 'string' || typeof _ogdata.title === 'number'))) { _ogdata.title = title }
|
||||||
if (!(_ogdata.description && (typeof _ogdata.description === 'string' || typeof _ogdata.description === 'number'))) { _ogdata.description = meta.description || '' }
|
if (!(_ogdata.description && (typeof _ogdata.description === 'string' || typeof _ogdata.description === 'number'))) { _ogdata.description = meta.description || '' }
|
||||||
|
@ -424,27 +424,27 @@ module.exports = function (sequelize, DataTypes) {
|
||||||
return _ogdata
|
return _ogdata
|
||||||
}
|
}
|
||||||
Note.updateAuthorshipByOperation = function (operation, userId, authorships) {
|
Note.updateAuthorshipByOperation = function (operation, userId, authorships) {
|
||||||
var index = 0
|
let index = 0
|
||||||
var timestamp = Date.now()
|
const timestamp = Date.now()
|
||||||
for (let i = 0; i < operation.length; i++) {
|
for (let i = 0; i < operation.length; i++) {
|
||||||
var op = operation[i]
|
const op = operation[i]
|
||||||
if (ot.TextOperation.isRetain(op)) {
|
if (ot.TextOperation.isRetain(op)) {
|
||||||
index += op
|
index += op
|
||||||
} else if (ot.TextOperation.isInsert(op)) {
|
} else if (ot.TextOperation.isInsert(op)) {
|
||||||
let opStart = index
|
const opStart = index
|
||||||
let opEnd = index + op.length
|
const opEnd = index + op.length
|
||||||
var inserted = false
|
let inserted = false
|
||||||
// authorship format: [userId, startPos, endPos, createdAt, updatedAt]
|
// authorship format: [userId, startPos, endPos, createdAt, updatedAt]
|
||||||
if (authorships.length <= 0) authorships.push([userId, opStart, opEnd, timestamp, timestamp])
|
if (authorships.length <= 0) authorships.push([userId, opStart, opEnd, timestamp, timestamp])
|
||||||
else {
|
else {
|
||||||
for (let j = 0; j < authorships.length; j++) {
|
for (let j = 0; j < authorships.length; j++) {
|
||||||
let authorship = authorships[j]
|
const authorship = authorships[j]
|
||||||
if (!inserted) {
|
if (!inserted) {
|
||||||
let nextAuthorship = authorships[j + 1] || -1
|
const nextAuthorship = authorships[j + 1] || -1
|
||||||
if ((nextAuthorship !== -1 && nextAuthorship[1] >= opEnd) || j >= authorships.length - 1) {
|
if ((nextAuthorship !== -1 && nextAuthorship[1] >= opEnd) || j >= authorships.length - 1) {
|
||||||
if (authorship[1] < opStart && authorship[2] > opStart) {
|
if (authorship[1] < opStart && authorship[2] > opStart) {
|
||||||
// divide
|
// divide
|
||||||
let postLength = authorship[2] - opStart
|
const postLength = authorship[2] - opStart
|
||||||
authorship[2] = opStart
|
authorship[2] = opStart
|
||||||
authorship[4] = timestamp
|
authorship[4] = timestamp
|
||||||
authorships.splice(j + 1, 0, [userId, opStart, opEnd, timestamp, timestamp])
|
authorships.splice(j + 1, 0, [userId, opStart, opEnd, timestamp, timestamp])
|
||||||
|
@ -470,13 +470,13 @@ module.exports = function (sequelize, DataTypes) {
|
||||||
}
|
}
|
||||||
index += op.length
|
index += op.length
|
||||||
} else if (ot.TextOperation.isDelete(op)) {
|
} else if (ot.TextOperation.isDelete(op)) {
|
||||||
let opStart = index
|
const opStart = index
|
||||||
let opEnd = index - op
|
const opEnd = index - op
|
||||||
if (operation.length === 1) {
|
if (operation.length === 1) {
|
||||||
authorships = []
|
authorships = []
|
||||||
} else if (authorships.length > 0) {
|
} else if (authorships.length > 0) {
|
||||||
for (let j = 0; j < authorships.length; j++) {
|
for (let j = 0; j < authorships.length; j++) {
|
||||||
let authorship = authorships[j]
|
const authorship = authorships[j]
|
||||||
if (authorship[1] >= opStart && authorship[1] <= opEnd && authorship[2] >= opStart && authorship[2] <= opEnd) {
|
if (authorship[1] >= opStart && authorship[1] <= opEnd && authorship[2] >= opStart && authorship[2] <= opEnd) {
|
||||||
authorships.splice(j, 1)
|
authorships.splice(j, 1)
|
||||||
j -= 1
|
j -= 1
|
||||||
|
@ -501,12 +501,12 @@ module.exports = function (sequelize, DataTypes) {
|
||||||
}
|
}
|
||||||
// merge
|
// merge
|
||||||
for (let j = 0; j < authorships.length; j++) {
|
for (let j = 0; j < authorships.length; j++) {
|
||||||
let authorship = authorships[j]
|
const authorship = authorships[j]
|
||||||
for (let k = j + 1; k < authorships.length; k++) {
|
for (let k = j + 1; k < authorships.length; k++) {
|
||||||
let nextAuthorship = authorships[k]
|
const nextAuthorship = authorships[k]
|
||||||
if (nextAuthorship && authorship[0] === nextAuthorship[0] && authorship[2] === nextAuthorship[1]) {
|
if (nextAuthorship && authorship[0] === nextAuthorship[0] && authorship[2] === nextAuthorship[1]) {
|
||||||
let minTimestamp = Math.min(authorship[3], nextAuthorship[3])
|
const minTimestamp = Math.min(authorship[3], nextAuthorship[3])
|
||||||
let maxTimestamp = Math.max(authorship[3], nextAuthorship[3])
|
const maxTimestamp = Math.max(authorship[3], nextAuthorship[3])
|
||||||
authorships.splice(j, 1, [authorship[0], authorship[1], nextAuthorship[2], minTimestamp, maxTimestamp])
|
authorships.splice(j, 1, [authorship[0], authorship[1], nextAuthorship[2], minTimestamp, maxTimestamp])
|
||||||
authorships.splice(k, 1)
|
authorships.splice(k, 1)
|
||||||
j -= 1
|
j -= 1
|
||||||
|
@ -516,7 +516,7 @@ module.exports = function (sequelize, DataTypes) {
|
||||||
}
|
}
|
||||||
// clear
|
// clear
|
||||||
for (let j = 0; j < authorships.length; j++) {
|
for (let j = 0; j < authorships.length; j++) {
|
||||||
let authorship = authorships[j]
|
const authorship = authorships[j]
|
||||||
if (!authorship[0]) {
|
if (!authorship[0]) {
|
||||||
authorships.splice(j, 1)
|
authorships.splice(j, 1)
|
||||||
j -= 1
|
j -= 1
|
||||||
|
@ -525,13 +525,13 @@ module.exports = function (sequelize, DataTypes) {
|
||||||
return authorships
|
return authorships
|
||||||
}
|
}
|
||||||
Note.transformPatchToOperations = function (patch, contentLength) {
|
Note.transformPatchToOperations = function (patch, contentLength) {
|
||||||
var operations = []
|
const operations = []
|
||||||
if (patch.length > 0) {
|
if (patch.length > 0) {
|
||||||
// calculate original content length
|
// calculate original content length
|
||||||
for (let j = patch.length - 1; j >= 0; j--) {
|
for (let j = patch.length - 1; j >= 0; j--) {
|
||||||
var p = patch[j]
|
const p = patch[j]
|
||||||
for (let i = 0; i < p.diffs.length; i++) {
|
for (let i = 0; i < p.diffs.length; i++) {
|
||||||
var diff = p.diffs[i]
|
const diff = p.diffs[i]
|
||||||
switch (diff[0]) {
|
switch (diff[0]) {
|
||||||
case 1: // insert
|
case 1: // insert
|
||||||
contentLength -= diff[1].length
|
contentLength -= diff[1].length
|
||||||
|
@ -543,15 +543,15 @@ module.exports = function (sequelize, DataTypes) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// generate operations
|
// generate operations
|
||||||
var bias = 0
|
let bias = 0
|
||||||
var lengthBias = 0
|
let lengthBias = 0
|
||||||
for (let j = 0; j < patch.length; j++) {
|
for (let j = 0; j < patch.length; j++) {
|
||||||
var operation = []
|
const operation = []
|
||||||
let p = patch[j]
|
const p = patch[j]
|
||||||
var currIndex = p.start1
|
let currIndex = p.start1
|
||||||
var currLength = contentLength - bias
|
const currLength = contentLength - bias
|
||||||
for (let i = 0; i < p.diffs.length; i++) {
|
for (let i = 0; i < p.diffs.length; i++) {
|
||||||
let diff = p.diffs[i]
|
const diff = p.diffs[i]
|
||||||
switch (diff[0]) {
|
switch (diff[0]) {
|
||||||
case 0: // retain
|
case 0: // retain
|
||||||
if (i === 0) {
|
if (i === 0) {
|
||||||
|
|
|
@ -1,22 +1,22 @@
|
||||||
'use strict'
|
'use strict'
|
||||||
// external modules
|
// external modules
|
||||||
var Sequelize = require('sequelize')
|
const Sequelize = require('sequelize')
|
||||||
var async = require('async')
|
const async = require('async')
|
||||||
var moment = require('moment')
|
const moment = require('moment')
|
||||||
var childProcess = require('child_process')
|
const childProcess = require('child_process')
|
||||||
var shortId = require('shortid')
|
const shortId = require('shortid')
|
||||||
var path = require('path')
|
const path = require('path')
|
||||||
|
|
||||||
var Op = Sequelize.Op
|
const Op = Sequelize.Op
|
||||||
|
|
||||||
// core
|
// core
|
||||||
var logger = require('../logger')
|
const logger = require('../logger')
|
||||||
|
|
||||||
var dmpWorker = createDmpWorker()
|
let dmpWorker = createDmpWorker()
|
||||||
var dmpCallbackCache = {}
|
const dmpCallbackCache = {}
|
||||||
|
|
||||||
function createDmpWorker () {
|
function createDmpWorker () {
|
||||||
var worker = childProcess.fork(path.resolve(__dirname, '../workers/dmpWorker.js'), {
|
const worker = childProcess.fork(path.resolve(__dirname, '../workers/dmpWorker.js'), {
|
||||||
stdio: 'ignore'
|
stdio: 'ignore'
|
||||||
})
|
})
|
||||||
logger.debug('dmp worker process started')
|
logger.debug('dmp worker process started')
|
||||||
|
@ -24,7 +24,7 @@ function createDmpWorker () {
|
||||||
if (!data || !data.msg || !data.cacheKey) {
|
if (!data || !data.msg || !data.cacheKey) {
|
||||||
return logger.error('dmp worker error: not enough data on message')
|
return logger.error('dmp worker error: not enough data on message')
|
||||||
}
|
}
|
||||||
var cacheKey = data.cacheKey
|
const cacheKey = data.cacheKey
|
||||||
switch (data.msg) {
|
switch (data.msg) {
|
||||||
case 'error':
|
case 'error':
|
||||||
dmpCallbackCache[cacheKey](data.error, null)
|
dmpCallbackCache[cacheKey](data.error, null)
|
||||||
|
@ -44,7 +44,7 @@ function createDmpWorker () {
|
||||||
|
|
||||||
function sendDmpWorker (data, callback) {
|
function sendDmpWorker (data, callback) {
|
||||||
if (!dmpWorker) dmpWorker = createDmpWorker()
|
if (!dmpWorker) dmpWorker = createDmpWorker()
|
||||||
var cacheKey = Date.now() + '_' + shortId.generate()
|
const cacheKey = Date.now() + '_' + shortId.generate()
|
||||||
dmpCallbackCache[cacheKey] = callback
|
dmpCallbackCache[cacheKey] = callback
|
||||||
data = Object.assign(data, {
|
data = Object.assign(data, {
|
||||||
cacheKey: cacheKey
|
cacheKey: cacheKey
|
||||||
|
@ -53,7 +53,7 @@ function sendDmpWorker (data, callback) {
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = function (sequelize, DataTypes) {
|
module.exports = function (sequelize, DataTypes) {
|
||||||
var Revision = sequelize.define('Revision', {
|
const Revision = sequelize.define('Revision', {
|
||||||
id: {
|
id: {
|
||||||
type: DataTypes.UUID,
|
type: DataTypes.UUID,
|
||||||
primaryKey: true,
|
primaryKey: true,
|
||||||
|
@ -116,9 +116,9 @@ module.exports = function (sequelize, DataTypes) {
|
||||||
},
|
},
|
||||||
order: [['createdAt', 'DESC']]
|
order: [['createdAt', 'DESC']]
|
||||||
}).then(function (revisions) {
|
}).then(function (revisions) {
|
||||||
var data = []
|
const data = []
|
||||||
for (var i = 0, l = revisions.length; i < l; i++) {
|
for (let i = 0, l = revisions.length; i < l; i++) {
|
||||||
var revision = revisions[i]
|
const revision = revisions[i]
|
||||||
data.push({
|
data.push({
|
||||||
time: moment(revision.createdAt).valueOf(),
|
time: moment(revision.createdAt).valueOf(),
|
||||||
length: revision.length
|
length: revision.length
|
||||||
|
@ -199,12 +199,12 @@ module.exports = function (sequelize, DataTypes) {
|
||||||
}
|
}
|
||||||
}).then(function (notes) {
|
}).then(function (notes) {
|
||||||
if (notes.length <= 0) return callback(null, notes)
|
if (notes.length <= 0) return callback(null, notes)
|
||||||
var savedNotes = []
|
const savedNotes = []
|
||||||
async.each(notes, function (note, _callback) {
|
async.each(notes, function (note, _callback) {
|
||||||
// revision saving policy: note not been modified for 5 mins or not save for 10 mins
|
// revision saving policy: note not been modified for 5 mins or not save for 10 mins
|
||||||
if (note.lastchangeAt && note.savedAt) {
|
if (note.lastchangeAt && note.savedAt) {
|
||||||
var lastchangeAt = moment(note.lastchangeAt)
|
const lastchangeAt = moment(note.lastchangeAt)
|
||||||
var savedAt = moment(note.savedAt)
|
const savedAt = moment(note.savedAt)
|
||||||
if (moment().isAfter(lastchangeAt.add(5, 'minutes'))) {
|
if (moment().isAfter(lastchangeAt.add(5, 'minutes'))) {
|
||||||
savedNotes.push(note)
|
savedNotes.push(note)
|
||||||
Revision.saveNoteRevision(note, _callback)
|
Revision.saveNoteRevision(note, _callback)
|
||||||
|
@ -223,7 +223,7 @@ module.exports = function (sequelize, DataTypes) {
|
||||||
return callback(err, null)
|
return callback(err, null)
|
||||||
}
|
}
|
||||||
// return null when no notes need saving at this moment but have delayed tasks to be done
|
// return null when no notes need saving at this moment but have delayed tasks to be done
|
||||||
var result = ((savedNotes.length === 0) && (notes.length > savedNotes.length)) ? null : savedNotes
|
const result = ((savedNotes.length === 0) && (notes.length > savedNotes.length)) ? null : savedNotes
|
||||||
return callback(null, result)
|
return callback(null, result)
|
||||||
})
|
})
|
||||||
}).catch(function (err) {
|
}).catch(function (err) {
|
||||||
|
@ -250,9 +250,9 @@ module.exports = function (sequelize, DataTypes) {
|
||||||
return callback(err, null)
|
return callback(err, null)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
var latestRevision = revisions[0]
|
const latestRevision = revisions[0]
|
||||||
var lastContent = latestRevision.content || latestRevision.lastContent
|
const lastContent = latestRevision.content || latestRevision.lastContent
|
||||||
var content = note.content
|
const content = note.content
|
||||||
sendDmpWorker({
|
sendDmpWorker({
|
||||||
msg: 'create patch',
|
msg: 'create patch',
|
||||||
lastDoc: lastContent,
|
lastDoc: lastContent,
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
'use strict'
|
'use strict'
|
||||||
// external modules
|
// external modules
|
||||||
var shortId = require('shortid')
|
const shortId = require('shortid')
|
||||||
|
|
||||||
module.exports = function (sequelize, DataTypes) {
|
module.exports = function (sequelize, DataTypes) {
|
||||||
var Temp = sequelize.define('Temp', {
|
const Temp = sequelize.define('Temp', {
|
||||||
id: {
|
id: {
|
||||||
type: DataTypes.STRING,
|
type: DataTypes.STRING,
|
||||||
primaryKey: true,
|
primaryKey: true,
|
||||||
|
|
|
@ -17,7 +17,7 @@ const logger = require('../logger')
|
||||||
const { generateAvatarURL } = require('../letter-avatars')
|
const { generateAvatarURL } = require('../letter-avatars')
|
||||||
|
|
||||||
module.exports = function (sequelize, DataTypes) {
|
module.exports = function (sequelize, DataTypes) {
|
||||||
var User = sequelize.define('User', {
|
const User = sequelize.define('User', {
|
||||||
id: {
|
id: {
|
||||||
type: DataTypes.UUID,
|
type: DataTypes.UUID,
|
||||||
primaryKey: true,
|
primaryKey: true,
|
||||||
|
@ -91,7 +91,7 @@ module.exports = function (sequelize, DataTypes) {
|
||||||
return profile
|
return profile
|
||||||
}
|
}
|
||||||
User.parsePhotoByProfile = function (profile, bigger) {
|
User.parsePhotoByProfile = function (profile, bigger) {
|
||||||
var photo = null
|
let photo = null
|
||||||
switch (profile.provider) {
|
switch (profile.provider) {
|
||||||
case 'facebook':
|
case 'facebook':
|
||||||
photo = 'https://graph.facebook.com/' + profile.id + '/picture'
|
photo = 'https://graph.facebook.com/' + profile.id + '/picture'
|
||||||
|
|
245
lib/realtime.js
245
lib/realtime.js
|
@ -1,25 +1,25 @@
|
||||||
'use strict'
|
'use strict'
|
||||||
// realtime
|
// realtime
|
||||||
// external modules
|
// external modules
|
||||||
var cookie = require('cookie')
|
const cookie = require('cookie')
|
||||||
var cookieParser = require('cookie-parser')
|
const cookieParser = require('cookie-parser')
|
||||||
var async = require('async')
|
const async = require('async')
|
||||||
var randomcolor = require('randomcolor')
|
const randomcolor = require('randomcolor')
|
||||||
var Chance = require('chance')
|
const Chance = require('chance')
|
||||||
var chance = new Chance()
|
const chance = new Chance()
|
||||||
var moment = require('moment')
|
const moment = require('moment')
|
||||||
|
|
||||||
// core
|
// core
|
||||||
var config = require('./config')
|
const config = require('./config')
|
||||||
var logger = require('./logger')
|
const logger = require('./logger')
|
||||||
var history = require('./history')
|
const history = require('./history')
|
||||||
var models = require('./models')
|
const models = require('./models')
|
||||||
|
|
||||||
// ot
|
// ot
|
||||||
var ot = require('./ot')
|
const ot = require('./ot')
|
||||||
|
|
||||||
// public
|
// public
|
||||||
var realtime = {
|
const realtime = {
|
||||||
io: null,
|
io: null,
|
||||||
onAuthorizeSuccess: onAuthorizeSuccess,
|
onAuthorizeSuccess: onAuthorizeSuccess,
|
||||||
onAuthorizeFail: onAuthorizeFail,
|
onAuthorizeFail: onAuthorizeFail,
|
||||||
|
@ -41,7 +41,7 @@ function onAuthorizeFail (data, message, error, accept) {
|
||||||
// secure the origin by the cookie
|
// secure the origin by the cookie
|
||||||
function secure (socket, next) {
|
function secure (socket, next) {
|
||||||
try {
|
try {
|
||||||
var handshakeData = socket.request
|
const handshakeData = socket.request
|
||||||
if (handshakeData.headers.cookie) {
|
if (handshakeData.headers.cookie) {
|
||||||
handshakeData.cookie = cookie.parse(handshakeData.headers.cookie)
|
handshakeData.cookie = cookie.parse(handshakeData.headers.cookie)
|
||||||
handshakeData.sessionID = cookieParser.signedCookie(handshakeData.cookie[config.sessionName], config.sessionSecret)
|
handshakeData.sessionID = cookieParser.signedCookie(handshakeData.cookie[config.sessionName], config.sessionSecret)
|
||||||
|
@ -62,7 +62,7 @@ function secure (socket, next) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function emitCheck (note) {
|
function emitCheck (note) {
|
||||||
var out = {
|
const out = {
|
||||||
title: note.title,
|
title: note.title,
|
||||||
updatetime: note.updatetime,
|
updatetime: note.updatetime,
|
||||||
lastchangeuser: note.lastchangeuser,
|
lastchangeuser: note.lastchangeuser,
|
||||||
|
@ -74,12 +74,12 @@ function emitCheck (note) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// actions
|
// actions
|
||||||
var users = {}
|
const users = {}
|
||||||
var notes = {}
|
const notes = {}
|
||||||
// update when the note is dirty
|
// update when the note is dirty
|
||||||
setInterval(function () {
|
setInterval(function () {
|
||||||
async.each(Object.keys(notes), function (key, callback) {
|
async.each(Object.keys(notes), function (key, callback) {
|
||||||
var note = notes[key]
|
const note = notes[key]
|
||||||
if (note.server.isDirty) {
|
if (note.server.isDirty) {
|
||||||
logger.debug(`updater found dirty note: ${key}`)
|
logger.debug(`updater found dirty note: ${key}`)
|
||||||
note.server.isDirty = false
|
note.server.isDirty = false
|
||||||
|
@ -93,8 +93,8 @@ setInterval(function () {
|
||||||
logger.error('note not found: ', note.id)
|
logger.error('note not found: ', note.id)
|
||||||
}
|
}
|
||||||
if (err || !_note) {
|
if (err || !_note) {
|
||||||
for (var i = 0, l = note.socks.length; i < l; i++) {
|
for (let i = 0, l = note.socks.length; i < l; i++) {
|
||||||
var sock = note.socks[i]
|
const sock = note.socks[i]
|
||||||
if (typeof sock !== 'undefined' && sock) {
|
if (typeof sock !== 'undefined' && sock) {
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
sock.disconnect(true)
|
sock.disconnect(true)
|
||||||
|
@ -123,7 +123,7 @@ function updateNote (note, callback) {
|
||||||
}).then(function (_note) {
|
}).then(function (_note) {
|
||||||
if (!_note) return callback(null, null)
|
if (!_note) return callback(null, null)
|
||||||
// update user note history
|
// update user note history
|
||||||
var tempUsers = Object.assign({}, note.tempUsers)
|
const tempUsers = Object.assign({}, note.tempUsers)
|
||||||
note.tempUsers = {}
|
note.tempUsers = {}
|
||||||
Object.keys(tempUsers).forEach(function (key) {
|
Object.keys(tempUsers).forEach(function (key) {
|
||||||
updateHistory(key, note, tempUsers[key])
|
updateHistory(key, note, tempUsers[key])
|
||||||
|
@ -157,9 +157,9 @@ function updateNote (note, callback) {
|
||||||
|
|
||||||
function finishUpdateNote (note, _note, callback) {
|
function finishUpdateNote (note, _note, callback) {
|
||||||
if (!note || !note.server) return callback(null, null)
|
if (!note || !note.server) return callback(null, null)
|
||||||
var body = note.server.document
|
const body = note.server.document
|
||||||
var title = note.title = models.Note.parseNoteTitle(body)
|
const title = note.title = models.Note.parseNoteTitle(body)
|
||||||
var values = {
|
const values = {
|
||||||
title: title,
|
title: title,
|
||||||
content: body,
|
content: body,
|
||||||
authorship: note.authorship,
|
authorship: note.authorship,
|
||||||
|
@ -178,7 +178,7 @@ function finishUpdateNote (note, _note, callback) {
|
||||||
// clean when user not in any rooms or user not in connected list
|
// clean when user not in any rooms or user not in connected list
|
||||||
setInterval(function () {
|
setInterval(function () {
|
||||||
async.each(Object.keys(users), function (key, callback) {
|
async.each(Object.keys(users), function (key, callback) {
|
||||||
var socket = realtime.io.sockets.connected[key]
|
let socket = realtime.io.sockets.connected[key]
|
||||||
if ((!socket && users[key]) ||
|
if ((!socket && users[key]) ||
|
||||||
(socket && (!socket.rooms || socket.rooms.length <= 0))) {
|
(socket && (!socket.rooms || socket.rooms.length <= 0))) {
|
||||||
logger.debug(`cleaner found redundant user: ${key}`)
|
logger.debug(`cleaner found redundant user: ${key}`)
|
||||||
|
@ -196,7 +196,7 @@ setInterval(function () {
|
||||||
})
|
})
|
||||||
}, 60000)
|
}, 60000)
|
||||||
|
|
||||||
var saverSleep = false
|
let saverSleep = false
|
||||||
// save note revision in interval
|
// save note revision in interval
|
||||||
setInterval(function () {
|
setInterval(function () {
|
||||||
if (saverSleep) return
|
if (saverSleep) return
|
||||||
|
@ -210,11 +210,11 @@ setInterval(function () {
|
||||||
|
|
||||||
function getStatus (callback) {
|
function getStatus (callback) {
|
||||||
models.Note.count().then(function (notecount) {
|
models.Note.count().then(function (notecount) {
|
||||||
var distinctaddresses = []
|
const distinctaddresses = []
|
||||||
var regaddresses = []
|
const regaddresses = []
|
||||||
var distinctregaddresses = []
|
const distinctregaddresses = []
|
||||||
Object.keys(users).forEach(function (key) {
|
Object.keys(users).forEach(function (key) {
|
||||||
var user = users[key]
|
const user = users[key]
|
||||||
if (!user) return
|
if (!user) return
|
||||||
let found = false
|
let found = false
|
||||||
for (let i = 0; i < distinctaddresses.length; i++) {
|
for (let i = 0; i < distinctaddresses.length; i++) {
|
||||||
|
@ -241,20 +241,22 @@ function getStatus (callback) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
models.User.count().then(function (regcount) {
|
models.User.count().then(function (regcount) {
|
||||||
// eslint-disable-next-line standard/no-callback-literal
|
return callback
|
||||||
return callback ? callback({
|
// eslint-disable-next-line node/no-callback-literal
|
||||||
onlineNotes: Object.keys(notes).length,
|
? callback({
|
||||||
onlineUsers: Object.keys(users).length,
|
onlineNotes: Object.keys(notes).length,
|
||||||
distinctOnlineUsers: distinctaddresses.length,
|
onlineUsers: Object.keys(users).length,
|
||||||
notesCount: notecount,
|
distinctOnlineUsers: distinctaddresses.length,
|
||||||
registeredUsers: regcount,
|
notesCount: notecount,
|
||||||
onlineRegisteredUsers: regaddresses.length,
|
registeredUsers: regcount,
|
||||||
distinctOnlineRegisteredUsers: distinctregaddresses.length,
|
onlineRegisteredUsers: regaddresses.length,
|
||||||
isConnectionBusy: isConnectionBusy,
|
distinctOnlineRegisteredUsers: distinctregaddresses.length,
|
||||||
connectionSocketQueueLength: connectionSocketQueue.length,
|
isConnectionBusy: isConnectionBusy,
|
||||||
isDisconnectBusy: isDisconnectBusy,
|
connectionSocketQueueLength: connectionSocketQueue.length,
|
||||||
disconnectSocketQueueLength: disconnectSocketQueue.length
|
isDisconnectBusy: isDisconnectBusy,
|
||||||
}) : null
|
disconnectSocketQueueLength: disconnectSocketQueue.length
|
||||||
|
})
|
||||||
|
: null
|
||||||
}).catch(function (err) {
|
}).catch(function (err) {
|
||||||
return logger.error('count user failed: ' + err)
|
return logger.error('count user failed: ' + err)
|
||||||
})
|
})
|
||||||
|
@ -282,7 +284,7 @@ function extractNoteIdFromSocket (socket) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseNoteIdFromSocket (socket, callback) {
|
function parseNoteIdFromSocket (socket, callback) {
|
||||||
var noteId = extractNoteIdFromSocket(socket)
|
const noteId = extractNoteIdFromSocket(socket)
|
||||||
if (!noteId) {
|
if (!noteId) {
|
||||||
return callback(null, null)
|
return callback(null, null)
|
||||||
}
|
}
|
||||||
|
@ -293,32 +295,32 @@ function parseNoteIdFromSocket (socket, callback) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function emitOnlineUsers (socket) {
|
function emitOnlineUsers (socket) {
|
||||||
var noteId = socket.noteId
|
const noteId = socket.noteId
|
||||||
if (!noteId || !notes[noteId]) return
|
if (!noteId || !notes[noteId]) return
|
||||||
var users = []
|
const users = []
|
||||||
Object.keys(notes[noteId].users).forEach(function (key) {
|
Object.keys(notes[noteId].users).forEach(function (key) {
|
||||||
var user = notes[noteId].users[key]
|
const user = notes[noteId].users[key]
|
||||||
if (user) { users.push(buildUserOutData(user)) }
|
if (user) { users.push(buildUserOutData(user)) }
|
||||||
})
|
})
|
||||||
var out = {
|
const out = {
|
||||||
users: users
|
users: users
|
||||||
}
|
}
|
||||||
realtime.io.to(noteId).emit('online users', out)
|
realtime.io.to(noteId).emit('online users', out)
|
||||||
}
|
}
|
||||||
|
|
||||||
function emitUserStatus (socket) {
|
function emitUserStatus (socket) {
|
||||||
var noteId = socket.noteId
|
const noteId = socket.noteId
|
||||||
var user = users[socket.id]
|
const user = users[socket.id]
|
||||||
if (!noteId || !notes[noteId] || !user) return
|
if (!noteId || !notes[noteId] || !user) return
|
||||||
var out = buildUserOutData(user)
|
const out = buildUserOutData(user)
|
||||||
socket.broadcast.to(noteId).emit('user status', out)
|
socket.broadcast.to(noteId).emit('user status', out)
|
||||||
}
|
}
|
||||||
|
|
||||||
function emitRefresh (socket) {
|
function emitRefresh (socket) {
|
||||||
var noteId = socket.noteId
|
const noteId = socket.noteId
|
||||||
if (!noteId || !notes[noteId]) return
|
if (!noteId || !notes[noteId]) return
|
||||||
var note = notes[noteId]
|
const note = notes[noteId]
|
||||||
var out = {
|
const out = {
|
||||||
title: note.title,
|
title: note.title,
|
||||||
docmaxlength: config.documentMaxLength,
|
docmaxlength: config.documentMaxLength,
|
||||||
owner: note.owner,
|
owner: note.owner,
|
||||||
|
@ -335,7 +337,7 @@ function emitRefresh (socket) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function isDuplicatedInSocketQueue (queue, socket) {
|
function isDuplicatedInSocketQueue (queue, socket) {
|
||||||
for (var i = 0; i < queue.length; i++) {
|
for (let i = 0; i < queue.length; i++) {
|
||||||
if (queue[i] && queue[i].id === socket.id) {
|
if (queue[i] && queue[i].id === socket.id) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -344,7 +346,7 @@ function isDuplicatedInSocketQueue (queue, socket) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function clearSocketQueue (queue, socket) {
|
function clearSocketQueue (queue, socket) {
|
||||||
for (var i = 0; i < queue.length; i++) {
|
for (let i = 0; i < queue.length; i++) {
|
||||||
if (!queue[i] || queue[i].id === socket.id) {
|
if (!queue[i] || queue[i].id === socket.id) {
|
||||||
queue.splice(i, 1)
|
queue.splice(i, 1)
|
||||||
i--
|
i--
|
||||||
|
@ -378,10 +380,10 @@ function checkViewPermission (req, note) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var isConnectionBusy = false
|
let isConnectionBusy = false
|
||||||
var connectionSocketQueue = []
|
const connectionSocketQueue = []
|
||||||
var isDisconnectBusy = false
|
let isDisconnectBusy = false
|
||||||
var disconnectSocketQueue = []
|
const disconnectSocketQueue = []
|
||||||
|
|
||||||
function finishConnection (socket, noteId, socketId) {
|
function finishConnection (socket, noteId, socketId) {
|
||||||
// if no valid info provided will drop the client
|
// if no valid info provided will drop the client
|
||||||
|
@ -393,8 +395,8 @@ function finishConnection (socket, noteId, socketId) {
|
||||||
interruptConnection(socket, noteId, socketId)
|
interruptConnection(socket, noteId, socketId)
|
||||||
return failConnection(403, 'connection forbidden', socket)
|
return failConnection(403, 'connection forbidden', socket)
|
||||||
}
|
}
|
||||||
let note = notes[noteId]
|
const note = notes[noteId]
|
||||||
let user = users[socketId]
|
const user = users[socketId]
|
||||||
// update user color to author color
|
// update user color to author color
|
||||||
if (note.authors[user.userid]) {
|
if (note.authors[user.userid]) {
|
||||||
user.color = users[socket.id].color = note.authors[user.userid].color
|
user.color = users[socket.id].color = note.authors[user.userid].color
|
||||||
|
@ -417,7 +419,7 @@ function finishConnection (socket, noteId, socketId) {
|
||||||
connectNextSocket()
|
connectNextSocket()
|
||||||
|
|
||||||
if (config.debug) {
|
if (config.debug) {
|
||||||
let noteId = socket.noteId
|
const noteId = socket.noteId
|
||||||
logger.debug(`SERVER connected a client to [${noteId}]:`)
|
logger.debug(`SERVER connected a client to [${noteId}]:`)
|
||||||
logger.debug(JSON.stringify(user))
|
logger.debug(JSON.stringify(user))
|
||||||
logger.debug(notes)
|
logger.debug(notes)
|
||||||
|
@ -431,13 +433,13 @@ function startConnection (socket) {
|
||||||
if (isConnectionBusy) return
|
if (isConnectionBusy) return
|
||||||
isConnectionBusy = true
|
isConnectionBusy = true
|
||||||
|
|
||||||
var noteId = socket.noteId
|
const noteId = socket.noteId
|
||||||
if (!noteId) {
|
if (!noteId) {
|
||||||
return failConnection(404, 'note id not found', socket)
|
return failConnection(404, 'note id not found', socket)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!notes[noteId]) {
|
if (!notes[noteId]) {
|
||||||
var include = [{
|
const include = [{
|
||||||
model: models.User,
|
model: models.User,
|
||||||
as: 'owner'
|
as: 'owner'
|
||||||
}, {
|
}, {
|
||||||
|
@ -461,21 +463,21 @@ function startConnection (socket) {
|
||||||
if (!note) {
|
if (!note) {
|
||||||
return failConnection(404, 'note not found', socket)
|
return failConnection(404, 'note not found', socket)
|
||||||
}
|
}
|
||||||
var owner = note.ownerId
|
const owner = note.ownerId
|
||||||
var ownerprofile = note.owner ? models.User.getProfile(note.owner) : null
|
const ownerprofile = note.owner ? models.User.getProfile(note.owner) : null
|
||||||
|
|
||||||
var lastchangeuser = note.lastchangeuserId
|
const lastchangeuser = note.lastchangeuserId
|
||||||
var lastchangeuserprofile = note.lastchangeuser ? models.User.getProfile(note.lastchangeuser) : null
|
const lastchangeuserprofile = note.lastchangeuser ? models.User.getProfile(note.lastchangeuser) : null
|
||||||
|
|
||||||
var body = note.content
|
const body = note.content
|
||||||
var createtime = note.createdAt
|
const createtime = note.createdAt
|
||||||
var updatetime = note.lastchangeAt
|
const updatetime = note.lastchangeAt
|
||||||
var server = new ot.EditorSocketIOServer(body, [], noteId, ifMayEdit, operationCallback)
|
const server = new ot.EditorSocketIOServer(body, [], noteId, ifMayEdit, operationCallback)
|
||||||
|
|
||||||
var authors = {}
|
const authors = {}
|
||||||
for (var i = 0; i < note.authors.length; i++) {
|
for (let i = 0; i < note.authors.length; i++) {
|
||||||
var author = note.authors[i]
|
const author = note.authors[i]
|
||||||
var profile = models.User.getProfile(author.user)
|
const profile = models.User.getProfile(author.user)
|
||||||
if (profile) {
|
if (profile) {
|
||||||
authors[author.userId] = {
|
authors[author.userId] = {
|
||||||
userid: author.userId,
|
userid: author.userId,
|
||||||
|
@ -536,16 +538,17 @@ function disconnect (socket) {
|
||||||
if (users[socket.id]) {
|
if (users[socket.id]) {
|
||||||
delete users[socket.id]
|
delete users[socket.id]
|
||||||
}
|
}
|
||||||
var noteId = socket.noteId
|
const noteId = socket.noteId
|
||||||
var note = notes[noteId]
|
const note = notes[noteId]
|
||||||
if (note) {
|
if (note) {
|
||||||
// delete user in users
|
// delete user in users
|
||||||
if (note.users[socket.id]) {
|
if (note.users[socket.id]) {
|
||||||
delete note.users[socket.id]
|
delete note.users[socket.id]
|
||||||
}
|
}
|
||||||
// remove sockets in the note socks
|
// remove sockets in the note socks
|
||||||
|
let index
|
||||||
do {
|
do {
|
||||||
var index = note.socks.indexOf(socket)
|
index = note.socks.indexOf(socket)
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
note.socks.splice(index, 1)
|
note.socks.splice(index, 1)
|
||||||
}
|
}
|
||||||
|
@ -590,7 +593,7 @@ function disconnect (socket) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildUserOutData (user) {
|
function buildUserOutData (user) {
|
||||||
var out = {
|
const out = {
|
||||||
id: user.id,
|
id: user.id,
|
||||||
login: user.login,
|
login: user.login,
|
||||||
userid: user.userid,
|
userid: user.userid,
|
||||||
|
@ -607,7 +610,7 @@ function buildUserOutData (user) {
|
||||||
function updateUserData (socket, user) {
|
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 = models.User.getProfile(socket.request.user)
|
const profile = models.User.getProfile(socket.request.user)
|
||||||
user.photo = profile.photo
|
user.photo = profile.photo
|
||||||
user.name = profile.name
|
user.name = profile.name
|
||||||
user.userid = socket.request.user.id
|
user.userid = socket.request.user.id
|
||||||
|
@ -620,10 +623,10 @@ function updateUserData (socket, user) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function ifMayEdit (socket, callback) {
|
function ifMayEdit (socket, callback) {
|
||||||
var noteId = socket.noteId
|
const noteId = socket.noteId
|
||||||
if (!noteId || !notes[noteId]) return
|
if (!noteId || !notes[noteId]) return
|
||||||
var note = notes[noteId]
|
const note = notes[noteId]
|
||||||
var mayEdit = true
|
let mayEdit = true
|
||||||
switch (note.permission) {
|
switch (note.permission) {
|
||||||
case 'freely':
|
case 'freely':
|
||||||
// not blocking anyone
|
// not blocking anyone
|
||||||
|
@ -650,13 +653,13 @@ function ifMayEdit (socket, callback) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function operationCallback (socket, operation) {
|
function operationCallback (socket, operation) {
|
||||||
var noteId = socket.noteId
|
const noteId = socket.noteId
|
||||||
if (!noteId || !notes[noteId]) return
|
if (!noteId || !notes[noteId]) return
|
||||||
var note = notes[noteId]
|
const note = notes[noteId]
|
||||||
var userId = null
|
let userId = null
|
||||||
// save authors
|
// save authors
|
||||||
if (socket.request.user && socket.request.user.logged_in) {
|
if (socket.request.user && socket.request.user.logged_in) {
|
||||||
var user = users[socket.id]
|
const user = users[socket.id]
|
||||||
if (!user) return
|
if (!user) return
|
||||||
userId = socket.request.user.id
|
userId = socket.request.user.id
|
||||||
if (!note.authors[userId]) {
|
if (!note.authors[userId]) {
|
||||||
|
@ -692,7 +695,7 @@ function operationCallback (socket, operation) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateHistory (userId, note, time) {
|
function updateHistory (userId, note, time) {
|
||||||
var noteId = note.alias ? note.alias : models.Note.encodeNoteId(note.id)
|
const noteId = note.alias ? note.alias : models.Note.encodeNoteId(note.id)
|
||||||
if (note.server) history.updateHistory(userId, noteId, note.server.document, time)
|
if (note.server) history.updateHistory(userId, noteId, note.server.document, time)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -713,12 +716,12 @@ function connection (socket) {
|
||||||
|
|
||||||
// initialize user data
|
// initialize user data
|
||||||
// random color
|
// random color
|
||||||
var color = randomcolor()
|
let color = randomcolor()
|
||||||
// make sure color not duplicated or reach max random count
|
// make sure color not duplicated or reach max random count
|
||||||
if (notes[noteId]) {
|
if (notes[noteId]) {
|
||||||
var randomcount = 0
|
let randomcount = 0
|
||||||
var maxrandomcount = 10
|
const maxrandomcount = 10
|
||||||
var found = false
|
let found = false
|
||||||
do {
|
do {
|
||||||
Object.keys(notes[noteId].users).forEach(function (userId) {
|
Object.keys(notes[noteId].users).forEach(function (userId) {
|
||||||
if (notes[noteId].users[userId].color === color) {
|
if (notes[noteId].users[userId].color === color) {
|
||||||
|
@ -758,8 +761,8 @@ function connection (socket) {
|
||||||
|
|
||||||
// received user status
|
// received user status
|
||||||
socket.on('user status', function (data) {
|
socket.on('user status', function (data) {
|
||||||
var noteId = socket.noteId
|
const noteId = socket.noteId
|
||||||
var user = users[socket.id]
|
const user = users[socket.id]
|
||||||
if (!noteId || !notes[noteId] || !user) return
|
if (!noteId || !notes[noteId] || !user) return
|
||||||
logger.debug(`SERVER received [${noteId}] user status from [${socket.id}]: ${JSON.stringify(data)}`)
|
logger.debug(`SERVER received [${noteId}] user status from [${socket.id}]: ${JSON.stringify(data)}`)
|
||||||
if (data) {
|
if (data) {
|
||||||
|
@ -773,9 +776,9 @@ function connection (socket) {
|
||||||
socket.on('permission', function (permission) {
|
socket.on('permission', function (permission) {
|
||||||
// need login to do more actions
|
// need login to do more actions
|
||||||
if (socket.request.user && socket.request.user.logged_in) {
|
if (socket.request.user && socket.request.user.logged_in) {
|
||||||
var noteId = socket.noteId
|
const noteId = socket.noteId
|
||||||
if (!noteId || !notes[noteId]) return
|
if (!noteId || !notes[noteId]) return
|
||||||
var note = notes[noteId]
|
const note = notes[noteId]
|
||||||
// Only owner can change permission
|
// Only owner can change permission
|
||||||
if (note.owner && note.owner === socket.request.user.id) {
|
if (note.owner && note.owner === socket.request.user.id) {
|
||||||
if (permission === 'freely' && !config.allowAnonymous && !config.allowAnonymousEdits) return
|
if (permission === 'freely' && !config.allowAnonymous && !config.allowAnonymousEdits) return
|
||||||
|
@ -790,12 +793,12 @@ function connection (socket) {
|
||||||
if (!count) {
|
if (!count) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var out = {
|
const out = {
|
||||||
permission: permission
|
permission: permission
|
||||||
}
|
}
|
||||||
realtime.io.to(note.id).emit('permission', out)
|
realtime.io.to(note.id).emit('permission', out)
|
||||||
for (var i = 0, l = note.socks.length; i < l; i++) {
|
for (let i = 0, l = note.socks.length; i < l; i++) {
|
||||||
var sock = note.socks[i]
|
const sock = note.socks[i]
|
||||||
if (typeof sock !== 'undefined' && sock) {
|
if (typeof sock !== 'undefined' && sock) {
|
||||||
// check view permission
|
// check view permission
|
||||||
if (!checkViewPermission(sock.request, note)) {
|
if (!checkViewPermission(sock.request, note)) {
|
||||||
|
@ -819,9 +822,9 @@ function connection (socket) {
|
||||||
socket.on('delete', function () {
|
socket.on('delete', function () {
|
||||||
// need login to do more actions
|
// need login to do more actions
|
||||||
if (socket.request.user && socket.request.user.logged_in) {
|
if (socket.request.user && socket.request.user.logged_in) {
|
||||||
var noteId = socket.noteId
|
const noteId = socket.noteId
|
||||||
if (!noteId || !notes[noteId]) return
|
if (!noteId || !notes[noteId]) return
|
||||||
var note = notes[noteId]
|
const note = notes[noteId]
|
||||||
// Only owner can delete note
|
// Only owner can delete note
|
||||||
if (note.owner && note.owner === socket.request.user.id) {
|
if (note.owner && note.owner === socket.request.user.id) {
|
||||||
models.Note.destroy({
|
models.Note.destroy({
|
||||||
|
@ -830,8 +833,8 @@ function connection (socket) {
|
||||||
}
|
}
|
||||||
}).then(function (count) {
|
}).then(function (count) {
|
||||||
if (!count) return
|
if (!count) return
|
||||||
for (var i = 0, l = note.socks.length; i < l; i++) {
|
for (let i = 0, l = note.socks.length; i < l; i++) {
|
||||||
var sock = note.socks[i]
|
const sock = note.socks[i]
|
||||||
if (typeof sock !== 'undefined' && sock) {
|
if (typeof sock !== 'undefined' && sock) {
|
||||||
sock.emit('delete')
|
sock.emit('delete')
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
|
@ -849,9 +852,9 @@ function connection (socket) {
|
||||||
// reveiced when user logout or changed
|
// reveiced when user logout or changed
|
||||||
socket.on('user changed', function () {
|
socket.on('user changed', function () {
|
||||||
logger.info('user changed')
|
logger.info('user changed')
|
||||||
var noteId = socket.noteId
|
const noteId = socket.noteId
|
||||||
if (!noteId || !notes[noteId]) return
|
if (!noteId || !notes[noteId]) return
|
||||||
var user = notes[noteId].users[socket.id]
|
const user = notes[noteId].users[socket.id]
|
||||||
if (!user) return
|
if (!user) return
|
||||||
updateUserData(socket, user)
|
updateUserData(socket, user)
|
||||||
emitOnlineUsers(socket)
|
emitOnlineUsers(socket)
|
||||||
|
@ -859,14 +862,14 @@ function connection (socket) {
|
||||||
|
|
||||||
// received sync of online users request
|
// received sync of online users request
|
||||||
socket.on('online users', function () {
|
socket.on('online users', function () {
|
||||||
var noteId = socket.noteId
|
const noteId = socket.noteId
|
||||||
if (!noteId || !notes[noteId]) return
|
if (!noteId || !notes[noteId]) return
|
||||||
var users = []
|
const users = []
|
||||||
Object.keys(notes[noteId].users).forEach(function (key) {
|
Object.keys(notes[noteId].users).forEach(function (key) {
|
||||||
var user = notes[noteId].users[key]
|
const user = notes[noteId].users[key]
|
||||||
if (user) { users.push(buildUserOutData(user)) }
|
if (user) { users.push(buildUserOutData(user)) }
|
||||||
})
|
})
|
||||||
var out = {
|
const out = {
|
||||||
users: users
|
users: users
|
||||||
}
|
}
|
||||||
socket.emit('online users', out)
|
socket.emit('online users', out)
|
||||||
|
@ -882,31 +885,31 @@ function connection (socket) {
|
||||||
|
|
||||||
// received cursor focus
|
// received cursor focus
|
||||||
socket.on('cursor focus', function (data) {
|
socket.on('cursor focus', function (data) {
|
||||||
var noteId = socket.noteId
|
const noteId = socket.noteId
|
||||||
var user = users[socket.id]
|
const user = users[socket.id]
|
||||||
if (!noteId || !notes[noteId] || !user) return
|
if (!noteId || !notes[noteId] || !user) return
|
||||||
user.cursor = data
|
user.cursor = data
|
||||||
var out = buildUserOutData(user)
|
const out = buildUserOutData(user)
|
||||||
socket.broadcast.to(noteId).emit('cursor focus', out)
|
socket.broadcast.to(noteId).emit('cursor focus', out)
|
||||||
})
|
})
|
||||||
|
|
||||||
// received cursor activity
|
// received cursor activity
|
||||||
socket.on('cursor activity', function (data) {
|
socket.on('cursor activity', function (data) {
|
||||||
var noteId = socket.noteId
|
const noteId = socket.noteId
|
||||||
var user = users[socket.id]
|
const user = users[socket.id]
|
||||||
if (!noteId || !notes[noteId] || !user) return
|
if (!noteId || !notes[noteId] || !user) return
|
||||||
user.cursor = data
|
user.cursor = data
|
||||||
var out = buildUserOutData(user)
|
const out = buildUserOutData(user)
|
||||||
socket.broadcast.to(noteId).emit('cursor activity', out)
|
socket.broadcast.to(noteId).emit('cursor activity', out)
|
||||||
})
|
})
|
||||||
|
|
||||||
// received cursor blur
|
// received cursor blur
|
||||||
socket.on('cursor blur', function () {
|
socket.on('cursor blur', function () {
|
||||||
var noteId = socket.noteId
|
const noteId = socket.noteId
|
||||||
var user = users[socket.id]
|
const user = users[socket.id]
|
||||||
if (!noteId || !notes[noteId] || !user) return
|
if (!noteId || !notes[noteId] || !user) return
|
||||||
user.cursor = null
|
user.cursor = null
|
||||||
var out = {
|
const out = {
|
||||||
id: socket.id
|
id: socket.id
|
||||||
}
|
}
|
||||||
socket.broadcast.to(noteId).emit('cursor blur', out)
|
socket.broadcast.to(noteId).emit('cursor blur', out)
|
||||||
|
|
|
@ -1,28 +1,28 @@
|
||||||
'use strict'
|
'use strict'
|
||||||
// response
|
// response
|
||||||
// external modules
|
// external modules
|
||||||
var fs = require('fs')
|
const fs = require('fs')
|
||||||
var path = require('path')
|
const path = require('path')
|
||||||
var request = require('request')
|
const request = require('request')
|
||||||
// core
|
// core
|
||||||
var config = require('./config')
|
const config = require('./config')
|
||||||
var logger = require('./logger')
|
const logger = require('./logger')
|
||||||
var models = require('./models')
|
const models = require('./models')
|
||||||
const noteUtil = require('./web/note/util')
|
const noteUtil = require('./web/note/util')
|
||||||
const errors = require('./errors')
|
const errors = require('./errors')
|
||||||
|
|
||||||
// public
|
// public
|
||||||
var response = {
|
const response = {
|
||||||
showIndex: showIndex,
|
showIndex: showIndex,
|
||||||
githubActions: githubActions,
|
githubActions: githubActions,
|
||||||
gitlabActions: gitlabActions
|
gitlabActions: gitlabActions
|
||||||
}
|
}
|
||||||
|
|
||||||
function showIndex (req, res, next) {
|
function showIndex (req, res, next) {
|
||||||
var authStatus = req.isAuthenticated()
|
const authStatus = req.isAuthenticated()
|
||||||
var deleteToken = ''
|
const deleteToken = ''
|
||||||
|
|
||||||
var data = {
|
const data = {
|
||||||
signin: authStatus,
|
signin: authStatus,
|
||||||
infoMessage: req.flash('info'),
|
infoMessage: req.flash('info'),
|
||||||
errorMessage: req.flash('error'),
|
errorMessage: req.flash('error'),
|
||||||
|
@ -49,9 +49,9 @@ function showIndex (req, res, next) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function githubActions (req, res, next) {
|
function githubActions (req, res, next) {
|
||||||
var noteId = req.params.noteId
|
const noteId = req.params.noteId
|
||||||
noteUtil.findNote(req, res, function (note) {
|
noteUtil.findNote(req, res, function (note) {
|
||||||
var action = req.params.action
|
const action = req.params.action
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case 'gist':
|
case 'gist':
|
||||||
githubActionGist(req, res, note)
|
githubActionGist(req, res, note)
|
||||||
|
@ -64,41 +64,41 @@ function githubActions (req, res, next) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function githubActionGist (req, res, note) {
|
function githubActionGist (req, res, note) {
|
||||||
var code = req.query.code
|
const code = req.query.code
|
||||||
var state = req.query.state
|
const state = req.query.state
|
||||||
if (!code || !state) {
|
if (!code || !state) {
|
||||||
return errors.errorForbidden(res)
|
return errors.errorForbidden(res)
|
||||||
} else {
|
} else {
|
||||||
var data = {
|
const data = {
|
||||||
client_id: config.github.clientID,
|
client_id: config.github.clientID,
|
||||||
client_secret: config.github.clientSecret,
|
client_secret: config.github.clientSecret,
|
||||||
code: code,
|
code: code,
|
||||||
state: state
|
state: state
|
||||||
}
|
}
|
||||||
var authUrl = 'https://github.com/login/oauth/access_token'
|
const authUrl = 'https://github.com/login/oauth/access_token'
|
||||||
request({
|
request({
|
||||||
url: authUrl,
|
url: authUrl,
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
json: data
|
json: data
|
||||||
}, function (error, httpResponse, body) {
|
}, function (error, httpResponse, body) {
|
||||||
if (!error && httpResponse.statusCode === 200) {
|
if (!error && httpResponse.statusCode === 200) {
|
||||||
var accessToken = body.access_token
|
const accessToken = body.access_token
|
||||||
if (accessToken) {
|
if (accessToken) {
|
||||||
var content = note.content
|
const content = note.content
|
||||||
var title = models.Note.decodeTitle(note.title)
|
const title = models.Note.decodeTitle(note.title)
|
||||||
var filename = title.replace('/', ' ') + '.md'
|
const filename = title.replace('/', ' ') + '.md'
|
||||||
var gist = {
|
const gist = {
|
||||||
'files': {}
|
files: {}
|
||||||
}
|
}
|
||||||
gist.files[filename] = {
|
gist.files[filename] = {
|
||||||
'content': content
|
content: content
|
||||||
}
|
}
|
||||||
var gistUrl = 'https://api.github.com/gists'
|
const gistUrl = 'https://api.github.com/gists'
|
||||||
request({
|
request({
|
||||||
url: gistUrl,
|
url: gistUrl,
|
||||||
headers: {
|
headers: {
|
||||||
'User-Agent': 'HedgeDoc',
|
'User-Agent': 'HedgeDoc',
|
||||||
'Authorization': 'token ' + accessToken
|
Authorization: 'token ' + accessToken
|
||||||
},
|
},
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
json: gist
|
json: gist
|
||||||
|
@ -121,9 +121,9 @@ function githubActionGist (req, res, note) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function gitlabActions (req, res, next) {
|
function gitlabActions (req, res, next) {
|
||||||
var noteId = req.params.noteId
|
const noteId = req.params.noteId
|
||||||
noteUtil.findNote(req, res, function (note) {
|
noteUtil.findNote(req, res, function (note) {
|
||||||
var action = req.params.action
|
const action = req.params.action
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case 'projects':
|
case 'projects':
|
||||||
gitlabActionProjects(req, res, note)
|
gitlabActionProjects(req, res, note)
|
||||||
|
@ -143,7 +143,7 @@ function gitlabActionProjects (req, res, note) {
|
||||||
}
|
}
|
||||||
}).then(function (user) {
|
}).then(function (user) {
|
||||||
if (!user) { return errors.errorNotFound(res) }
|
if (!user) { return errors.errorNotFound(res) }
|
||||||
var ret = { baseURL: config.gitlab.baseURL, version: config.gitlab.version }
|
const ret = { baseURL: config.gitlab.baseURL, version: config.gitlab.version }
|
||||||
ret.accesstoken = user.accessToken
|
ret.accesstoken = user.accessToken
|
||||||
ret.profileid = user.profileid
|
ret.profileid = user.profileid
|
||||||
request(
|
request(
|
||||||
|
|
|
@ -5,7 +5,7 @@ exports.isSQLite = function isSQLite (sequelize) {
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.getImageMimeType = function getImageMimeType (imagePath) {
|
exports.getImageMimeType = function getImageMimeType (imagePath) {
|
||||||
var fileExtension = /[^.]+$/.exec(imagePath)
|
const fileExtension = /[^.]+$/.exec(imagePath)
|
||||||
|
|
||||||
switch (fileExtension[0].toLowerCase()) {
|
switch (fileExtension[0].toLowerCase()) {
|
||||||
case 'bmp':
|
case 'bmp':
|
||||||
|
|
|
@ -6,7 +6,7 @@ const DropboxStrategy = require('passport-dropbox-oauth2').Strategy
|
||||||
const config = require('../../../config')
|
const config = require('../../../config')
|
||||||
const { passportGeneralCallback } = require('../utils')
|
const { passportGeneralCallback } = require('../utils')
|
||||||
|
|
||||||
let dropboxAuth = module.exports = Router()
|
const dropboxAuth = module.exports = Router()
|
||||||
|
|
||||||
passport.use(new DropboxStrategy({
|
passport.use(new DropboxStrategy({
|
||||||
apiVersion: '2',
|
apiVersion: '2',
|
||||||
|
|
|
@ -10,7 +10,7 @@ const logger = require('../../../logger')
|
||||||
const { urlencodedParser } = require('../../utils')
|
const { urlencodedParser } = require('../../utils')
|
||||||
const errors = require('../../../errors')
|
const errors = require('../../../errors')
|
||||||
|
|
||||||
let emailAuth = module.exports = Router()
|
const emailAuth = module.exports = Router()
|
||||||
|
|
||||||
passport.use(new LocalStrategy({
|
passport.use(new LocalStrategy({
|
||||||
usernameField: 'email'
|
usernameField: 'email'
|
||||||
|
|
|
@ -7,7 +7,7 @@ const FacebookStrategy = require('passport-facebook').Strategy
|
||||||
const config = require('../../../config')
|
const config = require('../../../config')
|
||||||
const { passportGeneralCallback } = require('../utils')
|
const { passportGeneralCallback } = require('../utils')
|
||||||
|
|
||||||
let facebookAuth = module.exports = Router()
|
const facebookAuth = module.exports = Router()
|
||||||
|
|
||||||
passport.use(new FacebookStrategy({
|
passport.use(new FacebookStrategy({
|
||||||
clientID: config.facebook.clientID,
|
clientID: config.facebook.clientID,
|
||||||
|
|
|
@ -7,7 +7,7 @@ const config = require('../../../config')
|
||||||
const response = require('../../../response')
|
const response = require('../../../response')
|
||||||
const { passportGeneralCallback } = require('../utils')
|
const { passportGeneralCallback } = require('../utils')
|
||||||
|
|
||||||
let githubAuth = module.exports = Router()
|
const githubAuth = module.exports = Router()
|
||||||
|
|
||||||
passport.use(new GithubStrategy({
|
passport.use(new GithubStrategy({
|
||||||
clientID: config.github.clientID,
|
clientID: config.github.clientID,
|
||||||
|
|
|
@ -7,7 +7,7 @@ const config = require('../../../config')
|
||||||
const response = require('../../../response')
|
const response = require('../../../response')
|
||||||
const { passportGeneralCallback } = require('../utils')
|
const { passportGeneralCallback } = require('../utils')
|
||||||
|
|
||||||
let gitlabAuth = module.exports = Router()
|
const gitlabAuth = module.exports = Router()
|
||||||
|
|
||||||
passport.use(new GitlabStrategy({
|
passport.use(new GitlabStrategy({
|
||||||
baseURL: config.gitlab.baseURL,
|
baseURL: config.gitlab.baseURL,
|
||||||
|
|
|
@ -2,11 +2,11 @@
|
||||||
|
|
||||||
const Router = require('express').Router
|
const Router = require('express').Router
|
||||||
const passport = require('passport')
|
const passport = require('passport')
|
||||||
var GoogleStrategy = require('passport-google-oauth20').Strategy
|
const GoogleStrategy = require('passport-google-oauth20').Strategy
|
||||||
const config = require('../../../config')
|
const config = require('../../../config')
|
||||||
const { passportGeneralCallback } = require('../utils')
|
const { passportGeneralCallback } = require('../utils')
|
||||||
|
|
||||||
let googleAuth = module.exports = Router()
|
const googleAuth = module.exports = Router()
|
||||||
|
|
||||||
passport.use(new GoogleStrategy({
|
passport.use(new GoogleStrategy({
|
||||||
clientID: config.google.clientID,
|
clientID: config.google.clientID,
|
||||||
|
|
|
@ -9,7 +9,7 @@ const logger = require('../../../logger')
|
||||||
const { urlencodedParser } = require('../../utils')
|
const { urlencodedParser } = require('../../utils')
|
||||||
const errors = require('../../../errors')
|
const errors = require('../../../errors')
|
||||||
|
|
||||||
let ldapAuth = module.exports = Router()
|
const ldapAuth = module.exports = Router()
|
||||||
|
|
||||||
passport.use(new LDAPStrategy({
|
passport.use(new LDAPStrategy({
|
||||||
server: {
|
server: {
|
||||||
|
@ -22,7 +22,7 @@ passport.use(new LDAPStrategy({
|
||||||
tlsOptions: config.ldap.tlsOptions || null
|
tlsOptions: config.ldap.tlsOptions || null
|
||||||
}
|
}
|
||||||
}, function (user, done) {
|
}, function (user, done) {
|
||||||
var uuid = user.uidNumber || user.uid || user.sAMAccountName || undefined
|
let uuid = user.uidNumber || user.uid || user.sAMAccountName || undefined
|
||||||
if (config.ldap.useridField && user[config.ldap.useridField]) {
|
if (config.ldap.useridField && user[config.ldap.useridField]) {
|
||||||
uuid = user[config.ldap.useridField]
|
uuid = user[config.ldap.useridField]
|
||||||
}
|
}
|
||||||
|
@ -34,12 +34,12 @@ passport.use(new LDAPStrategy({
|
||||||
'"useridField" option in ldap settings.')
|
'"useridField" option in ldap settings.')
|
||||||
}
|
}
|
||||||
|
|
||||||
var username = uuid
|
let username = uuid
|
||||||
if (config.ldap.usernameField && user[config.ldap.usernameField]) {
|
if (config.ldap.usernameField && user[config.ldap.usernameField]) {
|
||||||
username = user[config.ldap.usernameField]
|
username = user[config.ldap.usernameField]
|
||||||
}
|
}
|
||||||
|
|
||||||
var profile = {
|
const profile = {
|
||||||
id: 'LDAP-' + uuid,
|
id: 'LDAP-' + uuid,
|
||||||
username: username,
|
username: username,
|
||||||
displayName: user.displayName,
|
displayName: user.displayName,
|
||||||
|
@ -48,7 +48,7 @@ passport.use(new LDAPStrategy({
|
||||||
profileUrl: null,
|
profileUrl: null,
|
||||||
provider: 'ldap'
|
provider: 'ldap'
|
||||||
}
|
}
|
||||||
var stringifiedProfile = JSON.stringify(profile)
|
const stringifiedProfile = JSON.stringify(profile)
|
||||||
models.User.findOrCreate({
|
models.User.findOrCreate({
|
||||||
where: {
|
where: {
|
||||||
profileid: profile.id.toString()
|
profileid: profile.id.toString()
|
||||||
|
@ -58,7 +58,7 @@ passport.use(new LDAPStrategy({
|
||||||
}
|
}
|
||||||
}).spread(function (user, created) {
|
}).spread(function (user, created) {
|
||||||
if (user) {
|
if (user) {
|
||||||
var needSave = false
|
let needSave = false
|
||||||
if (user.profile !== stringifiedProfile) {
|
if (user.profile !== stringifiedProfile) {
|
||||||
user.profile = stringifiedProfile
|
user.profile = stringifiedProfile
|
||||||
needSave = true
|
needSave = true
|
||||||
|
|
|
@ -9,9 +9,9 @@ const { passportGeneralCallback } = require('../utils')
|
||||||
|
|
||||||
const mattermost = new Mattermost.Client()
|
const mattermost = new Mattermost.Client()
|
||||||
|
|
||||||
let mattermostAuth = module.exports = Router()
|
const mattermostAuth = module.exports = Router()
|
||||||
|
|
||||||
let mattermostStrategy = new OAuthStrategy({
|
const mattermostStrategy = new OAuthStrategy({
|
||||||
authorizationURL: config.mattermost.baseURL + '/oauth/authorize',
|
authorizationURL: config.mattermost.baseURL + '/oauth/authorize',
|
||||||
tokenURL: config.mattermost.baseURL + '/oauth/access_token',
|
tokenURL: config.mattermost.baseURL + '/oauth/access_token',
|
||||||
clientID: config.mattermost.clientID,
|
clientID: config.mattermost.clientID,
|
||||||
|
|
|
@ -7,7 +7,7 @@ const config = require('../../../config')
|
||||||
const logger = require('../../../logger')
|
const logger = require('../../../logger')
|
||||||
const { passportGeneralCallback } = require('../utils')
|
const { passportGeneralCallback } = require('../utils')
|
||||||
|
|
||||||
let oauth2Auth = module.exports = Router()
|
const oauth2Auth = module.exports = Router()
|
||||||
|
|
||||||
class OAuth2CustomStrategy extends Strategy {
|
class OAuth2CustomStrategy extends Strategy {
|
||||||
constructor (options, verify) {
|
constructor (options, verify) {
|
||||||
|
@ -20,7 +20,7 @@ class OAuth2CustomStrategy extends Strategy {
|
||||||
|
|
||||||
userProfile (accessToken, done) {
|
userProfile (accessToken, done) {
|
||||||
this._oauth2.get(this._userProfileURL, accessToken, function (err, body, res) {
|
this._oauth2.get(this._userProfileURL, accessToken, function (err, body, res) {
|
||||||
var json
|
let json
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
return done(new InternalOAuthError('Failed to fetch user profile', err))
|
return done(new InternalOAuthError('Failed to fetch user profile', err))
|
||||||
|
@ -33,7 +33,7 @@ class OAuth2CustomStrategy extends Strategy {
|
||||||
}
|
}
|
||||||
|
|
||||||
checkAuthorization(json, done)
|
checkAuthorization(json, done)
|
||||||
let profile = parseProfile(json)
|
const profile = parseProfile(json)
|
||||||
profile.provider = 'oauth2'
|
profile.provider = 'oauth2'
|
||||||
|
|
||||||
done(null, profile)
|
done(null, profile)
|
||||||
|
@ -91,7 +91,7 @@ function checkAuthorization (data, done) {
|
||||||
|
|
||||||
OAuth2CustomStrategy.prototype.userProfile = function (accessToken, done) {
|
OAuth2CustomStrategy.prototype.userProfile = function (accessToken, done) {
|
||||||
this._oauth2.get(this._userProfileURL, accessToken, function (err, body, res) {
|
this._oauth2.get(this._userProfileURL, accessToken, function (err, body, res) {
|
||||||
var json
|
let json
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
return done(new InternalOAuthError('Failed to fetch user profile', err))
|
return done(new InternalOAuthError('Failed to fetch user profile', err))
|
||||||
|
@ -104,7 +104,7 @@ OAuth2CustomStrategy.prototype.userProfile = function (accessToken, done) {
|
||||||
}
|
}
|
||||||
|
|
||||||
checkAuthorization(json, done)
|
checkAuthorization(json, done)
|
||||||
let profile = parseProfile(json)
|
const profile = parseProfile(json)
|
||||||
profile.provider = 'oauth2'
|
profile.provider = 'oauth2'
|
||||||
|
|
||||||
done(null, profile)
|
done(null, profile)
|
||||||
|
|
|
@ -8,14 +8,14 @@ const models = require('../../../models')
|
||||||
const logger = require('../../../logger')
|
const logger = require('../../../logger')
|
||||||
const { urlencodedParser } = require('../../utils')
|
const { urlencodedParser } = require('../../utils')
|
||||||
|
|
||||||
let openIDAuth = module.exports = Router()
|
const openIDAuth = module.exports = Router()
|
||||||
|
|
||||||
passport.use(new OpenIDStrategy({
|
passport.use(new OpenIDStrategy({
|
||||||
returnURL: config.serverURL + '/auth/openid/callback',
|
returnURL: config.serverURL + '/auth/openid/callback',
|
||||||
realm: config.serverURL,
|
realm: config.serverURL,
|
||||||
profile: true
|
profile: true
|
||||||
}, function (openid, profile, done) {
|
}, function (openid, profile, done) {
|
||||||
var stringifiedProfile = JSON.stringify(profile)
|
const stringifiedProfile = JSON.stringify(profile)
|
||||||
models.User.findOrCreate({
|
models.User.findOrCreate({
|
||||||
where: {
|
where: {
|
||||||
profileid: openid
|
profileid: openid
|
||||||
|
@ -25,7 +25,7 @@ passport.use(new OpenIDStrategy({
|
||||||
}
|
}
|
||||||
}).spread(function (user, created) {
|
}).spread(function (user, created) {
|
||||||
if (user) {
|
if (user) {
|
||||||
var needSave = false
|
let needSave = false
|
||||||
if (user.profile !== stringifiedProfile) {
|
if (user.profile !== stringifiedProfile) {
|
||||||
user.profile = stringifiedProfile
|
user.profile = stringifiedProfile
|
||||||
needSave = true
|
needSave = true
|
||||||
|
|
|
@ -10,19 +10,21 @@ const { urlencodedParser } = require('../../utils')
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const intersection = function (array1, array2) { return array1.filter((n) => array2.includes(n)) }
|
const intersection = function (array1, array2) { return array1.filter((n) => array2.includes(n)) }
|
||||||
|
|
||||||
let samlAuth = module.exports = Router()
|
const samlAuth = module.exports = Router()
|
||||||
|
|
||||||
passport.use(new SamlStrategy({
|
passport.use(new SamlStrategy({
|
||||||
callbackUrl: config.serverURL + '/auth/saml/callback',
|
callbackUrl: config.serverURL + '/auth/saml/callback',
|
||||||
entryPoint: config.saml.idpSsoUrl,
|
entryPoint: config.saml.idpSsoUrl,
|
||||||
issuer: config.saml.issuer || config.serverURL,
|
issuer: config.saml.issuer || config.serverURL,
|
||||||
privateCert: config.saml.clientCert === undefined ? undefined : (function () {
|
privateCert: config.saml.clientCert === undefined
|
||||||
try {
|
? undefined
|
||||||
return fs.readFileSync(config.saml.clientCert, 'utf-8')
|
: (function () {
|
||||||
} catch (e) {
|
try {
|
||||||
logger.error(`SAML client certificate: ${e.message}`)
|
return fs.readFileSync(config.saml.clientCert, 'utf-8')
|
||||||
}
|
} catch (e) {
|
||||||
}()),
|
logger.error(`SAML client certificate: ${e.message}`)
|
||||||
|
}
|
||||||
|
}()),
|
||||||
cert: (function () {
|
cert: (function () {
|
||||||
try {
|
try {
|
||||||
return fs.readFileSync(config.saml.idpCert, 'utf-8')
|
return fs.readFileSync(config.saml.idpCert, 'utf-8')
|
||||||
|
@ -36,7 +38,7 @@ passport.use(new SamlStrategy({
|
||||||
}, function (user, done) {
|
}, function (user, done) {
|
||||||
// check authorization if needed
|
// check authorization if needed
|
||||||
if (config.saml.externalGroups && config.saml.groupAttribute) {
|
if (config.saml.externalGroups && config.saml.groupAttribute) {
|
||||||
var externalGroups = intersection(config.saml.externalGroups, user[config.saml.groupAttribute])
|
const externalGroups = intersection(config.saml.externalGroups, user[config.saml.groupAttribute])
|
||||||
if (externalGroups.length > 0) {
|
if (externalGroups.length > 0) {
|
||||||
logger.error('saml permission denied: ' + externalGroups.join(', '))
|
logger.error('saml permission denied: ' + externalGroups.join(', '))
|
||||||
return done('Permission denied', null)
|
return done('Permission denied', null)
|
||||||
|
@ -49,8 +51,8 @@ passport.use(new SamlStrategy({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// user creation
|
// user creation
|
||||||
var uuid = user[config.saml.attribute.id] || user.nameID
|
const uuid = user[config.saml.attribute.id] || user.nameID
|
||||||
var profile = {
|
const profile = {
|
||||||
provider: 'saml',
|
provider: 'saml',
|
||||||
id: 'SAML-' + uuid,
|
id: 'SAML-' + uuid,
|
||||||
username: user[config.saml.attribute.username] || user.nameID,
|
username: user[config.saml.attribute.username] || user.nameID,
|
||||||
|
@ -59,7 +61,7 @@ passport.use(new SamlStrategy({
|
||||||
if (profile.emails.length === 0 && config.saml.identifierFormat === 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress') {
|
if (profile.emails.length === 0 && config.saml.identifierFormat === 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress') {
|
||||||
profile.emails.push(user.nameID)
|
profile.emails.push(user.nameID)
|
||||||
}
|
}
|
||||||
var stringifiedProfile = JSON.stringify(profile)
|
const stringifiedProfile = JSON.stringify(profile)
|
||||||
models.User.findOrCreate({
|
models.User.findOrCreate({
|
||||||
where: {
|
where: {
|
||||||
profileid: profile.id.toString()
|
profileid: profile.id.toString()
|
||||||
|
@ -69,7 +71,7 @@ passport.use(new SamlStrategy({
|
||||||
}
|
}
|
||||||
}).spread(function (user, created) {
|
}).spread(function (user, created) {
|
||||||
if (user) {
|
if (user) {
|
||||||
var needSave = false
|
let needSave = false
|
||||||
if (user.profile !== stringifiedProfile) {
|
if (user.profile !== stringifiedProfile) {
|
||||||
user.profile = stringifiedProfile
|
user.profile = stringifiedProfile
|
||||||
needSave = true
|
needSave = true
|
||||||
|
|
|
@ -7,7 +7,7 @@ const TwitterStrategy = require('passport-twitter').Strategy
|
||||||
const config = require('../../../config')
|
const config = require('../../../config')
|
||||||
const { passportGeneralCallback } = require('../utils')
|
const { passportGeneralCallback } = require('../utils')
|
||||||
|
|
||||||
let twitterAuth = module.exports = Router()
|
const twitterAuth = module.exports = Router()
|
||||||
|
|
||||||
passport.use(new TwitterStrategy({
|
passport.use(new TwitterStrategy({
|
||||||
consumerKey: config.twitter.consumerKey,
|
consumerKey: config.twitter.consumerKey,
|
||||||
|
|
|
@ -4,7 +4,7 @@ const models = require('../../models')
|
||||||
const logger = require('../../logger')
|
const logger = require('../../logger')
|
||||||
|
|
||||||
exports.passportGeneralCallback = function callback (accessToken, refreshToken, profile, done) {
|
exports.passportGeneralCallback = function callback (accessToken, refreshToken, profile, done) {
|
||||||
var stringifiedProfile = JSON.stringify(profile)
|
const stringifiedProfile = JSON.stringify(profile)
|
||||||
models.User.findOrCreate({
|
models.User.findOrCreate({
|
||||||
where: {
|
where: {
|
||||||
profileid: profile.id.toString()
|
profileid: profile.id.toString()
|
||||||
|
@ -16,7 +16,7 @@ exports.passportGeneralCallback = function callback (accessToken, refreshToken,
|
||||||
}
|
}
|
||||||
}).spread(function (user, created) {
|
}).spread(function (user, created) {
|
||||||
if (user) {
|
if (user) {
|
||||||
var needSave = false
|
let needSave = false
|
||||||
if (user.profile !== stringifiedProfile) {
|
if (user.profile !== stringifiedProfile) {
|
||||||
user.profile = stringifiedProfile
|
user.profile = stringifiedProfile
|
||||||
needSave = true
|
needSave = true
|
||||||
|
|
|
@ -17,7 +17,7 @@ exports.uploadImage = function (imagePath, callback) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var azureBlobService = azure.createBlobService(config.azure.connectionString)
|
const azureBlobService = azure.createBlobService(config.azure.connectionString)
|
||||||
|
|
||||||
azureBlobService.createContainerIfNotExists(config.azure.container, { publicAccessLevel: 'blob' }, function (err, result, response) {
|
azureBlobService.createContainerIfNotExists(config.azure.container, { publicAccessLevel: 'blob' }, function (err, result, response) {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
|
|
@ -12,20 +12,28 @@ const config = require('../../config')
|
||||||
const logger = require('../../logger')
|
const logger = require('../../logger')
|
||||||
const errors = require('../../errors')
|
const errors = require('../../errors')
|
||||||
|
|
||||||
const imageRouter = module.exports = Router()
|
const imageRouter = (module.exports = Router())
|
||||||
|
|
||||||
async function checkUploadType (filePath) {
|
async function checkUploadType (filePath) {
|
||||||
const typeFromMagic = await FileType.fromFile(filePath)
|
const typeFromMagic = await FileType.fromFile(filePath)
|
||||||
if (typeFromMagic === undefined) {
|
if (typeFromMagic === undefined) {
|
||||||
logger.error(`Image upload error: Could not determine MIME-type`)
|
logger.error('Image upload error: Could not determine MIME-type')
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if (path.extname(filePath) !== '.' + typeFromMagic.ext) {
|
if (path.extname(filePath) !== '.' + typeFromMagic.ext) {
|
||||||
logger.error(`Image upload error: Provided file extension does not match MIME-type`)
|
logger.error(
|
||||||
|
'Image upload error: Provided file extension does not match MIME-type'
|
||||||
|
)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if (!config.allowedUploadMimeTypes.includes(typeFromMagic.mime)) {
|
if (!config.allowedUploadMimeTypes.includes(typeFromMagic.mime)) {
|
||||||
logger.error(`Image upload error: MIME-type "${typeFromMagic.mime}" of uploaded file not allowed, only "${config.allowedUploadMimeTypes.join(', ')}" are allowed`)
|
logger.error(
|
||||||
|
`Image upload error: MIME-type "${
|
||||||
|
typeFromMagic.mime
|
||||||
|
}" of uploaded file not allowed, only "${config.allowedUploadMimeTypes.join(
|
||||||
|
', '
|
||||||
|
)}" are allowed`
|
||||||
|
)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
|
@ -33,12 +41,18 @@ async function checkUploadType (filePath) {
|
||||||
|
|
||||||
// upload image
|
// upload image
|
||||||
imageRouter.post('/uploadimage', function (req, res) {
|
imageRouter.post('/uploadimage', function (req, res) {
|
||||||
if (!req.isAuthenticated() && !config.allowAnonymous && !config.allowAnonymousEdits) {
|
if (
|
||||||
logger.error(`Image upload error: Anonymous edits and therefore uploads are not allowed)`)
|
!req.isAuthenticated() &&
|
||||||
|
!config.allowAnonymous &&
|
||||||
|
!config.allowAnonymousEdits
|
||||||
|
) {
|
||||||
|
logger.error(
|
||||||
|
'Image upload error: Anonymous edits and therefore uploads are not allowed'
|
||||||
|
)
|
||||||
return errors.errorForbidden(res)
|
return errors.errorForbidden(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
var form = new formidable.IncomingForm()
|
const form = new formidable.IncomingForm()
|
||||||
form.keepExtensions = true
|
form.keepExtensions = true
|
||||||
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'hedgedoc-'))
|
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'hedgedoc-'))
|
||||||
form.uploadDir = tmpDir
|
form.uploadDir = tmpDir
|
||||||
|
@ -49,17 +63,21 @@ imageRouter.post('/uploadimage', function (req, res) {
|
||||||
rimraf(tmpDir)
|
rimraf(tmpDir)
|
||||||
return errors.errorForbidden(res)
|
return errors.errorForbidden(res)
|
||||||
} else if (!files.image || !files.image.path) {
|
} else if (!files.image || !files.image.path) {
|
||||||
logger.error(`Image upload error: Upload didn't contain file)`)
|
logger.error("Image upload error: Upload didn't contain file)")
|
||||||
rimraf.sync(tmpDir)
|
rimraf.sync(tmpDir)
|
||||||
return errors.errorBadRequest(res)
|
return errors.errorBadRequest(res)
|
||||||
} else if (!await checkUploadType(files.image.path)) {
|
} else if (!(await checkUploadType(files.image.path))) {
|
||||||
rimraf.sync(tmpDir)
|
rimraf.sync(tmpDir)
|
||||||
return errors.errorBadRequest(res)
|
return errors.errorBadRequest(res)
|
||||||
} else {
|
} else {
|
||||||
logger.debug(`SERVER received uploadimage: ${JSON.stringify(files.image)}`)
|
logger.debug(
|
||||||
|
`SERVER received uploadimage: ${JSON.stringify(files.image)}`
|
||||||
|
)
|
||||||
|
|
||||||
const uploadProvider = require('./' + config.imageUploadType)
|
const uploadProvider = require('./' + config.imageUploadType)
|
||||||
logger.debug(`imageRouter: Uploading ${files.image.path} using ${config.imageUploadType}`)
|
logger.debug(
|
||||||
|
`imageRouter: Uploading ${files.image.path} using ${config.imageUploadType}`
|
||||||
|
)
|
||||||
uploadProvider.uploadImage(files.image.path, function (err, url) {
|
uploadProvider.uploadImage(files.image.path, function (err, url) {
|
||||||
rimraf.sync(tmpDir)
|
rimraf.sync(tmpDir)
|
||||||
if (err !== null) {
|
if (err !== null) {
|
||||||
|
|
|
@ -32,16 +32,16 @@ exports.uploadImage = function (imagePath, callback) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let key = path.join('uploads', path.basename(imagePath))
|
const key = path.join('uploads', path.basename(imagePath))
|
||||||
let protocol = config.minio.secure ? 'https' : 'http'
|
const protocol = config.minio.secure ? 'https' : 'http'
|
||||||
|
|
||||||
minioClient.putObject(config.s3bucket, key, buffer, buffer.size, getImageMimeType(imagePath), function (err, data) {
|
minioClient.putObject(config.s3bucket, key, buffer, buffer.size, getImageMimeType(imagePath), function (err, data) {
|
||||||
if (err) {
|
if (err) {
|
||||||
callback(new Error(err), null)
|
callback(new Error(err), null)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let hidePort = [80, 443].includes(config.minio.port)
|
const hidePort = [80, 443].includes(config.minio.port)
|
||||||
let urlPort = hidePort ? '' : `:${config.minio.port}`
|
const urlPort = hidePort ? '' : `:${config.minio.port}`
|
||||||
callback(null, `${protocol}://${config.minio.endPoint}${urlPort}/${config.s3bucket}/${key}`)
|
callback(null, `${protocol}://${config.minio.endPoint}${urlPort}/${config.s3bucket}/${key}`)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -26,7 +26,7 @@ exports.uploadImage = function (imagePath, callback) {
|
||||||
callback(new Error(err), null)
|
callback(new Error(err), null)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let params = {
|
const params = {
|
||||||
Bucket: config.s3bucket,
|
Bucket: config.s3bucket,
|
||||||
Key: path.join('uploads', path.basename(imagePath)),
|
Key: path.join('uploads', path.basename(imagePath)),
|
||||||
Body: buffer
|
Body: buffer
|
||||||
|
|
|
@ -25,11 +25,11 @@ statusRouter.get('/status', function (req, res, next) {
|
||||||
})
|
})
|
||||||
// get status
|
// get status
|
||||||
statusRouter.get('/temp', function (req, res) {
|
statusRouter.get('/temp', function (req, res) {
|
||||||
var host = req.get('host')
|
const host = req.get('host')
|
||||||
if (config.allowOrigin.indexOf(host) === -1) {
|
if (config.allowOrigin.indexOf(host) === -1) {
|
||||||
errors.errorForbidden(res)
|
errors.errorForbidden(res)
|
||||||
} else {
|
} else {
|
||||||
var tempid = req.query.tempid
|
const tempid = req.query.tempid
|
||||||
if (!tempid) {
|
if (!tempid) {
|
||||||
errors.errorForbidden(res)
|
errors.errorForbidden(res)
|
||||||
} else {
|
} else {
|
||||||
|
@ -60,11 +60,11 @@ statusRouter.get('/temp', function (req, res) {
|
||||||
})
|
})
|
||||||
// post status
|
// post status
|
||||||
statusRouter.post('/temp', urlencodedParser, function (req, res) {
|
statusRouter.post('/temp', urlencodedParser, function (req, res) {
|
||||||
var host = req.get('host')
|
const host = req.get('host')
|
||||||
if (config.allowOrigin.indexOf(host) === -1) {
|
if (config.allowOrigin.indexOf(host) === -1) {
|
||||||
errors.errorForbidden(res)
|
errors.errorForbidden(res)
|
||||||
} else {
|
} else {
|
||||||
var data = req.body.data
|
const data = req.body.data
|
||||||
if (!data) {
|
if (!data) {
|
||||||
errors.errorForbidden(res)
|
errors.errorForbidden(res)
|
||||||
} else {
|
} else {
|
||||||
|
@ -90,7 +90,7 @@ statusRouter.post('/temp', urlencodedParser, function (req, res) {
|
||||||
})
|
})
|
||||||
|
|
||||||
statusRouter.get('/config', function (req, res) {
|
statusRouter.get('/config', function (req, res) {
|
||||||
var data = {
|
const data = {
|
||||||
domain: config.domain,
|
domain: config.domain,
|
||||||
urlpath: config.urlPath,
|
urlpath: config.urlPath,
|
||||||
debug: config.debug,
|
debug: config.debug,
|
||||||
|
|
|
@ -21,7 +21,7 @@ UserRouter.get('/me', function (req, res) {
|
||||||
}
|
}
|
||||||
}).then(function (user) {
|
}).then(function (user) {
|
||||||
if (!user) { return errors.errorNotFound(res) }
|
if (!user) { return errors.errorNotFound(res) }
|
||||||
var profile = models.User.getProfile(user)
|
const profile = models.User.getProfile(user)
|
||||||
res.send({
|
res.send({
|
||||||
status: 'ok',
|
status: 'ok',
|
||||||
id: req.user.id,
|
id: req.user.id,
|
||||||
|
@ -70,7 +70,7 @@ UserRouter.get('/me/delete/:token?', function (req, res) {
|
||||||
UserRouter.get('/me/export', function (req, res) {
|
UserRouter.get('/me/export', function (req, res) {
|
||||||
if (req.isAuthenticated()) {
|
if (req.isAuthenticated()) {
|
||||||
// let output = fs.createWriteStream(__dirname + '/example.zip');
|
// let output = fs.createWriteStream(__dirname + '/example.zip');
|
||||||
let archive = archiver('zip', {
|
const archive = archiver('zip', {
|
||||||
zlib: { level: 3 } // Sets the compression level.
|
zlib: { level: 3 } // Sets the compression level.
|
||||||
})
|
})
|
||||||
res.setHeader('Content-Type', 'application/zip')
|
res.setHeader('Content-Type', 'application/zip')
|
||||||
|
@ -90,13 +90,13 @@ UserRouter.get('/me/export', function (req, res) {
|
||||||
ownerId: user.id
|
ownerId: user.id
|
||||||
}
|
}
|
||||||
}).then(function (notes) {
|
}).then(function (notes) {
|
||||||
let filenames = {}
|
const filenames = {}
|
||||||
async.each(notes, function (note, callback) {
|
async.each(notes, function (note, callback) {
|
||||||
let basename = note.title.replace(/\//g, '-') // Prevent subdirectories
|
const basename = note.title.replace(/\//g, '-') // Prevent subdirectories
|
||||||
let filename
|
let filename
|
||||||
let suffix = ''
|
let suffix = ''
|
||||||
do {
|
do {
|
||||||
let seperator = typeof suffix === 'number' ? '-' : ''
|
const seperator = typeof suffix === 'number' ? '-' : ''
|
||||||
filename = basename + seperator + suffix + '.md'
|
filename = basename + seperator + suffix + '.md'
|
||||||
suffix++
|
suffix++
|
||||||
} while (filenames[filename])
|
} while (filenames[filename])
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
'use strict'
|
'use strict'
|
||||||
// external modules
|
// external modules
|
||||||
var DiffMatchPatch = require('diff-match-patch')
|
const DiffMatchPatch = require('diff-match-patch')
|
||||||
var dmp = new DiffMatchPatch()
|
const dmp = new DiffMatchPatch()
|
||||||
|
|
||||||
// core
|
// core
|
||||||
var logger = require('../logger')
|
const logger = require('../logger')
|
||||||
|
|
||||||
process.on('message', function (data) {
|
process.on('message', function (data) {
|
||||||
if (!data || !data.msg || !data.cacheKey) {
|
if (!data || !data.msg || !data.cacheKey) {
|
||||||
|
@ -12,11 +12,16 @@ process.on('message', function (data) {
|
||||||
}
|
}
|
||||||
switch (data.msg) {
|
switch (data.msg) {
|
||||||
case 'create patch':
|
case 'create patch':
|
||||||
if (!data.hasOwnProperty('lastDoc') || !data.hasOwnProperty('currDoc')) {
|
if (
|
||||||
return logger.error('dmp worker error: not enough data on create patch')
|
!Object.prototype.hasOwnProperty.call(data, 'lastDoc') ||
|
||||||
|
!Object.prototype.hasOwnProperty.call(data, 'currDoc')
|
||||||
|
) {
|
||||||
|
return logger.error(
|
||||||
|
'dmp worker error: not enough data on create patch'
|
||||||
|
)
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
var patch = createPatch(data.lastDoc, data.currDoc)
|
const patch = createPatch(data.lastDoc, data.currDoc)
|
||||||
process.send({
|
process.send({
|
||||||
msg: 'check',
|
msg: 'check',
|
||||||
result: patch,
|
result: patch,
|
||||||
|
@ -32,11 +37,16 @@ process.on('message', function (data) {
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case 'get revision':
|
case 'get revision':
|
||||||
if (!data.hasOwnProperty('revisions') || !data.hasOwnProperty('count')) {
|
if (
|
||||||
return logger.error('dmp worker error: not enough data on get revision')
|
!Object.prototype.hasOwnProperty.call(data, 'revisions') ||
|
||||||
|
!Object.prototype.hasOwnProperty.call(data, 'count')
|
||||||
|
) {
|
||||||
|
return logger.error(
|
||||||
|
'dmp worker error: not enough data on get revision'
|
||||||
|
)
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
var result = getRevision(data.revisions, data.count)
|
const result = getRevision(data.revisions, data.count)
|
||||||
process.send({
|
process.send({
|
||||||
msg: 'check',
|
msg: 'check',
|
||||||
result: result,
|
result: result,
|
||||||
|
@ -55,31 +65,31 @@ process.on('message', function (data) {
|
||||||
})
|
})
|
||||||
|
|
||||||
function createPatch (lastDoc, currDoc) {
|
function createPatch (lastDoc, currDoc) {
|
||||||
var msStart = (new Date()).getTime()
|
const msStart = new Date().getTime()
|
||||||
var diff = dmp.diff_main(lastDoc, currDoc)
|
const diff = dmp.diff_main(lastDoc, currDoc)
|
||||||
var patch = dmp.patch_make(lastDoc, diff)
|
let patch = dmp.patch_make(lastDoc, diff)
|
||||||
patch = dmp.patch_toText(patch)
|
patch = dmp.patch_toText(patch)
|
||||||
var msEnd = (new Date()).getTime()
|
const msEnd = new Date().getTime()
|
||||||
logger.debug(patch)
|
logger.debug(patch)
|
||||||
logger.debug((msEnd - msStart) + 'ms')
|
logger.debug(msEnd - msStart + 'ms')
|
||||||
return patch
|
return patch
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRevision (revisions, count) {
|
function getRevision (revisions, count) {
|
||||||
var msStart = (new Date()).getTime()
|
const msStart = new Date().getTime()
|
||||||
var startContent = null
|
let startContent = null
|
||||||
var lastPatch = []
|
let lastPatch = []
|
||||||
var applyPatches = []
|
let applyPatches = []
|
||||||
var authorship = []
|
let authorship = []
|
||||||
if (count <= Math.round(revisions.length / 2)) {
|
if (count <= Math.round(revisions.length / 2)) {
|
||||||
// start from top to target
|
// start from top to target
|
||||||
for (let i = 0; i < count; i++) {
|
for (let i = 0; i < count; i++) {
|
||||||
let revision = revisions[i]
|
const revision = revisions[i]
|
||||||
if (i === 0) {
|
if (i === 0) {
|
||||||
startContent = revision.content || revision.lastContent
|
startContent = revision.content || revision.lastContent
|
||||||
}
|
}
|
||||||
if (i !== count - 1) {
|
if (i !== count - 1) {
|
||||||
let patch = dmp.patch_fromText(revision.patch)
|
const patch = dmp.patch_fromText(revision.patch)
|
||||||
applyPatches = applyPatches.concat(patch)
|
applyPatches = applyPatches.concat(patch)
|
||||||
}
|
}
|
||||||
lastPatch = revision.patch
|
lastPatch = revision.patch
|
||||||
|
@ -88,21 +98,25 @@ function getRevision (revisions, count) {
|
||||||
// swap DIFF_INSERT and DIFF_DELETE to achieve unpatching
|
// swap DIFF_INSERT and DIFF_DELETE to achieve unpatching
|
||||||
for (let i = 0, l = applyPatches.length; i < l; i++) {
|
for (let i = 0, l = applyPatches.length; i < l; i++) {
|
||||||
for (let j = 0, m = applyPatches[i].diffs.length; j < m; j++) {
|
for (let j = 0, m = applyPatches[i].diffs.length; j < m; j++) {
|
||||||
var diff = applyPatches[i].diffs[j]
|
const diff = applyPatches[i].diffs[j]
|
||||||
if (diff[0] === DiffMatchPatch.DIFF_INSERT) { diff[0] = DiffMatchPatch.DIFF_DELETE } else if (diff[0] === DiffMatchPatch.DIFF_DELETE) { diff[0] = DiffMatchPatch.DIFF_INSERT }
|
if (diff[0] === DiffMatchPatch.DIFF_INSERT) {
|
||||||
|
diff[0] = DiffMatchPatch.DIFF_DELETE
|
||||||
|
} else if (diff[0] === DiffMatchPatch.DIFF_DELETE) {
|
||||||
|
diff[0] = DiffMatchPatch.DIFF_INSERT
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// start from bottom to target
|
// start from bottom to target
|
||||||
var l = revisions.length - 1
|
const l = revisions.length - 1
|
||||||
for (var i = l; i >= count - 1; i--) {
|
for (let i = l; i >= count - 1; i--) {
|
||||||
let revision = revisions[i]
|
const revision = revisions[i]
|
||||||
if (i === l) {
|
if (i === l) {
|
||||||
startContent = revision.lastContent
|
startContent = revision.lastContent
|
||||||
authorship = revision.authorship
|
authorship = revision.authorship
|
||||||
}
|
}
|
||||||
if (revision.patch) {
|
if (revision.patch) {
|
||||||
let patch = dmp.patch_fromText(revision.patch)
|
const patch = dmp.patch_fromText(revision.patch)
|
||||||
applyPatches = applyPatches.concat(patch)
|
applyPatches = applyPatches.concat(patch)
|
||||||
}
|
}
|
||||||
lastPatch = revision.patch
|
lastPatch = revision.patch
|
||||||
|
@ -110,18 +124,18 @@ function getRevision (revisions, count) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
var finalContent = dmp.patch_apply(applyPatches, startContent)[0]
|
const finalContent = dmp.patch_apply(applyPatches, startContent)[0]
|
||||||
|
const data = {
|
||||||
|
content: finalContent,
|
||||||
|
patch: dmp.patch_fromText(lastPatch),
|
||||||
|
authorship: authorship
|
||||||
|
}
|
||||||
|
const msEnd = new Date().getTime()
|
||||||
|
logger.debug(msEnd - msStart + 'ms')
|
||||||
|
return data
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw new Error(err)
|
throw new Error(err)
|
||||||
}
|
}
|
||||||
var data = {
|
|
||||||
content: finalContent,
|
|
||||||
patch: dmp.patch_fromText(lastPatch),
|
|
||||||
authorship: authorship
|
|
||||||
}
|
|
||||||
var msEnd = (new Date()).getTime()
|
|
||||||
logger.debug((msEnd - msStart) + 'ms')
|
|
||||||
return data
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// log uncaught exception
|
// log uncaught exception
|
||||||
|
|
|
@ -1,28 +1,28 @@
|
||||||
// this config file is used in concert with the root .eslintrc.js in the root dir.
|
// this config file is used in concert with the root .eslintrc.js in the root dir.
|
||||||
module.exports = {
|
module.exports = {
|
||||||
"env": {
|
env: {
|
||||||
"browser": true
|
browser: true
|
||||||
},
|
},
|
||||||
"globals": {
|
globals: {
|
||||||
"$": false,
|
$: false,
|
||||||
"CodeMirror": false,
|
CodeMirror: false,
|
||||||
"Cookies": false,
|
Cookies: false,
|
||||||
"moment": false,
|
moment: false,
|
||||||
"editor": false,
|
editor: false,
|
||||||
"ui": false,
|
ui: false,
|
||||||
"Spinner": false,
|
Spinner: false,
|
||||||
"modeType": false,
|
modeType: false,
|
||||||
"Idle": false,
|
Idle: false,
|
||||||
"serverurl": false,
|
serverurl: false,
|
||||||
"key": false,
|
key: false,
|
||||||
"gapi": false,
|
gapi: false,
|
||||||
"Dropbox": false,
|
Dropbox: false,
|
||||||
"FilePicker": false,
|
FilePicker: false,
|
||||||
"ot": false,
|
ot: false,
|
||||||
"MediaUploader": false,
|
MediaUploader: false,
|
||||||
"hex2rgb": false,
|
hex2rgb: false,
|
||||||
"num_loaded": false,
|
num_loaded: false,
|
||||||
"Visibility": false,
|
Visibility: false,
|
||||||
"inlineAttachment": false
|
inlineAttachment: false
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
|
@ -262,8 +262,8 @@ function updateItemFromNow () {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var clearHistory = false
|
let clearHistory = false
|
||||||
var deleteId = null
|
let deleteId = null
|
||||||
|
|
||||||
function deleteHistory () {
|
function deleteHistory () {
|
||||||
checkIfAuth(() => {
|
checkIfAuth(() => {
|
||||||
|
@ -431,9 +431,9 @@ $('.search').keyup(() => {
|
||||||
|
|
||||||
// focus user field after opening login modal
|
// focus user field after opening login modal
|
||||||
$('.signin-modal').on('shown.bs.modal', function () {
|
$('.signin-modal').on('shown.bs.modal', function () {
|
||||||
let fieldLDAP = $('input[name=username]')
|
const fieldLDAP = $('input[name=username]')
|
||||||
let fieldEmail = $('input[name=email]')
|
const fieldEmail = $('input[name=email]')
|
||||||
let fieldOpenID = $('input[name=openid_identifier]')
|
const fieldOpenID = $('input[name=openid_identifier]')
|
||||||
if (fieldLDAP.length === 1) {
|
if (fieldLDAP.length === 1) {
|
||||||
fieldLDAP.focus()
|
fieldLDAP.focus()
|
||||||
} else if (fieldEmail.length === 1) {
|
} else if (fieldEmail.length === 1) {
|
||||||
|
|
|
@ -29,7 +29,7 @@ require('prismjs/components/prism-gherkin')
|
||||||
require('./lib/common/login')
|
require('./lib/common/login')
|
||||||
require('./locale')
|
require('./locale')
|
||||||
require('../vendor/md-toc')
|
require('../vendor/md-toc')
|
||||||
var Viz = require('viz.js')
|
const Viz = require('viz.js')
|
||||||
const ui = getUIElements()
|
const ui = getUIElements()
|
||||||
|
|
||||||
// auto update last change
|
// auto update last change
|
||||||
|
@ -314,8 +314,9 @@ export function finishView (view) {
|
||||||
// sequence diagram
|
// sequence diagram
|
||||||
const sequences = view.find('div.sequence-diagram.raw').removeClass('raw')
|
const sequences = view.find('div.sequence-diagram.raw').removeClass('raw')
|
||||||
sequences.each((key, value) => {
|
sequences.each((key, value) => {
|
||||||
|
let $value
|
||||||
try {
|
try {
|
||||||
var $value = $(value)
|
$value = $(value)
|
||||||
const $ele = $(value).parent().parent()
|
const $ele = $(value).parent().parent()
|
||||||
|
|
||||||
const sequence = $value
|
const sequence = $value
|
||||||
|
@ -337,15 +338,16 @@ export function finishView (view) {
|
||||||
// flowchart
|
// flowchart
|
||||||
const flow = view.find('div.flow-chart.raw').removeClass('raw')
|
const flow = view.find('div.flow-chart.raw').removeClass('raw')
|
||||||
flow.each((key, value) => {
|
flow.each((key, value) => {
|
||||||
|
let $value
|
||||||
try {
|
try {
|
||||||
var $value = $(value)
|
$value = $(value)
|
||||||
const $ele = $(value).parent().parent()
|
const $ele = $(value).parent().parent()
|
||||||
|
|
||||||
const chart = window.flowchart.parse($value.text())
|
const chart = window.flowchart.parse($value.text())
|
||||||
$value.html('')
|
$value.html('')
|
||||||
chart.drawSVG(value, {
|
chart.drawSVG(value, {
|
||||||
'line-width': 2,
|
'line-width': 2,
|
||||||
'fill': 'none',
|
fill: 'none',
|
||||||
'font-size': '16px',
|
'font-size': '16px',
|
||||||
'font-family': "'Andale Mono', monospace"
|
'font-family': "'Andale Mono', monospace"
|
||||||
})
|
})
|
||||||
|
@ -359,13 +361,14 @@ export function finishView (view) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
// graphviz
|
// graphviz
|
||||||
var graphvizs = view.find('div.graphviz.raw').removeClass('raw')
|
const graphvizs = view.find('div.graphviz.raw').removeClass('raw')
|
||||||
graphvizs.each(function (key, value) {
|
graphvizs.each(function (key, value) {
|
||||||
|
let $value
|
||||||
try {
|
try {
|
||||||
var $value = $(value)
|
$value = $(value)
|
||||||
var $ele = $(value).parent().parent()
|
const $ele = $(value).parent().parent()
|
||||||
|
|
||||||
var graphviz = Viz($value.text())
|
const graphviz = Viz($value.text())
|
||||||
if (!graphviz) throw Error('viz.js output empty graph')
|
if (!graphviz) throw Error('viz.js output empty graph')
|
||||||
$value.html(graphviz)
|
$value.html(graphviz)
|
||||||
|
|
||||||
|
@ -380,8 +383,9 @@ export function finishView (view) {
|
||||||
// mermaid
|
// mermaid
|
||||||
const mermaids = view.find('div.mermaid.raw').removeClass('raw')
|
const mermaids = view.find('div.mermaid.raw').removeClass('raw')
|
||||||
mermaids.each((key, value) => {
|
mermaids.each((key, value) => {
|
||||||
|
let $value
|
||||||
try {
|
try {
|
||||||
var $value = $(value)
|
$value = $(value)
|
||||||
const $ele = $(value).closest('pre')
|
const $ele = $(value).closest('pre')
|
||||||
|
|
||||||
window.mermaid.mermaidAPI.parse($value.text())
|
window.mermaid.mermaidAPI.parse($value.text())
|
||||||
|
@ -389,7 +393,7 @@ export function finishView (view) {
|
||||||
$ele.text($value.text())
|
$ele.text($value.text())
|
||||||
window.mermaid.init(undefined, $ele)
|
window.mermaid.init(undefined, $ele)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
var errormessage = err
|
let errormessage = err
|
||||||
if (err.str) {
|
if (err.str) {
|
||||||
errormessage = err.str
|
errormessage = err.str
|
||||||
}
|
}
|
||||||
|
@ -402,9 +406,10 @@ export function finishView (view) {
|
||||||
// abc.js
|
// abc.js
|
||||||
const abcs = view.find('div.abc.raw').removeClass('raw')
|
const abcs = view.find('div.abc.raw').removeClass('raw')
|
||||||
abcs.each((key, value) => {
|
abcs.each((key, value) => {
|
||||||
|
let $value
|
||||||
try {
|
try {
|
||||||
var $value = $(value)
|
$value = $(value)
|
||||||
var $ele = $(value).parent().parent()
|
const $ele = $(value).parent().parent()
|
||||||
|
|
||||||
window.ABCJS.renderAbc(value, $value.text())
|
window.ABCJS.renderAbc(value, $value.text())
|
||||||
|
|
||||||
|
@ -493,7 +498,7 @@ export function finishView (view) {
|
||||||
let code = ''
|
let code = ''
|
||||||
if (codeDiv.length > 0) code = codeDiv.html()
|
if (codeDiv.length > 0) code = codeDiv.html()
|
||||||
else code = langDiv.html()
|
else code = langDiv.html()
|
||||||
var result
|
let result
|
||||||
if (!reallang) {
|
if (!reallang) {
|
||||||
result = {
|
result = {
|
||||||
value: code
|
value: code
|
||||||
|
@ -571,7 +576,7 @@ export function postProcess (code) {
|
||||||
}
|
}
|
||||||
// show yaml meta paring error
|
// show yaml meta paring error
|
||||||
if (md.metaError) {
|
if (md.metaError) {
|
||||||
var warning = result.find('div#meta-error')
|
let warning = result.find('div#meta-error')
|
||||||
if (warning && warning.length > 0) {
|
if (warning && warning.length > 0) {
|
||||||
warning.text(md.metaError)
|
warning.text(md.metaError)
|
||||||
} else {
|
} else {
|
||||||
|
@ -583,14 +588,14 @@ export function postProcess (code) {
|
||||||
}
|
}
|
||||||
window.postProcess = postProcess
|
window.postProcess = postProcess
|
||||||
|
|
||||||
var domevents = Object.getOwnPropertyNames(document).concat(Object.getOwnPropertyNames(Object.getPrototypeOf(Object.getPrototypeOf(document)))).concat(Object.getOwnPropertyNames(Object.getPrototypeOf(window))).filter(function (i) {
|
const domevents = Object.getOwnPropertyNames(document).concat(Object.getOwnPropertyNames(Object.getPrototypeOf(Object.getPrototypeOf(document)))).concat(Object.getOwnPropertyNames(Object.getPrototypeOf(window))).filter(function (i) {
|
||||||
return !i.indexOf('on') && (document[i] === null || typeof document[i] === 'function')
|
return !i.indexOf('on') && (document[i] === null || typeof document[i] === 'function')
|
||||||
}).filter(function (elem, pos, self) {
|
}).filter(function (elem, pos, self) {
|
||||||
return self.indexOf(elem) === pos
|
return self.indexOf(elem) === pos
|
||||||
})
|
})
|
||||||
|
|
||||||
export function removeDOMEvents (view) {
|
export function removeDOMEvents (view) {
|
||||||
for (var i = 0, l = domevents.length; i < l; i++) {
|
for (let i = 0, l = domevents.length; i < l; i++) {
|
||||||
view.find('[' + domevents[i] + ']').removeAttr(domevents[i])
|
view.find('[' + domevents[i] + ']').removeAttr(domevents[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -739,13 +744,13 @@ export function generateToc (id) {
|
||||||
const target = $(`#${id}`)
|
const target = $(`#${id}`)
|
||||||
target.html('')
|
target.html('')
|
||||||
/* eslint-disable no-unused-vars */
|
/* eslint-disable no-unused-vars */
|
||||||
var toc = new window.Toc('doc', {
|
const toc = new window.Toc('doc', {
|
||||||
'level': 3,
|
level: 3,
|
||||||
'top': -1,
|
top: -1,
|
||||||
'class': 'toc',
|
class: 'toc',
|
||||||
'ulClass': 'nav',
|
ulClass: 'nav',
|
||||||
'targetId': id,
|
targetId: id,
|
||||||
'process': getHeaderContent
|
process: getHeaderContent
|
||||||
})
|
})
|
||||||
/* eslint-enable no-unused-vars */
|
/* eslint-enable no-unused-vars */
|
||||||
if (target.text() === 'undefined') { target.html('') }
|
if (target.text() === 'undefined') { target.html('') }
|
||||||
|
@ -858,7 +863,7 @@ const linkifyAnchors = (level, containingElement) => {
|
||||||
const headers = containingElement.getElementsByTagName(`h${level}`)
|
const headers = containingElement.getElementsByTagName(`h${level}`)
|
||||||
|
|
||||||
for (let i = 0, l = headers.length; i < l; i++) {
|
for (let i = 0, l = headers.length; i < l; i++) {
|
||||||
let header = headers[i]
|
const header = headers[i]
|
||||||
if (header.getElementsByClassName('anchor').length === 0) {
|
if (header.getElementsByClassName('anchor').length === 0) {
|
||||||
if (typeof header.id === 'undefined' || header.id === '') {
|
if (typeof header.id === 'undefined' || header.id === '') {
|
||||||
header.id = createHeaderId(getHeaderContent(header))
|
header.id = createHeaderId(getHeaderContent(header))
|
||||||
|
@ -903,7 +908,7 @@ export function deduplicatedHeaderId (view) {
|
||||||
if (window.linkifyHeaderStyle === 'gfm') {
|
if (window.linkifyHeaderStyle === 'gfm') {
|
||||||
// consistent with GitHub, GitLab, Pandoc & co.
|
// consistent with GitHub, GitLab, Pandoc & co.
|
||||||
// all headers contained in the document, in order of appearance
|
// all headers contained in the document, in order of appearance
|
||||||
const allHeaders = view.find(`:header`).toArray()
|
const allHeaders = view.find(':header').toArray()
|
||||||
// list of finaly assigned header IDs
|
// list of finaly assigned header IDs
|
||||||
const headerIds = new Set()
|
const headerIds = new Set()
|
||||||
for (let j = 0; j < allHeaders.length; j++) {
|
for (let j = 0; j < allHeaders.length; j++) {
|
||||||
|
@ -938,12 +943,12 @@ export function renderTOC (view) {
|
||||||
const target = $(`#${id}`)
|
const target = $(`#${id}`)
|
||||||
target.html('')
|
target.html('')
|
||||||
/* eslint-disable no-unused-vars */
|
/* eslint-disable no-unused-vars */
|
||||||
let TOC = new window.Toc('doc', {
|
const TOC = new window.Toc('doc', {
|
||||||
'level': 3,
|
level: 3,
|
||||||
'top': -1,
|
top: -1,
|
||||||
'class': 'toc',
|
class: 'toc',
|
||||||
'targetId': id,
|
targetId: id,
|
||||||
'process': getHeaderContent
|
process: getHeaderContent
|
||||||
})
|
})
|
||||||
/* eslint-enable no-unused-vars */
|
/* eslint-enable no-unused-vars */
|
||||||
if (target.text() === 'undefined') { target.html('') }
|
if (target.text() === 'undefined') { target.html('') }
|
||||||
|
@ -991,7 +996,7 @@ function highlightRender (code, lang) {
|
||||||
return result.value
|
return result.value
|
||||||
}
|
}
|
||||||
|
|
||||||
export let md = markdownit('default', {
|
export const md = markdownit('default', {
|
||||||
html: true,
|
html: true,
|
||||||
breaks: true,
|
breaks: true,
|
||||||
langPrefix: '',
|
langPrefix: '',
|
||||||
|
@ -1044,7 +1049,7 @@ md.use(markdownitContainer, 'info', { render: renderContainer })
|
||||||
md.use(markdownitContainer, 'warning', { render: renderContainer })
|
md.use(markdownitContainer, 'warning', { render: renderContainer })
|
||||||
md.use(markdownitContainer, 'danger', { render: renderContainer })
|
md.use(markdownitContainer, 'danger', { render: renderContainer })
|
||||||
|
|
||||||
let defaultImageRender = md.renderer.rules.image
|
const defaultImageRender = md.renderer.rules.image
|
||||||
md.renderer.rules.image = function (tokens, idx, options, env, self) {
|
md.renderer.rules.image = function (tokens, idx, options, env, self) {
|
||||||
tokens[idx].attrJoin('class', 'raw')
|
tokens[idx].attrJoin('class', 'raw')
|
||||||
return defaultImageRender(...arguments)
|
return defaultImageRender(...arguments)
|
||||||
|
@ -1203,7 +1208,8 @@ function meta (state, start, end, silent) {
|
||||||
if (!get(state, start).match(/^---$/)) return false
|
if (!get(state, start).match(/^---$/)) return false
|
||||||
|
|
||||||
const data = []
|
const data = []
|
||||||
for (var line = start + 1; line < end; line++) {
|
let line
|
||||||
|
for (line = start + 1; line < end; line++) {
|
||||||
const str = get(state, line)
|
const str = get(state, line)
|
||||||
if (str.match(/^(\.{3}|-{3})$/)) break
|
if (str.match(/^(\.{3}|-{3})$/)) break
|
||||||
if (state.tShift[line] < 0) break
|
if (state.tShift[line] < 0) break
|
||||||
|
|
|
@ -147,7 +147,7 @@ export function writeHistory (title, tags) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function writeHistoryToStorage (title, tags) {
|
function writeHistoryToStorage (title, tags) {
|
||||||
let data = store.get('notehistory')
|
const data = store.get('notehistory')
|
||||||
let notehistory
|
let notehistory
|
||||||
if (data && typeof data === 'string') {
|
if (data && typeof data === 'string') {
|
||||||
notehistory = JSON.parse(data)
|
notehistory = JSON.parse(data)
|
||||||
|
@ -220,7 +220,7 @@ export function getStorageHistory (callback) {
|
||||||
if (typeof data === 'string') { data = JSON.parse(data) }
|
if (typeof data === 'string') { data = JSON.parse(data) }
|
||||||
callback(data)
|
callback(data)
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line standard/no-callback-literal
|
// eslint-disable-next-line node/no-callback-literal
|
||||||
callback([])
|
callback([])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -263,7 +263,7 @@ function parseToHistory (list, notehistory, callback) {
|
||||||
for (let i = 0; i < notehistory.length; i++) {
|
for (let i = 0; i < notehistory.length; i++) {
|
||||||
// migrate LZString encoded id to base64url encoded id
|
// migrate LZString encoded id to base64url encoded id
|
||||||
try {
|
try {
|
||||||
let id = LZString.decompressFromBase64(notehistory[i].id)
|
const id = LZString.decompressFromBase64(notehistory[i].id)
|
||||||
if (id && checkNoteIdValid(id)) {
|
if (id && checkNoteIdValid(id)) {
|
||||||
notehistory[i].id = encodeNoteId(id)
|
notehistory[i].id = encodeNoteId(id)
|
||||||
}
|
}
|
||||||
|
|
2183
public/js/index.js
2183
public/js/index.js
File diff suppressed because it is too large
Load diff
|
@ -1,6 +1,6 @@
|
||||||
import modeType from './modeType'
|
import modeType from './modeType'
|
||||||
|
|
||||||
let state = {
|
const state = {
|
||||||
syncscroll: true,
|
syncscroll: true,
|
||||||
currentMode: modeType.view,
|
currentMode: modeType.view,
|
||||||
nightMode: false
|
nightMode: false
|
||||||
|
|
|
@ -7,7 +7,7 @@ let checkAuth = false
|
||||||
let profile = null
|
let profile = null
|
||||||
let lastLoginState = getLoginState()
|
let lastLoginState = getLoginState()
|
||||||
let lastUserId = getUserId()
|
let lastUserId = getUserId()
|
||||||
var loginStateChangeEvent = null
|
let loginStateChangeEvent = null
|
||||||
|
|
||||||
export function setloginStateChangeEvent (func) {
|
export function setloginStateChangeEvent (func) {
|
||||||
loginStateChangeEvent = func
|
loginStateChangeEvent = func
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
let config = {
|
const config = {
|
||||||
docmaxlength: null
|
docmaxlength: null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,30 +35,30 @@ export default class Editor {
|
||||||
},
|
},
|
||||||
Enter: 'newlineAndIndentContinueMarkdownList',
|
Enter: 'newlineAndIndentContinueMarkdownList',
|
||||||
Tab: function (cm) {
|
Tab: function (cm) {
|
||||||
var tab = '\t'
|
const tab = '\t'
|
||||||
|
|
||||||
// contruct x length spaces
|
// contruct x length spaces
|
||||||
var spaces = Array(parseInt(cm.getOption('indentUnit')) + 1).join(' ')
|
const spaces = Array(parseInt(cm.getOption('indentUnit')) + 1).join(' ')
|
||||||
|
|
||||||
// auto indent whole line when in list or blockquote
|
// auto indent whole line when in list or blockquote
|
||||||
var cursor = cm.getCursor()
|
const cursor = cm.getCursor()
|
||||||
var line = cm.getLine(cursor.line)
|
const line = cm.getLine(cursor.line)
|
||||||
|
|
||||||
// this regex match the following patterns
|
// this regex match the following patterns
|
||||||
// 1. blockquote starts with "> " or ">>"
|
// 1. blockquote starts with "> " or ">>"
|
||||||
// 2. unorder list starts with *+-
|
// 2. unorder list starts with *+-
|
||||||
// 3. order list starts with "1." or "1)"
|
// 3. order list starts with "1." or "1)"
|
||||||
var regex = /^(\s*)(>[> ]*|[*+-]\s|(\d+)([.)]))/
|
const regex = /^(\s*)(>[> ]*|[*+-]\s|(\d+)([.)]))/
|
||||||
|
|
||||||
var match
|
let match
|
||||||
var multiple = cm.getSelection().split('\n').length > 1 ||
|
const multiple = cm.getSelection().split('\n').length > 1 ||
|
||||||
cm.getSelections().length > 1
|
cm.getSelections().length > 1
|
||||||
|
|
||||||
if (multiple) {
|
if (multiple) {
|
||||||
cm.execCommand('defaultTab')
|
cm.execCommand('defaultTab')
|
||||||
} else if ((match = regex.exec(line)) !== null) {
|
} else if ((match = regex.exec(line)) !== null) {
|
||||||
var ch = match[1].length
|
const ch = match[1].length
|
||||||
var pos = {
|
const pos = {
|
||||||
line: cursor.line,
|
line: cursor.line,
|
||||||
ch: ch
|
ch: ch
|
||||||
}
|
}
|
||||||
|
@ -77,8 +77,8 @@ export default class Editor {
|
||||||
},
|
},
|
||||||
'Cmd-Left': 'goLineLeftSmart',
|
'Cmd-Left': 'goLineLeftSmart',
|
||||||
'Cmd-Right': 'goLineRight',
|
'Cmd-Right': 'goLineRight',
|
||||||
'Home': 'goLineLeftSmart',
|
Home: 'goLineLeftSmart',
|
||||||
'End': 'goLineRight',
|
End: 'goLineRight',
|
||||||
'Ctrl-C': function (cm) {
|
'Ctrl-C': function (cm) {
|
||||||
if (!isMac && cm.getOption('keyMap').substr(0, 3) === 'vim') {
|
if (!isMac && cm.getOption('keyMap').substr(0, 3) === 'vim') {
|
||||||
document.execCommand('copy')
|
document.execCommand('copy')
|
||||||
|
@ -140,27 +140,27 @@ export default class Editor {
|
||||||
}
|
}
|
||||||
|
|
||||||
addToolBar () {
|
addToolBar () {
|
||||||
var inlineAttach = inlineAttachment.editors.codemirror4.attach(this.editor)
|
const inlineAttach = inlineAttachment.editors.codemirror4.attach(this.editor)
|
||||||
this.toolBar = $(toolBarTemplate)
|
this.toolBar = $(toolBarTemplate)
|
||||||
this.toolbarPanel = this.editor.addPanel(this.toolBar[0], {
|
this.toolbarPanel = this.editor.addPanel(this.toolBar[0], {
|
||||||
position: 'top'
|
position: 'top'
|
||||||
})
|
})
|
||||||
|
|
||||||
var makeBold = $('#makeBold')
|
const makeBold = $('#makeBold')
|
||||||
var makeItalic = $('#makeItalic')
|
const makeItalic = $('#makeItalic')
|
||||||
var makeStrike = $('#makeStrike')
|
const makeStrike = $('#makeStrike')
|
||||||
var makeHeader = $('#makeHeader')
|
const makeHeader = $('#makeHeader')
|
||||||
var makeCode = $('#makeCode')
|
const makeCode = $('#makeCode')
|
||||||
var makeQuote = $('#makeQuote')
|
const makeQuote = $('#makeQuote')
|
||||||
var makeGenericList = $('#makeGenericList')
|
const makeGenericList = $('#makeGenericList')
|
||||||
var makeOrderedList = $('#makeOrderedList')
|
const makeOrderedList = $('#makeOrderedList')
|
||||||
var makeCheckList = $('#makeCheckList')
|
const makeCheckList = $('#makeCheckList')
|
||||||
var makeLink = $('#makeLink')
|
const makeLink = $('#makeLink')
|
||||||
var makeImage = $('#makeImage')
|
const makeImage = $('#makeImage')
|
||||||
var makeTable = $('#makeTable')
|
const makeTable = $('#makeTable')
|
||||||
var makeLine = $('#makeLine')
|
const makeLine = $('#makeLine')
|
||||||
var makeComment = $('#makeComment')
|
const makeComment = $('#makeComment')
|
||||||
var uploadImage = $('#uploadImage')
|
const uploadImage = $('#uploadImage')
|
||||||
|
|
||||||
makeBold.click(() => {
|
makeBold.click(() => {
|
||||||
utils.wrapTextWith(this.editor, this.editor, '**')
|
utils.wrapTextWith(this.editor, this.editor, '**')
|
||||||
|
@ -223,7 +223,7 @@ export default class Editor {
|
||||||
})
|
})
|
||||||
|
|
||||||
uploadImage.bind('change', function (e) {
|
uploadImage.bind('change', function (e) {
|
||||||
var files = e.target.files || e.dataTransfer.files
|
const files = e.target.files || e.dataTransfer.files
|
||||||
e.dataTransfer = {}
|
e.dataTransfer = {}
|
||||||
e.dataTransfer.files = files
|
e.dataTransfer.files = files
|
||||||
inlineAttach.onDrop(e)
|
inlineAttach.onDrop(e)
|
||||||
|
@ -256,12 +256,12 @@ export default class Editor {
|
||||||
updateStatusBar () {
|
updateStatusBar () {
|
||||||
if (!this.statusBar) return
|
if (!this.statusBar) return
|
||||||
|
|
||||||
var cursor = this.editor.getCursor()
|
const cursor = this.editor.getCursor()
|
||||||
var cursorText = 'Line ' + (cursor.line + 1) + ', Columns ' + (cursor.ch + 1)
|
const cursorText = 'Line ' + (cursor.line + 1) + ', Columns ' + (cursor.ch + 1)
|
||||||
this.statusCursor.text(cursorText)
|
this.statusCursor.text(cursorText)
|
||||||
var fileText = ' — ' + editor.lineCount() + ' Lines'
|
const fileText = ' — ' + editor.lineCount() + ' Lines'
|
||||||
this.statusFile.text(fileText)
|
this.statusFile.text(fileText)
|
||||||
var docLength = editor.getValue().length
|
const docLength = editor.getValue().length
|
||||||
this.statusLength.text('Length ' + docLength)
|
this.statusLength.text('Length ' + docLength)
|
||||||
if (docLength > (config.docmaxlength * 0.95)) {
|
if (docLength > (config.docmaxlength * 0.95)) {
|
||||||
this.statusLength.css('color', 'red')
|
this.statusLength.css('color', 'red')
|
||||||
|
@ -276,9 +276,9 @@ export default class Editor {
|
||||||
}
|
}
|
||||||
|
|
||||||
setIndent () {
|
setIndent () {
|
||||||
var cookieIndentType = Cookies.get('indent_type')
|
const cookieIndentType = Cookies.get('indent_type')
|
||||||
var cookieTabSize = parseInt(Cookies.get('tab_size'))
|
let cookieTabSize = parseInt(Cookies.get('tab_size'))
|
||||||
var cookieSpaceUnits = parseInt(Cookies.get('space_units'))
|
let cookieSpaceUnits = parseInt(Cookies.get('space_units'))
|
||||||
if (cookieIndentType) {
|
if (cookieIndentType) {
|
||||||
if (cookieIndentType === 'tab') {
|
if (cookieIndentType === 'tab') {
|
||||||
this.editor.setOption('indentWithTabs', true)
|
this.editor.setOption('indentWithTabs', true)
|
||||||
|
@ -296,9 +296,9 @@ export default class Editor {
|
||||||
this.editor.setOption('tabSize', cookieTabSize)
|
this.editor.setOption('tabSize', cookieTabSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
var type = this.statusIndicators.find('.indent-type')
|
const type = this.statusIndicators.find('.indent-type')
|
||||||
var widthLabel = this.statusIndicators.find('.indent-width-label')
|
const widthLabel = this.statusIndicators.find('.indent-width-label')
|
||||||
var widthInput = this.statusIndicators.find('.indent-width-input')
|
const widthInput = this.statusIndicators.find('.indent-width-input')
|
||||||
|
|
||||||
const setType = () => {
|
const setType = () => {
|
||||||
if (this.editor.getOption('indentWithTabs')) {
|
if (this.editor.getOption('indentWithTabs')) {
|
||||||
|
@ -318,7 +318,7 @@ export default class Editor {
|
||||||
setType()
|
setType()
|
||||||
|
|
||||||
const setUnit = () => {
|
const setUnit = () => {
|
||||||
var unit = this.editor.getOption('indentUnit')
|
const unit = this.editor.getOption('indentUnit')
|
||||||
if (this.editor.getOption('indentWithTabs')) {
|
if (this.editor.getOption('indentWithTabs')) {
|
||||||
Cookies.set('tab_size', unit, {
|
Cookies.set('tab_size', unit, {
|
||||||
expires: 365,
|
expires: 365,
|
||||||
|
@ -364,7 +364,7 @@ export default class Editor {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
widthInput.on('change', () => {
|
widthInput.on('change', () => {
|
||||||
var val = parseInt(widthInput.val())
|
let val = parseInt(widthInput.val())
|
||||||
if (!val) val = this.editor.getOption('indentUnit')
|
if (!val) val = this.editor.getOption('indentUnit')
|
||||||
if (val < 1) val = 1
|
if (val < 1) val = 1
|
||||||
else if (val > 10) val = 10
|
else if (val > 10) val = 10
|
||||||
|
@ -382,18 +382,18 @@ export default class Editor {
|
||||||
}
|
}
|
||||||
|
|
||||||
setKeymap () {
|
setKeymap () {
|
||||||
var cookieKeymap = Cookies.get('keymap')
|
const cookieKeymap = Cookies.get('keymap')
|
||||||
if (cookieKeymap) {
|
if (cookieKeymap) {
|
||||||
this.editor.setOption('keyMap', cookieKeymap)
|
this.editor.setOption('keyMap', cookieKeymap)
|
||||||
}
|
}
|
||||||
|
|
||||||
var label = this.statusIndicators.find('.ui-keymap-label')
|
const label = this.statusIndicators.find('.ui-keymap-label')
|
||||||
var sublime = this.statusIndicators.find('.ui-keymap-sublime')
|
const sublime = this.statusIndicators.find('.ui-keymap-sublime')
|
||||||
var emacs = this.statusIndicators.find('.ui-keymap-emacs')
|
const emacs = this.statusIndicators.find('.ui-keymap-emacs')
|
||||||
var vim = this.statusIndicators.find('.ui-keymap-vim')
|
const vim = this.statusIndicators.find('.ui-keymap-vim')
|
||||||
|
|
||||||
const setKeymapLabel = () => {
|
const setKeymapLabel = () => {
|
||||||
var keymap = this.editor.getOption('keyMap')
|
const keymap = this.editor.getOption('keyMap')
|
||||||
Cookies.set('keymap', keymap, {
|
Cookies.set('keymap', keymap, {
|
||||||
expires: 365,
|
expires: 365,
|
||||||
sameSite: window.cookiePolicy
|
sameSite: window.cookiePolicy
|
||||||
|
@ -419,15 +419,15 @@ export default class Editor {
|
||||||
}
|
}
|
||||||
|
|
||||||
setTheme () {
|
setTheme () {
|
||||||
var cookieTheme = Cookies.get('theme')
|
const cookieTheme = Cookies.get('theme')
|
||||||
if (cookieTheme) {
|
if (cookieTheme) {
|
||||||
this.editor.setOption('theme', cookieTheme)
|
this.editor.setOption('theme', cookieTheme)
|
||||||
}
|
}
|
||||||
|
|
||||||
var themeToggle = this.statusTheme.find('.ui-theme-toggle')
|
const themeToggle = this.statusTheme.find('.ui-theme-toggle')
|
||||||
|
|
||||||
const checkTheme = () => {
|
const checkTheme = () => {
|
||||||
var theme = this.editor.getOption('theme')
|
const theme = this.editor.getOption('theme')
|
||||||
if (theme === 'one-dark') {
|
if (theme === 'one-dark') {
|
||||||
themeToggle.removeClass('active')
|
themeToggle.removeClass('active')
|
||||||
} else {
|
} else {
|
||||||
|
@ -436,7 +436,7 @@ export default class Editor {
|
||||||
}
|
}
|
||||||
|
|
||||||
themeToggle.click(() => {
|
themeToggle.click(() => {
|
||||||
var theme = this.editor.getOption('theme')
|
let theme = this.editor.getOption('theme')
|
||||||
if (theme === 'one-dark') {
|
if (theme === 'one-dark') {
|
||||||
theme = 'default'
|
theme = 'default'
|
||||||
} else {
|
} else {
|
||||||
|
@ -455,9 +455,9 @@ export default class Editor {
|
||||||
}
|
}
|
||||||
|
|
||||||
setSpellcheck () {
|
setSpellcheck () {
|
||||||
var cookieSpellcheck = Cookies.get('spellcheck')
|
const cookieSpellcheck = Cookies.get('spellcheck')
|
||||||
if (cookieSpellcheck) {
|
if (cookieSpellcheck) {
|
||||||
var mode = null
|
let mode = null
|
||||||
if (cookieSpellcheck === 'true' || cookieSpellcheck === true) {
|
if (cookieSpellcheck === 'true' || cookieSpellcheck === true) {
|
||||||
mode = 'spell-checker'
|
mode = 'spell-checker'
|
||||||
} else {
|
} else {
|
||||||
|
@ -468,10 +468,10 @@ export default class Editor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var spellcheckToggle = this.statusSpellcheck.find('.ui-spellcheck-toggle')
|
const spellcheckToggle = this.statusSpellcheck.find('.ui-spellcheck-toggle')
|
||||||
|
|
||||||
const checkSpellcheck = () => {
|
const checkSpellcheck = () => {
|
||||||
var mode = this.editor.getOption('mode')
|
const mode = this.editor.getOption('mode')
|
||||||
if (mode === defaultEditorMode) {
|
if (mode === defaultEditorMode) {
|
||||||
spellcheckToggle.removeClass('active')
|
spellcheckToggle.removeClass('active')
|
||||||
} else {
|
} else {
|
||||||
|
@ -480,7 +480,7 @@ export default class Editor {
|
||||||
}
|
}
|
||||||
|
|
||||||
spellcheckToggle.click(() => {
|
spellcheckToggle.click(() => {
|
||||||
var mode = this.editor.getOption('mode')
|
let mode = this.editor.getOption('mode')
|
||||||
if (mode === defaultEditorMode) {
|
if (mode === defaultEditorMode) {
|
||||||
mode = 'spell-checker'
|
mode = 'spell-checker'
|
||||||
} else {
|
} else {
|
||||||
|
@ -501,7 +501,7 @@ export default class Editor {
|
||||||
|
|
||||||
// workaround spellcheck might not activate beacuse the ajax loading
|
// workaround spellcheck might not activate beacuse the ajax loading
|
||||||
if (window.num_loaded < 2) {
|
if (window.num_loaded < 2) {
|
||||||
var spellcheckTimer = setInterval(
|
const spellcheckTimer = setInterval(
|
||||||
() => {
|
() => {
|
||||||
if (window.num_loaded >= 2) {
|
if (window.num_loaded >= 2) {
|
||||||
if (this.editor.getOption('mode') === 'spell-checker') {
|
if (this.editor.getOption('mode') === 'spell-checker') {
|
||||||
|
@ -516,7 +516,7 @@ export default class Editor {
|
||||||
}
|
}
|
||||||
|
|
||||||
resetEditorKeymapToBrowserKeymap () {
|
resetEditorKeymapToBrowserKeymap () {
|
||||||
var keymap = this.editor.getOption('keyMap')
|
const keymap = this.editor.getOption('keyMap')
|
||||||
if (!this.jumpToAddressBarKeymapValue) {
|
if (!this.jumpToAddressBarKeymapValue) {
|
||||||
this.jumpToAddressBarKeymapValue = CodeMirror.keyMap[keymap][jumpToAddressBarKeymapName]
|
this.jumpToAddressBarKeymapValue = CodeMirror.keyMap[keymap][jumpToAddressBarKeymapName]
|
||||||
delete CodeMirror.keyMap[keymap][jumpToAddressBarKeymapName]
|
delete CodeMirror.keyMap[keymap][jumpToAddressBarKeymapName]
|
||||||
|
@ -524,14 +524,15 @@ export default class Editor {
|
||||||
}
|
}
|
||||||
|
|
||||||
restoreOverrideEditorKeymap () {
|
restoreOverrideEditorKeymap () {
|
||||||
var keymap = this.editor.getOption('keyMap')
|
const keymap = this.editor.getOption('keyMap')
|
||||||
if (this.jumpToAddressBarKeymapValue) {
|
if (this.jumpToAddressBarKeymapValue) {
|
||||||
CodeMirror.keyMap[keymap][jumpToAddressBarKeymapName] = this.jumpToAddressBarKeymapValue
|
CodeMirror.keyMap[keymap][jumpToAddressBarKeymapName] = this.jumpToAddressBarKeymapValue
|
||||||
this.jumpToAddressBarKeymapValue = null
|
this.jumpToAddressBarKeymapValue = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setOverrideBrowserKeymap () {
|
setOverrideBrowserKeymap () {
|
||||||
var overrideBrowserKeymap = $(
|
const overrideBrowserKeymap = $(
|
||||||
'.ui-preferences-override-browser-keymap label > input[type="checkbox"]'
|
'.ui-preferences-override-browser-keymap label > input[type="checkbox"]'
|
||||||
)
|
)
|
||||||
if (overrideBrowserKeymap.is(':checked')) {
|
if (overrideBrowserKeymap.is(':checked')) {
|
||||||
|
@ -547,10 +548,10 @@ export default class Editor {
|
||||||
}
|
}
|
||||||
|
|
||||||
setPreferences () {
|
setPreferences () {
|
||||||
var overrideBrowserKeymap = $(
|
const overrideBrowserKeymap = $(
|
||||||
'.ui-preferences-override-browser-keymap label > input[type="checkbox"]'
|
'.ui-preferences-override-browser-keymap label > input[type="checkbox"]'
|
||||||
)
|
)
|
||||||
var cookieOverrideBrowserKeymap = Cookies.get(
|
const cookieOverrideBrowserKeymap = Cookies.get(
|
||||||
'preferences-override-browser-keymap'
|
'preferences-override-browser-keymap'
|
||||||
)
|
)
|
||||||
if (cookieOverrideBrowserKeymap && cookieOverrideBrowserKeymap === 'true') {
|
if (cookieOverrideBrowserKeymap && cookieOverrideBrowserKeymap === 'true') {
|
||||||
|
|
|
@ -3,17 +3,17 @@ export function wrapTextWith (editor, cm, symbol) {
|
||||||
if (!cm.getSelection()) {
|
if (!cm.getSelection()) {
|
||||||
return CodeMirror.Pass
|
return CodeMirror.Pass
|
||||||
} else {
|
} else {
|
||||||
let ranges = cm.listSelections()
|
const ranges = cm.listSelections()
|
||||||
for (let i = 0; i < ranges.length; i++) {
|
for (let i = 0; i < ranges.length; i++) {
|
||||||
let range = ranges[i]
|
const range = ranges[i]
|
||||||
if (!range.empty()) {
|
if (!range.empty()) {
|
||||||
const from = range.from()
|
const from = range.from()
|
||||||
const to = range.to()
|
const to = range.to()
|
||||||
|
|
||||||
if (symbol !== 'Backspace') {
|
if (symbol !== 'Backspace') {
|
||||||
let selection = cm.getRange(from, to)
|
const selection = cm.getRange(from, to)
|
||||||
let anchorIndex = editor.indexFromPos(ranges[i].anchor)
|
const anchorIndex = editor.indexFromPos(ranges[i].anchor)
|
||||||
let headIndex = editor.indexFromPos(ranges[i].head)
|
const headIndex = editor.indexFromPos(ranges[i].head)
|
||||||
cm.replaceRange(symbol + selection + symbol, from, to, '+input')
|
cm.replaceRange(symbol + selection + symbol, from, to, '+input')
|
||||||
if (anchorIndex > headIndex) {
|
if (anchorIndex > headIndex) {
|
||||||
ranges[i].anchor.ch += symbol.length
|
ranges[i].anchor.ch += symbol.length
|
||||||
|
@ -24,18 +24,18 @@ export function wrapTextWith (editor, cm, symbol) {
|
||||||
}
|
}
|
||||||
cm.setSelections(ranges)
|
cm.setSelections(ranges)
|
||||||
} else {
|
} else {
|
||||||
let preEndPos = {
|
const preEndPos = {
|
||||||
line: to.line,
|
line: to.line,
|
||||||
ch: to.ch + symbol.length
|
ch: to.ch + symbol.length
|
||||||
}
|
}
|
||||||
let preText = cm.getRange(to, preEndPos)
|
const preText = cm.getRange(to, preEndPos)
|
||||||
let preIndex = wrapSymbols.indexOf(preText)
|
const preIndex = wrapSymbols.indexOf(preText)
|
||||||
let postEndPos = {
|
const postEndPos = {
|
||||||
line: from.line,
|
line: from.line,
|
||||||
ch: from.ch - symbol.length
|
ch: from.ch - symbol.length
|
||||||
}
|
}
|
||||||
let postText = cm.getRange(postEndPos, from)
|
const postText = cm.getRange(postEndPos, from)
|
||||||
let postIndex = wrapSymbols.indexOf(postText)
|
const postIndex = wrapSymbols.indexOf(postText)
|
||||||
// check if surround symbol are list in array and matched
|
// check if surround symbol are list in array and matched
|
||||||
if (preIndex > -1 && postIndex > -1 && preIndex === postIndex) {
|
if (preIndex > -1 && postIndex > -1 && preIndex === postIndex) {
|
||||||
cm.replaceRange('', to, preEndPos, '+delete')
|
cm.replaceRange('', to, preEndPos, '+delete')
|
||||||
|
@ -48,25 +48,25 @@ export function wrapTextWith (editor, cm, symbol) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function insertText (cm, text, cursorEnd = 0) {
|
export function insertText (cm, text, cursorEnd = 0) {
|
||||||
let cursor = cm.getCursor()
|
const cursor = cm.getCursor()
|
||||||
cm.replaceSelection(text, cursor, cursor)
|
cm.replaceSelection(text, cursor, cursor)
|
||||||
cm.focus()
|
cm.focus()
|
||||||
cm.setCursor({ line: cursor.line, ch: cursor.ch + cursorEnd })
|
cm.setCursor({ line: cursor.line, ch: cursor.ch + cursorEnd })
|
||||||
}
|
}
|
||||||
|
|
||||||
export function insertLink (cm, isImage) {
|
export function insertLink (cm, isImage) {
|
||||||
let cursor = cm.getCursor()
|
const cursor = cm.getCursor()
|
||||||
let ranges = cm.listSelections()
|
const ranges = cm.listSelections()
|
||||||
const linkEnd = '](https://)'
|
const linkEnd = '](https://)'
|
||||||
const symbol = (isImage) ? '![' : '['
|
const symbol = (isImage) ? '![' : '['
|
||||||
|
|
||||||
for (let i = 0; i < ranges.length; i++) {
|
for (let i = 0; i < ranges.length; i++) {
|
||||||
let range = ranges[i]
|
const range = ranges[i]
|
||||||
if (!range.empty()) {
|
if (!range.empty()) {
|
||||||
const from = range.from()
|
const from = range.from()
|
||||||
const to = range.to()
|
const to = range.to()
|
||||||
let anchorIndex = editor.indexFromPos(ranges[i].anchor)
|
const anchorIndex = editor.indexFromPos(ranges[i].anchor)
|
||||||
let headIndex = editor.indexFromPos(ranges[i].head)
|
const headIndex = editor.indexFromPos(ranges[i].head)
|
||||||
let selection = cm.getRange(from, to)
|
let selection = cm.getRange(from, to)
|
||||||
selection = symbol + selection + linkEnd
|
selection = symbol + selection + linkEnd
|
||||||
cm.replaceRange(selection, from, to)
|
cm.replaceRange(selection, from, to)
|
||||||
|
@ -87,9 +87,9 @@ export function insertLink (cm, isImage) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function insertHeader (cm) {
|
export function insertHeader (cm) {
|
||||||
let cursor = cm.getCursor()
|
const cursor = cm.getCursor()
|
||||||
let startOfLine = { line: cursor.line, ch: 0 }
|
const startOfLine = { line: cursor.line, ch: 0 }
|
||||||
let startOfLineText = cm.getRange(startOfLine, { line: cursor.line, ch: 1 })
|
const startOfLineText = cm.getRange(startOfLine, { line: cursor.line, ch: 1 })
|
||||||
// See if it is already a header
|
// See if it is already a header
|
||||||
if (startOfLineText === '#') {
|
if (startOfLineText === '#') {
|
||||||
cm.replaceRange('#', startOfLine, startOfLine)
|
cm.replaceRange('#', startOfLine, startOfLine)
|
||||||
|
@ -100,11 +100,11 @@ export function insertHeader (cm) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function insertOnStartOfLines (cm, symbol) {
|
export function insertOnStartOfLines (cm, symbol) {
|
||||||
let cursor = cm.getCursor()
|
const cursor = cm.getCursor()
|
||||||
let ranges = cm.listSelections()
|
const ranges = cm.listSelections()
|
||||||
|
|
||||||
for (let i = 0; i < ranges.length; i++) {
|
for (let i = 0; i < ranges.length; i++) {
|
||||||
let range = ranges[i]
|
const range = ranges[i]
|
||||||
if (!range.empty()) {
|
if (!range.empty()) {
|
||||||
const from = range.from()
|
const from = range.from()
|
||||||
const to = range.to()
|
const to = range.to()
|
||||||
|
|
|
@ -155,12 +155,12 @@ const buildMap = _.throttle(buildMapInner, buildMapThrottle)
|
||||||
// Optimizations are required only for big texts.
|
// Optimizations are required only for big texts.
|
||||||
function buildMapInner (callback) {
|
function buildMapInner (callback) {
|
||||||
if (!viewArea || !markdownArea) return
|
if (!viewArea || !markdownArea) return
|
||||||
let i, offset, nonEmptyList, pos, a, b, _lineHeightMap, linesCount, acc, _scrollMap
|
let i, pos, a, b, acc
|
||||||
|
|
||||||
offset = viewArea.scrollTop() - viewArea.offset().top
|
const offset = viewArea.scrollTop() - viewArea.offset().top
|
||||||
_scrollMap = []
|
const _scrollMap = []
|
||||||
nonEmptyList = []
|
const nonEmptyList = []
|
||||||
_lineHeightMap = []
|
const _lineHeightMap = []
|
||||||
viewTop = 0
|
viewTop = 0
|
||||||
viewBottom = viewArea[0].scrollHeight - viewArea.height()
|
viewBottom = viewArea[0].scrollHeight - viewArea.height()
|
||||||
|
|
||||||
|
@ -181,7 +181,7 @@ function buildMapInner (callback) {
|
||||||
acc += Math.round(h / lineHeight)
|
acc += Math.round(h / lineHeight)
|
||||||
}
|
}
|
||||||
_lineHeightMap.push(acc)
|
_lineHeightMap.push(acc)
|
||||||
linesCount = acc
|
const linesCount = acc
|
||||||
|
|
||||||
for (i = 0; i < linesCount; i++) {
|
for (i = 0; i < linesCount; i++) {
|
||||||
_scrollMap.push(-1)
|
_scrollMap.push(-1)
|
||||||
|
@ -290,11 +290,12 @@ export function syncScrollToEdit (event, preventAnimate) {
|
||||||
posTo += Math.ceil(posToNextDiff)
|
posTo += Math.ceil(posToNextDiff)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let duration = 0
|
||||||
if (preventAnimate) {
|
if (preventAnimate) {
|
||||||
editArea.scrollTop(posTo)
|
editArea.scrollTop(posTo)
|
||||||
} else {
|
} else {
|
||||||
const posDiff = Math.abs(scrollInfo.top - posTo)
|
const posDiff = Math.abs(scrollInfo.top - posTo)
|
||||||
var duration = posDiff / 50
|
duration = posDiff / 50
|
||||||
duration = duration >= 100 ? duration : 100
|
duration = duration >= 100 ? duration : 100
|
||||||
editArea.stop(true, true).animate({
|
editArea.stop(true, true).animate({
|
||||||
scrollTop: posTo
|
scrollTop: posTo
|
||||||
|
@ -331,11 +332,11 @@ export function syncScrollToView (event, preventAnimate) {
|
||||||
}
|
}
|
||||||
if (viewScrolling) return
|
if (viewScrolling) return
|
||||||
|
|
||||||
let lineNo, posTo
|
let posTo
|
||||||
let topDiffPercent, posToNextDiff
|
let topDiffPercent, posToNextDiff
|
||||||
const scrollInfo = editor.getScrollInfo()
|
const scrollInfo = editor.getScrollInfo()
|
||||||
const textHeight = editor.defaultTextHeight()
|
const textHeight = editor.defaultTextHeight()
|
||||||
lineNo = Math.floor(scrollInfo.top / textHeight)
|
const lineNo = Math.floor(scrollInfo.top / textHeight)
|
||||||
// if reach the last line, will start lerp to the bottom
|
// if reach the last line, will start lerp to the bottom
|
||||||
const diffToBottom = (scrollInfo.top + scrollInfo.clientHeight) - (scrollInfo.height - textHeight)
|
const diffToBottom = (scrollInfo.top + scrollInfo.clientHeight) - (scrollInfo.height - textHeight)
|
||||||
if (scrollInfo.height > scrollInfo.clientHeight && diffToBottom > 0) {
|
if (scrollInfo.height > scrollInfo.clientHeight && diffToBottom > 0) {
|
||||||
|
@ -350,11 +351,12 @@ export function syncScrollToView (event, preventAnimate) {
|
||||||
posTo += Math.floor(posToNextDiff)
|
posTo += Math.floor(posToNextDiff)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let duration = 0
|
||||||
if (preventAnimate) {
|
if (preventAnimate) {
|
||||||
viewArea.scrollTop(posTo)
|
viewArea.scrollTop(posTo)
|
||||||
} else {
|
} else {
|
||||||
const posDiff = Math.abs(viewArea.scrollTop() - posTo)
|
const posDiff = Math.abs(viewArea.scrollTop() - posTo)
|
||||||
var duration = posDiff / 50
|
duration = posDiff / 50
|
||||||
duration = duration >= 100 ? duration : 100
|
duration = duration >= 100 ? duration : 100
|
||||||
viewArea.stop(true, true).animate({
|
viewArea.stop(true, true).animate({
|
||||||
scrollTop: posTo
|
scrollTop: posTo
|
||||||
|
|
|
@ -1,40 +1,40 @@
|
||||||
/* eslint-env browser, jquery */
|
/* eslint-env browser, jquery */
|
||||||
// allow some attributes
|
// allow some attributes
|
||||||
|
|
||||||
var filterXSS = require('xss')
|
const filterXSS = require('xss')
|
||||||
|
|
||||||
var whiteListAttr = ['id', 'class', 'style']
|
const whiteListAttr = ['id', 'class', 'style']
|
||||||
window.whiteListAttr = whiteListAttr
|
window.whiteListAttr = whiteListAttr
|
||||||
// allow link starts with '.', '/' and custom protocol with '://', exclude link starts with javascript://
|
// allow link starts with '.', '/' and custom protocol with '://', exclude link starts with javascript://
|
||||||
var linkRegex = /^(?!javascript:\/\/)([\w|-]+:\/\/)|^([.|/])+/i
|
const linkRegex = /^(?!javascript:\/\/)([\w|-]+:\/\/)|^([.|/])+/i
|
||||||
// allow data uri, from https://gist.github.com/bgrins/6194623
|
// allow data uri, from https://gist.github.com/bgrins/6194623
|
||||||
var dataUriRegex = /^\s*data:([a-z]+\/[a-z0-9-+.]+(;[a-z-]+=[a-z0-9-]+)?)?(;base64)?,([a-z0-9!$&',()*+;=\-._~:@/?%\s]*)\s*$/i
|
const dataUriRegex = /^\s*data:([a-z]+\/[a-z0-9-+.]+(;[a-z-]+=[a-z0-9-]+)?)?(;base64)?,([a-z0-9!$&',()*+;=\-._~:@/?%\s]*)\s*$/i
|
||||||
// custom white list
|
// custom white list
|
||||||
var whiteList = filterXSS.whiteList
|
const whiteList = filterXSS.whiteList
|
||||||
// allow ol specify start number
|
// allow ol specify start number
|
||||||
whiteList['ol'] = ['start']
|
whiteList.ol = ['start']
|
||||||
// allow li specify value number
|
// allow li specify value number
|
||||||
whiteList['li'] = ['value']
|
whiteList.li = ['value']
|
||||||
// allow style tag
|
// allow style tag
|
||||||
whiteList['style'] = []
|
whiteList.style = []
|
||||||
// allow kbd tag
|
// allow kbd tag
|
||||||
whiteList['kbd'] = []
|
whiteList.kbd = []
|
||||||
// allow ifram tag with some safe attributes
|
// allow ifram tag with some safe attributes
|
||||||
whiteList['iframe'] = ['allowfullscreen', 'name', 'referrerpolicy', 'src', 'width', 'height']
|
whiteList.iframe = ['allowfullscreen', 'name', 'referrerpolicy', 'src', 'width', 'height']
|
||||||
// allow summary tag
|
// allow summary tag
|
||||||
whiteList['summary'] = []
|
whiteList.summary = []
|
||||||
// allow ruby tag
|
// allow ruby tag
|
||||||
whiteList['ruby'] = []
|
whiteList.ruby = []
|
||||||
// allow rp tag for ruby
|
// allow rp tag for ruby
|
||||||
whiteList['rp'] = []
|
whiteList.rp = []
|
||||||
// allow rt tag for ruby
|
// allow rt tag for ruby
|
||||||
whiteList['rt'] = []
|
whiteList.rt = []
|
||||||
// allow figure tag
|
// allow figure tag
|
||||||
whiteList['figure'] = []
|
whiteList.figure = []
|
||||||
// allow figcaption tag
|
// allow figcaption tag
|
||||||
whiteList['figcaption'] = []
|
whiteList.figcaption = []
|
||||||
|
|
||||||
var filterXSSOptions = {
|
const filterXSSOptions = {
|
||||||
allowCommentTag: true,
|
allowCommentTag: true,
|
||||||
whiteList: whiteList,
|
whiteList: whiteList,
|
||||||
escapeHtml: function (html) {
|
escapeHtml: function (html) {
|
||||||
|
|
|
@ -17,28 +17,28 @@ import { md } from './extra'
|
||||||
root.RevealMarkdown.initialize()
|
root.RevealMarkdown.initialize()
|
||||||
}
|
}
|
||||||
}(this, function () {
|
}(this, function () {
|
||||||
var DEFAULT_SLIDE_SEPARATOR = '^\r?\n---\r?\n$'
|
const DEFAULT_SLIDE_SEPARATOR = '^\r?\n---\r?\n$'
|
||||||
var DEFAULT_NOTES_SEPARATOR = '^note:'
|
const DEFAULT_NOTES_SEPARATOR = '^note:'
|
||||||
var DEFAULT_ELEMENT_ATTRIBUTES_SEPARATOR = '\\.element\\s*?(.+?)$'
|
const DEFAULT_ELEMENT_ATTRIBUTES_SEPARATOR = '\\.element\\s*?(.+?)$'
|
||||||
var DEFAULT_SLIDE_ATTRIBUTES_SEPARATOR = '\\.slide:\\s*?(\\S.+?)$'
|
const DEFAULT_SLIDE_ATTRIBUTES_SEPARATOR = '\\.slide:\\s*?(\\S.+?)$'
|
||||||
|
|
||||||
var SCRIPT_END_PLACEHOLDER = '__SCRIPT_END__'
|
const SCRIPT_END_PLACEHOLDER = '__SCRIPT_END__'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the markdown contents of a slide section
|
* Retrieves the markdown contents of a slide section
|
||||||
* element. Normalizes leading tabs/whitespace.
|
* element. Normalizes leading tabs/whitespace.
|
||||||
*/
|
*/
|
||||||
function getMarkdownFromSlide (section) {
|
function getMarkdownFromSlide (section) {
|
||||||
var template = section.querySelector('script')
|
const template = section.querySelector('script')
|
||||||
|
|
||||||
// strip leading whitespace so it isn't evaluated as code
|
// strip leading whitespace so it isn't evaluated as code
|
||||||
var text = (template || section).textContent
|
let text = (template || section).textContent
|
||||||
|
|
||||||
// restore script end tags
|
// restore script end tags
|
||||||
text = text.replace(new RegExp(SCRIPT_END_PLACEHOLDER, 'g'), '</script>')
|
text = text.replace(new RegExp(SCRIPT_END_PLACEHOLDER, 'g'), '</script>')
|
||||||
|
|
||||||
var leadingWs = text.match(/^\n?(\s*)/)[1].length
|
const leadingWs = text.match(/^\n?(\s*)/)[1].length
|
||||||
var leadingTabs = text.match(/^\n?(\t*)/)[1].length
|
const leadingTabs = text.match(/^\n?(\t*)/)[1].length
|
||||||
|
|
||||||
if (leadingTabs > 0) {
|
if (leadingTabs > 0) {
|
||||||
text = text.replace(new RegExp('\\n?\\t{' + leadingTabs + '}', 'g'), '\n')
|
text = text.replace(new RegExp('\\n?\\t{' + leadingTabs + '}', 'g'), '\n')
|
||||||
|
@ -56,12 +56,12 @@ import { md } from './extra'
|
||||||
* to the output markdown slide.
|
* to the output markdown slide.
|
||||||
*/
|
*/
|
||||||
function getForwardedAttributes (section) {
|
function getForwardedAttributes (section) {
|
||||||
var attributes = section.attributes
|
const attributes = section.attributes
|
||||||
var result = []
|
const result = []
|
||||||
|
|
||||||
for (var i = 0, len = attributes.length; i < len; i++) {
|
for (let i = 0, len = attributes.length; i < len; i++) {
|
||||||
var name = attributes[i].name
|
const name = attributes[i].name
|
||||||
var value = attributes[i].value
|
const value = attributes[i].value
|
||||||
|
|
||||||
// disregard attributes that are used for markdown loading/parsing
|
// disregard attributes that are used for markdown loading/parsing
|
||||||
if (/data-(markdown|separator|vertical|notes)/gi.test(name)) continue
|
if (/data-(markdown|separator|vertical|notes)/gi.test(name)) continue
|
||||||
|
@ -95,7 +95,7 @@ import { md } from './extra'
|
||||||
function createMarkdownSlide (content, options) {
|
function createMarkdownSlide (content, options) {
|
||||||
options = getSlidifyOptions(options)
|
options = getSlidifyOptions(options)
|
||||||
|
|
||||||
var notesMatch = content.split(new RegExp(options.notesSeparator, 'mgi'))
|
const notesMatch = content.split(new RegExp(options.notesSeparator, 'mgi'))
|
||||||
|
|
||||||
if (notesMatch.length === 2) {
|
if (notesMatch.length === 2) {
|
||||||
content = notesMatch[0] + '<aside class="notes" data-markdown>' + notesMatch[1].trim() + '</aside>'
|
content = notesMatch[0] + '<aside class="notes" data-markdown>' + notesMatch[1].trim() + '</aside>'
|
||||||
|
@ -115,15 +115,15 @@ import { md } from './extra'
|
||||||
function slidify (markdown, options) {
|
function slidify (markdown, options) {
|
||||||
options = getSlidifyOptions(options)
|
options = getSlidifyOptions(options)
|
||||||
|
|
||||||
var separatorRegex = new RegExp(options.separator + (options.verticalSeparator ? '|' + options.verticalSeparator : ''), 'mg')
|
const separatorRegex = new RegExp(options.separator + (options.verticalSeparator ? '|' + options.verticalSeparator : ''), 'mg')
|
||||||
var horizontalSeparatorRegex = new RegExp(options.separator)
|
const horizontalSeparatorRegex = new RegExp(options.separator)
|
||||||
|
|
||||||
var matches
|
let matches
|
||||||
var lastIndex = 0
|
let lastIndex = 0
|
||||||
var isHorizontal
|
let isHorizontal
|
||||||
var wasHorizontal = true
|
let wasHorizontal = true
|
||||||
var content
|
let content
|
||||||
var sectionStack = []
|
const sectionStack = []
|
||||||
|
|
||||||
// iterate until all blocks between separators are stacked up
|
// iterate until all blocks between separators are stacked up
|
||||||
while ((matches = separatorRegex.exec(markdown)) !== null) {
|
while ((matches = separatorRegex.exec(markdown)) !== null) {
|
||||||
|
@ -153,10 +153,10 @@ import { md } from './extra'
|
||||||
// add the remaining slide
|
// add the remaining slide
|
||||||
(wasHorizontal ? sectionStack : sectionStack[sectionStack.length - 1]).push(markdown.substring(lastIndex))
|
(wasHorizontal ? sectionStack : sectionStack[sectionStack.length - 1]).push(markdown.substring(lastIndex))
|
||||||
|
|
||||||
var markdownSections = ''
|
let markdownSections = ''
|
||||||
|
|
||||||
// flatten the hierarchical stack, and insert <section data-markdown> tags
|
// flatten the hierarchical stack, and insert <section data-markdown> tags
|
||||||
for (var i = 0, len = sectionStack.length; i < len; i++) {
|
for (let i = 0, len = sectionStack.length; i < len; i++) {
|
||||||
// vertical
|
// vertical
|
||||||
if (sectionStack[i] instanceof Array) {
|
if (sectionStack[i] instanceof Array) {
|
||||||
markdownSections += '<section ' + options.attributes + '>'
|
markdownSections += '<section ' + options.attributes + '>'
|
||||||
|
@ -180,17 +180,17 @@ import { md } from './extra'
|
||||||
* handles loading of external markdown.
|
* handles loading of external markdown.
|
||||||
*/
|
*/
|
||||||
function processSlides () {
|
function processSlides () {
|
||||||
var sections = document.querySelectorAll('[data-markdown]')
|
const sections = document.querySelectorAll('[data-markdown]')
|
||||||
var section
|
let section
|
||||||
|
|
||||||
for (var i = 0, len = sections.length; i < len; i++) {
|
for (let i = 0, len = sections.length; i < len; i++) {
|
||||||
section = sections[i]
|
section = sections[i]
|
||||||
|
|
||||||
if (section.getAttribute('data-markdown').length) {
|
if (section.getAttribute('data-markdown').length) {
|
||||||
var xhr = new XMLHttpRequest()
|
const xhr = new XMLHttpRequest()
|
||||||
var url = section.getAttribute('data-markdown')
|
const url = section.getAttribute('data-markdown')
|
||||||
|
|
||||||
var datacharset = section.getAttribute('data-charset')
|
const datacharset = section.getAttribute('data-charset')
|
||||||
|
|
||||||
// see https://developer.mozilla.org/en-US/docs/Web/API/element.getAttribute#Notes
|
// see https://developer.mozilla.org/en-US/docs/Web/API/element.getAttribute#Notes
|
||||||
if (datacharset !== null && datacharset !== '') {
|
if (datacharset !== null && datacharset !== '') {
|
||||||
|
@ -247,18 +247,18 @@ import { md } from './extra'
|
||||||
* http://stackoverflow.com/questions/5690269/disabling-chrome-cache-for-website-development/7000899#answer-11786277
|
* http://stackoverflow.com/questions/5690269/disabling-chrome-cache-for-website-development/7000899#answer-11786277
|
||||||
*/
|
*/
|
||||||
function addAttributeInElement (node, elementTarget, separator) {
|
function addAttributeInElement (node, elementTarget, separator) {
|
||||||
var mardownClassesInElementsRegex = new RegExp(separator, 'mg')
|
const mardownClassesInElementsRegex = new RegExp(separator, 'mg')
|
||||||
var mardownClassRegex = new RegExp('([^"= ]+?)="([^"=]+?)"', 'mg')
|
const mardownClassRegex = /([^"= ]+?)="([^"=]+?)"/mg
|
||||||
var nodeValue = node.nodeValue
|
let nodeValue = node.nodeValue
|
||||||
var matches
|
let matches
|
||||||
var matchesClass
|
let matchesClass
|
||||||
if ((matches = mardownClassesInElementsRegex.exec(nodeValue))) {
|
if ((matches = mardownClassesInElementsRegex.exec(nodeValue))) {
|
||||||
var classes = matches[1]
|
const classes = matches[1]
|
||||||
nodeValue = nodeValue.substring(0, matches.index) + nodeValue.substring(mardownClassesInElementsRegex.lastIndex)
|
nodeValue = nodeValue.substring(0, matches.index) + nodeValue.substring(mardownClassesInElementsRegex.lastIndex)
|
||||||
node.nodeValue = nodeValue
|
node.nodeValue = nodeValue
|
||||||
while ((matchesClass = mardownClassRegex.exec(classes))) {
|
while ((matchesClass = mardownClassRegex.exec(classes))) {
|
||||||
var name = matchesClass[1]
|
const name = matchesClass[1]
|
||||||
var value = matchesClass[2]
|
const value = matchesClass[2]
|
||||||
if (name.substr(0, 5) === 'data-' || window.whiteListAttr.indexOf(name) !== -1) { elementTarget.setAttribute(name, escapeAttrValue(value)) }
|
if (name.substr(0, 5) === 'data-' || window.whiteListAttr.indexOf(name) !== -1) { elementTarget.setAttribute(name, escapeAttrValue(value)) }
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
|
@ -272,13 +272,13 @@ import { md } from './extra'
|
||||||
*/
|
*/
|
||||||
function addAttributes (section, element, previousElement, separatorElementAttributes, separatorSectionAttributes) {
|
function addAttributes (section, element, previousElement, separatorElementAttributes, separatorSectionAttributes) {
|
||||||
if (element != null && element.childNodes !== undefined && element.childNodes.length > 0) {
|
if (element != null && element.childNodes !== undefined && element.childNodes.length > 0) {
|
||||||
var previousParentElement = element
|
let previousParentElement = element
|
||||||
for (var i = 0; i < element.childNodes.length; i++) {
|
for (let i = 0; i < element.childNodes.length; i++) {
|
||||||
var childElement = element.childNodes[i]
|
const childElement = element.childNodes[i]
|
||||||
if (i > 0) {
|
if (i > 0) {
|
||||||
let j = i - 1
|
let j = i - 1
|
||||||
while (j >= 0) {
|
while (j >= 0) {
|
||||||
var aPreviousChildElement = element.childNodes[j]
|
const aPreviousChildElement = element.childNodes[j]
|
||||||
if (typeof aPreviousChildElement.setAttribute === 'function' && aPreviousChildElement.tagName !== 'BR') {
|
if (typeof aPreviousChildElement.setAttribute === 'function' && aPreviousChildElement.tagName !== 'BR') {
|
||||||
previousParentElement = aPreviousChildElement
|
previousParentElement = aPreviousChildElement
|
||||||
break
|
break
|
||||||
|
@ -286,7 +286,7 @@ import { md } from './extra'
|
||||||
j = j - 1
|
j = j - 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var parentSection = section
|
let parentSection = section
|
||||||
if (childElement.nodeName === 'section') {
|
if (childElement.nodeName === 'section') {
|
||||||
parentSection = childElement
|
parentSection = childElement
|
||||||
previousParentElement = childElement
|
previousParentElement = childElement
|
||||||
|
@ -309,21 +309,21 @@ import { md } from './extra'
|
||||||
* DOM to HTML.
|
* DOM to HTML.
|
||||||
*/
|
*/
|
||||||
function convertSlides () {
|
function convertSlides () {
|
||||||
var sections = document.querySelectorAll('[data-markdown]')
|
const sections = document.querySelectorAll('[data-markdown]')
|
||||||
|
|
||||||
for (var i = 0, len = sections.length; i < len; i++) {
|
for (let i = 0, len = sections.length; i < len; i++) {
|
||||||
var section = sections[i]
|
const section = sections[i]
|
||||||
|
|
||||||
// Only parse the same slide once
|
// Only parse the same slide once
|
||||||
if (!section.getAttribute('data-markdown-parsed')) {
|
if (!section.getAttribute('data-markdown-parsed')) {
|
||||||
section.setAttribute('data-markdown-parsed', true)
|
section.setAttribute('data-markdown-parsed', true)
|
||||||
|
|
||||||
var notes = section.querySelector('aside.notes')
|
const notes = section.querySelector('aside.notes')
|
||||||
var markdown = getMarkdownFromSlide(section)
|
let markdown = getMarkdownFromSlide(section)
|
||||||
markdown = markdown.replace(/</g, '<').replace(/>/g, '>')
|
markdown = markdown.replace(/</g, '<').replace(/>/g, '>')
|
||||||
var rendered = md.render(markdown)
|
let rendered = md.render(markdown)
|
||||||
rendered = preventXSS(rendered)
|
rendered = preventXSS(rendered)
|
||||||
var result = window.postProcess(rendered)
|
const result = window.postProcess(rendered)
|
||||||
section.innerHTML = result[0].outerHTML
|
section.innerHTML = result[0].outerHTML
|
||||||
addAttributes(section, section, null, section.getAttribute('data-element-attributes') ||
|
addAttributes(section, section, null, section.getAttribute('data-element-attributes') ||
|
||||||
section.parentNode.getAttribute('data-element-attributes') ||
|
section.parentNode.getAttribute('data-element-attributes') ||
|
||||||
|
|
|
@ -26,7 +26,7 @@ function extend () {
|
||||||
|
|
||||||
for (const source of arguments) {
|
for (const source of arguments) {
|
||||||
for (const key in source) {
|
for (const key in source) {
|
||||||
if (source.hasOwnProperty(key)) {
|
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
||||||
target[key] = source[key]
|
target[key] = source[key]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,18 +36,21 @@ function extend () {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Optional libraries used to extend on reveal.js
|
// Optional libraries used to extend on reveal.js
|
||||||
const deps = [{
|
const deps = [
|
||||||
src: `${serverurl}/build/reveal.js/lib/js/classList.js`,
|
{
|
||||||
condition () {
|
src: `${serverurl}/build/reveal.js/lib/js/classList.js`,
|
||||||
return !document.body.classList
|
condition () {
|
||||||
|
return !document.body.classList
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: `${serverurl}/build/reveal.js/plugin/notes/notes.js`,
|
||||||
|
async: true,
|
||||||
|
condition () {
|
||||||
|
return !!document.body.classList
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, {
|
]
|
||||||
src: `${serverurl}/build/reveal.js/plugin/notes/notes.js`,
|
|
||||||
async: true,
|
|
||||||
condition () {
|
|
||||||
return !!document.body.classList
|
|
||||||
}
|
|
||||||
}]
|
|
||||||
|
|
||||||
const slideOptions = {
|
const slideOptions = {
|
||||||
separator: '^(\r\n?|\n)---(\r\n?|\n)$',
|
separator: '^(\r\n?|\n)---(\r\n?|\n)$',
|
||||||
|
@ -73,60 +76,64 @@ const defaultOptions = {
|
||||||
// options from yaml meta
|
// options from yaml meta
|
||||||
const meta = JSON.parse($('#meta').text())
|
const meta = JSON.parse($('#meta').text())
|
||||||
const metaSlideOptions = !!meta && !!meta.slideOptions ? meta.slideOptions : {}
|
const metaSlideOptions = !!meta && !!meta.slideOptions ? meta.slideOptions : {}
|
||||||
var options = {
|
let options =
|
||||||
autoPlayMedia: metaSlideOptions.autoPlayMedia,
|
{
|
||||||
autoSlide: metaSlideOptions.autoSlide,
|
autoPlayMedia: metaSlideOptions.autoPlayMedia,
|
||||||
autoSlideStoppable: metaSlideOptions.autoSlideStoppable,
|
autoSlide: metaSlideOptions.autoSlide,
|
||||||
backgroundTransition: metaSlideOptions.backgroundTransition,
|
autoSlideStoppable: metaSlideOptions.autoSlideStoppable,
|
||||||
center: metaSlideOptions.center,
|
backgroundTransition: metaSlideOptions.backgroundTransition,
|
||||||
controls: metaSlideOptions.controls,
|
center: metaSlideOptions.center,
|
||||||
controlsBackArrows: metaSlideOptions.controlsBackArrows,
|
controls: metaSlideOptions.controls,
|
||||||
controlsLayout: metaSlideOptions.controlsLayout,
|
controlsBackArrows: metaSlideOptions.controlsBackArrows,
|
||||||
controlsTutorial: metaSlideOptions.controlsTutorial,
|
controlsLayout: metaSlideOptions.controlsLayout,
|
||||||
defaultTiming: metaSlideOptions.defaultTiming,
|
controlsTutorial: metaSlideOptions.controlsTutorial,
|
||||||
display: metaSlideOptions.display,
|
defaultTiming: metaSlideOptions.defaultTiming,
|
||||||
embedded: metaSlideOptions.embedded,
|
display: metaSlideOptions.display,
|
||||||
fragmentInURL: metaSlideOptions.fragmentInURL,
|
embedded: metaSlideOptions.embedded,
|
||||||
fragments: metaSlideOptions.fragments,
|
fragmentInURL: metaSlideOptions.fragmentInURL,
|
||||||
hash: metaSlideOptions.hash,
|
fragments: metaSlideOptions.fragments,
|
||||||
height: metaSlideOptions.height,
|
hash: metaSlideOptions.hash,
|
||||||
help: metaSlideOptions.help,
|
height: metaSlideOptions.height,
|
||||||
hideAddressBar: metaSlideOptions.hideAddressBar,
|
help: metaSlideOptions.help,
|
||||||
hideCursorTime: metaSlideOptions.hideCursorTime,
|
hideAddressBar: metaSlideOptions.hideAddressBar,
|
||||||
hideInactiveCursor: metaSlideOptions.hideInactiveCursor,
|
hideCursorTime: metaSlideOptions.hideCursorTime,
|
||||||
history: metaSlideOptions.history,
|
hideInactiveCursor: metaSlideOptions.hideInactiveCursor,
|
||||||
keyboard: metaSlideOptions.keyboard,
|
history: metaSlideOptions.history,
|
||||||
loop: metaSlideOptions.loop,
|
keyboard: metaSlideOptions.keyboard,
|
||||||
margin: metaSlideOptions.margin,
|
loop: metaSlideOptions.loop,
|
||||||
maxScale: metaSlideOptions.maxScale,
|
margin: metaSlideOptions.margin,
|
||||||
minScale: metaSlideOptions.minScale,
|
maxScale: metaSlideOptions.maxScale,
|
||||||
minimumTimePerSlide: metaSlideOptions.minimumTimePerSlide,
|
minScale: metaSlideOptions.minScale,
|
||||||
mobileViewDistance: metaSlideOptions.mobileViewDistance,
|
minimumTimePerSlide: metaSlideOptions.minimumTimePerSlide,
|
||||||
mouseWheel: metaSlideOptions.mouseWheel,
|
mobileViewDistance: metaSlideOptions.mobileViewDistance,
|
||||||
navigationMode: metaSlideOptions.navigationMode,
|
mouseWheel: metaSlideOptions.mouseWheel,
|
||||||
overview: metaSlideOptions.overview,
|
navigationMode: metaSlideOptions.navigationMode,
|
||||||
parallaxBackgroundHorizontal: metaSlideOptions.parallaxBackgroundHorizontal,
|
overview: metaSlideOptions.overview,
|
||||||
parallaxBackgroundImage: metaSlideOptions.parallaxBackgroundImage,
|
parallaxBackgroundHorizontal: metaSlideOptions.parallaxBackgroundHorizontal,
|
||||||
parallaxBackgroundSize: metaSlideOptions.parallaxBackgroundSize,
|
parallaxBackgroundImage: metaSlideOptions.parallaxBackgroundImage,
|
||||||
parallaxBackgroundVertical: metaSlideOptions.parallaxBackgroundVertical,
|
parallaxBackgroundSize: metaSlideOptions.parallaxBackgroundSize,
|
||||||
preloadIframes: metaSlideOptions.preloadIframes,
|
parallaxBackgroundVertical: metaSlideOptions.parallaxBackgroundVertical,
|
||||||
previewLinks: metaSlideOptions.previewLinks,
|
preloadIframes: metaSlideOptions.preloadIframes,
|
||||||
progress: metaSlideOptions.progress,
|
previewLinks: metaSlideOptions.previewLinks,
|
||||||
rtl: metaSlideOptions.rtl,
|
progress: metaSlideOptions.progress,
|
||||||
showNotes: metaSlideOptions.showNotes,
|
rtl: metaSlideOptions.rtl,
|
||||||
shuffle: metaSlideOptions.shuffle,
|
showNotes: metaSlideOptions.showNotes,
|
||||||
slideNumber: metaSlideOptions.slideNumber,
|
shuffle: metaSlideOptions.shuffle,
|
||||||
theme: metaSlideOptions.theme,
|
slideNumber: metaSlideOptions.slideNumber,
|
||||||
totalTime: metaSlideOptions.totalTime,
|
theme: metaSlideOptions.theme,
|
||||||
touch: metaSlideOptions.touch,
|
totalTime: metaSlideOptions.totalTime,
|
||||||
transition: metaSlideOptions.transition,
|
touch: metaSlideOptions.touch,
|
||||||
transitionSpeed: metaSlideOptions.transitionSpeed,
|
transition: metaSlideOptions.transition,
|
||||||
viewDistance: metaSlideOptions.viewDistance,
|
transitionSpeed: metaSlideOptions.transitionSpeed,
|
||||||
width: metaSlideOptions.width
|
viewDistance: metaSlideOptions.viewDistance,
|
||||||
} || {}
|
width: metaSlideOptions.width
|
||||||
|
} || {}
|
||||||
|
|
||||||
for (const key in options) {
|
for (const key in options) {
|
||||||
if (options.hasOwnProperty(key) && options[key] === undefined) {
|
if (
|
||||||
|
Object.prototype.hasOwnProperty.call(options, key) &&
|
||||||
|
options[key] === undefined
|
||||||
|
) {
|
||||||
delete options[key]
|
delete options[key]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -165,14 +172,14 @@ window.viewAjaxCallback = () => {
|
||||||
function renderSlide (event) {
|
function renderSlide (event) {
|
||||||
if (window.location.search.match(/print-pdf/gi)) {
|
if (window.location.search.match(/print-pdf/gi)) {
|
||||||
const slides = $('.slides')
|
const slides = $('.slides')
|
||||||
let title = document.title
|
const title = document.title
|
||||||
finishView(slides)
|
finishView(slides)
|
||||||
document.title = title
|
document.title = title
|
||||||
Reveal.layout()
|
Reveal.layout()
|
||||||
} else {
|
} else {
|
||||||
const markdown = $(event.currentSlide)
|
const markdown = $(event.currentSlide)
|
||||||
if (!markdown.attr('data-rendered')) {
|
if (!markdown.attr('data-rendered')) {
|
||||||
let title = document.title
|
const title = document.title
|
||||||
finishView(markdown)
|
finishView(markdown)
|
||||||
markdown.attr('data-rendered', 'true')
|
markdown.attr('data-rendered', 'true')
|
||||||
document.title = title
|
document.title = title
|
||||||
|
@ -181,7 +188,7 @@ function renderSlide (event) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Reveal.addEventListener('ready', event => {
|
Reveal.addEventListener('ready', (event) => {
|
||||||
renderSlide(event)
|
renderSlide(event)
|
||||||
const markdown = $(event.currentSlide)
|
const markdown = $(event.currentSlide)
|
||||||
// force browser redraw
|
// force browser redraw
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import base64url from 'base64url'
|
import base64url from 'base64url'
|
||||||
|
|
||||||
let uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i
|
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i
|
||||||
|
|
||||||
export function checkNoteIdValid (id) {
|
export function checkNoteIdValid (id) {
|
||||||
let result = id.match(uuidRegex)
|
const result = id.match(uuidRegex)
|
||||||
if (result && result.length === 1) {
|
if (result && result.length === 1) {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
|
@ -13,16 +13,16 @@ export function checkNoteIdValid (id) {
|
||||||
|
|
||||||
export function encodeNoteId (id) {
|
export function encodeNoteId (id) {
|
||||||
// remove dashes in UUID and encode in url-safe base64
|
// remove dashes in UUID and encode in url-safe base64
|
||||||
let str = id.replace(/-/g, '')
|
const str = id.replace(/-/g, '')
|
||||||
let hexStr = Buffer.from(str, 'hex')
|
const hexStr = Buffer.from(str, 'hex')
|
||||||
return base64url.encode(hexStr)
|
return base64url.encode(hexStr)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function decodeNoteId (encodedId) {
|
export function decodeNoteId (encodedId) {
|
||||||
// decode from url-safe base64
|
// decode from url-safe base64
|
||||||
let id = base64url.toBuffer(encodedId).toString('hex')
|
const id = base64url.toBuffer(encodedId).toString('hex')
|
||||||
// add dashes between the UUID string parts
|
// add dashes between the UUID string parts
|
||||||
let idParts = []
|
const idParts = []
|
||||||
idParts.push(id.substr(0, 8))
|
idParts.push(id.substr(0, 8))
|
||||||
idParts.push(id.substr(8, 4))
|
idParts.push(id.substr(8, 4))
|
||||||
idParts.push(id.substr(12, 4))
|
idParts.push(id.substr(12, 4))
|
||||||
|
|
12
test/csp.js
12
test/csp.js
|
@ -46,7 +46,7 @@ describe('Content security policies', function () {
|
||||||
|
|
||||||
// beginnging Tests
|
// beginnging Tests
|
||||||
it('Disable CDN', function () {
|
it('Disable CDN', function () {
|
||||||
let testconfig = defaultConfig
|
const testconfig = defaultConfig
|
||||||
testconfig.useCDN = false
|
testconfig.useCDN = false
|
||||||
mock('../lib/config', testconfig)
|
mock('../lib/config', testconfig)
|
||||||
csp = mock.reRequire('../lib/csp')
|
csp = mock.reRequire('../lib/csp')
|
||||||
|
@ -60,7 +60,7 @@ describe('Content security policies', function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Disable Google Analytics', function () {
|
it('Disable Google Analytics', function () {
|
||||||
let testconfig = defaultConfig
|
const testconfig = defaultConfig
|
||||||
testconfig.csp.addGoogleAnalytics = false
|
testconfig.csp.addGoogleAnalytics = false
|
||||||
mock('../lib/config', testconfig)
|
mock('../lib/config', testconfig)
|
||||||
csp = mock.reRequire('../lib/csp')
|
csp = mock.reRequire('../lib/csp')
|
||||||
|
@ -69,7 +69,7 @@ describe('Content security policies', function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Disable Disqus', function () {
|
it('Disable Disqus', function () {
|
||||||
let testconfig = defaultConfig
|
const testconfig = defaultConfig
|
||||||
testconfig.csp.addDisqus = false
|
testconfig.csp.addDisqus = false
|
||||||
mock('../lib/config', testconfig)
|
mock('../lib/config', testconfig)
|
||||||
csp = mock.reRequire('../lib/csp')
|
csp = mock.reRequire('../lib/csp')
|
||||||
|
@ -82,7 +82,7 @@ describe('Content security policies', function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Include dropbox if configured', function () {
|
it('Include dropbox if configured', function () {
|
||||||
let testconfig = defaultConfig
|
const testconfig = defaultConfig
|
||||||
testconfig.dropbox.appKey = 'hedgedoc'
|
testconfig.dropbox.appKey = 'hedgedoc'
|
||||||
mock('../lib/config', testconfig)
|
mock('../lib/config', testconfig)
|
||||||
csp = mock.reRequire('../lib/csp')
|
csp = mock.reRequire('../lib/csp')
|
||||||
|
@ -92,7 +92,7 @@ describe('Content security policies', function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Set ReportURI', function () {
|
it('Set ReportURI', function () {
|
||||||
let testconfig = defaultConfig
|
const testconfig = defaultConfig
|
||||||
testconfig.csp.reportURI = 'https://example.com/reportURI'
|
testconfig.csp.reportURI = 'https://example.com/reportURI'
|
||||||
mock('../lib/config', testconfig)
|
mock('../lib/config', testconfig)
|
||||||
csp = mock.reRequire('../lib/csp')
|
csp = mock.reRequire('../lib/csp')
|
||||||
|
@ -101,7 +101,7 @@ describe('Content security policies', function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Set own directives', function () {
|
it('Set own directives', function () {
|
||||||
let testconfig = defaultConfig
|
const testconfig = defaultConfig
|
||||||
mock('../lib/config', defaultConfig)
|
mock('../lib/config', defaultConfig)
|
||||||
csp = mock.reRequire('../lib/csp')
|
csp = mock.reRequire('../lib/csp')
|
||||||
const unextendedCSP = csp.computeDirectives()
|
const unextendedCSP = csp.computeDirectives()
|
||||||
|
|
|
@ -9,7 +9,7 @@ describe('generateAvatarURL() gravatar enabled', function () {
|
||||||
let avatars
|
let avatars
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
// Reset config to make sure we don't influence other tests
|
// Reset config to make sure we don't influence other tests
|
||||||
let testconfig = {
|
const testconfig = {
|
||||||
allowGravatar: true,
|
allowGravatar: true,
|
||||||
serverURL: 'http://localhost:3000',
|
serverURL: 'http://localhost:3000',
|
||||||
port: 3000
|
port: 3000
|
||||||
|
@ -32,7 +32,7 @@ describe('generateAvatarURL() gravatar disabled', function () {
|
||||||
let avatars
|
let avatars
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
// Reset config to make sure we don't influence other tests
|
// Reset config to make sure we don't influence other tests
|
||||||
let testconfig = {
|
const testconfig = {
|
||||||
allowGravatar: false,
|
allowGravatar: false,
|
||||||
serverURL: 'http://localhost:3000',
|
serverURL: 'http://localhost:3000',
|
||||||
port: 3000
|
port: 3000
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue