Skip to content

Commit

Permalink
feat(ghCompatibleHeaderId): generate header ids compatible with github
Browse files Browse the repository at this point in the history
style

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

Closes # 320, closes #321
  • Loading branch information
tivie committed Dec 30, 2016
1 parent 08846c4 commit db97a90
Show file tree
Hide file tree
Showing 13 changed files with 91 additions and 40 deletions.
2 changes: 2 additions & 0 deletions README.md
Expand Up @@ -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.
Expand Down
25 changes: 23 additions & 2 deletions dist/showdown.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion dist/showdown.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/showdown.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/showdown.min.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion performance.json
@@ -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}]}]}
64 changes: 32 additions & 32 deletions performance.log.md
Expand Up @@ -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/)
Expand Down
5 changes: 5 additions & 0 deletions src/options.js
Expand Up @@ -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',
Expand Down
3 changes: 2 additions & 1 deletion src/showdown.js
Expand Up @@ -21,7 +21,8 @@ var showdown = {},
tasklists: true,
disableForced4SpacesIndentedSublists: true,
simpleLineBreaks: true,
requireSpaceBeforeHeadingText: true
requireSpaceBeforeHeadingText: true,
ghCompatibleHeaderId: true
},
vanilla: getDefaultOpts(true),
allOn: allOptionsOn()
Expand Down
17 changes: 16 additions & 1 deletion src/subParsers/headers.js
Expand Up @@ -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
Expand Down Expand Up @@ -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]++);
Expand Down
2 changes: 2 additions & 0 deletions test/features/#320.github-compatible-generated-header-id.html
@@ -0,0 +1,2 @@
<h1 id="some-header">some header</h1>
<h1 id="some-header-with--chars">some header with ~!@#$&amp;*()=:/,;?+'.\ chars</h1>
3 changes: 3 additions & 0 deletions test/features/#320.github-compatible-generated-header-id.md
@@ -0,0 +1,3 @@
# some header

# some header with ~!@#$&*()=:/,;?+'.\ chars
2 changes: 2 additions & 0 deletions test/node/testsuite.features.js
Expand Up @@ -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();
}
Expand Down

0 comments on commit db97a90

Please sign in to comment.