|
@@ -64,7 +64,28 @@
|
|
//
|
|
//
|
|
// Showdown namespace
|
|
// Showdown namespace
|
|
//
|
|
//
|
|
-var Showdown = {};
|
|
|
|
|
|
+var Showdown = { extensions: {} };
|
|
|
|
+
|
|
|
|
+//
|
|
|
|
+// forEach
|
|
|
|
+//
|
|
|
|
+var forEach = Showdown.forEach = function(obj, callback) {
|
|
|
|
+ if (typeof obj.forEach === 'function') {
|
|
|
|
+ obj.forEach(callback);
|
|
|
|
+ } else {
|
|
|
|
+ var i, len = obj.length;
|
|
|
|
+ for (i = 0; i < len; i++) {
|
|
|
|
+ callback(obj[i], i, obj);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+//
|
|
|
|
+// Standard extension naming
|
|
|
|
+//
|
|
|
|
+var stdExtName = function(s) {
|
|
|
|
+ return s.replace(/[_-]||\s/g, '').toLowerCase();
|
|
|
|
+};
|
|
|
|
|
|
//
|
|
//
|
|
// converter
|
|
// converter
|
|
@@ -72,7 +93,7 @@ var Showdown = {};
|
|
// Wraps all "globals" so that the only thing
|
|
// Wraps all "globals" so that the only thing
|
|
// exposed is makeHtml().
|
|
// exposed is makeHtml().
|
|
//
|
|
//
|
|
-Showdown.converter = function() {
|
|
|
|
|
|
+Showdown.converter = function(converter_options) {
|
|
|
|
|
|
//
|
|
//
|
|
// Globals:
|
|
// Globals:
|
|
@@ -87,6 +108,68 @@ var g_html_blocks;
|
|
// (see _ProcessListItems() for details):
|
|
// (see _ProcessListItems() for details):
|
|
var g_list_level = 0;
|
|
var g_list_level = 0;
|
|
|
|
|
|
|
|
+// Global extensions
|
|
|
|
+var g_lang_extensions = [];
|
|
|
|
+var g_output_modifiers = [];
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+//
|
|
|
|
+// Automatic Extension Loading (node only):
|
|
|
|
+//
|
|
|
|
+
|
|
|
|
+if (typeof module !== 'undefind' && typeof exports !== 'undefined' && typeof require !== 'undefind') {
|
|
|
|
+ var fs = require('fs');
|
|
|
|
+
|
|
|
|
+ if (fs) {
|
|
|
|
+ // Search extensions folder
|
|
|
|
+ var extensions = fs.readdirSync((__dirname || '.')+'/extensions').filter(function(file){
|
|
|
|
+ return ~file.indexOf('.js');
|
|
|
|
+ }).map(function(file){
|
|
|
|
+ return file.replace(/\.js$/, '');
|
|
|
|
+ });
|
|
|
|
+ // Load extensions into Showdown namespace
|
|
|
|
+ extensions.forEach(function(ext){
|
|
|
|
+ var name = stdExtName(ext);
|
|
|
|
+ Showdown.extensions[name] = require('./extensions/' + ext);
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+//
|
|
|
|
+// Options:
|
|
|
|
+//
|
|
|
|
+
|
|
|
|
+// Parse extensinos options into separate arrays
|
|
|
|
+if (converter_options && converter_options.extensions) {
|
|
|
|
+
|
|
|
|
+ // Iterate over each plugin
|
|
|
|
+ converter_options.extensions.forEach(function(plugin){
|
|
|
|
+
|
|
|
|
+ // Assume it's a bundled plugin if a string is given
|
|
|
|
+ if (typeof plugin === 'string') {
|
|
|
|
+ plugin = Showdown.extensions[stdExtName(plugin)];
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (typeof plugin === 'function') {
|
|
|
|
+ // Iterate over each extension within that plugin
|
|
|
|
+ plugin(this).forEach(function(ext){
|
|
|
|
+ // Sort extensions by type
|
|
|
|
+ if (ext.type) {
|
|
|
|
+ if (ext.type === 'language' || ext.type === 'lang') {
|
|
|
|
+ g_lang_extensions.push(ext);
|
|
|
|
+ } else if (ext.type === 'output' || ext.type === 'html') {
|
|
|
|
+ g_output_modifiers.push(ext);
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ // Assume language extension
|
|
|
|
+ g_output_modifiers.push(ext);
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ } else {
|
|
|
|
+ throw "Extension '" + plugin + "' could not be loaded. It was either not found or is not a valid extension.";
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+}
|
|
|
|
|
|
this.makeHtml = function(text) {
|
|
this.makeHtml = function(text) {
|
|
//
|
|
//
|
|
@@ -100,9 +183,9 @@ this.makeHtml = function(text) {
|
|
// from other articles when generating a page which contains more than
|
|
// from other articles when generating a page which contains more than
|
|
// one article (e.g. an index page that shows the N most recent
|
|
// one article (e.g. an index page that shows the N most recent
|
|
// articles):
|
|
// articles):
|
|
- g_urls = new Array();
|
|
|
|
- g_titles = new Array();
|
|
|
|
- g_html_blocks = new Array();
|
|
|
|
|
|
+ g_urls = {};
|
|
|
|
+ g_titles = {};
|
|
|
|
+ g_html_blocks = [];
|
|
|
|
|
|
// attacklab: Replace ~ with ~T
|
|
// attacklab: Replace ~ with ~T
|
|
// This lets us use tilde as an escape char to avoid md5 hashes
|
|
// This lets us use tilde as an escape char to avoid md5 hashes
|
|
@@ -131,6 +214,11 @@ this.makeHtml = function(text) {
|
|
// contorted like /[ \t]*\n+/ .
|
|
// contorted like /[ \t]*\n+/ .
|
|
text = text.replace(/^[ \t]+$/mg,"");
|
|
text = text.replace(/^[ \t]+$/mg,"");
|
|
|
|
|
|
|
|
+ // Run language extensions
|
|
|
|
+ g_lang_extensions.forEach(function(x){
|
|
|
|
+ text = _ExecuteExtension(x, text);
|
|
|
|
+ });
|
|
|
|
+
|
|
// Handle github codeblocks prior to running HashHTML so that
|
|
// Handle github codeblocks prior to running HashHTML so that
|
|
// HTML contained within the codeblock gets escaped propertly
|
|
// HTML contained within the codeblock gets escaped propertly
|
|
text = _DoGithubCodeBlocks(text);
|
|
text = _DoGithubCodeBlocks(text);
|
|
@@ -151,10 +239,24 @@ this.makeHtml = function(text) {
|
|
// attacklab: Restore tildes
|
|
// attacklab: Restore tildes
|
|
text = text.replace(/~T/g,"~");
|
|
text = text.replace(/~T/g,"~");
|
|
|
|
|
|
|
|
+ // Run output modifiers
|
|
|
|
+ g_output_modifiers.forEach(function(x){
|
|
|
|
+ text = _ExecuteExtension(x, text);
|
|
|
|
+ });
|
|
|
|
+
|
|
return text;
|
|
return text;
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
+var _ExecuteExtension = function(ext, text) {
|
|
|
|
+ if (ext.regex) {
|
|
|
|
+ var re = new RegExp(ext.regex, 'g');
|
|
|
|
+ return text.replace(re, ext.replace);
|
|
|
|
+ } else if (ext.filter) {
|
|
|
|
+ return ext.filter(text);
|
|
|
|
+ }
|
|
|
|
+};
|
|
|
|
+
|
|
var _StripLinkDefinitions = function(text) {
|
|
var _StripLinkDefinitions = function(text) {
|
|
//
|
|
//
|
|
// Strips link definitions from text, stores the URLs and titles in
|
|
// Strips link definitions from text, stores the URLs and titles in
|
|
@@ -483,7 +585,7 @@ var _DoAnchors = function(text) {
|
|
)
|
|
)
|
|
/g,writeAnchorTag);
|
|
/g,writeAnchorTag);
|
|
*/
|
|
*/
|
|
- text = text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\]\([ \t]*()<?(.*?)>?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,writeAnchorTag);
|
|
|
|
|
|
+ text = text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\]\([ \t]*()<?(.*?(?:\(.*?\).*?)?)>?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,writeAnchorTag);
|
|
|
|
|
|
//
|
|
//
|
|
// Last, handle reference-style shortcuts: [link text]
|
|
// Last, handle reference-style shortcuts: [link text]
|
|
@@ -1089,7 +1191,7 @@ var _FormParagraphs = function(text) {
|
|
text = text.replace(/\n+$/g,"");
|
|
text = text.replace(/\n+$/g,"");
|
|
|
|
|
|
var grafs = text.split(/\n{2,}/g);
|
|
var grafs = text.split(/\n{2,}/g);
|
|
- var grafsOut = new Array();
|
|
|
|
|
|
+ var grafsOut = [];
|
|
|
|
|
|
//
|
|
//
|
|
// Wrap <p> tags.
|
|
// Wrap <p> tags.
|
|
@@ -1208,16 +1310,9 @@ var _EncodeEmailAddress = function(addr) {
|
|
// mailing list: <http://tinyurl.com/yu7ue>
|
|
// mailing list: <http://tinyurl.com/yu7ue>
|
|
//
|
|
//
|
|
|
|
|
|
- // attacklab: why can't javascript speak hex?
|
|
|
|
- function char2hex(ch) {
|
|
|
|
- var hexDigits = '0123456789ABCDEF';
|
|
|
|
- var dec = ch.charCodeAt(0);
|
|
|
|
- return(hexDigits.charAt(dec>>4) + hexDigits.charAt(dec&15));
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
var encode = [
|
|
var encode = [
|
|
function(ch){return "&#"+ch.charCodeAt(0)+";";},
|
|
function(ch){return "&#"+ch.charCodeAt(0)+";";},
|
|
- function(ch){return "&#x"+char2hex(ch)+";";},
|
|
|
|
|
|
+ function(ch){return "&#x"+ch.charCodeAt(0).toString(16)+";";},
|
|
function(ch){return ch;}
|
|
function(ch){return ch;}
|
|
];
|
|
];
|
|
|
|
|
|
@@ -1337,5 +1432,15 @@ var escapeCharacters_callback = function(wholeMatch,m1) {
|
|
|
|
|
|
} // end of Showdown.converter
|
|
} // end of Showdown.converter
|
|
|
|
|
|
|
|
+
|
|
// export
|
|
// export
|
|
if (typeof module !== 'undefined') module.exports = Showdown;
|
|
if (typeof module !== 'undefined') module.exports = Showdown;
|
|
|
|
+
|
|
|
|
+// stolen from AMD branch of underscore
|
|
|
|
+// AMD define happens at the end for compatibility with AMD loaders
|
|
|
|
+// that don't enforce next-turn semantics on modules.
|
|
|
|
+if (typeof define === 'function' && define.amd) {
|
|
|
|
+ define('showdown', function() {
|
|
|
|
+ return Showdown;
|
|
|
|
+ });
|
|
|
|
+}
|