diff --git a/lib/marked.js b/lib/marked.js
index e34ac17151..6f7ce06720 100644
--- a/lib/marked.js
+++ b/lib/marked.js
@@ -1289,29 +1289,31 @@ Parser.prototype.tok = function() {
* Slugger generates header id
*/
-function Slugger () {
- this.seen = {};
-}
+function Slugger () {
+ this.seen = {};
+}
/**
* Convert string to unique id
*/
-Slugger.prototype.slug = function (value) {
- var slug = value
+Slugger.prototype.slug = function (value) {
+ var slug = value
.toLowerCase()
.trim()
.replace(/[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,./:;<=>?@[\]^`{|}~]/g, '')
.replace(/\s/g, '-');
-
- var count = this.seen.hasOwnProperty(slug) ? this.seen[slug] + 1 : 0;
- this.seen[slug] = count;
- if (count > 0) {
- slug = slug + '-' + count;
+ if (this.seen.hasOwnProperty(slug)) {
+ var originalSlug = slug;
+ do {
+ this.seen[originalSlug]++;
+ slug = originalSlug + '-' + this.seen[originalSlug];
+ } while (this.seen.hasOwnProperty(slug));
}
-
- return slug;
+ this.seen[slug] = 0;
+
+ return slug;
};
/**
@@ -1648,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/unit/marked-spec.js b/test/unit/marked-spec.js
index d476ceb57e..4f71a5ae06 100644
--- a/test/unit/marked-spec.js
+++ b/test/unit/marked-spec.js
@@ -2,29 +2,54 @@ var marked = require('../../lib/marked.js');
describe('Test heading ID functionality', function() {
it('should add id attribute by default', function() {
- var html = marked('# test');
- expect(html).toBe('
test
\n');
+ var renderer = new marked.Renderer();
+ var slugger = new marked.Slugger();
+ var header = renderer.heading('test', 1, 'test', slugger);
+ expect(header).toBe('test
\n');
});
- it('should add unique id for repeating heading 1280', function() {
- var html = marked('# test\n# test\n# test');
- expect(html).toBe('test
\ntest
\ntest
\n');
+ it('should NOT add id attribute when options set false', function() {
+ var renderer = new marked.Renderer({ headerIds: false });
+ var header = renderer.heading('test', 1, 'test');
+ expect(header).toBe('test
\n');
});
+});
- it('should add id with non-latin chars', function() {
- var html = marked('# привет');
- expect(html).toBe('привет
\n');
+describe('Test slugger functionality', function() {
+ it('should use lowercase slug', function() {
+ var slugger = new marked.Slugger();
+ expect(slugger.slug('Test')).toBe('test');
});
- it('should add id without ampersands 857', function() {
- var html = marked('# This & That Section');
- expect(html).toBe('This & That Section
\n');
+ 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 NOT add id attribute when options set false', function() {
- var options = { headerIds: false };
- var html = marked('# test', options);
- expect(html).toBe('test
\n');
+ it('should be unique to avoid collisions 1401', 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');
});
});