Alex Kocharin
10 years ago
9 changed files with 489 additions and 22 deletions
@ -0,0 +1,67 @@ |
|||||
|
// Process footnote reference list
|
||||
|
|
||||
|
'use strict'; |
||||
|
|
||||
|
|
||||
|
module.exports = function footnote(state, startLine, endLine, silent) { |
||||
|
var oldBMark, oldTShift, oldParentType, pos, label, |
||||
|
start = state.bMarks[startLine] + state.tShift[startLine], |
||||
|
max = state.eMarks[startLine]; |
||||
|
|
||||
|
// line should be at least 5 chars - "[^x]:"
|
||||
|
if (start + 4 > max) { return false; } |
||||
|
|
||||
|
if (state.src.charCodeAt(start) !== 0x5B/* [ */) { return false; } |
||||
|
if (state.src.charCodeAt(start + 1) !== 0x5E/* ^ */) { return false; } |
||||
|
if (state.level >= state.options.maxNesting) { return false; } |
||||
|
|
||||
|
for (pos = start + 2; pos < max; pos++) { |
||||
|
if (state.src.charCodeAt(pos) === 0x20) { return false; } |
||||
|
if (state.src.charCodeAt(pos) === 0x5D /* ] */) { |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (pos === start + 2) { return false; } // no empty footnote labels
|
||||
|
if (pos + 1 >= max || state.src.charCodeAt(++pos) !== 0x3A /* : */) { return false; } |
||||
|
if (silent) { return true; } |
||||
|
pos++; |
||||
|
|
||||
|
if (!state.env.footnotes) { state.env.footnotes = {}; } |
||||
|
if (!state.env.footnotes.available) { state.env.footnotes.available = Object.create(null); } |
||||
|
label = state.src.slice(start + 2, pos - 2); |
||||
|
state.env.footnotes.available[label] = true; |
||||
|
|
||||
|
state.tokens.push({ |
||||
|
type: 'footnote_reference_open', |
||||
|
label: label, |
||||
|
level: state.level++ |
||||
|
}); |
||||
|
|
||||
|
oldBMark = state.bMarks[startLine]; |
||||
|
oldTShift = state.tShift[startLine]; |
||||
|
oldParentType = state.parentType; |
||||
|
state.tShift[startLine] = state.skipSpaces(pos) - pos; |
||||
|
state.bMarks[startLine] = pos; |
||||
|
state.blkIndent += 4; |
||||
|
state.parentType = 'footnote'; |
||||
|
|
||||
|
if (state.tShift[startLine] < state.blkIndent) { |
||||
|
state.tShift[startLine] += state.blkIndent; |
||||
|
state.bMarks[startLine] -= state.blkIndent; |
||||
|
} |
||||
|
|
||||
|
state.parser.tokenize(state, startLine, endLine, true); |
||||
|
|
||||
|
state.parentType = oldParentType; |
||||
|
state.blkIndent -= 4; |
||||
|
state.tShift[startLine] = oldTShift; |
||||
|
state.bMarks[startLine] = oldBMark; |
||||
|
|
||||
|
state.tokens.push({ |
||||
|
type: 'footnote_reference_close', |
||||
|
level: --state.level |
||||
|
}); |
||||
|
|
||||
|
return true; |
||||
|
}; |
@ -0,0 +1,88 @@ |
|||||
|
'use strict'; |
||||
|
|
||||
|
|
||||
|
module.exports = function footnote_block(state) { |
||||
|
var i, l, t, list, tokens, current, currentLabel, anchor, |
||||
|
level = 0, |
||||
|
insideRef = false, |
||||
|
refTokens = Object.create(null); |
||||
|
|
||||
|
if (!state.env.footnotes) { return; } |
||||
|
|
||||
|
state.tokens = state.tokens.filter(function(tok) { |
||||
|
if (tok.type === 'footnote_reference_open') { |
||||
|
insideRef = true; |
||||
|
current = []; |
||||
|
currentLabel = tok.label; |
||||
|
return false; |
||||
|
} |
||||
|
if (tok.type === 'footnote_reference_close') { |
||||
|
insideRef = false; |
||||
|
refTokens[currentLabel] = current; |
||||
|
return false; |
||||
|
} |
||||
|
if (insideRef) { current.push(tok); } |
||||
|
return !insideRef; |
||||
|
}); |
||||
|
|
||||
|
if (!state.env.footnotes.list) { return; } |
||||
|
list = state.env.footnotes.list; |
||||
|
|
||||
|
state.tokens.push({ |
||||
|
type: 'footnote_block_open', |
||||
|
level: level++ |
||||
|
}); |
||||
|
for (i = 0, l = list.length; i < l; i++) { |
||||
|
state.tokens.push({ |
||||
|
type: 'footnote_open', |
||||
|
id: i, |
||||
|
level: level++ |
||||
|
}); |
||||
|
|
||||
|
if (list[i].tokens) { |
||||
|
tokens = []; |
||||
|
tokens.push({ |
||||
|
type: 'paragraph_open', |
||||
|
tight: false, |
||||
|
level: level++ |
||||
|
}); |
||||
|
tokens.push({ |
||||
|
type: 'inline', |
||||
|
content: '', |
||||
|
level: level, |
||||
|
children: list[i].tokens |
||||
|
}); |
||||
|
tokens.push({ |
||||
|
type: 'paragraph_close', |
||||
|
tight: false, |
||||
|
level: --level |
||||
|
}); |
||||
|
} else if (list[i].label) { |
||||
|
tokens = refTokens[list[i].label]; |
||||
|
} |
||||
|
|
||||
|
anchor = { |
||||
|
type: 'footnote_anchor', |
||||
|
id: i, |
||||
|
level: level |
||||
|
}; |
||||
|
|
||||
|
state.tokens = state.tokens.concat(tokens); |
||||
|
if (state.tokens[state.tokens.length - 1].type === 'paragraph_close') { |
||||
|
t = state.tokens.pop(); |
||||
|
state.tokens.push(anchor); |
||||
|
state.tokens.push(t); |
||||
|
} else { |
||||
|
state.tokens.push(anchor); |
||||
|
} |
||||
|
|
||||
|
state.tokens.push({ |
||||
|
type: 'footnote_close', |
||||
|
level: --level |
||||
|
}); |
||||
|
} |
||||
|
state.tokens.push({ |
||||
|
type: 'footnote_block_close', |
||||
|
level: --level |
||||
|
}); |
||||
|
}; |
@ -0,0 +1,54 @@ |
|||||
|
// Process inline footnotes (^[...])
|
||||
|
|
||||
|
'use strict'; |
||||
|
|
||||
|
var parseLinkLabel = require('../helpers/parse_link_label'); |
||||
|
|
||||
|
|
||||
|
module.exports = function footnote_inline(state, silent) { |
||||
|
var labelStart, |
||||
|
labelEnd, |
||||
|
pos, |
||||
|
footnoteId, |
||||
|
oldLength, |
||||
|
max = state.posMax, |
||||
|
start = state.pos; |
||||
|
|
||||
|
if (start + 2 >= max) { return false; } |
||||
|
if (state.src.charCodeAt(start) !== 0x5E/* ^ */) { return false; } |
||||
|
if (state.src.charCodeAt(start + 1) !== 0x5B/* [ */) { return false; } |
||||
|
if (state.level >= state.options.maxNesting) { return false; } |
||||
|
|
||||
|
labelStart = start + 2; |
||||
|
labelEnd = parseLinkLabel(state, start + 1); |
||||
|
|
||||
|
// parser failed to find ']', so it's not a valid note
|
||||
|
if (labelEnd < 0) { return false; } |
||||
|
|
||||
|
// We found the end of the link, and know for a fact it's a valid link;
|
||||
|
// so all that's left to do is to call tokenizer.
|
||||
|
//
|
||||
|
if (!silent) { |
||||
|
if (!state.env.footnotes) { state.env.footnotes = {}; } |
||||
|
if (!state.env.footnotes.list) { state.env.footnotes.list = []; } |
||||
|
footnoteId = state.env.footnotes.list.length; |
||||
|
|
||||
|
state.pos = labelStart; |
||||
|
state.posMax = labelEnd; |
||||
|
|
||||
|
state.push({ |
||||
|
type: 'footnote_ref', |
||||
|
id: footnoteId, |
||||
|
level: state.level |
||||
|
}); |
||||
|
state.linkLevel++; |
||||
|
oldLength = state.tokens.length; |
||||
|
state.parser.tokenize(state); |
||||
|
state.env.footnotes.list[footnoteId] = { tokens: state.tokens.splice(oldLength) }; |
||||
|
state.linkLevel--; |
||||
|
} |
||||
|
|
||||
|
state.pos = pos; |
||||
|
state.posMax = max; |
||||
|
return true; |
||||
|
}; |
@ -0,0 +1,52 @@ |
|||||
|
// Process footnote references ([^...])
|
||||
|
|
||||
|
'use strict'; |
||||
|
|
||||
|
|
||||
|
module.exports = function footnote_ref(state, silent) { |
||||
|
var label, |
||||
|
pos, |
||||
|
footnoteId, |
||||
|
max = state.posMax, |
||||
|
start = state.pos; |
||||
|
|
||||
|
// should be at least 4 chars - "[^x]"
|
||||
|
if (start + 3 > max) { return false; } |
||||
|
|
||||
|
if (!state.env.footnotes || !state.env.footnotes.available) { return false; } |
||||
|
if (state.src.charCodeAt(start) !== 0x5B/* [ */) { return false; } |
||||
|
if (state.src.charCodeAt(start + 1) !== 0x5E/* ^ */) { return false; } |
||||
|
if (state.level >= state.options.maxNesting) { return false; } |
||||
|
|
||||
|
for (pos = start + 2; pos < max; pos++) { |
||||
|
if (state.src.charCodeAt(pos) === 0x20) { return false; } |
||||
|
if (state.src.charCodeAt(pos) === 0x0A) { return false; } |
||||
|
if (state.src.charCodeAt(pos) === 0x5D /* ] */) { |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (pos === start + 2) { return false; } // no empty footnote labels
|
||||
|
if (pos >= max) { return false; } |
||||
|
pos++; |
||||
|
|
||||
|
label = state.src.slice(start + 2, pos - 1); |
||||
|
if (!state.env.footnotes.available[label]) { return false; } |
||||
|
|
||||
|
if (!silent) { |
||||
|
if (!state.env.footnotes.list) { state.env.footnotes.list = []; } |
||||
|
footnoteId = state.env.footnotes.list.length; |
||||
|
|
||||
|
state.push({ |
||||
|
type: 'footnote_ref', |
||||
|
id: footnoteId, |
||||
|
level: state.level |
||||
|
}); |
||||
|
|
||||
|
state.env.footnotes.list[footnoteId] = { label: label }; |
||||
|
} |
||||
|
|
||||
|
state.pos = pos; |
||||
|
state.posMax = max; |
||||
|
return true; |
||||
|
}; |
@ -0,0 +1,179 @@ |
|||||
|
|
||||
|
Pandoc example: |
||||
|
. |
||||
|
Here is a footnote reference,[^1] and another.[^longnote] |
||||
|
|
||||
|
[^1]: Here is the footnote. |
||||
|
|
||||
|
[^longnote]: Here's one with multiple blocks. |
||||
|
|
||||
|
Subsequent paragraphs are indented to show that they |
||||
|
belong to the previous footnote. |
||||
|
|
||||
|
{ some.code } |
||||
|
|
||||
|
The whole paragraph can be indented, or just the first |
||||
|
line. In this way, multi-paragraph footnotes work like |
||||
|
multi-paragraph list items. |
||||
|
|
||||
|
This paragraph won't be part of the note, because it |
||||
|
isn't indented. |
||||
|
. |
||||
|
<p>Here is a footnote reference,<a href="#fn1" class="footnoteRef" id="fnref1"><sup>1</sup></a> and another.<a href="#fn2" class="footnoteRef" id="fnref2"><sup>2</sup></a></p> |
||||
|
<p>This paragraph won’t be part of the note, because it |
||||
|
isn’t indented.</p> |
||||
|
<div class="footnotes"> |
||||
|
<hr> |
||||
|
<ol> |
||||
|
<li id="fn1"><p>Here is the footnote.<a href="#fnref1">↩</a></p> |
||||
|
</li> |
||||
|
<li id="fn2"><p>Here’s one with multiple blocks.</p> |
||||
|
<p>Subsequent paragraphs are indented to show that they |
||||
|
belong to the previous footnote.</p> |
||||
|
<pre><code>{ some.code } |
||||
|
</code></pre> |
||||
|
<p>The whole paragraph can be indented, or just the first |
||||
|
line. In this way, multi-paragraph footnotes work like |
||||
|
multi-paragraph list items.<a href="#fnref2">↩</a></p> |
||||
|
</li> |
||||
|
</ol> |
||||
|
</div> |
||||
|
. |
||||
|
|
||||
|
They could terminate each other: |
||||
|
|
||||
|
. |
||||
|
[^1][^2][^3] |
||||
|
|
||||
|
[^1]: foo |
||||
|
[^2]: bar |
||||
|
[^3]: baz |
||||
|
. |
||||
|
<p><a href="#fn1" class="footnoteRef" id="fnref1"><sup>1</sup></a><a href="#fn2" class="footnoteRef" id="fnref2"><sup>2</sup></a><a href="#fn3" class="footnoteRef" id="fnref3"><sup>3</sup></a></p> |
||||
|
<div class="footnotes"> |
||||
|
<hr> |
||||
|
<ol> |
||||
|
<li id="fn1"><p>foo<a href="#fnref1">↩</a></p> |
||||
|
</li> |
||||
|
<li id="fn2"><p>bar<a href="#fnref2">↩</a></p> |
||||
|
</li> |
||||
|
<li id="fn3"><p>baz<a href="#fnref3">↩</a></p> |
||||
|
</li> |
||||
|
</ol> |
||||
|
</div> |
||||
|
. |
||||
|
|
||||
|
|
||||
|
They could be inside blockquotes, and are lazy: |
||||
|
. |
||||
|
[^foo] |
||||
|
|
||||
|
> [^foo]: bar |
||||
|
baz |
||||
|
. |
||||
|
<p><a href="#fn1" class="footnoteRef" id="fnref1"><sup>1</sup></a></p> |
||||
|
<blockquote> |
||||
|
</blockquote> |
||||
|
<div class="footnotes"> |
||||
|
<hr> |
||||
|
<ol> |
||||
|
<li id="fn1"><p>bar |
||||
|
baz<a href="#fnref1">↩</a></p> |
||||
|
</li> |
||||
|
</ol> |
||||
|
</div> |
||||
|
. |
||||
|
|
||||
|
|
||||
|
Their labels could not contain spaces or newlines: |
||||
|
|
||||
|
. |
||||
|
[^ foo]: bar baz |
||||
|
|
||||
|
[^foo |
||||
|
]: bar baz |
||||
|
. |
||||
|
<p>[^ foo]: bar baz</p> |
||||
|
<p>[^foo |
||||
|
]: bar baz</p> |
||||
|
. |
||||
|
|
||||
|
We support inline notes too (pandoc example): |
||||
|
|
||||
|
. |
||||
|
Here is an inline note.^[Inlines notes are easier to write, since |
||||
|
you don't have to pick an identifier and move down to type the |
||||
|
note.] |
||||
|
. |
||||
|
<p>Here is an inline note.<a href="#fn1" class="footnoteRef" id="fnref1"><sup>1</sup></a></p> |
||||
|
<div class="footnotes"> |
||||
|
<hr> |
||||
|
<ol> |
||||
|
<li id="fn1"><p>Inlines notes are easier to write, since |
||||
|
you don’t have to pick an identifier and move down to type the |
||||
|
note.<a href="#fnref1">↩</a></p> |
||||
|
</li> |
||||
|
</ol> |
||||
|
</div> |
||||
|
. |
||||
|
|
||||
|
They could have arbitrary markup: |
||||
|
|
||||
|
. |
||||
|
foo^[ *bar* ] |
||||
|
. |
||||
|
<p>foo<a href="#fn1" class="footnoteRef" id="fnref1"><sup>1</sup></a></p> |
||||
|
<div class="footnotes"> |
||||
|
<hr> |
||||
|
<ol> |
||||
|
<li id="fn1"><p> <em>bar</em> <a href="#fnref1">↩</a></p> |
||||
|
</li> |
||||
|
</ol> |
||||
|
</div> |
||||
|
. |
||||
|
|
||||
|
Indents: |
||||
|
|
||||
|
. |
||||
|
[^xxxxx] [^yyyyy] |
||||
|
|
||||
|
[^xxxxx]: foo |
||||
|
--- |
||||
|
|
||||
|
[^yyyyy]: foo |
||||
|
--- |
||||
|
. |
||||
|
<p><a href="#fn1" class="footnoteRef" id="fnref1"><sup>1</sup></a> <a href="#fn2" class="footnoteRef" id="fnref2"><sup>2</sup></a></p> |
||||
|
<hr> |
||||
|
<div class="footnotes"> |
||||
|
<hr> |
||||
|
<ol> |
||||
|
<li id="fn1"><h2>foo</h2> |
||||
|
<a href="#fnref1">↩</a></li> |
||||
|
<li id="fn2"><p>foo<a href="#fnref2">↩</a></p> |
||||
|
</li> |
||||
|
</ol> |
||||
|
</div> |
||||
|
. |
||||
|
|
||||
|
Indents for the first line: |
||||
|
|
||||
|
. |
||||
|
[^xxxxx] [^yyyyy] |
||||
|
|
||||
|
[^xxxxx]: foo |
||||
|
|
||||
|
[^yyyyy]: foo |
||||
|
. |
||||
|
<p><a href="#fn1" class="footnoteRef" id="fnref1"><sup>1</sup></a> <a href="#fn2" class="footnoteRef" id="fnref2"><sup>2</sup></a></p> |
||||
|
<div class="footnotes"> |
||||
|
<hr> |
||||
|
<ol> |
||||
|
<li id="fn1"><p>foo<a href="#fnref1">↩</a></p> |
||||
|
</li> |
||||
|
<li id="fn2"><pre><code>foo |
||||
|
</code></pre> |
||||
|
<a href="#fnref2">↩</a></li> |
||||
|
</ol> |
||||
|
</div> |
||||
|
. |
Loading…
Reference in new issue