Converter = function (converterOptions) {
'use strict';
var
/**
* Options used by this converter
* @private
* @type {{}}
*/
options = {},
/**
* Language extensions used by this converter
* @private
* @type {Array}
*/
langExtensions = [],
/**
* Output modifiers extensions used by this converter
* @private
* @type {Array}
*/
outputModifiers = [],
/**
* Event listeners
* @private
* @type {{}}
*/
listeners = {},
/**
* The flavor set in this converter
*/
setConvFlavor = setFlavor;
_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
* @param {string} [name='']
* @private
*/
function _parseExtension (ext, name) {
name = name || null;
// If it's a string, the extension was previously loaded
if (showdown.helper.isString(ext)) {
ext = showdown.helper.stdExtName(ext);
name = ext;
// LEGACY_SUPPORT CODE
if (showdown.extensions[ext]) {
console.warn('DEPRECATION WARNING: ' + ext + ' is an old extension that uses a deprecated loading method.' +
'Please inform the developer that the extension should be updated!');
legacyExtensionLoading(showdown.extensions[ext], ext);
return;
// 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.');
}
}
if (typeof ext === 'function') {
ext = ext();
}
if (!showdown.helper.isArray(ext)) {
ext = [ext];
}
var validExt = validate(ext, name);
if (!validExt.valid) {
throw Error(validExt.error);
}
for (var i = 0; i < ext.length; ++i) {
switch (ext[i].type) {
case 'lang':
langExtensions.push(ext[i]);
break;
case 'output':
outputModifiers.push(ext[i]);
break;
}
if (ext[i].hasOwnProperty('listeners')) {
for (var ln in ext[i].listeners) {
if (ext[i].listeners.hasOwnProperty(ln)) {
listen(ln, ext[i].listeners[ln]);
}
}
}
}
}
/**
* LEGACY_SUPPORT
* @param {*} ext
* @param {string} name
*/
function legacyExtensionLoading (ext, name) {
if (typeof ext === 'function') {
ext = ext(new showdown.Converter());
}
if (!showdown.helper.isArray(ext)) {
ext = [ext];
}
var valid = validate(ext, name);
if (!valid.valid) {
throw Error(valid.error);
}
for (var i = 0; i < ext.length; ++i) {
switch (ext[i].type) {
case 'lang':
langExtensions.push(ext[i]);
break;
case 'output':
outputModifiers.push(ext[i]);
break;
default:// should never reach here
throw Error('Extension loader error: Type unrecognized!!!');
}
}
}
/**
* Listen to an event
* @param {string} name
* @param {function} callback
*/
function listen (name, callback) {
if (!showdown.helper.isString(name)) {
throw Error('Invalid argument in converter.listen ......
## Quick Example
### Node
```js
var showdown = require('showdown'),
converter = new showdown.Converter(),
text = '#hello, markdown!',
html = converter.makeHtml(text);
```
### Browser
```js
...extension = function (name, ext) {
'use strict';
if (!showdown.helper.isString(name)) {
throw Error('Extension \'name\' must be a string');
}
name = showdown.helper.stdExtName(name);
// Getter
if (showdown.helper.isUndefined(ext)) {
if (!extensions.hasOwnProperty(name)) {
throw Error('Extension named ' + name + ' is not registered!');
}
return extensions[name];
// Setter
} else {
// Expand extension if it's wrapped in a function
if (typeof ext === 'function') {
ext = ext();
}
// Ensure extension is an array
if (!showdown.helper.isArray(ext)) {
ext = [ext];
}
var validExtension = validate(ext, name);
if (validExtension.valid) {
extensions[name] = ext;
} else {
throw Error(validExtension.error);
}
}
}...
* **image dimensions:** add support for setting image dimensions within markdown syntax ([af82c2b6](http://github.com/showdownjs
/showdown/commit/af82c2b6), closes [#143](http://github.com/showdownjs/showdown/issues/143))
* **noHeaderId:** add option to suppress automatic generation of ids in headers ([7ac893e9](http://github.com/showdownjs/showdown
/commit/7ac893e9))
* **showdown.getDefaultOptions:** add method to retrieve default global options keypairs ([2de53a7d](http://github.com/showdownjs
/showdown/commit/2de53a7d))
### Breaking Changes
* Deprecates `showdown.extensions` property. To migrate, extensions should use the new method `showdown.extension(<ext name>, <extension>)` to register.
For more information on the new extension loading mechanism, please check the wiki pages.
([4ebd0caa](http://github.com/showdownjs/showdown/commit/4ebd0caa))
<a name"1.0.2"></a>
## [1.0.2](https://github.com/showdownjs/showdown/compare/1.0.1...1.0.2) (2015-05-28)
...getAllExtensions = function () {
'use strict';
return extensions;
}n/a
getDefaultOptions = function (simple) {
'use strict';
return getDefaultOpts(simple);
}...
var thisConverterSpecificOptions = converter.getOptions();
```
### Retrieve the default options
You can get showdown's default options with:
```js
var defaultOptions = showdown.getDefaultOptions();
```
### Valid Options
* **omitExtraWLInCodeBlocks**: (boolean) [default false] Omit the trailing newline in a code block. Ex:
This:
...getFlavor = function () {
'use strict';
return setFlavor;
}n/a
getFlavorOptions = function (name) {
'use strict';
if (flavor.hasOwnProperty(name)) {
return flavor[name];
}
}...
// write the output
messenger.printMsg('Writing data to ' + writeMode + '...');
write(html, append);
messenger.okExit();
function parseOptions (flavor) {
var options = {},
flavorOpts = showdown.getFlavorOptions(flavor) || {};
// if flavor is not undefined, let's tell the user we're loading that preset
if (flavor) {
messenger.printMsg('Loading ' + flavor + ' flavor.');
}
for (var opt in argv) {
...getOption = function (key) {
'use strict';
return globalOptions[key];
}...
Showdown provides 2 methods (both local and global) to retrieve previous set options.
#### getOption()
```js
// Global
var myOption = showdown.getOption('optionKey');
//Local
var myOption = converter.getOption('optionKey');
```
#### getOptions()
...getOptions = function () {
'use strict';
return globalOptions;
}...
var myOption = converter.getOption('optionKey');
```
#### getOptions()
```js
// Global
var showdownGlobalOptions = showdown.getOptions();
//Local
var thisConverterSpecificOptions = converter.getOptions();
```
### Retrieve the default options
...removeExtension = function (name) {
'use strict';
delete extensions[name];
}n/a
resetExtensions = function () {
'use strict';
extensions = {};
}n/a
resetOptions = function () {
'use strict';
globalOptions = getDefaultOpts(true);
}n/a
setFlavor = function (name) {
'use strict';
if (!flavor.hasOwnProperty(name)) {
throw Error(name + ' flavor was not found');
}
var preset = flavor[name];
setFlavor = name;
for (var option in preset) {
if (preset.hasOwnProperty(option)) {
globalOptions[option] = preset[option];
}
}
}...
* original - original markdown flavor as in [John Gruber's spec](https://daringfireball.net/projects/markdown/)
* vanilla - showdown base flavor (as from v1.3.1)
* github - GFM (GitHub Flavored Markdown)
### Global
```javascript
showdown.setFlavor('github');
```
### Instance
```javascript
converter.setFlavor('github');
```
...setOption = function (key, value) {
'use strict';
globalOptions[key] = value;
return this;
}...
<ul><li>three</li></ul>
</li>
</ul>
```
To migrate either fix source md files or activate the option `disableForced4SpacesIndentedSublists`:
```md
showdown.setOption('disableForced4SpacesIndentedSublists', true);
```
<a name="1.4.4"></a>
## [1.4.4](https://github.com/showdownjs/showdown/compare/1.4.3...1.4.4) (2016-11-02)
...subParser = function (name, func) {
'use strict';
if (showdown.helper.isString(name)) {
if (typeof func !== 'undefined') {
parsers[name] = func;
} else {
if (parsers.hasOwnProperty(name)) {
return parsers[name];
} else {
throw Error('SubParser named ' + name + ' not registered!');
}
}
}
}...
text = rTrimInputText(text);
}
// 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);
/**
* Strip any lines consisting only of spaces and tabs.
* This makes subsequent regexs easier to write, because we can
* match consecutive blank lines with /\n+/ instead of something
* contorted like /[ \t]*\n+/
*/
...validateExtension = function (ext) {
'use strict';
var validateExtension = validate(ext, null);
if (!validateExtension.valid) {
console.warn(validateExtension.error);
return false;
}
return true;
}n/a
encodeEmailAddress = function (mail) {
'use strict';
var encode = [
function (ch) {
return '&#' + ch.charCodeAt(0) + ';';
},
function (ch) {
return '&#x' + ch.charCodeAt(0).toString(16) + ';';
},
function (ch) {
return ch;
}
];
mail = mail.replace(/./g, function (ch) {
if (ch === '@') {
// this *must* be encoded. I insist.
ch = encode[Math.floor(Math.random() * 2)](ch);
} else {
var r = Math.random();
// roughly 10% raw, 45% hex, 45% dec
ch = (
r > 0.9 ? encode[2](ch) : r > 0.45 ? encode[1](ch) : encode[0](ch)
);
}
return ch;
});
return mail;
}...
}
function replaceMail (wholeMatch, b, mail) {
var href = 'mailto:';
b = b || '';
mail = showdown.subParser('unescapeSpecialChars')(mail, options, globals);
if (options.encodeEmails) {
href = showdown.helper.encodeEmailAddress(href + mail);
mail = showdown.helper.encodeEmailAddress(mail);
} else {
href = href + mail;
}
return b + '<a href="' + href + '">' + mail + '</a>';
}
...escapeCharacters = function (text, charsToEscape, afterBackslash) {
'use strict';
// First we have to escape the escape characters so that
// we can build a character class out of them
var regexString = '([' + charsToEscape.replace(/([\[\]\\])/g, '\\$1') + '])';
if (afterBackslash) {
regexString = '\\\\' + regexString;
}
var regex = new RegExp(regexString, 'g');
text = text.replace(regex, escapeCharactersCallback);
return text;
}...
url = '';
} else {
return wholeMatch;
}
}
}
//url = showdown.helper.escapeCharacters(url, '*_', false); // replaced line
to improve performance
url = url.replace(showdown.helper.regexes.asteriskAndDash, showdown.helper.escapeCharactersCallback);
var result = '<a href="' + url + '"';
if (title !== '' && title !== null) {
title = title.replace(/"/g, '"');
//title = showdown.helper.escapeCharacters(title, '*_', false); // replaced line to improve performance
...function escapeCharactersCallback(wholeMatch, m1) {
'use strict';
var charCodeToEscape = m1.charCodeAt(0);
return '¨E' + charCodeToEscape + 'E';
}n/a
forEach = function (obj, callback) {
'use strict';
// check if obj is defined
if (showdown.helper.isUndefined(obj)) {
throw new Error('obj param is required');
}
if (showdown.helper.isUndefined(callback)) {
throw new Error('callback param is required');
}
if (!showdown.helper.isFunction(callback)) {
throw new Error('callback param must be a function/closure');
}
if (typeof obj.forEach === 'function') {
obj.forEach(callback);
} else if (showdown.helper.isArray(obj)) {
for (var i = 0; i < obj.length; i++) {
callback(obj[i], i, obj);
}
} else if (typeof (obj) === 'object') {
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) {
callback(obj[prop], prop, obj);
}
}
} else {
throw new Error('obj does not seem to be an array or an iterable object');
}
}...
}
if (!showdown.helper.isFunction(callback)) {
throw new Error('callback param must be a function/closure');
}
if (typeof obj.forEach === 'function') {
obj.forEach(callback);
} else if (showdown.helper.isArray(obj)) {
for (var i = 0; i < obj.length; i++) {
callback(obj[i], i, obj);
}
} else if (typeof (obj) === 'object') {
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) {
...isArray = function (a) {
'use strict';
return a.constructor === Array;
}...
} else {
// Expand extension if it's wrapped in a function
if (typeof ext === 'function') {
ext = ext();
}
// Ensure extension is an array
if (!showdown.helper.isArray(ext)) {
ext = [ext];
}
var validExtension = validate(ext, name);
if (validExtension.valid) {
extensions[name] = ext;
...isFunction = function (a) {
'use strict';
var getType = {};
return a && getType.toString.call(a) === '[object Function]';
}...
throw new Error('obj param is required');
}
if (showdown.helper.isUndefined(callback)) {
throw new Error('callback param is required');
}
if (!showdown.helper.isFunction(callback)) {
throw new Error('callback param must be a function/closure');
}
if (typeof obj.forEach === 'function') {
obj.forEach(callback);
} else if (showdown.helper.isArray(obj)) {
for (var i = 0; i < obj.length; i++) {
...isString = function (a) {
'use strict';
return (typeof a === 'string' || a instanceof String);
}...
* @static
* @param {string} name
* @param {function} [func]
* @returns {*}
*/
showdown.subParser = function (name, func) {
'use strict';
if (showdown.helper.isString(name)) {
if (typeof func !== 'undefined') {
parsers[name] = func;
} else {
if (parsers.hasOwnProperty(name)) {
return parsers[name];
} else {
throw Error('SubParser named ' + name + ' not registered!');
...isUndefined = function (value) {
'use strict';
return typeof value === 'undefined';
}...
if (!showdown.helper.isString(name)) {
throw Error('Extension \'name\' must be a string');
}
name = showdown.helper.stdExtName(name);
// Getter
if (showdown.helper.isUndefined(ext)) {
if (!extensions.hasOwnProperty(name)) {
throw Error('Extension named ' + name + ' is not registered!');
}
return extensions[name];
// Setter
} else {
...matchRecursiveRegExp = function (str, left, right, flags) {
'use strict';
var matchPos = rgxFindMatchPos (str, left, right, flags),
results = [];
for (var i = 0; i < matchPos.length; ++i) {
results.push([
str.slice(matchPos[i].wholeMatch.start, matchPos[i].wholeMatch.end),
str.slice(matchPos[i].match.start, matchPos[i].match.end),
str.slice(matchPos[i].left.start, matchPos[i].left.end),
str.slice(matchPos[i].right.start, matchPos[i].right.end)
]);
}
return results;
}...
});
// Hash self closing tags without />
text = text.replace(/<[^>]+?>/gi, function (wm) {
return hashHTMLSpan(wm);
});
/*showdown.helper.matchRecursiveRegExp(text, '<code\\b[^>]*>'
;, '</code>', 'gi');*/
text = globals.converter._dispatch('hashHTMLSpans.after', text, options, globals);
return text;
});
/**
* Unhash HTML spans
...replaceRecursiveRegExp = function (str, replacement, left, right, flags) {
'use strict';
if (!showdown.helper.isFunction(replacement)) {
var repStr = replacement;
replacement = function () {
return repStr;
};
}
var matchPos = rgxFindMatchPos(str, left, right, flags),
finalStr = str,
lng = matchPos.length;
if (lng > 0) {
var bits = [];
if (matchPos[0].wholeMatch.start !== 0) {
bits.push(str.slice(0, matchPos[0].wholeMatch.start));
}
for (var i = 0; i < lng; ++i) {
bits.push(
replacement(
str.slice(matchPos[i].wholeMatch.start, matchPos[i].wholeMatch.end),
str.slice(matchPos[i].match.start, matchPos[i].match.end),
str.slice(matchPos[i].left.start, matchPos[i].left.end),
str.slice(matchPos[i].right.start, matchPos[i].right.end)
)
);
if (i < lng - 1) {
bits.push(str.slice(matchPos[i].wholeMatch.end, matchPos[i + 1].wholeMatch.start));
}
}
if (matchPos[lng - 1].wholeMatch.end < str.length) {
bits.push(str.slice(matchPos[lng - 1].wholeMatch.end));
}
finalStr = bits.join('');
}
return finalStr;
}...
var repFunc = function (wholeMatch, match, left, right) {
var codeblock = left + showdown.subParser('encodeCode')(match, options, globals) + right;
return '¨C' + (globals.gHtmlSpans.push(codeblock) - 1) + 'C';
};
// Hash naked <code>
text = showdown.helper.replaceRecursiveRegExp(text, repFunc, '<code\\b[^>
;]*>', '</code>', 'gim');
text = globals.converter._dispatch('hashCodeTags.after', text, options, globals);
return text;
});
showdown.subParser('hashElement', function (text, options, globals) {
'use strict';
...stdExtName = function (s) {
'use strict';
return s.replace(/[_?*+\/\\.^-]/g, '').replace(/\s/g, '').toLowerCase();
}...
showdown.extension = function (name, ext) {
'use strict';
if (!showdown.helper.isString(name)) {
throw Error('Extension \'name\' must be a string');
}
name = showdown.helper.stdExtName(name);
// Getter
if (showdown.helper.isUndefined(ext)) {
if (!extensions.hasOwnProperty(name)) {
throw Error('Extension named ' + name + ' is not registered!');
}
return extensions[name];
...