Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add slugger option, remove headerIds / headerPrefix #1354

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 70 additions & 19 deletions docs/USING_ADVANCED.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,25 +39,26 @@ console.log(myMarked('I am using __markdown__.'));

<h2 id="options">Options</h2>

|Member |Type |Default |Since |Notes |
|:-----------|:---------|:--------|:--------|:-------------|
|baseUrl |`string` |`null` |0.3.9 |A prefix url for any relative link. |
|breaks |`boolean` |`false` |v0.2.7 |If true, add `<br>` on a single line break (copies GitHub). Requires `gfm` be `true`.|
|gfm |`boolean` |`true` |v0.2.1 |If true, use approved [GitHub Flavored Markdown (GFM) specification](https://github.github.com/gfm/).|
|headerIds |`boolean` |`true` |v0.4.0 |If true, include an `id` attribute when emitting headings (h1, h2, h3, etc).|
|headerPrefix|`string` |`''` |v0.3.0 |A string to prefix the `id` attribute when emitting headings (h1, h2, h3, etc).|
|highlight |`function`|`null` |v0.3.0 |A function to highlight code blocks, see <a href="#highlight">Asynchronous highlighting</a>.|
|langPrefix |`string` |`'language-'`|v0.3.0|A string to prefix the className in a `<code>` block. Useful for syntax highlighting.|
|mangle |`boolean` |`true` |v0.3.4 |If true, autolinked email address is escaped with HTML character references.|
|pedantic |`boolean` |`false` |v0.2.1 |If true, conform to the original `markdown.pl` as much as possible. Don't fix original markdown bugs or behavior. Turns off and overrides `gfm`.|
|renderer |`object` |`new Renderer()`|v0.3.0|An object containing functions to render tokens to HTML. See [extensibility](USING_PRO.md) for more details.|
|sanitize |`boolean` |`false` |v0.2.1 |If true, sanitize the HTML passed into `markdownString` with the `sanitizer` function.|
|sanitizer |`function`|`null` |v0.3.4 |A function to sanitize the HTML passed into `markdownString`.|
|silent |`boolean` |`false` |v0.2.7 |If true, the parser does not throw any exception.|
|smartLists |`boolean` |`false` |v0.2.8 |If true, use smarter list behavior than those found in `markdown.pl`.|
|smartypants |`boolean` |`false` |v0.2.9 |If true, use "smart" typographic punctuation for things like quotes and dashes.|
|tables |`boolean` |`true` |v0.2.7 |If true and `gfm` is true, use [GFM Tables extension](https://github.github.com/gfm/#tables-extension-).|
|xhtml |`boolean` |`false` |v0.3.2 |If true, emit self-closing HTML tags for void elements (&lt;br/&gt;, &lt;img/&gt;, etc.) with a "/" as required by XHTML.|
|Member |Type |Default |Since |Removed |Notes |
|:-----------|:---------|:--------|:--------|:-------|:-------------|
|baseUrl |`string` |`null` |0.3.9 |nope |A prefix url for any relative link. |
|breaks |`boolean` |`false` |v0.2.7 |nope |If true, add `<br>` on a single line break (copies GitHub). Requires `gfm` be `true`.|
|gfm |`boolean` |`true` |v0.2.1 |nope |If true, use approved [GitHub Flavored Markdown (GFM) specification](https://github.github.com/gfm/).|
|~~headerIds~~|`boolean` |`true` |v0.4.0 |v0.6.0 |If true, include an `id` attribute when emitting headings (h1, h2, h3, etc).|
|~~headerPrefix~~|`string` |`''` |v0.3.0 |v0.6.0 |A string to prefix the `id` attribute when emitting headings (h1, h2, h3, etc).|
|highlight |`function`|`null` |v0.3.0 |nope |A function to highlight code blocks, see <a href="#highlight">Asynchronous highlighting</a>.|
|langPrefix |`string` |`'language-'`|v0.3.0|nope |A string to prefix the className in a `<code>` block. Useful for syntax highlighting.|
|mangle |`boolean` |`true` |v0.3.4 |nope |If true, autolinked email address is escaped with HTML character references.|
|pedantic |`boolean` |`false` |v0.2.1 |nope |If true, conform to the original `markdown.pl` as much as possible. Don't fix original markdown bugs or behavior. Turns off and overrides `gfm`.|
|renderer |`object` |`new Renderer()`|v0.3.0|nope |An object containing functions to render tokens to HTML. See [extensibility](USING_PRO.md) for more details.|
|sanitize |`boolean` |`false` |v0.2.1 |nope |If true, sanitize the HTML passed into `markdownString` with the `sanitizer` function.|
|sanitizer |`function`|`null` |v0.3.4 |nope |A function to sanitize the HTML passed into `markdownString`.|
|silent |`boolean` |`false` |v0.2.7 |nope |If true, the parser does not throw any exception.|
|slugger |`function`|`null` |v0.6.0 |nope |A function used when generating header ids.|
|smartLists |`boolean` |`false` |v0.2.8 |nope |If true, use smarter list behavior than those found in `markdown.pl`.|
|smartypants |`boolean` |`false` |v0.2.9 |nope |If true, use "smart" typographic punctuation for things like quotes and dashes.|
|tables |`boolean` |`true` |v0.2.7 |nope |If true and `gfm` is true, use [GFM Tables extension](https://github.github.com/gfm/#tables-extension-).|
|xhtml |`boolean` |`false` |v0.3.2 |nope |If true, emit self-closing HTML tags for void elements (&lt;br/&gt;, &lt;img/&gt;, etc.) with a "/" as required by XHTML.|

<h2 id="highlight">Asynchronous highlighting</h2>

Expand All @@ -76,3 +77,53 @@ console.log(myMarked(markdownString));
```

In both examples, `code` is a `string` representing the section of code to pass to the highlighter. In this example, `lang` is a `string` informing the highlighter what programming lnaguage to use for the `code` and `callback` is the `function` the asynchronous highlighter will call once complete.

<h2 id="slugger">Slugger</h2>

The default behavior of marked is to follow the CommonMark specification. However, in some cases, it may be desirable to generate IDs for [ATX Headers](https://spec.commonmark.org/0.28/#atx-heading) for the purpose of permalinks or maybe a Table of Contents.

In this case, marked offers a `slugger` option that can take an implementation of [github-slugger](https://www.npmjs.com/package/github-slugger) or your own class with a `slug` method.

Below is a slugger implementation that generates a lowercase header ID (formerly the `headerIds` option). It also avoids duplicate header IDs by incrementing a count of seen slugs.

```js
function Slugger () {
this.seen = {};
}

Slugger.prototype.slug = function (raw) {
var slug = raw.toLowerCase().replace(/[^\w]+/g, '-');
var count = this.seen.hasOwnProperty(slug) ? this.seen[slug] + 1 : 0;
this.seen[slug] = count;

if (count > 0) {
slug = slug + '-' + count;
}

return slug;
};

var options = { slugger: new Slugger() };
var markdownString = `# My Heading
This is a paragraph.`;
var html = marked(markdownString, options);
```

Sometimes, it is desirable to mix static HTML with dynamic markdown (which of course renders to HTML). Generating dynamic IDs with a slugger might cause collisions with other ID properties on the page. In this case, you can add a prefix to the slugger.

Below is an example that extends the `github-slugger` implementation to add a prefix to each ID (formerly the `headerPrefix` option).

```js
var marked = require('marked');
var GithubSlugger = require('github-slugger');
var ghslugger = new GithubSlugger();

function PrefixSlugger (prefix) {
this.headerPrefix = prefix;
}
PrefixSlugger.prototype.slug = function (value) {
return this.headerPrefix + ghslugger.slug(value);
};

var options = { slugger: new PrefixSlugger('comment-') };
```
1 change: 1 addition & 0 deletions docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ <h1>Marked.js Documentation</h1>
<ul>
<li><a href="#/USING_ADVANCED.md#options">Options</a></li>
<li><a href="#/USING_ADVANCED.md#highlight">Highlighting</a></li>
<li><a href="#/USING_ADVANCED.md#slugger">Slugger</a></li>
</ul>
</li>
<li>
Expand Down
8 changes: 3 additions & 5 deletions lib/marked.js
Original file line number Diff line number Diff line change
Expand Up @@ -947,12 +947,11 @@ Renderer.prototype.html = function(html) {
};

Renderer.prototype.heading = function(text, level, raw) {
if (this.options.headerIds) {
if (this.options.slugger) {
return '<h'
+ level
+ ' id="'
+ this.options.headerPrefix
+ raw.toLowerCase().replace(/[^\w]+/g, '-')
+ this.options.slugger.slug(raw)
+ '">'
+ text
+ '</h'
Expand Down Expand Up @@ -1567,15 +1566,14 @@ marked.getDefaults = function () {
baseUrl: null,
breaks: false,
gfm: true,
headerIds: true,
headerPrefix: '',
highlight: null,
langPrefix: 'language-',
mangle: true,
pedantic: false,
renderer: new Renderer(),
sanitize: false,
sanitizer: null,
slugger: null,
silent: false,
smartLists: false,
smartypants: false,
Expand Down
14 changes: 0 additions & 14 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -393,20 +393,6 @@ function fix() {
.replace(/&__QUOT__;/g, '"')
.replace(/&__APOS__;/g, '\'');

// add heading id's
html = html.replace(/<(h[1-6])>([^<]+)<\/\1>/g, function(s, h, text) {
var id = text
.replace(/&#39;/g, '\'')
.replace(/&quot;/g, '"')
.replace(/&gt;/g, '>')
.replace(/&lt;/g, '<')
.replace(/&amp;/g, '&');

id = id.toLowerCase().replace(/[^\w]+/g, '-');

return '<' + h + ' id="' + id + '">' + text + '</' + h + '>';
});

fs.writeFileSync(file, html);
});

Expand Down
38 changes: 19 additions & 19 deletions test/new/cm_autolinks.html
Original file line number Diff line number Diff line change
@@ -1,91 +1,91 @@
<p>Here are some valid autolinks:</p>

<h3 id="example-565">Example 565</h3>
<h3>Example 565</h3>

<p><a href="http://foo.bar.baz">http://foo.bar.baz</a></p>

<h3 id="example-566">Example 566</h3>
<h3>Example 566</h3>

<p><a href="http://foo.bar.baz/test?q=hello&amp;id=22&amp;boolean">http://foo.bar.baz/test?q=hello&amp;id=22&amp;boolean</a></p>

<h3 id="example-567">Example 567</h3>
<h3>Example 567</h3>

<p><a href="irc://foo.bar:2233/baz">irc://foo.bar:2233/baz</a></p>

<h3 id="example-568">Example 568</h3>
<h3>Example 568</h3>

<p>Uppercase is also fine:</p>

<p><a href="MAILTO:FOO@BAR.BAZ">MAILTO:FOO@BAR.BAZ</a></p>

<p>Note that many strings that count as absolute URIs for purposes of this spec are not valid URIs, because their schemes are not registered or because of other problems with their syntax:</p>

<h3 id="example-569">Example 569</h3>
<h3>Example 569</h3>

<p><a href="a+b+c:d">a+b+c:d</a></p>

<h3 id="example-570">Example 570</h3>
<h3>Example 570</h3>

<p><a href="made-up-scheme://foo,bar">made-up-scheme://foo,bar</a></p>

<h3 id="example-571">Example 571</h3>
<h3>Example 571</h3>

<p><a href="http://../">http://../</a></p>

<h3 id="example-572">Example 572</h3>
<h3>Example 572</h3>

<p><a href="localhost:5001/foo">localhost:5001/foo</a></p>

<h3 id="example-573">Example 573</h3>
<h3>Example 573</h3>

<p>Spaces are not allowed in autolinks:</p>

<p>&lt;http://foo.bar/baz bim&gt;</p>

<h3 id="example-574">Example 574</h3>
<h3>Example 574</h3>

<p>Backslash-escapes do not work inside autolinks:</p>

<p><a href="http://example.com/%5C%5B%5C">http://example.com/\[\</a></p>

<p>Examples of email autolinks:</p>

<h3 id="example-575">Example 575</h3>
<h3>Example 575</h3>

<p><a href="mailto:foo@bar.example.com">foo@bar.example.com</a></p>

<h3 id="example-576">Example 576</h3>
<h3>Example 576</h3>

<p><a href="mailto:foo+special@Bar.baz-bar0.com">foo+special@Bar.baz-bar0.com</a></p>

<h3 id="example-577">Example 577</h3>
<h3>Example 577</h3>

<p>Backslash-escapes do not work inside email autolinks:</p>

<p>&lt;foo+@bar.example.com&gt;</p>

<p>These are not autolinks:</p>

<h3 id="example-578">Example 578</h3>
<h3>Example 578</h3>

<p>&lt;&gt;</p>

<h3 id="example-579">Example 579</h3>
<h3>Example 579</h3>

<p>&lt; http://foo.bar &gt;</p>

<h3 id="example-580">Example 580</h3>
<h3>Example 580</h3>

<p>&lt;m:abc&gt;</p>

<h3 id="example-581">Example 581</h3>
<h3>Example 581</h3>

<p>&lt;foo.bar.baz&gt;</p>

<h3 id="example-582">Example 582</h3>
<h3>Example 582</h3>

<p>http://example.com</p>

<h3 id="example-583">Example 583</h3>
<h3>Example 583</h3>

<p>foo@bar.example.com</p>
Loading