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