/** * 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]; } } } else { throw Error('Converter expects the passed parameter to be an object, but ' + typeof converterOptions + ' was passed instead.'); } 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 }; }; };