mirror of
https://github.com/showdownjs/showdown.git
synced 2024-03-22 13:30:55 +08:00
269 lines
6.3 KiB
JavaScript
269 lines
6.3 KiB
JavaScript
|
/**
|
||
|
* Created by Estevao on 31-05-2015.
|
||
|
*/
|
||
|
|
||
|
/**
|
||
|
* Showdown Converter class
|
||
|
* @class
|
||
|
* @param {object} [converterOptions]
|
||
|
* @returns {
|
||
|
* {makeHtml: Function},
|
||
|
* {setOption: Function},
|
||
|
* {getOption: Function},
|
||
|
* {getOptions: Function}
|
||
|
* }
|
||
|
*/
|
||
|
showdown.Converter = function (converterOptions) {
|
||
|
'use strict';
|
||
|
|
||
|
var
|
||
|
/**
|
||
|
* Options used by this converter
|
||
|
* @private
|
||
|
* @type {{}}
|
||
|
*/
|
||
|
options = {
|
||
|
omitExtraWLInCodeBlocks: false,
|
||
|
prefixHeaderId: false
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Language extensions used by this converter
|
||
|
* @private
|
||
|
* @type {Array}
|
||
|
*/
|
||
|
langExtensions = [],
|
||
|
|
||
|
/**
|
||
|
* Output modifiers extensions used by this converter
|
||
|
* @private
|
||
|
* @type {Array}
|
||
|
*/
|
||
|
outputModifiers = [],
|
||
|
|
||
|
/**
|
||
|
* The parser Order
|
||
|
* @private
|
||
|
* @type {string[]}
|
||
|
*/
|
||
|
parserOrder = [
|
||
|
'githubCodeBlocks',
|
||
|
'hashHTMLBlocks',
|
||
|
'stripLinkDefinitions',
|
||
|
'blockGamut',
|
||
|
'unescapeSpecialChars'
|
||
|
];
|
||
|
|
||
|
_constructor();
|
||
|
|
||
|
/**
|
||
|
* Converter constructor
|
||
|
* @private
|
||
|
*/
|
||
|
function _constructor() {
|
||
|
converterOptions = converterOptions || {};
|
||
|
|
||
|
for (var gOpt in globalOptions) {
|
||
|
if (globalOptions.hasOwnProperty(gOpt)) {
|
||
|
options[gOpt] = globalOptions[gOpt];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Merge options
|
||
|
if (typeof converterOptions === 'object') {
|
||
|
for (var opt in converterOptions) {
|
||
|
if (converterOptions.hasOwnProperty(opt)) {
|
||
|
options[opt] = converterOptions[opt];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (options.extensions) {
|
||
|
showdown.helper.forEach(options.extensions, _parseExtension);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Parse extension
|
||
|
* @param {*} ext
|
||
|
* @private
|
||
|
*/
|
||
|
function _parseExtension(ext) {
|
||
|
|
||
|
// If it's a string, the extension was previously loaded
|
||
|
if (showdown.helper.isString(ext)) {
|
||
|
ext = showdown.helper.stdExtName(ext);
|
||
|
|
||
|
// TODO LEGACY SUPPORT CODE
|
||
|
if (!showdown.helper.isUndefined(showdown.extensions[ext]) && showdown.extensions[ext]) {
|
||
|
console.warn(ext + ' is an old extension that uses a deprecated loading method.' +
|
||
|
'Please inform the developer that the extension should be updated!');
|
||
|
ext = showdown.extensions[ext];
|
||
|
// END LEGACY SUPPORT CODE
|
||
|
|
||
|
} else if (!showdown.helper.isUndefined(extensions[ext])) {
|
||
|
ext = extensions[ext];
|
||
|
|
||
|
} else {
|
||
|
throw Error('Extension "' + ext + '" could not be loaded. It was either not found or is not a valid extension.');
|
||
|
}
|
||
|
} else if (typeof ext === 'function') {
|
||
|
ext = ext();
|
||
|
}
|
||
|
|
||
|
if (!showdown.validateExtension(ext)) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
switch (ext.type) {
|
||
|
case 'lang':
|
||
|
langExtensions.push(ext);
|
||
|
break;
|
||
|
|
||
|
case 'output':
|
||
|
outputModifiers.push(ext);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
// should never reach here
|
||
|
throw Error('Extension loader error: Type unrecognized!!!');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Converts a markdown string into HTML
|
||
|
* @param {string} text
|
||
|
* @returns {*}
|
||
|
*/
|
||
|
this.makeHtml = function (text) {
|
||
|
//check if text is not falsy
|
||
|
if (!text) {
|
||
|
return text;
|
||
|
}
|
||
|
|
||
|
var globals = {
|
||
|
gHtmlBlocks: [],
|
||
|
gUrls: {},
|
||
|
gTitles: {},
|
||
|
gListLevel: 0,
|
||
|
hashLinkCounts: {},
|
||
|
langExtensions: langExtensions,
|
||
|
outputModifiers: outputModifiers,
|
||
|
converter: this
|
||
|
};
|
||
|
|
||
|
// attacklab: Replace ~ with ~T
|
||
|
// This lets us use tilde as an escape char to avoid md5 hashes
|
||
|
// The choice of character is arbitrary; anything that isn't
|
||
|
// magic in Markdown will work.
|
||
|
text = text.replace(/~/g, '~T');
|
||
|
|
||
|
// attacklab: Replace $ with ~D
|
||
|
// RegExp interprets $ as a special character
|
||
|
// when it's in a replacement string
|
||
|
text = text.replace(/\$/g, '~D');
|
||
|
|
||
|
// Standardize line endings
|
||
|
text = text.replace(/\r\n/g, '\n'); // DOS to Unix
|
||
|
text = text.replace(/\r/g, '\n'); // Mac to Unix
|
||
|
|
||
|
// Make sure text begins and ends with a couple of newlines:
|
||
|
text = '\n\n' + text + '\n\n';
|
||
|
|
||
|
// detab
|
||
|
text = showdown.subParser('detab')(text, options, globals);
|
||
|
|
||
|
// stripBlankLines
|
||
|
text = showdown.subParser('stripBlankLines')(text, options, globals);
|
||
|
|
||
|
//run languageExtensions
|
||
|
showdown.helper.forEach(langExtensions, function (ext) {
|
||
|
text = showdown.subParser('runExtension')(ext, text, options, globals);
|
||
|
});
|
||
|
|
||
|
// Run all registered parsers
|
||
|
for (var i = 0; i < parserOrder.length; ++i) {
|
||
|
var name = parserOrder[i];
|
||
|
text = parsers[name](text, options, globals);
|
||
|
}
|
||
|
|
||
|
// attacklab: Restore dollar signs
|
||
|
text = text.replace(/~D/g, '$$');
|
||
|
|
||
|
// attacklab: Restore tildes
|
||
|
text = text.replace(/~T/g, '~');
|
||
|
|
||
|
// Run output modifiers
|
||
|
showdown.helper.forEach(outputModifiers, function (ext) {
|
||
|
text = showdown.subParser('runExtension')(ext, text, options, globals);
|
||
|
});
|
||
|
text = parsers.outputModifiers(text, options, globals);
|
||
|
|
||
|
return text;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Set an option of this Converter instance
|
||
|
* @param {string} key
|
||
|
* @param {*} value
|
||
|
*/
|
||
|
this.setOption = function (key, value) {
|
||
|
options[key] = value;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Get the option of this Converter instance
|
||
|
* @param {string} key
|
||
|
* @returns {*}
|
||
|
*/
|
||
|
this.getOption = function (key) {
|
||
|
return options[key];
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Get the options of this Converter instance
|
||
|
* @returns {{}}
|
||
|
*/
|
||
|
this.getOptions = function () {
|
||
|
return options;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Add extension to THIS converter
|
||
|
* @param {{}} extension
|
||
|
*/
|
||
|
this.addExtension = function (extension) {
|
||
|
_parseExtension(extension);
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Remove an extension from THIS converter
|
||
|
* @param {{}} extension
|
||
|
*/
|
||
|
this.removeExtension = function (extension) {
|
||
|
for (var i = 0; i < langExtensions.length; ++i) {
|
||
|
if (langExtensions[i] === extension) {
|
||
|
langExtensions[i].splice(i, 1);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
for (var ii = 0; ii < outputModifiers.length; ++i) {
|
||
|
if (outputModifiers[ii] === extension) {
|
||
|
outputModifiers[ii].splice(i, 1);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Get all extension of THIS converter
|
||
|
* @returns {{language: Array, output: Array}}
|
||
|
*/
|
||
|
this.getAllExtensions = function () {
|
||
|
return {
|
||
|
language: langExtensions,
|
||
|
output: outputModifiers
|
||
|
};
|
||
|
};
|
||
|
};
|