Moved footnotes into Markdown Extra extension
This commit is contained in:
parent
4ae1aac96b
commit
416fbb68f2
@ -15,7 +15,6 @@ define([
|
||||
"extensions/workingIndicator",
|
||||
"extensions/notifications",
|
||||
"extensions/markdownExtra",
|
||||
"extensions/markdownFootnotes",
|
||||
"extensions/toc",
|
||||
"extensions/mathJax",
|
||||
"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.defaultConfig = {
|
||||
tex: "{}",
|
||||
tex2jax: '{ inlineMath: [["$","$"],["\\\\(","\\\\)"]], displayMath: [["$$","$$"],["\\[","\\]"]], processEscapes: true }'
|
||||
tex2jax: '{ inlineMath: [["$","$"],["\\\\\\\\(","\\\\\\\\)"]], displayMath: [["$$","$$"],["\\\\[","\\\\]"]], processEscapes: true }'
|
||||
};
|
||||
|
||||
mathJax.onLoadSettings = function() {
|
||||
|
@ -120,6 +120,15 @@
|
||||
});
|
||||
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 *
|
||||
@ -135,6 +144,10 @@
|
||||
// Stores html blocks we generate in hooks so that
|
||||
// they're not destroyed if the user is using a sanitizing converter
|
||||
this.hashBlocks = [];
|
||||
|
||||
// Stores footnotes
|
||||
this.footnotes = {};
|
||||
this.usedFootnotes = [];
|
||||
|
||||
// Special attribute blocks for fenced code blocks and headers enabled.
|
||||
this.attributeBlocks = false;
|
||||
@ -160,7 +173,7 @@
|
||||
options = options || {};
|
||||
options.extensions = 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")) {
|
||||
postNormalizationTransformations.push("hashFcbAttributeBlocks");
|
||||
@ -177,6 +190,11 @@
|
||||
if (contains(options.extensions, "def_list")) {
|
||||
preBlockGamutTransformations.push("definitionLists");
|
||||
}
|
||||
if (contains(options.extensions, "footnotes")) {
|
||||
postNormalizationTransformations.push("stripFootnoteDefinitions");
|
||||
preBlockGamutTransformations.push("doFootnotes");
|
||||
postConversionTransformations.push("printFootnotes");
|
||||
}
|
||||
|
||||
converter.hooks.chain("postNormalization", function(text) {
|
||||
return extra.doTransform(postNormalizationTransformations, text) + '\n';
|
||||
@ -194,7 +212,9 @@
|
||||
converter.hooks.chain("postConversion", function(text) {
|
||||
text = extra.doTransform(postConversionTransformations, text);
|
||||
// Clear state vars that may use unnecessary memory
|
||||
this.hashBlocks = [];
|
||||
extra.hashBlocks = [];
|
||||
extra.footnotes = {};
|
||||
extra.usedFootnotes = [];
|
||||
return text;
|
||||
});
|
||||
|
||||
@ -276,7 +296,7 @@
|
||||
// TODO: use sentinels. Should we just add/remove them in doConversion?
|
||||
// TODO: better matches for id / class attributes
|
||||
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");
|
||||
|
||||
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) *
|
||||
******************************************************************/
|
||||
|
Loading…
Reference in New Issue
Block a user