diff --git a/js/extensionMgr.js b/js/extensionMgr.js
index 1dfad50e..c789c9e9 100644
--- a/js/extensionMgr.js
+++ b/js/extensionMgr.js
@@ -15,7 +15,6 @@ define([
"extensions/workingIndicator",
"extensions/notifications",
"extensions/markdownExtra",
- "extensions/markdownFootnotes",
"extensions/toc",
"extensions/mathJax",
"extensions/emailConverter",
diff --git a/js/extensions/markdownFootnotes.js b/js/extensions/markdownFootnotes.js
deleted file mode 100644
index c8777094..00000000
--- a/js/extensions/markdownFootnotes.js
+++ /dev/null
@@ -1,100 +0,0 @@
-define([
- "underscore",
- "utils",
- "classes/Extension",
-], function(_, utils, Extension) {
-
- var markdownFootnotes = new Extension("markdownFootnotes", "Markdown Footnotes", true);
- markdownFootnotes.settingsBlock = '
Adds support for Markdown footnotes.
';
-
- 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 '';
- });
-
- 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';
- 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;
-});
\ No newline at end of file
diff --git a/js/extensions/mathJax.js b/js/extensions/mathJax.js
index 337771b4..7f8a3534 100644
--- a/js/extensions/mathJax.js
+++ b/js/extensions/mathJax.js
@@ -9,7 +9,7 @@ define([
mathJax.settingsBlock = mathJaxSettingsBlockHTML;
mathJax.defaultConfig = {
tex: "{}",
- tex2jax: '{ inlineMath: [["$","$"],["\\\\(","\\\\)"]], displayMath: [["$$","$$"],["\\[","\\]"]], processEscapes: true }'
+ tex2jax: '{ inlineMath: [["$","$"],["\\\\\\\\(","\\\\\\\\)"]], displayMath: [["$$","$$"],["\\\\[","\\\\]"]], processEscapes: true }'
};
mathJax.onLoadSettings = function() {
diff --git a/js/libs/Markdown.Extra.js b/js/libs/Markdown.Extra.js
index 990dc158..c85683e3 100644
--- a/js/libs/Markdown.Extra.js
+++ b/js/libs/Markdown.Extra.js
@@ -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 '';
+ });
+
+ 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';
+ return text;
+ };
+
+
/******************************************************************
* Fenced Code Blocks (gfm) *
******************************************************************/