feat(ghCompatibleHeaderId): generate header ids compatible with github

style

Github replaces spaces with dashes and removes a bunch of characters
from generated header ids ][&~$!@#*()=:/,;?+'.\
This feature implements this.

Closes # 320, closes #321
pull/336/head
Estevao Soares dos Santos 2016-12-30 19:01:44 +00:00
parent 08846c4519
commit db97a90d5b
13 changed files with 91 additions and 40 deletions

View File

@ -189,6 +189,8 @@ var defaultOptions = showdown.getDefaultOptions();
* **noHeaderId**: (boolean) [default false] Disable the automatic generation of header ids. Setting to true overrides **prefixHeaderId**
* **ghCompatibleHeaderId**: (boolean) [default false] Generate header ids compatible with github style (spaces are replaced with dashes, ][&~$!@#*()=:/,;?+'.\ chars are removed
* **prefixHeaderId**: (string/boolean) [default false] Add a prefix to the generated header ids. Passing a string will prefix that string to the header id. Setting to `true` will add a generic 'section' prefix.
* **parseImgDimensions**: (boolean) [default false] Enable support for setting image dimensions from within markdown syntax.

25
dist/showdown.js vendored
View File

@ -23,6 +23,11 @@ function getDefaultOpts(simple) {
describe: 'Specify a prefix to generated header ids',
type: 'string'
},
ghCompatibleHeaderId: {
defaultValue: false,
describe: 'Generate header ids compatible with github style (spaces are replaced with dashes, &~$!@#*()=:/,;?+\'. chars are removed)',
type: 'string'
},
headerLevelStart: {
defaultValue: false,
describe: 'The header blocks level start',
@ -146,7 +151,8 @@ var showdown = {},
tasklists: true,
disableForced4SpacesIndentedSublists: true,
simpleLineBreaks: true,
requireSpaceBeforeHeadingText: true
requireSpaceBeforeHeadingText: true,
ghCompatibleHeaderId: true
},
vanilla: getDefaultOpts(true),
allOn: allOptionsOn()
@ -1786,6 +1792,7 @@ showdown.subParser('headers', function (text, options, globals) {
var prefixHeader = options.prefixHeaderId,
headerLevelStart = (isNaN(parseInt(options.headerLevelStart))) ? 1 : parseInt(options.headerLevelStart),
ghHeaderId = options.ghCompatibleHeaderId,
// Set text-style headers:
// Header 1
@ -1833,7 +1840,21 @@ showdown.subParser('headers', function (text, options, globals) {
});
function headerId(m) {
var title, escapedId = m.replace(/[^\w]/g, '').toLowerCase();
var title, escapedId;
if (ghHeaderId) {
escapedId = m
.replace(/ /g, '-')
//replace previously escaped chars (&, ~ and $)
.replace(/&/g, '')
.replace(/~T/g, '')
.replace(/~D/g, '')
//replace rest of the chars (&~$ are repeated as they might have been escaped)
.replace(/[&~$!@#*()=:/,;?+'.\\]/g, '')
.toLowerCase();
} else {
escapedId = m.replace(/[^\w]/g, '').toLowerCase();
}
if (globals.hashLinkCounts[escapedId]) {
title = escapedId + '-' + (globals.hashLinkCounts[escapedId]++);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
{"1.5.5":[{"suiteName":"Basic","cycles":100,"tests":[{"name":"Simple \"Hello World\"","time":0.31472779000000006,"maxTime":5.7157219999999995,"minTime":0.1630309999999966},{"name":"readme.md","time":8.35187898,"maxTime":18.016863999999998,"minTime":7.207408999999984}]},{"suiteName":"subParsers","cycles":1000,"tests":[{"name":"hashHTMLBlocks","time":0.673032811000001,"maxTime":1.1442289999999957,"minTime":0.6216859999999542},{"name":"anchors","time":0.17780483999999888,"maxTime":0.5279669999999896,"minTime":0.15700300000003153},{"name":"autoLinks","time":0.014361767000001236,"maxTime":0.19256300000006377,"minTime":0.012655999999878986},{"name":"blockGamut","time":7.3270934659999964,"maxTime":16.04994700000134,"minTime":6.464278000000377},{"name":"blockQuotes","time":0.06438723300000856,"maxTime":0.2558470000003581,"minTime":0.05816000000049826},{"name":"codeBlocks","time":0.07155547099998148,"maxTime":1.5896259999990434,"minTime":0.05936599999949976},{"name":"codeSpans","time":0.1839475929999935,"maxTime":1.4350329999997484,"minTime":0.16453699999874516},{"name":"detab","time":0.024342816999995194,"maxTime":0.19678200000089419,"minTime":0.02199799999834795},{"name":"encodeAmpsAndAngles","time":0.016193075999983195,"maxTime":0.35378599999967264,"minTime":0.013559999999415595},{"name":"encodeBackslashEscapes","time":0.01439162099999885,"maxTime":0.23565699999926437,"minTime":0.012354999998933636},{"name":"encodeCode","time":0.16650568799998655,"maxTime":0.48909199999980046,"minTime":0.1485659999998461},{"name":"encodeEmailAddress","time":2.2962369480000233,"maxTime":3.975118999998813,"minTime":2.079321000001073},{"name":"escapeSpecialCharsWithinTagAttributes","time":0.06015714999998454,"maxTime":0.41375499999958265,"minTime":0.05303699999967648},{"name":"githubCodeBlocks","time":0.07636622499999976,"maxTime":4.3967090000005555,"minTime":0.05514699999912409},{"name":"hashBlock","time":0.023663276999977823,"maxTime":5.066611999998713,"minTime":0.010847999999896274},{"name":"hashElement","time":0.0007292739999957121,"maxTime":0.23927300000104879,"minTime":0},{"name":"hashHTMLSpans","time":0.031071996999991826,"maxTime":3.5767329999998765,"minTime":0.010245999999824562},{"name":"hashPreCodeTags","time":0.018975442000006296,"maxTime":0.3323899999995774,"minTime":0.014765999998417101},{"name":"headers","time":0.45554695500000525,"maxTime":2.677201000000423,"minTime":0.3911530000004859},{"name":"images","time":0.04111330700001417,"maxTime":1.6565260000006674,"minTime":0.0322439999999915},{"name":"italicsAndBold","time":0.038311039999998686,"maxTime":0.2980360000001383,"minTime":0.03465499999947497},{"name":"lists","time":5.024616153999989,"maxTime":9.631475000000137,"minTime":4.572998999999982},{"name":"outdent","time":0.0506727579999897,"maxTime":0.5855249999985972,"minTime":0.04369599999699858},{"name":"paragraphs","time":1.3435436269999992,"maxTime":2.651585999999952,"minTime":1.1951570000019274},{"name":"spanGamut","time":0.6120240349999804,"maxTime":1.057440000000497,"minTime":0.5553889999973762},{"name":"strikethrough","time":0.00045323800000551275,"maxTime":0.22752000000036787,"minTime":0},{"name":"stripBlankLines","time":0.03007630300002711,"maxTime":0.29351499999756925,"minTime":0.02681999999913387},{"name":"stripLinkDefinitions","time":0.06855551200000627,"maxTime":0.4496159999980591,"minTime":0.06026999999812688},{"name":"tables","time":0.0008534279999839782,"maxTime":0.2046169999994163,"minTime":0},{"name":"unescapeSpecialChars","time":0.0035457099999912315,"maxTime":0.1681540000026871,"minTime":0.0027119999977003317}]}],"1.5.4":[{"suiteName":"Basic","cycles":100,"tests":[{"name":"Simple \"Hello World\"","time":0.30990324000000025,"maxTime":5.549075,"minTime":0.14916800000000308},{"name":"readme.md","time":8.072704459999997,"maxTime":17.975579000000003,"minTime":7.220064999999977}]}]}
{"1.5.5":[{"suiteName":"Basic","cycles":100,"tests":[{"name":"Simple \"Hello World\"","time":0.6435890300000008,"maxTime":13.067478000000001,"minTime":0.17839999999999634},{"name":"readme.md","time":8.181142640000001,"maxTime":26.315163000000013,"minTime":7.018763000000035}]},{"suiteName":"subParsers","cycles":1000,"tests":[{"name":"hashHTMLBlocks","time":0.6653926609999921,"maxTime":1.1002320000000054,"minTime":0.6201799999998912},{"name":"anchors","time":0.18655217499999913,"maxTime":0.5605129999999008,"minTime":0.16122199999995246},{"name":"autoLinks","time":0.015408060000002478,"maxTime":0.19828899999993155,"minTime":0.013559999999870342},{"name":"blockGamut","time":7.104302655000017,"maxTime":15.671752000000197,"minTime":6.344039000000521},{"name":"blockQuotes","time":0.06466147199998705,"maxTime":0.22782099999858474,"minTime":0.05815999999867927},{"name":"codeBlocks","time":0.07110766699999113,"maxTime":1.0583440000009432,"minTime":0.06057099999998172},{"name":"codeSpans","time":0.18858266200001708,"maxTime":1.0360440000004019,"minTime":0.1681540000008681},{"name":"detab","time":0.02511971000000085,"maxTime":0.25132699999994657,"minTime":0.02199800000016694},{"name":"encodeAmpsAndAngles","time":0.01657520100001966,"maxTime":0.2675989999988815,"minTime":0.01356099999975413},{"name":"encodeBackslashEscapes","time":0.013978774000006524,"maxTime":0.2570529999993596,"minTime":0.012354999998933636},{"name":"encodeCode","time":0.17534642000000894,"maxTime":0.6436849999990955,"minTime":0.1494700000002922},{"name":"encodeEmailAddress","time":2.291462927999999,"maxTime":3.8542770000003657,"minTime":2.071786999998949},{"name":"escapeSpecialCharsWithinTagAttributes","time":0.059004508000005444,"maxTime":0.43816399999923306,"minTime":0.05303699999967648},{"name":"githubCodeBlocks","time":0.08072707700000319,"maxTime":4.627243000000817,"minTime":0.05635199999960605},{"name":"hashBlock","time":0.0287458620000034,"maxTime":5.0681189999995695,"minTime":0.011149999998451676},{"name":"hashElement","time":0.0006084250000003522,"maxTime":0.1970839999994496,"minTime":0},{"name":"hashHTMLSpans","time":0.023783199999992576,"maxTime":3.8319769999998243,"minTime":0.010245000001305016},{"name":"hashPreCodeTags","time":0.017276158000002397,"maxTime":0.41375499999958265,"minTime":0.014463999999861699},{"name":"headers","time":0.4617156479999994,"maxTime":2.67147500000101,"minTime":0.3932629999999335},{"name":"images","time":0.039165076999997835,"maxTime":0.7331869999998162,"minTime":0.032545999998546904},{"name":"italicsAndBold","time":0.037845750000002225,"maxTime":0.3085829999999987,"minTime":0.03435399999943911},{"name":"lists","time":4.972232302000022,"maxTime":11.410649999999805,"minTime":4.442514999998821},{"name":"outdent","time":0.049343790000006264,"maxTime":0.536404999998922,"minTime":0.04369600000063656},{"name":"paragraphs","time":1.3606319900000234,"maxTime":2.980360000001383,"minTime":1.2026909999985946},{"name":"spanGamut","time":0.629801320999999,"maxTime":1.2726049999982934,"minTime":0.5505679999987478},{"name":"strikethrough","time":0.00038121299999693294,"maxTime":0.2079330000015034,"minTime":0},{"name":"stripBlankLines","time":0.030241460999983247,"maxTime":0.27091400000063004,"minTime":0.02681999999913387},{"name":"stripLinkDefinitions","time":0.06758093699999518,"maxTime":0.3387189999994007,"minTime":0.06087200000183657},{"name":"tables","time":0.0005864269999910903,"maxTime":0.18593300000065938,"minTime":0},{"name":"unescapeSpecialChars","time":0.003310937999998714,"maxTime":0.21094599999923958,"minTime":0.0027119999977003317}]}],"1.5.4":[{"suiteName":"Basic","cycles":100,"tests":[{"name":"Simple \"Hello World\"","time":0.30990324000000025,"maxTime":5.549075,"minTime":0.14916800000000308},{"name":"readme.md","time":8.072704459999997,"maxTime":17.975579000000003,"minTime":7.220064999999977}]}]}

View File

@ -6,42 +6,42 @@
### Test Suite: Basic (100 cycles)
| test | avgTime | max | min |
|:-----|--------:|----:|----:|
|Simple "Hello World"|0.315|5.716|0.163|
|readme.md|8.352|18.017|7.207|
|Simple "Hello World"|0.644|13.067|0.178|
|readme.md|8.181|26.315|7.019|
### Test Suite: subParsers (1000 cycles)
| test | avgTime | max | min |
|:-----|--------:|----:|----:|
|hashHTMLBlocks|0.673|1.144|0.622|
|anchors|0.178|0.528|0.157|
|autoLinks|0.014|0.193|0.013|
|blockGamut|7.327|16.050|6.464|
|blockQuotes|0.064|0.256|0.058|
|codeBlocks|0.072|1.590|0.059|
|codeSpans|0.184|1.435|0.165|
|detab|0.024|0.197|0.022|
|encodeAmpsAndAngles|0.016|0.354|0.014|
|encodeBackslashEscapes|0.014|0.236|0.012|
|encodeCode|0.167|0.489|0.149|
|encodeEmailAddress|2.296|3.975|2.079|
|escapeSpecialCharsWithinTagAttributes|0.060|0.414|0.053|
|githubCodeBlocks|0.076|4.397|0.055|
|hashBlock|0.024|5.067|0.011|
|hashElement|0.001|0.239|0.000|
|hashHTMLSpans|0.031|3.577|0.010|
|hashPreCodeTags|0.019|0.332|0.015|
|headers|0.456|2.677|0.391|
|images|0.041|1.657|0.032|
|italicsAndBold|0.038|0.298|0.035|
|lists|5.025|9.631|4.573|
|outdent|0.051|0.586|0.044|
|paragraphs|1.344|2.652|1.195|
|spanGamut|0.612|1.057|0.555|
|strikethrough|0.000|0.228|0.000|
|stripBlankLines|0.030|0.294|0.027|
|stripLinkDefinitions|0.069|0.450|0.060|
|tables|0.001|0.205|0.000|
|unescapeSpecialChars|0.004|0.168|0.003|
|hashHTMLBlocks|0.665|1.100|0.620|
|anchors|0.187|0.561|0.161|
|autoLinks|0.015|0.198|0.014|
|blockGamut|7.104|15.672|6.344|
|blockQuotes|0.065|0.228|0.058|
|codeBlocks|0.071|1.058|0.061|
|codeSpans|0.189|1.036|0.168|
|detab|0.025|0.251|0.022|
|encodeAmpsAndAngles|0.017|0.268|0.014|
|encodeBackslashEscapes|0.014|0.257|0.012|
|encodeCode|0.175|0.644|0.149|
|encodeEmailAddress|2.291|3.854|2.072|
|escapeSpecialCharsWithinTagAttributes|0.059|0.438|0.053|
|githubCodeBlocks|0.081|4.627|0.056|
|hashBlock|0.029|5.068|0.011|
|hashElement|0.001|0.197|0.000|
|hashHTMLSpans|0.024|3.832|0.010|
|hashPreCodeTags|0.017|0.414|0.014|
|headers|0.462|2.671|0.393|
|images|0.039|0.733|0.033|
|italicsAndBold|0.038|0.309|0.034|
|lists|4.972|11.411|4.443|
|outdent|0.049|0.536|0.044|
|paragraphs|1.361|2.980|1.203|
|spanGamut|0.630|1.273|0.551|
|strikethrough|0.000|0.208|0.000|
|stripBlankLines|0.030|0.271|0.027|
|stripLinkDefinitions|0.068|0.339|0.061|
|tables|0.001|0.186|0.000|
|unescapeSpecialChars|0.003|0.211|0.003|
## [version 1.5.4](https://github.com/showdownjs/showdown/tree/)

View File

@ -21,6 +21,11 @@ function getDefaultOpts(simple) {
describe: 'Specify a prefix to generated header ids',
type: 'string'
},
ghCompatibleHeaderId: {
defaultValue: false,
describe: 'Generate header ids compatible with github style (spaces are replaced with dashes, &~$!@#*()=:/,;?+\'. chars are removed)',
type: 'string'
},
headerLevelStart: {
defaultValue: false,
describe: 'The header blocks level start',

View File

@ -21,7 +21,8 @@ var showdown = {},
tasklists: true,
disableForced4SpacesIndentedSublists: true,
simpleLineBreaks: true,
requireSpaceBeforeHeadingText: true
requireSpaceBeforeHeadingText: true,
ghCompatibleHeaderId: true
},
vanilla: getDefaultOpts(true),
allOn: allOptionsOn()

View File

@ -5,6 +5,7 @@ showdown.subParser('headers', function (text, options, globals) {
var prefixHeader = options.prefixHeaderId,
headerLevelStart = (isNaN(parseInt(options.headerLevelStart))) ? 1 : parseInt(options.headerLevelStart),
ghHeaderId = options.ghCompatibleHeaderId,
// Set text-style headers:
// Header 1
@ -52,7 +53,21 @@ showdown.subParser('headers', function (text, options, globals) {
});
function headerId(m) {
var title, escapedId = m.replace(/[^\w]/g, '').toLowerCase();
var title, escapedId;
if (ghHeaderId) {
escapedId = m
.replace(/ /g, '-')
//replace previously escaped chars (&, ~ and $)
.replace(/&/g, '')
.replace(/~T/g, '')
.replace(/~D/g, '')
//replace rest of the chars (&~$ are repeated as they might have been escaped)
.replace(/[&~$!@#*()=:/,;?+'.\\]/g, '')
.toLowerCase();
} else {
escapedId = m.replace(/[^\w]/g, '').toLowerCase();
}
if (globals.hashLinkCounts[escapedId]) {
title = escapedId + '-' + (globals.hashLinkCounts[escapedId]++);

View File

@ -0,0 +1,2 @@
<h1 id="some-header">some header</h1>
<h1 id="some-header-with--chars">some header with ~!@#$&amp;*()=:/,;?+'.\ chars</h1>

View File

@ -0,0 +1,3 @@
# some header
# some header with ~!@#$&*()=:/,;?+'.\ chars

View File

@ -43,6 +43,8 @@ describe('makeHtml() features testsuite', function () {
converter = new showdown.Converter({simplifiedAutoLink: true, excludeTrailingPunctuationFromURLs: true});
} else if (testsuite[i].name === 'requireSpaceBeforeHeadingText') {
converter = new showdown.Converter({requireSpaceBeforeHeadingText: true});
} else if (testsuite[i].name === '#320.github-compatible-generated-header-id') {
converter = new showdown.Converter({ghCompatibleHeaderId: true});
} else {
converter = new showdown.Converter();
}