Skip to content
This repository has been archived by the owner on Jan 26, 2024. It is now read-only.

Commit

Permalink
Merge pull request hackmdio#218 from hoijui/linkifyHeaderStyle
Browse files Browse the repository at this point in the history
Linkify header style
  • Loading branch information
SISheogorath authored Dec 3, 2019
2 parents 2ddec15 + ad1a2fb commit 33150b7
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 23 deletions.
6 changes: 4 additions & 2 deletions config.json.example
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
"db": {
"dialect": "sqlite",
"storage": ":memory:"
}
},
"linkifyHeaderStyle": "gfm"
},
"development": {
"loglevel": "debug",
Expand All @@ -13,7 +14,8 @@
"db": {
"dialect": "sqlite",
"storage": "./db.codimd.sqlite"
}
},
"linkifyHeaderStyle": "gfm"
},
"production": {
"domain": "localhost",
Expand Down
13 changes: 13 additions & 0 deletions lib/config/default.js
Original file line number Diff line number Diff line change
Expand Up @@ -158,5 +158,18 @@ module.exports = {
allowGravatar: true,
allowPDFExport: true,
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'
}
3 changes: 2 additions & 1 deletion lib/config/environment.js
Original file line number Diff line number Diff line change
Expand Up @@ -134,5 +134,6 @@ module.exports = {
allowEmailRegister: toBooleanConfig(process.env.CMD_ALLOW_EMAIL_REGISTER),
allowGravatar: toBooleanConfig(process.env.CMD_ALLOW_GRAVATAR),
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
}
85 changes: 66 additions & 19 deletions public/js/extra.js
Original file line number Diff line number Diff line change
Expand Up @@ -825,20 +825,44 @@ const anchorForId = id => {
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 headers = containingElement.getElementsByTagName(`h${level}`)

for (let i = 0, l = headers.length; i < l; i++) {
let header = headers[i]
if (header.getElementsByClassName('anchor').length === 0) {
if (typeof header.id === 'undefined' || header.id === '') {
// to escape characters not allow in css and humanize
let id = slugifyWithUTF8(getHeaderContent(header))
// to make compatible with GitHub, GitLab, Pandoc and many more
if (window.linkifyHeaderStyle !== 'keep-case') {
id = id.toLowerCase()
}
header.id = id
header.id = createHeaderId(getHeaderContent(header))
}
if (!(typeof header.id === 'undefined' || header.id === '')) {
header.insertBefore(anchorForId(header.id), header.firstChild)
Expand All @@ -864,20 +888,43 @@ function getHeaderContent (header) {
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) {
// headers contained in the last change
const headers = view.find(':header.raw').removeClass('raw').toArray()
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 $duplicatedHeader = $(duplicatedHeaders[j])
$duplicatedHeader.attr('id', newId)
const $headerLink = $duplicatedHeader.find(`> a.anchor[href="#${id}"]`)
$headerLink.attr('href', `#${newId}`)
$headerLink.attr('title', newId)
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++) {
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)
}
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion public/js/lib/common/constant.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ window.version = '<%- version %>'

window.allowedUploadMimeTypes = <%- JSON.stringify(allowedUploadMimeTypes) %>

window.linkifyHeaderStyle = <%- JSON.stringify(linkifyHeaderStyle) %>
window.linkifyHeaderStyle = '<%- linkifyHeaderStyle %>'

window.DROPBOX_APP_KEY = '<%- DROPBOX_APP_KEY %>'

0 comments on commit 33150b7

Please sign in to comment.