Moved footnotes into Markdown Extra extension

This commit is contained in:
benweet 2013-06-25 22:43:00 +01:00
parent 4ae1aac96b
commit 416fbb68f2
4 changed files with 93 additions and 105 deletions

View File

@ -15,7 +15,6 @@ define([
"extensions/workingIndicator",
"extensions/notifications",
"extensions/markdownExtra",
"extensions/markdownFootnotes",
"extensions/toc",
"extensions/mathJax",
"extensions/emailConverter",

View File

@ -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">&#8617;</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;
});

View File

@ -9,7 +9,7 @@ define([
mathJax.settingsBlock = mathJaxSettingsBlockHTML;
mathJax.defaultConfig = {
tex: "{}",
tex2jax: '{ inlineMath: [["$","$"],["\\\\(","\\\\)"]], displayMath: [["$$","$$"],["\\[","\\]"]], processEscapes: true }'
tex2jax: '{ inlineMath: [["$","$"],["\\\\\\\\(","\\\\\\\\)"]], displayMath: [["$$","$$"],["\\\\[","\\\\]"]], processEscapes: true }'
};
mathJax.onLoadSettings = function() {

View File

@ -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">&#8617;</a></li>\n\n';
}
text += '</ol>\n</div>';
return text;
};
/******************************************************************
* Fenced Code Blocks (gfm) *
******************************************************************/