Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Add anchorAlias option
- Fixed heading id collision
- Add anchorAlias option
- Add test code
- Update README.md
  • Loading branch information
mdluo committed Feb 10, 2018
1 parent 26bc183 commit 4123b5f
Show file tree
Hide file tree
Showing 4 changed files with 137 additions and 2 deletions.
2 changes: 2 additions & 0 deletions README.md
Expand Up @@ -29,6 +29,7 @@ marked:
smartLists: true
smartypants: true
modifyAnchors: ''
anchorAlias: false
autolink: true
```

Expand All @@ -40,6 +41,7 @@ marked:
- **smartLists** - Use smarter list behavior than the original markdown.
- **smartypants** - Use "smart" typograhic punctuation for things like quotes and dashes.
- **modifyAnchors** - Use for transform anchorIds. if 1 to lowerCase and if 2 to upperCase.
- **anchorAlias** - For link in headings, use href as anchor ID alias. E.g. `## [Anchor](#alias)` will become `<h2><a href="#alias">Anchor</a></h2>`
- **autolink** - Enable autolink for URLs. E.g. `https://hexo.io` will become `<a href="https://hexo.io">https://hexo.io</a>`.

## Extras
Expand Down
1 change: 1 addition & 0 deletions index.js
Expand Up @@ -14,6 +14,7 @@ hexo.config.marked = assign({
smartLists: true,
smartypants: true,
modifyAnchors: '',
anchorAlias: false,
autolink: true
}, hexo.config.marked);

Expand Down
36 changes: 34 additions & 2 deletions lib/renderer.js
Expand Up @@ -31,20 +31,52 @@ Renderer.prototype.listitem = function(text) {
return result;
};


// Add id attribute to headings
Renderer.prototype.heading = function(text, level) {
var transformOption = this.options.modifyAnchors;
var id = anchorId(stripHTML(text), transformOption);
var aliasOption = this.options.anchorAlias;

var hrefRegex = /(<a\s+(?:[^>]*?\s+)?href=(["']))(.*?)\2/i;
var matches = text.match(hrefRegex);
var isLink = matches && matches[3] && matches[3].startsWith('#');
var innerText = stripHTML(text);
var idText = innerText;

// Use href attribute as id
if (aliasOption && isLink) {
idText = matches[3];
if (!idText) {
idText = innerText;
}
}

// For `[]()`
if (!innerText && !idText) {
return '<h' + level + '></h' + level + '>';
}

var id = anchorId(idText, transformOption);
var headingId = this._headingId;

// Add a number after id if repeated
// Add output id back to the map for collision resistance
if (headingId[id]) {
id += '-' + headingId[id]++;
headingId[id] = 1;
} else {
headingId[id] = 1;
}

// if `text` is a <a> element, replace the href with the generated id;
// else, `text` is plain string, just keep it.
var innerHTML = text;
if (isLink) {
innerHTML = text.replace(hrefRegex, '$1#' + id + '$2');
}

// add headerlink
return '<h' + level + ' id="' + id + '"><a href="#' + id + '" class="headerlink" title="' + stripHTML(text) + '"></a>' + text + '</h' + level + '>';
return '<h' + level + ' id="' + id + '"><a href="#' + id + '" class="headerlink" title="' + innerText + '"></a>' + innerHTML + '</h' + level + '>';
};

function anchorId(str, transformOption) {
Expand Down
100 changes: 100 additions & 0 deletions test/index.js
Expand Up @@ -52,6 +52,33 @@ describe('Marked renderer', function() {
].join(''));
});

it('should render headings with id collision', function() {
var body = [
'## example',
'',
'# example',
'',
'## example-1',
'',
'### example',
'',
'#### example-2',
'',
'## example-1'
].join('\n');

var result = r({text: body});

result.should.eql([
'<h2 id="example"><a href="#example" class="headerlink" title="example"></a>example</h2>',
'<h1 id="example-1"><a href="#example-1" class="headerlink" title="example"></a>example</h1>',
'<h2 id="example-1-1"><a href="#example-1-1" class="headerlink" title="example-1"></a>example-1</h2>',
'<h3 id="example-2"><a href="#example-2" class="headerlink" title="example"></a>example</h3>',
'<h4 id="example-2-1"><a href="#example-2-1" class="headerlink" title="example-2"></a>example-2</h4>',
'<h2 id="example-1-2"><a href="#example-1-2" class="headerlink" title="example-1"></a>example-1</h2>'
].join(''));
});

it('should handle chinese headers properly', function() {
var body = '# 中文';
var result = r({text: body});
Expand Down Expand Up @@ -204,4 +231,77 @@ describe('Marked renderer', function() {
].join('\n'));
});
});

describe('anchorAlias option tests', function() {
var body = [
'# example',
'',
'## example',
'',
'### [example-1]',
'',
'#### [example](http://example.com)',
'',
'##### example-2',
'',
'# [example-1](#example)',
'',
'## [example](#example-1)',
'',
'### [example](#example-anchor)',
'',
'#### [中文](#chinese)',
'',
'##### []()',
'',
'[example-1]: http://example.com'
].join('\n');

var renderer = require('../lib/renderer');

var ctx = {
config: {
marked: {
anchorAlias: false
}
}
};

it('should not modify anchors with default options', function() {
var r = renderer.bind(ctx);
var result = r({text: body});

result.should.eql([
'<h1 id="example"><a href="#example" class="headerlink" title="example"></a>example</h1>',
'<h2 id="example-1"><a href="#example-1" class="headerlink" title="example"></a>example</h2>',
'<h3 id="example-1-1"><a href="#example-1-1" class="headerlink" title="example-1"></a><a href="http://example.com">example-1</a></h3>',
'<h4 id="example-2"><a href="#example-2" class="headerlink" title="example"></a><a href="http://example.com">example</a></h4>',
'<h5 id="example-2-1"><a href="#example-2-1" class="headerlink" title="example-2"></a>example-2</h5>',
'<h1 id="example-1-2"><a href="#example-1-2" class="headerlink" title="example-1"></a><a href="#example-1-2">example-1</a></h1>',
'<h2 id="example-3"><a href="#example-3" class="headerlink" title="example"></a><a href="#example-3">example</a></h2>',
'<h3 id="example-4"><a href="#example-4" class="headerlink" title="example"></a><a href="#example-4">example</a></h3>',
'<h4 id="中文"><a href="#中文" class="headerlink" title="中文"></a><a href="#中文">中文</a></h4>',
'<h5></h5>'
].join(''));
});

it('should us link as id with anchorAlias option set to true', function() {
ctx.config.marked.anchorAlias = 1;
var r = renderer.bind(ctx);
var result = r({text: body});

result.should.eql([
'<h1 id="example"><a href="#example" class="headerlink" title="example"></a>example</h1>',
'<h2 id="example-1"><a href="#example-1" class="headerlink" title="example"></a>example</h2>',
'<h3 id="example-1-1"><a href="#example-1-1" class="headerlink" title="example-1"></a><a href="http://example.com">example-1</a></h3>',
'<h4 id="example-2"><a href="#example-2" class="headerlink" title="example"></a><a href="http://example.com">example</a></h4>',
'<h5 id="example-2-1"><a href="#example-2-1" class="headerlink" title="example-2"></a>example-2</h5>',
'<h1 id="example-3"><a href="#example-3" class="headerlink" title="example-1"></a><a href="#example-3">example-1</a></h1>',
'<h2 id="example-1-2"><a href="#example-1-2" class="headerlink" title="example"></a><a href="#example-1-2">example</a></h2>',
'<h3 id="example-anchor"><a href="#example-anchor" class="headerlink" title="example"></a><a href="#example-anchor">example</a></h3>',
'<h4 id="chinese"><a href="#chinese" class="headerlink" title="中文"></a><a href="#chinese">中文</a></h4>',
'<h5></h5>'
].join(''));
});
});
});

0 comments on commit 4123b5f

Please sign in to comment.