showdown/src/showdown.js

287 lines
6.2 KiB
JavaScript
Raw Normal View History

2015-01-16 05:21:33 +08:00
/**
* Created by Tivie on 06-01-2015.
*/
// Private properties
var showdown = {},
parsers = {},
2015-03-02 02:15:32 +08:00
extensions = {},
defaultOptions = {
omitExtraWLInCodeBlocks: false,
prefixHeaderId: false,
noHeaderId: false,
headerLevelStart: 1,
parseImgDimensions: false,
simplifiedAutoLink: false,
literalMidWordUnderscores: false,
strikethrough: false,
tables: false
},
globalOptions = JSON.parse(JSON.stringify(defaultOptions)); //clone default options out of laziness =P
2015-01-16 05:21:33 +08:00
/**
* helper namespace
* @type {{}}
*/
showdown.helper = {};
/**
* TODO LEGACY SUPPORT CODE
* @type {{}}
*/
2015-01-16 05:21:33 +08:00
showdown.extensions = {};
/**
* Set a global option
2015-03-02 02:15:32 +08:00
* @static
* @param {string} key
* @param {*} value
* @returns {showdown}
*/
2015-01-16 05:21:33 +08:00
showdown.setOption = function (key, value) {
2015-01-19 19:37:21 +08:00
'use strict';
globalOptions[key] = value;
return this;
2015-01-16 05:21:33 +08:00
};
/**
* Get a global option
2015-03-02 02:15:32 +08:00
* @static
* @param {string} key
* @returns {*}
*/
showdown.getOption = function (key) {
2015-01-19 19:37:21 +08:00
'use strict';
return globalOptions[key];
};
/**
* Get the global options
2015-03-02 02:15:32 +08:00
* @static
* @returns {{}}
*/
showdown.getOptions = function () {
2015-01-19 19:37:21 +08:00
'use strict';
return globalOptions;
};
/**
* Reset global options to the default values
* @static
*/
showdown.resetOptions = function () {
'use strict';
globalOptions = JSON.parse(JSON.stringify(defaultOptions));
};
/**
* Get the default options
* @static
* @returns {{}}
*/
showdown.getDefaultOptions = function () {
'use strict';
return defaultOptions;
};
2015-01-16 05:21:33 +08:00
/**
* Get or set a subParser
2015-01-16 05:21:33 +08:00
*
* subParser(name) - Get a registered subParser
* subParser(name, func) - Register a subParser
2015-03-02 02:15:32 +08:00
* @static
2015-01-16 05:21:33 +08:00
* @param {string} name
* @param {function} [func]
* @returns {*}
*/
showdown.subParser = function (name, func) {
2015-01-19 19:37:21 +08:00
'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!');
}
2015-01-16 05:21:33 +08:00
}
2015-01-19 19:37:21 +08:00
}
2015-01-16 05:21:33 +08:00
};
/**
* Gets or registers an extension
* @static
* @param {string} name
* @param {object|function=} ext
* @returns {*}
*/
2015-03-02 02:15:32 +08:00
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
2015-03-02 02:15:32 +08:00
if (showdown.helper.isUndefined(ext)) {
if (!extensions.hasOwnProperty(name)) {
throw Error('Extension named ' + name + ' is not registered!');
}
return extensions[name];
// Setter
2015-03-02 02:15:32 +08:00
} else {
// Expand extension if it's wrapped in a function
if (typeof ext === 'function') {
ext = ext();
}
2015-03-02 02:15:32 +08:00
// Ensure extension is an array
if (!showdown.helper.isArray(ext)) {
ext = [ext];
}
var validExtension = validate(ext, name);
2015-03-02 02:15:32 +08:00
if (validExtension.valid) {
extensions[name] = ext;
} else {
throw Error(validExtension.error);
}
2015-03-02 02:15:32 +08:00
}
};
2015-03-02 02:15:32 +08:00
/**
* Gets all extensions registered
* @returns {{}}
*/
showdown.getAllExtensions = function () {
2015-03-02 02:15:32 +08:00
'use strict';
return extensions;
};
2015-03-02 02:15:32 +08:00
/**
* Remove an extension
* @param {string} name
*/
showdown.removeExtension = function (name) {
'use strict';
delete extensions[name];
};
2015-03-02 02:15:32 +08:00
/**
* Removes all extensions
*/
showdown.resetExtensions = function () {
'use strict';
extensions = {};
};
2015-03-02 02:15:32 +08:00
2015-01-16 05:21:33 +08:00
/**
* Validate extension
* @param {array} extension
* @param {string} name
* @returns {{valid: boolean, error: string}}
2015-01-16 05:21:33 +08:00
*/
function validate(extension, name) {
2015-01-19 19:37:21 +08:00
'use strict';
var errMsg = (name) ? 'Error in ' + name + ' extension->' : 'Error in unnamed extension',
ret = {
valid: true,
error: ''
};
if (!showdown.helper.isArray(extension)) {
extension = [extension];
2015-01-19 19:37:21 +08:00
}
2015-01-16 05:21:33 +08:00
for (var i = 0; i < extension.length; ++i) {
var baseMsg = errMsg + 'sub-extension ' + i + ': ',
ext = extension[i];
if (typeof ext !== 'object') {
ret.valid = false;
ret.error = baseMsg + 'must be an object, but ' + typeof ext + ' given';
return ret;
}
if (!showdown.helper.isString(ext.type)) {
ret.valid = false;
ret.error = baseMsg + 'property "type" must be a string, but ' + typeof ext.type + ' given';
return ret;
}
var type = ext.type = ext.type.toLowerCase();
2015-03-02 02:15:32 +08:00
// normalize extension type
if (type === 'language') {
type = ext.type = 'lang';
}
2015-03-02 02:15:32 +08:00
if (type === 'html') {
type = ext.type = 'output';
}
2015-01-19 19:37:21 +08:00
if (type !== 'lang' && type !== 'output') {
ret.valid = false;
ret.error = baseMsg + 'type ' + type + ' is not recognized. Valid values: "lang" or "output"';
return ret;
2015-01-16 05:21:33 +08:00
}
if (ext.filter) {
if (typeof ext.filter !== 'function') {
ret.valid = false;
ret.error = baseMsg + '"filter" must be a function, but ' + typeof ext.filter + ' given';
return ret;
}
} else if (ext.regex) {
if (showdown.helper.isString(ext.regex)) {
ext.regex = new RegExp(ext.regex, 'g');
}
if (!ext.regex instanceof RegExp) {
ret.valid = false;
ret.error = baseMsg + '"regex" property must either be a string or a RegExp object, but ' +
typeof ext.regex + ' given';
return ret;
}
if (showdown.helper.isUndefined(ext.replace)) {
ret.valid = false;
ret.error = baseMsg + '"regex" extensions must implement a replace string or function';
return ret;
}
} else {
ret.valid = false;
ret.error = baseMsg + 'extensions must define either a "regex" property or a "filter" method';
return ret;
}
if (showdown.helper.isUndefined(ext.filter) && showdown.helper.isUndefined(ext.regex)) {
ret.valid = false;
ret.error = baseMsg + 'output extensions must define a filter property';
return ret;
2015-01-19 19:37:21 +08:00
}
}
return ret;
}
/**
* Validate extension
* @param {object} ext
* @returns {boolean}
*/
showdown.validateExtension = function (ext) {
'use strict';
var validateExtension = validate(ext, null);
if (!validateExtension.valid) {
console.warn(validateExtension.error);
return false;
}
return true;
2015-01-16 05:21:33 +08:00
};