showdown.js 61 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244
  1. ;/*! showdown 12-07-2015 */
  2. (function(){
  3. /**
  4. * Created by Tivie on 06-01-2015.
  5. */
  6. // Private properties
  7. var showdown = {},
  8. parsers = {},
  9. extensions = {},
  10. defaultOptions = {
  11. omitExtraWLInCodeBlocks: false,
  12. prefixHeaderId: false,
  13. noHeaderId: false,
  14. headerLevelStart: 1,
  15. parseImgDimensions: false,
  16. simplifiedAutoLink: false,
  17. literalMidWordUnderscores: false,
  18. strikethrough: false,
  19. tables: false,
  20. tablesHeaderId: false,
  21. ghCodeBlocks: true, // true due to historical reasons
  22. tasklists: false
  23. },
  24. globalOptions = JSON.parse(JSON.stringify(defaultOptions)),
  25. flavor = {
  26. github: {
  27. omitExtraWLInCodeBlocks: true,
  28. prefixHeaderId: 'user-content-',
  29. simplifiedAutoLink: true,
  30. literalMidWordUnderscores: true,
  31. strikethrough: true,
  32. tables: true,
  33. tablesHeaderId: true,
  34. ghCodeBlocks: true,
  35. tasklists: true
  36. },
  37. vanilla: JSON.parse(JSON.stringify(defaultOptions))
  38. };
  39. /**
  40. * helper namespace
  41. * @type {{}}
  42. */
  43. showdown.helper = {};
  44. /**
  45. * TODO LEGACY SUPPORT CODE
  46. * @type {{}}
  47. */
  48. showdown.extensions = {};
  49. /**
  50. * Set a global option
  51. * @static
  52. * @param {string} key
  53. * @param {*} value
  54. * @returns {showdown}
  55. */
  56. showdown.setOption = function (key, value) {
  57. 'use strict';
  58. globalOptions[key] = value;
  59. return this;
  60. };
  61. /**
  62. * Get a global option
  63. * @static
  64. * @param {string} key
  65. * @returns {*}
  66. */
  67. showdown.getOption = function (key) {
  68. 'use strict';
  69. return globalOptions[key];
  70. };
  71. /**
  72. * Get the global options
  73. * @static
  74. * @returns {{}}
  75. */
  76. showdown.getOptions = function () {
  77. 'use strict';
  78. return globalOptions;
  79. };
  80. /**
  81. * Reset global options to the default values
  82. * @static
  83. */
  84. showdown.resetOptions = function () {
  85. 'use strict';
  86. globalOptions = JSON.parse(JSON.stringify(defaultOptions));
  87. };
  88. /**
  89. * Set the flavor showdown should use as default
  90. * @param {string} name
  91. */
  92. showdown.setFlavor = function (name) {
  93. 'use strict';
  94. if (flavor.hasOwnProperty(name)) {
  95. var preset = flavor[name];
  96. for (var option in preset) {
  97. if (preset.hasOwnProperty(option)) {
  98. globalOptions[option] = preset[option];
  99. }
  100. }
  101. }
  102. };
  103. /**
  104. * Get the default options
  105. * @static
  106. * @returns {{}}
  107. */
  108. showdown.getDefaultOptions = function () {
  109. 'use strict';
  110. return defaultOptions;
  111. };
  112. /**
  113. * Get or set a subParser
  114. *
  115. * subParser(name) - Get a registered subParser
  116. * subParser(name, func) - Register a subParser
  117. * @static
  118. * @param {string} name
  119. * @param {function} [func]
  120. * @returns {*}
  121. */
  122. showdown.subParser = function (name, func) {
  123. 'use strict';
  124. if (showdown.helper.isString(name)) {
  125. if (typeof func !== 'undefined') {
  126. parsers[name] = func;
  127. } else {
  128. if (parsers.hasOwnProperty(name)) {
  129. return parsers[name];
  130. } else {
  131. throw Error('SubParser named ' + name + ' not registered!');
  132. }
  133. }
  134. }
  135. };
  136. /**
  137. * Gets or registers an extension
  138. * @static
  139. * @param {string} name
  140. * @param {object|function=} ext
  141. * @returns {*}
  142. */
  143. showdown.extension = function (name, ext) {
  144. 'use strict';
  145. if (!showdown.helper.isString(name)) {
  146. throw Error('Extension \'name\' must be a string');
  147. }
  148. name = showdown.helper.stdExtName(name);
  149. // Getter
  150. if (showdown.helper.isUndefined(ext)) {
  151. if (!extensions.hasOwnProperty(name)) {
  152. throw Error('Extension named ' + name + ' is not registered!');
  153. }
  154. return extensions[name];
  155. // Setter
  156. } else {
  157. // Expand extension if it's wrapped in a function
  158. if (typeof ext === 'function') {
  159. ext = ext();
  160. }
  161. // Ensure extension is an array
  162. if (!showdown.helper.isArray(ext)) {
  163. ext = [ext];
  164. }
  165. var validExtension = validate(ext, name);
  166. if (validExtension.valid) {
  167. extensions[name] = ext;
  168. } else {
  169. throw Error(validExtension.error);
  170. }
  171. }
  172. };
  173. /**
  174. * Gets all extensions registered
  175. * @returns {{}}
  176. */
  177. showdown.getAllExtensions = function () {
  178. 'use strict';
  179. return extensions;
  180. };
  181. /**
  182. * Remove an extension
  183. * @param {string} name
  184. */
  185. showdown.removeExtension = function (name) {
  186. 'use strict';
  187. delete extensions[name];
  188. };
  189. /**
  190. * Removes all extensions
  191. */
  192. showdown.resetExtensions = function () {
  193. 'use strict';
  194. extensions = {};
  195. };
  196. /**
  197. * Validate extension
  198. * @param {array} extension
  199. * @param {string} name
  200. * @returns {{valid: boolean, error: string}}
  201. */
  202. function validate(extension, name) {
  203. 'use strict';
  204. var errMsg = (name) ? 'Error in ' + name + ' extension->' : 'Error in unnamed extension',
  205. ret = {
  206. valid: true,
  207. error: ''
  208. };
  209. if (!showdown.helper.isArray(extension)) {
  210. extension = [extension];
  211. }
  212. for (var i = 0; i < extension.length; ++i) {
  213. var baseMsg = errMsg + 'sub-extension ' + i + ': ',
  214. ext = extension[i];
  215. if (typeof ext !== 'object') {
  216. ret.valid = false;
  217. ret.error = baseMsg + 'must be an object, but ' + typeof ext + ' given';
  218. return ret;
  219. }
  220. if (!showdown.helper.isString(ext.type)) {
  221. ret.valid = false;
  222. ret.error = baseMsg + 'property "type" must be a string, but ' + typeof ext.type + ' given';
  223. return ret;
  224. }
  225. var type = ext.type = ext.type.toLowerCase();
  226. // normalize extension type
  227. if (type === 'language') {
  228. type = ext.type = 'lang';
  229. }
  230. if (type === 'html') {
  231. type = ext.type = 'output';
  232. }
  233. if (type !== 'lang' && type !== 'output') {
  234. ret.valid = false;
  235. ret.error = baseMsg + 'type ' + type + ' is not recognized. Valid values: "lang" or "output"';
  236. return ret;
  237. }
  238. if (ext.filter) {
  239. if (typeof ext.filter !== 'function') {
  240. ret.valid = false;
  241. ret.error = baseMsg + '"filter" must be a function, but ' + typeof ext.filter + ' given';
  242. return ret;
  243. }
  244. } else if (ext.regex) {
  245. if (showdown.helper.isString(ext.regex)) {
  246. ext.regex = new RegExp(ext.regex, 'g');
  247. }
  248. if (!ext.regex instanceof RegExp) {
  249. ret.valid = false;
  250. ret.error = baseMsg + '"regex" property must either be a string or a RegExp object, but ' +
  251. typeof ext.regex + ' given';
  252. return ret;
  253. }
  254. if (showdown.helper.isUndefined(ext.replace)) {
  255. ret.valid = false;
  256. ret.error = baseMsg + '"regex" extensions must implement a replace string or function';
  257. return ret;
  258. }
  259. } else {
  260. ret.valid = false;
  261. ret.error = baseMsg + 'extensions must define either a "regex" property or a "filter" method';
  262. return ret;
  263. }
  264. if (showdown.helper.isUndefined(ext.filter) && showdown.helper.isUndefined(ext.regex)) {
  265. ret.valid = false;
  266. ret.error = baseMsg + 'output extensions must define a filter property';
  267. return ret;
  268. }
  269. }
  270. return ret;
  271. }
  272. /**
  273. * Validate extension
  274. * @param {object} ext
  275. * @returns {boolean}
  276. */
  277. showdown.validateExtension = function (ext) {
  278. 'use strict';
  279. var validateExtension = validate(ext, null);
  280. if (!validateExtension.valid) {
  281. console.warn(validateExtension.error);
  282. return false;
  283. }
  284. return true;
  285. };
  286. /**
  287. * showdownjs helper functions
  288. */
  289. if (!showdown.hasOwnProperty('helper')) {
  290. showdown.helper = {};
  291. }
  292. /**
  293. * Check if var is string
  294. * @static
  295. * @param {string} a
  296. * @returns {boolean}
  297. */
  298. showdown.helper.isString = function isString(a) {
  299. 'use strict';
  300. return (typeof a === 'string' || a instanceof String);
  301. };
  302. /**
  303. * ForEach helper function
  304. * @static
  305. * @param {*} obj
  306. * @param {function} callback
  307. */
  308. showdown.helper.forEach = function forEach(obj, callback) {
  309. 'use strict';
  310. if (typeof obj.forEach === 'function') {
  311. obj.forEach(callback);
  312. } else {
  313. for (var i = 0; i < obj.length; i++) {
  314. callback(obj[i], i, obj);
  315. }
  316. }
  317. };
  318. /**
  319. * isArray helper function
  320. * @static
  321. * @param {*} a
  322. * @returns {boolean}
  323. */
  324. showdown.helper.isArray = function isArray(a) {
  325. 'use strict';
  326. return a.constructor === Array;
  327. };
  328. /**
  329. * Check if value is undefined
  330. * @static
  331. * @param {*} value The value to check.
  332. * @returns {boolean} Returns `true` if `value` is `undefined`, else `false`.
  333. */
  334. showdown.helper.isUndefined = function isUndefined(value) {
  335. 'use strict';
  336. return typeof value === 'undefined';
  337. };
  338. /**
  339. * Standardidize extension name
  340. * @static
  341. * @param {string} s extension name
  342. * @returns {string}
  343. */
  344. showdown.helper.stdExtName = function (s) {
  345. 'use strict';
  346. return s.replace(/[_-]||\s/g, '').toLowerCase();
  347. };
  348. function escapeCharactersCallback(wholeMatch, m1) {
  349. 'use strict';
  350. var charCodeToEscape = m1.charCodeAt(0);
  351. return '~E' + charCodeToEscape + 'E';
  352. }
  353. /**
  354. * Callback used to escape characters when passing through String.replace
  355. * @static
  356. * @param {string} wholeMatch
  357. * @param {string} m1
  358. * @returns {string}
  359. */
  360. showdown.helper.escapeCharactersCallback = escapeCharactersCallback;
  361. /**
  362. * Escape characters in a string
  363. * @static
  364. * @param {string} text
  365. * @param {string} charsToEscape
  366. * @param {boolean} afterBackslash
  367. * @returns {XML|string|void|*}
  368. */
  369. showdown.helper.escapeCharacters = function escapeCharacters(text, charsToEscape, afterBackslash) {
  370. 'use strict';
  371. // First we have to escape the escape characters so that
  372. // we can build a character class out of them
  373. var regexString = '([' + charsToEscape.replace(/([\[\]\\])/g, '\\$1') + '])';
  374. if (afterBackslash) {
  375. regexString = '\\\\' + regexString;
  376. }
  377. var regex = new RegExp(regexString, 'g');
  378. text = text.replace(regex, escapeCharactersCallback);
  379. return text;
  380. };
  381. /**
  382. * POLYFILLS
  383. */
  384. if (showdown.helper.isUndefined(console)) {
  385. console = {
  386. warn: function (msg) {
  387. 'use strict';
  388. alert(msg);
  389. },
  390. log: function (msg) {
  391. 'use strict';
  392. alert(msg);
  393. }
  394. };
  395. }
  396. /**
  397. * Created by Estevao on 31-05-2015.
  398. */
  399. /**
  400. * Showdown Converter class
  401. * @class
  402. * @param {object} [converterOptions]
  403. * @returns {
  404. * {makeHtml: Function},
  405. * {setOption: Function},
  406. * {getOption: Function},
  407. * {getOptions: Function}
  408. * }
  409. */
  410. showdown.Converter = function (converterOptions) {
  411. 'use strict';
  412. var
  413. /**
  414. * Options used by this converter
  415. * @private
  416. * @type {{}}
  417. */
  418. options = {},
  419. /**
  420. * Language extensions used by this converter
  421. * @private
  422. * @type {Array}
  423. */
  424. langExtensions = [],
  425. /**
  426. * Output modifiers extensions used by this converter
  427. * @private
  428. * @type {Array}
  429. */
  430. outputModifiers = [],
  431. /**
  432. * The parser Order
  433. * @private
  434. * @type {string[]}
  435. */
  436. parserOrder = [
  437. 'githubCodeBlocks',
  438. 'hashHTMLBlocks',
  439. 'stripLinkDefinitions',
  440. 'blockGamut',
  441. 'unescapeSpecialChars'
  442. ];
  443. _constructor();
  444. /**
  445. * Converter constructor
  446. * @private
  447. */
  448. function _constructor() {
  449. converterOptions = converterOptions || {};
  450. for (var gOpt in globalOptions) {
  451. if (globalOptions.hasOwnProperty(gOpt)) {
  452. options[gOpt] = globalOptions[gOpt];
  453. }
  454. }
  455. // Merge options
  456. if (typeof converterOptions === 'object') {
  457. for (var opt in converterOptions) {
  458. if (converterOptions.hasOwnProperty(opt)) {
  459. options[opt] = converterOptions[opt];
  460. }
  461. }
  462. } else {
  463. throw Error('Converter expects the passed parameter to be an object, but ' + typeof converterOptions +
  464. ' was passed instead.');
  465. }
  466. if (options.extensions) {
  467. showdown.helper.forEach(options.extensions, _parseExtension);
  468. }
  469. }
  470. /**
  471. * Parse extension
  472. * @param {*} ext
  473. * @private
  474. */
  475. function _parseExtension(ext) {
  476. // If it's a string, the extension was previously loaded
  477. if (showdown.helper.isString(ext)) {
  478. ext = showdown.helper.stdExtName(ext);
  479. // LEGACY_SUPPORT CODE
  480. if (showdown.extensions[ext]) {
  481. console.warn('DEPRECATION WARNING: ' + ext + ' is an old extension that uses a deprecated loading method.' +
  482. 'Please inform the developer that the extension should be updated!');
  483. legacyExtensionLoading(showdown.extensions[ext], ext);
  484. return;
  485. // END LEGACY SUPPORT CODE
  486. } else if (!showdown.helper.isUndefined(extensions[ext])) {
  487. ext = extensions[ext];
  488. } else {
  489. throw Error('Extension "' + ext + '" could not be loaded. It was either not found or is not a valid extension.');
  490. }
  491. }
  492. if (typeof ext === 'function') {
  493. ext = ext();
  494. }
  495. if (!showdown.helper.isArray(ext)) {
  496. ext = [ext];
  497. }
  498. if (!showdown.validateExtension(ext)) {
  499. return;
  500. }
  501. for (var i = 0; i < ext.length; ++i) {
  502. switch (ext[i].type) {
  503. case 'lang':
  504. langExtensions.push(ext[i]);
  505. break;
  506. case 'output':
  507. outputModifiers.push(ext[i]);
  508. break;
  509. default:
  510. // should never reach here
  511. throw Error('Extension loader error: Type unrecognized!!!');
  512. }
  513. }
  514. }
  515. /**
  516. * LEGACY_SUPPORT
  517. * @param {*} ext
  518. * @param {string} name
  519. */
  520. function legacyExtensionLoading(ext, name) {
  521. if (typeof ext === 'function') {
  522. ext = ext(new showdown.Converter());
  523. }
  524. if (!showdown.helper.isArray(ext)) {
  525. ext = [ext];
  526. }
  527. var valid = validate(ext, name);
  528. if (!valid.valid) {
  529. throw Error(valid.error);
  530. }
  531. for (var i = 0; i < ext.length; ++i) {
  532. switch (ext[i].type) {
  533. case 'lang':
  534. langExtensions.push(ext[i]);
  535. break;
  536. case 'output':
  537. outputModifiers.push(ext[i]);
  538. break;
  539. default:// should never reach here
  540. throw Error('Extension loader error: Type unrecognized!!!');
  541. }
  542. }
  543. }
  544. /**
  545. * Converts a markdown string into HTML
  546. * @param {string} text
  547. * @returns {*}
  548. */
  549. this.makeHtml = function (text) {
  550. //check if text is not falsy
  551. if (!text) {
  552. return text;
  553. }
  554. var globals = {
  555. gHtmlBlocks: [],
  556. gUrls: {},
  557. gTitles: {},
  558. gDimensions: {},
  559. gListLevel: 0,
  560. hashLinkCounts: {},
  561. langExtensions: langExtensions,
  562. outputModifiers: outputModifiers,
  563. converter: this
  564. };
  565. // attacklab: Replace ~ with ~T
  566. // This lets us use tilde as an escape char to avoid md5 hashes
  567. // The choice of character is arbitrary; anything that isn't
  568. // magic in Markdown will work.
  569. text = text.replace(/~/g, '~T');
  570. // attacklab: Replace $ with ~D
  571. // RegExp interprets $ as a special character
  572. // when it's in a replacement string
  573. text = text.replace(/\$/g, '~D');
  574. // Standardize line endings
  575. text = text.replace(/\r\n/g, '\n'); // DOS to Unix
  576. text = text.replace(/\r/g, '\n'); // Mac to Unix
  577. // Make sure text begins and ends with a couple of newlines:
  578. text = '\n\n' + text + '\n\n';
  579. // detab
  580. text = showdown.subParser('detab')(text, options, globals);
  581. // stripBlankLines
  582. text = showdown.subParser('stripBlankLines')(text, options, globals);
  583. //run languageExtensions
  584. showdown.helper.forEach(langExtensions, function (ext) {
  585. text = showdown.subParser('runExtension')(ext, text, options, globals);
  586. });
  587. // Run all registered parsers
  588. for (var i = 0; i < parserOrder.length; ++i) {
  589. var name = parserOrder[i];
  590. text = parsers[name](text, options, globals);
  591. }
  592. // attacklab: Restore dollar signs
  593. text = text.replace(/~D/g, '$$');
  594. // attacklab: Restore tildes
  595. text = text.replace(/~T/g, '~');
  596. // Run output modifiers
  597. showdown.helper.forEach(outputModifiers, function (ext) {
  598. text = showdown.subParser('runExtension')(ext, text, options, globals);
  599. });
  600. return text;
  601. };
  602. /**
  603. * Set an option of this Converter instance
  604. * @param {string} key
  605. * @param {*} value
  606. */
  607. this.setOption = function (key, value) {
  608. options[key] = value;
  609. };
  610. /**
  611. * Get the option of this Converter instance
  612. * @param {string} key
  613. * @returns {*}
  614. */
  615. this.getOption = function (key) {
  616. return options[key];
  617. };
  618. /**
  619. * Get the options of this Converter instance
  620. * @returns {{}}
  621. */
  622. this.getOptions = function () {
  623. return options;
  624. };
  625. /**
  626. * Add extension to THIS converter
  627. * @param {{}} extension
  628. */
  629. this.addExtension = function (extension) {
  630. _parseExtension(extension);
  631. };
  632. /**
  633. * Use a global registered extension with THIS converter
  634. * @param {string} extensionName Name of the previously registered extension
  635. */
  636. this.useExtension = function (extensionName) {
  637. _parseExtension(extensionName);
  638. };
  639. /**
  640. * Set the flavor THIS converter should use
  641. * @param {string} name
  642. */
  643. this.setFlavor = function (name) {
  644. if (flavor.hasOwnProperty(name)) {
  645. var preset = flavor[name];
  646. for (var option in preset) {
  647. if (preset.hasOwnProperty(option)) {
  648. options[option] = preset[option];
  649. }
  650. }
  651. }
  652. };
  653. /**
  654. * Remove an extension from THIS converter.
  655. * Note: This is a costly operation. It's better to initialize a new converter
  656. * and specify the extensions you wish to use
  657. * @param {Array} extension
  658. */
  659. this.removeExtension = function (extension) {
  660. if (!showdown.helper.isArray(extension)) {
  661. extension = [extension];
  662. }
  663. for (var a = 0; a < extension.length; ++a) {
  664. var ext = extension[a];
  665. for (var i = 0; i < langExtensions.length; ++i) {
  666. if (langExtensions[i] === ext) {
  667. langExtensions[i].splice(i, 1);
  668. }
  669. }
  670. for (var ii = 0; ii < outputModifiers.length; ++i) {
  671. if (outputModifiers[ii] === ext) {
  672. outputModifiers[ii].splice(i, 1);
  673. }
  674. }
  675. }
  676. };
  677. /**
  678. * Get all extension of THIS converter
  679. * @returns {{language: Array, output: Array}}
  680. */
  681. this.getAllExtensions = function () {
  682. return {
  683. language: langExtensions,
  684. output: outputModifiers
  685. };
  686. };
  687. };
  688. /**
  689. * Turn Markdown link shortcuts into XHTML <a> tags.
  690. */
  691. showdown.subParser('anchors', function (text, config, globals) {
  692. 'use strict';
  693. var writeAnchorTag = function (wholeMatch, m1, m2, m3, m4, m5, m6, m7) {
  694. if (showdown.helper.isUndefined(m7)) {
  695. m7 = '';
  696. }
  697. wholeMatch = m1;
  698. var linkText = m2,
  699. linkId = m3.toLowerCase(),
  700. url = m4,
  701. title = m7;
  702. if (!url) {
  703. if (!linkId) {
  704. // lower-case and turn embedded newlines into spaces
  705. linkId = linkText.toLowerCase().replace(/ ?\n/g, ' ');
  706. }
  707. url = '#' + linkId;
  708. if (!showdown.helper.isUndefined(globals.gUrls[linkId])) {
  709. url = globals.gUrls[linkId];
  710. if (!showdown.helper.isUndefined(globals.gTitles[linkId])) {
  711. title = globals.gTitles[linkId];
  712. }
  713. } else {
  714. if (wholeMatch.search(/\(\s*\)$/m) > -1) {
  715. // Special case for explicit empty url
  716. url = '';
  717. } else {
  718. return wholeMatch;
  719. }
  720. }
  721. }
  722. url = showdown.helper.escapeCharacters(url, '*_', false);
  723. var result = '<a href="' + url + '"';
  724. if (title !== '' && title !== null) {
  725. title = title.replace(/"/g, '&quot;');
  726. title = showdown.helper.escapeCharacters(title, '*_', false);
  727. result += ' title="' + title + '"';
  728. }
  729. result += '>' + linkText + '</a>';
  730. return result;
  731. };
  732. // First, handle reference-style links: [link text] [id]
  733. /*
  734. text = text.replace(/
  735. ( // wrap whole match in $1
  736. \[
  737. (
  738. (?:
  739. \[[^\]]*\] // allow brackets nested one level
  740. |
  741. [^\[] // or anything else
  742. )*
  743. )
  744. \]
  745. [ ]? // one optional space
  746. (?:\n[ ]*)? // one optional newline followed by spaces
  747. \[
  748. (.*?) // id = $3
  749. \]
  750. )()()()() // pad remaining backreferences
  751. /g,_DoAnchors_callback);
  752. */
  753. text = text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g, writeAnchorTag);
  754. //
  755. // Next, inline-style links: [link text](url "optional title")
  756. //
  757. /*
  758. text = text.replace(/
  759. ( // wrap whole match in $1
  760. \[
  761. (
  762. (?:
  763. \[[^\]]*\] // allow brackets nested one level
  764. |
  765. [^\[\]] // or anything else
  766. )
  767. )
  768. \]
  769. \( // literal paren
  770. [ \t]*
  771. () // no id, so leave $3 empty
  772. <?(.*?)>? // href = $4
  773. [ \t]*
  774. ( // $5
  775. (['"]) // quote char = $6
  776. (.*?) // Title = $7
  777. \6 // matching quote
  778. [ \t]* // ignore any spaces/tabs between closing quote and )
  779. )? // title is optional
  780. \)
  781. )
  782. /g,writeAnchorTag);
  783. */
  784. text = text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\]\([ \t]*()<?(.*?(?:\(.*?\).*?)?)>?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,
  785. writeAnchorTag);
  786. //
  787. // Last, handle reference-style shortcuts: [link text]
  788. // These must come last in case you've also got [link test][1]
  789. // or [link test](/foo)
  790. //
  791. /*
  792. text = text.replace(/
  793. ( // wrap whole match in $1
  794. \[
  795. ([^\[\]]+) // link text = $2; can't contain '[' or ']'
  796. \]
  797. )()()()()() // pad rest of backreferences
  798. /g, writeAnchorTag);
  799. */
  800. text = text.replace(/(\[([^\[\]]+)\])()()()()()/g, writeAnchorTag);
  801. return text;
  802. });
  803. showdown.subParser('autoLinks', function (text, options) {
  804. 'use strict';
  805. //simpleURLRegex = /\b(((https?|ftp|dict):\/\/|www\.)[-.+~:?#@!$&'()*,;=[\]\w]+)\b/gi,
  806. var simpleURLRegex = /\b(((https?|ftp|dict):\/\/|www\.)[^'">\s]+\.[^'">\s]+)(?=\s|$)(?!["<>])/gi,
  807. delimUrlRegex = /<(((https?|ftp|dict):\/\/|www\.)[^'">\s]+)>/gi,
  808. simpleMailRegex = /\b(?:mailto:)?([-.\w]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)\b/gi,
  809. delimMailRegex = /<(?:mailto:)?([-.\w]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi;
  810. text = text.replace(delimUrlRegex, '<a href=\"$1\">$1</a>');
  811. text = text.replace(delimMailRegex, replaceMail);
  812. //simpleURLRegex = /\b(((https?|ftp|dict):\/\/|www\.)[-.+~:?#@!$&'()*,;=[\]\w]+)\b/gi,
  813. // Email addresses: <address@domain.foo>
  814. if (options.simplifiedAutoLink) {
  815. text = text.replace(simpleURLRegex, '<a href=\"$1\">$1</a>');
  816. text = text.replace(simpleMailRegex, '<a href=\"$1\">$1</a>');
  817. }
  818. function replaceMail(wholeMatch, m1) {
  819. var unescapedStr = showdown.subParser('unescapeSpecialChars')(m1);
  820. return showdown.subParser('encodeEmailAddress')(unescapedStr);
  821. }
  822. return text;
  823. });
  824. /**
  825. * These are all the transformations that form block-level
  826. * tags like paragraphs, headers, and list items.
  827. */
  828. showdown.subParser('blockGamut', function (text, options, globals) {
  829. 'use strict';
  830. text = showdown.subParser('headers')(text, options, globals);
  831. // Do Horizontal Rules:
  832. var key = showdown.subParser('hashBlock')('<hr />', options, globals);
  833. text = text.replace(/^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$/gm, key);
  834. text = text.replace(/^[ ]{0,2}([ ]?\-[ ]?){3,}[ \t]*$/gm, key);
  835. text = text.replace(/^[ ]{0,2}([ ]?_[ ]?){3,}[ \t]*$/gm, key);
  836. text = showdown.subParser('tables')(text, options, globals);
  837. text = showdown.subParser('lists')(text, options, globals);
  838. text = showdown.subParser('codeBlocks')(text, options, globals);
  839. text = showdown.subParser('blockQuotes')(text, options, globals);
  840. // We already ran _HashHTMLBlocks() before, in Markdown(), but that
  841. // was to escape raw HTML in the original Markdown source. This time,
  842. // we're escaping the markup we've just created, so that we don't wrap
  843. // <p> tags around block-level tags.
  844. text = showdown.subParser('hashHTMLBlocks')(text, options, globals);
  845. text = showdown.subParser('paragraphs')(text, options, globals);
  846. return text;
  847. });
  848. showdown.subParser('blockQuotes', function (text, options, globals) {
  849. 'use strict';
  850. /*
  851. text = text.replace(/
  852. ( // Wrap whole match in $1
  853. (
  854. ^[ \t]*>[ \t]? // '>' at the start of a line
  855. .+\n // rest of the first line
  856. (.+\n)* // subsequent consecutive lines
  857. \n* // blanks
  858. )+
  859. )
  860. /gm, function(){...});
  861. */
  862. text = text.replace(/((^[ \t]*>[ \t]?.+\n(.+\n)*\n*)+)/gm, function (wholeMatch, m1) {
  863. var bq = m1;
  864. // attacklab: hack around Konqueror 3.5.4 bug:
  865. // "----------bug".replace(/^-/g,"") == "bug"
  866. bq = bq.replace(/^[ \t]*>[ \t]?/gm, '~0'); // trim one level of quoting
  867. // attacklab: clean up hack
  868. bq = bq.replace(/~0/g, '');
  869. bq = bq.replace(/^[ \t]+$/gm, ''); // trim whitespace-only lines
  870. bq = showdown.subParser('blockGamut')(bq, options, globals); // recurse
  871. bq = bq.replace(/(^|\n)/g, '$1 ');
  872. // These leading spaces screw with <pre> content, so we need to fix that:
  873. bq = bq.replace(/(\s*<pre>[^\r]+?<\/pre>)/gm, function (wholeMatch, m1) {
  874. var pre = m1;
  875. // attacklab: hack around Konqueror 3.5.4 bug:
  876. pre = pre.replace(/^ /mg, '~0');
  877. pre = pre.replace(/~0/g, '');
  878. return pre;
  879. });
  880. return showdown.subParser('hashBlock')('<blockquote>\n' + bq + '\n</blockquote>', options, globals);
  881. });
  882. return text;
  883. });
  884. /**
  885. * Process Markdown `<pre><code>` blocks.
  886. */
  887. showdown.subParser('codeBlocks', function (text, options, globals) {
  888. 'use strict';
  889. /*
  890. text = text.replace(text,
  891. /(?:\n\n|^)
  892. ( // $1 = the code block -- one or more lines, starting with a space/tab
  893. (?:
  894. (?:[ ]{4}|\t) // Lines must start with a tab or a tab-width of spaces - attacklab: g_tab_width
  895. .*\n+
  896. )+
  897. )
  898. (\n*[ ]{0,3}[^ \t\n]|(?=~0)) // attacklab: g_tab_width
  899. /g,function(){...});
  900. */
  901. // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
  902. text += '~0';
  903. var pattern = /(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=~0))/g;
  904. text = text.replace(pattern, function (wholeMatch, m1, m2) {
  905. var codeblock = m1,
  906. nextChar = m2,
  907. end = '\n';
  908. codeblock = showdown.subParser('outdent')(codeblock);
  909. codeblock = showdown.subParser('encodeCode')(codeblock);
  910. codeblock = showdown.subParser('detab')(codeblock);
  911. codeblock = codeblock.replace(/^\n+/g, ''); // trim leading newlines
  912. codeblock = codeblock.replace(/\n+$/g, ''); // trim trailing newlines
  913. if (options.omitExtraWLInCodeBlocks) {
  914. end = '';
  915. }
  916. codeblock = '<pre><code>' + codeblock + end + '</code></pre>';
  917. return showdown.subParser('hashBlock')(codeblock, options, globals) + nextChar;
  918. });
  919. // attacklab: strip sentinel
  920. text = text.replace(/~0/, '');
  921. return text;
  922. });
  923. /**
  924. *
  925. * * Backtick quotes are used for <code></code> spans.
  926. *
  927. * * You can use multiple backticks as the delimiters if you want to
  928. * include literal backticks in the code span. So, this input:
  929. *
  930. * Just type ``foo `bar` baz`` at the prompt.
  931. *
  932. * Will translate to:
  933. *
  934. * <p>Just type <code>foo `bar` baz</code> at the prompt.</p>
  935. *
  936. * There's no arbitrary limit to the number of backticks you
  937. * can use as delimters. If you need three consecutive backticks
  938. * in your code, use four for delimiters, etc.
  939. *
  940. * * You can use spaces to get literal backticks at the edges:
  941. *
  942. * ... type `` `bar` `` ...
  943. *
  944. * Turns to:
  945. *
  946. * ... type <code>`bar`</code> ...
  947. */
  948. showdown.subParser('codeSpans', function (text) {
  949. 'use strict';
  950. /*
  951. text = text.replace(/
  952. (^|[^\\]) // Character before opening ` can't be a backslash
  953. (`+) // $2 = Opening run of `
  954. ( // $3 = The code block
  955. [^\r]*?
  956. [^`] // attacklab: work around lack of lookbehind
  957. )
  958. \2 // Matching closer
  959. (?!`)
  960. /gm, function(){...});
  961. */
  962. text = text.replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm, function (wholeMatch, m1, m2, m3) {
  963. var c = m3;
  964. c = c.replace(/^([ \t]*)/g, ''); // leading whitespace
  965. c = c.replace(/[ \t]*$/g, ''); // trailing whitespace
  966. c = showdown.subParser('encodeCode')(c);
  967. return m1 + '<code>' + c + '</code>';
  968. });
  969. return text;
  970. });
  971. /**
  972. * Convert all tabs to spaces
  973. */
  974. showdown.subParser('detab', function (text) {
  975. 'use strict';
  976. // expand first n-1 tabs
  977. text = text.replace(/\t(?=\t)/g, ' '); // g_tab_width
  978. // replace the nth with two sentinels
  979. text = text.replace(/\t/g, '~A~B');
  980. // use the sentinel to anchor our regex so it doesn't explode
  981. text = text.replace(/~B(.+?)~A/g, function (wholeMatch, m1) {
  982. var leadingText = m1,
  983. numSpaces = 4 - leadingText.length % 4; // g_tab_width
  984. // there *must* be a better way to do this:
  985. for (var i = 0; i < numSpaces; i++) {
  986. leadingText += ' ';
  987. }
  988. return leadingText;
  989. });
  990. // clean up sentinels
  991. text = text.replace(/~A/g, ' '); // g_tab_width
  992. text = text.replace(/~B/g, '');
  993. return text;
  994. });
  995. /**
  996. * Smart processing for ampersands and angle brackets that need to be encoded.
  997. */
  998. showdown.subParser('encodeAmpsAndAngles', function (text) {
  999. 'use strict';
  1000. // Ampersand-encoding based entirely on Nat Irons's Amputator MT plugin:
  1001. // http://bumppo.net/projects/amputator/
  1002. text = text.replace(/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/g, '&amp;');
  1003. // Encode naked <'s
  1004. text = text.replace(/<(?![a-z\/?\$!])/gi, '&lt;');
  1005. return text;
  1006. });
  1007. /**
  1008. * Returns the string, with after processing the following backslash escape sequences.
  1009. *
  1010. * attacklab: The polite way to do this is with the new escapeCharacters() function:
  1011. *
  1012. * text = escapeCharacters(text,"\\",true);
  1013. * text = escapeCharacters(text,"`*_{}[]()>#+-.!",true);
  1014. *
  1015. * ...but we're sidestepping its use of the (slow) RegExp constructor
  1016. * as an optimization for Firefox. This function gets called a LOT.
  1017. */
  1018. showdown.subParser('encodeBackslashEscapes', function (text) {
  1019. 'use strict';
  1020. text = text.replace(/\\(\\)/g, showdown.helper.escapeCharactersCallback);
  1021. text = text.replace(/\\([`*_{}\[\]()>#+-.!])/g, showdown.helper.escapeCharactersCallback);
  1022. return text;
  1023. });
  1024. /**
  1025. * Encode/escape certain characters inside Markdown code runs.
  1026. * The point is that in code, these characters are literals,
  1027. * and lose their special Markdown meanings.
  1028. */
  1029. showdown.subParser('encodeCode', function (text) {
  1030. 'use strict';
  1031. // Encode all ampersands; HTML entities are not
  1032. // entities within a Markdown code span.
  1033. text = text.replace(/&/g, '&amp;');
  1034. // Do the angle bracket song and dance:
  1035. text = text.replace(/</g, '&lt;');
  1036. text = text.replace(/>/g, '&gt;');
  1037. // Now, escape characters that are magic in Markdown:
  1038. text = showdown.helper.escapeCharacters(text, '*_{}[]\\', false);
  1039. // jj the line above breaks this:
  1040. //---
  1041. //* Item
  1042. // 1. Subitem
  1043. // special char: *
  1044. // ---
  1045. return text;
  1046. });
  1047. /**
  1048. * Input: an email address, e.g. "foo@example.com"
  1049. *
  1050. * Output: the email address as a mailto link, with each character
  1051. * of the address encoded as either a decimal or hex entity, in
  1052. * the hopes of foiling most address harvesting spam bots. E.g.:
  1053. *
  1054. * <a href="&#x6D;&#97;&#105;&#108;&#x74;&#111;:&#102;&#111;&#111;&#64;&#101;
  1055. * x&#x61;&#109;&#x70;&#108;&#x65;&#x2E;&#99;&#111;&#109;">&#102;&#111;&#111;
  1056. * &#64;&#101;x&#x61;&#109;&#x70;&#108;&#x65;&#x2E;&#99;&#111;&#109;</a>
  1057. *
  1058. * Based on a filter by Matthew Wickline, posted to the BBEdit-Talk
  1059. * mailing list: <http://tinyurl.com/yu7ue>
  1060. *
  1061. */
  1062. showdown.subParser('encodeEmailAddress', function (addr) {
  1063. 'use strict';
  1064. var encode = [
  1065. function (ch) {
  1066. return '&#' + ch.charCodeAt(0) + ';';
  1067. },
  1068. function (ch) {
  1069. return '&#x' + ch.charCodeAt(0).toString(16) + ';';
  1070. },
  1071. function (ch) {
  1072. return ch;
  1073. }
  1074. ];
  1075. addr = 'mailto:' + addr;
  1076. addr = addr.replace(/./g, function (ch) {
  1077. if (ch === '@') {
  1078. // this *must* be encoded. I insist.
  1079. ch = encode[Math.floor(Math.random() * 2)](ch);
  1080. } else if (ch !== ':') {
  1081. // leave ':' alone (to spot mailto: later)
  1082. var r = Math.random();
  1083. // roughly 10% raw, 45% hex, 45% dec
  1084. ch = (
  1085. r > 0.9 ? encode[2](ch) : r > 0.45 ? encode[1](ch) : encode[0](ch)
  1086. );
  1087. }
  1088. return ch;
  1089. });
  1090. addr = '<a href="' + addr + '">' + addr + '</a>';
  1091. addr = addr.replace(/">.+:/g, '">'); // strip the mailto: from the visible part
  1092. return addr;
  1093. });
  1094. /**
  1095. * Within tags -- meaning between < and > -- encode [\ ` * _] so they
  1096. * don't conflict with their use in Markdown for code, italics and strong.
  1097. */
  1098. showdown.subParser('escapeSpecialCharsWithinTagAttributes', function (text) {
  1099. 'use strict';
  1100. // Build a regex to find HTML tags and comments. See Friedl's
  1101. // "Mastering Regular Expressions", 2nd Ed., pp. 200-201.
  1102. var regex = /(<[a-z\/!$]("[^"]*"|'[^']*'|[^'">])*>|<!(--.*?--\s*)+>)/gi;
  1103. text = text.replace(regex, function (wholeMatch) {
  1104. var tag = wholeMatch.replace(/(.)<\/?code>(?=.)/g, '$1`');
  1105. tag = showdown.helper.escapeCharacters(tag, '\\`*_', false);
  1106. return tag;
  1107. });
  1108. return text;
  1109. });
  1110. /**
  1111. * Handle github codeblocks prior to running HashHTML so that
  1112. * HTML contained within the codeblock gets escaped properly
  1113. * Example:
  1114. * ```ruby
  1115. * def hello_world(x)
  1116. * puts "Hello, #{x}"
  1117. * end
  1118. * ```
  1119. */
  1120. showdown.subParser('githubCodeBlocks', function (text, options, globals) {
  1121. 'use strict';
  1122. // early exit if option is not enabled
  1123. if (!options.ghCodeBlocks) {
  1124. return text;
  1125. }
  1126. text += '~0';
  1127. text = text.replace(/(?:^|\n)```(.*)\n([\s\S]*?)\n```/g, function (wholeMatch, m1, m2) {
  1128. var language = m1,
  1129. codeblock = m2,
  1130. end = '\n';
  1131. if (options.omitExtraWLInCodeBlocks) {
  1132. end = '';
  1133. }
  1134. codeblock = showdown.subParser('encodeCode')(codeblock);
  1135. codeblock = showdown.subParser('detab')(codeblock);
  1136. codeblock = codeblock.replace(/^\n+/g, ''); // trim leading newlines
  1137. codeblock = codeblock.replace(/\n+$/g, ''); // trim trailing whitespace
  1138. codeblock = '<pre><code' + (language ? ' class="' + language + '"' : '') + '>' + codeblock + end + '</code></pre>';
  1139. return showdown.subParser('hashBlock')(codeblock, options, globals);
  1140. });
  1141. // attacklab: strip sentinel
  1142. text = text.replace(/~0/, '');
  1143. return text;
  1144. });
  1145. showdown.subParser('hashBlock', function (text, options, globals) {
  1146. 'use strict';
  1147. text = text.replace(/(^\n+|\n+$)/g, '');
  1148. return '\n\n~K' + (globals.gHtmlBlocks.push(text) - 1) + 'K\n\n';
  1149. });
  1150. showdown.subParser('hashElement', function (text, options, globals) {
  1151. 'use strict';
  1152. return function (wholeMatch, m1) {
  1153. var blockText = m1;
  1154. // Undo double lines
  1155. blockText = blockText.replace(/\n\n/g, '\n');
  1156. blockText = blockText.replace(/^\n/, '');
  1157. // strip trailing blank lines
  1158. blockText = blockText.replace(/\n+$/g, '');
  1159. // Replace the element text with a marker ("~KxK" where x is its key)
  1160. blockText = '\n\n~K' + (globals.gHtmlBlocks.push(blockText) - 1) + 'K\n\n';
  1161. return blockText;
  1162. };
  1163. });
  1164. showdown.subParser('hashHTMLBlocks', function (text, options, globals) {
  1165. 'use strict';
  1166. // attacklab: Double up blank lines to reduce lookaround
  1167. text = text.replace(/\n/g, '\n\n');
  1168. // Hashify HTML blocks:
  1169. // We only want to do this for block-level HTML tags, such as headers,
  1170. // lists, and tables. That's because we still want to wrap <p>s around
  1171. // "paragraphs" that are wrapped in non-block-level tags, such as anchors,
  1172. // phrase emphasis, and spans. The list of tags we're looking for is
  1173. // hard-coded:
  1174. //var block_tags_a =
  1175. // 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del|style|section|header|footer|nav|article|aside';
  1176. // var block_tags_b =
  1177. // 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|style|section|header|footer|nav|article|aside';
  1178. // First, look for nested blocks, e.g.:
  1179. // <div>
  1180. // <div>
  1181. // tags for inner block must be indented.
  1182. // </div>
  1183. // </div>
  1184. //
  1185. // The outermost tags must start at the left margin for this to match, and
  1186. // the inner nested divs must be indented.
  1187. // We need to do this before the next, more liberal match, because the next
  1188. // match will start at the first `<div>` and stop at the first `</div>`.
  1189. // attacklab: This regex can be expensive when it fails.
  1190. /*
  1191. var text = text.replace(/
  1192. ( // save in $1
  1193. ^ // start of line (with /m)
  1194. <($block_tags_a) // start tag = $2
  1195. \b // word break
  1196. // attacklab: hack around khtml/pcre bug...
  1197. [^\r]*?\n // any number of lines, minimally matching
  1198. </\2> // the matching end tag
  1199. [ \t]* // trailing spaces/tabs
  1200. (?=\n+) // followed by a newline
  1201. ) // attacklab: there are sentinel newlines at end of document
  1202. /gm,function(){...}};
  1203. */
  1204. text = text.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del)\b[^\r]*?\n<\/\2>[ \t]*(?=\n+))/gm,
  1205. showdown.subParser('hashElement')(text, options, globals));
  1206. //
  1207. // Now match more liberally, simply from `\n<tag>` to `</tag>\n`
  1208. //
  1209. /*
  1210. var text = text.replace(/
  1211. ( // save in $1
  1212. ^ // start of line (with /m)
  1213. <($block_tags_b) // start tag = $2
  1214. \b // word break
  1215. // attacklab: hack around khtml/pcre bug...
  1216. [^\r]*? // any number of lines, minimally matching
  1217. </\2> // the matching end tag
  1218. [ \t]* // trailing spaces/tabs
  1219. (?=\n+) // followed by a newline
  1220. ) // attacklab: there are sentinel newlines at end of document
  1221. /gm,function(){...}};
  1222. */
  1223. text = text.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|style|section|header|footer|nav|article|aside|address|audio|canvas|figure|hgroup|output|video)\b[^\r]*?<\/\2>[ \t]*(?=\n+)\n)/gm,
  1224. showdown.subParser('hashElement')(text, options, globals));
  1225. // Special case just for <hr />. It was easier to make a special case than
  1226. // to make the other regex more complicated.
  1227. /*
  1228. text = text.replace(/
  1229. ( // save in $1
  1230. \n\n // Starting after a blank line
  1231. [ ]{0,3}
  1232. (<(hr) // start tag = $2
  1233. \b // word break
  1234. ([^<>])*? //
  1235. \/?>) // the matching end tag
  1236. [ \t]*
  1237. (?=\n{2,}) // followed by a blank line
  1238. )
  1239. /g,showdown.subParser('hashElement')(text, options, globals));
  1240. */
  1241. text = text.replace(/(\n[ ]{0,3}(<(hr)\b([^<>])*?\/?>)[ \t]*(?=\n{2,}))/g,
  1242. showdown.subParser('hashElement')(text, options, globals));
  1243. // Special case for standalone HTML comments:
  1244. /*
  1245. text = text.replace(/
  1246. ( // save in $1
  1247. \n\n // Starting after a blank line
  1248. [ ]{0,3} // attacklab: g_tab_width - 1
  1249. <!
  1250. (--[^\r]*?--\s*)+
  1251. >
  1252. [ \t]*
  1253. (?=\n{2,}) // followed by a blank line
  1254. )
  1255. /g,showdown.subParser('hashElement')(text, options, globals));
  1256. */
  1257. text = text.replace(/(\n\n[ ]{0,3}<!(--[^\r]*?--\s*)+>[ \t]*(?=\n{2,}))/g,
  1258. showdown.subParser('hashElement')(text, options, globals));
  1259. // PHP and ASP-style processor instructions (<?...?> and <%...%>)
  1260. /*
  1261. text = text.replace(/
  1262. (?:
  1263. \n\n // Starting after a blank line
  1264. )
  1265. ( // save in $1
  1266. [ ]{0,3} // attacklab: g_tab_width - 1
  1267. (?:
  1268. <([?%]) // $2
  1269. [^\r]*?
  1270. \2>
  1271. )
  1272. [ \t]*
  1273. (?=\n{2,}) // followed by a blank line
  1274. )
  1275. /g,showdown.subParser('hashElement')(text, options, globals));
  1276. */
  1277. text = text.replace(/(?:\n\n)([ ]{0,3}(?:<([?%])[^\r]*?\2>)[ \t]*(?=\n{2,}))/g,
  1278. showdown.subParser('hashElement')(text, options, globals));
  1279. // attacklab: Undo double lines (see comment at top of this function)
  1280. text = text.replace(/\n\n/g, '\n');
  1281. return text;
  1282. });
  1283. showdown.subParser('headers', function (text, options, globals) {
  1284. 'use strict';
  1285. var prefixHeader = options.prefixHeaderId;
  1286. // Set text-style headers:
  1287. // Header 1
  1288. // ========
  1289. //
  1290. // Header 2
  1291. // --------
  1292. //
  1293. text = text.replace(/^(.+)[ \t]*\n=+[ \t]*\n+/gm, function (wholeMatch, m1) {
  1294. var spanGamut = showdown.subParser('spanGamut')(m1, options, globals),
  1295. hID = (options.noHeaderId) ? '' : ' id="' + headerId(m1) + '"',
  1296. hLevel = parseInt(options.headerLevelStart),
  1297. hashBlock = '<h' + hLevel + hID + '>' + spanGamut + '</h' + hLevel + '>';
  1298. return showdown.subParser('hashBlock')(hashBlock, options, globals);
  1299. });
  1300. text = text.replace(/^(.+)[ \t]*\n-+[ \t]*\n+/gm, function (matchFound, m1) {
  1301. var spanGamut = showdown.subParser('spanGamut')(m1, options, globals),
  1302. hID = (options.noHeaderId) ? '' : ' id="' + headerId(m1) + '"',
  1303. hLevel = parseInt(options.headerLevelStart) + 1,
  1304. hashBlock = '<h' + hLevel + hID + '>' + spanGamut + '</h' + hLevel + '>';
  1305. return showdown.subParser('hashBlock')(hashBlock, options, globals);
  1306. });
  1307. // atx-style headers:
  1308. // # Header 1
  1309. // ## Header 2
  1310. // ## Header 2 with closing hashes ##
  1311. // ...
  1312. // ###### Header 6
  1313. //
  1314. /*
  1315. text = text.replace(/
  1316. ^(\#{1,6}) // $1 = string of #'s
  1317. [ \t]*
  1318. (.+?) // $2 = Header text
  1319. [ \t]*
  1320. \#* // optional closing #'s (not counted)
  1321. \n+
  1322. /gm, function() {...});
  1323. */
  1324. text = text.replace(/^(#{1,6})[ \t]*(.+?)[ \t]*#*\n+/gm, function (wholeMatch, m1, m2) {
  1325. var span = showdown.subParser('spanGamut')(m2, options, globals),
  1326. hID = (options.noHeaderId) ? '' : ' id="' + headerId(m2) + '"',
  1327. hLevel = parseInt(options.headerLevelStart) - 1 + m1.length,
  1328. header = '<h' + hLevel + hID + '>' + span + '</h' + hLevel + '>';
  1329. return showdown.subParser('hashBlock')(header, options, globals);
  1330. });
  1331. function headerId(m) {
  1332. var title, escapedId = m.replace(/[^\w]/g, '').toLowerCase();
  1333. if (globals.hashLinkCounts[escapedId]) {
  1334. title = escapedId + '-' + (globals.hashLinkCounts[escapedId]++);
  1335. } else {
  1336. title = escapedId;
  1337. globals.hashLinkCounts[escapedId] = 1;
  1338. }
  1339. // Prefix id to prevent causing inadvertent pre-existing style matches.
  1340. if (prefixHeader === true) {
  1341. prefixHeader = 'section';
  1342. }
  1343. if (showdown.helper.isString(prefixHeader)) {
  1344. return prefixHeader + title;
  1345. }
  1346. return title;
  1347. }
  1348. return text;
  1349. });
  1350. /**
  1351. * Turn Markdown image shortcuts into <img> tags.
  1352. */
  1353. showdown.subParser('images', function (text, options, globals) {
  1354. 'use strict';
  1355. var inlineRegExp = /!\[(.*?)]\s?\([ \t]*()<?(\S+?)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*(?:(['"])(.*?)\6[ \t]*)?\)/g,
  1356. referenceRegExp = /!\[(.*?)][ ]?(?:\n[ ]*)?\[(.*?)]()()()()()/g;
  1357. function writeImageTag (wholeMatch, altText, linkId, url, width, height, m5, title) {
  1358. var gUrls = globals.gUrls,
  1359. gTitles = globals.gTitles,
  1360. gDims = globals.gDimensions;
  1361. linkId = linkId.toLowerCase();
  1362. if (!title) {
  1363. title = '';
  1364. }
  1365. if (url === '' || url === null) {
  1366. if (linkId === '' || linkId === null) {
  1367. // lower-case and turn embedded newlines into spaces
  1368. linkId = altText.toLowerCase().replace(/ ?\n/g, ' ');
  1369. }
  1370. url = '#' + linkId;
  1371. if (!showdown.helper.isUndefined(gUrls[linkId])) {
  1372. url = gUrls[linkId];
  1373. if (!showdown.helper.isUndefined(gTitles[linkId])) {
  1374. title = gTitles[linkId];
  1375. }
  1376. if (!showdown.helper.isUndefined(gDims[linkId])) {
  1377. width = gDims[linkId].width;
  1378. height = gDims[linkId].height;
  1379. }
  1380. } else {
  1381. return wholeMatch;
  1382. }
  1383. }
  1384. altText = altText.replace(/"/g, '&quot;');
  1385. url = showdown.helper.escapeCharacters(url, '*_', false);
  1386. var result = '<img src="' + url + '" alt="' + altText + '"';
  1387. if (title) {
  1388. title = title.replace(/"/g, '&quot;');
  1389. title = showdown.helper.escapeCharacters(title, '*_', false);
  1390. result += ' title="' + title + '"';
  1391. }
  1392. if (width && height) {
  1393. width = (width === '*') ? 'auto' : width;
  1394. height = (height === '*') ? 'auto' : height;
  1395. result += ' width="' + width + '"';
  1396. result += ' height="' + height + '"';
  1397. }
  1398. result += ' />';
  1399. return result;
  1400. }
  1401. // First, handle reference-style labeled images: ![alt text][id]
  1402. text = text.replace(referenceRegExp, writeImageTag);
  1403. // Next, handle inline images: ![alt text](url =<width>x<height> "optional title")
  1404. text = text.replace(inlineRegExp, writeImageTag);
  1405. return text;
  1406. });
  1407. showdown.subParser('italicsAndBold', function (text, options) {
  1408. 'use strict';
  1409. if (options.literalMidWordUnderscores) {
  1410. //underscores
  1411. // Since we are consuming a \s character, we need to add it
  1412. text = text.replace(/(\s)__(?=\S)([^]+?)__(?=\s)/g, '$1<strong>$2</strong>');
  1413. text = text.replace(/(\s)_(?=\S)([^]+?)_(?=\s)/g, '$1<em>$2</em>');
  1414. //asterisks
  1415. text = text.replace(/\*\*(?=\S)([^]+?)\*\*/g, '<strong>$1</strong>');
  1416. text = text.replace(/\*(?=\S)([^]+?)\*/g, '<em>$1</em>');
  1417. } else {
  1418. // <strong> must go first:
  1419. text = text.replace(/(\*\*|__)(?=\S)([^\r]*?\S[*_]*)\1/g, '<strong>$2</strong>');
  1420. text = text.replace(/(\*|_)(?=\S)([^\r]*?\S)\1/g, '<em>$2</em>');
  1421. }
  1422. return text;
  1423. });
  1424. /**
  1425. * Form HTML ordered (numbered) and unordered (bulleted) lists.
  1426. */
  1427. showdown.subParser('lists', function (text, options, globals) {
  1428. 'use strict';
  1429. var spl = '~1';
  1430. /**
  1431. * Process the contents of a single ordered or unordered list, splitting it
  1432. * into individual list items.
  1433. * @param {string} listStr
  1434. * @returns {string}
  1435. */
  1436. function processListItems (listStr) {
  1437. // The $g_list_level global keeps track of when we're inside a list.
  1438. // Each time we enter a list, we increment it; when we leave a list,
  1439. // we decrement. If it's zero, we're not in a list anymore.
  1440. //
  1441. // We do this because when we're not inside a list, we want to treat
  1442. // something like this:
  1443. //
  1444. // I recommend upgrading to version
  1445. // 8. Oops, now this line is treated
  1446. // as a sub-list.
  1447. //
  1448. // As a single paragraph, despite the fact that the second line starts
  1449. // with a digit-period-space sequence.
  1450. //
  1451. // Whereas when we're inside a list (or sub-list), that line will be
  1452. // treated as the start of a sub-list. What a kludge, huh? This is
  1453. // an aspect of Markdown's syntax that's hard to parse perfectly
  1454. // without resorting to mind-reading. Perhaps the solution is to
  1455. // change the syntax rules such that sub-lists must start with a
  1456. // starting cardinal number; e.g. "1." or "a.".
  1457. globals.gListLevel++;
  1458. // trim trailing blank lines:
  1459. listStr = listStr.replace(/\n{2,}$/, '\n');
  1460. // attacklab: add sentinel to emulate \z
  1461. listStr += '~0';
  1462. var rgx = /(\n)?(^[ \t]*)([*+-]|\d+[.])[ \t]+((\[(x| )?])?[ \t]*[^\r]+?(\n{1,2}))(?=\n*(~0|\2([*+-]|\d+[.])[ \t]+))/gm;
  1463. listStr = listStr.replace(rgx, function (wholeMatch, m1, m2, m3, m4, taskbtn, checked) {
  1464. checked = (checked && checked.trim() !== '');
  1465. var item = showdown.subParser('outdent')(m4, options, globals);
  1466. //m1 - LeadingLine
  1467. if (m1 || (item.search(/\n{2,}/) > -1)) {
  1468. item = showdown.subParser('blockGamut')(item, options, globals);
  1469. } else {
  1470. if (taskbtn && options.tasklists) {
  1471. item = item.replace(taskbtn, function () {
  1472. var otp = '<input type="checkbox" disabled style="margin: 0px 0.35em 0.25em -1.6em; vertical-align: middle;"';
  1473. if (checked) {
  1474. otp += ' checked';
  1475. }
  1476. otp += '>';
  1477. return otp;
  1478. });
  1479. }
  1480. // Recursion for sub-lists:
  1481. item = showdown.subParser('lists')(item, options, globals);
  1482. item = item.replace(/\n$/, ''); // chomp(item)
  1483. item = showdown.subParser('spanGamut')(item, options, globals);
  1484. }
  1485. // this is a "hack" to differentiate between ordered and unordered lists
  1486. // related to issue #142
  1487. var tp = (m3.search(/[*+-]/g) > -1) ? 'ul' : 'ol',
  1488. bulletStyle = '';
  1489. if (taskbtn) {
  1490. bulletStyle = ' class="task-list-item" style="list-style-type: none;"';
  1491. }
  1492. return spl + tp + '<li' + bulletStyle + '>' + item + '</li>\n';
  1493. });
  1494. // attacklab: strip sentinel
  1495. listStr = listStr.replace(/~0/g, '');
  1496. globals.gListLevel--;
  1497. return listStr;
  1498. }
  1499. /**
  1500. * Slit consecutive ol/ul lists (related to issue 142)
  1501. * @param {Array} results
  1502. * @param {string} listType
  1503. * @returns {string|*}
  1504. */
  1505. function splitConsecutiveLists (results, listType) {
  1506. // parsing html with regex...
  1507. // This will surely fail if some extension decides to change paragraph markup directly
  1508. var cthulhu = /(<p[^>]+?>|<p>|<\/p>)/img,
  1509. holder = [[]],
  1510. res = '',
  1511. y = 0;
  1512. // Initialize first sublist
  1513. holder[0].type = listType;
  1514. for (var i = 0; i < results.length; ++i) {
  1515. var txt = results[i].slice(2),
  1516. nListType = results[i].slice(0, 2);
  1517. if (listType !== nListType) {
  1518. y++;
  1519. holder[y] = [];
  1520. holder[y].type = nListType;
  1521. listType = nListType;
  1522. }
  1523. holder[y].push(txt);
  1524. }
  1525. for (i = 0; i < holder.length; ++i) {
  1526. res += '<' + holder[i].type + '>\n';
  1527. for (var ii = 0; ii < holder[i].length; ++ii) {
  1528. if (holder[i].length > 1 && ii === holder[i].length - 1 && !cthulhu.test(holder[i][ii - 1])) {
  1529. //holder[i][ii] = holder[i][ii].replace(cthulhu, '');
  1530. }
  1531. res += holder[i][ii];
  1532. }
  1533. res += '</' + holder[i].type + '>\n';
  1534. }
  1535. return res;
  1536. }
  1537. // attacklab: add sentinel to hack around khtml/safari bug:
  1538. // http://bugs.webkit.org/show_bug.cgi?id=11231
  1539. text += '~0';
  1540. // Re-usable pattern to match any entire ul or ol list:
  1541. var wholeList = /^(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm;
  1542. if (globals.gListLevel) {
  1543. text = text.replace(wholeList, function (wholeMatch, m1, m2) {
  1544. var listType = (m2.search(/[*+-]/g) > -1) ? 'ul' : 'ol',
  1545. result = processListItems(m1);
  1546. // Turn double returns into triple returns, so that we can make a
  1547. // paragraph for the last item in a list, if necessary:
  1548. //list = list.replace(/\n{2,}/g, '\n\n\n');
  1549. //result = processListItems(list);
  1550. // Trim any trailing whitespace, to put the closing `</$list_type>`
  1551. // up on the preceding line, to get it past the current stupid
  1552. // HTML block parser. This is a hack to work around the terrible
  1553. // hack that is the HTML block parser.
  1554. result = result.replace(/\s+$/, '');
  1555. var splRes = result.split(spl);
  1556. splRes.shift();
  1557. result = splitConsecutiveLists(splRes, listType);
  1558. return result;
  1559. });
  1560. } else {
  1561. wholeList = /(\n\n|^\n?)(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/g;
  1562. //wholeList = /(\n\n|^\n?)( {0,3}([*+-]|\d+\.)[ \t]+[\s\S]+?)(?=(~0)|(\n\n(?!\t| {2,}| {0,3}([*+-]|\d+\.)[ \t])))/g;
  1563. text = text.replace(wholeList, function (wholeMatch, m1, m2, m3) {
  1564. // Turn double returns into triple returns, so that we can make a
  1565. // paragraph for the last item in a list, if necessary:
  1566. var list = m2.replace(/\n{2,}/g, '\n\n\n'),
  1567. //var list = (m2.slice(-2) !== '~0') ? m2 + '\n' : m2, //add a newline after the list
  1568. listType = (m3.search(/[*+-]/g) > -1) ? 'ul' : 'ol',
  1569. result = processListItems(list),
  1570. splRes = result.split(spl);
  1571. splRes.shift();
  1572. return m1 + splitConsecutiveLists(splRes, listType) + '\n';
  1573. });
  1574. }
  1575. // attacklab: strip sentinel
  1576. text = text.replace(/~0/, '');
  1577. return text;
  1578. });
  1579. /**
  1580. * Remove one level of line-leading tabs or spaces
  1581. */
  1582. showdown.subParser('outdent', function (text) {
  1583. 'use strict';
  1584. // attacklab: hack around Konqueror 3.5.4 bug:
  1585. // "----------bug".replace(/^-/g,"") == "bug"
  1586. text = text.replace(/^(\t|[ ]{1,4})/gm, '~0'); // attacklab: g_tab_width
  1587. // attacklab: clean up hack
  1588. text = text.replace(/~0/g, '');
  1589. return text;
  1590. });
  1591. /**
  1592. *
  1593. */
  1594. showdown.subParser('paragraphs', function (text, options, globals) {
  1595. 'use strict';
  1596. // Strip leading and trailing lines:
  1597. text = text.replace(/^\n+/g, '');
  1598. text = text.replace(/\n+$/g, '');
  1599. var grafs = text.split(/\n{2,}/g),
  1600. grafsOut = [],
  1601. end = grafs.length; // Wrap <p> tags
  1602. for (var i = 0; i < end; i++) {
  1603. var str = grafs[i];
  1604. // if this is an HTML marker, copy it
  1605. if (str.search(/~K(\d+)K/g) >= 0) {
  1606. grafsOut.push(str);
  1607. } else if (str.search(/\S/) >= 0) {
  1608. str = showdown.subParser('spanGamut')(str, options, globals);
  1609. str = str.replace(/^([ \t]*)/g, '<p>');
  1610. str += '</p>';
  1611. grafsOut.push(str);
  1612. }
  1613. }
  1614. /** Unhashify HTML blocks */
  1615. end = grafsOut.length;
  1616. for (i = 0; i < end; i++) {
  1617. // if this is a marker for an html block...
  1618. while (grafsOut[i].search(/~K(\d+)K/) >= 0) {
  1619. var blockText = globals.gHtmlBlocks[RegExp.$1];
  1620. blockText = blockText.replace(/\$/g, '$$$$'); // Escape any dollar signs
  1621. grafsOut[i] = grafsOut[i].replace(/~K\d+K/, blockText);
  1622. }
  1623. }
  1624. return grafsOut.join('\n\n');
  1625. });
  1626. /**
  1627. * Run extension
  1628. */
  1629. showdown.subParser('runExtension', function (ext, text, options, globals) {
  1630. 'use strict';
  1631. if (ext.filter) {
  1632. text = ext.filter(text, globals.converter, options);
  1633. } else if (ext.regex) {
  1634. // TODO remove this when old extension loading mechanism is deprecated
  1635. var re = ext.regex;
  1636. if (!re instanceof RegExp) {
  1637. re = new RegExp(re, 'g');
  1638. }
  1639. text = text.replace(re, ext.replace);
  1640. }
  1641. return text;
  1642. });
  1643. /**
  1644. * These are all the transformations that occur *within* block-level
  1645. * tags like paragraphs, headers, and list items.
  1646. */
  1647. showdown.subParser('spanGamut', function (text, options, globals) {
  1648. 'use strict';
  1649. text = showdown.subParser('codeSpans')(text, options, globals);
  1650. text = showdown.subParser('escapeSpecialCharsWithinTagAttributes')(text, options, globals);
  1651. text = showdown.subParser('encodeBackslashEscapes')(text, options, globals);
  1652. // Process anchor and image tags. Images must come first,
  1653. // because ![foo][f] looks like an anchor.
  1654. text = showdown.subParser('images')(text, options, globals);
  1655. text = showdown.subParser('anchors')(text, options, globals);
  1656. // Make links out of things like `<http://example.com/>`
  1657. // Must come after _DoAnchors(), because you can use < and >
  1658. // delimiters in inline links like [this](<url>).
  1659. text = showdown.subParser('autoLinks')(text, options, globals);
  1660. text = showdown.subParser('encodeAmpsAndAngles')(text, options, globals);
  1661. text = showdown.subParser('italicsAndBold')(text, options, globals);
  1662. text = showdown.subParser('strikethrough')(text, options, globals);
  1663. // Do hard breaks:
  1664. text = text.replace(/ +\n/g, ' <br />\n');
  1665. return text;
  1666. });
  1667. showdown.subParser('strikethrough', function (text, options) {
  1668. 'use strict';
  1669. if (options.strikethrough) {
  1670. text = text.replace(/(?:~T){2}([^~]+)(?:~T){2}/g, '<del>$1</del>');
  1671. }
  1672. return text;
  1673. });
  1674. /**
  1675. * Strip any lines consisting only of spaces and tabs.
  1676. * This makes subsequent regexs easier to write, because we can
  1677. * match consecutive blank lines with /\n+/ instead of something
  1678. * contorted like /[ \t]*\n+/
  1679. */
  1680. showdown.subParser('stripBlankLines', function (text) {
  1681. 'use strict';
  1682. return text.replace(/^[ \t]+$/mg, '');
  1683. });
  1684. /**
  1685. * Strips link definitions from text, stores the URLs and titles in
  1686. * hash references.
  1687. * Link defs are in the form: ^[id]: url "optional title"
  1688. *
  1689. * ^[ ]{0,3}\[(.+)\]: // id = $1 attacklab: g_tab_width - 1
  1690. * [ \t]*
  1691. * \n? // maybe *one* newline
  1692. * [ \t]*
  1693. * <?(\S+?)>? // url = $2
  1694. * [ \t]*
  1695. * \n? // maybe one newline
  1696. * [ \t]*
  1697. * (?:
  1698. * (\n*) // any lines skipped = $3 attacklab: lookbehind removed
  1699. * ["(]
  1700. * (.+?) // title = $4
  1701. * [")]
  1702. * [ \t]*
  1703. * )? // title is optional
  1704. * (?:\n+|$)
  1705. * /gm,
  1706. * function(){...});
  1707. *
  1708. */
  1709. showdown.subParser('stripLinkDefinitions', function (text, options, globals) {
  1710. 'use strict';
  1711. 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;
  1712. // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
  1713. text += '~0';
  1714. text = text.replace(regex, function (wholeMatch, linkId, url, width, height, blankLines, title) {
  1715. linkId = linkId.toLowerCase();
  1716. globals.gUrls[linkId] = showdown.subParser('encodeAmpsAndAngles')(url); // Link IDs are case-insensitive
  1717. if (blankLines) {
  1718. // Oops, found blank lines, so it's not a title.
  1719. // Put back the parenthetical statement we stole.
  1720. return blankLines + title;
  1721. } else {
  1722. if (title) {
  1723. globals.gTitles[linkId] = title.replace(/"|'/g, '&quot;');
  1724. }
  1725. if (options.parseImgDimensions && width && height) {
  1726. globals.gDimensions[linkId] = {
  1727. width: width,
  1728. height: height
  1729. };
  1730. }
  1731. }
  1732. // Completely remove the definition from the text
  1733. return '';
  1734. });
  1735. // attacklab: strip sentinel
  1736. text = text.replace(/~0/, '');
  1737. return text;
  1738. });
  1739. showdown.subParser('tables', function (text, options, globals) {
  1740. 'use strict';
  1741. var table = function () {
  1742. var tables = {},
  1743. filter;
  1744. tables.th = function (header, style) {
  1745. var id = '';
  1746. header = header.trim();
  1747. if (header === '') {
  1748. return '';
  1749. }
  1750. if (options.tableHeaderId) {
  1751. id = ' id="' + header.replace(/ /g, '_').toLowerCase() + '"';
  1752. }
  1753. if (style.trim() === '') {
  1754. style = '';
  1755. } else {
  1756. style = ' style="' + style + '"';
  1757. }
  1758. return '<th' + id + style + '>' + header + '</th>';
  1759. };
  1760. tables.td = function (cell, style) {
  1761. var subText = showdown.subParser('spanGamut')(cell.trim(), options, globals);
  1762. if (style.trim() === '') {
  1763. style = '';
  1764. } else {
  1765. style = ' style="' + style + '"';
  1766. }
  1767. return '<td' + style + '>' + subText + '</td>';
  1768. };
  1769. tables.ths = function () {
  1770. var out = '',
  1771. i = 0,
  1772. hs = [].slice.apply(arguments[0]),
  1773. style = [].slice.apply(arguments[1]);
  1774. for (i; i < hs.length; i += 1) {
  1775. out += tables.th(hs[i], style[i]) + '\n';
  1776. }
  1777. return out;
  1778. };
  1779. tables.tds = function () {
  1780. var out = '',
  1781. i = 0,
  1782. ds = [].slice.apply(arguments[0]),
  1783. style = [].slice.apply(arguments[1]);
  1784. for (i; i < ds.length; i += 1) {
  1785. out += tables.td(ds[i], style[i]) + '\n';
  1786. }
  1787. return out;
  1788. };
  1789. tables.thead = function () {
  1790. var out,
  1791. hs = [].slice.apply(arguments[0]),
  1792. style = [].slice.apply(arguments[1]);
  1793. out = '<thead>\n';
  1794. out += '<tr>\n';
  1795. out += tables.ths.apply(this, [hs, style]);
  1796. out += '</tr>\n';
  1797. out += '</thead>\n';
  1798. return out;
  1799. };
  1800. tables.tr = function () {
  1801. var out,
  1802. cs = [].slice.apply(arguments[0]),
  1803. style = [].slice.apply(arguments[1]);
  1804. out = '<tr>\n';
  1805. out += tables.tds.apply(this, [cs, style]);
  1806. out += '</tr>\n';
  1807. return out;
  1808. };
  1809. filter = function (text) {
  1810. var i = 0,
  1811. lines = text.split('\n'),
  1812. line,
  1813. hs,
  1814. out = [];
  1815. for (i; i < lines.length; i += 1) {
  1816. line = lines[i];
  1817. // looks like a table heading
  1818. if (line.trim().match(/^[|].*[|]$/)) {
  1819. line = line.trim();
  1820. var tbl = [],
  1821. align = lines[i + 1].trim(),
  1822. styles = [],
  1823. j = 0;
  1824. if (align.match(/^[|][-=|: ]+[|]$/)) {
  1825. styles = align.substring(1, align.length - 1).split('|');
  1826. for (j = 0; j < styles.length; ++j) {
  1827. styles[j] = styles[j].trim();
  1828. if (styles[j].match(/^[:][-=| ]+[:]$/)) {
  1829. styles[j] = 'text-align:center;';
  1830. } else if (styles[j].match(/^[-=| ]+[:]$/)) {
  1831. styles[j] = 'text-align:right;';
  1832. } else if (styles[j].match(/^[:][-=| ]+$/)) {
  1833. styles[j] = 'text-align:left;';
  1834. } else {
  1835. styles[j] = '';
  1836. }
  1837. }
  1838. }
  1839. tbl.push('<table>');
  1840. hs = line.substring(1, line.length - 1).split('|');
  1841. if (styles.length === 0) {
  1842. for (j = 0; j < hs.length; ++j) {
  1843. styles.push('text-align:left');
  1844. }
  1845. }
  1846. tbl.push(tables.thead.apply(this, [hs, styles]));
  1847. line = lines[++i];
  1848. if (!line.trim().match(/^[|][-=|: ]+[|]$/)) {
  1849. // not a table rolling back
  1850. line = lines[--i];
  1851. } else {
  1852. line = lines[++i];
  1853. tbl.push('<tbody>');
  1854. while (line.trim().match(/^[|].*[|]$/)) {
  1855. line = line.trim();
  1856. tbl.push(tables.tr.apply(this, [line.substring(1, line.length - 1).split('|'), styles]));
  1857. line = lines[++i];
  1858. }
  1859. tbl.push('</tbody>');
  1860. tbl.push('</table>');
  1861. // we are done with this table and we move along
  1862. out.push(tbl.join('\n'));
  1863. continue;
  1864. }
  1865. }
  1866. out.push(line);
  1867. }
  1868. return out.join('\n');
  1869. };
  1870. return {parse: filter};
  1871. };
  1872. if (options.tables) {
  1873. var tableParser = table();
  1874. return tableParser.parse(text);
  1875. } else {
  1876. return text;
  1877. }
  1878. });
  1879. /**
  1880. * Swap back in all the special characters we've hidden.
  1881. */
  1882. showdown.subParser('unescapeSpecialChars', function (text) {
  1883. 'use strict';
  1884. text = text.replace(/~E(\d+)E/g, function (wholeMatch, m1) {
  1885. var charCodeToReplace = parseInt(m1);
  1886. return String.fromCharCode(charCodeToReplace);
  1887. });
  1888. return text;
  1889. });
  1890. var root = this;
  1891. // CommonJS/nodeJS Loader
  1892. if (typeof module !== 'undefined' && module.exports) {
  1893. module.exports = showdown;
  1894. // AMD Loader
  1895. } else if (typeof define === 'function' && define.amd) {
  1896. define('showdown', function () {
  1897. 'use strict';
  1898. return showdown;
  1899. });
  1900. // Regular Browser loader
  1901. } else {
  1902. root.showdown = showdown;
  1903. }
  1904. }).call(this);
  1905. //# sourceMappingURL=showdown.js.map