diff --git a/lib/marked.js b/lib/marked.js index 462229d233..6f7ce06720 100644 --- a/lib/marked.js +++ b/lib/marked.js @@ -953,13 +953,13 @@ Renderer.prototype.html = function(html) { return html; }; -Renderer.prototype.heading = function(text, level, raw) { +Renderer.prototype.heading = function(text, level, raw, slugger) { if (this.options.headerIds) { return '' + text + '?@[\]^`{|}~]/g, '') + .replace(/\s/g, '-'); + + if (this.seen.hasOwnProperty(slug)) { + var originalSlug = slug; + do { + this.seen[originalSlug]++; + slug = originalSlug + '-' + this.seen[originalSlug]; + } while (this.seen.hasOwnProperty(slug)); + } + this.seen[slug] = 0; + + return slug; +}; + /** * Helpers */ @@ -1617,6 +1650,8 @@ marked.lexer = Lexer.lex; marked.InlineLexer = InlineLexer; marked.inlineLexer = InlineLexer.output; +marked.Slugger = Slugger; + marked.parse = marked; if (typeof module !== 'undefined' && typeof exports === 'object') { diff --git a/test/new/cm_blockquotes.html b/test/new/cm_blockquotes.html index f63e5e0067..b4d51b1f6b 100644 --- a/test/new/cm_blockquotes.html +++ b/test/new/cm_blockquotes.html @@ -11,7 +11,7 @@

Example 192

The spaces after the > characters can be omitted:

-

Foo

+

Bar

bar baz

@@ -21,7 +21,7 @@

Example 193

The > characters can be indented 1-3 spaces:

-

Foo

+

Baz

bar baz

@@ -30,7 +30,7 @@

Example 194

Four spaces gives us a code block:

-
> # Foo
+
> # Qux
 > bar
 > baz
@@ -39,7 +39,7 @@

Example 195

The Laziness clause allows us to omit the > before paragraph continuation text:

-

Foo

+

Quux

bar baz

diff --git a/test/new/cm_blockquotes.md b/test/new/cm_blockquotes.md index 95a317de44..6a80a6f32d 100644 --- a/test/new/cm_blockquotes.md +++ b/test/new/cm_blockquotes.md @@ -8,7 +8,7 @@ The spaces after the `>` characters can be omitted: -># Foo +># Bar >bar > baz @@ -16,7 +16,7 @@ The spaces after the `>` characters can be omitted: The `>` characters can be indented 1-3 spaces: - > # Foo + > # Baz > bar > baz @@ -24,7 +24,7 @@ The `>` characters can be indented 1-3 spaces: Four spaces gives us a code block: - > # Foo + > # Qux > bar > baz @@ -32,7 +32,7 @@ Four spaces gives us a code block: The Laziness clause allows us to omit the `>` before paragraph continuation text: -> # Foo +> # Quux > bar baz diff --git a/test/new/toplevel_paragraphs.html b/test/new/toplevel_paragraphs.html index d15bfccef0..7fd509470e 100644 --- a/test/new/toplevel_paragraphs.html +++ b/test/new/toplevel_paragraphs.html @@ -12,7 +12,7 @@

how are you

paragraph before head with equals

-

how are you

+

how are you again

paragraph before blockquote

text for blockquote

diff --git a/test/new/toplevel_paragraphs.md b/test/new/toplevel_paragraphs.md index de29be7557..f344fbe349 100644 --- a/test/new/toplevel_paragraphs.md +++ b/test/new/toplevel_paragraphs.md @@ -17,7 +17,7 @@ paragraph before head with hash # how are you paragraph before head with equals -how are you +how are you again =========== paragraph before blockquote diff --git a/test/original/markdown_documentation_basics.html b/test/original/markdown_documentation_basics.html index d5bdbb29a5..dede9d748a 100644 --- a/test/original/markdown_documentation_basics.html +++ b/test/original/markdown_documentation_basics.html @@ -1,4 +1,4 @@ -

Markdown: Basics

+

Markdown: Basics

-

Getting the Gist of Markdown's Formatting Syntax

+

Getting the Gist of Markdown's Formatting Syntax

This page offers a brief overview of what it's like to use Markdown. The syntax page provides complete, detailed documentation for @@ -24,7 +24,7 @@

Getting the Gist of Markdown's Formatting Syntax

Note: This document is itself written using Markdown; you can see the source for it by adding '.text' to the URL.

-

Paragraphs, Headers, Blockquotes

+

Paragraphs, Headers, Blockquotes

A paragraph is simply one or more consecutive lines of text, separated by one or more blank lines. (A blank line is any line that looks like a @@ -88,7 +88,7 @@

Paragraphs, Headers, Blockquotes

</blockquote>
-

Phrase Emphasis

+

Phrase Emphasis

Markdown uses asterisks and underscores to indicate spans of emphasis.

@@ -110,7 +110,7 @@

Phrase Emphasis

Or, if you prefer, <strong>use two underscores instead</strong>.</p> -

Lists

+

Lists

Unordered (bulleted) lists use asterisks, pluses, and hyphens (*, +, and -) as list markers. These three markers are @@ -181,7 +181,7 @@

Lists

</ul> -

Links

+

Markdown supports two styles for creating links: inline and reference. With both styles, you use square brackets to delimit the @@ -244,7 +244,7 @@

Links

<a href="http://www.nytimes.com/">The New York Times</a>.</p> -

Images

+

Images

Image syntax is very much like link syntax.

@@ -265,7 +265,7 @@

Images

<img src="/path/to/img.jpg" alt="alt text" title="Title" />
 
-

Code

+

Code

In a regular paragraph, you can create code span by wrapping text in backtick quotes. Any ampersands (&) and angle brackets (< or diff --git a/test/unit/marked-spec.js b/test/unit/marked-spec.js index 97789afe09..c365e8599c 100644 --- a/test/unit/marked-spec.js +++ b/test/unit/marked-spec.js @@ -2,8 +2,9 @@ var marked = require('../../lib/marked.js'); describe('Test heading ID functionality', function() { it('should add id attribute by default', function() { - var renderer = new marked.Renderer(marked.defaults); - var header = renderer.heading('test', 1, 'test'); + var renderer = new marked.Renderer(); + var slugger = new marked.Slugger(); + var header = renderer.heading('test', 1, 'test', slugger); expect(header).toBe('

test

\n'); }); @@ -14,6 +15,51 @@ describe('Test heading ID functionality', function() { }); }); +describe('Test slugger functionality', function() { + it('should use lowercase slug', function() { + var slugger = new marked.Slugger(); + expect(slugger.slug('Test')).toBe('test'); + }); + + it('should be unique to avoid collisions 1280', function() { + var slugger = new marked.Slugger(); + expect(slugger.slug('test')).toBe('test'); + expect(slugger.slug('test')).toBe('test-1'); + expect(slugger.slug('test')).toBe('test-2'); + }); + + it('should be unique when slug ends with number', function() { + var slugger = new marked.Slugger(); + expect(slugger.slug('test 1')).toBe('test-1'); + expect(slugger.slug('test')).toBe('test'); + expect(slugger.slug('test')).toBe('test-2'); + }); + + it('should be unique when slug ends with hyphen number', function() { + var slugger = new marked.Slugger(); + expect(slugger.slug('foo')).toBe('foo'); + expect(slugger.slug('foo')).toBe('foo-1'); + expect(slugger.slug('foo 1')).toBe('foo-1-1'); + expect(slugger.slug('foo-1')).toBe('foo-1-2'); + expect(slugger.slug('foo')).toBe('foo-2'); + }); + + it('should allow non-latin chars', function() { + var slugger = new marked.Slugger(); + expect(slugger.slug('привет')).toBe('привет'); + }); + + it('should remove ampersands 857', function() { + var slugger = new marked.Slugger(); + expect(slugger.slug('This & That Section')).toBe('this--that-section'); + }); + + it('should remove periods', function() { + var slugger = new marked.Slugger(); + expect(slugger.slug('file.txt')).toBe('filetxt'); + }); +}); + describe('Test paragraph token type', function () { it('should use the "paragraph" type on top level', function () { const md = 'A Paragraph.\n\n> A blockquote\n\n- list item\n';