mirror of https://github.com/status-im/codimd.git
Merge branch 'develop' into feature/configurable-break-style
This commit is contained in:
commit
75ee5ad255
|
@ -3,7 +3,8 @@
|
||||||
"db": {
|
"db": {
|
||||||
"dialect": "sqlite",
|
"dialect": "sqlite",
|
||||||
"storage": ":memory:"
|
"storage": ":memory:"
|
||||||
}
|
},
|
||||||
|
"linkifyHeaderStyle": "gfm"
|
||||||
},
|
},
|
||||||
"development": {
|
"development": {
|
||||||
"loglevel": "debug",
|
"loglevel": "debug",
|
||||||
|
@ -13,7 +14,8 @@
|
||||||
"db": {
|
"db": {
|
||||||
"dialect": "sqlite",
|
"dialect": "sqlite",
|
||||||
"storage": "./db.codimd.sqlite"
|
"storage": "./db.codimd.sqlite"
|
||||||
}
|
},
|
||||||
|
"linkifyHeaderStyle": "gfm"
|
||||||
},
|
},
|
||||||
"production": {
|
"production": {
|
||||||
"domain": "localhost",
|
"domain": "localhost",
|
||||||
|
@ -127,6 +129,7 @@
|
||||||
"plantuml":
|
"plantuml":
|
||||||
{
|
{
|
||||||
"server": "https://www.plantuml.com/plantuml"
|
"server": "https://www.plantuml.com/plantuml"
|
||||||
}
|
},
|
||||||
|
"linkifyHeaderStyle": "gfm"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,6 @@ RUN set -xe && \
|
||||||
FROM hackmdio/runtime:1.0.4
|
FROM hackmdio/runtime:1.0.4
|
||||||
USER hackmd
|
USER hackmd
|
||||||
WORKDIR /home/hackmd/app
|
WORKDIR /home/hackmd/app
|
||||||
COPY --from=BUILD /home/hackmd/app .
|
COPY --chown=1500:1500 --from=BUILD /home/hackmd/app .
|
||||||
EXPOSE 3000
|
EXPOSE 3000
|
||||||
ENTRYPOINT ["/home/hackmd/app/docker-entrypoint.sh"]
|
ENTRYPOINT ["/home/hackmd/app/docker-entrypoint.sh"]
|
||||||
|
|
|
@ -104,6 +104,7 @@ module.exports = {
|
||||||
consumerSecret: undefined
|
consumerSecret: undefined
|
||||||
},
|
},
|
||||||
github: {
|
github: {
|
||||||
|
enterpriseURL: undefined, // if you use github.com, not need to specify
|
||||||
clientID: undefined,
|
clientID: undefined,
|
||||||
clientSecret: undefined
|
clientSecret: undefined
|
||||||
},
|
},
|
||||||
|
@ -163,5 +164,19 @@ module.exports = {
|
||||||
allowGravatar: true,
|
allowGravatar: true,
|
||||||
allowPDFExport: true,
|
allowPDFExport: true,
|
||||||
openID: false,
|
openID: false,
|
||||||
defaultUseHardbreak: true
|
defaultUseHardbreak: true,
|
||||||
|
// linkifyHeaderStyle - How is a header text converted into a link id.
|
||||||
|
// Header Example: "3.1. Good Morning my Friend! - Do you have 5$?"
|
||||||
|
// * 'keep-case' is the legacy CodiMD value.
|
||||||
|
// Generated id: "31-Good-Morning-my-Friend---Do-you-have-5"
|
||||||
|
// * 'lower-case' is the same like legacy (see above), but converted to lower-case.
|
||||||
|
// Generated id: "#31-good-morning-my-friend---do-you-have-5"
|
||||||
|
// * 'gfm' _GitHub-Flavored Markdown_ style as described here:
|
||||||
|
// https://gist.github.com/asabaylus/3071099#gistcomment-1593627
|
||||||
|
// It works like 'lower-case', but making sure the ID is unique.
|
||||||
|
// This is What GitHub, GitLab and (hopefully) most other tools use.
|
||||||
|
// Generated id: "31-good-morning-my-friend---do-you-have-5"
|
||||||
|
// 2nd appearance: "31-good-morning-my-friend---do-you-have-5-1"
|
||||||
|
// 3rd appearance: "31-good-morning-my-friend---do-you-have-5-2"
|
||||||
|
linkifyHeaderStyle: 'keep-case'
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,6 +66,7 @@ module.exports = {
|
||||||
consumerSecret: process.env.CMD_TWITTER_CONSUMERSECRET
|
consumerSecret: process.env.CMD_TWITTER_CONSUMERSECRET
|
||||||
},
|
},
|
||||||
github: {
|
github: {
|
||||||
|
enterpriseURL: process.env.CMD_GITHUB_ENTERPRISE_URL,
|
||||||
clientID: process.env.CMD_GITHUB_CLIENTID,
|
clientID: process.env.CMD_GITHUB_CLIENTID,
|
||||||
clientSecret: process.env.CMD_GITHUB_CLIENTSECRET
|
clientSecret: process.env.CMD_GITHUB_CLIENTSECRET
|
||||||
},
|
},
|
||||||
|
@ -136,5 +137,6 @@ module.exports = {
|
||||||
allowGravatar: toBooleanConfig(process.env.CMD_ALLOW_GRAVATAR),
|
allowGravatar: toBooleanConfig(process.env.CMD_ALLOW_GRAVATAR),
|
||||||
allowPDFExport: toBooleanConfig(process.env.CMD_ALLOW_PDF_EXPORT),
|
allowPDFExport: toBooleanConfig(process.env.CMD_ALLOW_PDF_EXPORT),
|
||||||
openID: toBooleanConfig(process.env.CMD_OPENID),
|
openID: toBooleanConfig(process.env.CMD_OPENID),
|
||||||
defaultUseHardbreak: toBooleanConfig(process.env.CMD_DEFAULT_USE_HARD_BREAK)
|
defaultUseHardbreak: toBooleanConfig(process.env.CMD_DEFAULT_USE_HARD_BREAK),
|
||||||
|
linkifyHeaderStyle: process.env.CMD_LINKIFY_HEADER_STYLE
|
||||||
}
|
}
|
||||||
|
|
|
@ -200,6 +200,7 @@ config.sslCAPath.forEach(function (capath, i, array) {
|
||||||
array[i] = path.resolve(appRootPath, capath)
|
array[i] = path.resolve(appRootPath, capath)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
config.appRootPath = appRootPath
|
||||||
config.sslCertPath = path.resolve(appRootPath, config.sslCertPath)
|
config.sslCertPath = path.resolve(appRootPath, config.sslCertPath)
|
||||||
config.sslKeyPath = path.resolve(appRootPath, config.sslKeyPath)
|
config.sslKeyPath = path.resolve(appRootPath, config.sslKeyPath)
|
||||||
config.dhParamPath = path.resolve(appRootPath, config.dhParamPath)
|
config.dhParamPath = path.resolve(appRootPath, config.dhParamPath)
|
||||||
|
|
|
@ -103,7 +103,8 @@ module.exports = function (sequelize, DataTypes) {
|
||||||
else photo += '?size=bigger'
|
else photo += '?size=bigger'
|
||||||
break
|
break
|
||||||
case 'github':
|
case 'github':
|
||||||
photo = 'https://avatars.githubusercontent.com/u/' + profile.id
|
if (profile.photos && profile.photos[0]) photo = profile.photos[0].value.replace('?', '')
|
||||||
|
else photo = 'https://avatars.githubusercontent.com/u/' + profile.id
|
||||||
if (bigger) photo += '?s=400'
|
if (bigger) photo += '?s=400'
|
||||||
else photo += '?s=96'
|
else photo += '?s=96'
|
||||||
break
|
break
|
||||||
|
|
|
@ -316,17 +316,22 @@ function actionPDF (req, res, note) {
|
||||||
var content = extracted.markdown
|
var content = extracted.markdown
|
||||||
var title = models.Note.decodeTitle(note.title)
|
var title = models.Note.decodeTitle(note.title)
|
||||||
|
|
||||||
|
var highlightCssPath = path.join(config.appRootPath, '/node_modules/highlight.js/styles/github-gist.css')
|
||||||
|
|
||||||
if (!fs.existsSync(config.tmpPath)) {
|
if (!fs.existsSync(config.tmpPath)) {
|
||||||
fs.mkdirSync(config.tmpPath)
|
fs.mkdirSync(config.tmpPath)
|
||||||
}
|
}
|
||||||
var path = config.tmpPath + '/' + Date.now() + '.pdf'
|
var pdfPath = config.tmpPath + '/' + Date.now() + '.pdf'
|
||||||
content = content.replace(/\]\(\//g, '](' + url + '/')
|
content = content.replace(/\]\(\//g, '](' + url + '/')
|
||||||
markdownpdf().from.string(content).to(path, function () {
|
var markdownpdfOptions = {
|
||||||
if (!fs.existsSync(path)) {
|
highlightCssPath: highlightCssPath
|
||||||
logger.error('PDF seems to not be generated as expected. File doesn\'t exist: ' + path)
|
}
|
||||||
|
markdownpdf(markdownpdfOptions).from.string(content).to(pdfPath, function () {
|
||||||
|
if (!fs.existsSync(pdfPath)) {
|
||||||
|
logger.error('PDF seems to not be generated as expected. File doesn\'t exist: ' + pdfPath)
|
||||||
return errorInternalError(res)
|
return errorInternalError(res)
|
||||||
}
|
}
|
||||||
var stream = fs.createReadStream(path)
|
var stream = fs.createReadStream(pdfPath)
|
||||||
var filename = title
|
var filename = title
|
||||||
// Be careful of special characters
|
// Be careful of special characters
|
||||||
filename = encodeURIComponent(filename)
|
filename = encodeURIComponent(filename)
|
||||||
|
@ -336,7 +341,7 @@ function actionPDF (req, res, note) {
|
||||||
res.setHeader('Content-Type', 'application/pdf; charset=UTF-8')
|
res.setHeader('Content-Type', 'application/pdf; charset=UTF-8')
|
||||||
res.setHeader('X-Robots-Tag', 'noindex, nofollow') // prevent crawling
|
res.setHeader('X-Robots-Tag', 'noindex, nofollow') // prevent crawling
|
||||||
stream.pipe(res)
|
stream.pipe(res)
|
||||||
fs.unlinkSync(path)
|
fs.unlinkSync(pdfPath)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,13 +6,21 @@ const GithubStrategy = require('passport-github').Strategy
|
||||||
const config = require('../../../config')
|
const config = require('../../../config')
|
||||||
const response = require('../../../response')
|
const response = require('../../../response')
|
||||||
const { setReturnToFromReferer, passportGeneralCallback } = require('../utils')
|
const { setReturnToFromReferer, passportGeneralCallback } = require('../utils')
|
||||||
|
const { URL } = require('url')
|
||||||
|
|
||||||
const githubAuth = module.exports = Router()
|
const githubAuth = module.exports = Router()
|
||||||
|
|
||||||
|
function githubUrl (path) {
|
||||||
|
return config.github.enterpriseURL && new URL(path, config.github.enterpriseURL).toString()
|
||||||
|
}
|
||||||
|
|
||||||
passport.use(new GithubStrategy({
|
passport.use(new GithubStrategy({
|
||||||
clientID: config.github.clientID,
|
clientID: config.github.clientID,
|
||||||
clientSecret: config.github.clientSecret,
|
clientSecret: config.github.clientSecret,
|
||||||
callbackURL: config.serverURL + '/auth/github/callback'
|
callbackURL: config.serverURL + '/auth/github/callback',
|
||||||
|
authorizationURL: githubUrl('login/oauth/authorize'),
|
||||||
|
tokenURL: githubUrl('login/oauth/access_token'),
|
||||||
|
userProfileURL: githubUrl('api/v3/user')
|
||||||
}, passportGeneralCallback))
|
}, passportGeneralCallback))
|
||||||
|
|
||||||
githubAuth.get('/auth/github', function (req, res, next) {
|
githubAuth.get('/auth/github', function (req, res, next) {
|
||||||
|
|
|
@ -6,16 +6,24 @@ const GitlabStrategy = require('passport-gitlab2').Strategy
|
||||||
const config = require('../../../config')
|
const config = require('../../../config')
|
||||||
const response = require('../../../response')
|
const response = require('../../../response')
|
||||||
const { setReturnToFromReferer, passportGeneralCallback } = require('../utils')
|
const { setReturnToFromReferer, passportGeneralCallback } = require('../utils')
|
||||||
|
const HttpsProxyAgent = require('https-proxy-agent')
|
||||||
|
|
||||||
const gitlabAuth = module.exports = Router()
|
const gitlabAuth = module.exports = Router()
|
||||||
|
|
||||||
passport.use(new GitlabStrategy({
|
const gitlabAuthStrategy = new GitlabStrategy({
|
||||||
baseURL: config.gitlab.baseURL,
|
baseURL: config.gitlab.baseURL,
|
||||||
clientID: config.gitlab.clientID,
|
clientID: config.gitlab.clientID,
|
||||||
clientSecret: config.gitlab.clientSecret,
|
clientSecret: config.gitlab.clientSecret,
|
||||||
scope: config.gitlab.scope,
|
scope: config.gitlab.scope,
|
||||||
callbackURL: config.serverURL + '/auth/gitlab/callback'
|
callbackURL: config.serverURL + '/auth/gitlab/callback'
|
||||||
}, passportGeneralCallback))
|
}, passportGeneralCallback)
|
||||||
|
|
||||||
|
if (process.env['https_proxy']) {
|
||||||
|
const httpsProxyAgent = new HttpsProxyAgent(process.env['https_proxy'])
|
||||||
|
gitlabAuthStrategy._oauth2.setAgent(httpsProxyAgent)
|
||||||
|
}
|
||||||
|
|
||||||
|
passport.use(gitlabAuthStrategy)
|
||||||
|
|
||||||
gitlabAuth.get('/auth/gitlab', function (req, res, next) {
|
gitlabAuth.get('/auth/gitlab', function (req, res, next) {
|
||||||
setReturnToFromReferer(req)
|
setReturnToFromReferer(req)
|
||||||
|
|
|
@ -100,7 +100,8 @@ statusRouter.get('/config', function (req, res) {
|
||||||
plantumlServer: config.plantuml.server,
|
plantumlServer: config.plantuml.server,
|
||||||
DROPBOX_APP_KEY: config.dropbox.appKey,
|
DROPBOX_APP_KEY: config.dropbox.appKey,
|
||||||
allowedUploadMimeTypes: config.allowedUploadMimeTypes,
|
allowedUploadMimeTypes: config.allowedUploadMimeTypes,
|
||||||
defaultUseHardbreak: config.defaultUseHardbreak
|
defaultUseHardbreak: config.defaultUseHardbreak,
|
||||||
|
linkifyHeaderStyle: config.linkifyHeaderStyle
|
||||||
}
|
}
|
||||||
res.set({
|
res.set({
|
||||||
'Cache-Control': 'private', // only cache by client
|
'Cache-Control': 'private', // only cache by client
|
||||||
|
|
|
@ -67,6 +67,7 @@
|
||||||
"handlebars": "~4.1.2",
|
"handlebars": "~4.1.2",
|
||||||
"helmet": "~3.20.0",
|
"helmet": "~3.20.0",
|
||||||
"highlight.js": "~9.15.9",
|
"highlight.js": "~9.15.9",
|
||||||
|
"https-proxy-agent": "^3.0.1",
|
||||||
"i18n": "~0.8.3",
|
"i18n": "~0.8.3",
|
||||||
"ionicons": "~2.0.1",
|
"ionicons": "~2.0.1",
|
||||||
"isomorphic-fetch": "~2.2.1",
|
"isomorphic-fetch": "~2.2.1",
|
||||||
|
|
|
@ -168,11 +168,11 @@ export function renderTags (view) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function slugifyWithUTF8 (text) {
|
function slugifyWithUTF8 (text) {
|
||||||
// remove html tags and trim spaces
|
// remove HTML tags and trim spaces
|
||||||
let newText = stripTags(text.toString().trim())
|
let newText = stripTags(text.toString().trim())
|
||||||
// replace all spaces in between to dashes
|
// replace space between words with dashes
|
||||||
newText = newText.replace(/\s+/g, '-')
|
newText = newText.replace(/\s+/g, '-')
|
||||||
// slugify string to make it valid for attribute
|
// slugify string to make it valid as an attribute
|
||||||
newText = newText.replace(/([!"#$%&'()*+,./:;<=>?@[\\\]^`{|}~])/g, '')
|
newText = newText.replace(/([!"#$%&'()*+,./:;<=>?@[\\\]^`{|}~])/g, '')
|
||||||
return newText
|
return newText
|
||||||
}
|
}
|
||||||
|
@ -859,6 +859,36 @@ const anchorForId = id => {
|
||||||
return anchor
|
return anchor
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const createHeaderId = (headerContent, headerIds = null) => {
|
||||||
|
// to escape characters not allow in css and humanize
|
||||||
|
const slug = slugifyWithUTF8(headerContent)
|
||||||
|
let id
|
||||||
|
if (window.linkifyHeaderStyle === 'keep-case') {
|
||||||
|
id = slug
|
||||||
|
} else if (window.linkifyHeaderStyle === 'lower-case') {
|
||||||
|
// to make compatible with GitHub, GitLab, Pandoc and many more
|
||||||
|
id = slug.toLowerCase()
|
||||||
|
} else if (window.linkifyHeaderStyle === 'gfm') {
|
||||||
|
// see GitHub implementation reference:
|
||||||
|
// https://gist.github.com/asabaylus/3071099#gistcomment-1593627
|
||||||
|
// it works like 'lower-case', but ...
|
||||||
|
const idBase = slug.toLowerCase()
|
||||||
|
id = idBase
|
||||||
|
if (headerIds !== null) {
|
||||||
|
// ... making sure the id is unique
|
||||||
|
let i = 1
|
||||||
|
while (headerIds.has(id)) {
|
||||||
|
id = idBase + '-' + i
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
headerIds.add(id)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Error('Unknown linkifyHeaderStyle value "' + window.linkifyHeaderStyle + '"')
|
||||||
|
}
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
|
||||||
const linkifyAnchors = (level, containingElement) => {
|
const linkifyAnchors = (level, containingElement) => {
|
||||||
const headers = containingElement.getElementsByTagName(`h${level}`)
|
const headers = containingElement.getElementsByTagName(`h${level}`)
|
||||||
|
|
||||||
|
@ -866,9 +896,7 @@ const linkifyAnchors = (level, containingElement) => {
|
||||||
const 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 === '') {
|
||||||
// to escape characters not allow in css and humanize
|
header.id = createHeaderId(getHeaderContent(header))
|
||||||
const id = slugifyWithUTF8(getHeaderContent(header))
|
|
||||||
header.id = id
|
|
||||||
}
|
}
|
||||||
if (!(typeof header.id === 'undefined' || header.id === '')) {
|
if (!(typeof header.id === 'undefined' || header.id === '')) {
|
||||||
header.insertBefore(anchorForId(header.id), header.firstChild)
|
header.insertBefore(anchorForId(header.id), header.firstChild)
|
||||||
|
@ -894,20 +922,43 @@ function getHeaderContent (header) {
|
||||||
return headerHTML[0].innerHTML
|
return headerHTML[0].innerHTML
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function changeHeaderId ($header, id, newId) {
|
||||||
|
$header.attr('id', newId)
|
||||||
|
const $headerLink = $header.find(`> a.anchor[href="#${id}"]`)
|
||||||
|
$headerLink.attr('href', `#${newId}`)
|
||||||
|
$headerLink.attr('title', newId)
|
||||||
|
}
|
||||||
|
|
||||||
export function deduplicatedHeaderId (view) {
|
export function deduplicatedHeaderId (view) {
|
||||||
|
// headers contained in the last change
|
||||||
const headers = view.find(':header.raw').removeClass('raw').toArray()
|
const headers = view.find(':header.raw').removeClass('raw').toArray()
|
||||||
for (let i = 0; i < headers.length; i++) {
|
if (headers.length === 0) {
|
||||||
const id = $(headers[i]).attr('id')
|
return
|
||||||
if (!id) continue
|
}
|
||||||
const duplicatedHeaders = view.find(`:header[id="${id}"]`).toArray()
|
if (window.linkifyHeaderStyle === 'gfm') {
|
||||||
for (let j = 0; j < duplicatedHeaders.length; j++) {
|
// consistent with GitHub, GitLab, Pandoc & co.
|
||||||
if (duplicatedHeaders[j] !== headers[i]) {
|
// all headers contained in the document, in order of appearance
|
||||||
const newId = id + j
|
const allHeaders = view.find(`:header`).toArray()
|
||||||
const $duplicatedHeader = $(duplicatedHeaders[j])
|
// list of finaly assigned header IDs
|
||||||
$duplicatedHeader.attr('id', newId)
|
const headerIds = new Set()
|
||||||
const $headerLink = $duplicatedHeader.find(`> a.anchor[href="#${id}"]`)
|
for (let j = 0; j < allHeaders.length; j++) {
|
||||||
$headerLink.attr('href', `#${newId}`)
|
const $header = $(allHeaders[j])
|
||||||
$headerLink.attr('title', newId)
|
const id = $header.attr('id')
|
||||||
|
const newId = createHeaderId(getHeaderContent($header), headerIds)
|
||||||
|
changeHeaderId($header, id, newId)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// the legacy way
|
||||||
|
for (let i = 0; i < headers.length; i++) {
|
||||||
|
const id = $(headers[i]).attr('id')
|
||||||
|
if (!id) continue
|
||||||
|
const duplicatedHeaders = view.find(`:header[id="${id}"]`).toArray()
|
||||||
|
for (let j = 0; j < duplicatedHeaders.length; j++) {
|
||||||
|
if (duplicatedHeaders[j] !== headers[i]) {
|
||||||
|
const newId = id + j
|
||||||
|
const $header = $(duplicatedHeaders[j])
|
||||||
|
changeHeaderId($header, id, newId)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3116,6 +3116,27 @@ function matchInContainer (text) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const textCompleteKeyMap = {
|
||||||
|
Up: function () {
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
Right: function () {
|
||||||
|
editor.doc.cm.execCommand('goCharRight')
|
||||||
|
},
|
||||||
|
Down: function () {
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
Left: function () {
|
||||||
|
editor.doc.cm.execCommand('goCharLeft')
|
||||||
|
},
|
||||||
|
Enter: function () {
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
Backspace: function () {
|
||||||
|
editor.doc.cm.execCommand('delCharBefore')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$(editor.getInputField())
|
$(editor.getInputField())
|
||||||
.textcomplete([
|
.textcomplete([
|
||||||
{ // emoji strategy
|
{ // emoji strategy
|
||||||
|
@ -3317,29 +3338,10 @@ $(editor.getInputField())
|
||||||
},
|
},
|
||||||
'textComplete:show': function (e) {
|
'textComplete:show': function (e) {
|
||||||
$(this).data('autocompleting', true)
|
$(this).data('autocompleting', true)
|
||||||
editor.setOption('extraKeys', {
|
editor.addKeyMap(textCompleteKeyMap)
|
||||||
Up: function () {
|
|
||||||
return false
|
|
||||||
},
|
|
||||||
Right: function () {
|
|
||||||
editor.doc.cm.execCommand('goCharRight')
|
|
||||||
},
|
|
||||||
Down: function () {
|
|
||||||
return false
|
|
||||||
},
|
|
||||||
Left: function () {
|
|
||||||
editor.doc.cm.execCommand('goCharLeft')
|
|
||||||
},
|
|
||||||
Enter: function () {
|
|
||||||
return false
|
|
||||||
},
|
|
||||||
Backspace: function () {
|
|
||||||
editor.doc.cm.execCommand('delCharBefore')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
'textComplete:hide': function (e) {
|
'textComplete:hide': function (e) {
|
||||||
$(this).data('autocompleting', false)
|
$(this).data('autocompleting', false)
|
||||||
editor.setOption('extraKeys', editorInstance.defaultExtraKeys)
|
editor.removeKeyMap(textCompleteKeyMap)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -8,4 +8,6 @@ window.allowedUploadMimeTypes = <%- JSON.stringify(allowedUploadMimeTypes) %>
|
||||||
|
|
||||||
window.defaultUseHardbreak = <%- defaultUseHardbreak %>
|
window.defaultUseHardbreak = <%- defaultUseHardbreak %>
|
||||||
|
|
||||||
|
window.linkifyHeaderStyle = '<%- linkifyHeaderStyle %>'
|
||||||
|
|
||||||
window.DROPBOX_APP_KEY = '<%- DROPBOX_APP_KEY %>'
|
window.DROPBOX_APP_KEY = '<%- DROPBOX_APP_KEY %>'
|
||||||
|
|
|
@ -166,19 +166,25 @@ export function initTableEditor (editor) {
|
||||||
'Alt-Shift-Ctrl-Down': () => { tableEditor.moveRow(1, opts) },
|
'Alt-Shift-Ctrl-Down': () => { tableEditor.moveRow(1, opts) },
|
||||||
'Alt-Shift-Cmd-Down': () => { tableEditor.moveRow(1, opts) }
|
'Alt-Shift-Cmd-Down': () => { tableEditor.moveRow(1, opts) }
|
||||||
})
|
})
|
||||||
|
let lastActive
|
||||||
// enable keymap if the cursor is in a table
|
// enable keymap if the cursor is in a table
|
||||||
function updateActiveState () {
|
function updateActiveState () {
|
||||||
const tableTools = $('.toolbar .table-tools')
|
const tableTools = $('.toolbar .table-tools')
|
||||||
const active = tableEditor.cursorIsInTable(opts)
|
const active = tableEditor.cursorIsInTable(opts)
|
||||||
|
// avoid to update if state not changed
|
||||||
|
if (lastActive === active) {
|
||||||
|
return
|
||||||
|
}
|
||||||
if (active) {
|
if (active) {
|
||||||
tableTools.show()
|
tableTools.show()
|
||||||
tableTools.parent().scrollLeft(tableTools.parent()[0].scrollWidth)
|
tableTools.parent().scrollLeft(tableTools.parent()[0].scrollWidth)
|
||||||
editor.setOption('extraKeys', keyMap)
|
editor.addKeyMap(keyMap)
|
||||||
} else {
|
} else {
|
||||||
tableTools.hide()
|
tableTools.hide()
|
||||||
editor.setOption('extraKeys', null)
|
editor.removeKeyMap(keyMap)
|
||||||
tableEditor.resetSmartCursor()
|
tableEditor.resetSmartCursor()
|
||||||
}
|
}
|
||||||
|
lastActive = active
|
||||||
}
|
}
|
||||||
// event subscriptions
|
// event subscriptions
|
||||||
editor.on('cursorActivity', () => {
|
editor.on('cursorActivity', () => {
|
||||||
|
|
|
@ -6449,6 +6449,14 @@ https-browserify@^1.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73"
|
resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73"
|
||||||
integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=
|
integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=
|
||||||
|
|
||||||
|
https-proxy-agent@^3.0.1:
|
||||||
|
version "3.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-3.0.1.tgz#b8c286433e87602311b01c8ea34413d856a4af81"
|
||||||
|
integrity sha512-+ML2Rbh6DAuee7d07tYGEKOEi2voWPUGan+ExdPbPW6Z3svq+JCqr0v8WmKPOkz1vOVykPCBSuobe7G8GJUtVg==
|
||||||
|
dependencies:
|
||||||
|
agent-base "^4.3.0"
|
||||||
|
debug "^3.1.0"
|
||||||
|
|
||||||
i18n@~0.8.3:
|
i18n@~0.8.3:
|
||||||
version "0.8.3"
|
version "0.8.3"
|
||||||
resolved "https://registry.yarnpkg.com/i18n/-/i18n-0.8.3.tgz#2d8cf1c24722602c2041d01ba6ae5eaa51388f0e"
|
resolved "https://registry.yarnpkg.com/i18n/-/i18n-0.8.3.tgz#2d8cf1c24722602c2041d01ba6ae5eaa51388f0e"
|
||||||
|
|
Loading…
Reference in New Issue