-
Notifications
You must be signed in to change notification settings - Fork 22
Expand file tree
/
Copy pathanchor-markdown-header.js
More file actions
188 lines (160 loc) · 6.04 KB
/
anchor-markdown-header.js
File metadata and controls
188 lines (160 loc) · 6.04 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
'use strict';
var emojiRegex = require('emoji-regex');
var removeMd = require('remove-markdown');
// https://github.com/joyent/node/blob/192192a09e2d2e0d6bdd0934f602d2dbbf10ed06/tools/doc/html.js#L172-L183
function getNodejsId(text, repetition) {
text = text.replace(/[^a-z0-9]+/g, '_');
text = text.replace(/^_+|_+$/, '');
text = text.replace(/^([^a-z])/, '_$1');
// If no repetition, or if the repetition is 0 then ignore. Otherwise append '_' and the number.
// An example may be found here: http://nodejs.org/api/domain.html#domain_example_1
if (repetition) {
text += '_' + repetition;
}
return text;
}
function basicGithubId(text) {
return text.replace(/ /g,'-')
// escape codes
.replace(/%([abcdef]|\d){2,2}/ig, '')
// single chars that are removed
.replace(/[\/\\?!%:\[\]`.,()*"';{}+=<>~\$|#@&–—]/g,'')
// CJK punctuations that are removed
.replace(/[。?!,、;:“”【】()〔〕[]﹃﹄“ ”‘’﹁﹂—…-~《》〈〉「」]/g, '')
// latin-1 supplement chars that are removed
.replace(/[¡¢£¤¥¦§¨©«¬®¯°±²³´¶·¸¹»¼½¾¿]/g, '')
;
}
function getGithubId(text, repetition) {
text = basicGithubId(text);
// If no repetition, or if the repetition is 0 then ignore. Otherwise append '-' and the number.
if (repetition) {
text += '-' + repetition;
}
// Strip emojis
text = text.replace(emojiRegex(), '')
// Strip embedded markdown formatting
text = removeMd(text)
return text;
}
function getBitbucketId(text, repetition) {
text = 'markdown-header-' + basicGithubId(text);
// BitBucket condenses consecutive hyphens (GitHub doesn't)
text = text.replace(/--+/g, '-');
// If no repetition, or if the repetition is 0 then ignore. Otherwise append '_' and the number.
// https://groups.google.com/d/msg/bitbucket-users/XnEWbbzs5wU/Fat0UdIecZkJ
if (repetition) {
text += '_' + repetition;
}
return text;
}
function basicGhostId(text) {
return text.replace(/ /g,'')
// escape codes are not removed
// single chars that are removed
.replace(/[\/?:\[\]`.,()*"';{}\-+=<>!@#%^&\\\|]/g,'')
// $ replaced with d
.replace(/\$/g, 'd')
// ~ replaced with t
.replace(/~/g, 't')
;
}
function getGhostId(text, repetition) {
text = basicGhostId(text);
// If no repetition, or if the repetition is 0 then ignore. Otherwise append '-' and the number.
if (repetition) {
text += '-' + repetition;
}
return text;
}
// see: https://github.com/gitlabhq/gitlabhq/blob/master/doc/user/markdown.md#header-ids-and-links
function getGitlabId(text, repetition) {
text = text
.replace(/<(.*)>(.*)<\/\1>/g,"$2") // html tags
.replace(/!\[.*\]\(.*\)/g,'') // image tags
.replace(/\[(.*)\]\(.*\)/,"$1") // url
.replace(/\s+/g, '-') // All spaces are converted to hyphens
.replace(/[\/?!:\[\]`.,()*"';{}+=<>~\$|#@]/g,'') // All non-word text (e.g., punctuation, HTML) is removed
.replace(/[。?!,、;:“”【】()〔〕[]﹃﹄“ ”‘’﹁﹂—…-~《》〈〉「」]/g, '') // remove CJK punctuations
.replace(/[¹²³]/g, '') // remove snall subset of latin-1 supplement chars
.replace(/[-]+/g,'-') // duplicated hyphen
.replace(/^-/,'') // ltrim hyphen
.replace(/-$/,''); // rtrim hyphen
// If no repetition, or if the repetition is 0 then ignore. Otherwise append '-' and the number.
if (repetition) {
text += '-' + repetition;
}
return text;
}
/**
* Generates an anchor for the given header and mode.
*
* @name anchorMarkdownHeader
* @function
* @param header {String} The header to be anchored.
* @param mode {String} The anchor mode (github.com|nodejs.org|bitbucket.org|ghost.org|gitlab.com).
* @param repetition {Number} The nth occurrence of this header text, starting with 0. Not required for the 0th instance.
* @param href {String} The href to be used in the anchor.
* @return {String} The header anchor that is compatible with the given mode.
*/
module.exports = function anchorMarkdownHeader(header, mode, repetition, href) {
mode = mode || 'github.com';
var replace;
var customEncodeURI = encodeURI;
var customCasing = asciiOnlyToLowerCase;
// Extended Markdown heading IDs: `Header text {#id}` or kramdown's `{:#id}` at end of line.
// See https://www.markdownlang.com/extended/heading-ids.html
var idMatch = header.match(/^(.*?)[ \t]*\{:?#([^\s}]+)\}[ \t]*$/);
if (idMatch) {
header = idMatch[1];
href = idMatch[2];
}
switch(mode) {
case 'github.com':
replace = getGithubId;
customEncodeURI = function(uri) {
var newURI = encodeURI(uri);
// encodeURI replaces the zero width joiner character
// (used to generate emoji sequences, e.g.Female Construction Worker 👷🏼♀️)
// github doesn't URL encode them, so we replace them after url encoding to preserve the zwj character.
return newURI.replace(/%E2%80%8D/g, '\u200D');
};
customCasing = function(input) {
// GitHub prefers to lowercase all characters, not just ASCII ones. Previously this was not the case.
return input.toLowerCase();
}
break;
case 'bitbucket.org':
replace = getBitbucketId;
break;
case 'gitlab.com':
replace = getGitlabId;
break;
case 'nodejs.org':
replace = getNodejsId;
break;
case 'ghost.org':
replace = getGhostId;
break;
case 'custom':
if(href === undefined){
throw new Error('Missing href');
}
break;
default:
throw new Error('Unknown mode: ' + mode);
}
function asciiOnlyToLowerCase(input) {
var result = '';
for (var i = 0; i < input.length; ++i) {
if (input[i] >= 'A' && input[i] <= 'Z') {
result += input[i].toLowerCase();
} else {
result += input[i];
}
}
return result;
}
href = href || replace(customCasing(header.trim()), repetition);
return '[' + header + '](#' + customEncodeURI(href) + ')';
};