| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532 | /** *  * showdown: https://github.com/showdownjs/showdown *  * author: Di (微信小程序开发工程师) * organization: WeAppDev(微信小程序开发论坛)(http://weappdev.com) *               垂直微信小程序开发交流社区 *  * github地址: https://github.com/icindy/wxParse *  * for: 微信小程序富文本解析 * detail : http://weappdev.com/t/wxparse-alpha0-1-html-markdown/184 */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 propertiesvar 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;
 |