mirror of https://github.com/status-im/codimd.git
Allow to generate lower case header references through the conf… (#1310)
Allow to generate lower case header references through the config
This commit is contained in:
commit
65ecb6d2ec
|
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -162,5 +162,19 @@ module.exports = {
|
||||||
allowEmailRegister: true,
|
allowEmailRegister: true,
|
||||||
allowGravatar: true,
|
allowGravatar: true,
|
||||||
allowPDFExport: true,
|
allowPDFExport: true,
|
||||||
openID: false
|
openID: false,
|
||||||
|
// 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'
|
||||||
}
|
}
|
||||||
|
|
|
@ -135,5 +135,6 @@ module.exports = {
|
||||||
allowEmailRegister: toBooleanConfig(process.env.CMD_ALLOW_EMAIL_REGISTER),
|
allowEmailRegister: toBooleanConfig(process.env.CMD_ALLOW_EMAIL_REGISTER),
|
||||||
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),
|
||||||
|
linkifyHeaderStyle: process.env.CMD_LINKIFY_HEADER_STYLE
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,7 +99,8 @@ statusRouter.get('/config', function (req, res) {
|
||||||
version: config.fullversion,
|
version: config.fullversion,
|
||||||
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,
|
||||||
|
linkifyHeaderStyle: config.linkifyHeaderStyle
|
||||||
}
|
}
|
||||||
res.set({
|
res.set({
|
||||||
'Cache-Control': 'private', // only cache by client
|
'Cache-Control': 'private', // only cache by client
|
||||||
|
|
|
@ -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,8 +922,33 @@ 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()
|
||||||
|
if (headers.length === 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (window.linkifyHeaderStyle === 'gfm') {
|
||||||
|
// consistent with GitHub, GitLab, Pandoc & co.
|
||||||
|
// all headers contained in the document, in order of appearance
|
||||||
|
const allHeaders = view.find(`:header`).toArray()
|
||||||
|
// list of finaly assigned header IDs
|
||||||
|
const headerIds = new Set()
|
||||||
|
for (let j = 0; j < allHeaders.length; j++) {
|
||||||
|
const $header = $(allHeaders[j])
|
||||||
|
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++) {
|
for (let i = 0; i < headers.length; i++) {
|
||||||
const id = $(headers[i]).attr('id')
|
const id = $(headers[i]).attr('id')
|
||||||
if (!id) continue
|
if (!id) continue
|
||||||
|
@ -903,11 +956,9 @@ export function deduplicatedHeaderId (view) {
|
||||||
for (let j = 0; j < duplicatedHeaders.length; j++) {
|
for (let j = 0; j < duplicatedHeaders.length; j++) {
|
||||||
if (duplicatedHeaders[j] !== headers[i]) {
|
if (duplicatedHeaders[j] !== headers[i]) {
|
||||||
const newId = id + j
|
const newId = id + j
|
||||||
const $duplicatedHeader = $(duplicatedHeaders[j])
|
const $header = $(duplicatedHeaders[j])
|
||||||
$duplicatedHeader.attr('id', newId)
|
changeHeaderId($header, id, newId)
|
||||||
const $headerLink = $duplicatedHeader.find(`> a.anchor[href="#${id}"]`)
|
}
|
||||||
$headerLink.attr('href', `#${newId}`)
|
|
||||||
$headerLink.attr('title', newId)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,4 +6,6 @@ window.plantumlServer = '<%- plantumlServer %>'
|
||||||
|
|
||||||
window.allowedUploadMimeTypes = <%- JSON.stringify(allowedUploadMimeTypes) %>
|
window.allowedUploadMimeTypes = <%- JSON.stringify(allowedUploadMimeTypes) %>
|
||||||
|
|
||||||
|
window.linkifyHeaderStyle = '<%- linkifyHeaderStyle %>'
|
||||||
|
|
||||||
window.DROPBOX_APP_KEY = '<%- DROPBOX_APP_KEY %>'
|
window.DROPBOX_APP_KEY = '<%- DROPBOX_APP_KEY %>'
|
||||||
|
|
Loading…
Reference in New Issue