Moved footnotes into Markdown Extra extension
This commit is contained in:
parent
4ae1aac96b
commit
416fbb68f2
@ -15,7 +15,6 @@ define([
|
|||||||
"extensions/workingIndicator",
|
"extensions/workingIndicator",
|
||||||
"extensions/notifications",
|
"extensions/notifications",
|
||||||
"extensions/markdownExtra",
|
"extensions/markdownExtra",
|
||||||
"extensions/markdownFootnotes",
|
|
||||||
"extensions/toc",
|
"extensions/toc",
|
||||||
"extensions/mathJax",
|
"extensions/mathJax",
|
||||||
"extensions/emailConverter",
|
"extensions/emailConverter",
|
||||||
|
@ -1,100 +0,0 @@
|
|||||||
define([
|
|
||||||
"underscore",
|
|
||||||
"utils",
|
|
||||||
"classes/Extension",
|
|
||||||
], function(_, utils, Extension) {
|
|
||||||
|
|
||||||
var markdownFootnotes = new Extension("markdownFootnotes", "Markdown Footnotes", true);
|
|
||||||
markdownFootnotes.settingsBlock = '<p>Adds support for Markdown footnotes.</p>';
|
|
||||||
|
|
||||||
var inlineTags = new RegExp([
|
|
||||||
'^(<\\/?(a|abbr|acronym|applet|area|b|basefont|',
|
|
||||||
'bdo|big|button|cite|code|del|dfn|em|figcaption|',
|
|
||||||
'font|i|iframe|img|input|ins|kbd|label|map|',
|
|
||||||
'mark|meter|object|param|progress|q|ruby|rp|rt|s|',
|
|
||||||
'samp|script|select|small|span|strike|strong|',
|
|
||||||
'sub|sup|textarea|time|tt|u|var|wbr)[^>]*>|',
|
|
||||||
'<(br)\\s?\\/?>)$'
|
|
||||||
].join(''), 'i');
|
|
||||||
|
|
||||||
var previousPostConversion = undefined;
|
|
||||||
markdownFootnotes.onEditorConfigure = function(editor) {
|
|
||||||
var converter = editor.getConverter();
|
|
||||||
previousPostConversion = converter.hooks.postConversion;
|
|
||||||
converter.hooks.chain("postNormalization", _StripFootnoteDefinitions);
|
|
||||||
converter.hooks.chain("postBlockGamut", _DoFootnotes);
|
|
||||||
converter.hooks.chain("postConversion", _PrintFootnotes);
|
|
||||||
};
|
|
||||||
|
|
||||||
var footnotes = undefined;
|
|
||||||
var usedFootnotes = undefined;
|
|
||||||
function _StripFootnoteDefinitions(text) {
|
|
||||||
footnotes = {};
|
|
||||||
usedFootnotes = [];
|
|
||||||
|
|
||||||
text = text.replace(/\n[ ]{0,3}\[\^(.+?)\]\:[ \t]*\n?([\s\S]*?)\n{1,2}((?=\n[ ]{0,3}\S)|\Z)/g, function(wholeMatch, m1, m2) {
|
|
||||||
m1 = utils.slugify(m1);
|
|
||||||
m2 += "\n";
|
|
||||||
m2 = m2.replace(/^[ ]{0,3}/g, "");
|
|
||||||
footnotes[m1] = m2;
|
|
||||||
return "\n";
|
|
||||||
});
|
|
||||||
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
|
|
||||||
var blockGamutHookCallback = undefined;
|
|
||||||
function _DoFootnotes(text, blockGamutHookCallbackParam) {
|
|
||||||
blockGamutHookCallback = blockGamutHookCallbackParam;
|
|
||||||
var footnoteCounter = 0;
|
|
||||||
text = text.replace(/\[\^(.+?)\]/g, function(wholeMatch, m1) {
|
|
||||||
var id = utils.slugify(m1);
|
|
||||||
var footnote = footnotes[id];
|
|
||||||
if(footnote === undefined) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
footnoteCounter++;
|
|
||||||
usedFootnotes.push(id);
|
|
||||||
return '<a href="#fn:' + id + '" id="fnref:' + id + '" title="See footnote" class="footnote">' + footnoteCounter + '</a>';
|
|
||||||
});
|
|
||||||
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
|
|
||||||
function _PrintFootnotes(text) {
|
|
||||||
if(usedFootnotes.length === 0) {
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
|
|
||||||
_.each(footnotes, function(footnote, id) {
|
|
||||||
var formattedfootnote = blockGamutHookCallback(footnote);
|
|
||||||
formattedfootnote = unescapeSpecialChars(formattedfootnote);
|
|
||||||
formattedfootnote = formattedfootnote.replace(/~D/g, "$$").replace(/~T/g, "~");
|
|
||||||
formattedfootnote = previousPostConversion(formattedfootnote);
|
|
||||||
formattedfootnote = formattedfootnote.replace(/<[^>]*>?/gi, function(tag) {
|
|
||||||
return tag.match(inlineTags) ? tag : '';
|
|
||||||
});
|
|
||||||
footnotes[id] = formattedfootnote;
|
|
||||||
});
|
|
||||||
|
|
||||||
text += '\n\n<div class="footnotes">\n<hr>\n<ol>\n\n';
|
|
||||||
_.each(usedFootnotes, function(id) {
|
|
||||||
var footnote = footnotes[id];
|
|
||||||
text += '<li id="fn:' + id + '">' + footnote + ' <a href="#fnref:' + id + '" title="Return to article" class="reversefootnote">↩</a></li>\n\n';
|
|
||||||
});
|
|
||||||
text += '</ol>\n</div>';
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Duplicated from PageDown converter
|
|
||||||
function unescapeSpecialChars(text) {
|
|
||||||
// Swap back in all the special characters we've hidden.
|
|
||||||
text = text.replace(/~E(\d+)E/g, function(wholeMatch, m1) {
|
|
||||||
var charCodeToReplace = parseInt(m1);
|
|
||||||
return String.fromCharCode(charCodeToReplace);
|
|
||||||
});
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
|
|
||||||
return markdownFootnotes;
|
|
||||||
});
|
|
@ -9,7 +9,7 @@ define([
|
|||||||
mathJax.settingsBlock = mathJaxSettingsBlockHTML;
|
mathJax.settingsBlock = mathJaxSettingsBlockHTML;
|
||||||
mathJax.defaultConfig = {
|
mathJax.defaultConfig = {
|
||||||
tex: "{}",
|
tex: "{}",
|
||||||
tex2jax: '{ inlineMath: [["$","$"],["\\\\(","\\\\)"]], displayMath: [["$$","$$"],["\\[","\\]"]], processEscapes: true }'
|
tex2jax: '{ inlineMath: [["$","$"],["\\\\\\\\(","\\\\\\\\)"]], displayMath: [["$$","$$"],["\\\\[","\\\\]"]], processEscapes: true }'
|
||||||
};
|
};
|
||||||
|
|
||||||
mathJax.onLoadSettings = function() {
|
mathJax.onLoadSettings = function() {
|
||||||
|
@ -121,6 +121,15 @@
|
|||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function slugify(text) {
|
||||||
|
return text.toLowerCase()
|
||||||
|
.replace(/\s+/g, '-') // Replace spaces with -
|
||||||
|
.replace(/[^\w\-]+/g, '') // Remove all non-word chars
|
||||||
|
.replace(/\-\-+/g, '-') // Replace multiple - with single -
|
||||||
|
.replace(/^-+/, '') // Trim - from start of text
|
||||||
|
.replace(/-+$/, ''); // Trim - from end of text
|
||||||
|
}
|
||||||
|
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
* Markdown.Extra *
|
* Markdown.Extra *
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
@ -136,6 +145,10 @@
|
|||||||
// they're not destroyed if the user is using a sanitizing converter
|
// they're not destroyed if the user is using a sanitizing converter
|
||||||
this.hashBlocks = [];
|
this.hashBlocks = [];
|
||||||
|
|
||||||
|
// Stores footnotes
|
||||||
|
this.footnotes = {};
|
||||||
|
this.usedFootnotes = [];
|
||||||
|
|
||||||
// Special attribute blocks for fenced code blocks and headers enabled.
|
// Special attribute blocks for fenced code blocks and headers enabled.
|
||||||
this.attributeBlocks = false;
|
this.attributeBlocks = false;
|
||||||
|
|
||||||
@ -160,7 +173,7 @@
|
|||||||
options = options || {};
|
options = options || {};
|
||||||
options.extensions = options.extensions || ["all"];
|
options.extensions = options.extensions || ["all"];
|
||||||
if (contains(options.extensions, "all")) {
|
if (contains(options.extensions, "all")) {
|
||||||
options.extensions = ["tables", "fenced_code_gfm", "def_list", "attr_list"];
|
options.extensions = ["tables", "fenced_code_gfm", "def_list", "attr_list", "footnotes"];
|
||||||
}
|
}
|
||||||
if (contains(options.extensions, "attr_list")) {
|
if (contains(options.extensions, "attr_list")) {
|
||||||
postNormalizationTransformations.push("hashFcbAttributeBlocks");
|
postNormalizationTransformations.push("hashFcbAttributeBlocks");
|
||||||
@ -177,6 +190,11 @@
|
|||||||
if (contains(options.extensions, "def_list")) {
|
if (contains(options.extensions, "def_list")) {
|
||||||
preBlockGamutTransformations.push("definitionLists");
|
preBlockGamutTransformations.push("definitionLists");
|
||||||
}
|
}
|
||||||
|
if (contains(options.extensions, "footnotes")) {
|
||||||
|
postNormalizationTransformations.push("stripFootnoteDefinitions");
|
||||||
|
preBlockGamutTransformations.push("doFootnotes");
|
||||||
|
postConversionTransformations.push("printFootnotes");
|
||||||
|
}
|
||||||
|
|
||||||
converter.hooks.chain("postNormalization", function(text) {
|
converter.hooks.chain("postNormalization", function(text) {
|
||||||
return extra.doTransform(postNormalizationTransformations, text) + '\n';
|
return extra.doTransform(postNormalizationTransformations, text) + '\n';
|
||||||
@ -194,7 +212,9 @@
|
|||||||
converter.hooks.chain("postConversion", function(text) {
|
converter.hooks.chain("postConversion", function(text) {
|
||||||
text = extra.doTransform(postConversionTransformations, text);
|
text = extra.doTransform(postConversionTransformations, text);
|
||||||
// Clear state vars that may use unnecessary memory
|
// Clear state vars that may use unnecessary memory
|
||||||
this.hashBlocks = [];
|
extra.hashBlocks = [];
|
||||||
|
extra.footnotes = {};
|
||||||
|
extra.usedFootnotes = [];
|
||||||
return text;
|
return text;
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -276,7 +296,7 @@
|
|||||||
// TODO: use sentinels. Should we just add/remove them in doConversion?
|
// TODO: use sentinels. Should we just add/remove them in doConversion?
|
||||||
// TODO: better matches for id / class attributes
|
// TODO: better matches for id / class attributes
|
||||||
var attrBlock = "\\{\\s*[.|#][^}]+\\}";
|
var attrBlock = "\\{\\s*[.|#][^}]+\\}";
|
||||||
var fcbAttributes = new RegExp("^(```[^{]*)\\s+(" + attrBlock + ")[ \\t]*\\n" +
|
var fcbAttributes = new RegExp("^(```[^{\\n]*)\\s+(" + attrBlock + ")[ \\t]*\\n" +
|
||||||
"(?=([\\s\\S]*?)\\n```\\s*(\\n|0x03))", "gm");
|
"(?=([\\s\\S]*?)\\n```\\s*(\\n|0x03))", "gm");
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
@ -437,6 +457,75 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************
|
||||||
|
* Footnotes *
|
||||||
|
*****************************************************************/
|
||||||
|
|
||||||
|
// Strip footnote, store in hashes.
|
||||||
|
Markdown.Extra.prototype.stripFootnoteDefinitions = function(text) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
text = text.replace(
|
||||||
|
/\n[ ]{0,3}\[\^(.+?)\]\:[ \t]*\n?([\s\S]*?)\n{1,2}((?=\n[ ]{0,3}\S)|$)/g,
|
||||||
|
function(wholeMatch, m1, m2) {
|
||||||
|
m1 = slugify(m1);
|
||||||
|
m2 += "\n";
|
||||||
|
m2 = m2.replace(/^[ ]{0,3}/g, "");
|
||||||
|
self.footnotes[m1] = m2;
|
||||||
|
return "\n";
|
||||||
|
});
|
||||||
|
|
||||||
|
return text;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Find and convert footnotes references.
|
||||||
|
Markdown.Extra.prototype.doFootnotes = function(text) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
var footnoteCounter = 0;
|
||||||
|
text = text.replace(/\[\^(.+?)\]/g, function(wholeMatch, m1) {
|
||||||
|
var id = slugify(m1);
|
||||||
|
var footnote = self.footnotes[id];
|
||||||
|
if (footnote === undefined) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
footnoteCounter++;
|
||||||
|
self.usedFootnotes.push(id);
|
||||||
|
return '<a href="#fn:' + id + '" id="fnref:' + id
|
||||||
|
+ '" title="See footnote" class="footnote">' + footnoteCounter
|
||||||
|
+ '</a>';
|
||||||
|
});
|
||||||
|
|
||||||
|
return text;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Print footnotes at the end of the document
|
||||||
|
Markdown.Extra.prototype.printFootnotes = function(text) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
if (self.usedFootnotes.length === 0) {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
text += '\n\n<div class="footnotes">\n<hr>\n<ol>\n\n';
|
||||||
|
for(var i=0; i<self.usedFootnotes.length; i++) {
|
||||||
|
var id = self.usedFootnotes[i];
|
||||||
|
var footnote = self.footnotes[id];
|
||||||
|
var formattedfootnote = convertSpans(footnote, self);
|
||||||
|
text += '<li id="fn:'
|
||||||
|
+ id
|
||||||
|
+ '">'
|
||||||
|
+ formattedfootnote
|
||||||
|
+ ' <a href="#fnref:'
|
||||||
|
+ id
|
||||||
|
+ '" title="Return to article" class="reversefootnote">↩</a></li>\n\n';
|
||||||
|
}
|
||||||
|
text += '</ol>\n</div>';
|
||||||
|
return text;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/******************************************************************
|
/******************************************************************
|
||||||
* Fenced Code Blocks (gfm) *
|
* Fenced Code Blocks (gfm) *
|
||||||
******************************************************************/
|
******************************************************************/
|
||||||
|
Loading…
Reference in New Issue
Block a user