|  | @@ -0,0 +1,2518 @@
 | 
	
		
			
				|  |  | +function getDefaultOpts(simple) {
 | 
	
		
			
				|  |  | +  'use strict';
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  var defaultOptions = {
 | 
	
		
			
				|  |  | +    omitExtraWLInCodeBlocks: {
 | 
	
		
			
				|  |  | +      defaultValue: false,
 | 
	
		
			
				|  |  | +      describe: 'Omit the default extra whiteline added to code blocks',
 | 
	
		
			
				|  |  | +      type: 'boolean'
 | 
	
		
			
				|  |  | +    },
 | 
	
		
			
				|  |  | +    noHeaderId: {
 | 
	
		
			
				|  |  | +      defaultValue: false,
 | 
	
		
			
				|  |  | +      describe: 'Turn on/off generated header id',
 | 
	
		
			
				|  |  | +      type: 'boolean'
 | 
	
		
			
				|  |  | +    },
 | 
	
		
			
				|  |  | +    prefixHeaderId: {
 | 
	
		
			
				|  |  | +      defaultValue: false,
 | 
	
		
			
				|  |  | +      describe: 'Specify a prefix to generated header ids',
 | 
	
		
			
				|  |  | +      type: 'string'
 | 
	
		
			
				|  |  | +    },
 | 
	
		
			
				|  |  | +    headerLevelStart: {
 | 
	
		
			
				|  |  | +      defaultValue: false,
 | 
	
		
			
				|  |  | +      describe: 'The header blocks level start',
 | 
	
		
			
				|  |  | +      type: 'integer'
 | 
	
		
			
				|  |  | +    },
 | 
	
		
			
				|  |  | +    parseImgDimensions: {
 | 
	
		
			
				|  |  | +      defaultValue: false,
 | 
	
		
			
				|  |  | +      describe: 'Turn on/off image dimension parsing',
 | 
	
		
			
				|  |  | +      type: 'boolean'
 | 
	
		
			
				|  |  | +    },
 | 
	
		
			
				|  |  | +    simplifiedAutoLink: {
 | 
	
		
			
				|  |  | +      defaultValue: false,
 | 
	
		
			
				|  |  | +      describe: 'Turn on/off GFM autolink style',
 | 
	
		
			
				|  |  | +      type: 'boolean'
 | 
	
		
			
				|  |  | +    },
 | 
	
		
			
				|  |  | +    literalMidWordUnderscores: {
 | 
	
		
			
				|  |  | +      defaultValue: false,
 | 
	
		
			
				|  |  | +      describe: 'Parse midword underscores as literal underscores',
 | 
	
		
			
				|  |  | +      type: 'boolean'
 | 
	
		
			
				|  |  | +    },
 | 
	
		
			
				|  |  | +    strikethrough: {
 | 
	
		
			
				|  |  | +      defaultValue: false,
 | 
	
		
			
				|  |  | +      describe: 'Turn on/off strikethrough support',
 | 
	
		
			
				|  |  | +      type: 'boolean'
 | 
	
		
			
				|  |  | +    },
 | 
	
		
			
				|  |  | +    tables: {
 | 
	
		
			
				|  |  | +      defaultValue: false,
 | 
	
		
			
				|  |  | +      describe: 'Turn on/off tables support',
 | 
	
		
			
				|  |  | +      type: 'boolean'
 | 
	
		
			
				|  |  | +    },
 | 
	
		
			
				|  |  | +    tablesHeaderId: {
 | 
	
		
			
				|  |  | +      defaultValue: false,
 | 
	
		
			
				|  |  | +      describe: 'Add an id to table headers',
 | 
	
		
			
				|  |  | +      type: 'boolean'
 | 
	
		
			
				|  |  | +    },
 | 
	
		
			
				|  |  | +    ghCodeBlocks: {
 | 
	
		
			
				|  |  | +      defaultValue: true,
 | 
	
		
			
				|  |  | +      describe: 'Turn on/off GFM fenced code blocks support',
 | 
	
		
			
				|  |  | +      type: 'boolean'
 | 
	
		
			
				|  |  | +    },
 | 
	
		
			
				|  |  | +    tasklists: {
 | 
	
		
			
				|  |  | +      defaultValue: false,
 | 
	
		
			
				|  |  | +      describe: 'Turn on/off GFM tasklist support',
 | 
	
		
			
				|  |  | +      type: 'boolean'
 | 
	
		
			
				|  |  | +    },
 | 
	
		
			
				|  |  | +    smoothLivePreview: {
 | 
	
		
			
				|  |  | +      defaultValue: false,
 | 
	
		
			
				|  |  | +      describe: 'Prevents weird effects in live previews due to incomplete input',
 | 
	
		
			
				|  |  | +      type: 'boolean'
 | 
	
		
			
				|  |  | +    },
 | 
	
		
			
				|  |  | +    smartIndentationFix: {
 | 
	
		
			
				|  |  | +      defaultValue: false,
 | 
	
		
			
				|  |  | +      description: 'Tries to smartly fix identation in es6 strings',
 | 
	
		
			
				|  |  | +      type: 'boolean'
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  };
 | 
	
		
			
				|  |  | +  if (simple === false) {
 | 
	
		
			
				|  |  | +    return JSON.parse(JSON.stringify(defaultOptions));
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  var ret = {};
 | 
	
		
			
				|  |  | +  for (var opt in defaultOptions) {
 | 
	
		
			
				|  |  | +    if (defaultOptions.hasOwnProperty(opt)) {
 | 
	
		
			
				|  |  | +      ret[opt] = defaultOptions[opt].defaultValue;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  return ret;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * Created by Tivie on 06-01-2015.
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// Private properties
 | 
	
		
			
				|  |  | +var showdown = {},
 | 
	
		
			
				|  |  | +    parsers = {},
 | 
	
		
			
				|  |  | +    extensions = {},
 | 
	
		
			
				|  |  | +    globalOptions = getDefaultOpts(true),
 | 
	
		
			
				|  |  | +    flavor = {
 | 
	
		
			
				|  |  | +      github: {
 | 
	
		
			
				|  |  | +        omitExtraWLInCodeBlocks:   true,
 | 
	
		
			
				|  |  | +        prefixHeaderId:            'user-content-',
 | 
	
		
			
				|  |  | +        simplifiedAutoLink:        true,
 | 
	
		
			
				|  |  | +        literalMidWordUnderscores: true,
 | 
	
		
			
				|  |  | +        strikethrough:             true,
 | 
	
		
			
				|  |  | +        tables:                    true,
 | 
	
		
			
				|  |  | +        tablesHeaderId:            true,
 | 
	
		
			
				|  |  | +        ghCodeBlocks:              true,
 | 
	
		
			
				|  |  | +        tasklists:                 true
 | 
	
		
			
				|  |  | +      },
 | 
	
		
			
				|  |  | +      vanilla: getDefaultOpts(true)
 | 
	
		
			
				|  |  | +    };
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * helper namespace
 | 
	
		
			
				|  |  | + * @type {{}}
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +showdown.helper = {};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * TODO LEGACY SUPPORT CODE
 | 
	
		
			
				|  |  | + * @type {{}}
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +showdown.extensions = {};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * Set a global option
 | 
	
		
			
				|  |  | + * @static
 | 
	
		
			
				|  |  | + * @param {string} key
 | 
	
		
			
				|  |  | + * @param {*} value
 | 
	
		
			
				|  |  | + * @returns {showdown}
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +showdown.setOption = function (key, value) {
 | 
	
		
			
				|  |  | +  'use strict';
 | 
	
		
			
				|  |  | +  globalOptions[key] = value;
 | 
	
		
			
				|  |  | +  return this;
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * Get a global option
 | 
	
		
			
				|  |  | + * @static
 | 
	
		
			
				|  |  | + * @param {string} key
 | 
	
		
			
				|  |  | + * @returns {*}
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +showdown.getOption = function (key) {
 | 
	
		
			
				|  |  | +  'use strict';
 | 
	
		
			
				|  |  | +  return globalOptions[key];
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * Get the global options
 | 
	
		
			
				|  |  | + * @static
 | 
	
		
			
				|  |  | + * @returns {{}}
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +showdown.getOptions = function () {
 | 
	
		
			
				|  |  | +  'use strict';
 | 
	
		
			
				|  |  | +  return globalOptions;
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * Reset global options to the default values
 | 
	
		
			
				|  |  | + * @static
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +showdown.resetOptions = function () {
 | 
	
		
			
				|  |  | +  'use strict';
 | 
	
		
			
				|  |  | +  globalOptions = getDefaultOpts(true);
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * Set the flavor showdown should use as default
 | 
	
		
			
				|  |  | + * @param {string} name
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +showdown.setFlavor = function (name) {
 | 
	
		
			
				|  |  | +  'use strict';
 | 
	
		
			
				|  |  | +  if (flavor.hasOwnProperty(name)) {
 | 
	
		
			
				|  |  | +    var preset = flavor[name];
 | 
	
		
			
				|  |  | +    for (var option in preset) {
 | 
	
		
			
				|  |  | +      if (preset.hasOwnProperty(option)) {
 | 
	
		
			
				|  |  | +        globalOptions[option] = preset[option];
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * Get the default options
 | 
	
		
			
				|  |  | + * @static
 | 
	
		
			
				|  |  | + * @param {boolean} [simple=true]
 | 
	
		
			
				|  |  | + * @returns {{}}
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +showdown.getDefaultOptions = function (simple) {
 | 
	
		
			
				|  |  | +  'use strict';
 | 
	
		
			
				|  |  | +  return getDefaultOpts(simple);
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * Get or set a subParser
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * subParser(name)       - Get a registered subParser
 | 
	
		
			
				|  |  | + * subParser(name, func) - Register a subParser
 | 
	
		
			
				|  |  | + * @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!');
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * Gets or registers an extension
 | 
	
		
			
				|  |  | + * @static
 | 
	
		
			
				|  |  | + * @param {string} name
 | 
	
		
			
				|  |  | + * @param {object|function=} ext
 | 
	
		
			
				|  |  | + * @returns {*}
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +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];
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    // 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);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * Gets all extensions registered
 | 
	
		
			
				|  |  | + * @returns {{}}
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +showdown.getAllExtensions = function () {
 | 
	
		
			
				|  |  | +  'use strict';
 | 
	
		
			
				|  |  | +  return extensions;
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * Remove an extension
 | 
	
		
			
				|  |  | + * @param {string} name
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +showdown.removeExtension = function (name) {
 | 
	
		
			
				|  |  | +  'use strict';
 | 
	
		
			
				|  |  | +  delete extensions[name];
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * Removes all extensions
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +showdown.resetExtensions = function () {
 | 
	
		
			
				|  |  | +  'use strict';
 | 
	
		
			
				|  |  | +  extensions = {};
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * Validate extension
 | 
	
		
			
				|  |  | + * @param {array} extension
 | 
	
		
			
				|  |  | + * @param {string} name
 | 
	
		
			
				|  |  | + * @returns {{valid: boolean, error: string}}
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +function validate(extension, name) {
 | 
	
		
			
				|  |  | +  'use strict';
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  var errMsg = (name) ? 'Error in ' + name + ' extension->' : 'Error in unnamed extension',
 | 
	
		
			
				|  |  | +    ret = {
 | 
	
		
			
				|  |  | +      valid: true,
 | 
	
		
			
				|  |  | +      error: ''
 | 
	
		
			
				|  |  | +    };
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  if (!showdown.helper.isArray(extension)) {
 | 
	
		
			
				|  |  | +    extension = [extension];
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  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();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    // normalize extension type
 | 
	
		
			
				|  |  | +    if (type === 'language') {
 | 
	
		
			
				|  |  | +      type = ext.type = 'lang';
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if (type === 'html') {
 | 
	
		
			
				|  |  | +      type = ext.type = 'output';
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if (type !== 'lang' && type !== 'output' && type !== 'listener') {
 | 
	
		
			
				|  |  | +      ret.valid = false;
 | 
	
		
			
				|  |  | +      ret.error = baseMsg + 'type ' + type + ' is not recognized. Valid values: "lang/language", "output/html" or "listener"';
 | 
	
		
			
				|  |  | +      return ret;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if (type === 'listener') {
 | 
	
		
			
				|  |  | +      if (showdown.helper.isUndefined(ext.listeners)) {
 | 
	
		
			
				|  |  | +        ret.valid = false;
 | 
	
		
			
				|  |  | +        ret.error = baseMsg + '. Extensions of type "listener" must have a property called "listeners"';
 | 
	
		
			
				|  |  | +        return ret;
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    } else {
 | 
	
		
			
				|  |  | +      if (showdown.helper.isUndefined(ext.filter) && showdown.helper.isUndefined(ext.regex)) {
 | 
	
		
			
				|  |  | +        ret.valid = false;
 | 
	
		
			
				|  |  | +        ret.error = baseMsg + type + ' extensions must define either a "regex" property or a "filter" method';
 | 
	
		
			
				|  |  | +        return ret;
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if (ext.listeners) {
 | 
	
		
			
				|  |  | +      if (typeof ext.listeners !== 'object') {
 | 
	
		
			
				|  |  | +        ret.valid = false;
 | 
	
		
			
				|  |  | +        ret.error = baseMsg + '"listeners" property must be an object but ' + typeof ext.listeners + ' given';
 | 
	
		
			
				|  |  | +        return ret;
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      for (var ln in ext.listeners) {
 | 
	
		
			
				|  |  | +        if (ext.listeners.hasOwnProperty(ln)) {
 | 
	
		
			
				|  |  | +          if (typeof ext.listeners[ln] !== 'function') {
 | 
	
		
			
				|  |  | +            ret.valid = false;
 | 
	
		
			
				|  |  | +            ret.error = baseMsg + '"listeners" property must be an hash of [event name]: [callback]. listeners.' + ln +
 | 
	
		
			
				|  |  | +              ' must be a function but ' + typeof ext.listeners[ln] + ' given';
 | 
	
		
			
				|  |  | +            return ret;
 | 
	
		
			
				|  |  | +          }
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    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;
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  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;
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * showdownjs helper functions
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +if (!showdown.hasOwnProperty('helper')) {
 | 
	
		
			
				|  |  | +  showdown.helper = {};
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * Check if var is string
 | 
	
		
			
				|  |  | + * @static
 | 
	
		
			
				|  |  | + * @param {string} a
 | 
	
		
			
				|  |  | + * @returns {boolean}
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +showdown.helper.isString = function isString(a) {
 | 
	
		
			
				|  |  | +  'use strict';
 | 
	
		
			
				|  |  | +  return (typeof a === 'string' || a instanceof String);
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * Check if var is a function
 | 
	
		
			
				|  |  | + * @static
 | 
	
		
			
				|  |  | + * @param {string} a
 | 
	
		
			
				|  |  | + * @returns {boolean}
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +showdown.helper.isFunction = function isFunction(a) {
 | 
	
		
			
				|  |  | +  'use strict';
 | 
	
		
			
				|  |  | +  var getType = {};
 | 
	
		
			
				|  |  | +  return a && getType.toString.call(a) === '[object Function]';
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * ForEach helper function
 | 
	
		
			
				|  |  | + * @static
 | 
	
		
			
				|  |  | + * @param {*} obj
 | 
	
		
			
				|  |  | + * @param {function} callback
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +showdown.helper.forEach = function forEach(obj, callback) {
 | 
	
		
			
				|  |  | +  'use strict';
 | 
	
		
			
				|  |  | +  if (typeof obj.forEach === 'function') {
 | 
	
		
			
				|  |  | +    obj.forEach(callback);
 | 
	
		
			
				|  |  | +  } else {
 | 
	
		
			
				|  |  | +    for (var i = 0; i < obj.length; i++) {
 | 
	
		
			
				|  |  | +      callback(obj[i], i, obj);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * isArray helper function
 | 
	
		
			
				|  |  | + * @static
 | 
	
		
			
				|  |  | + * @param {*} a
 | 
	
		
			
				|  |  | + * @returns {boolean}
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +showdown.helper.isArray = function isArray(a) {
 | 
	
		
			
				|  |  | +  'use strict';
 | 
	
		
			
				|  |  | +  return a.constructor === Array;
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * Check if value is undefined
 | 
	
		
			
				|  |  | + * @static
 | 
	
		
			
				|  |  | + * @param {*} value The value to check.
 | 
	
		
			
				|  |  | + * @returns {boolean} Returns `true` if `value` is `undefined`, else `false`.
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +showdown.helper.isUndefined = function isUndefined(value) {
 | 
	
		
			
				|  |  | +  'use strict';
 | 
	
		
			
				|  |  | +  return typeof value === 'undefined';
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * Standardidize extension name
 | 
	
		
			
				|  |  | + * @static
 | 
	
		
			
				|  |  | + * @param {string} s extension name
 | 
	
		
			
				|  |  | + * @returns {string}
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +showdown.helper.stdExtName = function (s) {
 | 
	
		
			
				|  |  | +  'use strict';
 | 
	
		
			
				|  |  | +  return s.replace(/[_-]||\s/g, '').toLowerCase();
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +function escapeCharactersCallback(wholeMatch, m1) {
 | 
	
		
			
				|  |  | +  'use strict';
 | 
	
		
			
				|  |  | +  var charCodeToEscape = m1.charCodeAt(0);
 | 
	
		
			
				|  |  | +  return '~E' + charCodeToEscape + 'E';
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * Callback used to escape characters when passing through String.replace
 | 
	
		
			
				|  |  | + * @static
 | 
	
		
			
				|  |  | + * @param {string} wholeMatch
 | 
	
		
			
				|  |  | + * @param {string} m1
 | 
	
		
			
				|  |  | + * @returns {string}
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +showdown.helper.escapeCharactersCallback = escapeCharactersCallback;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * Escape characters in a string
 | 
	
		
			
				|  |  | + * @static
 | 
	
		
			
				|  |  | + * @param {string} text
 | 
	
		
			
				|  |  | + * @param {string} charsToEscape
 | 
	
		
			
				|  |  | + * @param {boolean} afterBackslash
 | 
	
		
			
				|  |  | + * @returns {XML|string|void|*}
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +showdown.helper.escapeCharacters = function escapeCharacters(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;
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +var rgxFindMatchPos = function (str, left, right, flags) {
 | 
	
		
			
				|  |  | +  'use strict';
 | 
	
		
			
				|  |  | +  var f = flags || '',
 | 
	
		
			
				|  |  | +    g = f.indexOf('g') > -1,
 | 
	
		
			
				|  |  | +    x = new RegExp(left + '|' + right, 'g' + f.replace(/g/g, '')),
 | 
	
		
			
				|  |  | +    l = new RegExp(left, f.replace(/g/g, '')),
 | 
	
		
			
				|  |  | +    pos = [],
 | 
	
		
			
				|  |  | +    t, s, m, start, end;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  do {
 | 
	
		
			
				|  |  | +    t = 0;
 | 
	
		
			
				|  |  | +    while ((m = x.exec(str))) {
 | 
	
		
			
				|  |  | +      if (l.test(m[0])) {
 | 
	
		
			
				|  |  | +        if (!(t++)) {
 | 
	
		
			
				|  |  | +          s = x.lastIndex;
 | 
	
		
			
				|  |  | +          start = s - m[0].length;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +      } else if (t) {
 | 
	
		
			
				|  |  | +        if (!--t) {
 | 
	
		
			
				|  |  | +          end = m.index + m[0].length;
 | 
	
		
			
				|  |  | +          var obj = {
 | 
	
		
			
				|  |  | +            left: {start: start, end: s},
 | 
	
		
			
				|  |  | +            match: {start: s, end: m.index},
 | 
	
		
			
				|  |  | +            right: {start: m.index, end: end},
 | 
	
		
			
				|  |  | +            wholeMatch: {start: start, end: end}
 | 
	
		
			
				|  |  | +          };
 | 
	
		
			
				|  |  | +          pos.push(obj);
 | 
	
		
			
				|  |  | +          if (!g) {
 | 
	
		
			
				|  |  | +            return pos;
 | 
	
		
			
				|  |  | +          }
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  } while (t && (x.lastIndex = s));
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  return pos;
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * matchRecursiveRegExp
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * (c) 2007 Steven Levithan <stevenlevithan.com>
 | 
	
		
			
				|  |  | + * MIT License
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * Accepts a string to search, a left and right format delimiter
 | 
	
		
			
				|  |  | + * as regex patterns, and optional regex flags. Returns an array
 | 
	
		
			
				|  |  | + * of matches, allowing nested instances of left/right delimiters.
 | 
	
		
			
				|  |  | + * Use the "g" flag to return all matches, otherwise only the
 | 
	
		
			
				|  |  | + * first is returned. Be careful to ensure that the left and
 | 
	
		
			
				|  |  | + * right format delimiters produce mutually exclusive matches.
 | 
	
		
			
				|  |  | + * Backreferences are not supported within the right delimiter
 | 
	
		
			
				|  |  | + * due to how it is internally combined with the left delimiter.
 | 
	
		
			
				|  |  | + * When matching strings whose format delimiters are unbalanced
 | 
	
		
			
				|  |  | + * to the left or right, the output is intentionally as a
 | 
	
		
			
				|  |  | + * conventional regex library with recursion support would
 | 
	
		
			
				|  |  | + * produce, e.g. "<<x>" and "<x>>" both produce ["x"] when using
 | 
	
		
			
				|  |  | + * "<" and ">" as the delimiters (both strings contain a single,
 | 
	
		
			
				|  |  | + * balanced instance of "<x>").
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * examples:
 | 
	
		
			
				|  |  | + * matchRecursiveRegExp("test", "\\(", "\\)")
 | 
	
		
			
				|  |  | + * returns: []
 | 
	
		
			
				|  |  | + * matchRecursiveRegExp("<t<<e>><s>>t<>", "<", ">", "g")
 | 
	
		
			
				|  |  | + * returns: ["t<<e>><s>", ""]
 | 
	
		
			
				|  |  | + * matchRecursiveRegExp("<div id=\"x\">test</div>", "<div\\b[^>]*>", "</div>", "gi")
 | 
	
		
			
				|  |  | + * returns: ["test"]
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +showdown.helper.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;
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * @param {string} str
 | 
	
		
			
				|  |  | + * @param {string|function} replacement
 | 
	
		
			
				|  |  | + * @param {string} left
 | 
	
		
			
				|  |  | + * @param {string} right
 | 
	
		
			
				|  |  | + * @param {string} flags
 | 
	
		
			
				|  |  | + * @returns {string}
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +showdown.helper.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;
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * POLYFILLS
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +if (showdown.helper.isUndefined(console)) {
 | 
	
		
			
				|  |  | +  console = {
 | 
	
		
			
				|  |  | +    warn: function (msg) {
 | 
	
		
			
				|  |  | +      'use strict';
 | 
	
		
			
				|  |  | +      alert(msg);
 | 
	
		
			
				|  |  | +    },
 | 
	
		
			
				|  |  | +    log: function (msg) {
 | 
	
		
			
				|  |  | +      'use strict';
 | 
	
		
			
				|  |  | +      alert(msg);
 | 
	
		
			
				|  |  | +    },
 | 
	
		
			
				|  |  | +    error: function (msg) {
 | 
	
		
			
				|  |  | +      'use strict';
 | 
	
		
			
				|  |  | +      throw msg;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  };
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * Created by Estevao on 31-05-2015.
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * Showdown Converter class
 | 
	
		
			
				|  |  | + * @class
 | 
	
		
			
				|  |  | + * @param {object} [converterOptions]
 | 
	
		
			
				|  |  | + * @returns {Converter}
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +showdown.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 = {};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  _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() method: name must be a string, but ' + typeof name + ' given');
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if (typeof callback !== 'function') {
 | 
	
		
			
				|  |  | +      throw Error('Invalid argument in converter.listen() method: callback must be a function, but ' + typeof callback + ' given');
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if (!listeners.hasOwnProperty(name)) {
 | 
	
		
			
				|  |  | +      listeners[name] = [];
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    listeners[name].push(callback);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  function rTrimInputText(text) {
 | 
	
		
			
				|  |  | +    var rsp = text.match(/^\s*/)[0].length,
 | 
	
		
			
				|  |  | +        rgx = new RegExp('^\\s{0,' + rsp + '}', 'gm');
 | 
	
		
			
				|  |  | +    return text.replace(rgx, '');
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /**
 | 
	
		
			
				|  |  | +   * Dispatch an event
 | 
	
		
			
				|  |  | +   * @private
 | 
	
		
			
				|  |  | +   * @param {string} evtName Event name
 | 
	
		
			
				|  |  | +   * @param {string} text Text
 | 
	
		
			
				|  |  | +   * @param {{}} options Converter Options
 | 
	
		
			
				|  |  | +   * @param {{}} globals
 | 
	
		
			
				|  |  | +   * @returns {string}
 | 
	
		
			
				|  |  | +   */
 | 
	
		
			
				|  |  | +  this._dispatch = function dispatch (evtName, text, options, globals) {
 | 
	
		
			
				|  |  | +    if (listeners.hasOwnProperty(evtName)) {
 | 
	
		
			
				|  |  | +      for (var ei = 0; ei < listeners[evtName].length; ++ei) {
 | 
	
		
			
				|  |  | +        var nText = listeners[evtName][ei](evtName, text, this, options, globals);
 | 
	
		
			
				|  |  | +        if (nText && typeof nText !== 'undefined') {
 | 
	
		
			
				|  |  | +          text = nText;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    return text;
 | 
	
		
			
				|  |  | +  };
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /**
 | 
	
		
			
				|  |  | +   * Listen to an event
 | 
	
		
			
				|  |  | +   * @param {string} name
 | 
	
		
			
				|  |  | +   * @param {function} callback
 | 
	
		
			
				|  |  | +   * @returns {showdown.Converter}
 | 
	
		
			
				|  |  | +   */
 | 
	
		
			
				|  |  | +  this.listen = function (name, callback) {
 | 
	
		
			
				|  |  | +    listen(name, callback);
 | 
	
		
			
				|  |  | +    return this;
 | 
	
		
			
				|  |  | +  };
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /**
 | 
	
		
			
				|  |  | +   * 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:     [],
 | 
	
		
			
				|  |  | +      gHtmlMdBlocks:   [],
 | 
	
		
			
				|  |  | +      gHtmlSpans:      [],
 | 
	
		
			
				|  |  | +      gUrls:           {},
 | 
	
		
			
				|  |  | +      gTitles:         {},
 | 
	
		
			
				|  |  | +      gDimensions:     {},
 | 
	
		
			
				|  |  | +      gListLevel:      0,
 | 
	
		
			
				|  |  | +      hashLinkCounts:  {},
 | 
	
		
			
				|  |  | +      langExtensions:  langExtensions,
 | 
	
		
			
				|  |  | +      outputModifiers: outputModifiers,
 | 
	
		
			
				|  |  | +      converter:       this,
 | 
	
		
			
				|  |  | +      ghCodeBlocks:    []
 | 
	
		
			
				|  |  | +    };
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    // 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
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if (options.smartIndentationFix) {
 | 
	
		
			
				|  |  | +      text = rTrimInputText(text);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    // Make sure text begins and ends with a couple of newlines:
 | 
	
		
			
				|  |  | +    //text = '\n\n' + text + '\n\n';
 | 
	
		
			
				|  |  | +    text = text;
 | 
	
		
			
				|  |  | +    // 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 the sub parsers
 | 
	
		
			
				|  |  | +    text = showdown.subParser('hashPreCodeTags')(text, options, globals);
 | 
	
		
			
				|  |  | +    text = showdown.subParser('githubCodeBlocks')(text, options, globals);
 | 
	
		
			
				|  |  | +    text = showdown.subParser('hashHTMLBlocks')(text, options, globals);
 | 
	
		
			
				|  |  | +    text = showdown.subParser('hashHTMLSpans')(text, options, globals);
 | 
	
		
			
				|  |  | +    text = showdown.subParser('stripLinkDefinitions')(text, options, globals);
 | 
	
		
			
				|  |  | +    text = showdown.subParser('blockGamut')(text, options, globals);
 | 
	
		
			
				|  |  | +    text = showdown.subParser('unhashHTMLSpans')(text, options, globals);
 | 
	
		
			
				|  |  | +    text = showdown.subParser('unescapeSpecialChars')(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);
 | 
	
		
			
				|  |  | +    });
 | 
	
		
			
				|  |  | +    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
 | 
	
		
			
				|  |  | +   * @param {string} [name=null]
 | 
	
		
			
				|  |  | +   */
 | 
	
		
			
				|  |  | +  this.addExtension = function (extension, name) {
 | 
	
		
			
				|  |  | +    name = name || null;
 | 
	
		
			
				|  |  | +    _parseExtension(extension, name);
 | 
	
		
			
				|  |  | +  };
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /**
 | 
	
		
			
				|  |  | +   * Use a global registered extension with THIS converter
 | 
	
		
			
				|  |  | +   * @param {string} extensionName Name of the previously registered extension
 | 
	
		
			
				|  |  | +   */
 | 
	
		
			
				|  |  | +  this.useExtension = function (extensionName) {
 | 
	
		
			
				|  |  | +    _parseExtension(extensionName);
 | 
	
		
			
				|  |  | +  };
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /**
 | 
	
		
			
				|  |  | +   * Set the flavor THIS converter should use
 | 
	
		
			
				|  |  | +   * @param {string} name
 | 
	
		
			
				|  |  | +   */
 | 
	
		
			
				|  |  | +  this.setFlavor = function (name) {
 | 
	
		
			
				|  |  | +    if (flavor.hasOwnProperty(name)) {
 | 
	
		
			
				|  |  | +      var preset = flavor[name];
 | 
	
		
			
				|  |  | +      for (var option in preset) {
 | 
	
		
			
				|  |  | +        if (preset.hasOwnProperty(option)) {
 | 
	
		
			
				|  |  | +          options[option] = preset[option];
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  };
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /**
 | 
	
		
			
				|  |  | +   * Remove an extension from THIS converter.
 | 
	
		
			
				|  |  | +   * Note: This is a costly operation. It's better to initialize a new converter
 | 
	
		
			
				|  |  | +   * and specify the extensions you wish to use
 | 
	
		
			
				|  |  | +   * @param {Array} extension
 | 
	
		
			
				|  |  | +   */
 | 
	
		
			
				|  |  | +  this.removeExtension = function (extension) {
 | 
	
		
			
				|  |  | +    if (!showdown.helper.isArray(extension)) {
 | 
	
		
			
				|  |  | +      extension = [extension];
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    for (var a = 0; a < extension.length; ++a) {
 | 
	
		
			
				|  |  | +      var ext = extension[a];
 | 
	
		
			
				|  |  | +      for (var i = 0; i < langExtensions.length; ++i) {
 | 
	
		
			
				|  |  | +        if (langExtensions[i] === ext) {
 | 
	
		
			
				|  |  | +          langExtensions[i].splice(i, 1);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      for (var ii = 0; ii < outputModifiers.length; ++i) {
 | 
	
		
			
				|  |  | +        if (outputModifiers[ii] === ext) {
 | 
	
		
			
				|  |  | +          outputModifiers[ii].splice(i, 1);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  };
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /**
 | 
	
		
			
				|  |  | +   * Get all extension of THIS converter
 | 
	
		
			
				|  |  | +   * @returns {{language: Array, output: Array}}
 | 
	
		
			
				|  |  | +   */
 | 
	
		
			
				|  |  | +  this.getAllExtensions = function () {
 | 
	
		
			
				|  |  | +    return {
 | 
	
		
			
				|  |  | +      language: langExtensions,
 | 
	
		
			
				|  |  | +      output: outputModifiers
 | 
	
		
			
				|  |  | +    };
 | 
	
		
			
				|  |  | +  };
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * Turn Markdown link shortcuts into XHTML <a> tags.
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +showdown.subParser('anchors', function (text, options, globals) {
 | 
	
		
			
				|  |  | +  'use strict';
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  text = globals.converter._dispatch('anchors.before', text, options, globals);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  var writeAnchorTag = function (wholeMatch, m1, m2, m3, m4, m5, m6, m7) {
 | 
	
		
			
				|  |  | +    if (showdown.helper.isUndefined(m7)) {
 | 
	
		
			
				|  |  | +      m7 = '';
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    wholeMatch = m1;
 | 
	
		
			
				|  |  | +    var linkText = m2,
 | 
	
		
			
				|  |  | +        linkId = m3.toLowerCase(),
 | 
	
		
			
				|  |  | +        url = m4,
 | 
	
		
			
				|  |  | +        title = m7;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if (!url) {
 | 
	
		
			
				|  |  | +      if (!linkId) {
 | 
	
		
			
				|  |  | +        // lower-case and turn embedded newlines into spaces
 | 
	
		
			
				|  |  | +        linkId = linkText.toLowerCase().replace(/ ?\n/g, ' ');
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      url = '#' + linkId;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +      if (!showdown.helper.isUndefined(globals.gUrls[linkId])) {
 | 
	
		
			
				|  |  | +        url = globals.gUrls[linkId];
 | 
	
		
			
				|  |  | +        if (!showdown.helper.isUndefined(globals.gTitles[linkId])) {
 | 
	
		
			
				|  |  | +          title = globals.gTitles[linkId];
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +      } else {
 | 
	
		
			
				|  |  | +        if (wholeMatch.search(/\(\s*\)$/m) > -1) {
 | 
	
		
			
				|  |  | +          // Special case for explicit empty url
 | 
	
		
			
				|  |  | +          url = '';
 | 
	
		
			
				|  |  | +        } else {
 | 
	
		
			
				|  |  | +          return wholeMatch;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    url = showdown.helper.escapeCharacters(url, '*_', false);
 | 
	
		
			
				|  |  | +    var result = '<a href="' + url + '"';
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if (title !== '' && title !== null) {
 | 
	
		
			
				|  |  | +      title = title.replace(/"/g, '"');
 | 
	
		
			
				|  |  | +      title = showdown.helper.escapeCharacters(title, '*_', false);
 | 
	
		
			
				|  |  | +      result += ' title="' + title + '"';
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    result += '>' + linkText + '</a>';
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    return result;
 | 
	
		
			
				|  |  | +  };
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // First, handle reference-style links: [link text] [id]
 | 
	
		
			
				|  |  | +  /*
 | 
	
		
			
				|  |  | +   text = text.replace(/
 | 
	
		
			
				|  |  | +   (							// wrap whole match in $1
 | 
	
		
			
				|  |  | +   \[
 | 
	
		
			
				|  |  | +   (
 | 
	
		
			
				|  |  | +   (?:
 | 
	
		
			
				|  |  | +   \[[^\]]*\]		// allow brackets nested one level
 | 
	
		
			
				|  |  | +   |
 | 
	
		
			
				|  |  | +   [^\[]			// or anything else
 | 
	
		
			
				|  |  | +   )*
 | 
	
		
			
				|  |  | +   )
 | 
	
		
			
				|  |  | +   \]
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +   [ ]?					// one optional space
 | 
	
		
			
				|  |  | +   (?:\n[ ]*)?				// one optional newline followed by spaces
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +   \[
 | 
	
		
			
				|  |  | +   (.*?)					// id = $3
 | 
	
		
			
				|  |  | +   \]
 | 
	
		
			
				|  |  | +   )()()()()					// pad remaining backreferences
 | 
	
		
			
				|  |  | +   /g,_DoAnchors_callback);
 | 
	
		
			
				|  |  | +   */
 | 
	
		
			
				|  |  | +  text = text.replace(/(\[((?:\[[^\]]*]|[^\[\]])*)][ ]?(?:\n[ ]*)?\[(.*?)])()()()()/g, writeAnchorTag);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  //
 | 
	
		
			
				|  |  | +  // Next, inline-style links: [link text](url "optional title")
 | 
	
		
			
				|  |  | +  //
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /*
 | 
	
		
			
				|  |  | +   text = text.replace(/
 | 
	
		
			
				|  |  | +   (						// wrap whole match in $1
 | 
	
		
			
				|  |  | +   \[
 | 
	
		
			
				|  |  | +   (
 | 
	
		
			
				|  |  | +   (?:
 | 
	
		
			
				|  |  | +   \[[^\]]*\]	// allow brackets nested one level
 | 
	
		
			
				|  |  | +   |
 | 
	
		
			
				|  |  | +   [^\[\]]			// or anything else
 | 
	
		
			
				|  |  | +   )
 | 
	
		
			
				|  |  | +   )
 | 
	
		
			
				|  |  | +   \]
 | 
	
		
			
				|  |  | +   \(						// literal paren
 | 
	
		
			
				|  |  | +   [ \t]*
 | 
	
		
			
				|  |  | +   ()						// no id, so leave $3 empty
 | 
	
		
			
				|  |  | +   <?(.*?)>?				// href = $4
 | 
	
		
			
				|  |  | +   [ \t]*
 | 
	
		
			
				|  |  | +   (						// $5
 | 
	
		
			
				|  |  | +   (['"])				// quote char = $6
 | 
	
		
			
				|  |  | +   (.*?)				// Title = $7
 | 
	
		
			
				|  |  | +   \6					// matching quote
 | 
	
		
			
				|  |  | +   [ \t]*				// ignore any spaces/tabs between closing quote and )
 | 
	
		
			
				|  |  | +   )?						// title is optional
 | 
	
		
			
				|  |  | +   \)
 | 
	
		
			
				|  |  | +   )
 | 
	
		
			
				|  |  | +   /g,writeAnchorTag);
 | 
	
		
			
				|  |  | +   */
 | 
	
		
			
				|  |  | +  text = text.replace(/(\[((?:\[[^\]]*]|[^\[\]])*)]\([ \t]*()<?(.*?(?:\(.*?\).*?)?)>?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,
 | 
	
		
			
				|  |  | +                      writeAnchorTag);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  //
 | 
	
		
			
				|  |  | +  // Last, handle reference-style shortcuts: [link text]
 | 
	
		
			
				|  |  | +  // These must come last in case you've also got [link test][1]
 | 
	
		
			
				|  |  | +  // or [link test](/foo)
 | 
	
		
			
				|  |  | +  //
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /*
 | 
	
		
			
				|  |  | +   text = text.replace(/
 | 
	
		
			
				|  |  | +   (                // wrap whole match in $1
 | 
	
		
			
				|  |  | +   \[
 | 
	
		
			
				|  |  | +   ([^\[\]]+)       // link text = $2; can't contain '[' or ']'
 | 
	
		
			
				|  |  | +   \]
 | 
	
		
			
				|  |  | +   )()()()()()      // pad rest of backreferences
 | 
	
		
			
				|  |  | +   /g, writeAnchorTag);
 | 
	
		
			
				|  |  | +   */
 | 
	
		
			
				|  |  | +  text = text.replace(/(\[([^\[\]]+)])()()()()()/g, writeAnchorTag);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  text = globals.converter._dispatch('anchors.after', text, options, globals);
 | 
	
		
			
				|  |  | +  return text;
 | 
	
		
			
				|  |  | +});
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +showdown.subParser('autoLinks', function (text, options, globals) {
 | 
	
		
			
				|  |  | +  'use strict';
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  text = globals.converter._dispatch('autoLinks.before', text, options, globals);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  var simpleURLRegex  = /\b(((https?|ftp|dict):\/\/|www\.)[^'">\s]+\.[^'">\s]+)(?=\s|$)(?!["<>])/gi,
 | 
	
		
			
				|  |  | +      delimUrlRegex   = /<(((https?|ftp|dict):\/\/|www\.)[^'">\s]+)>/gi,
 | 
	
		
			
				|  |  | +      simpleMailRegex = /(?:^|[ \n\t])([A-Za-z0-9!#$%&'*+-/=?^_`\{|}~\.]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)(?:$|[ \n\t])/gi,
 | 
	
		
			
				|  |  | +      delimMailRegex  = /<(?:mailto:)?([-.\w]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  text = text.replace(delimUrlRegex, replaceLink);
 | 
	
		
			
				|  |  | +  text = text.replace(delimMailRegex, replaceMail);
 | 
	
		
			
				|  |  | +  // simpleURLRegex  = /\b(((https?|ftp|dict):\/\/|www\.)[-.+~:?#@!$&'()*,;=[\]\w]+)\b/gi,
 | 
	
		
			
				|  |  | +  // Email addresses: <address@domain.foo>
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  if (options.simplifiedAutoLink) {
 | 
	
		
			
				|  |  | +    text = text.replace(simpleURLRegex, replaceLink);
 | 
	
		
			
				|  |  | +    text = text.replace(simpleMailRegex, replaceMail);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  function replaceLink(wm, link) {
 | 
	
		
			
				|  |  | +    var lnkTxt = link;
 | 
	
		
			
				|  |  | +    if (/^www\./i.test(link)) {
 | 
	
		
			
				|  |  | +      link = link.replace(/^www\./i, 'http://www.');
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    return '<a href="' + link + '">' + lnkTxt + '</a>';
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  function replaceMail(wholeMatch, m1) {
 | 
	
		
			
				|  |  | +    var unescapedStr = showdown.subParser('unescapeSpecialChars')(m1);
 | 
	
		
			
				|  |  | +    return showdown.subParser('encodeEmailAddress')(unescapedStr);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  text = globals.converter._dispatch('autoLinks.after', text, options, globals);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  return text;
 | 
	
		
			
				|  |  | +});
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * These are all the transformations that form block-level
 | 
	
		
			
				|  |  | + * tags like paragraphs, headers, and list items.
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +showdown.subParser('blockGamut', function (text, options, globals) {
 | 
	
		
			
				|  |  | +  'use strict';
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  text = globals.converter._dispatch('blockGamut.before', text, options, globals);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // we parse blockquotes first so that we can have headings and hrs
 | 
	
		
			
				|  |  | +  // inside blockquotes
 | 
	
		
			
				|  |  | +  text = showdown.subParser('blockQuotes')(text, options, globals);
 | 
	
		
			
				|  |  | +  text = showdown.subParser('headers')(text, options, globals);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // Do Horizontal Rules:
 | 
	
		
			
				|  |  | +  var key = showdown.subParser('hashBlock')('<hr />', options, globals);
 | 
	
		
			
				|  |  | +  text = text.replace(/^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$/gm, key);
 | 
	
		
			
				|  |  | +  text = text.replace(/^[ ]{0,2}([ ]?\-[ ]?){3,}[ \t]*$/gm, key);
 | 
	
		
			
				|  |  | +  text = text.replace(/^[ ]{0,2}([ ]?_[ ]?){3,}[ \t]*$/gm, key);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  text = showdown.subParser('lists')(text, options, globals);
 | 
	
		
			
				|  |  | +  text = showdown.subParser('codeBlocks')(text, options, globals);
 | 
	
		
			
				|  |  | +  text = showdown.subParser('tables')(text, options, globals);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // We already ran _HashHTMLBlocks() before, in Markdown(), but that
 | 
	
		
			
				|  |  | +  // was to escape raw HTML in the original Markdown source. This time,
 | 
	
		
			
				|  |  | +  // we're escaping the markup we've just created, so that we don't wrap
 | 
	
		
			
				|  |  | +  // <p> tags around block-level tags.
 | 
	
		
			
				|  |  | +  text = showdown.subParser('hashHTMLBlocks')(text, options, globals);
 | 
	
		
			
				|  |  | +  text = showdown.subParser('paragraphs')(text, options, globals);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  text = globals.converter._dispatch('blockGamut.after', text, options, globals);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  return text;
 | 
	
		
			
				|  |  | +});
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +showdown.subParser('blockQuotes', function (text, options, globals) {
 | 
	
		
			
				|  |  | +  'use strict';
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  text = globals.converter._dispatch('blockQuotes.before', text, options, globals);
 | 
	
		
			
				|  |  | +  /*
 | 
	
		
			
				|  |  | +   text = text.replace(/
 | 
	
		
			
				|  |  | +   (								// Wrap whole match in $1
 | 
	
		
			
				|  |  | +   (
 | 
	
		
			
				|  |  | +   ^[ \t]*>[ \t]?			// '>' at the start of a line
 | 
	
		
			
				|  |  | +   .+\n					// rest of the first line
 | 
	
		
			
				|  |  | +   (.+\n)*					// subsequent consecutive lines
 | 
	
		
			
				|  |  | +   \n*						// blanks
 | 
	
		
			
				|  |  | +   )+
 | 
	
		
			
				|  |  | +   )
 | 
	
		
			
				|  |  | +   /gm, function(){...});
 | 
	
		
			
				|  |  | +   */
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  text = text.replace(/((^[ \t]{0,3}>[ \t]?.+\n(.+\n)*\n*)+)/gm, function (wholeMatch, m1) {
 | 
	
		
			
				|  |  | +    var bq = m1;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    // attacklab: hack around Konqueror 3.5.4 bug:
 | 
	
		
			
				|  |  | +    // "----------bug".replace(/^-/g,"") == "bug"
 | 
	
		
			
				|  |  | +    bq = bq.replace(/^[ \t]*>[ \t]?/gm, '~0'); // trim one level of quoting
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    // attacklab: clean up hack
 | 
	
		
			
				|  |  | +    bq = bq.replace(/~0/g, '');
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    bq = bq.replace(/^[ \t]+$/gm, ''); // trim whitespace-only lines
 | 
	
		
			
				|  |  | +    bq = showdown.subParser('githubCodeBlocks')(bq, options, globals);
 | 
	
		
			
				|  |  | +    bq = showdown.subParser('blockGamut')(bq, options, globals); // recurse
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    bq = bq.replace(/(^|\n)/g, '$1  ');
 | 
	
		
			
				|  |  | +    // These leading spaces screw with <pre> content, so we need to fix that:
 | 
	
		
			
				|  |  | +    bq = bq.replace(/(\s*<pre>[^\r]+?<\/pre>)/gm, function (wholeMatch, m1) {
 | 
	
		
			
				|  |  | +      var pre = m1;
 | 
	
		
			
				|  |  | +      // attacklab: hack around Konqueror 3.5.4 bug:
 | 
	
		
			
				|  |  | +      pre = pre.replace(/^  /mg, '~0');
 | 
	
		
			
				|  |  | +      pre = pre.replace(/~0/g, '');
 | 
	
		
			
				|  |  | +      return pre;
 | 
	
		
			
				|  |  | +    });
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    return showdown.subParser('hashBlock')('<blockquote>\n' + bq + '\n</blockquote>', options, globals);
 | 
	
		
			
				|  |  | +  });
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  text = globals.converter._dispatch('blockQuotes.after', text, options, globals);
 | 
	
		
			
				|  |  | +  return text;
 | 
	
		
			
				|  |  | +});
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * Process Markdown `<pre><code>` blocks.
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +showdown.subParser('codeBlocks', function (text, options, globals) {
 | 
	
		
			
				|  |  | +  'use strict';
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  text = globals.converter._dispatch('codeBlocks.before', text, options, globals);
 | 
	
		
			
				|  |  | +  /*
 | 
	
		
			
				|  |  | +   text = text.replace(text,
 | 
	
		
			
				|  |  | +   /(?:\n\n|^)
 | 
	
		
			
				|  |  | +   (								// $1 = the code block -- one or more lines, starting with a space/tab
 | 
	
		
			
				|  |  | +   (?:
 | 
	
		
			
				|  |  | +   (?:[ ]{4}|\t)			// Lines must start with a tab or a tab-width of spaces - attacklab: g_tab_width
 | 
	
		
			
				|  |  | +   .*\n+
 | 
	
		
			
				|  |  | +   )+
 | 
	
		
			
				|  |  | +   )
 | 
	
		
			
				|  |  | +   (\n*[ ]{0,3}[^ \t\n]|(?=~0))	// attacklab: g_tab_width
 | 
	
		
			
				|  |  | +   /g,function(){...});
 | 
	
		
			
				|  |  | +   */
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
 | 
	
		
			
				|  |  | +  text += '~0';
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  var pattern = /(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=~0))/g;
 | 
	
		
			
				|  |  | +  text = text.replace(pattern, function (wholeMatch, m1, m2) {
 | 
	
		
			
				|  |  | +    var codeblock = m1,
 | 
	
		
			
				|  |  | +        nextChar = m2,
 | 
	
		
			
				|  |  | +        end = '\n';
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    codeblock = showdown.subParser('outdent')(codeblock);
 | 
	
		
			
				|  |  | +    codeblock = showdown.subParser('encodeCode')(codeblock);
 | 
	
		
			
				|  |  | +    codeblock = showdown.subParser('detab')(codeblock);
 | 
	
		
			
				|  |  | +    codeblock = codeblock.replace(/^\n+/g, ''); // trim leading newlines
 | 
	
		
			
				|  |  | +    codeblock = codeblock.replace(/\n+$/g, ''); // trim trailing newlines
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if (options.omitExtraWLInCodeBlocks) {
 | 
	
		
			
				|  |  | +      end = '';
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    codeblock = '<pre><code>' + codeblock + end + '</code></pre>';
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    return showdown.subParser('hashBlock')(codeblock, options, globals) + nextChar;
 | 
	
		
			
				|  |  | +  });
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // attacklab: strip sentinel
 | 
	
		
			
				|  |  | +  text = text.replace(/~0/, '');
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  text = globals.converter._dispatch('codeBlocks.after', text, options, globals);
 | 
	
		
			
				|  |  | +  return text;
 | 
	
		
			
				|  |  | +});
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + *   *  Backtick quotes are used for <code></code> spans.
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + *   *  You can use multiple backticks as the delimiters if you want to
 | 
	
		
			
				|  |  | + *     include literal backticks in the code span. So, this input:
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + *         Just type ``foo `bar` baz`` at the prompt.
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + *       Will translate to:
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + *         <p>Just type <code>foo `bar` baz</code> at the prompt.</p>
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + *    There's no arbitrary limit to the number of backticks you
 | 
	
		
			
				|  |  | + *    can use as delimters. If you need three consecutive backticks
 | 
	
		
			
				|  |  | + *    in your code, use four for delimiters, etc.
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + *  *  You can use spaces to get literal backticks at the edges:
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + *         ... type `` `bar` `` ...
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + *       Turns to:
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + *         ... type <code>`bar`</code> ...
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +showdown.subParser('codeSpans', function (text, options, globals) {
 | 
	
		
			
				|  |  | +  'use strict';
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  text = globals.converter._dispatch('codeSpans.before', text, options, globals);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /*
 | 
	
		
			
				|  |  | +   text = text.replace(/
 | 
	
		
			
				|  |  | +   (^|[^\\])					// Character before opening ` can't be a backslash
 | 
	
		
			
				|  |  | +   (`+)						// $2 = Opening run of `
 | 
	
		
			
				|  |  | +   (							// $3 = The code block
 | 
	
		
			
				|  |  | +   [^\r]*?
 | 
	
		
			
				|  |  | +   [^`]					// attacklab: work around lack of lookbehind
 | 
	
		
			
				|  |  | +   )
 | 
	
		
			
				|  |  | +   \2							// Matching closer
 | 
	
		
			
				|  |  | +   (?!`)
 | 
	
		
			
				|  |  | +   /gm, function(){...});
 | 
	
		
			
				|  |  | +   */
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  if (typeof(text) === 'undefined') {
 | 
	
		
			
				|  |  | +    text = '';
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  text = text.replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm,
 | 
	
		
			
				|  |  | +    function (wholeMatch, m1, m2, m3) {
 | 
	
		
			
				|  |  | +      var c = m3;
 | 
	
		
			
				|  |  | +      c = c.replace(/^([ \t]*)/g, '');	// leading whitespace
 | 
	
		
			
				|  |  | +      c = c.replace(/[ \t]*$/g, '');	// trailing whitespace
 | 
	
		
			
				|  |  | +      c = showdown.subParser('encodeCode')(c);
 | 
	
		
			
				|  |  | +      return m1 + '<code>' + c + '</code>';
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  );
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  text = globals.converter._dispatch('codeSpans.after', text, options, globals);
 | 
	
		
			
				|  |  | +  return text;
 | 
	
		
			
				|  |  | +});
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * Convert all tabs to spaces
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +showdown.subParser('detab', function (text) {
 | 
	
		
			
				|  |  | +  'use strict';
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // expand first n-1 tabs
 | 
	
		
			
				|  |  | +  text = text.replace(/\t(?=\t)/g, '    '); // g_tab_width
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // replace the nth with two sentinels
 | 
	
		
			
				|  |  | +  text = text.replace(/\t/g, '~A~B');
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // use the sentinel to anchor our regex so it doesn't explode
 | 
	
		
			
				|  |  | +  text = text.replace(/~B(.+?)~A/g, function (wholeMatch, m1) {
 | 
	
		
			
				|  |  | +    var leadingText = m1,
 | 
	
		
			
				|  |  | +        numSpaces = 4 - leadingText.length % 4;  // g_tab_width
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    // there *must* be a better way to do this:
 | 
	
		
			
				|  |  | +    for (var i = 0; i < numSpaces; i++) {
 | 
	
		
			
				|  |  | +      leadingText += ' ';
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    return leadingText;
 | 
	
		
			
				|  |  | +  });
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // clean up sentinels
 | 
	
		
			
				|  |  | +  text = text.replace(/~A/g, '    ');  // g_tab_width
 | 
	
		
			
				|  |  | +  text = text.replace(/~B/g, '');
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  return text;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +});
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * Smart processing for ampersands and angle brackets that need to be encoded.
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +showdown.subParser('encodeAmpsAndAngles', function (text) {
 | 
	
		
			
				|  |  | +  'use strict';
 | 
	
		
			
				|  |  | +  // Ampersand-encoding based entirely on Nat Irons's Amputator MT plugin:
 | 
	
		
			
				|  |  | +  // http://bumppo.net/projects/amputator/
 | 
	
		
			
				|  |  | +  text = text.replace(/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/g, '&');
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // Encode naked <'s
 | 
	
		
			
				|  |  | +  text = text.replace(/<(?![a-z\/?\$!])/gi, '<');
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  return text;
 | 
	
		
			
				|  |  | +});
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * Returns the string, with after processing the following backslash escape sequences.
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * attacklab: The polite way to do this is with the new escapeCharacters() function:
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + *    text = escapeCharacters(text,"\\",true);
 | 
	
		
			
				|  |  | + *    text = escapeCharacters(text,"`*_{}[]()>#+-.!",true);
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * ...but we're sidestepping its use of the (slow) RegExp constructor
 | 
	
		
			
				|  |  | + * as an optimization for Firefox.  This function gets called a LOT.
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +showdown.subParser('encodeBackslashEscapes', function (text) {
 | 
	
		
			
				|  |  | +  'use strict';
 | 
	
		
			
				|  |  | +  text = text.replace(/\\(\\)/g, showdown.helper.escapeCharactersCallback);
 | 
	
		
			
				|  |  | +  text = text.replace(/\\([`*_{}\[\]()>#+-.!])/g, showdown.helper.escapeCharactersCallback);
 | 
	
		
			
				|  |  | +  return text;
 | 
	
		
			
				|  |  | +});
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * Encode/escape certain characters inside Markdown code runs.
 | 
	
		
			
				|  |  | + * The point is that in code, these characters are literals,
 | 
	
		
			
				|  |  | + * and lose their special Markdown meanings.
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +showdown.subParser('encodeCode', function (text) {
 | 
	
		
			
				|  |  | +  'use strict';
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // Encode all ampersands; HTML entities are not
 | 
	
		
			
				|  |  | +  // entities within a Markdown code span.
 | 
	
		
			
				|  |  | +  text = text.replace(/&/g, '&');
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // Do the angle bracket song and dance:
 | 
	
		
			
				|  |  | +  text = text.replace(/</g, '<');
 | 
	
		
			
				|  |  | +  text = text.replace(/>/g, '>');
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // Now, escape characters that are magic in Markdown:
 | 
	
		
			
				|  |  | +  text = showdown.helper.escapeCharacters(text, '*_{}[]\\', false);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // jj the line above breaks this:
 | 
	
		
			
				|  |  | +  //---
 | 
	
		
			
				|  |  | +  //* Item
 | 
	
		
			
				|  |  | +  //   1. Subitem
 | 
	
		
			
				|  |  | +  //            special char: *
 | 
	
		
			
				|  |  | +  // ---
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  return text;
 | 
	
		
			
				|  |  | +});
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + *  Input: an email address, e.g. "foo@example.com"
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + *  Output: the email address as a mailto link, with each character
 | 
	
		
			
				|  |  | + *    of the address encoded as either a decimal or hex entity, in
 | 
	
		
			
				|  |  | + *    the hopes of foiling most address harvesting spam bots. E.g.:
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + *    <a href="mailto:foo@e
 | 
	
		
			
				|  |  | + *       xample.com">foo
 | 
	
		
			
				|  |  | + *       @example.com</a>
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + *  Based on a filter by Matthew Wickline, posted to the BBEdit-Talk
 | 
	
		
			
				|  |  | + *  mailing list: <http://tinyurl.com/yu7ue>
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +showdown.subParser('encodeEmailAddress', function (addr) {
 | 
	
		
			
				|  |  | +  'use strict';
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  var encode = [
 | 
	
		
			
				|  |  | +    function (ch) {
 | 
	
		
			
				|  |  | +      return '&#' + ch.charCodeAt(0) + ';';
 | 
	
		
			
				|  |  | +    },
 | 
	
		
			
				|  |  | +    function (ch) {
 | 
	
		
			
				|  |  | +      return '&#x' + ch.charCodeAt(0).toString(16) + ';';
 | 
	
		
			
				|  |  | +    },
 | 
	
		
			
				|  |  | +    function (ch) {
 | 
	
		
			
				|  |  | +      return ch;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  ];
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  addr = 'mailto:' + addr;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  addr = addr.replace(/./g, function (ch) {
 | 
	
		
			
				|  |  | +    if (ch === '@') {
 | 
	
		
			
				|  |  | +      // this *must* be encoded. I insist.
 | 
	
		
			
				|  |  | +      ch = encode[Math.floor(Math.random() * 2)](ch);
 | 
	
		
			
				|  |  | +    } else if (ch !== ':') {
 | 
	
		
			
				|  |  | +      // leave ':' alone (to spot mailto: later)
 | 
	
		
			
				|  |  | +      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;
 | 
	
		
			
				|  |  | +  });
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  addr = '<a href="' + addr + '">' + addr + '</a>';
 | 
	
		
			
				|  |  | +  addr = addr.replace(/">.+:/g, '">'); // strip the mailto: from the visible part
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  return addr;
 | 
	
		
			
				|  |  | +});
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * Within tags -- meaning between < and > -- encode [\ ` * _] so they
 | 
	
		
			
				|  |  | + * don't conflict with their use in Markdown for code, italics and strong.
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +showdown.subParser('escapeSpecialCharsWithinTagAttributes', function (text) {
 | 
	
		
			
				|  |  | +  'use strict';
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // Build a regex to find HTML tags and comments.  See Friedl's
 | 
	
		
			
				|  |  | +  // "Mastering Regular Expressions", 2nd Ed., pp. 200-201.
 | 
	
		
			
				|  |  | +  var regex = /(<[a-z\/!$]("[^"]*"|'[^']*'|[^'">])*>|<!(--.*?--\s*)+>)/gi;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  text = text.replace(regex, function (wholeMatch) {
 | 
	
		
			
				|  |  | +    var tag = wholeMatch.replace(/(.)<\/?code>(?=.)/g, '$1`');
 | 
	
		
			
				|  |  | +    tag = showdown.helper.escapeCharacters(tag, '\\`*_', false);
 | 
	
		
			
				|  |  | +    return tag;
 | 
	
		
			
				|  |  | +  });
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  return text;
 | 
	
		
			
				|  |  | +});
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * Handle github codeblocks prior to running HashHTML so that
 | 
	
		
			
				|  |  | + * HTML contained within the codeblock gets escaped properly
 | 
	
		
			
				|  |  | + * Example:
 | 
	
		
			
				|  |  | + * ```ruby
 | 
	
		
			
				|  |  | + *     def hello_world(x)
 | 
	
		
			
				|  |  | + *       puts "Hello, #{x}"
 | 
	
		
			
				|  |  | + *     end
 | 
	
		
			
				|  |  | + * ```
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +showdown.subParser('githubCodeBlocks', function (text, options, globals) {
 | 
	
		
			
				|  |  | +  'use strict';
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // early exit if option is not enabled
 | 
	
		
			
				|  |  | +  if (!options.ghCodeBlocks) {
 | 
	
		
			
				|  |  | +    return text;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  text = globals.converter._dispatch('githubCodeBlocks.before', text, options, globals);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  text += '~0';
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  text = text.replace(/(?:^|\n)```(.*)\n([\s\S]*?)\n```/g, function (wholeMatch, language, codeblock) {
 | 
	
		
			
				|  |  | +    var end = (options.omitExtraWLInCodeBlocks) ? '' : '\n';
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    // First parse the github code block
 | 
	
		
			
				|  |  | +    codeblock = showdown.subParser('encodeCode')(codeblock);
 | 
	
		
			
				|  |  | +    codeblock = showdown.subParser('detab')(codeblock);
 | 
	
		
			
				|  |  | +    codeblock = codeblock.replace(/^\n+/g, ''); // trim leading newlines
 | 
	
		
			
				|  |  | +    codeblock = codeblock.replace(/\n+$/g, ''); // trim trailing whitespace
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    codeblock = '<pre><code' + (language ? ' class="' + language + ' language-' + language + '"' : '') + '>' + codeblock + end + '</code></pre>';
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    codeblock = showdown.subParser('hashBlock')(codeblock, options, globals);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    // Since GHCodeblocks can be false positives, we need to
 | 
	
		
			
				|  |  | +    // store the primitive text and the parsed text in a global var,
 | 
	
		
			
				|  |  | +    // and then return a token
 | 
	
		
			
				|  |  | +    return '\n\n~G' + (globals.ghCodeBlocks.push({text: wholeMatch, codeblock: codeblock}) - 1) + 'G\n\n';
 | 
	
		
			
				|  |  | +  });
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // attacklab: strip sentinel
 | 
	
		
			
				|  |  | +  text = text.replace(/~0/, '');
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  return globals.converter._dispatch('githubCodeBlocks.after', text, options, globals);
 | 
	
		
			
				|  |  | +});
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +showdown.subParser('hashBlock', function (text, options, globals) {
 | 
	
		
			
				|  |  | +  'use strict';
 | 
	
		
			
				|  |  | +  text = text.replace(/(^\n+|\n+$)/g, '');
 | 
	
		
			
				|  |  | +  return '\n\n~K' + (globals.gHtmlBlocks.push(text) - 1) + 'K\n\n';
 | 
	
		
			
				|  |  | +});
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +showdown.subParser('hashElement', function (text, options, globals) {
 | 
	
		
			
				|  |  | +  'use strict';
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  return function (wholeMatch, m1) {
 | 
	
		
			
				|  |  | +    var blockText = m1;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    // Undo double lines
 | 
	
		
			
				|  |  | +    blockText = blockText.replace(/\n\n/g, '\n');
 | 
	
		
			
				|  |  | +    blockText = blockText.replace(/^\n/, '');
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    // strip trailing blank lines
 | 
	
		
			
				|  |  | +    blockText = blockText.replace(/\n+$/g, '');
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    // Replace the element text with a marker ("~KxK" where x is its key)
 | 
	
		
			
				|  |  | +    blockText = '\n\n~K' + (globals.gHtmlBlocks.push(blockText) - 1) + 'K\n\n';
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    return blockText;
 | 
	
		
			
				|  |  | +  };
 | 
	
		
			
				|  |  | +});
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +showdown.subParser('hashHTMLBlocks', function (text, options, globals) {
 | 
	
		
			
				|  |  | +  'use strict';
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  var blockTags = [
 | 
	
		
			
				|  |  | +      'pre',
 | 
	
		
			
				|  |  | +      'div',
 | 
	
		
			
				|  |  | +      'h1',
 | 
	
		
			
				|  |  | +      'h2',
 | 
	
		
			
				|  |  | +      'h3',
 | 
	
		
			
				|  |  | +      'h4',
 | 
	
		
			
				|  |  | +      'h5',
 | 
	
		
			
				|  |  | +      'h6',
 | 
	
		
			
				|  |  | +      'blockquote',
 | 
	
		
			
				|  |  | +      'table',
 | 
	
		
			
				|  |  | +      'dl',
 | 
	
		
			
				|  |  | +      'ol',
 | 
	
		
			
				|  |  | +      'ul',
 | 
	
		
			
				|  |  | +      'script',
 | 
	
		
			
				|  |  | +      'noscript',
 | 
	
		
			
				|  |  | +      'form',
 | 
	
		
			
				|  |  | +      'fieldset',
 | 
	
		
			
				|  |  | +      'iframe',
 | 
	
		
			
				|  |  | +      'math',
 | 
	
		
			
				|  |  | +      'style',
 | 
	
		
			
				|  |  | +      'section',
 | 
	
		
			
				|  |  | +      'header',
 | 
	
		
			
				|  |  | +      'footer',
 | 
	
		
			
				|  |  | +      'nav',
 | 
	
		
			
				|  |  | +      'article',
 | 
	
		
			
				|  |  | +      'aside',
 | 
	
		
			
				|  |  | +      'address',
 | 
	
		
			
				|  |  | +      'audio',
 | 
	
		
			
				|  |  | +      'canvas',
 | 
	
		
			
				|  |  | +      'figure',
 | 
	
		
			
				|  |  | +      'hgroup',
 | 
	
		
			
				|  |  | +      'output',
 | 
	
		
			
				|  |  | +      'video',
 | 
	
		
			
				|  |  | +      'p'
 | 
	
		
			
				|  |  | +    ],
 | 
	
		
			
				|  |  | +    repFunc = function (wholeMatch, match, left, right) {
 | 
	
		
			
				|  |  | +      var txt = wholeMatch;
 | 
	
		
			
				|  |  | +      // check if this html element is marked as markdown
 | 
	
		
			
				|  |  | +      // if so, it's contents should be parsed as markdown
 | 
	
		
			
				|  |  | +      if (left.search(/\bmarkdown\b/) !== -1) {
 | 
	
		
			
				|  |  | +        txt = left + globals.converter.makeHtml(match) + right;
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      return '\n\n~K' + (globals.gHtmlBlocks.push(txt) - 1) + 'K\n\n';
 | 
	
		
			
				|  |  | +    };
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  for (var i = 0; i < blockTags.length; ++i) {
 | 
	
		
			
				|  |  | +    text = showdown.helper.replaceRecursiveRegExp(text, repFunc, '^(?: |\\t){0,3}<' + blockTags[i] + '\\b[^>]*>', '</' + blockTags[i] + '>', 'gim');
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // HR SPECIAL CASE
 | 
	
		
			
				|  |  | +  text = text.replace(/(\n[ ]{0,3}(<(hr)\b([^<>])*?\/?>)[ \t]*(?=\n{2,}))/g,
 | 
	
		
			
				|  |  | +    showdown.subParser('hashElement')(text, options, globals));
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // Special case for standalone HTML comments:
 | 
	
		
			
				|  |  | +  text = text.replace(/(<!--[\s\S]*?-->)/g,
 | 
	
		
			
				|  |  | +    showdown.subParser('hashElement')(text, options, globals));
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // PHP and ASP-style processor instructions (<?...?> and <%...%>)
 | 
	
		
			
				|  |  | +  text = text.replace(/(?:\n\n)([ ]{0,3}(?:<([?%])[^\r]*?\2>)[ \t]*(?=\n{2,}))/g,
 | 
	
		
			
				|  |  | +    showdown.subParser('hashElement')(text, options, globals));
 | 
	
		
			
				|  |  | +  return text;
 | 
	
		
			
				|  |  | +});
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * Hash span elements that should not be parsed as markdown
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +showdown.subParser('hashHTMLSpans', function (text, config, globals) {
 | 
	
		
			
				|  |  | +  'use strict';
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  var matches = showdown.helper.matchRecursiveRegExp(text, '<code\\b[^>]*>', '</code>', 'gi');
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  for (var i = 0; i < matches.length; ++i) {
 | 
	
		
			
				|  |  | +    text = text.replace(matches[i][0], '~L' + (globals.gHtmlSpans.push(matches[i][0]) - 1) + 'L');
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  return text;
 | 
	
		
			
				|  |  | +});
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * Unhash HTML spans
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +showdown.subParser('unhashHTMLSpans', function (text, config, globals) {
 | 
	
		
			
				|  |  | +  'use strict';
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  for (var i = 0; i < globals.gHtmlSpans.length; ++i) {
 | 
	
		
			
				|  |  | +    text = text.replace('~L' + i + 'L', globals.gHtmlSpans[i]);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  return text;
 | 
	
		
			
				|  |  | +});
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * Hash span elements that should not be parsed as markdown
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +showdown.subParser('hashPreCodeTags', function (text, config, globals) {
 | 
	
		
			
				|  |  | +  'use strict';
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  var repFunc = function (wholeMatch, match, left, right) {
 | 
	
		
			
				|  |  | +    // encode html entities
 | 
	
		
			
				|  |  | +    var codeblock = left + showdown.subParser('encodeCode')(match) + right;
 | 
	
		
			
				|  |  | +    return '\n\n~G' + (globals.ghCodeBlocks.push({text: wholeMatch, codeblock: codeblock}) - 1) + 'G\n\n';
 | 
	
		
			
				|  |  | +  };
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  text = showdown.helper.replaceRecursiveRegExp(text, repFunc, '^(?: |\\t){0,3}<pre\\b[^>]*>\\s*<code\\b[^>]*>', '^(?: |\\t){0,3}</code>\\s*</pre>', 'gim');
 | 
	
		
			
				|  |  | +  return text;
 | 
	
		
			
				|  |  | +});
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +showdown.subParser('headers', function (text, options, globals) {
 | 
	
		
			
				|  |  | +  'use strict';
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  text = globals.converter._dispatch('headers.before', text, options, globals);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  var prefixHeader = options.prefixHeaderId,
 | 
	
		
			
				|  |  | +      headerLevelStart = (isNaN(parseInt(options.headerLevelStart))) ? 1 : parseInt(options.headerLevelStart),
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // Set text-style headers:
 | 
	
		
			
				|  |  | +  //	Header 1
 | 
	
		
			
				|  |  | +  //	========
 | 
	
		
			
				|  |  | +  //
 | 
	
		
			
				|  |  | +  //	Header 2
 | 
	
		
			
				|  |  | +  //	--------
 | 
	
		
			
				|  |  | +  //
 | 
	
		
			
				|  |  | +      setextRegexH1 = (options.smoothLivePreview) ? /^(.+)[ \t]*\n={2,}[ \t]*\n+/gm : /^(.+)[ \t]*\n=+[ \t]*\n+/gm,
 | 
	
		
			
				|  |  | +      setextRegexH2 = (options.smoothLivePreview) ? /^(.+)[ \t]*\n-{2,}[ \t]*\n+/gm : /^(.+)[ \t]*\n-+[ \t]*\n+/gm;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  text = text.replace(setextRegexH1, function (wholeMatch, m1) {
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    var spanGamut = showdown.subParser('spanGamut')(m1, options, globals),
 | 
	
		
			
				|  |  | +        hID = (options.noHeaderId) ? '' : ' id="' + headerId(m1) + '"',
 | 
	
		
			
				|  |  | +        hLevel = headerLevelStart,
 | 
	
		
			
				|  |  | +        hashBlock = '<h' + hLevel + hID + '>' + spanGamut + '</h' + hLevel + '>';
 | 
	
		
			
				|  |  | +    return showdown.subParser('hashBlock')(hashBlock, options, globals);
 | 
	
		
			
				|  |  | +  });
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  text = text.replace(setextRegexH2, function (matchFound, m1) {
 | 
	
		
			
				|  |  | +    var spanGamut = showdown.subParser('spanGamut')(m1, options, globals),
 | 
	
		
			
				|  |  | +        hID = (options.noHeaderId) ? '' : ' id="' + headerId(m1) + '"',
 | 
	
		
			
				|  |  | +        hLevel = headerLevelStart + 1,
 | 
	
		
			
				|  |  | +      hashBlock = '<h' + hLevel + hID + '>' + spanGamut + '</h' + hLevel + '>';
 | 
	
		
			
				|  |  | +    return showdown.subParser('hashBlock')(hashBlock, options, globals);
 | 
	
		
			
				|  |  | +  });
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // atx-style headers:
 | 
	
		
			
				|  |  | +  //  # Header 1
 | 
	
		
			
				|  |  | +  //  ## Header 2
 | 
	
		
			
				|  |  | +  //  ## Header 2 with closing hashes ##
 | 
	
		
			
				|  |  | +  //  ...
 | 
	
		
			
				|  |  | +  //  ###### Header 6
 | 
	
		
			
				|  |  | +  //
 | 
	
		
			
				|  |  | +  text = text.replace(/^(#{1,6})[ \t]*(.+?)[ \t]*#*\n+/gm, function (wholeMatch, m1, m2) {
 | 
	
		
			
				|  |  | +    var span = showdown.subParser('spanGamut')(m2, options, globals),
 | 
	
		
			
				|  |  | +        hID = (options.noHeaderId) ? '' : ' id="' + headerId(m2) + '"',
 | 
	
		
			
				|  |  | +        hLevel = headerLevelStart - 1 + m1.length,
 | 
	
		
			
				|  |  | +        header = '<h' + hLevel + hID + '>' + span + '</h' + hLevel + '>';
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    return showdown.subParser('hashBlock')(header, options, globals);
 | 
	
		
			
				|  |  | +  });
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  function headerId(m) {
 | 
	
		
			
				|  |  | +    var title, escapedId = m.replace(/[^\w]/g, '').toLowerCase();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if (globals.hashLinkCounts[escapedId]) {
 | 
	
		
			
				|  |  | +      title = escapedId + '-' + (globals.hashLinkCounts[escapedId]++);
 | 
	
		
			
				|  |  | +    } else {
 | 
	
		
			
				|  |  | +      title = escapedId;
 | 
	
		
			
				|  |  | +      globals.hashLinkCounts[escapedId] = 1;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    // Prefix id to prevent causing inadvertent pre-existing style matches.
 | 
	
		
			
				|  |  | +    if (prefixHeader === true) {
 | 
	
		
			
				|  |  | +      prefixHeader = 'section';
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if (showdown.helper.isString(prefixHeader)) {
 | 
	
		
			
				|  |  | +      return prefixHeader + title;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    return title;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  text = globals.converter._dispatch('headers.after', text, options, globals);
 | 
	
		
			
				|  |  | +  return text;
 | 
	
		
			
				|  |  | +});
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * Turn Markdown image shortcuts into <img> tags.
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +showdown.subParser('images', function (text, options, globals) {
 | 
	
		
			
				|  |  | +  'use strict';
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  text = globals.converter._dispatch('images.before', text, options, globals);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  var inlineRegExp    = /!\[(.*?)]\s?\([ \t]*()<?(\S+?)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*(?:(['"])(.*?)\6[ \t]*)?\)/g,
 | 
	
		
			
				|  |  | +      referenceRegExp = /!\[([^\]]*?)] ?(?:\n *)?\[(.*?)]()()()()()/g;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  function writeImageTag (wholeMatch, altText, linkId, url, width, height, m5, title) {
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    var gUrls   = globals.gUrls,
 | 
	
		
			
				|  |  | +        gTitles = globals.gTitles,
 | 
	
		
			
				|  |  | +        gDims   = globals.gDimensions;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    linkId = linkId.toLowerCase();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if (!title) {
 | 
	
		
			
				|  |  | +      title = '';
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if (url === '' || url === null) {
 | 
	
		
			
				|  |  | +      if (linkId === '' || linkId === null) {
 | 
	
		
			
				|  |  | +        // lower-case and turn embedded newlines into spaces
 | 
	
		
			
				|  |  | +        linkId = altText.toLowerCase().replace(/ ?\n/g, ' ');
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      url = '#' + linkId;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +      if (!showdown.helper.isUndefined(gUrls[linkId])) {
 | 
	
		
			
				|  |  | +        url = gUrls[linkId];
 | 
	
		
			
				|  |  | +        if (!showdown.helper.isUndefined(gTitles[linkId])) {
 | 
	
		
			
				|  |  | +          title = gTitles[linkId];
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        if (!showdown.helper.isUndefined(gDims[linkId])) {
 | 
	
		
			
				|  |  | +          width = gDims[linkId].width;
 | 
	
		
			
				|  |  | +          height = gDims[linkId].height;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +      } else {
 | 
	
		
			
				|  |  | +        return wholeMatch;
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    altText = altText.replace(/"/g, '"');
 | 
	
		
			
				|  |  | +    altText = showdown.helper.escapeCharacters(altText, '*_', false);
 | 
	
		
			
				|  |  | +    url = showdown.helper.escapeCharacters(url, '*_', false);
 | 
	
		
			
				|  |  | +    var result = '<img src="' + url + '" alt="' + altText + '"';
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if (title) {
 | 
	
		
			
				|  |  | +      title = title.replace(/"/g, '"');
 | 
	
		
			
				|  |  | +      title = showdown.helper.escapeCharacters(title, '*_', false);
 | 
	
		
			
				|  |  | +      result += ' title="' + title + '"';
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if (width && height) {
 | 
	
		
			
				|  |  | +      width  = (width === '*') ? 'auto' : width;
 | 
	
		
			
				|  |  | +      height = (height === '*') ? 'auto' : height;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +      result += ' width="' + width + '"';
 | 
	
		
			
				|  |  | +      result += ' height="' + height + '"';
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    result += ' />';
 | 
	
		
			
				|  |  | +    return result;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // First, handle reference-style labeled images: ![alt text][id]
 | 
	
		
			
				|  |  | +  text = text.replace(referenceRegExp, writeImageTag);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // Next, handle inline images:  
 | 
	
		
			
				|  |  | +  text = text.replace(inlineRegExp, writeImageTag);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  text = globals.converter._dispatch('images.after', text, options, globals);
 | 
	
		
			
				|  |  | +  return text;
 | 
	
		
			
				|  |  | +});
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +showdown.subParser('italicsAndBold', function (text, options, globals) {
 | 
	
		
			
				|  |  | +  'use strict';
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  text = globals.converter._dispatch('italicsAndBold.before', text, options, globals);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  if (options.literalMidWordUnderscores) {
 | 
	
		
			
				|  |  | +    //underscores
 | 
	
		
			
				|  |  | +    // Since we are consuming a \s character, we need to add it
 | 
	
		
			
				|  |  | +    text = text.replace(/(^|\s|>|\b)__(?=\S)([\s\S]+?)__(?=\b|<|\s|$)/gm, '$1<strong>$2</strong>');
 | 
	
		
			
				|  |  | +    text = text.replace(/(^|\s|>|\b)_(?=\S)([\s\S]+?)_(?=\b|<|\s|$)/gm, '$1<em>$2</em>');
 | 
	
		
			
				|  |  | +    //asterisks
 | 
	
		
			
				|  |  | +    text = text.replace(/(\*\*)(?=\S)([^\r]*?\S[*]*)\1/g, '<strong>$2</strong>');
 | 
	
		
			
				|  |  | +    text = text.replace(/(\*)(?=\S)([^\r]*?\S)\1/g, '<em>$2</em>');
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  } else {
 | 
	
		
			
				|  |  | +    // <strong> must go first:
 | 
	
		
			
				|  |  | +    text = text.replace(/(\*\*|__)(?=\S)([^\r]*?\S[*_]*)\1/g, '<strong>$2</strong>');
 | 
	
		
			
				|  |  | +    text = text.replace(/(\*|_)(?=\S)([^\r]*?\S)\1/g, '<em>$2</em>');
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  text = globals.converter._dispatch('italicsAndBold.after', text, options, globals);
 | 
	
		
			
				|  |  | +  return text;
 | 
	
		
			
				|  |  | +});
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * Form HTML ordered (numbered) and unordered (bulleted) lists.
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +showdown.subParser('lists', function (text, options, globals) {
 | 
	
		
			
				|  |  | +  'use strict';
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  text = globals.converter._dispatch('lists.before', text, options, globals);
 | 
	
		
			
				|  |  | +  /**
 | 
	
		
			
				|  |  | +   * Process the contents of a single ordered or unordered list, splitting it
 | 
	
		
			
				|  |  | +   * into individual list items.
 | 
	
		
			
				|  |  | +   * @param {string} listStr
 | 
	
		
			
				|  |  | +   * @param {boolean} trimTrailing
 | 
	
		
			
				|  |  | +   * @returns {string}
 | 
	
		
			
				|  |  | +   */
 | 
	
		
			
				|  |  | +  function processListItems (listStr, trimTrailing) {
 | 
	
		
			
				|  |  | +    // The $g_list_level global keeps track of when we're inside a list.
 | 
	
		
			
				|  |  | +    // Each time we enter a list, we increment it; when we leave a list,
 | 
	
		
			
				|  |  | +    // we decrement. If it's zero, we're not in a list anymore.
 | 
	
		
			
				|  |  | +    //
 | 
	
		
			
				|  |  | +    // We do this because when we're not inside a list, we want to treat
 | 
	
		
			
				|  |  | +    // something like this:
 | 
	
		
			
				|  |  | +    //
 | 
	
		
			
				|  |  | +    //    I recommend upgrading to version
 | 
	
		
			
				|  |  | +    //    8. Oops, now this line is treated
 | 
	
		
			
				|  |  | +    //    as a sub-list.
 | 
	
		
			
				|  |  | +    //
 | 
	
		
			
				|  |  | +    // As a single paragraph, despite the fact that the second line starts
 | 
	
		
			
				|  |  | +    // with a digit-period-space sequence.
 | 
	
		
			
				|  |  | +    //
 | 
	
		
			
				|  |  | +    // Whereas when we're inside a list (or sub-list), that line will be
 | 
	
		
			
				|  |  | +    // treated as the start of a sub-list. What a kludge, huh? This is
 | 
	
		
			
				|  |  | +    // an aspect of Markdown's syntax that's hard to parse perfectly
 | 
	
		
			
				|  |  | +    // without resorting to mind-reading. Perhaps the solution is to
 | 
	
		
			
				|  |  | +    // change the syntax rules such that sub-lists must start with a
 | 
	
		
			
				|  |  | +    // starting cardinal number; e.g. "1." or "a.".
 | 
	
		
			
				|  |  | +    globals.gListLevel++;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    // trim trailing blank lines:
 | 
	
		
			
				|  |  | +    listStr = listStr.replace(/\n{2,}$/, '\n');
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    // attacklab: add sentinel to emulate \z
 | 
	
		
			
				|  |  | +    listStr += '~0';
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    var rgx = /(\n)?(^[ \t]*)([*+-]|\d+[.])[ \t]+((\[(x|X| )?])?[ \t]*[^\r]+?(\n{1,2}))(?=\n*(~0|\2([*+-]|\d+[.])[ \t]+))/gm,
 | 
	
		
			
				|  |  | +        isParagraphed = (/\n[ \t]*\n(?!~0)/.test(listStr));
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    listStr = listStr.replace(rgx, function (wholeMatch, m1, m2, m3, m4, taskbtn, checked) {
 | 
	
		
			
				|  |  | +      checked = (checked && checked.trim() !== '');
 | 
	
		
			
				|  |  | +      var item = showdown.subParser('outdent')(m4, options, globals),
 | 
	
		
			
				|  |  | +          bulletStyle = '';
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +      // Support for github tasklists
 | 
	
		
			
				|  |  | +      if (taskbtn && options.tasklists) {
 | 
	
		
			
				|  |  | +        bulletStyle = ' class="task-list-item" style="list-style-type: none;"';
 | 
	
		
			
				|  |  | +        item = item.replace(/^[ \t]*\[(x|X| )?]/m, function () {
 | 
	
		
			
				|  |  | +          var otp = '<input type="checkbox" disabled style="margin: 0px 0.35em 0.25em -1.6em; vertical-align: middle;"';
 | 
	
		
			
				|  |  | +          if (checked) {
 | 
	
		
			
				|  |  | +            otp += ' checked';
 | 
	
		
			
				|  |  | +          }
 | 
	
		
			
				|  |  | +          otp += '>';
 | 
	
		
			
				|  |  | +          return otp;
 | 
	
		
			
				|  |  | +        });
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      // m1 - Leading line or
 | 
	
		
			
				|  |  | +      // Has a double return (multi paragraph) or
 | 
	
		
			
				|  |  | +      // Has sublist
 | 
	
		
			
				|  |  | +      if (m1 || (item.search(/\n{2,}/) > -1)) {
 | 
	
		
			
				|  |  | +        item = showdown.subParser('githubCodeBlocks')(item, options, globals);
 | 
	
		
			
				|  |  | +        item = showdown.subParser('blockGamut')(item, options, globals);
 | 
	
		
			
				|  |  | +      } else {
 | 
	
		
			
				|  |  | +        // Recursion for sub-lists:
 | 
	
		
			
				|  |  | +        item = showdown.subParser('lists')(item, options, globals);
 | 
	
		
			
				|  |  | +        item = item.replace(/\n$/, ''); // chomp(item)
 | 
	
		
			
				|  |  | +        if (isParagraphed) {
 | 
	
		
			
				|  |  | +          item = showdown.subParser('paragraphs')(item, options, globals);
 | 
	
		
			
				|  |  | +        } else {
 | 
	
		
			
				|  |  | +          item = showdown.subParser('spanGamut')(item, options, globals);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      item =  '\n<li' + bulletStyle + '>' + item + '</li>\n';
 | 
	
		
			
				|  |  | +      return item;
 | 
	
		
			
				|  |  | +    });
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    // attacklab: strip sentinel
 | 
	
		
			
				|  |  | +    listStr = listStr.replace(/~0/g, '');
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    globals.gListLevel--;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if (trimTrailing) {
 | 
	
		
			
				|  |  | +      listStr = listStr.replace(/\s+$/, '');
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    return listStr;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /**
 | 
	
		
			
				|  |  | +   * Check and parse consecutive lists (better fix for issue #142)
 | 
	
		
			
				|  |  | +   * @param {string} list
 | 
	
		
			
				|  |  | +   * @param {string} listType
 | 
	
		
			
				|  |  | +   * @param {boolean} trimTrailing
 | 
	
		
			
				|  |  | +   * @returns {string}
 | 
	
		
			
				|  |  | +   */
 | 
	
		
			
				|  |  | +  function parseConsecutiveLists(list, listType, trimTrailing) {
 | 
	
		
			
				|  |  | +    // check if we caught 2 or more consecutive lists by mistake
 | 
	
		
			
				|  |  | +    // we use the counterRgx, meaning if listType is UL we look for UL and vice versa
 | 
	
		
			
				|  |  | +    var counterRxg = (listType === 'ul') ? /^ {0,2}\d+\.[ \t]/gm : /^ {0,2}[*+-][ \t]/gm,
 | 
	
		
			
				|  |  | +      subLists = [],
 | 
	
		
			
				|  |  | +      result = '';
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if (list.search(counterRxg) !== -1) {
 | 
	
		
			
				|  |  | +      (function parseCL(txt) {
 | 
	
		
			
				|  |  | +        var pos = txt.search(counterRxg);
 | 
	
		
			
				|  |  | +        if (pos !== -1) {
 | 
	
		
			
				|  |  | +          // slice
 | 
	
		
			
				|  |  | +          result += '\n\n<' + listType + '>' + processListItems(txt.slice(0, pos), !!trimTrailing) + '</' + listType + '>\n\n';
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +          // invert counterType and listType
 | 
	
		
			
				|  |  | +          listType = (listType === 'ul') ? 'ol' : 'ul';
 | 
	
		
			
				|  |  | +          counterRxg = (listType === 'ul') ? /^ {0,2}\d+\.[ \t]/gm : /^ {0,2}[*+-][ \t]/gm;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +          //recurse
 | 
	
		
			
				|  |  | +          parseCL(txt.slice(pos));
 | 
	
		
			
				|  |  | +        } else {
 | 
	
		
			
				|  |  | +          result += '\n\n<' + listType + '>' + processListItems(txt, !!trimTrailing) + '</' + listType + '>\n\n';
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +      })(list);
 | 
	
		
			
				|  |  | +      for (var i = 0; i < subLists.length; ++i) {
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    } else {
 | 
	
		
			
				|  |  | +      result = '\n\n<' + listType + '>' + processListItems(list, !!trimTrailing) + '</' + listType + '>\n\n';
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    return result;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // attacklab: add sentinel to hack around khtml/safari bug:
 | 
	
		
			
				|  |  | +  // http://bugs.webkit.org/show_bug.cgi?id=11231
 | 
	
		
			
				|  |  | +  text += '~0';
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // Re-usable pattern to match any entire ul or ol list:
 | 
	
		
			
				|  |  | +  var wholeList = /^(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  if (globals.gListLevel) {
 | 
	
		
			
				|  |  | +    text = text.replace(wholeList, function (wholeMatch, list, m2) {
 | 
	
		
			
				|  |  | +      var listType = (m2.search(/[*+-]/g) > -1) ? 'ul' : 'ol';
 | 
	
		
			
				|  |  | +      return parseConsecutiveLists(list, listType, true);
 | 
	
		
			
				|  |  | +    });
 | 
	
		
			
				|  |  | +  } else {
 | 
	
		
			
				|  |  | +    wholeList = /(\n\n|^\n?)(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm;
 | 
	
		
			
				|  |  | +    //wholeList = /(\n\n|^\n?)( {0,3}([*+-]|\d+\.)[ \t]+[\s\S]+?)(?=(~0)|(\n\n(?!\t| {2,}| {0,3}([*+-]|\d+\.)[ \t])))/g;
 | 
	
		
			
				|  |  | +    text = text.replace(wholeList, function (wholeMatch, m1, list, m3) {
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +      var listType = (m3.search(/[*+-]/g) > -1) ? 'ul' : 'ol';
 | 
	
		
			
				|  |  | +      return parseConsecutiveLists(list, listType);
 | 
	
		
			
				|  |  | +    });
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // attacklab: strip sentinel
 | 
	
		
			
				|  |  | +  text = text.replace(/~0/, '');
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  text = globals.converter._dispatch('lists.after', text, options, globals);
 | 
	
		
			
				|  |  | +  return text;
 | 
	
		
			
				|  |  | +});
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * Remove one level of line-leading tabs or spaces
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +showdown.subParser('outdent', function (text) {
 | 
	
		
			
				|  |  | +  'use strict';
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // attacklab: hack around Konqueror 3.5.4 bug:
 | 
	
		
			
				|  |  | +  // "----------bug".replace(/^-/g,"") == "bug"
 | 
	
		
			
				|  |  | +  text = text.replace(/^(\t|[ ]{1,4})/gm, '~0'); // attacklab: g_tab_width
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // attacklab: clean up hack
 | 
	
		
			
				|  |  | +  text = text.replace(/~0/g, '');
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  return text;
 | 
	
		
			
				|  |  | +});
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +showdown.subParser('paragraphs', function (text, options, globals) {
 | 
	
		
			
				|  |  | +  'use strict';
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  text = globals.converter._dispatch('paragraphs.before', text, options, globals);
 | 
	
		
			
				|  |  | +  // Strip leading and trailing lines:
 | 
	
		
			
				|  |  | +  text = text.replace(/^\n+/g, '');
 | 
	
		
			
				|  |  | +  text = text.replace(/\n+$/g, '');
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  var grafs = text.split(/\n{2,}/g),
 | 
	
		
			
				|  |  | +      grafsOut = [],
 | 
	
		
			
				|  |  | +      end = grafs.length; // Wrap <p> tags
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  for (var i = 0; i < end; i++) {
 | 
	
		
			
				|  |  | +    var str = grafs[i];
 | 
	
		
			
				|  |  | +    // if this is an HTML marker, copy it
 | 
	
		
			
				|  |  | +    if (str.search(/~(K|G)(\d+)\1/g) >= 0) {
 | 
	
		
			
				|  |  | +      grafsOut.push(str);
 | 
	
		
			
				|  |  | +    } else {
 | 
	
		
			
				|  |  | +      str = showdown.subParser('spanGamut')(str, options, globals);
 | 
	
		
			
				|  |  | +      str = str.replace(/^([ \t]*)/g, '<p>');
 | 
	
		
			
				|  |  | +      str += '</p>';
 | 
	
		
			
				|  |  | +      grafsOut.push(str);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /** Unhashify HTML blocks */
 | 
	
		
			
				|  |  | +  end = grafsOut.length;
 | 
	
		
			
				|  |  | +  for (i = 0; i < end; i++) {
 | 
	
		
			
				|  |  | +    var blockText = '',
 | 
	
		
			
				|  |  | +        grafsOutIt = grafsOut[i],
 | 
	
		
			
				|  |  | +        codeFlag = false;
 | 
	
		
			
				|  |  | +    // if this is a marker for an html block...
 | 
	
		
			
				|  |  | +    while (grafsOutIt.search(/~(K|G)(\d+)\1/) >= 0) {
 | 
	
		
			
				|  |  | +      var delim = RegExp.$1,
 | 
	
		
			
				|  |  | +          num   = RegExp.$2;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +      if (delim === 'K') {
 | 
	
		
			
				|  |  | +        blockText = globals.gHtmlBlocks[num];
 | 
	
		
			
				|  |  | +      } else {
 | 
	
		
			
				|  |  | +        // we need to check if ghBlock is a false positive
 | 
	
		
			
				|  |  | +        if (codeFlag) {
 | 
	
		
			
				|  |  | +          // use encoded version of all text
 | 
	
		
			
				|  |  | +          blockText = showdown.subParser('encodeCode')(globals.ghCodeBlocks[num].text);
 | 
	
		
			
				|  |  | +        } else {
 | 
	
		
			
				|  |  | +          blockText = globals.ghCodeBlocks[num].codeblock;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      blockText = blockText.replace(/\$/g, '$$$$'); // Escape any dollar signs
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +      grafsOutIt = grafsOutIt.replace(/(\n\n)?~(K|G)\d+\2(\n\n)?/, blockText);
 | 
	
		
			
				|  |  | +      // Check if grafsOutIt is a pre->code
 | 
	
		
			
				|  |  | +      if (/^<pre\b[^>]*>\s*<code\b[^>]*>/.test(grafsOutIt)) {
 | 
	
		
			
				|  |  | +        codeFlag = true;
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    grafsOut[i] = grafsOutIt;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  text = grafsOut.join('\n\n');
 | 
	
		
			
				|  |  | +  // Strip leading and trailing lines:
 | 
	
		
			
				|  |  | +  text = text.replace(/^\n+/g, '');
 | 
	
		
			
				|  |  | +  text = text.replace(/\n+$/g, '');
 | 
	
		
			
				|  |  | +  return globals.converter._dispatch('paragraphs.after', text, options, globals);
 | 
	
		
			
				|  |  | +});
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * Run extension
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +showdown.subParser('runExtension', function (ext, text, options, globals) {
 | 
	
		
			
				|  |  | +  'use strict';
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  if (ext.filter) {
 | 
	
		
			
				|  |  | +    text = ext.filter(text, globals.converter, options);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  } else if (ext.regex) {
 | 
	
		
			
				|  |  | +    // TODO remove this when old extension loading mechanism is deprecated
 | 
	
		
			
				|  |  | +    var re = ext.regex;
 | 
	
		
			
				|  |  | +    if (!re instanceof RegExp) {
 | 
	
		
			
				|  |  | +      re = new RegExp(re, 'g');
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    text = text.replace(re, ext.replace);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  return text;
 | 
	
		
			
				|  |  | +});
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * These are all the transformations that occur *within* block-level
 | 
	
		
			
				|  |  | + * tags like paragraphs, headers, and list items.
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +showdown.subParser('spanGamut', function (text, options, globals) {
 | 
	
		
			
				|  |  | +  'use strict';
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  text = globals.converter._dispatch('spanGamut.before', text, options, globals);
 | 
	
		
			
				|  |  | +  text = showdown.subParser('codeSpans')(text, options, globals);
 | 
	
		
			
				|  |  | +  text = showdown.subParser('escapeSpecialCharsWithinTagAttributes')(text, options, globals);
 | 
	
		
			
				|  |  | +  text = showdown.subParser('encodeBackslashEscapes')(text, options, globals);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // Process anchor and image tags. Images must come first,
 | 
	
		
			
				|  |  | +  // because ![foo][f] looks like an anchor.
 | 
	
		
			
				|  |  | +  text = showdown.subParser('images')(text, options, globals);
 | 
	
		
			
				|  |  | +  text = showdown.subParser('anchors')(text, options, globals);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // Make links out of things like `<http://example.com/>`
 | 
	
		
			
				|  |  | +  // Must come after _DoAnchors(), because you can use < and >
 | 
	
		
			
				|  |  | +  // delimiters in inline links like [this](<url>).
 | 
	
		
			
				|  |  | +  text = showdown.subParser('autoLinks')(text, options, globals);
 | 
	
		
			
				|  |  | +  text = showdown.subParser('encodeAmpsAndAngles')(text, options, globals);
 | 
	
		
			
				|  |  | +  text = showdown.subParser('italicsAndBold')(text, options, globals);
 | 
	
		
			
				|  |  | +  text = showdown.subParser('strikethrough')(text, options, globals);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // Do hard breaks:
 | 
	
		
			
				|  |  | +  text = text.replace(/  +\n/g, ' <br />\n');
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  text = globals.converter._dispatch('spanGamut.after', text, options, globals);
 | 
	
		
			
				|  |  | +  return text;
 | 
	
		
			
				|  |  | +});
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +showdown.subParser('strikethrough', function (text, options, globals) {
 | 
	
		
			
				|  |  | +  'use strict';
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  if (options.strikethrough) {
 | 
	
		
			
				|  |  | +    text = globals.converter._dispatch('strikethrough.before', text, options, globals);
 | 
	
		
			
				|  |  | +    text = text.replace(/(?:~T){2}([\s\S]+?)(?:~T){2}/g, '<del>$1</del>');
 | 
	
		
			
				|  |  | +    text = globals.converter._dispatch('strikethrough.after', text, options, globals);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  return text;
 | 
	
		
			
				|  |  | +});
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * 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+/
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +showdown.subParser('stripBlankLines', function (text) {
 | 
	
		
			
				|  |  | +  'use strict';
 | 
	
		
			
				|  |  | +  return text.replace(/^[ \t]+$/mg, '');
 | 
	
		
			
				|  |  | +});
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * Strips link definitions from text, stores the URLs and titles in
 | 
	
		
			
				|  |  | + * hash references.
 | 
	
		
			
				|  |  | + * Link defs are in the form: ^[id]: url "optional title"
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * ^[ ]{0,3}\[(.+)\]: // id = $1  attacklab: g_tab_width - 1
 | 
	
		
			
				|  |  | + * [ \t]*
 | 
	
		
			
				|  |  | + * \n?                  // maybe *one* newline
 | 
	
		
			
				|  |  | + * [ \t]*
 | 
	
		
			
				|  |  | + * <?(\S+?)>?          // url = $2
 | 
	
		
			
				|  |  | + * [ \t]*
 | 
	
		
			
				|  |  | + * \n?                // maybe one newline
 | 
	
		
			
				|  |  | + * [ \t]*
 | 
	
		
			
				|  |  | + * (?:
 | 
	
		
			
				|  |  | + * (\n*)              // any lines skipped = $3 attacklab: lookbehind removed
 | 
	
		
			
				|  |  | + * ["(]
 | 
	
		
			
				|  |  | + * (.+?)              // title = $4
 | 
	
		
			
				|  |  | + * [")]
 | 
	
		
			
				|  |  | + * [ \t]*
 | 
	
		
			
				|  |  | + * )?                 // title is optional
 | 
	
		
			
				|  |  | + * (?:\n+|$)
 | 
	
		
			
				|  |  | + * /gm,
 | 
	
		
			
				|  |  | + * function(){...});
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +showdown.subParser('stripLinkDefinitions', function (text, options, globals) {
 | 
	
		
			
				|  |  | +  'use strict';
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  var regex = /^ {0,3}\[(.+)]:[ \t]*\n?[ \t]*<?(\S+?)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*\n?[ \t]*(?:(\n*)["|'(](.+?)["|')][ \t]*)?(?:\n+|(?=~0))/gm;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
 | 
	
		
			
				|  |  | +  text += '~0';
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  text = text.replace(regex, function (wholeMatch, linkId, url, width, height, blankLines, title) {
 | 
	
		
			
				|  |  | +    linkId = linkId.toLowerCase();
 | 
	
		
			
				|  |  | +    globals.gUrls[linkId] = showdown.subParser('encodeAmpsAndAngles')(url);  // Link IDs are case-insensitive
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if (blankLines) {
 | 
	
		
			
				|  |  | +      // Oops, found blank lines, so it's not a title.
 | 
	
		
			
				|  |  | +      // Put back the parenthetical statement we stole.
 | 
	
		
			
				|  |  | +      return blankLines + title;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    } else {
 | 
	
		
			
				|  |  | +      if (title) {
 | 
	
		
			
				|  |  | +        globals.gTitles[linkId] = title.replace(/"|'/g, '"');
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      if (options.parseImgDimensions && width && height) {
 | 
	
		
			
				|  |  | +        globals.gDimensions[linkId] = {
 | 
	
		
			
				|  |  | +          width:  width,
 | 
	
		
			
				|  |  | +          height: height
 | 
	
		
			
				|  |  | +        };
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    // Completely remove the definition from the text
 | 
	
		
			
				|  |  | +    return '';
 | 
	
		
			
				|  |  | +  });
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // attacklab: strip sentinel
 | 
	
		
			
				|  |  | +  text = text.replace(/~0/, '');
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  return text;
 | 
	
		
			
				|  |  | +});
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +showdown.subParser('tables', function (text, options, globals) {
 | 
	
		
			
				|  |  | +  'use strict';
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  if (!options.tables) {
 | 
	
		
			
				|  |  | +    return text;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  var tableRgx = /^[ \t]{0,3}\|?.+\|.+\n[ \t]{0,3}\|?[ \t]*:?[ \t]*(?:-|=){2,}[ \t]*:?[ \t]*\|[ \t]*:?[ \t]*(?:-|=){2,}[\s\S]+?(?:\n\n|~0)/gm;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  function parseStyles(sLine) {
 | 
	
		
			
				|  |  | +    if (/^:[ \t]*--*$/.test(sLine)) {
 | 
	
		
			
				|  |  | +      return ' style="text-align:left;"';
 | 
	
		
			
				|  |  | +    } else if (/^--*[ \t]*:[ \t]*$/.test(sLine)) {
 | 
	
		
			
				|  |  | +      return ' style="text-align:right;"';
 | 
	
		
			
				|  |  | +    } else if (/^:[ \t]*--*[ \t]*:$/.test(sLine)) {
 | 
	
		
			
				|  |  | +      return ' style="text-align:center;"';
 | 
	
		
			
				|  |  | +    } else {
 | 
	
		
			
				|  |  | +      return '';
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  function parseHeaders(header, style) {
 | 
	
		
			
				|  |  | +    var id = '';
 | 
	
		
			
				|  |  | +    header = header.trim();
 | 
	
		
			
				|  |  | +    if (options.tableHeaderId) {
 | 
	
		
			
				|  |  | +      id = ' id="' + header.replace(/ /g, '_').toLowerCase() + '"';
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    header = showdown.subParser('spanGamut')(header, options, globals);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    return '<th' + id + style + '>' + header + '</th>\n';
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  function parseCells(cell, style) {
 | 
	
		
			
				|  |  | +    var subText = showdown.subParser('spanGamut')(cell, options, globals);
 | 
	
		
			
				|  |  | +    return '<td' + style + '>' + subText + '</td>\n';
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  function buildTable(headers, cells) {
 | 
	
		
			
				|  |  | +    var tb = '<table>\n<thead>\n<tr>\n',
 | 
	
		
			
				|  |  | +        tblLgn = headers.length;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    for (var i = 0; i < tblLgn; ++i) {
 | 
	
		
			
				|  |  | +      tb += headers[i];
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    tb += '</tr>\n</thead>\n<tbody>\n';
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    for (i = 0; i < cells.length; ++i) {
 | 
	
		
			
				|  |  | +      tb += '<tr>\n';
 | 
	
		
			
				|  |  | +      for (var ii = 0; ii < tblLgn; ++ii) {
 | 
	
		
			
				|  |  | +        tb += cells[i][ii];
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      tb += '</tr>\n';
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    tb += '</tbody>\n</table>\n';
 | 
	
		
			
				|  |  | +    return tb;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  text = globals.converter._dispatch('tables.before', text, options, globals);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  text = text.replace(tableRgx, function (rawTable) {
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    var i, tableLines = rawTable.split('\n');
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    // strip wrong first and last column if wrapped tables are used
 | 
	
		
			
				|  |  | +    for (i = 0; i < tableLines.length; ++i) {
 | 
	
		
			
				|  |  | +      if (/^[ \t]{0,3}\|/.test(tableLines[i])) {
 | 
	
		
			
				|  |  | +        tableLines[i] = tableLines[i].replace(/^[ \t]{0,3}\|/, '');
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      if (/\|[ \t]*$/.test(tableLines[i])) {
 | 
	
		
			
				|  |  | +        tableLines[i] = tableLines[i].replace(/\|[ \t]*$/, '');
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    var rawHeaders = tableLines[0].split('|').map(function (s) { return s.trim();}),
 | 
	
		
			
				|  |  | +        rawStyles = tableLines[1].split('|').map(function (s) { return s.trim();}),
 | 
	
		
			
				|  |  | +        rawCells = [],
 | 
	
		
			
				|  |  | +        headers = [],
 | 
	
		
			
				|  |  | +        styles = [],
 | 
	
		
			
				|  |  | +        cells = [];
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    tableLines.shift();
 | 
	
		
			
				|  |  | +    tableLines.shift();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    for (i = 0; i < tableLines.length; ++i) {
 | 
	
		
			
				|  |  | +      if (tableLines[i].trim() === '') {
 | 
	
		
			
				|  |  | +        continue;
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      rawCells.push(
 | 
	
		
			
				|  |  | +        tableLines[i]
 | 
	
		
			
				|  |  | +          .split('|')
 | 
	
		
			
				|  |  | +          .map(function (s) {
 | 
	
		
			
				|  |  | +            return s.trim();
 | 
	
		
			
				|  |  | +          })
 | 
	
		
			
				|  |  | +      );
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if (rawHeaders.length < rawStyles.length) {
 | 
	
		
			
				|  |  | +      return rawTable;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    for (i = 0; i < rawStyles.length; ++i) {
 | 
	
		
			
				|  |  | +      styles.push(parseStyles(rawStyles[i]));
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    for (i = 0; i < rawHeaders.length; ++i) {
 | 
	
		
			
				|  |  | +      if (showdown.helper.isUndefined(styles[i])) {
 | 
	
		
			
				|  |  | +        styles[i] = '';
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      headers.push(parseHeaders(rawHeaders[i], styles[i]));
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    for (i = 0; i < rawCells.length; ++i) {
 | 
	
		
			
				|  |  | +      var row = [];
 | 
	
		
			
				|  |  | +      for (var ii = 0; ii < headers.length; ++ii) {
 | 
	
		
			
				|  |  | +        if (showdown.helper.isUndefined(rawCells[i][ii])) {
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        row.push(parseCells(rawCells[i][ii], styles[ii]));
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      cells.push(row);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    return buildTable(headers, cells);
 | 
	
		
			
				|  |  | +  });
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  text = globals.converter._dispatch('tables.after', text, options, globals);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  return text;
 | 
	
		
			
				|  |  | +});
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * Swap back in all the special characters we've hidden.
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +showdown.subParser('unescapeSpecialChars', function (text) {
 | 
	
		
			
				|  |  | +  'use strict';
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  text = text.replace(/~E(\d+)E/g, function (wholeMatch, m1) {
 | 
	
		
			
				|  |  | +    var charCodeToReplace = parseInt(m1);
 | 
	
		
			
				|  |  | +    return String.fromCharCode(charCodeToReplace);
 | 
	
		
			
				|  |  | +  });
 | 
	
		
			
				|  |  | +  return text;
 | 
	
		
			
				|  |  | +});
 | 
	
		
			
				|  |  | +module.exports = showdown;
 |