diff options
author | Sheogorath | 2019-12-03 14:40:00 +0100 |
---|---|---|
committer | GitHub | 2019-12-03 14:40:00 +0100 |
commit | 33150b79c7a4c3ba456afdadb31c709b3be18980 (patch) | |
tree | e8f4be1a385a70336830e1851ab86d7d9fb686a5 | |
parent | 2ddec15af8e77fcad85e62ee8c0b6eccefc52405 (diff) | |
parent | ad1a2fb19c842ea5e4cb46a24989ce95b2041902 (diff) |
Merge pull request #218 from hoijui/linkifyHeaderStyle
Linkify header style
-rw-r--r-- | config.json.example | 6 | ||||
-rw-r--r-- | lib/config/default.js | 13 | ||||
-rw-r--r-- | lib/config/environment.js | 3 | ||||
-rw-r--r-- | public/js/extra.js | 85 | ||||
-rw-r--r-- | public/js/lib/common/constant.ejs | 2 |
5 files changed, 86 insertions, 23 deletions
diff --git a/config.json.example b/config.json.example index e3bd54b4..0366c3b2 100644 --- a/config.json.example +++ b/config.json.example @@ -3,7 +3,8 @@ "db": { "dialect": "sqlite", "storage": ":memory:" - } + }, + "linkifyHeaderStyle": "gfm" }, "development": { "loglevel": "debug", @@ -13,7 +14,8 @@ "db": { "dialect": "sqlite", "storage": "./db.codimd.sqlite" - } + }, + "linkifyHeaderStyle": "gfm" }, "production": { "domain": "localhost", diff --git a/lib/config/default.js b/lib/config/default.js index f5cceac6..a52a8a4f 100644 --- a/lib/config/default.js +++ b/lib/config/default.js @@ -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' } diff --git a/lib/config/environment.js b/lib/config/environment.js index 716f8b75..944a2fb2 100644 --- a/lib/config/environment.js +++ b/lib/config/environment.js @@ -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 } diff --git a/public/js/extra.js b/public/js/extra.js index d381576f..6cda6171 100644 --- a/public/js/extra.js +++ b/public/js/extra.js @@ -825,6 +825,36 @@ 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}`) @@ -832,13 +862,7 @@ const linkifyAnchors = (level, containingElement) => { 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) @@ -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) + } } } } diff --git a/public/js/lib/common/constant.ejs b/public/js/lib/common/constant.ejs index bbcb8c7a..114a9077 100644 --- a/public/js/lib/common/constant.ejs +++ b/public/js/lib/common/constant.ejs @@ -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 %>' |