@peaceroad/markdown-it-footnote-here 0.2.1 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -1
- package/index.js +211 -81
- package/package.json +6 -1
package/README.md
CHANGED
|
@@ -28,6 +28,34 @@ HTML:
|
|
|
28
28
|
|
|
29
29
|
Notice. When multiple instances of the same footnote number appear in the main content, the default behavior is that the backlink from the footnote will refer to the first instance.
|
|
30
30
|
|
|
31
|
+
## Endnotes
|
|
32
|
+
|
|
33
|
+
When a footnote label starts with the endnote prefix (default: `en-`), it is collected at the end of the document and rendered as endnotes. The reference/backlink label for endnotes is prefixed by `endnotesLabelPrefix` (default: `E`), so endnotes appear as `[E1]`, `[E2]`, ...
|
|
34
|
+
|
|
35
|
+
Markdown:
|
|
36
|
+
|
|
37
|
+
```md
|
|
38
|
+
A paragraph.[^en-1]
|
|
39
|
+
|
|
40
|
+
[^en-1]: A endnote.
|
|
41
|
+
|
|
42
|
+
A paragraph.
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
HTML:
|
|
46
|
+
|
|
47
|
+
```html
|
|
48
|
+
<p>A paragraph.<a href="#en1" id="en-ref1" class="en-noteref" role="doc-noteref">[E1]</a></p>
|
|
49
|
+
<p>A paragraph.</p>
|
|
50
|
+
<section aria-label="Notes" id="endnotes" role="doc-endnotes">
|
|
51
|
+
<ol>
|
|
52
|
+
<li id="en1">
|
|
53
|
+
<p><a href="#en-ref1" class="en-backlink" role="doc-backlink">[E1]</a> A endnote.</p>
|
|
54
|
+
</li>
|
|
55
|
+
</ol>
|
|
56
|
+
</section>
|
|
57
|
+
```
|
|
58
|
+
|
|
31
59
|
## Use
|
|
32
60
|
|
|
33
61
|
```js
|
|
@@ -53,7 +81,13 @@ npm install @peaceroad/markdown-it-footnote-here
|
|
|
53
81
|
- afterBacklinkSuffixArabicNumerals (boolean): If true, backlink suffix uses numbers (1, 2, ...) instead of letters (a, b, ...).
|
|
54
82
|
- afterBacklinkdAriaLabelPrefix (string): Prefix for aria-label of backlink (default: 'Back to reference ').
|
|
55
83
|
- labelBra (string): Bracket to use before footnote number (default: '[').
|
|
56
|
-
- labelKet (string): Bracket to use after footnote number (default: ']').
|
|
84
|
+
- labelKet (string): Bracket to use after footnote number (default: ']').
|
|
57
85
|
- labelSupTag (boolean): If true, wraps footnote reference in `<sup>` tag.
|
|
58
86
|
- backLabelBra (string): Bracket to use before backlink number (default: '[').
|
|
59
87
|
- backLabelKet (string): Bracket to use after backlink number (default: ']').
|
|
88
|
+
- endnotesPrefix (string): Prefix that marks a footnote as an endnote (default: `'en-'`).
|
|
89
|
+
- endnotesLabelPrefix (string): Label prefix for endnote refs/backlinks (default: `'E'`, e.g., `[E1]`).
|
|
90
|
+
- endnotesSectionId (string): `id` attribute for the endnotes section wrapper; omitted when empty (default: `'endnotes'`).
|
|
91
|
+
- endnotesSectionClass (string): `class` attribute for the endnotes section wrapper; omitted when empty (default: `''`).
|
|
92
|
+
- endnotesSectionAriaLabel (string): Used as `aria-label` when `endnotesUseHeading` is false. When `endnotesUseHeading` is true, this value becomes the heading text (default: `'Notes'`).
|
|
93
|
+
- endnotesUseHeading (boolean): If true, render `<h2>{endnotesSectionAriaLabel}</h2>` and omit `aria-label`. If false (default), omit the heading and set `aria-label` when provided.
|
package/index.js
CHANGED
|
@@ -1,78 +1,130 @@
|
|
|
1
|
-
const render_footnote_anchor_name = (tokens, idx,
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
let prefix = ''
|
|
5
|
-
if (typeof env.docId === 'string') {
|
|
6
|
-
prefix = '-' + env.docId + '-'
|
|
7
|
-
}
|
|
1
|
+
const render_footnote_anchor_name = (tokens, idx, _opt, env) => {
|
|
2
|
+
const n = tokens[idx].meta.id + 1
|
|
3
|
+
const prefix = typeof env.docId === 'string' ? `-${env.docId}-` : ''
|
|
8
4
|
return prefix + n
|
|
9
5
|
}
|
|
10
6
|
|
|
7
|
+
const isEndnoteLabel = (label, opt) => {
|
|
8
|
+
if (!opt.endnotesPrefix) return false
|
|
9
|
+
return label.startsWith(opt.endnotesPrefix)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const ensureNotesEnv = (env, key) => {
|
|
13
|
+
if (!env[key]) {
|
|
14
|
+
env[key] = { length: 0, refs: {}, positions: [] }
|
|
15
|
+
}
|
|
16
|
+
return env[key]
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const ENDNOTE_DOM_PREFIX = 'en'
|
|
20
|
+
|
|
21
|
+
const selectNoteEnv = (label, env, preferEndnote) => {
|
|
22
|
+
const footRefs = env.footnotes && env.footnotes.refs
|
|
23
|
+
const endRefs = env.endnotes && env.endnotes.refs
|
|
24
|
+
if (!footRefs && !endRefs) return null
|
|
25
|
+
const key = ':' + label
|
|
26
|
+
|
|
27
|
+
if (preferEndnote && endRefs) {
|
|
28
|
+
const endId = endRefs[key]
|
|
29
|
+
if (endId !== undefined) {
|
|
30
|
+
return { env: env.endnotes, id: endId, isEndnote: true }
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
if (footRefs) {
|
|
34
|
+
const footId = footRefs[key]
|
|
35
|
+
if (footId !== undefined) {
|
|
36
|
+
return { env: env.footnotes, id: footId, isEndnote: false }
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if (!preferEndnote && endRefs) {
|
|
40
|
+
const endId = endRefs[key]
|
|
41
|
+
if (endId !== undefined) {
|
|
42
|
+
return { env: env.endnotes, id: endId, isEndnote: true }
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return null
|
|
46
|
+
}
|
|
47
|
+
|
|
11
48
|
const render_footnote_ref = (tokens, idx, opt, env) => {
|
|
12
49
|
const token = tokens[idx]
|
|
13
50
|
const id = token.meta.id
|
|
14
51
|
const n = id + 1
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
52
|
+
const isEndnote = token.meta.isEndnote
|
|
53
|
+
const notes = isEndnote ? env.endnotes : env.footnotes
|
|
54
|
+
const noteDomPrefix = isEndnote ? ENDNOTE_DOM_PREFIX : 'fn'
|
|
55
|
+
const displayPrefix = isEndnote ? opt.endnotesLabelPrefix : ''
|
|
56
|
+
const totalCounts = notes.totalCounts ? notes.totalCounts[id] || 0 : 0
|
|
21
57
|
let suffix = ''
|
|
22
|
-
let label = `${opt.labelBra}${n}${opt.labelKet}`
|
|
23
|
-
if (
|
|
58
|
+
let label = `${opt.labelBra}${displayPrefix}${n}${opt.labelKet}`
|
|
59
|
+
if (totalCounts > 1) {
|
|
60
|
+
const refCount = notes._refCount || (notes._refCount = [])
|
|
61
|
+
let refIdx = (refCount[id] = (refCount[id] || 0) + 1)
|
|
62
|
+
if (!opt.afterBacklinkSuffixArabicNumerals) {
|
|
63
|
+
refIdx = String.fromCharCode(96 + refIdx)
|
|
64
|
+
}
|
|
24
65
|
suffix = '-' + refIdx
|
|
25
66
|
if (opt.beforeSameBacklink) {
|
|
26
|
-
label = `${opt.labelBra}${n}${suffix}${opt.labelKet}`
|
|
67
|
+
label = `${opt.labelBra}${displayPrefix}${n}${suffix}${opt.labelKet}`
|
|
27
68
|
}
|
|
28
69
|
}
|
|
29
|
-
|
|
30
|
-
|
|
70
|
+
const href = `${noteDomPrefix}${n}`
|
|
71
|
+
let refCont = `<a href="#${href}" id="${noteDomPrefix}-ref${n}${suffix}" class="${noteDomPrefix}-noteref" role="doc-noteref">${label}</a>`
|
|
72
|
+
if (opt.labelSupTag) refCont = `<sup class="${noteDomPrefix}-noteref-wrapper">${refCont}</sup>`
|
|
31
73
|
return refCont
|
|
32
74
|
}
|
|
33
75
|
|
|
34
76
|
const render_footnote_open = (tokens, idx, opt, env, slf) => {
|
|
35
77
|
const id = slf.rules.footnote_anchor_name(tokens, idx, opt, env, slf)
|
|
78
|
+
const isEndnote = tokens[idx].meta.isEndnote
|
|
79
|
+
if (isEndnote) return `<li id="${ENDNOTE_DOM_PREFIX}${id}">\n`
|
|
36
80
|
return `<aside id="fn${id}" class="fn" role="doc-footnote">\n`
|
|
37
81
|
}
|
|
38
82
|
|
|
39
|
-
const render_footnote_close = () => {
|
|
83
|
+
const render_footnote_close = (tokens, idx) => {
|
|
84
|
+
const isEndnote = tokens[idx].meta.isEndnote
|
|
85
|
+
if (isEndnote) return `</li>\n`
|
|
40
86
|
return `</aside>\n`
|
|
41
87
|
}
|
|
42
88
|
|
|
43
89
|
const render_footnote_anchor = (tokens, idx, opt, env) => {
|
|
44
90
|
const idNum = tokens[idx].meta.id
|
|
45
91
|
const n = idNum + 1
|
|
46
|
-
const
|
|
47
|
-
const
|
|
48
|
-
|
|
92
|
+
const isEndnote = tokens[idx].meta.isEndnote
|
|
93
|
+
const notes = isEndnote ? env.endnotes : env.footnotes
|
|
94
|
+
const totalCounts = notes.totalCounts
|
|
95
|
+
const count = totalCounts ? totalCounts[idNum] || 0 : 0
|
|
96
|
+
const noteDomPrefix = isEndnote ? ENDNOTE_DOM_PREFIX : 'fn'
|
|
97
|
+
const displayPrefix = isEndnote ? opt.endnotesLabelPrefix : ''
|
|
98
|
+
|
|
99
|
+
if (opt.beforeSameBacklink && count > 1) {
|
|
49
100
|
let links = ''
|
|
50
|
-
for (let i = 1; i <=
|
|
101
|
+
for (let i = 1; i <= count; i++) {
|
|
51
102
|
const suffix = '-' + String.fromCharCode(96 + i); // a, b, c ...
|
|
52
|
-
links += `<a href="
|
|
103
|
+
links += `<a href="#${noteDomPrefix}-ref${n}${suffix}" class="${noteDomPrefix}-backlink" role="doc-backlink">${opt.backLabelBra}${displayPrefix}${n}${suffix}${opt.backLabelKet}</a>`
|
|
53
104
|
}
|
|
54
105
|
return links + ' '
|
|
55
106
|
}
|
|
56
107
|
|
|
57
108
|
if (opt.afterBacklink) {
|
|
58
|
-
return `<span class="
|
|
109
|
+
return `<span class="${noteDomPrefix}-label">${opt.backLabelBra}${displayPrefix}${n}${opt.backLabelKet}</span> `
|
|
59
110
|
}
|
|
60
111
|
|
|
61
|
-
if (
|
|
62
|
-
return `<a href="
|
|
112
|
+
if (count > 1) {
|
|
113
|
+
return `<a href="#${noteDomPrefix}-ref${n}-a" class="${noteDomPrefix}-backlink" role="doc-backlink">${opt.backLabelBra}${displayPrefix}${n}${opt.backLabelKet}</a> `
|
|
63
114
|
}
|
|
64
115
|
|
|
65
|
-
return `<a href="
|
|
116
|
+
return `<a href="#${noteDomPrefix}-ref${n}" class="${noteDomPrefix}-backlink" role="doc-backlink">${opt.backLabelBra}${displayPrefix}${n}${opt.backLabelKet}</a> `
|
|
66
117
|
}
|
|
67
118
|
|
|
68
|
-
function createAfterBackLinkToken(state, counts, n, opt) {
|
|
119
|
+
function createAfterBackLinkToken(state, counts, n, opt, noteDomPrefix, isEndnote) {
|
|
120
|
+
const displayPrefix = isEndnote ? opt.endnotesLabelPrefix : ''
|
|
69
121
|
let html = ' '
|
|
70
122
|
if (counts && counts > 1) {
|
|
71
123
|
for (let i = 1; i <= counts; i++) {
|
|
72
124
|
const suffixChar = opt.afterBacklinkSuffixArabicNumerals ? i : String.fromCharCode(96 + i)
|
|
73
125
|
const suffix = '-' + suffixChar
|
|
74
|
-
html += `<a href="
|
|
75
|
-
if (opt.afterBacklinkdAriaLabelPrefix) html += ` aria-label="${opt.afterBacklinkdAriaLabelPrefix}${n}${suffix}"`
|
|
126
|
+
html += `<a href="#${noteDomPrefix}-ref${n}${suffix}" class="${noteDomPrefix}-backlink" role="doc-backlink"`
|
|
127
|
+
if (opt.afterBacklinkdAriaLabelPrefix) html += ` aria-label="${opt.afterBacklinkdAriaLabelPrefix}${displayPrefix}${n}${suffix}"`
|
|
76
128
|
html += `>${opt.afterBacklinkContent}`
|
|
77
129
|
if (opt.afterBacklinkWithNumber) {
|
|
78
130
|
html += `<sup>${suffixChar}</sup>`
|
|
@@ -80,8 +132,8 @@ function createAfterBackLinkToken(state, counts, n, opt) {
|
|
|
80
132
|
html += `</a>`
|
|
81
133
|
}
|
|
82
134
|
} else {
|
|
83
|
-
html += `<a href="
|
|
84
|
-
if (opt.afterBacklinkdAriaLabelPrefix) html += ` aria-label="${opt.afterBacklinkdAriaLabelPrefix}${n}"`
|
|
135
|
+
html += `<a href="#${noteDomPrefix}-ref${n}" class="${noteDomPrefix}-backlink" role="doc-backlink"`
|
|
136
|
+
if (opt.afterBacklinkdAriaLabelPrefix) html += ` aria-label="${opt.afterBacklinkdAriaLabelPrefix}${displayPrefix}${n}"`
|
|
85
137
|
html += `>${opt.afterBacklinkContent}</a>`
|
|
86
138
|
}
|
|
87
139
|
const token = new state.Token('html_inline', '', 0)
|
|
@@ -102,16 +154,22 @@ const footnote_plugin = (md, option) =>{
|
|
|
102
154
|
afterBacklinkWithNumber: false,
|
|
103
155
|
afterBacklinkSuffixArabicNumerals: false,
|
|
104
156
|
afterBacklinkdAriaLabelPrefix: 'Back to reference ', /* 戻る:本文参照 */
|
|
157
|
+
endnotesPrefix: 'en-',
|
|
158
|
+
endnotesLabelPrefix: 'E',
|
|
159
|
+
endnotesSectionId: 'endnotes',
|
|
160
|
+
endnotesSectionClass: '',
|
|
161
|
+
endnotesSectionAriaLabel: 'Notes',
|
|
162
|
+
endnotesUseHeading: false,
|
|
105
163
|
}
|
|
106
164
|
if (option) Object.assign(opt, option)
|
|
107
165
|
|
|
108
166
|
const isSpace = md.utils.isSpace
|
|
109
167
|
|
|
110
168
|
md.renderer.rules.footnote_ref = (tokens, idx, _options, env) => render_footnote_ref(tokens, idx, opt, env)
|
|
111
|
-
md.renderer.rules.footnote_open = render_footnote_open
|
|
112
|
-
md.renderer.rules.footnote_close = render_footnote_close
|
|
169
|
+
md.renderer.rules.footnote_open = (tokens, idx, _options, env, slf) => render_footnote_open(tokens, idx, opt, env, slf)
|
|
170
|
+
md.renderer.rules.footnote_close = (tokens, idx, _options, env, slf) => render_footnote_close(tokens, idx, opt, env, slf)
|
|
113
171
|
md.renderer.rules.footnote_anchor = (tokens, idx, _options, env, slf) => render_footnote_anchor(tokens, idx, opt, env, slf)
|
|
114
|
-
md.renderer.rules.footnote_anchor_name = render_footnote_anchor_name
|
|
172
|
+
md.renderer.rules.footnote_anchor_name = (tokens, idx, _options, env, slf) => render_footnote_anchor_name(tokens, idx, opt, env, slf)
|
|
115
173
|
|
|
116
174
|
// Process footnote block definition
|
|
117
175
|
const footnote_def = (state, startLine, endLine, silent) => {
|
|
@@ -134,16 +192,13 @@ const footnote_plugin = (md, option) =>{
|
|
|
134
192
|
|
|
135
193
|
if (silent) { return true; }
|
|
136
194
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
state.env.footnotes = { length: 0, refs: {}, positions: [] }
|
|
140
|
-
}
|
|
141
|
-
const fn = state.env.footnotes
|
|
195
|
+
const isEndnote = isEndnoteLabel(label, opt)
|
|
196
|
+
const fn = ensureNotesEnv(state.env, isEndnote ? 'endnotes' : 'footnotes')
|
|
142
197
|
const id = fn.length++
|
|
143
198
|
fn.refs[':' + label] = id
|
|
144
199
|
|
|
145
200
|
const token = new state.Token('footnote_open', '', 1)
|
|
146
|
-
token.meta = { id,
|
|
201
|
+
token.meta = { id, isEndnote }
|
|
147
202
|
token.level = state.level++
|
|
148
203
|
state.tokens.push(token)
|
|
149
204
|
fn.positions.push(state.tokens.length - 1)
|
|
@@ -193,6 +248,7 @@ const footnote_plugin = (md, option) =>{
|
|
|
193
248
|
|
|
194
249
|
const closeToken = new state.Token('footnote_close', '', -1)
|
|
195
250
|
closeToken.level = --state.level
|
|
251
|
+
closeToken.meta = { isEndnote }
|
|
196
252
|
state.tokens.push(closeToken)
|
|
197
253
|
|
|
198
254
|
return true
|
|
@@ -208,38 +264,30 @@ const footnote_plugin = (md, option) =>{
|
|
|
208
264
|
return false
|
|
209
265
|
}
|
|
210
266
|
|
|
211
|
-
const env = state.env
|
|
212
|
-
if (!env.footnotes || !env.footnotes.refs) { return false; }
|
|
213
|
-
|
|
214
267
|
let pos = start + 2
|
|
215
|
-
|
|
216
|
-
for (; pos < posMax && !found; pos++) {
|
|
268
|
+
for (; pos < posMax; pos++) {
|
|
217
269
|
const ch = src.charCodeAt(pos)
|
|
270
|
+
if (ch === 0x5D /* ] */) break
|
|
218
271
|
if (ch === 0x20 || ch === 0x0A) { return false; } // space or linebreak
|
|
219
|
-
if (ch === 0x5D /* ] */) { found = true; break; }
|
|
220
272
|
}
|
|
221
273
|
|
|
222
|
-
if (
|
|
274
|
+
if (pos >= posMax || pos === start + 2) { return false; }
|
|
223
275
|
pos++; // pos set next ']' position.
|
|
224
276
|
|
|
225
277
|
const label = src.slice(start + 2, pos - 1)
|
|
226
|
-
const
|
|
227
|
-
|
|
228
|
-
|
|
278
|
+
const env = state.env
|
|
279
|
+
const preferEndnote = isEndnoteLabel(label, opt)
|
|
280
|
+
const resolved = selectNoteEnv(label, env, preferEndnote)
|
|
229
281
|
|
|
282
|
+
if (!resolved) { return false; }
|
|
230
283
|
if (!silent) {
|
|
231
|
-
const fn = env
|
|
284
|
+
const fn = resolved.env
|
|
232
285
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
const footnoteId = fn.list.length
|
|
236
|
-
fn.list[footnoteId] = { label, count: 0 }
|
|
237
|
-
|
|
238
|
-
fn.totalCounts = fn.totalCounts || {}
|
|
239
|
-
fn.totalCounts[id] = (fn.totalCounts[id] || 0) + 1
|
|
286
|
+
fn.totalCounts = fn.totalCounts || []
|
|
287
|
+
fn.totalCounts[resolved.id] = (fn.totalCounts[resolved.id] || 0) + 1
|
|
240
288
|
|
|
241
289
|
const token = state.push('footnote_ref', '', 0)
|
|
242
|
-
token.meta = { id,
|
|
290
|
+
token.meta = { id: resolved.id, isEndnote: resolved.isEndnote }
|
|
243
291
|
}
|
|
244
292
|
|
|
245
293
|
state.pos = pos
|
|
@@ -247,42 +295,124 @@ const footnote_plugin = (md, option) =>{
|
|
|
247
295
|
}
|
|
248
296
|
|
|
249
297
|
const footnote_anchor = (state) => {
|
|
298
|
+
if (!state.env.footnotes && !state.env.endnotes) return
|
|
250
299
|
const tokens = state.tokens
|
|
251
|
-
const
|
|
252
|
-
const positions = fn && fn.positions
|
|
253
|
-
if (!positions || positions.length === 0) { return; }
|
|
254
|
-
|
|
255
|
-
const createAnchorToken = (id) => {
|
|
300
|
+
const createAnchorToken = (id, isEndnote) => {
|
|
256
301
|
const aToken = new state.Token('footnote_anchor', '', 0)
|
|
257
|
-
aToken.meta = { id,
|
|
302
|
+
aToken.meta = { id, isEndnote }
|
|
258
303
|
return aToken
|
|
259
304
|
}
|
|
260
305
|
|
|
261
|
-
|
|
262
|
-
const
|
|
263
|
-
if (
|
|
306
|
+
const injectAnchors = (notes, isEndnote) => {
|
|
307
|
+
const positions = notes && notes.positions
|
|
308
|
+
if (!positions || positions.length === 0) { return; }
|
|
264
309
|
|
|
265
|
-
|
|
266
|
-
|
|
310
|
+
if (opt.afterBacklink) {
|
|
311
|
+
const noteDomPrefix = isEndnote ? ENDNOTE_DOM_PREFIX : 'fn'
|
|
312
|
+
const totalCounts = notes.totalCounts
|
|
313
|
+
for (let j = 0, len = positions.length; j < len; ++j) {
|
|
314
|
+
const posOpen = positions[j]
|
|
315
|
+
if (posOpen + 2 >= tokens.length) continue
|
|
267
316
|
|
|
268
|
-
|
|
269
|
-
|
|
317
|
+
const t1 = tokens[posOpen + 1]
|
|
318
|
+
if (t1.type !== 'paragraph_open') continue
|
|
270
319
|
|
|
271
|
-
|
|
272
|
-
|
|
320
|
+
const t2 = tokens[posOpen + 2]
|
|
321
|
+
if (t2.type !== 'inline') continue
|
|
273
322
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
323
|
+
const t0 = tokens[posOpen]
|
|
324
|
+
const id = t0.meta.id
|
|
325
|
+
|
|
326
|
+
t2.children.unshift(createAnchorToken(id, isEndnote))
|
|
327
|
+
const n = id + 1
|
|
328
|
+
const counts = totalCounts && totalCounts[id]
|
|
329
|
+
t2.children.push(createAfterBackLinkToken(state, counts, n, opt, noteDomPrefix, isEndnote))
|
|
330
|
+
}
|
|
331
|
+
return
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
for (let j = 0, len = positions.length; j < len; ++j) {
|
|
335
|
+
const posOpen = positions[j]
|
|
336
|
+
if (posOpen + 2 >= tokens.length) continue
|
|
337
|
+
|
|
338
|
+
const t1 = tokens[posOpen + 1]
|
|
339
|
+
if (t1.type !== 'paragraph_open') continue
|
|
340
|
+
|
|
341
|
+
const t2 = tokens[posOpen + 2]
|
|
342
|
+
if (t2.type !== 'inline') continue
|
|
343
|
+
|
|
344
|
+
const t0 = tokens[posOpen]
|
|
345
|
+
const id = t0.meta.id
|
|
346
|
+
|
|
347
|
+
t2.children.unshift(createAnchorToken(id, isEndnote))
|
|
279
348
|
}
|
|
280
349
|
}
|
|
350
|
+
|
|
351
|
+
injectAnchors(state.env.footnotes, false)
|
|
352
|
+
injectAnchors(state.env.endnotes, true)
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
const move_endnotes_to_section = (state) => {
|
|
356
|
+
if (!opt.endnotesPrefix) return
|
|
357
|
+
if (!state.env.endnotes || !state.env.endnotes.positions || state.env.endnotes.positions.length === 0) {
|
|
358
|
+
return
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
const tokens = state.tokens
|
|
362
|
+
const endnoteTokens = []
|
|
363
|
+
|
|
364
|
+
let write = 0
|
|
365
|
+
let i = 0
|
|
366
|
+
while (i < tokens.length) {
|
|
367
|
+
const token = tokens[i]
|
|
368
|
+
if (token.type === 'footnote_open' && token.meta && token.meta.isEndnote) {
|
|
369
|
+
endnoteTokens.push(token)
|
|
370
|
+
i++
|
|
371
|
+
while (i < tokens.length) {
|
|
372
|
+
const t = tokens[i]
|
|
373
|
+
endnoteTokens.push(t)
|
|
374
|
+
if (t.type === 'footnote_close' && t.meta && t.meta.isEndnote) {
|
|
375
|
+
i++
|
|
376
|
+
break
|
|
377
|
+
}
|
|
378
|
+
i++
|
|
379
|
+
}
|
|
380
|
+
continue
|
|
381
|
+
}
|
|
382
|
+
tokens[write++] = token
|
|
383
|
+
i++
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
if (endnoteTokens.length === 0) return
|
|
387
|
+
|
|
388
|
+
const sectionOpen = new state.Token('html_block', '', 0)
|
|
389
|
+
const attrs = []
|
|
390
|
+
if (!opt.endnotesUseHeading && opt.endnotesSectionAriaLabel) {
|
|
391
|
+
attrs.push(`aria-label="${opt.endnotesSectionAriaLabel}"`)
|
|
392
|
+
}
|
|
393
|
+
if (opt.endnotesSectionId) attrs.push(`id="${opt.endnotesSectionId}"`)
|
|
394
|
+
if (opt.endnotesSectionClass) attrs.push(`class="${opt.endnotesSectionClass}"`)
|
|
395
|
+
attrs.push('role="doc-endnotes"')
|
|
396
|
+
let sectionContent = `<section ${attrs.join(' ')}>\n`
|
|
397
|
+
if (opt.endnotesUseHeading && opt.endnotesSectionAriaLabel) {
|
|
398
|
+
sectionContent += `<h2>${opt.endnotesSectionAriaLabel}</h2>\n`
|
|
399
|
+
}
|
|
400
|
+
sectionContent += '<ol>\n'
|
|
401
|
+
sectionOpen.content = sectionContent
|
|
402
|
+
|
|
403
|
+
const sectionClose = new state.Token('html_block', '', 0)
|
|
404
|
+
sectionClose.content = '</ol>\n</section>\n'
|
|
405
|
+
|
|
406
|
+
tokens.length = write
|
|
407
|
+
tokens.push(sectionOpen)
|
|
408
|
+
for (let j = 0; j < endnoteTokens.length; j++) tokens.push(endnoteTokens[j])
|
|
409
|
+
tokens.push(sectionClose)
|
|
281
410
|
}
|
|
282
411
|
|
|
283
412
|
md.block.ruler.before('reference', 'footnote_def', footnote_def, { alt: [ 'paragraph', 'reference' ] })
|
|
284
413
|
md.inline.ruler.after('image', 'footnote_ref', footnote_ref)
|
|
285
414
|
md.core.ruler.after('inline', 'footnote_anchor', footnote_anchor)
|
|
415
|
+
md.core.ruler.after('footnote_anchor', 'endnotes_move', move_endnotes_to_section)
|
|
286
416
|
}
|
|
287
417
|
|
|
288
418
|
export default footnote_plugin
|
package/package.json
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@peaceroad/markdown-it-footnote-here",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "A markdown-it plugin. This generate aside[role|doc-footnote] element just below the footnote reference paragraph.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type":"module",
|
|
7
|
+
"files" : [
|
|
8
|
+
"index.js",
|
|
9
|
+
"LICENSE",
|
|
10
|
+
"README.md"
|
|
11
|
+
],
|
|
7
12
|
"author": "peaceroad <peaceroad@gmail.com>",
|
|
8
13
|
"homepage": "https://github.com/peaceroad/markdown-it-footnote-here#readme",
|
|
9
14
|
"repository": {
|