UNPKG

160 kBJavaScriptView Raw
1;/*! showdown v 2.0.1 - 01-03-2022 */
2(function(){
3/**
4 * Created by Tivie on 13-07-2015.
5 */
6
7function getDefaultOpts (simple) {
8 'use strict';
9
10 var defaultOptions = {
11 omitExtraWLInCodeBlocks: {
12 defaultValue: false,
13 describe: 'Omit the default extra whiteline added to code blocks',
14 type: 'boolean'
15 },
16 noHeaderId: {
17 defaultValue: false,
18 describe: 'Turn on/off generated header id',
19 type: 'boolean'
20 },
21 prefixHeaderId: {
22 defaultValue: false,
23 describe: 'Add a prefix to the generated header ids. Passing a string will prefix that string to the header id. Setting to true will add a generic \'section-\' prefix',
24 type: 'string'
25 },
26 rawPrefixHeaderId: {
27 defaultValue: false,
28 describe: 'Setting this option to true will prevent showdown from modifying the prefix. This might result in malformed IDs (if, for instance, the " char is used in the prefix)',
29 type: 'boolean'
30 },
31 ghCompatibleHeaderId: {
32 defaultValue: false,
33 describe: 'Generate header ids compatible with github style (spaces are replaced with dashes, a bunch of non alphanumeric chars are removed)',
34 type: 'boolean'
35 },
36 rawHeaderId: {
37 defaultValue: false,
38 describe: 'Remove only spaces, \' and " from generated header ids (including prefixes), replacing them with dashes (-). WARNING: This might result in malformed ids',
39 type: 'boolean'
40 },
41 headerLevelStart: {
42 defaultValue: false,
43 describe: 'The header blocks level start',
44 type: 'integer'
45 },
46 parseImgDimensions: {
47 defaultValue: false,
48 describe: 'Turn on/off image dimension parsing',
49 type: 'boolean'
50 },
51 simplifiedAutoLink: {
52 defaultValue: false,
53 describe: 'Turn on/off GFM autolink style',
54 type: 'boolean'
55 },
56 excludeTrailingPunctuationFromURLs: {
57 defaultValue: false,
58 describe: 'Excludes trailing punctuation from links generated with autoLinking',
59 type: 'boolean'
60 },
61 literalMidWordUnderscores: {
62 defaultValue: false,
63 describe: 'Parse midword underscores as literal underscores',
64 type: 'boolean'
65 },
66 literalMidWordAsterisks: {
67 defaultValue: false,
68 describe: 'Parse midword asterisks as literal asterisks',
69 type: 'boolean'
70 },
71 strikethrough: {
72 defaultValue: false,
73 describe: 'Turn on/off strikethrough support',
74 type: 'boolean'
75 },
76 tables: {
77 defaultValue: false,
78 describe: 'Turn on/off tables support',
79 type: 'boolean'
80 },
81 tablesHeaderId: {
82 defaultValue: false,
83 describe: 'Add an id to table headers',
84 type: 'boolean'
85 },
86 ghCodeBlocks: {
87 defaultValue: true,
88 describe: 'Turn on/off GFM fenced code blocks support',
89 type: 'boolean'
90 },
91 tasklists: {
92 defaultValue: false,
93 describe: 'Turn on/off GFM tasklist support',
94 type: 'boolean'
95 },
96 smoothLivePreview: {
97 defaultValue: false,
98 describe: 'Prevents weird effects in live previews due to incomplete input',
99 type: 'boolean'
100 },
101 smartIndentationFix: {
102 defaultValue: false,
103 description: 'Tries to smartly fix indentation in es6 strings',
104 type: 'boolean'
105 },
106 disableForced4SpacesIndentedSublists: {
107 defaultValue: false,
108 description: 'Disables the requirement of indenting nested sublists by 4 spaces',
109 type: 'boolean'
110 },
111 simpleLineBreaks: {
112 defaultValue: false,
113 description: 'Parses simple line breaks as <br> (GFM Style)',
114 type: 'boolean'
115 },
116 requireSpaceBeforeHeadingText: {
117 defaultValue: false,
118 description: 'Makes adding a space between `#` and the header text mandatory (GFM Style)',
119 type: 'boolean'
120 },
121 ghMentions: {
122 defaultValue: false,
123 description: 'Enables github @mentions',
124 type: 'boolean'
125 },
126 ghMentionsLink: {
127 defaultValue: 'https://github.com/{u}',
128 description: 'Changes the link generated by @mentions. Only applies if ghMentions option is enabled.',
129 type: 'string'
130 },
131 encodeEmails: {
132 defaultValue: true,
133 description: 'Encode e-mail addresses through the use of Character Entities, transforming ASCII e-mail addresses into its equivalent decimal entities',
134 type: 'boolean'
135 },
136 openLinksInNewWindow: {
137 defaultValue: false,
138 description: 'Open all links in new windows',
139 type: 'boolean'
140 },
141 backslashEscapesHTMLTags: {
142 defaultValue: false,
143 description: 'Support for HTML Tag escaping. ex: \<div>foo\</div>',
144 type: 'boolean'
145 },
146 emoji: {
147 defaultValue: false,
148 description: 'Enable emoji support. Ex: `this is a :smile: emoji`',
149 type: 'boolean'
150 },
151 underline: {
152 defaultValue: false,
153 description: 'Enable support for underline. Syntax is double or triple underscores: `__underline word__`. With this option enabled, underscores no longer parses into `<em>` and `<strong>`',
154 type: 'boolean'
155 },
156 ellipsis: {
157 defaultValue: true,
158 description: 'Replaces three dots with the ellipsis unicode character',
159 type: 'boolean'
160 },
161 completeHTMLDocument: {
162 defaultValue: false,
163 description: 'Outputs a complete html document, including `<html>`, `<head>` and `<body>` tags',
164 type: 'boolean'
165 },
166 metadata: {
167 defaultValue: false,
168 description: 'Enable support for document metadata (defined at the top of the document between `«««` and `»»»` or between `---` and `---`).',
169 type: 'boolean'
170 },
171 splitAdjacentBlockquotes: {
172 defaultValue: false,
173 description: 'Split adjacent blockquote blocks',
174 type: 'boolean'
175 }
176 };
177 if (simple === false) {
178 return JSON.parse(JSON.stringify(defaultOptions));
179 }
180 var ret = {};
181 for (var opt in defaultOptions) {
182 if (defaultOptions.hasOwnProperty(opt)) {
183 ret[opt] = defaultOptions[opt].defaultValue;
184 }
185 }
186 return ret;
187}
188
189function allOptionsOn () {
190 'use strict';
191 var options = getDefaultOpts(true),
192 ret = {};
193 for (var opt in options) {
194 if (options.hasOwnProperty(opt)) {
195 ret[opt] = true;
196 }
197 }
198 return ret;
199}
200
201/**
202 * Created by Tivie on 06-01-2015.
203 */
204
205// Private properties
206var showdown = {},
207 parsers = {},
208 extensions = {},
209 globalOptions = getDefaultOpts(true),
210 setFlavor = 'vanilla',
211 flavor = {
212 github: {
213 omitExtraWLInCodeBlocks: true,
214 simplifiedAutoLink: true,
215 excludeTrailingPunctuationFromURLs: true,
216 literalMidWordUnderscores: true,
217 strikethrough: true,
218 tables: true,
219 tablesHeaderId: true,
220 ghCodeBlocks: true,
221 tasklists: true,
222 disableForced4SpacesIndentedSublists: true,
223 simpleLineBreaks: true,
224 requireSpaceBeforeHeadingText: true,
225 ghCompatibleHeaderId: true,
226 ghMentions: true,
227 backslashEscapesHTMLTags: true,
228 emoji: true,
229 splitAdjacentBlockquotes: true
230 },
231 original: {
232 noHeaderId: true,
233 ghCodeBlocks: false
234 },
235 ghost: {
236 omitExtraWLInCodeBlocks: true,
237 parseImgDimensions: true,
238 simplifiedAutoLink: true,
239 excludeTrailingPunctuationFromURLs: true,
240 literalMidWordUnderscores: true,
241 strikethrough: true,
242 tables: true,
243 tablesHeaderId: true,
244 ghCodeBlocks: true,
245 tasklists: true,
246 smoothLivePreview: true,
247 simpleLineBreaks: true,
248 requireSpaceBeforeHeadingText: true,
249 ghMentions: false,
250 encodeEmails: true
251 },
252 vanilla: getDefaultOpts(true),
253 allOn: allOptionsOn()
254 };
255
256/**
257 * helper namespace
258 * @type {{}}
259 */
260showdown.helper = {};
261
262/**
263 * TODO LEGACY SUPPORT CODE
264 * @type {{}}
265 */
266showdown.extensions = {};
267
268/**
269 * Set a global option
270 * @static
271 * @param {string} key
272 * @param {*} value
273 * @returns {showdown}
274 */
275showdown.setOption = function (key, value) {
276 'use strict';
277 globalOptions[key] = value;
278 return this;
279};
280
281/**
282 * Get a global option
283 * @static
284 * @param {string} key
285 * @returns {*}
286 */
287showdown.getOption = function (key) {
288 'use strict';
289 return globalOptions[key];
290};
291
292/**
293 * Get the global options
294 * @static
295 * @returns {{}}
296 */
297showdown.getOptions = function () {
298 'use strict';
299 return globalOptions;
300};
301
302/**
303 * Reset global options to the default values
304 * @static
305 */
306showdown.resetOptions = function () {
307 'use strict';
308 globalOptions = getDefaultOpts(true);
309};
310
311/**
312 * Set the flavor showdown should use as default
313 * @param {string} name
314 */
315showdown.setFlavor = function (name) {
316 'use strict';
317 if (!flavor.hasOwnProperty(name)) {
318 throw Error(name + ' flavor was not found');
319 }
320 showdown.resetOptions();
321 var preset = flavor[name];
322 setFlavor = name;
323 for (var option in preset) {
324 if (preset.hasOwnProperty(option)) {
325 globalOptions[option] = preset[option];
326 }
327 }
328};
329
330/**
331 * Get the currently set flavor
332 * @returns {string}
333 */
334showdown.getFlavor = function () {
335 'use strict';
336 return setFlavor;
337};
338
339/**
340 * Get the options of a specified flavor. Returns undefined if the flavor was not found
341 * @param {string} name Name of the flavor
342 * @returns {{}|undefined}
343 */
344showdown.getFlavorOptions = function (name) {
345 'use strict';
346 if (flavor.hasOwnProperty(name)) {
347 return flavor[name];
348 }
349};
350
351/**
352 * Get the default options
353 * @static
354 * @param {boolean} [simple=true]
355 * @returns {{}}
356 */
357showdown.getDefaultOptions = function (simple) {
358 'use strict';
359 return getDefaultOpts(simple);
360};
361
362/**
363 * Get or set a subParser
364 *
365 * subParser(name) - Get a registered subParser
366 * subParser(name, func) - Register a subParser
367 * @static
368 * @param {string} name
369 * @param {function} [func]
370 * @returns {*}
371 */
372showdown.subParser = function (name, func) {
373 'use strict';
374 if (showdown.helper.isString(name)) {
375 if (typeof func !== 'undefined') {
376 parsers[name] = func;
377 } else {
378 if (parsers.hasOwnProperty(name)) {
379 return parsers[name];
380 } else {
381 throw Error('SubParser named ' + name + ' not registered!');
382 }
383 }
384 }
385};
386
387/**
388 * Gets or registers an extension
389 * @static
390 * @param {string} name
391 * @param {object|function=} ext
392 * @returns {*}
393 */
394showdown.extension = function (name, ext) {
395 'use strict';
396
397 if (!showdown.helper.isString(name)) {
398 throw Error('Extension \'name\' must be a string');
399 }
400
401 name = showdown.helper.stdExtName(name);
402
403 // Getter
404 if (showdown.helper.isUndefined(ext)) {
405 if (!extensions.hasOwnProperty(name)) {
406 throw Error('Extension named ' + name + ' is not registered!');
407 }
408 return extensions[name];
409
410 // Setter
411 } else {
412 // Expand extension if it's wrapped in a function
413 if (typeof ext === 'function') {
414 ext = ext();
415 }
416
417 // Ensure extension is an array
418 if (!showdown.helper.isArray(ext)) {
419 ext = [ext];
420 }
421
422 var validExtension = validate(ext, name);
423
424 if (validExtension.valid) {
425 extensions[name] = ext;
426 } else {
427 throw Error(validExtension.error);
428 }
429 }
430};
431
432/**
433 * Gets all extensions registered
434 * @returns {{}}
435 */
436showdown.getAllExtensions = function () {
437 'use strict';
438 return extensions;
439};
440
441/**
442 * Remove an extension
443 * @param {string} name
444 */
445showdown.removeExtension = function (name) {
446 'use strict';
447 delete extensions[name];
448};
449
450/**
451 * Removes all extensions
452 */
453showdown.resetExtensions = function () {
454 'use strict';
455 extensions = {};
456};
457
458/**
459 * Validate extension
460 * @param {array} extension
461 * @param {string} name
462 * @returns {{valid: boolean, error: string}}
463 */
464function validate (extension, name) {
465 'use strict';
466
467 var errMsg = (name) ? 'Error in ' + name + ' extension->' : 'Error in unnamed extension',
468 ret = {
469 valid: true,
470 error: ''
471 };
472
473 if (!showdown.helper.isArray(extension)) {
474 extension = [extension];
475 }
476
477 for (var i = 0; i < extension.length; ++i) {
478 var baseMsg = errMsg + ' sub-extension ' + i + ': ',
479 ext = extension[i];
480 if (typeof ext !== 'object') {
481 ret.valid = false;
482 ret.error = baseMsg + 'must be an object, but ' + typeof ext + ' given';
483 return ret;
484 }
485
486 if (!showdown.helper.isString(ext.type)) {
487 ret.valid = false;
488 ret.error = baseMsg + 'property "type" must be a string, but ' + typeof ext.type + ' given';
489 return ret;
490 }
491
492 var type = ext.type = ext.type.toLowerCase();
493
494 // normalize extension type
495 if (type === 'language') {
496 type = ext.type = 'lang';
497 }
498
499 if (type === 'html') {
500 type = ext.type = 'output';
501 }
502
503 if (type !== 'lang' && type !== 'output' && type !== 'listener') {
504 ret.valid = false;
505 ret.error = baseMsg + 'type ' + type + ' is not recognized. Valid values: "lang/language", "output/html" or "listener"';
506 return ret;
507 }
508
509 if (type === 'listener') {
510 if (showdown.helper.isUndefined(ext.listeners)) {
511 ret.valid = false;
512 ret.error = baseMsg + '. Extensions of type "listener" must have a property called "listeners"';
513 return ret;
514 }
515 } else {
516 if (showdown.helper.isUndefined(ext.filter) && showdown.helper.isUndefined(ext.regex)) {
517 ret.valid = false;
518 ret.error = baseMsg + type + ' extensions must define either a "regex" property or a "filter" method';
519 return ret;
520 }
521 }
522
523 if (ext.listeners) {
524 if (typeof ext.listeners !== 'object') {
525 ret.valid = false;
526 ret.error = baseMsg + '"listeners" property must be an object but ' + typeof ext.listeners + ' given';
527 return ret;
528 }
529 for (var ln in ext.listeners) {
530 if (ext.listeners.hasOwnProperty(ln)) {
531 if (typeof ext.listeners[ln] !== 'function') {
532 ret.valid = false;
533 ret.error = baseMsg + '"listeners" property must be an hash of [event name]: [callback]. listeners.' + ln +
534 ' must be a function but ' + typeof ext.listeners[ln] + ' given';
535 return ret;
536 }
537 }
538 }
539 }
540
541 if (ext.filter) {
542 if (typeof ext.filter !== 'function') {
543 ret.valid = false;
544 ret.error = baseMsg + '"filter" must be a function, but ' + typeof ext.filter + ' given';
545 return ret;
546 }
547 } else if (ext.regex) {
548 if (showdown.helper.isString(ext.regex)) {
549 ext.regex = new RegExp(ext.regex, 'g');
550 }
551 if (!(ext.regex instanceof RegExp)) {
552 ret.valid = false;
553 ret.error = baseMsg + '"regex" property must either be a string or a RegExp object, but ' + typeof ext.regex + ' given';
554 return ret;
555 }
556 if (showdown.helper.isUndefined(ext.replace)) {
557 ret.valid = false;
558 ret.error = baseMsg + '"regex" extensions must implement a replace string or function';
559 return ret;
560 }
561 }
562 }
563 return ret;
564}
565
566/**
567 * Validate extension
568 * @param {object} ext
569 * @returns {boolean}
570 */
571showdown.validateExtension = function (ext) {
572 'use strict';
573
574 var validateExtension = validate(ext, null);
575 if (!validateExtension.valid) {
576 console.warn(validateExtension.error);
577 return false;
578 }
579 return true;
580};
581
582/**
583 * showdownjs helper functions
584 */
585
586if (!showdown.hasOwnProperty('helper')) {
587 showdown.helper = {};
588}
589
590/**
591 * Check if var is string
592 * @static
593 * @param {string} a
594 * @returns {boolean}
595 */
596showdown.helper.isString = function (a) {
597 'use strict';
598 return (typeof a === 'string' || a instanceof String);
599};
600
601/**
602 * Check if var is a function
603 * @static
604 * @param {*} a
605 * @returns {boolean}
606 */
607showdown.helper.isFunction = function (a) {
608 'use strict';
609 var getType = {};
610 return a && getType.toString.call(a) === '[object Function]';
611};
612
613/**
614 * isArray helper function
615 * @static
616 * @param {*} a
617 * @returns {boolean}
618 */
619showdown.helper.isArray = function (a) {
620 'use strict';
621 return Array.isArray(a);
622};
623
624/**
625 * Check if value is undefined
626 * @static
627 * @param {*} value The value to check.
628 * @returns {boolean} Returns `true` if `value` is `undefined`, else `false`.
629 */
630showdown.helper.isUndefined = function (value) {
631 'use strict';
632 return typeof value === 'undefined';
633};
634
635/**
636 * ForEach helper function
637 * Iterates over Arrays and Objects (own properties only)
638 * @static
639 * @param {*} obj
640 * @param {function} callback Accepts 3 params: 1. value, 2. key, 3. the original array/object
641 */
642showdown.helper.forEach = function (obj, callback) {
643 'use strict';
644 // check if obj is defined
645 if (showdown.helper.isUndefined(obj)) {
646 throw new Error('obj param is required');
647 }
648
649 if (showdown.helper.isUndefined(callback)) {
650 throw new Error('callback param is required');
651 }
652
653 if (!showdown.helper.isFunction(callback)) {
654 throw new Error('callback param must be a function/closure');
655 }
656
657 if (typeof obj.forEach === 'function') {
658 obj.forEach(callback);
659 } else if (showdown.helper.isArray(obj)) {
660 for (var i = 0; i < obj.length; i++) {
661 callback(obj[i], i, obj);
662 }
663 } else if (typeof (obj) === 'object') {
664 for (var prop in obj) {
665 if (obj.hasOwnProperty(prop)) {
666 callback(obj[prop], prop, obj);
667 }
668 }
669 } else {
670 throw new Error('obj does not seem to be an array or an iterable object');
671 }
672};
673
674/**
675 * Standardidize extension name
676 * @static
677 * @param {string} s extension name
678 * @returns {string}
679 */
680showdown.helper.stdExtName = function (s) {
681 'use strict';
682 return s.replace(/[_?*+\/\\.^-]/g, '').replace(/\s/g, '').toLowerCase();
683};
684
685function escapeCharactersCallback (wholeMatch, m1) {
686 'use strict';
687 var charCodeToEscape = m1.charCodeAt(0);
688 return '¨E' + charCodeToEscape + 'E';
689}
690
691/**
692 * Callback used to escape characters when passing through String.replace
693 * @static
694 * @param {string} wholeMatch
695 * @param {string} m1
696 * @returns {string}
697 */
698showdown.helper.escapeCharactersCallback = escapeCharactersCallback;
699
700/**
701 * Escape characters in a string
702 * @static
703 * @param {string} text
704 * @param {string} charsToEscape
705 * @param {boolean} afterBackslash
706 * @returns {XML|string|void|*}
707 */
708showdown.helper.escapeCharacters = function (text, charsToEscape, afterBackslash) {
709 'use strict';
710 // First we have to escape the escape characters so that
711 // we can build a character class out of them
712 var regexString = '([' + charsToEscape.replace(/([\[\]\\])/g, '\\$1') + '])';
713
714 if (afterBackslash) {
715 regexString = '\\\\' + regexString;
716 }
717
718 var regex = new RegExp(regexString, 'g');
719 text = text.replace(regex, escapeCharactersCallback);
720
721 return text;
722};
723
724/**
725 * Unescape HTML entities
726 * @param txt
727 * @returns {string}
728 */
729showdown.helper.unescapeHTMLEntities = function (txt) {
730 'use strict';
731
732 return txt
733 .replace(/&quot;/g, '"')
734 .replace(/&lt;/g, '<')
735 .replace(/&gt;/g, '>')
736 .replace(/&amp;/g, '&');
737};
738
739var rgxFindMatchPos = function (str, left, right, flags) {
740 'use strict';
741 var f = flags || '',
742 g = f.indexOf('g') > -1,
743 x = new RegExp(left + '|' + right, 'g' + f.replace(/g/g, '')),
744 l = new RegExp(left, f.replace(/g/g, '')),
745 pos = [],
746 t, s, m, start, end;
747
748 do {
749 t = 0;
750 while ((m = x.exec(str))) {
751 if (l.test(m[0])) {
752 if (!(t++)) {
753 s = x.lastIndex;
754 start = s - m[0].length;
755 }
756 } else if (t) {
757 if (!--t) {
758 end = m.index + m[0].length;
759 var obj = {
760 left: {start: start, end: s},
761 match: {start: s, end: m.index},
762 right: {start: m.index, end: end},
763 wholeMatch: {start: start, end: end}
764 };
765 pos.push(obj);
766 if (!g) {
767 return pos;
768 }
769 }
770 }
771 }
772 } while (t && (x.lastIndex = s));
773
774 return pos;
775};
776
777/**
778 * matchRecursiveRegExp
779 *
780 * (c) 2007 Steven Levithan <stevenlevithan.com>
781 * MIT License
782 *
783 * Accepts a string to search, a left and right format delimiter
784 * as regex patterns, and optional regex flags. Returns an array
785 * of matches, allowing nested instances of left/right delimiters.
786 * Use the "g" flag to return all matches, otherwise only the
787 * first is returned. Be careful to ensure that the left and
788 * right format delimiters produce mutually exclusive matches.
789 * Backreferences are not supported within the right delimiter
790 * due to how it is internally combined with the left delimiter.
791 * When matching strings whose format delimiters are unbalanced
792 * to the left or right, the output is intentionally as a
793 * conventional regex library with recursion support would
794 * produce, e.g. "<<x>" and "<x>>" both produce ["x"] when using
795 * "<" and ">" as the delimiters (both strings contain a single,
796 * balanced instance of "<x>").
797 *
798 * examples:
799 * matchRecursiveRegExp("test", "\\(", "\\)")
800 * returns: []
801 * matchRecursiveRegExp("<t<<e>><s>>t<>", "<", ">", "g")
802 * returns: ["t<<e>><s>", ""]
803 * matchRecursiveRegExp("<div id=\"x\">test</div>", "<div\\b[^>]*>", "</div>", "gi")
804 * returns: ["test"]
805 */
806showdown.helper.matchRecursiveRegExp = function (str, left, right, flags) {
807 'use strict';
808
809 var matchPos = rgxFindMatchPos (str, left, right, flags),
810 results = [];
811
812 for (var i = 0; i < matchPos.length; ++i) {
813 results.push([
814 str.slice(matchPos[i].wholeMatch.start, matchPos[i].wholeMatch.end),
815 str.slice(matchPos[i].match.start, matchPos[i].match.end),
816 str.slice(matchPos[i].left.start, matchPos[i].left.end),
817 str.slice(matchPos[i].right.start, matchPos[i].right.end)
818 ]);
819 }
820 return results;
821};
822
823/**
824 *
825 * @param {string} str
826 * @param {string|function} replacement
827 * @param {string} left
828 * @param {string} right
829 * @param {string} flags
830 * @returns {string}
831 */
832showdown.helper.replaceRecursiveRegExp = function (str, replacement, left, right, flags) {
833 'use strict';
834
835 if (!showdown.helper.isFunction(replacement)) {
836 var repStr = replacement;
837 replacement = function () {
838 return repStr;
839 };
840 }
841
842 var matchPos = rgxFindMatchPos(str, left, right, flags),
843 finalStr = str,
844 lng = matchPos.length;
845
846 if (lng > 0) {
847 var bits = [];
848 if (matchPos[0].wholeMatch.start !== 0) {
849 bits.push(str.slice(0, matchPos[0].wholeMatch.start));
850 }
851 for (var i = 0; i < lng; ++i) {
852 bits.push(
853 replacement(
854 str.slice(matchPos[i].wholeMatch.start, matchPos[i].wholeMatch.end),
855 str.slice(matchPos[i].match.start, matchPos[i].match.end),
856 str.slice(matchPos[i].left.start, matchPos[i].left.end),
857 str.slice(matchPos[i].right.start, matchPos[i].right.end)
858 )
859 );
860 if (i < lng - 1) {
861 bits.push(str.slice(matchPos[i].wholeMatch.end, matchPos[i + 1].wholeMatch.start));
862 }
863 }
864 if (matchPos[lng - 1].wholeMatch.end < str.length) {
865 bits.push(str.slice(matchPos[lng - 1].wholeMatch.end));
866 }
867 finalStr = bits.join('');
868 }
869 return finalStr;
870};
871
872/**
873 * Returns the index within the passed String object of the first occurrence of the specified regex,
874 * starting the search at fromIndex. Returns -1 if the value is not found.
875 *
876 * @param {string} str string to search
877 * @param {RegExp} regex Regular expression to search
878 * @param {int} [fromIndex = 0] Index to start the search
879 * @returns {Number}
880 * @throws InvalidArgumentError
881 */
882showdown.helper.regexIndexOf = function (str, regex, fromIndex) {
883 'use strict';
884 if (!showdown.helper.isString(str)) {
885 throw 'InvalidArgumentError: first parameter of showdown.helper.regexIndexOf function must be a string';
886 }
887 if (regex instanceof RegExp === false) {
888 throw 'InvalidArgumentError: second parameter of showdown.helper.regexIndexOf function must be an instance of RegExp';
889 }
890 var indexOf = str.substring(fromIndex || 0).search(regex);
891 return (indexOf >= 0) ? (indexOf + (fromIndex || 0)) : indexOf;
892};
893
894/**
895 * Splits the passed string object at the defined index, and returns an array composed of the two substrings
896 * @param {string} str string to split
897 * @param {int} index index to split string at
898 * @returns {[string,string]}
899 * @throws InvalidArgumentError
900 */
901showdown.helper.splitAtIndex = function (str, index) {
902 'use strict';
903 if (!showdown.helper.isString(str)) {
904 throw 'InvalidArgumentError: first parameter of showdown.helper.regexIndexOf function must be a string';
905 }
906 return [str.substring(0, index), str.substring(index)];
907};
908
909/**
910 * Obfuscate an e-mail address through the use of Character Entities,
911 * transforming ASCII characters into their equivalent decimal or hex entities.
912 *
913 * Since it has a random component, subsequent calls to this function produce different results
914 *
915 * @param {string} mail
916 * @returns {string}
917 */
918showdown.helper.encodeEmailAddress = function (mail) {
919 'use strict';
920 var encode = [
921 function (ch) {
922 return '&#' + ch.charCodeAt(0) + ';';
923 },
924 function (ch) {
925 return '&#x' + ch.charCodeAt(0).toString(16) + ';';
926 },
927 function (ch) {
928 return ch;
929 }
930 ];
931
932 mail = mail.replace(/./g, function (ch) {
933 if (ch === '@') {
934 // this *must* be encoded. I insist.
935 ch = encode[Math.floor(Math.random() * 2)](ch);
936 } else {
937 var r = Math.random();
938 // roughly 10% raw, 45% hex, 45% dec
939 ch = (
940 r > 0.9 ? encode[2](ch) : r > 0.45 ? encode[1](ch) : encode[0](ch)
941 );
942 }
943 return ch;
944 });
945
946 return mail;
947};
948
949/**
950 *
951 * @param str
952 * @param targetLength
953 * @param padString
954 * @returns {string}
955 */
956showdown.helper.padEnd = function padEnd (str, targetLength, padString) {
957 'use strict';
958 /*jshint bitwise: false*/
959 // eslint-disable-next-line space-infix-ops
960 targetLength = targetLength>>0; //floor if number or convert non-number to 0;
961 /*jshint bitwise: true*/
962 padString = String(padString || ' ');
963 if (str.length > targetLength) {
964 return String(str);
965 } else {
966 targetLength = targetLength - str.length;
967 if (targetLength > padString.length) {
968 padString += padString.repeat(targetLength / padString.length); //append to original to ensure we are longer than needed
969 }
970 return String(str) + padString.slice(0,targetLength);
971 }
972};
973
974/**
975 * POLYFILLS
976 */
977// use this instead of builtin is undefined for IE8 compatibility
978if (typeof (console) === 'undefined') {
979 console = {
980 warn: function (msg) {
981 'use strict';
982 alert(msg);
983 },
984 log: function (msg) {
985 'use strict';
986 alert(msg);
987 },
988 error: function (msg) {
989 'use strict';
990 throw msg;
991 }
992 };
993}
994
995/**
996 * Common regexes.
997 * We declare some common regexes to improve performance
998 */
999showdown.helper.regexes = {
1000 asteriskDashAndColon: /([*_:~])/g
1001};
1002
1003/**
1004 * EMOJIS LIST
1005 */
1006showdown.helper.emojis = {
1007 '+1':'\ud83d\udc4d',
1008 '-1':'\ud83d\udc4e',
1009 '100':'\ud83d\udcaf',
1010 '1234':'\ud83d\udd22',
1011 '1st_place_medal':'\ud83e\udd47',
1012 '2nd_place_medal':'\ud83e\udd48',
1013 '3rd_place_medal':'\ud83e\udd49',
1014 '8ball':'\ud83c\udfb1',
1015 'a':'\ud83c\udd70\ufe0f',
1016 'ab':'\ud83c\udd8e',
1017 'abc':'\ud83d\udd24',
1018 'abcd':'\ud83d\udd21',
1019 'accept':'\ud83c\ude51',
1020 'aerial_tramway':'\ud83d\udea1',
1021 'airplane':'\u2708\ufe0f',
1022 'alarm_clock':'\u23f0',
1023 'alembic':'\u2697\ufe0f',
1024 'alien':'\ud83d\udc7d',
1025 'ambulance':'\ud83d\ude91',
1026 'amphora':'\ud83c\udffa',
1027 'anchor':'\u2693\ufe0f',
1028 'angel':'\ud83d\udc7c',
1029 'anger':'\ud83d\udca2',
1030 'angry':'\ud83d\ude20',
1031 'anguished':'\ud83d\ude27',
1032 'ant':'\ud83d\udc1c',
1033 'apple':'\ud83c\udf4e',
1034 'aquarius':'\u2652\ufe0f',
1035 'aries':'\u2648\ufe0f',
1036 'arrow_backward':'\u25c0\ufe0f',
1037 'arrow_double_down':'\u23ec',
1038 'arrow_double_up':'\u23eb',
1039 'arrow_down':'\u2b07\ufe0f',
1040 'arrow_down_small':'\ud83d\udd3d',
1041 'arrow_forward':'\u25b6\ufe0f',
1042 'arrow_heading_down':'\u2935\ufe0f',
1043 'arrow_heading_up':'\u2934\ufe0f',
1044 'arrow_left':'\u2b05\ufe0f',
1045 'arrow_lower_left':'\u2199\ufe0f',
1046 'arrow_lower_right':'\u2198\ufe0f',
1047 'arrow_right':'\u27a1\ufe0f',
1048 'arrow_right_hook':'\u21aa\ufe0f',
1049 'arrow_up':'\u2b06\ufe0f',
1050 'arrow_up_down':'\u2195\ufe0f',
1051 'arrow_up_small':'\ud83d\udd3c',
1052 'arrow_upper_left':'\u2196\ufe0f',
1053 'arrow_upper_right':'\u2197\ufe0f',
1054 'arrows_clockwise':'\ud83d\udd03',
1055 'arrows_counterclockwise':'\ud83d\udd04',
1056 'art':'\ud83c\udfa8',
1057 'articulated_lorry':'\ud83d\ude9b',
1058 'artificial_satellite':'\ud83d\udef0',
1059 'astonished':'\ud83d\ude32',
1060 'athletic_shoe':'\ud83d\udc5f',
1061 'atm':'\ud83c\udfe7',
1062 'atom_symbol':'\u269b\ufe0f',
1063 'avocado':'\ud83e\udd51',
1064 'b':'\ud83c\udd71\ufe0f',
1065 'baby':'\ud83d\udc76',
1066 'baby_bottle':'\ud83c\udf7c',
1067 'baby_chick':'\ud83d\udc24',
1068 'baby_symbol':'\ud83d\udebc',
1069 'back':'\ud83d\udd19',
1070 'bacon':'\ud83e\udd53',
1071 'badminton':'\ud83c\udff8',
1072 'baggage_claim':'\ud83d\udec4',
1073 'baguette_bread':'\ud83e\udd56',
1074 'balance_scale':'\u2696\ufe0f',
1075 'balloon':'\ud83c\udf88',
1076 'ballot_box':'\ud83d\uddf3',
1077 'ballot_box_with_check':'\u2611\ufe0f',
1078 'bamboo':'\ud83c\udf8d',
1079 'banana':'\ud83c\udf4c',
1080 'bangbang':'\u203c\ufe0f',
1081 'bank':'\ud83c\udfe6',
1082 'bar_chart':'\ud83d\udcca',
1083 'barber':'\ud83d\udc88',
1084 'baseball':'\u26be\ufe0f',
1085 'basketball':'\ud83c\udfc0',
1086 'basketball_man':'\u26f9\ufe0f',
1087 'basketball_woman':'\u26f9\ufe0f&zwj;\u2640\ufe0f',
1088 'bat':'\ud83e\udd87',
1089 'bath':'\ud83d\udec0',
1090 'bathtub':'\ud83d\udec1',
1091 'battery':'\ud83d\udd0b',
1092 'beach_umbrella':'\ud83c\udfd6',
1093 'bear':'\ud83d\udc3b',
1094 'bed':'\ud83d\udecf',
1095 'bee':'\ud83d\udc1d',
1096 'beer':'\ud83c\udf7a',
1097 'beers':'\ud83c\udf7b',
1098 'beetle':'\ud83d\udc1e',
1099 'beginner':'\ud83d\udd30',
1100 'bell':'\ud83d\udd14',
1101 'bellhop_bell':'\ud83d\udece',
1102 'bento':'\ud83c\udf71',
1103 'biking_man':'\ud83d\udeb4',
1104 'bike':'\ud83d\udeb2',
1105 'biking_woman':'\ud83d\udeb4&zwj;\u2640\ufe0f',
1106 'bikini':'\ud83d\udc59',
1107 'biohazard':'\u2623\ufe0f',
1108 'bird':'\ud83d\udc26',
1109 'birthday':'\ud83c\udf82',
1110 'black_circle':'\u26ab\ufe0f',
1111 'black_flag':'\ud83c\udff4',
1112 'black_heart':'\ud83d\udda4',
1113 'black_joker':'\ud83c\udccf',
1114 'black_large_square':'\u2b1b\ufe0f',
1115 'black_medium_small_square':'\u25fe\ufe0f',
1116 'black_medium_square':'\u25fc\ufe0f',
1117 'black_nib':'\u2712\ufe0f',
1118 'black_small_square':'\u25aa\ufe0f',
1119 'black_square_button':'\ud83d\udd32',
1120 'blonde_man':'\ud83d\udc71',
1121 'blonde_woman':'\ud83d\udc71&zwj;\u2640\ufe0f',
1122 'blossom':'\ud83c\udf3c',
1123 'blowfish':'\ud83d\udc21',
1124 'blue_book':'\ud83d\udcd8',
1125 'blue_car':'\ud83d\ude99',
1126 'blue_heart':'\ud83d\udc99',
1127 'blush':'\ud83d\ude0a',
1128 'boar':'\ud83d\udc17',
1129 'boat':'\u26f5\ufe0f',
1130 'bomb':'\ud83d\udca3',
1131 'book':'\ud83d\udcd6',
1132 'bookmark':'\ud83d\udd16',
1133 'bookmark_tabs':'\ud83d\udcd1',
1134 'books':'\ud83d\udcda',
1135 'boom':'\ud83d\udca5',
1136 'boot':'\ud83d\udc62',
1137 'bouquet':'\ud83d\udc90',
1138 'bowing_man':'\ud83d\ude47',
1139 'bow_and_arrow':'\ud83c\udff9',
1140 'bowing_woman':'\ud83d\ude47&zwj;\u2640\ufe0f',
1141 'bowling':'\ud83c\udfb3',
1142 'boxing_glove':'\ud83e\udd4a',
1143 'boy':'\ud83d\udc66',
1144 'bread':'\ud83c\udf5e',
1145 'bride_with_veil':'\ud83d\udc70',
1146 'bridge_at_night':'\ud83c\udf09',
1147 'briefcase':'\ud83d\udcbc',
1148 'broken_heart':'\ud83d\udc94',
1149 'bug':'\ud83d\udc1b',
1150 'building_construction':'\ud83c\udfd7',
1151 'bulb':'\ud83d\udca1',
1152 'bullettrain_front':'\ud83d\ude85',
1153 'bullettrain_side':'\ud83d\ude84',
1154 'burrito':'\ud83c\udf2f',
1155 'bus':'\ud83d\ude8c',
1156 'business_suit_levitating':'\ud83d\udd74',
1157 'busstop':'\ud83d\ude8f',
1158 'bust_in_silhouette':'\ud83d\udc64',
1159 'busts_in_silhouette':'\ud83d\udc65',
1160 'butterfly':'\ud83e\udd8b',
1161 'cactus':'\ud83c\udf35',
1162 'cake':'\ud83c\udf70',
1163 'calendar':'\ud83d\udcc6',
1164 'call_me_hand':'\ud83e\udd19',
1165 'calling':'\ud83d\udcf2',
1166 'camel':'\ud83d\udc2b',
1167 'camera':'\ud83d\udcf7',
1168 'camera_flash':'\ud83d\udcf8',
1169 'camping':'\ud83c\udfd5',
1170 'cancer':'\u264b\ufe0f',
1171 'candle':'\ud83d\udd6f',
1172 'candy':'\ud83c\udf6c',
1173 'canoe':'\ud83d\udef6',
1174 'capital_abcd':'\ud83d\udd20',
1175 'capricorn':'\u2651\ufe0f',
1176 'car':'\ud83d\ude97',
1177 'card_file_box':'\ud83d\uddc3',
1178 'card_index':'\ud83d\udcc7',
1179 'card_index_dividers':'\ud83d\uddc2',
1180 'carousel_horse':'\ud83c\udfa0',
1181 'carrot':'\ud83e\udd55',
1182 'cat':'\ud83d\udc31',
1183 'cat2':'\ud83d\udc08',
1184 'cd':'\ud83d\udcbf',
1185 'chains':'\u26d3',
1186 'champagne':'\ud83c\udf7e',
1187 'chart':'\ud83d\udcb9',
1188 'chart_with_downwards_trend':'\ud83d\udcc9',
1189 'chart_with_upwards_trend':'\ud83d\udcc8',
1190 'checkered_flag':'\ud83c\udfc1',
1191 'cheese':'\ud83e\uddc0',
1192 'cherries':'\ud83c\udf52',
1193 'cherry_blossom':'\ud83c\udf38',
1194 'chestnut':'\ud83c\udf30',
1195 'chicken':'\ud83d\udc14',
1196 'children_crossing':'\ud83d\udeb8',
1197 'chipmunk':'\ud83d\udc3f',
1198 'chocolate_bar':'\ud83c\udf6b',
1199 'christmas_tree':'\ud83c\udf84',
1200 'church':'\u26ea\ufe0f',
1201 'cinema':'\ud83c\udfa6',
1202 'circus_tent':'\ud83c\udfaa',
1203 'city_sunrise':'\ud83c\udf07',
1204 'city_sunset':'\ud83c\udf06',
1205 'cityscape':'\ud83c\udfd9',
1206 'cl':'\ud83c\udd91',
1207 'clamp':'\ud83d\udddc',
1208 'clap':'\ud83d\udc4f',
1209 'clapper':'\ud83c\udfac',
1210 'classical_building':'\ud83c\udfdb',
1211 'clinking_glasses':'\ud83e\udd42',
1212 'clipboard':'\ud83d\udccb',
1213 'clock1':'\ud83d\udd50',
1214 'clock10':'\ud83d\udd59',
1215 'clock1030':'\ud83d\udd65',
1216 'clock11':'\ud83d\udd5a',
1217 'clock1130':'\ud83d\udd66',
1218 'clock12':'\ud83d\udd5b',
1219 'clock1230':'\ud83d\udd67',
1220 'clock130':'\ud83d\udd5c',
1221 'clock2':'\ud83d\udd51',
1222 'clock230':'\ud83d\udd5d',
1223 'clock3':'\ud83d\udd52',
1224 'clock330':'\ud83d\udd5e',
1225 'clock4':'\ud83d\udd53',
1226 'clock430':'\ud83d\udd5f',
1227 'clock5':'\ud83d\udd54',
1228 'clock530':'\ud83d\udd60',
1229 'clock6':'\ud83d\udd55',
1230 'clock630':'\ud83d\udd61',
1231 'clock7':'\ud83d\udd56',
1232 'clock730':'\ud83d\udd62',
1233 'clock8':'\ud83d\udd57',
1234 'clock830':'\ud83d\udd63',
1235 'clock9':'\ud83d\udd58',
1236 'clock930':'\ud83d\udd64',
1237 'closed_book':'\ud83d\udcd5',
1238 'closed_lock_with_key':'\ud83d\udd10',
1239 'closed_umbrella':'\ud83c\udf02',
1240 'cloud':'\u2601\ufe0f',
1241 'cloud_with_lightning':'\ud83c\udf29',
1242 'cloud_with_lightning_and_rain':'\u26c8',
1243 'cloud_with_rain':'\ud83c\udf27',
1244 'cloud_with_snow':'\ud83c\udf28',
1245 'clown_face':'\ud83e\udd21',
1246 'clubs':'\u2663\ufe0f',
1247 'cocktail':'\ud83c\udf78',
1248 'coffee':'\u2615\ufe0f',
1249 'coffin':'\u26b0\ufe0f',
1250 'cold_sweat':'\ud83d\ude30',
1251 'comet':'\u2604\ufe0f',
1252 'computer':'\ud83d\udcbb',
1253 'computer_mouse':'\ud83d\uddb1',
1254 'confetti_ball':'\ud83c\udf8a',
1255 'confounded':'\ud83d\ude16',
1256 'confused':'\ud83d\ude15',
1257 'congratulations':'\u3297\ufe0f',
1258 'construction':'\ud83d\udea7',
1259 'construction_worker_man':'\ud83d\udc77',
1260 'construction_worker_woman':'\ud83d\udc77&zwj;\u2640\ufe0f',
1261 'control_knobs':'\ud83c\udf9b',
1262 'convenience_store':'\ud83c\udfea',
1263 'cookie':'\ud83c\udf6a',
1264 'cool':'\ud83c\udd92',
1265 'policeman':'\ud83d\udc6e',
1266 'copyright':'\u00a9\ufe0f',
1267 'corn':'\ud83c\udf3d',
1268 'couch_and_lamp':'\ud83d\udecb',
1269 'couple':'\ud83d\udc6b',
1270 'couple_with_heart_woman_man':'\ud83d\udc91',
1271 'couple_with_heart_man_man':'\ud83d\udc68&zwj;\u2764\ufe0f&zwj;\ud83d\udc68',
1272 'couple_with_heart_woman_woman':'\ud83d\udc69&zwj;\u2764\ufe0f&zwj;\ud83d\udc69',
1273 'couplekiss_man_man':'\ud83d\udc68&zwj;\u2764\ufe0f&zwj;\ud83d\udc8b&zwj;\ud83d\udc68',
1274 'couplekiss_man_woman':'\ud83d\udc8f',
1275 'couplekiss_woman_woman':'\ud83d\udc69&zwj;\u2764\ufe0f&zwj;\ud83d\udc8b&zwj;\ud83d\udc69',
1276 'cow':'\ud83d\udc2e',
1277 'cow2':'\ud83d\udc04',
1278 'cowboy_hat_face':'\ud83e\udd20',
1279 'crab':'\ud83e\udd80',
1280 'crayon':'\ud83d\udd8d',
1281 'credit_card':'\ud83d\udcb3',
1282 'crescent_moon':'\ud83c\udf19',
1283 'cricket':'\ud83c\udfcf',
1284 'crocodile':'\ud83d\udc0a',
1285 'croissant':'\ud83e\udd50',
1286 'crossed_fingers':'\ud83e\udd1e',
1287 'crossed_flags':'\ud83c\udf8c',
1288 'crossed_swords':'\u2694\ufe0f',
1289 'crown':'\ud83d\udc51',
1290 'cry':'\ud83d\ude22',
1291 'crying_cat_face':'\ud83d\ude3f',
1292 'crystal_ball':'\ud83d\udd2e',
1293 'cucumber':'\ud83e\udd52',
1294 'cupid':'\ud83d\udc98',
1295 'curly_loop':'\u27b0',
1296 'currency_exchange':'\ud83d\udcb1',
1297 'curry':'\ud83c\udf5b',
1298 'custard':'\ud83c\udf6e',
1299 'customs':'\ud83d\udec3',
1300 'cyclone':'\ud83c\udf00',
1301 'dagger':'\ud83d\udde1',
1302 'dancer':'\ud83d\udc83',
1303 'dancing_women':'\ud83d\udc6f',
1304 'dancing_men':'\ud83d\udc6f&zwj;\u2642\ufe0f',
1305 'dango':'\ud83c\udf61',
1306 'dark_sunglasses':'\ud83d\udd76',
1307 'dart':'\ud83c\udfaf',
1308 'dash':'\ud83d\udca8',
1309 'date':'\ud83d\udcc5',
1310 'deciduous_tree':'\ud83c\udf33',
1311 'deer':'\ud83e\udd8c',
1312 'department_store':'\ud83c\udfec',
1313 'derelict_house':'\ud83c\udfda',
1314 'desert':'\ud83c\udfdc',
1315 'desert_island':'\ud83c\udfdd',
1316 'desktop_computer':'\ud83d\udda5',
1317 'male_detective':'\ud83d\udd75\ufe0f',
1318 'diamond_shape_with_a_dot_inside':'\ud83d\udca0',
1319 'diamonds':'\u2666\ufe0f',
1320 'disappointed':'\ud83d\ude1e',
1321 'disappointed_relieved':'\ud83d\ude25',
1322 'dizzy':'\ud83d\udcab',
1323 'dizzy_face':'\ud83d\ude35',
1324 'do_not_litter':'\ud83d\udeaf',
1325 'dog':'\ud83d\udc36',
1326 'dog2':'\ud83d\udc15',
1327 'dollar':'\ud83d\udcb5',
1328 'dolls':'\ud83c\udf8e',
1329 'dolphin':'\ud83d\udc2c',
1330 'door':'\ud83d\udeaa',
1331 'doughnut':'\ud83c\udf69',
1332 'dove':'\ud83d\udd4a',
1333 'dragon':'\ud83d\udc09',
1334 'dragon_face':'\ud83d\udc32',
1335 'dress':'\ud83d\udc57',
1336 'dromedary_camel':'\ud83d\udc2a',
1337 'drooling_face':'\ud83e\udd24',
1338 'droplet':'\ud83d\udca7',
1339 'drum':'\ud83e\udd41',
1340 'duck':'\ud83e\udd86',
1341 'dvd':'\ud83d\udcc0',
1342 'e-mail':'\ud83d\udce7',
1343 'eagle':'\ud83e\udd85',
1344 'ear':'\ud83d\udc42',
1345 'ear_of_rice':'\ud83c\udf3e',
1346 'earth_africa':'\ud83c\udf0d',
1347 'earth_americas':'\ud83c\udf0e',
1348 'earth_asia':'\ud83c\udf0f',
1349 'egg':'\ud83e\udd5a',
1350 'eggplant':'\ud83c\udf46',
1351 'eight_pointed_black_star':'\u2734\ufe0f',
1352 'eight_spoked_asterisk':'\u2733\ufe0f',
1353 'electric_plug':'\ud83d\udd0c',
1354 'elephant':'\ud83d\udc18',
1355 'email':'\u2709\ufe0f',
1356 'end':'\ud83d\udd1a',
1357 'envelope_with_arrow':'\ud83d\udce9',
1358 'euro':'\ud83d\udcb6',
1359 'european_castle':'\ud83c\udff0',
1360 'european_post_office':'\ud83c\udfe4',
1361 'evergreen_tree':'\ud83c\udf32',
1362 'exclamation':'\u2757\ufe0f',
1363 'expressionless':'\ud83d\ude11',
1364 'eye':'\ud83d\udc41',
1365 'eye_speech_bubble':'\ud83d\udc41&zwj;\ud83d\udde8',
1366 'eyeglasses':'\ud83d\udc53',
1367 'eyes':'\ud83d\udc40',
1368 'face_with_head_bandage':'\ud83e\udd15',
1369 'face_with_thermometer':'\ud83e\udd12',
1370 'fist_oncoming':'\ud83d\udc4a',
1371 'factory':'\ud83c\udfed',
1372 'fallen_leaf':'\ud83c\udf42',
1373 'family_man_woman_boy':'\ud83d\udc6a',
1374 'family_man_boy':'\ud83d\udc68&zwj;\ud83d\udc66',
1375 'family_man_boy_boy':'\ud83d\udc68&zwj;\ud83d\udc66&zwj;\ud83d\udc66',
1376 'family_man_girl':'\ud83d\udc68&zwj;\ud83d\udc67',
1377 'family_man_girl_boy':'\ud83d\udc68&zwj;\ud83d\udc67&zwj;\ud83d\udc66',
1378 'family_man_girl_girl':'\ud83d\udc68&zwj;\ud83d\udc67&zwj;\ud83d\udc67',
1379 'family_man_man_boy':'\ud83d\udc68&zwj;\ud83d\udc68&zwj;\ud83d\udc66',
1380 'family_man_man_boy_boy':'\ud83d\udc68&zwj;\ud83d\udc68&zwj;\ud83d\udc66&zwj;\ud83d\udc66',
1381 'family_man_man_girl':'\ud83d\udc68&zwj;\ud83d\udc68&zwj;\ud83d\udc67',
1382 'family_man_man_girl_boy':'\ud83d\udc68&zwj;\ud83d\udc68&zwj;\ud83d\udc67&zwj;\ud83d\udc66',
1383 'family_man_man_girl_girl':'\ud83d\udc68&zwj;\ud83d\udc68&zwj;\ud83d\udc67&zwj;\ud83d\udc67',
1384 'family_man_woman_boy_boy':'\ud83d\udc68&zwj;\ud83d\udc69&zwj;\ud83d\udc66&zwj;\ud83d\udc66',
1385 'family_man_woman_girl':'\ud83d\udc68&zwj;\ud83d\udc69&zwj;\ud83d\udc67',
1386 'family_man_woman_girl_boy':'\ud83d\udc68&zwj;\ud83d\udc69&zwj;\ud83d\udc67&zwj;\ud83d\udc66',
1387 'family_man_woman_girl_girl':'\ud83d\udc68&zwj;\ud83d\udc69&zwj;\ud83d\udc67&zwj;\ud83d\udc67',
1388 'family_woman_boy':'\ud83d\udc69&zwj;\ud83d\udc66',
1389 'family_woman_boy_boy':'\ud83d\udc69&zwj;\ud83d\udc66&zwj;\ud83d\udc66',
1390 'family_woman_girl':'\ud83d\udc69&zwj;\ud83d\udc67',
1391 'family_woman_girl_boy':'\ud83d\udc69&zwj;\ud83d\udc67&zwj;\ud83d\udc66',
1392 'family_woman_girl_girl':'\ud83d\udc69&zwj;\ud83d\udc67&zwj;\ud83d\udc67',
1393 'family_woman_woman_boy':'\ud83d\udc69&zwj;\ud83d\udc69&zwj;\ud83d\udc66',
1394 'family_woman_woman_boy_boy':'\ud83d\udc69&zwj;\ud83d\udc69&zwj;\ud83d\udc66&zwj;\ud83d\udc66',
1395 'family_woman_woman_girl':'\ud83d\udc69&zwj;\ud83d\udc69&zwj;\ud83d\udc67',
1396 'family_woman_woman_girl_boy':'\ud83d\udc69&zwj;\ud83d\udc69&zwj;\ud83d\udc67&zwj;\ud83d\udc66',
1397 'family_woman_woman_girl_girl':'\ud83d\udc69&zwj;\ud83d\udc69&zwj;\ud83d\udc67&zwj;\ud83d\udc67',
1398 'fast_forward':'\u23e9',
1399 'fax':'\ud83d\udce0',
1400 'fearful':'\ud83d\ude28',
1401 'feet':'\ud83d\udc3e',
1402 'female_detective':'\ud83d\udd75\ufe0f&zwj;\u2640\ufe0f',
1403 'ferris_wheel':'\ud83c\udfa1',
1404 'ferry':'\u26f4',
1405 'field_hockey':'\ud83c\udfd1',
1406 'file_cabinet':'\ud83d\uddc4',
1407 'file_folder':'\ud83d\udcc1',
1408 'film_projector':'\ud83d\udcfd',
1409 'film_strip':'\ud83c\udf9e',
1410 'fire':'\ud83d\udd25',
1411 'fire_engine':'\ud83d\ude92',
1412 'fireworks':'\ud83c\udf86',
1413 'first_quarter_moon':'\ud83c\udf13',
1414 'first_quarter_moon_with_face':'\ud83c\udf1b',
1415 'fish':'\ud83d\udc1f',
1416 'fish_cake':'\ud83c\udf65',
1417 'fishing_pole_and_fish':'\ud83c\udfa3',
1418 'fist_raised':'\u270a',
1419 'fist_left':'\ud83e\udd1b',
1420 'fist_right':'\ud83e\udd1c',
1421 'flags':'\ud83c\udf8f',
1422 'flashlight':'\ud83d\udd26',
1423 'fleur_de_lis':'\u269c\ufe0f',
1424 'flight_arrival':'\ud83d\udeec',
1425 'flight_departure':'\ud83d\udeeb',
1426 'floppy_disk':'\ud83d\udcbe',
1427 'flower_playing_cards':'\ud83c\udfb4',
1428 'flushed':'\ud83d\ude33',
1429 'fog':'\ud83c\udf2b',
1430 'foggy':'\ud83c\udf01',
1431 'football':'\ud83c\udfc8',
1432 'footprints':'\ud83d\udc63',
1433 'fork_and_knife':'\ud83c\udf74',
1434 'fountain':'\u26f2\ufe0f',
1435 'fountain_pen':'\ud83d\udd8b',
1436 'four_leaf_clover':'\ud83c\udf40',
1437 'fox_face':'\ud83e\udd8a',
1438 'framed_picture':'\ud83d\uddbc',
1439 'free':'\ud83c\udd93',
1440 'fried_egg':'\ud83c\udf73',
1441 'fried_shrimp':'\ud83c\udf64',
1442 'fries':'\ud83c\udf5f',
1443 'frog':'\ud83d\udc38',
1444 'frowning':'\ud83d\ude26',
1445 'frowning_face':'\u2639\ufe0f',
1446 'frowning_man':'\ud83d\ude4d&zwj;\u2642\ufe0f',
1447 'frowning_woman':'\ud83d\ude4d',
1448 'middle_finger':'\ud83d\udd95',
1449 'fuelpump':'\u26fd\ufe0f',
1450 'full_moon':'\ud83c\udf15',
1451 'full_moon_with_face':'\ud83c\udf1d',
1452 'funeral_urn':'\u26b1\ufe0f',
1453 'game_die':'\ud83c\udfb2',
1454 'gear':'\u2699\ufe0f',
1455 'gem':'\ud83d\udc8e',
1456 'gemini':'\u264a\ufe0f',
1457 'ghost':'\ud83d\udc7b',
1458 'gift':'\ud83c\udf81',
1459 'gift_heart':'\ud83d\udc9d',
1460 'girl':'\ud83d\udc67',
1461 'globe_with_meridians':'\ud83c\udf10',
1462 'goal_net':'\ud83e\udd45',
1463 'goat':'\ud83d\udc10',
1464 'golf':'\u26f3\ufe0f',
1465 'golfing_man':'\ud83c\udfcc\ufe0f',
1466 'golfing_woman':'\ud83c\udfcc\ufe0f&zwj;\u2640\ufe0f',
1467 'gorilla':'\ud83e\udd8d',
1468 'grapes':'\ud83c\udf47',
1469 'green_apple':'\ud83c\udf4f',
1470 'green_book':'\ud83d\udcd7',
1471 'green_heart':'\ud83d\udc9a',
1472 'green_salad':'\ud83e\udd57',
1473 'grey_exclamation':'\u2755',
1474 'grey_question':'\u2754',
1475 'grimacing':'\ud83d\ude2c',
1476 'grin':'\ud83d\ude01',
1477 'grinning':'\ud83d\ude00',
1478 'guardsman':'\ud83d\udc82',
1479 'guardswoman':'\ud83d\udc82&zwj;\u2640\ufe0f',
1480 'guitar':'\ud83c\udfb8',
1481 'gun':'\ud83d\udd2b',
1482 'haircut_woman':'\ud83d\udc87',
1483 'haircut_man':'\ud83d\udc87&zwj;\u2642\ufe0f',
1484 'hamburger':'\ud83c\udf54',
1485 'hammer':'\ud83d\udd28',
1486 'hammer_and_pick':'\u2692',
1487 'hammer_and_wrench':'\ud83d\udee0',
1488 'hamster':'\ud83d\udc39',
1489 'hand':'\u270b',
1490 'handbag':'\ud83d\udc5c',
1491 'handshake':'\ud83e\udd1d',
1492 'hankey':'\ud83d\udca9',
1493 'hatched_chick':'\ud83d\udc25',
1494 'hatching_chick':'\ud83d\udc23',
1495 'headphones':'\ud83c\udfa7',
1496 'hear_no_evil':'\ud83d\ude49',
1497 'heart':'\u2764\ufe0f',
1498 'heart_decoration':'\ud83d\udc9f',
1499 'heart_eyes':'\ud83d\ude0d',
1500 'heart_eyes_cat':'\ud83d\ude3b',
1501 'heartbeat':'\ud83d\udc93',
1502 'heartpulse':'\ud83d\udc97',
1503 'hearts':'\u2665\ufe0f',
1504 'heavy_check_mark':'\u2714\ufe0f',
1505 'heavy_division_sign':'\u2797',
1506 'heavy_dollar_sign':'\ud83d\udcb2',
1507 'heavy_heart_exclamation':'\u2763\ufe0f',
1508 'heavy_minus_sign':'\u2796',
1509 'heavy_multiplication_x':'\u2716\ufe0f',
1510 'heavy_plus_sign':'\u2795',
1511 'helicopter':'\ud83d\ude81',
1512 'herb':'\ud83c\udf3f',
1513 'hibiscus':'\ud83c\udf3a',
1514 'high_brightness':'\ud83d\udd06',
1515 'high_heel':'\ud83d\udc60',
1516 'hocho':'\ud83d\udd2a',
1517 'hole':'\ud83d\udd73',
1518 'honey_pot':'\ud83c\udf6f',
1519 'horse':'\ud83d\udc34',
1520 'horse_racing':'\ud83c\udfc7',
1521 'hospital':'\ud83c\udfe5',
1522 'hot_pepper':'\ud83c\udf36',
1523 'hotdog':'\ud83c\udf2d',
1524 'hotel':'\ud83c\udfe8',
1525 'hotsprings':'\u2668\ufe0f',
1526 'hourglass':'\u231b\ufe0f',
1527 'hourglass_flowing_sand':'\u23f3',
1528 'house':'\ud83c\udfe0',
1529 'house_with_garden':'\ud83c\udfe1',
1530 'houses':'\ud83c\udfd8',
1531 'hugs':'\ud83e\udd17',
1532 'hushed':'\ud83d\ude2f',
1533 'ice_cream':'\ud83c\udf68',
1534 'ice_hockey':'\ud83c\udfd2',
1535 'ice_skate':'\u26f8',
1536 'icecream':'\ud83c\udf66',
1537 'id':'\ud83c\udd94',
1538 'ideograph_advantage':'\ud83c\ude50',
1539 'imp':'\ud83d\udc7f',
1540 'inbox_tray':'\ud83d\udce5',
1541 'incoming_envelope':'\ud83d\udce8',
1542 'tipping_hand_woman':'\ud83d\udc81',
1543 'information_source':'\u2139\ufe0f',
1544 'innocent':'\ud83d\ude07',
1545 'interrobang':'\u2049\ufe0f',
1546 'iphone':'\ud83d\udcf1',
1547 'izakaya_lantern':'\ud83c\udfee',
1548 'jack_o_lantern':'\ud83c\udf83',
1549 'japan':'\ud83d\uddfe',
1550 'japanese_castle':'\ud83c\udfef',
1551 'japanese_goblin':'\ud83d\udc7a',
1552 'japanese_ogre':'\ud83d\udc79',
1553 'jeans':'\ud83d\udc56',
1554 'joy':'\ud83d\ude02',
1555 'joy_cat':'\ud83d\ude39',
1556 'joystick':'\ud83d\udd79',
1557 'kaaba':'\ud83d\udd4b',
1558 'key':'\ud83d\udd11',
1559 'keyboard':'\u2328\ufe0f',
1560 'keycap_ten':'\ud83d\udd1f',
1561 'kick_scooter':'\ud83d\udef4',
1562 'kimono':'\ud83d\udc58',
1563 'kiss':'\ud83d\udc8b',
1564 'kissing':'\ud83d\ude17',
1565 'kissing_cat':'\ud83d\ude3d',
1566 'kissing_closed_eyes':'\ud83d\ude1a',
1567 'kissing_heart':'\ud83d\ude18',
1568 'kissing_smiling_eyes':'\ud83d\ude19',
1569 'kiwi_fruit':'\ud83e\udd5d',
1570 'koala':'\ud83d\udc28',
1571 'koko':'\ud83c\ude01',
1572 'label':'\ud83c\udff7',
1573 'large_blue_circle':'\ud83d\udd35',
1574 'large_blue_diamond':'\ud83d\udd37',
1575 'large_orange_diamond':'\ud83d\udd36',
1576 'last_quarter_moon':'\ud83c\udf17',
1577 'last_quarter_moon_with_face':'\ud83c\udf1c',
1578 'latin_cross':'\u271d\ufe0f',
1579 'laughing':'\ud83d\ude06',
1580 'leaves':'\ud83c\udf43',
1581 'ledger':'\ud83d\udcd2',
1582 'left_luggage':'\ud83d\udec5',
1583 'left_right_arrow':'\u2194\ufe0f',
1584 'leftwards_arrow_with_hook':'\u21a9\ufe0f',
1585 'lemon':'\ud83c\udf4b',
1586 'leo':'\u264c\ufe0f',
1587 'leopard':'\ud83d\udc06',
1588 'level_slider':'\ud83c\udf9a',
1589 'libra':'\u264e\ufe0f',
1590 'light_rail':'\ud83d\ude88',
1591 'link':'\ud83d\udd17',
1592 'lion':'\ud83e\udd81',
1593 'lips':'\ud83d\udc44',
1594 'lipstick':'\ud83d\udc84',
1595 'lizard':'\ud83e\udd8e',
1596 'lock':'\ud83d\udd12',
1597 'lock_with_ink_pen':'\ud83d\udd0f',
1598 'lollipop':'\ud83c\udf6d',
1599 'loop':'\u27bf',
1600 'loud_sound':'\ud83d\udd0a',
1601 'loudspeaker':'\ud83d\udce2',
1602 'love_hotel':'\ud83c\udfe9',
1603 'love_letter':'\ud83d\udc8c',
1604 'low_brightness':'\ud83d\udd05',
1605 'lying_face':'\ud83e\udd25',
1606 'm':'\u24c2\ufe0f',
1607 'mag':'\ud83d\udd0d',
1608 'mag_right':'\ud83d\udd0e',
1609 'mahjong':'\ud83c\udc04\ufe0f',
1610 'mailbox':'\ud83d\udceb',
1611 'mailbox_closed':'\ud83d\udcea',
1612 'mailbox_with_mail':'\ud83d\udcec',
1613 'mailbox_with_no_mail':'\ud83d\udced',
1614 'man':'\ud83d\udc68',
1615 'man_artist':'\ud83d\udc68&zwj;\ud83c\udfa8',
1616 'man_astronaut':'\ud83d\udc68&zwj;\ud83d\ude80',
1617 'man_cartwheeling':'\ud83e\udd38&zwj;\u2642\ufe0f',
1618 'man_cook':'\ud83d\udc68&zwj;\ud83c\udf73',
1619 'man_dancing':'\ud83d\udd7a',
1620 'man_facepalming':'\ud83e\udd26&zwj;\u2642\ufe0f',
1621 'man_factory_worker':'\ud83d\udc68&zwj;\ud83c\udfed',
1622 'man_farmer':'\ud83d\udc68&zwj;\ud83c\udf3e',
1623 'man_firefighter':'\ud83d\udc68&zwj;\ud83d\ude92',
1624 'man_health_worker':'\ud83d\udc68&zwj;\u2695\ufe0f',
1625 'man_in_tuxedo':'\ud83e\udd35',
1626 'man_judge':'\ud83d\udc68&zwj;\u2696\ufe0f',
1627 'man_juggling':'\ud83e\udd39&zwj;\u2642\ufe0f',
1628 'man_mechanic':'\ud83d\udc68&zwj;\ud83d\udd27',
1629 'man_office_worker':'\ud83d\udc68&zwj;\ud83d\udcbc',
1630 'man_pilot':'\ud83d\udc68&zwj;\u2708\ufe0f',
1631 'man_playing_handball':'\ud83e\udd3e&zwj;\u2642\ufe0f',
1632 'man_playing_water_polo':'\ud83e\udd3d&zwj;\u2642\ufe0f',
1633 'man_scientist':'\ud83d\udc68&zwj;\ud83d\udd2c',
1634 'man_shrugging':'\ud83e\udd37&zwj;\u2642\ufe0f',
1635 'man_singer':'\ud83d\udc68&zwj;\ud83c\udfa4',
1636 'man_student':'\ud83d\udc68&zwj;\ud83c\udf93',
1637 'man_teacher':'\ud83d\udc68&zwj;\ud83c\udfeb',
1638 'man_technologist':'\ud83d\udc68&zwj;\ud83d\udcbb',
1639 'man_with_gua_pi_mao':'\ud83d\udc72',
1640 'man_with_turban':'\ud83d\udc73',
1641 'tangerine':'\ud83c\udf4a',
1642 'mans_shoe':'\ud83d\udc5e',
1643 'mantelpiece_clock':'\ud83d\udd70',
1644 'maple_leaf':'\ud83c\udf41',
1645 'martial_arts_uniform':'\ud83e\udd4b',
1646 'mask':'\ud83d\ude37',
1647 'massage_woman':'\ud83d\udc86',
1648 'massage_man':'\ud83d\udc86&zwj;\u2642\ufe0f',
1649 'meat_on_bone':'\ud83c\udf56',
1650 'medal_military':'\ud83c\udf96',
1651 'medal_sports':'\ud83c\udfc5',
1652 'mega':'\ud83d\udce3',
1653 'melon':'\ud83c\udf48',
1654 'memo':'\ud83d\udcdd',
1655 'men_wrestling':'\ud83e\udd3c&zwj;\u2642\ufe0f',
1656 'menorah':'\ud83d\udd4e',
1657 'mens':'\ud83d\udeb9',
1658 'metal':'\ud83e\udd18',
1659 'metro':'\ud83d\ude87',
1660 'microphone':'\ud83c\udfa4',
1661 'microscope':'\ud83d\udd2c',
1662 'milk_glass':'\ud83e\udd5b',
1663 'milky_way':'\ud83c\udf0c',
1664 'minibus':'\ud83d\ude90',
1665 'minidisc':'\ud83d\udcbd',
1666 'mobile_phone_off':'\ud83d\udcf4',
1667 'money_mouth_face':'\ud83e\udd11',
1668 'money_with_wings':'\ud83d\udcb8',
1669 'moneybag':'\ud83d\udcb0',
1670 'monkey':'\ud83d\udc12',
1671 'monkey_face':'\ud83d\udc35',
1672 'monorail':'\ud83d\ude9d',
1673 'moon':'\ud83c\udf14',
1674 'mortar_board':'\ud83c\udf93',
1675 'mosque':'\ud83d\udd4c',
1676 'motor_boat':'\ud83d\udee5',
1677 'motor_scooter':'\ud83d\udef5',
1678 'motorcycle':'\ud83c\udfcd',
1679 'motorway':'\ud83d\udee3',
1680 'mount_fuji':'\ud83d\uddfb',
1681 'mountain':'\u26f0',
1682 'mountain_biking_man':'\ud83d\udeb5',
1683 'mountain_biking_woman':'\ud83d\udeb5&zwj;\u2640\ufe0f',
1684 'mountain_cableway':'\ud83d\udea0',
1685 'mountain_railway':'\ud83d\ude9e',
1686 'mountain_snow':'\ud83c\udfd4',
1687 'mouse':'\ud83d\udc2d',
1688 'mouse2':'\ud83d\udc01',
1689 'movie_camera':'\ud83c\udfa5',
1690 'moyai':'\ud83d\uddff',
1691 'mrs_claus':'\ud83e\udd36',
1692 'muscle':'\ud83d\udcaa',
1693 'mushroom':'\ud83c\udf44',
1694 'musical_keyboard':'\ud83c\udfb9',
1695 'musical_note':'\ud83c\udfb5',
1696 'musical_score':'\ud83c\udfbc',
1697 'mute':'\ud83d\udd07',
1698 'nail_care':'\ud83d\udc85',
1699 'name_badge':'\ud83d\udcdb',
1700 'national_park':'\ud83c\udfde',
1701 'nauseated_face':'\ud83e\udd22',
1702 'necktie':'\ud83d\udc54',
1703 'negative_squared_cross_mark':'\u274e',
1704 'nerd_face':'\ud83e\udd13',
1705 'neutral_face':'\ud83d\ude10',
1706 'new':'\ud83c\udd95',
1707 'new_moon':'\ud83c\udf11',
1708 'new_moon_with_face':'\ud83c\udf1a',
1709 'newspaper':'\ud83d\udcf0',
1710 'newspaper_roll':'\ud83d\uddde',
1711 'next_track_button':'\u23ed',
1712 'ng':'\ud83c\udd96',
1713 'no_good_man':'\ud83d\ude45&zwj;\u2642\ufe0f',
1714 'no_good_woman':'\ud83d\ude45',
1715 'night_with_stars':'\ud83c\udf03',
1716 'no_bell':'\ud83d\udd15',
1717 'no_bicycles':'\ud83d\udeb3',
1718 'no_entry':'\u26d4\ufe0f',
1719 'no_entry_sign':'\ud83d\udeab',
1720 'no_mobile_phones':'\ud83d\udcf5',
1721 'no_mouth':'\ud83d\ude36',
1722 'no_pedestrians':'\ud83d\udeb7',
1723 'no_smoking':'\ud83d\udead',
1724 'non-potable_water':'\ud83d\udeb1',
1725 'nose':'\ud83d\udc43',
1726 'notebook':'\ud83d\udcd3',
1727 'notebook_with_decorative_cover':'\ud83d\udcd4',
1728 'notes':'\ud83c\udfb6',
1729 'nut_and_bolt':'\ud83d\udd29',
1730 'o':'\u2b55\ufe0f',
1731 'o2':'\ud83c\udd7e\ufe0f',
1732 'ocean':'\ud83c\udf0a',
1733 'octopus':'\ud83d\udc19',
1734 'oden':'\ud83c\udf62',
1735 'office':'\ud83c\udfe2',
1736 'oil_drum':'\ud83d\udee2',
1737 'ok':'\ud83c\udd97',
1738 'ok_hand':'\ud83d\udc4c',
1739 'ok_man':'\ud83d\ude46&zwj;\u2642\ufe0f',
1740 'ok_woman':'\ud83d\ude46',
1741 'old_key':'\ud83d\udddd',
1742 'older_man':'\ud83d\udc74',
1743 'older_woman':'\ud83d\udc75',
1744 'om':'\ud83d\udd49',
1745 'on':'\ud83d\udd1b',
1746 'oncoming_automobile':'\ud83d\ude98',
1747 'oncoming_bus':'\ud83d\ude8d',
1748 'oncoming_police_car':'\ud83d\ude94',
1749 'oncoming_taxi':'\ud83d\ude96',
1750 'open_file_folder':'\ud83d\udcc2',
1751 'open_hands':'\ud83d\udc50',
1752 'open_mouth':'\ud83d\ude2e',
1753 'open_umbrella':'\u2602\ufe0f',
1754 'ophiuchus':'\u26ce',
1755 'orange_book':'\ud83d\udcd9',
1756 'orthodox_cross':'\u2626\ufe0f',
1757 'outbox_tray':'\ud83d\udce4',
1758 'owl':'\ud83e\udd89',
1759 'ox':'\ud83d\udc02',
1760 'package':'\ud83d\udce6',
1761 'page_facing_up':'\ud83d\udcc4',
1762 'page_with_curl':'\ud83d\udcc3',
1763 'pager':'\ud83d\udcdf',
1764 'paintbrush':'\ud83d\udd8c',
1765 'palm_tree':'\ud83c\udf34',
1766 'pancakes':'\ud83e\udd5e',
1767 'panda_face':'\ud83d\udc3c',
1768 'paperclip':'\ud83d\udcce',
1769 'paperclips':'\ud83d\udd87',
1770 'parasol_on_ground':'\u26f1',
1771 'parking':'\ud83c\udd7f\ufe0f',
1772 'part_alternation_mark':'\u303d\ufe0f',
1773 'partly_sunny':'\u26c5\ufe0f',
1774 'passenger_ship':'\ud83d\udef3',
1775 'passport_control':'\ud83d\udec2',
1776 'pause_button':'\u23f8',
1777 'peace_symbol':'\u262e\ufe0f',
1778 'peach':'\ud83c\udf51',
1779 'peanuts':'\ud83e\udd5c',
1780 'pear':'\ud83c\udf50',
1781 'pen':'\ud83d\udd8a',
1782 'pencil2':'\u270f\ufe0f',
1783 'penguin':'\ud83d\udc27',
1784 'pensive':'\ud83d\ude14',
1785 'performing_arts':'\ud83c\udfad',
1786 'persevere':'\ud83d\ude23',
1787 'person_fencing':'\ud83e\udd3a',
1788 'pouting_woman':'\ud83d\ude4e',
1789 'phone':'\u260e\ufe0f',
1790 'pick':'\u26cf',
1791 'pig':'\ud83d\udc37',
1792 'pig2':'\ud83d\udc16',
1793 'pig_nose':'\ud83d\udc3d',
1794 'pill':'\ud83d\udc8a',
1795 'pineapple':'\ud83c\udf4d',
1796 'ping_pong':'\ud83c\udfd3',
1797 'pisces':'\u2653\ufe0f',
1798 'pizza':'\ud83c\udf55',
1799 'place_of_worship':'\ud83d\uded0',
1800 'plate_with_cutlery':'\ud83c\udf7d',
1801 'play_or_pause_button':'\u23ef',
1802 'point_down':'\ud83d\udc47',
1803 'point_left':'\ud83d\udc48',
1804 'point_right':'\ud83d\udc49',
1805 'point_up':'\u261d\ufe0f',
1806 'point_up_2':'\ud83d\udc46',
1807 'police_car':'\ud83d\ude93',
1808 'policewoman':'\ud83d\udc6e&zwj;\u2640\ufe0f',
1809 'poodle':'\ud83d\udc29',
1810 'popcorn':'\ud83c\udf7f',
1811 'post_office':'\ud83c\udfe3',
1812 'postal_horn':'\ud83d\udcef',
1813 'postbox':'\ud83d\udcee',
1814 'potable_water':'\ud83d\udeb0',
1815 'potato':'\ud83e\udd54',
1816 'pouch':'\ud83d\udc5d',
1817 'poultry_leg':'\ud83c\udf57',
1818 'pound':'\ud83d\udcb7',
1819 'rage':'\ud83d\ude21',
1820 'pouting_cat':'\ud83d\ude3e',
1821 'pouting_man':'\ud83d\ude4e&zwj;\u2642\ufe0f',
1822 'pray':'\ud83d\ude4f',
1823 'prayer_beads':'\ud83d\udcff',
1824 'pregnant_woman':'\ud83e\udd30',
1825 'previous_track_button':'\u23ee',
1826 'prince':'\ud83e\udd34',
1827 'princess':'\ud83d\udc78',
1828 'printer':'\ud83d\udda8',
1829 'purple_heart':'\ud83d\udc9c',
1830 'purse':'\ud83d\udc5b',
1831 'pushpin':'\ud83d\udccc',
1832 'put_litter_in_its_place':'\ud83d\udeae',
1833 'question':'\u2753',
1834 'rabbit':'\ud83d\udc30',
1835 'rabbit2':'\ud83d\udc07',
1836 'racehorse':'\ud83d\udc0e',
1837 'racing_car':'\ud83c\udfce',
1838 'radio':'\ud83d\udcfb',
1839 'radio_button':'\ud83d\udd18',
1840 'radioactive':'\u2622\ufe0f',
1841 'railway_car':'\ud83d\ude83',
1842 'railway_track':'\ud83d\udee4',
1843 'rainbow':'\ud83c\udf08',
1844 'rainbow_flag':'\ud83c\udff3\ufe0f&zwj;\ud83c\udf08',
1845 'raised_back_of_hand':'\ud83e\udd1a',
1846 'raised_hand_with_fingers_splayed':'\ud83d\udd90',
1847 'raised_hands':'\ud83d\ude4c',
1848 'raising_hand_woman':'\ud83d\ude4b',
1849 'raising_hand_man':'\ud83d\ude4b&zwj;\u2642\ufe0f',
1850 'ram':'\ud83d\udc0f',
1851 'ramen':'\ud83c\udf5c',
1852 'rat':'\ud83d\udc00',
1853 'record_button':'\u23fa',
1854 'recycle':'\u267b\ufe0f',
1855 'red_circle':'\ud83d\udd34',
1856 'registered':'\u00ae\ufe0f',
1857 'relaxed':'\u263a\ufe0f',
1858 'relieved':'\ud83d\ude0c',
1859 'reminder_ribbon':'\ud83c\udf97',
1860 'repeat':'\ud83d\udd01',
1861 'repeat_one':'\ud83d\udd02',
1862 'rescue_worker_helmet':'\u26d1',
1863 'restroom':'\ud83d\udebb',
1864 'revolving_hearts':'\ud83d\udc9e',
1865 'rewind':'\u23ea',
1866 'rhinoceros':'\ud83e\udd8f',
1867 'ribbon':'\ud83c\udf80',
1868 'rice':'\ud83c\udf5a',
1869 'rice_ball':'\ud83c\udf59',
1870 'rice_cracker':'\ud83c\udf58',
1871 'rice_scene':'\ud83c\udf91',
1872 'right_anger_bubble':'\ud83d\uddef',
1873 'ring':'\ud83d\udc8d',
1874 'robot':'\ud83e\udd16',
1875 'rocket':'\ud83d\ude80',
1876 'rofl':'\ud83e\udd23',
1877 'roll_eyes':'\ud83d\ude44',
1878 'roller_coaster':'\ud83c\udfa2',
1879 'rooster':'\ud83d\udc13',
1880 'rose':'\ud83c\udf39',
1881 'rosette':'\ud83c\udff5',
1882 'rotating_light':'\ud83d\udea8',
1883 'round_pushpin':'\ud83d\udccd',
1884 'rowing_man':'\ud83d\udea3',
1885 'rowing_woman':'\ud83d\udea3&zwj;\u2640\ufe0f',
1886 'rugby_football':'\ud83c\udfc9',
1887 'running_man':'\ud83c\udfc3',
1888 'running_shirt_with_sash':'\ud83c\udfbd',
1889 'running_woman':'\ud83c\udfc3&zwj;\u2640\ufe0f',
1890 'sa':'\ud83c\ude02\ufe0f',
1891 'sagittarius':'\u2650\ufe0f',
1892 'sake':'\ud83c\udf76',
1893 'sandal':'\ud83d\udc61',
1894 'santa':'\ud83c\udf85',
1895 'satellite':'\ud83d\udce1',
1896 'saxophone':'\ud83c\udfb7',
1897 'school':'\ud83c\udfeb',
1898 'school_satchel':'\ud83c\udf92',
1899 'scissors':'\u2702\ufe0f',
1900 'scorpion':'\ud83e\udd82',
1901 'scorpius':'\u264f\ufe0f',
1902 'scream':'\ud83d\ude31',
1903 'scream_cat':'\ud83d\ude40',
1904 'scroll':'\ud83d\udcdc',
1905 'seat':'\ud83d\udcba',
1906 'secret':'\u3299\ufe0f',
1907 'see_no_evil':'\ud83d\ude48',
1908 'seedling':'\ud83c\udf31',
1909 'selfie':'\ud83e\udd33',
1910 'shallow_pan_of_food':'\ud83e\udd58',
1911 'shamrock':'\u2618\ufe0f',
1912 'shark':'\ud83e\udd88',
1913 'shaved_ice':'\ud83c\udf67',
1914 'sheep':'\ud83d\udc11',
1915 'shell':'\ud83d\udc1a',
1916 'shield':'\ud83d\udee1',
1917 'shinto_shrine':'\u26e9',
1918 'ship':'\ud83d\udea2',
1919 'shirt':'\ud83d\udc55',
1920 'shopping':'\ud83d\udecd',
1921 'shopping_cart':'\ud83d\uded2',
1922 'shower':'\ud83d\udebf',
1923 'shrimp':'\ud83e\udd90',
1924 'signal_strength':'\ud83d\udcf6',
1925 'six_pointed_star':'\ud83d\udd2f',
1926 'ski':'\ud83c\udfbf',
1927 'skier':'\u26f7',
1928 'skull':'\ud83d\udc80',
1929 'skull_and_crossbones':'\u2620\ufe0f',
1930 'sleeping':'\ud83d\ude34',
1931 'sleeping_bed':'\ud83d\udecc',
1932 'sleepy':'\ud83d\ude2a',
1933 'slightly_frowning_face':'\ud83d\ude41',
1934 'slightly_smiling_face':'\ud83d\ude42',
1935 'slot_machine':'\ud83c\udfb0',
1936 'small_airplane':'\ud83d\udee9',
1937 'small_blue_diamond':'\ud83d\udd39',
1938 'small_orange_diamond':'\ud83d\udd38',
1939 'small_red_triangle':'\ud83d\udd3a',
1940 'small_red_triangle_down':'\ud83d\udd3b',
1941 'smile':'\ud83d\ude04',
1942 'smile_cat':'\ud83d\ude38',
1943 'smiley':'\ud83d\ude03',
1944 'smiley_cat':'\ud83d\ude3a',
1945 'smiling_imp':'\ud83d\ude08',
1946 'smirk':'\ud83d\ude0f',
1947 'smirk_cat':'\ud83d\ude3c',
1948 'smoking':'\ud83d\udeac',
1949 'snail':'\ud83d\udc0c',
1950 'snake':'\ud83d\udc0d',
1951 'sneezing_face':'\ud83e\udd27',
1952 'snowboarder':'\ud83c\udfc2',
1953 'snowflake':'\u2744\ufe0f',
1954 'snowman':'\u26c4\ufe0f',
1955 'snowman_with_snow':'\u2603\ufe0f',
1956 'sob':'\ud83d\ude2d',
1957 'soccer':'\u26bd\ufe0f',
1958 'soon':'\ud83d\udd1c',
1959 'sos':'\ud83c\udd98',
1960 'sound':'\ud83d\udd09',
1961 'space_invader':'\ud83d\udc7e',
1962 'spades':'\u2660\ufe0f',
1963 'spaghetti':'\ud83c\udf5d',
1964 'sparkle':'\u2747\ufe0f',
1965 'sparkler':'\ud83c\udf87',
1966 'sparkles':'\u2728',
1967 'sparkling_heart':'\ud83d\udc96',
1968 'speak_no_evil':'\ud83d\ude4a',
1969 'speaker':'\ud83d\udd08',
1970 'speaking_head':'\ud83d\udde3',
1971 'speech_balloon':'\ud83d\udcac',
1972 'speedboat':'\ud83d\udea4',
1973 'spider':'\ud83d\udd77',
1974 'spider_web':'\ud83d\udd78',
1975 'spiral_calendar':'\ud83d\uddd3',
1976 'spiral_notepad':'\ud83d\uddd2',
1977 'spoon':'\ud83e\udd44',
1978 'squid':'\ud83e\udd91',
1979 'stadium':'\ud83c\udfdf',
1980 'star':'\u2b50\ufe0f',
1981 'star2':'\ud83c\udf1f',
1982 'star_and_crescent':'\u262a\ufe0f',
1983 'star_of_david':'\u2721\ufe0f',
1984 'stars':'\ud83c\udf20',
1985 'station':'\ud83d\ude89',
1986 'statue_of_liberty':'\ud83d\uddfd',
1987 'steam_locomotive':'\ud83d\ude82',
1988 'stew':'\ud83c\udf72',
1989 'stop_button':'\u23f9',
1990 'stop_sign':'\ud83d\uded1',
1991 'stopwatch':'\u23f1',
1992 'straight_ruler':'\ud83d\udccf',
1993 'strawberry':'\ud83c\udf53',
1994 'stuck_out_tongue':'\ud83d\ude1b',
1995 'stuck_out_tongue_closed_eyes':'\ud83d\ude1d',
1996 'stuck_out_tongue_winking_eye':'\ud83d\ude1c',
1997 'studio_microphone':'\ud83c\udf99',
1998 'stuffed_flatbread':'\ud83e\udd59',
1999 'sun_behind_large_cloud':'\ud83c\udf25',
2000 'sun_behind_rain_cloud':'\ud83c\udf26',
2001 'sun_behind_small_cloud':'\ud83c\udf24',
2002 'sun_with_face':'\ud83c\udf1e',
2003 'sunflower':'\ud83c\udf3b',
2004 'sunglasses':'\ud83d\ude0e',
2005 'sunny':'\u2600\ufe0f',
2006 'sunrise':'\ud83c\udf05',
2007 'sunrise_over_mountains':'\ud83c\udf04',
2008 'surfing_man':'\ud83c\udfc4',
2009 'surfing_woman':'\ud83c\udfc4&zwj;\u2640\ufe0f',
2010 'sushi':'\ud83c\udf63',
2011 'suspension_railway':'\ud83d\ude9f',
2012 'sweat':'\ud83d\ude13',
2013 'sweat_drops':'\ud83d\udca6',
2014 'sweat_smile':'\ud83d\ude05',
2015 'sweet_potato':'\ud83c\udf60',
2016 'swimming_man':'\ud83c\udfca',
2017 'swimming_woman':'\ud83c\udfca&zwj;\u2640\ufe0f',
2018 'symbols':'\ud83d\udd23',
2019 'synagogue':'\ud83d\udd4d',
2020 'syringe':'\ud83d\udc89',
2021 'taco':'\ud83c\udf2e',
2022 'tada':'\ud83c\udf89',
2023 'tanabata_tree':'\ud83c\udf8b',
2024 'taurus':'\u2649\ufe0f',
2025 'taxi':'\ud83d\ude95',
2026 'tea':'\ud83c\udf75',
2027 'telephone_receiver':'\ud83d\udcde',
2028 'telescope':'\ud83d\udd2d',
2029 'tennis':'\ud83c\udfbe',
2030 'tent':'\u26fa\ufe0f',
2031 'thermometer':'\ud83c\udf21',
2032 'thinking':'\ud83e\udd14',
2033 'thought_balloon':'\ud83d\udcad',
2034 'ticket':'\ud83c\udfab',
2035 'tickets':'\ud83c\udf9f',
2036 'tiger':'\ud83d\udc2f',
2037 'tiger2':'\ud83d\udc05',
2038 'timer_clock':'\u23f2',
2039 'tipping_hand_man':'\ud83d\udc81&zwj;\u2642\ufe0f',
2040 'tired_face':'\ud83d\ude2b',
2041 'tm':'\u2122\ufe0f',
2042 'toilet':'\ud83d\udebd',
2043 'tokyo_tower':'\ud83d\uddfc',
2044 'tomato':'\ud83c\udf45',
2045 'tongue':'\ud83d\udc45',
2046 'top':'\ud83d\udd1d',
2047 'tophat':'\ud83c\udfa9',
2048 'tornado':'\ud83c\udf2a',
2049 'trackball':'\ud83d\uddb2',
2050 'tractor':'\ud83d\ude9c',
2051 'traffic_light':'\ud83d\udea5',
2052 'train':'\ud83d\ude8b',
2053 'train2':'\ud83d\ude86',
2054 'tram':'\ud83d\ude8a',
2055 'triangular_flag_on_post':'\ud83d\udea9',
2056 'triangular_ruler':'\ud83d\udcd0',
2057 'trident':'\ud83d\udd31',
2058 'triumph':'\ud83d\ude24',
2059 'trolleybus':'\ud83d\ude8e',
2060 'trophy':'\ud83c\udfc6',
2061 'tropical_drink':'\ud83c\udf79',
2062 'tropical_fish':'\ud83d\udc20',
2063 'truck':'\ud83d\ude9a',
2064 'trumpet':'\ud83c\udfba',
2065 'tulip':'\ud83c\udf37',
2066 'tumbler_glass':'\ud83e\udd43',
2067 'turkey':'\ud83e\udd83',
2068 'turtle':'\ud83d\udc22',
2069 'tv':'\ud83d\udcfa',
2070 'twisted_rightwards_arrows':'\ud83d\udd00',
2071 'two_hearts':'\ud83d\udc95',
2072 'two_men_holding_hands':'\ud83d\udc6c',
2073 'two_women_holding_hands':'\ud83d\udc6d',
2074 'u5272':'\ud83c\ude39',
2075 'u5408':'\ud83c\ude34',
2076 'u55b6':'\ud83c\ude3a',
2077 'u6307':'\ud83c\ude2f\ufe0f',
2078 'u6708':'\ud83c\ude37\ufe0f',
2079 'u6709':'\ud83c\ude36',
2080 'u6e80':'\ud83c\ude35',
2081 'u7121':'\ud83c\ude1a\ufe0f',
2082 'u7533':'\ud83c\ude38',
2083 'u7981':'\ud83c\ude32',
2084 'u7a7a':'\ud83c\ude33',
2085 'umbrella':'\u2614\ufe0f',
2086 'unamused':'\ud83d\ude12',
2087 'underage':'\ud83d\udd1e',
2088 'unicorn':'\ud83e\udd84',
2089 'unlock':'\ud83d\udd13',
2090 'up':'\ud83c\udd99',
2091 'upside_down_face':'\ud83d\ude43',
2092 'v':'\u270c\ufe0f',
2093 'vertical_traffic_light':'\ud83d\udea6',
2094 'vhs':'\ud83d\udcfc',
2095 'vibration_mode':'\ud83d\udcf3',
2096 'video_camera':'\ud83d\udcf9',
2097 'video_game':'\ud83c\udfae',
2098 'violin':'\ud83c\udfbb',
2099 'virgo':'\u264d\ufe0f',
2100 'volcano':'\ud83c\udf0b',
2101 'volleyball':'\ud83c\udfd0',
2102 'vs':'\ud83c\udd9a',
2103 'vulcan_salute':'\ud83d\udd96',
2104 'walking_man':'\ud83d\udeb6',
2105 'walking_woman':'\ud83d\udeb6&zwj;\u2640\ufe0f',
2106 'waning_crescent_moon':'\ud83c\udf18',
2107 'waning_gibbous_moon':'\ud83c\udf16',
2108 'warning':'\u26a0\ufe0f',
2109 'wastebasket':'\ud83d\uddd1',
2110 'watch':'\u231a\ufe0f',
2111 'water_buffalo':'\ud83d\udc03',
2112 'watermelon':'\ud83c\udf49',
2113 'wave':'\ud83d\udc4b',
2114 'wavy_dash':'\u3030\ufe0f',
2115 'waxing_crescent_moon':'\ud83c\udf12',
2116 'wc':'\ud83d\udebe',
2117 'weary':'\ud83d\ude29',
2118 'wedding':'\ud83d\udc92',
2119 'weight_lifting_man':'\ud83c\udfcb\ufe0f',
2120 'weight_lifting_woman':'\ud83c\udfcb\ufe0f&zwj;\u2640\ufe0f',
2121 'whale':'\ud83d\udc33',
2122 'whale2':'\ud83d\udc0b',
2123 'wheel_of_dharma':'\u2638\ufe0f',
2124 'wheelchair':'\u267f\ufe0f',
2125 'white_check_mark':'\u2705',
2126 'white_circle':'\u26aa\ufe0f',
2127 'white_flag':'\ud83c\udff3\ufe0f',
2128 'white_flower':'\ud83d\udcae',
2129 'white_large_square':'\u2b1c\ufe0f',
2130 'white_medium_small_square':'\u25fd\ufe0f',
2131 'white_medium_square':'\u25fb\ufe0f',
2132 'white_small_square':'\u25ab\ufe0f',
2133 'white_square_button':'\ud83d\udd33',
2134 'wilted_flower':'\ud83e\udd40',
2135 'wind_chime':'\ud83c\udf90',
2136 'wind_face':'\ud83c\udf2c',
2137 'wine_glass':'\ud83c\udf77',
2138 'wink':'\ud83d\ude09',
2139 'wolf':'\ud83d\udc3a',
2140 'woman':'\ud83d\udc69',
2141 'woman_artist':'\ud83d\udc69&zwj;\ud83c\udfa8',
2142 'woman_astronaut':'\ud83d\udc69&zwj;\ud83d\ude80',
2143 'woman_cartwheeling':'\ud83e\udd38&zwj;\u2640\ufe0f',
2144 'woman_cook':'\ud83d\udc69&zwj;\ud83c\udf73',
2145 'woman_facepalming':'\ud83e\udd26&zwj;\u2640\ufe0f',
2146 'woman_factory_worker':'\ud83d\udc69&zwj;\ud83c\udfed',
2147 'woman_farmer':'\ud83d\udc69&zwj;\ud83c\udf3e',
2148 'woman_firefighter':'\ud83d\udc69&zwj;\ud83d\ude92',
2149 'woman_health_worker':'\ud83d\udc69&zwj;\u2695\ufe0f',
2150 'woman_judge':'\ud83d\udc69&zwj;\u2696\ufe0f',
2151 'woman_juggling':'\ud83e\udd39&zwj;\u2640\ufe0f',
2152 'woman_mechanic':'\ud83d\udc69&zwj;\ud83d\udd27',
2153 'woman_office_worker':'\ud83d\udc69&zwj;\ud83d\udcbc',
2154 'woman_pilot':'\ud83d\udc69&zwj;\u2708\ufe0f',
2155 'woman_playing_handball':'\ud83e\udd3e&zwj;\u2640\ufe0f',
2156 'woman_playing_water_polo':'\ud83e\udd3d&zwj;\u2640\ufe0f',
2157 'woman_scientist':'\ud83d\udc69&zwj;\ud83d\udd2c',
2158 'woman_shrugging':'\ud83e\udd37&zwj;\u2640\ufe0f',
2159 'woman_singer':'\ud83d\udc69&zwj;\ud83c\udfa4',
2160 'woman_student':'\ud83d\udc69&zwj;\ud83c\udf93',
2161 'woman_teacher':'\ud83d\udc69&zwj;\ud83c\udfeb',
2162 'woman_technologist':'\ud83d\udc69&zwj;\ud83d\udcbb',
2163 'woman_with_turban':'\ud83d\udc73&zwj;\u2640\ufe0f',
2164 'womans_clothes':'\ud83d\udc5a',
2165 'womans_hat':'\ud83d\udc52',
2166 'women_wrestling':'\ud83e\udd3c&zwj;\u2640\ufe0f',
2167 'womens':'\ud83d\udeba',
2168 'world_map':'\ud83d\uddfa',
2169 'worried':'\ud83d\ude1f',
2170 'wrench':'\ud83d\udd27',
2171 'writing_hand':'\u270d\ufe0f',
2172 'x':'\u274c',
2173 'yellow_heart':'\ud83d\udc9b',
2174 'yen':'\ud83d\udcb4',
2175 'yin_yang':'\u262f\ufe0f',
2176 'yum':'\ud83d\ude0b',
2177 'zap':'\u26a1\ufe0f',
2178 'zipper_mouth_face':'\ud83e\udd10',
2179 'zzz':'\ud83d\udca4',
2180
2181 /* special emojis :P */
2182 'octocat': '<img alt=":octocat:" height="20" width="20" align="absmiddle" src="https://assets-cdn.github.com/images/icons/emoji/octocat.png">',
2183 'showdown': '<span style="font-family: \'Anonymous Pro\', monospace; text-decoration: underline; text-decoration-style: dashed; text-decoration-color: #3e8b8a;text-underline-position: under;">S</span>'
2184};
2185
2186/**
2187 * Created by Estevao on 31-05-2015.
2188 */
2189
2190/**
2191 * Showdown Converter class
2192 * @class
2193 * @param {object} [converterOptions]
2194 * @returns {Converter}
2195 */
2196showdown.Converter = function (converterOptions) {
2197 'use strict';
2198
2199 var
2200 /**
2201 * Options used by this converter
2202 * @private
2203 * @type {{}}
2204 */
2205 options = {},
2206
2207 /**
2208 * Language extensions used by this converter
2209 * @private
2210 * @type {Array}
2211 */
2212 langExtensions = [],
2213
2214 /**
2215 * Output modifiers extensions used by this converter
2216 * @private
2217 * @type {Array}
2218 */
2219 outputModifiers = [],
2220
2221 /**
2222 * Event listeners
2223 * @private
2224 * @type {{}}
2225 */
2226 listeners = {},
2227
2228 /**
2229 * The flavor set in this converter
2230 */
2231 setConvFlavor = setFlavor,
2232
2233 /**
2234 * Metadata of the document
2235 * @type {{parsed: {}, raw: string, format: string}}
2236 */
2237 metadata = {
2238 parsed: {},
2239 raw: '',
2240 format: ''
2241 };
2242
2243 _constructor();
2244
2245 /**
2246 * Converter constructor
2247 * @private
2248 */
2249 function _constructor () {
2250 converterOptions = converterOptions || {};
2251
2252 for (var gOpt in globalOptions) {
2253 if (globalOptions.hasOwnProperty(gOpt)) {
2254 options[gOpt] = globalOptions[gOpt];
2255 }
2256 }
2257
2258 // Merge options
2259 if (typeof converterOptions === 'object') {
2260 for (var opt in converterOptions) {
2261 if (converterOptions.hasOwnProperty(opt)) {
2262 options[opt] = converterOptions[opt];
2263 }
2264 }
2265 } else {
2266 throw Error('Converter expects the passed parameter to be an object, but ' + typeof converterOptions +
2267 ' was passed instead.');
2268 }
2269
2270 if (options.extensions) {
2271 showdown.helper.forEach(options.extensions, _parseExtension);
2272 }
2273 }
2274
2275 /**
2276 * Parse extension
2277 * @param {*} ext
2278 * @param {string} [name='']
2279 * @private
2280 */
2281 function _parseExtension (ext, name) {
2282
2283 name = name || null;
2284 // If it's a string, the extension was previously loaded
2285 if (showdown.helper.isString(ext)) {
2286 ext = showdown.helper.stdExtName(ext);
2287 name = ext;
2288
2289 // LEGACY_SUPPORT CODE
2290 if (showdown.extensions[ext]) {
2291 console.warn('DEPRECATION WARNING: ' + ext + ' is an old extension that uses a deprecated loading method.' +
2292 'Please inform the developer that the extension should be updated!');
2293 legacyExtensionLoading(showdown.extensions[ext], ext);
2294 return;
2295 // END LEGACY SUPPORT CODE
2296
2297 } else if (!showdown.helper.isUndefined(extensions[ext])) {
2298 ext = extensions[ext];
2299
2300 } else {
2301 throw Error('Extension "' + ext + '" could not be loaded. It was either not found or is not a valid extension.');
2302 }
2303 }
2304
2305 if (typeof ext === 'function') {
2306 ext = ext();
2307 }
2308
2309 if (!showdown.helper.isArray(ext)) {
2310 ext = [ext];
2311 }
2312
2313 var validExt = validate(ext, name);
2314 if (!validExt.valid) {
2315 throw Error(validExt.error);
2316 }
2317
2318 for (var i = 0; i < ext.length; ++i) {
2319 switch (ext[i].type) {
2320
2321 case 'lang':
2322 langExtensions.push(ext[i]);
2323 break;
2324
2325 case 'output':
2326 outputModifiers.push(ext[i]);
2327 break;
2328 }
2329 if (ext[i].hasOwnProperty('listeners')) {
2330 for (var ln in ext[i].listeners) {
2331 if (ext[i].listeners.hasOwnProperty(ln)) {
2332 listen(ln, ext[i].listeners[ln]);
2333 }
2334 }
2335 }
2336 }
2337
2338 }
2339
2340 /**
2341 * LEGACY_SUPPORT
2342 * @param {*} ext
2343 * @param {string} name
2344 */
2345 function legacyExtensionLoading (ext, name) {
2346 if (typeof ext === 'function') {
2347 ext = ext(new showdown.Converter());
2348 }
2349 if (!showdown.helper.isArray(ext)) {
2350 ext = [ext];
2351 }
2352 var valid = validate(ext, name);
2353
2354 if (!valid.valid) {
2355 throw Error(valid.error);
2356 }
2357
2358 for (var i = 0; i < ext.length; ++i) {
2359 switch (ext[i].type) {
2360 case 'lang':
2361 langExtensions.push(ext[i]);
2362 break;
2363 case 'output':
2364 outputModifiers.push(ext[i]);
2365 break;
2366 default:// should never reach here
2367 throw Error('Extension loader error: Type unrecognized!!!');
2368 }
2369 }
2370 }
2371
2372 /**
2373 * Listen to an event
2374 * @param {string} name
2375 * @param {function} callback
2376 */
2377 function listen (name, callback) {
2378 if (!showdown.helper.isString(name)) {
2379 throw Error('Invalid argument in converter.listen() method: name must be a string, but ' + typeof name + ' given');
2380 }
2381
2382 if (typeof callback !== 'function') {
2383 throw Error('Invalid argument in converter.listen() method: callback must be a function, but ' + typeof callback + ' given');
2384 }
2385
2386 if (!listeners.hasOwnProperty(name)) {
2387 listeners[name] = [];
2388 }
2389 listeners[name].push(callback);
2390 }
2391
2392 function rTrimInputText (text) {
2393 var rsp = text.match(/^\s*/)[0].length,
2394 rgx = new RegExp('^\\s{0,' + rsp + '}', 'gm');
2395 return text.replace(rgx, '');
2396 }
2397
2398 /**
2399 * Dispatch an event
2400 * @private
2401 * @param {string} evtName Event name
2402 * @param {string} text Text
2403 * @param {{}} options Converter Options
2404 * @param {{}} globals
2405 * @returns {string}
2406 */
2407 this._dispatch = function dispatch (evtName, text, options, globals) {
2408 if (listeners.hasOwnProperty(evtName)) {
2409 for (var ei = 0; ei < listeners[evtName].length; ++ei) {
2410 var nText = listeners[evtName][ei](evtName, text, this, options, globals);
2411 if (nText && typeof nText !== 'undefined') {
2412 text = nText;
2413 }
2414 }
2415 }
2416 return text;
2417 };
2418
2419 /**
2420 * Listen to an event
2421 * @param {string} name
2422 * @param {function} callback
2423 * @returns {showdown.Converter}
2424 */
2425 this.listen = function (name, callback) {
2426 listen(name, callback);
2427 return this;
2428 };
2429
2430 /**
2431 * Converts a markdown string into HTML
2432 * @param {string} text
2433 * @returns {*}
2434 */
2435 this.makeHtml = function (text) {
2436 //check if text is not falsy
2437 if (!text) {
2438 return text;
2439 }
2440
2441 var globals = {
2442 gHtmlBlocks: [],
2443 gHtmlMdBlocks: [],
2444 gHtmlSpans: [],
2445 gUrls: {},
2446 gTitles: {},
2447 gDimensions: {},
2448 gListLevel: 0,
2449 hashLinkCounts: {},
2450 langExtensions: langExtensions,
2451 outputModifiers: outputModifiers,
2452 converter: this,
2453 ghCodeBlocks: [],
2454 metadata: {
2455 parsed: {},
2456 raw: '',
2457 format: ''
2458 }
2459 };
2460
2461 // This lets us use ¨ trema as an escape char to avoid md5 hashes
2462 // The choice of character is arbitrary; anything that isn't
2463 // magic in Markdown will work.
2464 text = text.replace(/¨/g, '¨T');
2465
2466 // Replace $ with ¨D
2467 // RegExp interprets $ as a special character
2468 // when it's in a replacement string
2469 text = text.replace(/\$/g, '¨D');
2470
2471 // Standardize line endings
2472 text = text.replace(/\r\n/g, '\n'); // DOS to Unix
2473 text = text.replace(/\r/g, '\n'); // Mac to Unix
2474
2475 // Stardardize line spaces
2476 text = text.replace(/\u00A0/g, '&nbsp;');
2477
2478 if (options.smartIndentationFix) {
2479 text = rTrimInputText(text);
2480 }
2481
2482 // Make sure text begins and ends with a couple of newlines:
2483 text = '\n\n' + text + '\n\n';
2484
2485 // detab
2486 text = showdown.subParser('detab')(text, options, globals);
2487
2488 /**
2489 * Strip any lines consisting only of spaces and tabs.
2490 * This makes subsequent regexs easier to write, because we can
2491 * match consecutive blank lines with /\n+/ instead of something
2492 * contorted like /[ \t]*\n+/
2493 */
2494 text = text.replace(/^[ \t]+$/mg, '');
2495
2496 //run languageExtensions
2497 showdown.helper.forEach(langExtensions, function (ext) {
2498 text = showdown.subParser('runExtension')(ext, text, options, globals);
2499 });
2500
2501 // run the sub parsers
2502 text = showdown.subParser('metadata')(text, options, globals);
2503 text = showdown.subParser('hashPreCodeTags')(text, options, globals);
2504 text = showdown.subParser('githubCodeBlocks')(text, options, globals);
2505 text = showdown.subParser('hashHTMLBlocks')(text, options, globals);
2506 text = showdown.subParser('hashCodeTags')(text, options, globals);
2507 text = showdown.subParser('stripLinkDefinitions')(text, options, globals);
2508 text = showdown.subParser('blockGamut')(text, options, globals);
2509 text = showdown.subParser('unhashHTMLSpans')(text, options, globals);
2510 text = showdown.subParser('unescapeSpecialChars')(text, options, globals);
2511
2512 // attacklab: Restore dollar signs
2513 text = text.replace(/¨D/g, '$$');
2514
2515 // attacklab: Restore tremas
2516 text = text.replace(/¨T/g, '¨');
2517
2518 // render a complete html document instead of a partial if the option is enabled
2519 text = showdown.subParser('completeHTMLDocument')(text, options, globals);
2520
2521 // Run output modifiers
2522 showdown.helper.forEach(outputModifiers, function (ext) {
2523 text = showdown.subParser('runExtension')(ext, text, options, globals);
2524 });
2525
2526 // update metadata
2527 metadata = globals.metadata;
2528 return text;
2529 };
2530
2531 /**
2532 * Converts an HTML string into a markdown string
2533 * @param src
2534 * @param [HTMLParser] A WHATWG DOM and HTML parser, such as JSDOM. If none is supplied, window.document will be used.
2535 * @returns {string}
2536 */
2537 this.makeMarkdown = this.makeMd = function (src, HTMLParser) {
2538
2539 // replace \r\n with \n
2540 src = src.replace(/\r\n/g, '\n');
2541 src = src.replace(/\r/g, '\n'); // old macs
2542
2543 // due to an edge case, we need to find this: > <
2544 // to prevent removing of non silent white spaces
2545 // ex: <em>this is</em> <strong>sparta</strong>
2546 src = src.replace(/>[ \t]+</, '>¨NBSP;<');
2547
2548 if (!HTMLParser) {
2549 if (window && window.document) {
2550 HTMLParser = window.document;
2551 } else {
2552 throw new Error('HTMLParser is undefined. If in a webworker or nodejs environment, you need to provide a WHATWG DOM and HTML such as JSDOM');
2553 }
2554 }
2555
2556 var doc = HTMLParser.createElement('div');
2557 doc.innerHTML = src;
2558
2559 var globals = {
2560 preList: substitutePreCodeTags(doc)
2561 };
2562
2563 // remove all newlines and collapse spaces
2564 clean(doc);
2565
2566 // some stuff, like accidental reference links must now be escaped
2567 // TODO
2568 // doc.innerHTML = doc.innerHTML.replace(/\[[\S\t ]]/);
2569
2570 var nodes = doc.childNodes,
2571 mdDoc = '';
2572
2573 for (var i = 0; i < nodes.length; i++) {
2574 mdDoc += showdown.subParser('makeMarkdown.node')(nodes[i], globals);
2575 }
2576
2577 function clean (node) {
2578 for (var n = 0; n < node.childNodes.length; ++n) {
2579 var child = node.childNodes[n];
2580 if (child.nodeType === 3) {
2581 if (!/\S/.test(child.nodeValue) && !/^[ ]+$/.test(child.nodeValue)) {
2582 node.removeChild(child);
2583 --n;
2584 } else {
2585 child.nodeValue = child.nodeValue.split('\n').join(' ');
2586 child.nodeValue = child.nodeValue.replace(/(\s)+/g, '$1');
2587 }
2588 } else if (child.nodeType === 1) {
2589 clean(child);
2590 }
2591 }
2592 }
2593
2594 // find all pre tags and replace contents with placeholder
2595 // we need this so that we can remove all indentation from html
2596 // to ease up parsing
2597 function substitutePreCodeTags (doc) {
2598
2599 var pres = doc.querySelectorAll('pre'),
2600 presPH = [];
2601
2602 for (var i = 0; i < pres.length; ++i) {
2603
2604 if (pres[i].childElementCount === 1 && pres[i].firstChild.tagName.toLowerCase() === 'code') {
2605 var content = pres[i].firstChild.innerHTML.trim(),
2606 language = pres[i].firstChild.getAttribute('data-language') || '';
2607
2608 // if data-language attribute is not defined, then we look for class language-*
2609 if (language === '') {
2610 var classes = pres[i].firstChild.className.split(' ');
2611 for (var c = 0; c < classes.length; ++c) {
2612 var matches = classes[c].match(/^language-(.+)$/);
2613 if (matches !== null) {
2614 language = matches[1];
2615 break;
2616 }
2617 }
2618 }
2619
2620 // unescape html entities in content
2621 content = showdown.helper.unescapeHTMLEntities(content);
2622
2623 presPH.push(content);
2624 pres[i].outerHTML = '<precode language="' + language + '" precodenum="' + i.toString() + '"></precode>';
2625 } else {
2626 presPH.push(pres[i].innerHTML);
2627 pres[i].innerHTML = '';
2628 pres[i].setAttribute('prenum', i.toString());
2629 }
2630 }
2631 return presPH;
2632 }
2633
2634 return mdDoc;
2635 };
2636
2637 /**
2638 * Set an option of this Converter instance
2639 * @param {string} key
2640 * @param {*} value
2641 */
2642 this.setOption = function (key, value) {
2643 options[key] = value;
2644 };
2645
2646 /**
2647 * Get the option of this Converter instance
2648 * @param {string} key
2649 * @returns {*}
2650 */
2651 this.getOption = function (key) {
2652 return options[key];
2653 };
2654
2655 /**
2656 * Get the options of this Converter instance
2657 * @returns {{}}
2658 */
2659 this.getOptions = function () {
2660 return options;
2661 };
2662
2663 /**
2664 * Add extension to THIS converter
2665 * @param {{}} extension
2666 * @param {string} [name=null]
2667 */
2668 this.addExtension = function (extension, name) {
2669 name = name || null;
2670 _parseExtension(extension, name);
2671 };
2672
2673 /**
2674 * Use a global registered extension with THIS converter
2675 * @param {string} extensionName Name of the previously registered extension
2676 */
2677 this.useExtension = function (extensionName) {
2678 _parseExtension(extensionName);
2679 };
2680
2681 /**
2682 * Set the flavor THIS converter should use
2683 * @param {string} name
2684 */
2685 this.setFlavor = function (name) {
2686 if (!flavor.hasOwnProperty(name)) {
2687 throw Error(name + ' flavor was not found');
2688 }
2689 var preset = flavor[name];
2690 setConvFlavor = name;
2691 for (var option in preset) {
2692 if (preset.hasOwnProperty(option)) {
2693 options[option] = preset[option];
2694 }
2695 }
2696 };
2697
2698 /**
2699 * Get the currently set flavor of this converter
2700 * @returns {string}
2701 */
2702 this.getFlavor = function () {
2703 return setConvFlavor;
2704 };
2705
2706 /**
2707 * Remove an extension from THIS converter.
2708 * Note: This is a costly operation. It's better to initialize a new converter
2709 * and specify the extensions you wish to use
2710 * @param {Array} extension
2711 */
2712 this.removeExtension = function (extension) {
2713 if (!showdown.helper.isArray(extension)) {
2714 extension = [extension];
2715 }
2716 for (var a = 0; a < extension.length; ++a) {
2717 var ext = extension[a];
2718 for (var i = 0; i < langExtensions.length; ++i) {
2719 if (langExtensions[i] === ext) {
2720 langExtensions.splice(i, 1);
2721 }
2722 }
2723 for (var ii = 0; ii < outputModifiers.length; ++ii) {
2724 if (outputModifiers[ii] === ext) {
2725 outputModifiers.splice(ii, 1);
2726 }
2727 }
2728 }
2729 };
2730
2731 /**
2732 * Get all extension of THIS converter
2733 * @returns {{language: Array, output: Array}}
2734 */
2735 this.getAllExtensions = function () {
2736 return {
2737 language: langExtensions,
2738 output: outputModifiers
2739 };
2740 };
2741
2742 /**
2743 * Get the metadata of the previously parsed document
2744 * @param raw
2745 * @returns {string|{}}
2746 */
2747 this.getMetadata = function (raw) {
2748 if (raw) {
2749 return metadata.raw;
2750 } else {
2751 return metadata.parsed;
2752 }
2753 };
2754
2755 /**
2756 * Get the metadata format of the previously parsed document
2757 * @returns {string}
2758 */
2759 this.getMetadataFormat = function () {
2760 return metadata.format;
2761 };
2762
2763 /**
2764 * Private: set a single key, value metadata pair
2765 * @param {string} key
2766 * @param {string} value
2767 */
2768 this._setMetadataPair = function (key, value) {
2769 metadata.parsed[key] = value;
2770 };
2771
2772 /**
2773 * Private: set metadata format
2774 * @param {string} format
2775 */
2776 this._setMetadataFormat = function (format) {
2777 metadata.format = format;
2778 };
2779
2780 /**
2781 * Private: set metadata raw text
2782 * @param {string} raw
2783 */
2784 this._setMetadataRaw = function (raw) {
2785 metadata.raw = raw;
2786 };
2787};
2788
2789/**
2790 * Turn Markdown link shortcuts into XHTML <a> tags.
2791 */
2792showdown.subParser('anchors', function (text, options, globals) {
2793 'use strict';
2794
2795 text = globals.converter._dispatch('anchors.before', text, options, globals);
2796
2797 var writeAnchorTag = function (wholeMatch, linkText, linkId, url, m5, m6, title) {
2798 if (showdown.helper.isUndefined(title)) {
2799 title = '';
2800 }
2801 linkId = linkId.toLowerCase();
2802
2803 // Special case for explicit empty url
2804 if (wholeMatch.search(/\(<?\s*>? ?(['"].*['"])?\)$/m) > -1) {
2805 url = '';
2806 } else if (!url) {
2807 if (!linkId) {
2808 // lower-case and turn embedded newlines into spaces
2809 linkId = linkText.toLowerCase().replace(/ ?\n/g, ' ');
2810 }
2811 url = '#' + linkId;
2812
2813 if (!showdown.helper.isUndefined(globals.gUrls[linkId])) {
2814 url = globals.gUrls[linkId];
2815 if (!showdown.helper.isUndefined(globals.gTitles[linkId])) {
2816 title = globals.gTitles[linkId];
2817 }
2818 } else {
2819 return wholeMatch;
2820 }
2821 }
2822
2823 //url = showdown.helper.escapeCharacters(url, '*_', false); // replaced line to improve performance
2824 url = url.replace(showdown.helper.regexes.asteriskDashAndColon, showdown.helper.escapeCharactersCallback);
2825
2826 var result = '<a href="' + url + '"';
2827
2828 if (title !== '' && title !== null) {
2829 title = title.replace(/"/g, '&quot;');
2830 //title = showdown.helper.escapeCharacters(title, '*_', false); // replaced line to improve performance
2831 title = title.replace(showdown.helper.regexes.asteriskDashAndColon, showdown.helper.escapeCharactersCallback);
2832 result += ' title="' + title + '"';
2833 }
2834
2835 // optionLinksInNewWindow only applies
2836 // to external links. Hash links (#) open in same page
2837 if (options.openLinksInNewWindow && !/^#/.test(url)) {
2838 // escaped _
2839 result += ' rel="noopener noreferrer" target="¨E95Eblank"';
2840 }
2841
2842 result += '>' + linkText + '</a>';
2843
2844 return result;
2845 };
2846
2847 // First, handle reference-style links: [link text] [id]
2848 text = text.replace(/\[((?:\[[^\]]*]|[^\[\]])*)] ?(?:\n *)?\[(.*?)]()()()()/g, writeAnchorTag);
2849
2850 // Next, inline-style links: [link text](url "optional title")
2851 // cases with crazy urls like ./image/cat1).png
2852 text = text.replace(/\[((?:\[[^\]]*]|[^\[\]])*)]()[ \t]*\([ \t]?<([^>]*)>(?:[ \t]*((["'])([^"]*?)\5))?[ \t]?\)/g,
2853 writeAnchorTag);
2854
2855 // normal cases
2856 text = text.replace(/\[((?:\[[^\]]*]|[^\[\]])*)]()[ \t]*\([ \t]?<?([\S]+?(?:\([\S]*?\)[\S]*?)?)>?(?:[ \t]*((["'])([^"]*?)\5))?[ \t]?\)/g,
2857 writeAnchorTag);
2858
2859 // handle reference-style shortcuts: [link text]
2860 // These must come last in case you've also got [link test][1]
2861 // or [link test](/foo)
2862 text = text.replace(/\[([^\[\]]+)]()()()()()/g, writeAnchorTag);
2863
2864 // Lastly handle GithubMentions if option is enabled
2865 if (options.ghMentions) {
2866 text = text.replace(/(^|\s)(\\)?(@([a-z\d]+(?:[a-z\d.-]+?[a-z\d]+)*))/gmi, function (wm, st, escape, mentions, username) {
2867 if (escape === '\\') {
2868 return st + mentions;
2869 }
2870
2871 //check if options.ghMentionsLink is a string
2872 if (!showdown.helper.isString(options.ghMentionsLink)) {
2873 throw new Error('ghMentionsLink option must be a string');
2874 }
2875 var lnk = options.ghMentionsLink.replace(/\{u}/g, username),
2876 target = '';
2877 if (options.openLinksInNewWindow) {
2878 target = ' rel="noopener noreferrer" target="¨E95Eblank"';
2879 }
2880 return st + '<a href="' + lnk + '"' + target + '>' + mentions + '</a>';
2881 });
2882 }
2883
2884 text = globals.converter._dispatch('anchors.after', text, options, globals);
2885 return text;
2886});
2887
2888// url allowed chars [a-z\d_.~:/?#[]@!$&'()*+,;=-]
2889
2890var simpleURLRegex = /([*~_]+|\b)(((https?|ftp|dict):\/\/|www\.)[^'">\s]+?\.[^'">\s]+?)()(\1)?(?=\s|$)(?!["<>])/gi,
2891 simpleURLRegex2 = /([*~_]+|\b)(((https?|ftp|dict):\/\/|www\.)[^'">\s]+\.[^'">\s]+?)([.!?,()\[\]])?(\1)?(?=\s|$)(?!["<>])/gi,
2892 delimUrlRegex = /()<(((https?|ftp|dict):\/\/|www\.)[^'">\s]+)()>()/gi,
2893 simpleMailRegex = /(^|\s)(?:mailto:)?([A-Za-z0-9!#$%&'*+-/=?^_`{|}~.]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)(?=$|\s)/gmi,
2894 delimMailRegex = /<()(?:mailto:)?([-.\w]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi,
2895
2896 replaceLink = function (options) {
2897 'use strict';
2898 return function (wm, leadingMagicChars, link, m2, m3, trailingPunctuation, trailingMagicChars) {
2899 link = link.replace(showdown.helper.regexes.asteriskDashAndColon, showdown.helper.escapeCharactersCallback);
2900 var lnkTxt = link,
2901 append = '',
2902 target = '',
2903 lmc = leadingMagicChars || '',
2904 tmc = trailingMagicChars || '';
2905 if (/^www\./i.test(link)) {
2906 link = link.replace(/^www\./i, 'http://www.');
2907 }
2908 if (options.excludeTrailingPunctuationFromURLs && trailingPunctuation) {
2909 append = trailingPunctuation;
2910 }
2911 if (options.openLinksInNewWindow) {
2912 target = ' rel="noopener noreferrer" target="¨E95Eblank"';
2913 }
2914 return lmc + '<a href="' + link + '"' + target + '>' + lnkTxt + '</a>' + append + tmc;
2915 };
2916 },
2917
2918 replaceMail = function (options, globals) {
2919 'use strict';
2920 return function (wholeMatch, b, mail) {
2921 var href = 'mailto:';
2922 b = b || '';
2923 mail = showdown.subParser('unescapeSpecialChars')(mail, options, globals);
2924 if (options.encodeEmails) {
2925 href = showdown.helper.encodeEmailAddress(href + mail);
2926 mail = showdown.helper.encodeEmailAddress(mail);
2927 } else {
2928 href = href + mail;
2929 }
2930 return b + '<a href="' + href + '">' + mail + '</a>';
2931 };
2932 };
2933
2934showdown.subParser('autoLinks', function (text, options, globals) {
2935 'use strict';
2936
2937 text = globals.converter._dispatch('autoLinks.before', text, options, globals);
2938
2939 text = text.replace(delimUrlRegex, replaceLink(options));
2940 text = text.replace(delimMailRegex, replaceMail(options, globals));
2941
2942 text = globals.converter._dispatch('autoLinks.after', text, options, globals);
2943
2944 return text;
2945});
2946
2947showdown.subParser('simplifiedAutoLinks', function (text, options, globals) {
2948 'use strict';
2949
2950 if (!options.simplifiedAutoLink) {
2951 return text;
2952 }
2953
2954 text = globals.converter._dispatch('simplifiedAutoLinks.before', text, options, globals);
2955
2956 if (options.excludeTrailingPunctuationFromURLs) {
2957 text = text.replace(simpleURLRegex2, replaceLink(options));
2958 } else {
2959 text = text.replace(simpleURLRegex, replaceLink(options));
2960 }
2961 text = text.replace(simpleMailRegex, replaceMail(options, globals));
2962
2963 text = globals.converter._dispatch('simplifiedAutoLinks.after', text, options, globals);
2964
2965 return text;
2966});
2967
2968/**
2969 * These are all the transformations that form block-level
2970 * tags like paragraphs, headers, and list items.
2971 */
2972showdown.subParser('blockGamut', function (text, options, globals) {
2973 'use strict';
2974
2975 text = globals.converter._dispatch('blockGamut.before', text, options, globals);
2976
2977 // we parse blockquotes first so that we can have headings and hrs
2978 // inside blockquotes
2979 text = showdown.subParser('blockQuotes')(text, options, globals);
2980 text = showdown.subParser('headers')(text, options, globals);
2981
2982 // Do Horizontal Rules:
2983 text = showdown.subParser('horizontalRule')(text, options, globals);
2984
2985 text = showdown.subParser('lists')(text, options, globals);
2986 text = showdown.subParser('codeBlocks')(text, options, globals);
2987 text = showdown.subParser('tables')(text, options, globals);
2988
2989 // We already ran _HashHTMLBlocks() before, in Markdown(), but that
2990 // was to escape raw HTML in the original Markdown source. This time,
2991 // we're escaping the markup we've just created, so that we don't wrap
2992 // <p> tags around block-level tags.
2993 text = showdown.subParser('hashHTMLBlocks')(text, options, globals);
2994 text = showdown.subParser('paragraphs')(text, options, globals);
2995
2996 text = globals.converter._dispatch('blockGamut.after', text, options, globals);
2997
2998 return text;
2999});
3000
3001showdown.subParser('blockQuotes', function (text, options, globals) {
3002 'use strict';
3003
3004 text = globals.converter._dispatch('blockQuotes.before', text, options, globals);
3005
3006 // add a couple extra lines after the text and endtext mark
3007 text = text + '\n\n';
3008
3009 var rgx = /(^ {0,3}>[ \t]?.+\n(.+\n)*\n*)+/gm;
3010
3011 if (options.splitAdjacentBlockquotes) {
3012 rgx = /^ {0,3}>[\s\S]*?(?:\n\n)/gm;
3013 }
3014
3015 text = text.replace(rgx, function (bq) {
3016 // attacklab: hack around Konqueror 3.5.4 bug:
3017 // "----------bug".replace(/^-/g,"") == "bug"
3018 bq = bq.replace(/^[ \t]*>[ \t]?/gm, ''); // trim one level of quoting
3019
3020 // attacklab: clean up hack
3021 bq = bq.replace(/¨0/g, '');
3022
3023 bq = bq.replace(/^[ \t]+$/gm, ''); // trim whitespace-only lines
3024 bq = showdown.subParser('githubCodeBlocks')(bq, options, globals);
3025 bq = showdown.subParser('blockGamut')(bq, options, globals); // recurse
3026
3027 bq = bq.replace(/(^|\n)/g, '$1 ');
3028 // These leading spaces screw with <pre> content, so we need to fix that:
3029 bq = bq.replace(/(\s*<pre>[^\r]+?<\/pre>)/gm, function (wholeMatch, m1) {
3030 var pre = m1;
3031 // attacklab: hack around Konqueror 3.5.4 bug:
3032 pre = pre.replace(/^ /mg, '¨0');
3033 pre = pre.replace(/¨0/g, '');
3034 return pre;
3035 });
3036
3037 return showdown.subParser('hashBlock')('<blockquote>\n' + bq + '\n</blockquote>', options, globals);
3038 });
3039
3040 text = globals.converter._dispatch('blockQuotes.after', text, options, globals);
3041 return text;
3042});
3043
3044/**
3045 * Process Markdown `<pre><code>` blocks.
3046 */
3047showdown.subParser('codeBlocks', function (text, options, globals) {
3048 'use strict';
3049
3050 text = globals.converter._dispatch('codeBlocks.before', text, options, globals);
3051
3052 // sentinel workarounds for lack of \A and \Z, safari\khtml bug
3053 text += '¨0';
3054
3055 var pattern = /(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=¨0))/g;
3056 text = text.replace(pattern, function (wholeMatch, m1, m2) {
3057 var codeblock = m1,
3058 nextChar = m2,
3059 end = '\n';
3060
3061 codeblock = showdown.subParser('outdent')(codeblock, options, globals);
3062 codeblock = showdown.subParser('encodeCode')(codeblock, options, globals);
3063 codeblock = showdown.subParser('detab')(codeblock, options, globals);
3064 codeblock = codeblock.replace(/^\n+/g, ''); // trim leading newlines
3065 codeblock = codeblock.replace(/\n+$/g, ''); // trim trailing newlines
3066
3067 if (options.omitExtraWLInCodeBlocks) {
3068 end = '';
3069 }
3070
3071 codeblock = '<pre><code>' + codeblock + end + '</code></pre>';
3072
3073 return showdown.subParser('hashBlock')(codeblock, options, globals) + nextChar;
3074 });
3075
3076 // strip sentinel
3077 text = text.replace(/¨0/, '');
3078
3079 text = globals.converter._dispatch('codeBlocks.after', text, options, globals);
3080 return text;
3081});
3082
3083/**
3084 *
3085 * * Backtick quotes are used for <code></code> spans.
3086 *
3087 * * You can use multiple backticks as the delimiters if you want to
3088 * include literal backticks in the code span. So, this input:
3089 *
3090 * Just type ``foo `bar` baz`` at the prompt.
3091 *
3092 * Will translate to:
3093 *
3094 * <p>Just type <code>foo `bar` baz</code> at the prompt.</p>
3095 *
3096 * There's no arbitrary limit to the number of backticks you
3097 * can use as delimters. If you need three consecutive backticks
3098 * in your code, use four for delimiters, etc.
3099 *
3100 * * You can use spaces to get literal backticks at the edges:
3101 *
3102 * ... type `` `bar` `` ...
3103 *
3104 * Turns to:
3105 *
3106 * ... type <code>`bar`</code> ...
3107 */
3108showdown.subParser('codeSpans', function (text, options, globals) {
3109 'use strict';
3110
3111 text = globals.converter._dispatch('codeSpans.before', text, options, globals);
3112
3113 if (typeof (text) === 'undefined') {
3114 text = '';
3115 }
3116 text = text.replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm,
3117 function (wholeMatch, m1, m2, m3) {
3118 var c = m3;
3119 c = c.replace(/^([ \t]*)/g, ''); // leading whitespace
3120 c = c.replace(/[ \t]*$/g, ''); // trailing whitespace
3121 c = showdown.subParser('encodeCode')(c, options, globals);
3122 c = m1 + '<code>' + c + '</code>';
3123 c = showdown.subParser('hashHTMLSpans')(c, options, globals);
3124 return c;
3125 }
3126 );
3127
3128 text = globals.converter._dispatch('codeSpans.after', text, options, globals);
3129 return text;
3130});
3131
3132/**
3133 * Create a full HTML document from the processed markdown
3134 */
3135showdown.subParser('completeHTMLDocument', function (text, options, globals) {
3136 'use strict';
3137
3138 if (!options.completeHTMLDocument) {
3139 return text;
3140 }
3141
3142 text = globals.converter._dispatch('completeHTMLDocument.before', text, options, globals);
3143
3144 var doctype = 'html',
3145 doctypeParsed = '<!DOCTYPE HTML>\n',
3146 title = '',
3147 charset = '<meta charset="utf-8">\n',
3148 lang = '',
3149 metadata = '';
3150
3151 if (typeof globals.metadata.parsed.doctype !== 'undefined') {
3152 doctypeParsed = '<!DOCTYPE ' + globals.metadata.parsed.doctype + '>\n';
3153 doctype = globals.metadata.parsed.doctype.toString().toLowerCase();
3154 if (doctype === 'html' || doctype === 'html5') {
3155 charset = '<meta charset="utf-8">';
3156 }
3157 }
3158
3159 for (var meta in globals.metadata.parsed) {
3160 if (globals.metadata.parsed.hasOwnProperty(meta)) {
3161 switch (meta.toLowerCase()) {
3162 case 'doctype':
3163 break;
3164
3165 case 'title':
3166 title = '<title>' + globals.metadata.parsed.title + '</title>\n';
3167 break;
3168
3169 case 'charset':
3170 if (doctype === 'html' || doctype === 'html5') {
3171 charset = '<meta charset="' + globals.metadata.parsed.charset + '">\n';
3172 } else {
3173 charset = '<meta name="charset" content="' + globals.metadata.parsed.charset + '">\n';
3174 }
3175 break;
3176
3177 case 'language':
3178 case 'lang':
3179 lang = ' lang="' + globals.metadata.parsed[meta] + '"';
3180 metadata += '<meta name="' + meta + '" content="' + globals.metadata.parsed[meta] + '">\n';
3181 break;
3182
3183 default:
3184 metadata += '<meta name="' + meta + '" content="' + globals.metadata.parsed[meta] + '">\n';
3185 }
3186 }
3187 }
3188
3189 text = doctypeParsed + '<html' + lang + '>\n<head>\n' + title + charset + metadata + '</head>\n<body>\n' + text.trim() + '\n</body>\n</html>';
3190
3191 text = globals.converter._dispatch('completeHTMLDocument.after', text, options, globals);
3192 return text;
3193});
3194
3195/**
3196 * Convert all tabs to spaces
3197 */
3198showdown.subParser('detab', function (text, options, globals) {
3199 'use strict';
3200 text = globals.converter._dispatch('detab.before', text, options, globals);
3201
3202 // expand first n-1 tabs
3203 text = text.replace(/\t(?=\t)/g, ' '); // g_tab_width
3204
3205 // replace the nth with two sentinels
3206 text = text.replace(/\t/g, '¨A¨B');
3207
3208 // use the sentinel to anchor our regex so it doesn't explode
3209 text = text.replace(/¨B(.+?)¨A/g, function (wholeMatch, m1) {
3210 var leadingText = m1,
3211 numSpaces = 4 - leadingText.length % 4; // g_tab_width
3212
3213 // there *must* be a better way to do this:
3214 for (var i = 0; i < numSpaces; i++) {
3215 leadingText += ' ';
3216 }
3217
3218 return leadingText;
3219 });
3220
3221 // clean up sentinels
3222 text = text.replace(/¨A/g, ' '); // g_tab_width
3223 text = text.replace(/¨B/g, '');
3224
3225 text = globals.converter._dispatch('detab.after', text, options, globals);
3226 return text;
3227});
3228
3229showdown.subParser('ellipsis', function (text, options, globals) {
3230 'use strict';
3231
3232 if (!options.ellipsis) {
3233 return text;
3234 }
3235
3236 text = globals.converter._dispatch('ellipsis.before', text, options, globals);
3237
3238 text = text.replace(/\.\.\./g, '…');
3239
3240 text = globals.converter._dispatch('ellipsis.after', text, options, globals);
3241
3242 return text;
3243});
3244
3245/**
3246 * Turn emoji codes into emojis
3247 *
3248 * List of supported emojis: https://github.com/showdownjs/showdown/wiki/Emojis
3249 */
3250showdown.subParser('emoji', function (text, options, globals) {
3251 'use strict';
3252
3253 if (!options.emoji) {
3254 return text;
3255 }
3256
3257 text = globals.converter._dispatch('emoji.before', text, options, globals);
3258
3259 var emojiRgx = /:([\S]+?):/g;
3260
3261 text = text.replace(emojiRgx, function (wm, emojiCode) {
3262 if (showdown.helper.emojis.hasOwnProperty(emojiCode)) {
3263 return showdown.helper.emojis[emojiCode];
3264 }
3265 return wm;
3266 });
3267
3268 text = globals.converter._dispatch('emoji.after', text, options, globals);
3269
3270 return text;
3271});
3272
3273/**
3274 * Smart processing for ampersands and angle brackets that need to be encoded.
3275 */
3276showdown.subParser('encodeAmpsAndAngles', function (text, options, globals) {
3277 'use strict';
3278 text = globals.converter._dispatch('encodeAmpsAndAngles.before', text, options, globals);
3279
3280 // Ampersand-encoding based entirely on Nat Irons's Amputator MT plugin:
3281 // http://bumppo.net/projects/amputator/
3282 text = text.replace(/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/g, '&amp;');
3283
3284 // Encode naked <'s
3285 text = text.replace(/<(?![a-z\/?$!])/gi, '&lt;');
3286
3287 // Encode <
3288 text = text.replace(/</g, '&lt;');
3289
3290 // Encode >
3291 text = text.replace(/>/g, '&gt;');
3292
3293 text = globals.converter._dispatch('encodeAmpsAndAngles.after', text, options, globals);
3294 return text;
3295});
3296
3297/**
3298 * Returns the string, with after processing the following backslash escape sequences.
3299 *
3300 * attacklab: The polite way to do this is with the new escapeCharacters() function:
3301 *
3302 * text = escapeCharacters(text,"\\",true);
3303 * text = escapeCharacters(text,"`*_{}[]()>#+-.!",true);
3304 *
3305 * ...but we're sidestepping its use of the (slow) RegExp constructor
3306 * as an optimization for Firefox. This function gets called a LOT.
3307 */
3308showdown.subParser('encodeBackslashEscapes', function (text, options, globals) {
3309 'use strict';
3310 text = globals.converter._dispatch('encodeBackslashEscapes.before', text, options, globals);
3311
3312 text = text.replace(/\\(\\)/g, showdown.helper.escapeCharactersCallback);
3313 text = text.replace(/\\([`*_{}\[\]()>#+.!~=|:-])/g, showdown.helper.escapeCharactersCallback);
3314
3315 text = globals.converter._dispatch('encodeBackslashEscapes.after', text, options, globals);
3316 return text;
3317});
3318
3319/**
3320 * Encode/escape certain characters inside Markdown code runs.
3321 * The point is that in code, these characters are literals,
3322 * and lose their special Markdown meanings.
3323 */
3324showdown.subParser('encodeCode', function (text, options, globals) {
3325 'use strict';
3326
3327 text = globals.converter._dispatch('encodeCode.before', text, options, globals);
3328
3329 // Encode all ampersands; HTML entities are not
3330 // entities within a Markdown code span.
3331 text = text
3332 .replace(/&/g, '&amp;')
3333 // Do the angle bracket song and dance:
3334 .replace(/</g, '&lt;')
3335 .replace(/>/g, '&gt;')
3336 // Now, escape characters that are magic in Markdown:
3337 .replace(/([*_{}\[\]\\=~-])/g, showdown.helper.escapeCharactersCallback);
3338
3339 text = globals.converter._dispatch('encodeCode.after', text, options, globals);
3340 return text;
3341});
3342
3343/**
3344 * Within tags -- meaning between < and > -- encode [\ ` * _ ~ =] so they
3345 * don't conflict with their use in Markdown for code, italics and strong.
3346 */
3347showdown.subParser('escapeSpecialCharsWithinTagAttributes', function (text, options, globals) {
3348 'use strict';
3349 text = globals.converter._dispatch('escapeSpecialCharsWithinTagAttributes.before', text, options, globals);
3350
3351 // Build a regex to find HTML tags.
3352 var tags = /<\/?[a-z\d_:-]+(?:[\s]+[\s\S]+?)?>/gi,
3353 comments = /<!(--(?:(?:[^>-]|-[^>])(?:[^-]|-[^-])*)--)>/gi;
3354
3355 text = text.replace(tags, function (wholeMatch) {
3356 return wholeMatch
3357 .replace(/(.)<\/?code>(?=.)/g, '$1`')
3358 .replace(/([\\`*_~=|])/g, showdown.helper.escapeCharactersCallback);
3359 });
3360
3361 text = text.replace(comments, function (wholeMatch) {
3362 return wholeMatch
3363 .replace(/([\\`*_~=|])/g, showdown.helper.escapeCharactersCallback);
3364 });
3365
3366 text = globals.converter._dispatch('escapeSpecialCharsWithinTagAttributes.after', text, options, globals);
3367 return text;
3368});
3369
3370/**
3371 * Handle github codeblocks prior to running HashHTML so that
3372 * HTML contained within the codeblock gets escaped properly
3373 * Example:
3374 * ```ruby
3375 * def hello_world(x)
3376 * puts "Hello, #{x}"
3377 * end
3378 * ```
3379 */
3380showdown.subParser('githubCodeBlocks', function (text, options, globals) {
3381 'use strict';
3382
3383 // early exit if option is not enabled
3384 if (!options.ghCodeBlocks) {
3385 return text;
3386 }
3387
3388 text = globals.converter._dispatch('githubCodeBlocks.before', text, options, globals);
3389
3390 text += '¨0';
3391
3392 text = text.replace(/(?:^|\n)(?: {0,3})(```+|~~~+)(?: *)([^\s`~]*)\n([\s\S]*?)\n(?: {0,3})\1/g, function (wholeMatch, delim, language, codeblock) {
3393 var end = (options.omitExtraWLInCodeBlocks) ? '' : '\n';
3394
3395 // First parse the github code block
3396 codeblock = showdown.subParser('encodeCode')(codeblock, options, globals);
3397 codeblock = showdown.subParser('detab')(codeblock, options, globals);
3398 codeblock = codeblock.replace(/^\n+/g, ''); // trim leading newlines
3399 codeblock = codeblock.replace(/\n+$/g, ''); // trim trailing whitespace
3400
3401 codeblock = '<pre><code' + (language ? ' class="' + language + ' language-' + language + '"' : '') + '>' + codeblock + end + '</code></pre>';
3402
3403 codeblock = showdown.subParser('hashBlock')(codeblock, options, globals);
3404
3405 // Since GHCodeblocks can be false positives, we need to
3406 // store the primitive text and the parsed text in a global var,
3407 // and then return a token
3408 return '\n\n¨G' + (globals.ghCodeBlocks.push({text: wholeMatch, codeblock: codeblock}) - 1) + 'G\n\n';
3409 });
3410
3411 // attacklab: strip sentinel
3412 text = text.replace(/¨0/, '');
3413
3414 return globals.converter._dispatch('githubCodeBlocks.after', text, options, globals);
3415});
3416
3417showdown.subParser('hashBlock', function (text, options, globals) {
3418 'use strict';
3419 text = globals.converter._dispatch('hashBlock.before', text, options, globals);
3420 text = text.replace(/(^\n+|\n+$)/g, '');
3421 text = '\n\n¨K' + (globals.gHtmlBlocks.push(text) - 1) + 'K\n\n';
3422 text = globals.converter._dispatch('hashBlock.after', text, options, globals);
3423 return text;
3424});
3425
3426/**
3427 * Hash and escape <code> elements that should not be parsed as markdown
3428 */
3429showdown.subParser('hashCodeTags', function (text, options, globals) {
3430 'use strict';
3431 text = globals.converter._dispatch('hashCodeTags.before', text, options, globals);
3432
3433 var repFunc = function (wholeMatch, match, left, right) {
3434 var codeblock = left + showdown.subParser('encodeCode')(match, options, globals) + right;
3435 return '¨C' + (globals.gHtmlSpans.push(codeblock) - 1) + 'C';
3436 };
3437
3438 // Hash naked <code>
3439 text = showdown.helper.replaceRecursiveRegExp(text, repFunc, '<code\\b[^>]*>', '</code>', 'gim');
3440
3441 text = globals.converter._dispatch('hashCodeTags.after', text, options, globals);
3442 return text;
3443});
3444
3445showdown.subParser('hashElement', function (text, options, globals) {
3446 'use strict';
3447
3448 return function (wholeMatch, m1) {
3449 var blockText = m1;
3450
3451 // Undo double lines
3452 blockText = blockText.replace(/\n\n/g, '\n');
3453 blockText = blockText.replace(/^\n/, '');
3454
3455 // strip trailing blank lines
3456 blockText = blockText.replace(/\n+$/g, '');
3457
3458 // Replace the element text with a marker ("¨KxK" where x is its key)
3459 blockText = '\n\n¨K' + (globals.gHtmlBlocks.push(blockText) - 1) + 'K\n\n';
3460
3461 return blockText;
3462 };
3463});
3464
3465showdown.subParser('hashHTMLBlocks', function (text, options, globals) {
3466 'use strict';
3467 text = globals.converter._dispatch('hashHTMLBlocks.before', text, options, globals);
3468
3469 var blockTags = [
3470 'pre',
3471 'div',
3472 'h1',
3473 'h2',
3474 'h3',
3475 'h4',
3476 'h5',
3477 'h6',
3478 'blockquote',
3479 'table',
3480 'dl',
3481 'ol',
3482 'ul',
3483 'script',
3484 'noscript',
3485 'form',
3486 'fieldset',
3487 'iframe',
3488 'math',
3489 'style',
3490 'section',
3491 'header',
3492 'footer',
3493 'nav',
3494 'article',
3495 'aside',
3496 'address',
3497 'audio',
3498 'canvas',
3499 'figure',
3500 'hgroup',
3501 'output',
3502 'video',
3503 'p'
3504 ],
3505 repFunc = function (wholeMatch, match, left, right) {
3506 var txt = wholeMatch;
3507 // check if this html element is marked as markdown
3508 // if so, it's contents should be parsed as markdown
3509 if (left.search(/\bmarkdown\b/) !== -1) {
3510 txt = left + globals.converter.makeHtml(match) + right;
3511 }
3512 return '\n\n¨K' + (globals.gHtmlBlocks.push(txt) - 1) + 'K\n\n';
3513 };
3514
3515 if (options.backslashEscapesHTMLTags) {
3516 // encode backslash escaped HTML tags
3517 text = text.replace(/\\<(\/?[^>]+?)>/g, function (wm, inside) {
3518 return '&lt;' + inside + '&gt;';
3519 });
3520 }
3521
3522 // hash HTML Blocks
3523 for (var i = 0; i < blockTags.length; ++i) {
3524
3525 var opTagPos,
3526 rgx1 = new RegExp('^ {0,3}(<' + blockTags[i] + '\\b[^>]*>)', 'im'),
3527 patLeft = '<' + blockTags[i] + '\\b[^>]*>',
3528 patRight = '</' + blockTags[i] + '>';
3529 // 1. Look for the first position of the first opening HTML tag in the text
3530 while ((opTagPos = showdown.helper.regexIndexOf(text, rgx1)) !== -1) {
3531
3532 // if the HTML tag is \ escaped, we need to escape it and break
3533
3534
3535 //2. Split the text in that position
3536 var subTexts = showdown.helper.splitAtIndex(text, opTagPos),
3537 //3. Match recursively
3538 newSubText1 = showdown.helper.replaceRecursiveRegExp(subTexts[1], repFunc, patLeft, patRight, 'im');
3539
3540 // prevent an infinite loop
3541 if (newSubText1 === subTexts[1]) {
3542 break;
3543 }
3544 text = subTexts[0].concat(newSubText1);
3545 }
3546 }
3547 // HR SPECIAL CASE
3548 text = text.replace(/(\n {0,3}(<(hr)\b([^<>])*?\/?>)[ \t]*(?=\n{2,}))/g,
3549 showdown.subParser('hashElement')(text, options, globals));
3550
3551 // Special case for standalone HTML comments
3552 text = showdown.helper.replaceRecursiveRegExp(text, function (txt) {
3553 return '\n\n¨K' + (globals.gHtmlBlocks.push(txt) - 1) + 'K\n\n';
3554 }, '^ {0,3}<!--', '-->', 'gm');
3555
3556 // PHP and ASP-style processor instructions (<?...?> and <%...%>)
3557 text = text.replace(/(?:\n\n)( {0,3}(?:<([?%])[^\r]*?\2>)[ \t]*(?=\n{2,}))/g,
3558 showdown.subParser('hashElement')(text, options, globals));
3559
3560 text = globals.converter._dispatch('hashHTMLBlocks.after', text, options, globals);
3561 return text;
3562});
3563
3564/**
3565 * Hash span elements that should not be parsed as markdown
3566 */
3567showdown.subParser('hashHTMLSpans', function (text, options, globals) {
3568 'use strict';
3569 text = globals.converter._dispatch('hashHTMLSpans.before', text, options, globals);
3570
3571 function hashHTMLSpan (html) {
3572 return '¨C' + (globals.gHtmlSpans.push(html) - 1) + 'C';
3573 }
3574
3575 // Hash Self Closing tags
3576 text = text.replace(/<[^>]+?\/>/gi, function (wm) {
3577 return hashHTMLSpan(wm);
3578 });
3579
3580 // Hash tags without properties
3581 text = text.replace(/<([^>]+?)>[\s\S]*?<\/\1>/g, function (wm) {
3582 return hashHTMLSpan(wm);
3583 });
3584
3585 // Hash tags with properties
3586 text = text.replace(/<([^>]+?)\s[^>]+?>[\s\S]*?<\/\1>/g, function (wm) {
3587 return hashHTMLSpan(wm);
3588 });
3589
3590 // Hash self closing tags without />
3591 text = text.replace(/<[^>]+?>/gi, function (wm) {
3592 return hashHTMLSpan(wm);
3593 });
3594
3595 /*showdown.helper.matchRecursiveRegExp(text, '<code\\b[^>]*>', '</code>', 'gi');*/
3596
3597 text = globals.converter._dispatch('hashHTMLSpans.after', text, options, globals);
3598 return text;
3599});
3600
3601/**
3602 * Unhash HTML spans
3603 */
3604showdown.subParser('unhashHTMLSpans', function (text, options, globals) {
3605 'use strict';
3606 text = globals.converter._dispatch('unhashHTMLSpans.before', text, options, globals);
3607
3608 for (var i = 0; i < globals.gHtmlSpans.length; ++i) {
3609 var repText = globals.gHtmlSpans[i],
3610 // limiter to prevent infinite loop (assume 10 as limit for recurse)
3611 limit = 0;
3612
3613 while (/¨C(\d+)C/.test(repText)) {
3614 var num = RegExp.$1;
3615 repText = repText.replace('¨C' + num + 'C', globals.gHtmlSpans[num]);
3616 if (limit === 10) {
3617 console.error('maximum nesting of 10 spans reached!!!');
3618 break;
3619 }
3620 ++limit;
3621 }
3622 text = text.replace('¨C' + i + 'C', repText);
3623 }
3624
3625 text = globals.converter._dispatch('unhashHTMLSpans.after', text, options, globals);
3626 return text;
3627});
3628
3629/**
3630 * Hash and escape <pre><code> elements that should not be parsed as markdown
3631 */
3632showdown.subParser('hashPreCodeTags', function (text, options, globals) {
3633 'use strict';
3634 text = globals.converter._dispatch('hashPreCodeTags.before', text, options, globals);
3635
3636 var repFunc = function (wholeMatch, match, left, right) {
3637 // encode html entities
3638 var codeblock = left + showdown.subParser('encodeCode')(match, options, globals) + right;
3639 return '\n\n¨G' + (globals.ghCodeBlocks.push({text: wholeMatch, codeblock: codeblock}) - 1) + 'G\n\n';
3640 };
3641
3642 // Hash <pre><code>
3643 text = showdown.helper.replaceRecursiveRegExp(text, repFunc, '^ {0,3}<pre\\b[^>]*>\\s*<code\\b[^>]*>', '^ {0,3}</code>\\s*</pre>', 'gim');
3644
3645 text = globals.converter._dispatch('hashPreCodeTags.after', text, options, globals);
3646 return text;
3647});
3648
3649showdown.subParser('headers', function (text, options, globals) {
3650 'use strict';
3651
3652 text = globals.converter._dispatch('headers.before', text, options, globals);
3653
3654 var headerLevelStart = (isNaN(parseInt(options.headerLevelStart))) ? 1 : parseInt(options.headerLevelStart),
3655
3656 // Set text-style headers:
3657 // Header 1
3658 // ========
3659 //
3660 // Header 2
3661 // --------
3662 //
3663 setextRegexH1 = (options.smoothLivePreview) ? /^(.+)[ \t]*\n={2,}[ \t]*\n+/gm : /^(.+)[ \t]*\n=+[ \t]*\n+/gm,
3664 setextRegexH2 = (options.smoothLivePreview) ? /^(.+)[ \t]*\n-{2,}[ \t]*\n+/gm : /^(.+)[ \t]*\n-+[ \t]*\n+/gm;
3665
3666 text = text.replace(setextRegexH1, function (wholeMatch, m1) {
3667
3668 var spanGamut = showdown.subParser('spanGamut')(m1, options, globals),
3669 hID = (options.noHeaderId) ? '' : ' id="' + headerId(m1) + '"',
3670 hLevel = headerLevelStart,
3671 hashBlock = '<h' + hLevel + hID + '>' + spanGamut + '</h' + hLevel + '>';
3672 return showdown.subParser('hashBlock')(hashBlock, options, globals);
3673 });
3674
3675 text = text.replace(setextRegexH2, function (matchFound, m1) {
3676 var spanGamut = showdown.subParser('spanGamut')(m1, options, globals),
3677 hID = (options.noHeaderId) ? '' : ' id="' + headerId(m1) + '"',
3678 hLevel = headerLevelStart + 1,
3679 hashBlock = '<h' + hLevel + hID + '>' + spanGamut + '</h' + hLevel + '>';
3680 return showdown.subParser('hashBlock')(hashBlock, options, globals);
3681 });
3682
3683 // atx-style headers:
3684 // # Header 1
3685 // ## Header 2
3686 // ## Header 2 with closing hashes ##
3687 // ...
3688 // ###### Header 6
3689 //
3690 var atxStyle = (options.requireSpaceBeforeHeadingText) ? /^(#{1,6})[ \t]+(.+?)[ \t]*#*\n+/gm : /^(#{1,6})[ \t]*(.+?)[ \t]*#*\n+/gm;
3691
3692 text = text.replace(atxStyle, function (wholeMatch, m1, m2) {
3693 var hText = m2;
3694 if (options.customizedHeaderId) {
3695 hText = m2.replace(/\s?\{([^{]+?)}\s*$/, '');
3696 }
3697
3698 var span = showdown.subParser('spanGamut')(hText, options, globals),
3699 hID = (options.noHeaderId) ? '' : ' id="' + headerId(m2) + '"',
3700 hLevel = headerLevelStart - 1 + m1.length,
3701 header = '<h' + hLevel + hID + '>' + span + '</h' + hLevel + '>';
3702
3703 return showdown.subParser('hashBlock')(header, options, globals);
3704 });
3705
3706 function headerId (m) {
3707 var title,
3708 prefix;
3709
3710 // It is separate from other options to allow combining prefix and customized
3711 if (options.customizedHeaderId) {
3712 var match = m.match(/\{([^{]+?)}\s*$/);
3713 if (match && match[1]) {
3714 m = match[1];
3715 }
3716 }
3717
3718 title = m;
3719
3720 // Prefix id to prevent causing inadvertent pre-existing style matches.
3721 if (showdown.helper.isString(options.prefixHeaderId)) {
3722 prefix = options.prefixHeaderId;
3723 } else if (options.prefixHeaderId === true) {
3724 prefix = 'section-';
3725 } else {
3726 prefix = '';
3727 }
3728
3729 if (!options.rawPrefixHeaderId) {
3730 title = prefix + title;
3731 }
3732
3733 if (options.ghCompatibleHeaderId) {
3734 title = title
3735 .replace(/ /g, '-')
3736 // replace previously escaped chars (&, ¨ and $)
3737 .replace(/&amp;/g, '')
3738 .replace(/¨T/g, '')
3739 .replace(/¨D/g, '')
3740 // replace rest of the chars (&~$ are repeated as they might have been escaped)
3741 // borrowed from github's redcarpet (some they should produce similar results)
3742 .replace(/[&+$,\/:;=?@"#{}|^¨~\[\]`\\*)(%.!'<>]/g, '')
3743 .toLowerCase();
3744 } else if (options.rawHeaderId) {
3745 title = title
3746 .replace(/ /g, '-')
3747 // replace previously escaped chars (&, ¨ and $)
3748 .replace(/&amp;/g, '&')
3749 .replace(/¨T/g, '¨')
3750 .replace(/¨D/g, '$')
3751 // replace " and '
3752 .replace(/["']/g, '-')
3753 .toLowerCase();
3754 } else {
3755 title = title
3756 .replace(/[^\w]/g, '')
3757 .toLowerCase();
3758 }
3759
3760 if (options.rawPrefixHeaderId) {
3761 title = prefix + title;
3762 }
3763
3764 if (globals.hashLinkCounts[title]) {
3765 title = title + '-' + (globals.hashLinkCounts[title]++);
3766 } else {
3767 globals.hashLinkCounts[title] = 1;
3768 }
3769 return title;
3770 }
3771
3772 text = globals.converter._dispatch('headers.after', text, options, globals);
3773 return text;
3774});
3775
3776/**
3777 * Turn Markdown link shortcuts into XHTML <a> tags.
3778 */
3779showdown.subParser('horizontalRule', function (text, options, globals) {
3780 'use strict';
3781 text = globals.converter._dispatch('horizontalRule.before', text, options, globals);
3782
3783 var key = showdown.subParser('hashBlock')('<hr />', options, globals);
3784 text = text.replace(/^ {0,2}( ?-){3,}[ \t]*$/gm, key);
3785 text = text.replace(/^ {0,2}( ?\*){3,}[ \t]*$/gm, key);
3786 text = text.replace(/^ {0,2}( ?_){3,}[ \t]*$/gm, key);
3787
3788 text = globals.converter._dispatch('horizontalRule.after', text, options, globals);
3789 return text;
3790});
3791
3792/**
3793 * Turn Markdown image shortcuts into <img> tags.
3794 */
3795showdown.subParser('images', function (text, options, globals) {
3796 'use strict';
3797
3798 text = globals.converter._dispatch('images.before', text, options, globals);
3799
3800 var inlineRegExp = /!\[([^\]]*?)][ \t]*()\([ \t]?<?([\S]+?(?:\([\S]*?\)[\S]*?)?)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*(?:(["'])([^"]*?)\6)?[ \t]?\)/g,
3801 crazyRegExp = /!\[([^\]]*?)][ \t]*()\([ \t]?<([^>]*)>(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*(?:(?:(["'])([^"]*?)\6))?[ \t]?\)/g,
3802 base64RegExp = /!\[([^\]]*?)][ \t]*()\([ \t]?<?(data:.+?\/.+?;base64,[A-Za-z0-9+/=\n]+?)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*(?:(["'])([^"]*?)\6)?[ \t]?\)/g,
3803 referenceRegExp = /!\[([^\]]*?)] ?(?:\n *)?\[([\s\S]*?)]()()()()()/g,
3804 refShortcutRegExp = /!\[([^\[\]]+)]()()()()()/g;
3805
3806 function writeImageTagBase64 (wholeMatch, altText, linkId, url, width, height, m5, title) {
3807 url = url.replace(/\s/g, '');
3808 return writeImageTag (wholeMatch, altText, linkId, url, width, height, m5, title);
3809 }
3810
3811 function writeImageTag (wholeMatch, altText, linkId, url, width, height, m5, title) {
3812
3813 var gUrls = globals.gUrls,
3814 gTitles = globals.gTitles,
3815 gDims = globals.gDimensions;
3816
3817 linkId = linkId.toLowerCase();
3818
3819 if (!title) {
3820 title = '';
3821 }
3822 // Special case for explicit empty url
3823 if (wholeMatch.search(/\(<?\s*>? ?(['"].*['"])?\)$/m) > -1) {
3824 url = '';
3825
3826 } else if (url === '' || url === null) {
3827 if (linkId === '' || linkId === null) {
3828 // lower-case and turn embedded newlines into spaces
3829 linkId = altText.toLowerCase().replace(/ ?\n/g, ' ');
3830 }
3831 url = '#' + linkId;
3832
3833 if (!showdown.helper.isUndefined(gUrls[linkId])) {
3834 url = gUrls[linkId];
3835 if (!showdown.helper.isUndefined(gTitles[linkId])) {
3836 title = gTitles[linkId];
3837 }
3838 if (!showdown.helper.isUndefined(gDims[linkId])) {
3839 width = gDims[linkId].width;
3840 height = gDims[linkId].height;
3841 }
3842 } else {
3843 return wholeMatch;
3844 }
3845 }
3846
3847 altText = altText
3848 .replace(/"/g, '&quot;')
3849 //altText = showdown.helper.escapeCharacters(altText, '*_', false);
3850 .replace(showdown.helper.regexes.asteriskDashAndColon, showdown.helper.escapeCharactersCallback);
3851 //url = showdown.helper.escapeCharacters(url, '*_', false);
3852 url = url.replace(showdown.helper.regexes.asteriskDashAndColon, showdown.helper.escapeCharactersCallback);
3853 var result = '<img src="' + url + '" alt="' + altText + '"';
3854
3855 if (title && showdown.helper.isString(title)) {
3856 title = title
3857 .replace(/"/g, '&quot;')
3858 //title = showdown.helper.escapeCharacters(title, '*_', false);
3859 .replace(showdown.helper.regexes.asteriskDashAndColon, showdown.helper.escapeCharactersCallback);
3860 result += ' title="' + title + '"';
3861 }
3862
3863 if (width && height) {
3864 width = (width === '*') ? 'auto' : width;
3865 height = (height === '*') ? 'auto' : height;
3866
3867 result += ' width="' + width + '"';
3868 result += ' height="' + height + '"';
3869 }
3870
3871 result += ' />';
3872
3873 return result;
3874 }
3875
3876 // First, handle reference-style labeled images: ![alt text][id]
3877 text = text.replace(referenceRegExp, writeImageTag);
3878
3879 // Next, handle inline images: ![alt text](url =<width>x<height> "optional title")
3880
3881 // base64 encoded images
3882 text = text.replace(base64RegExp, writeImageTagBase64);
3883
3884 // cases with crazy urls like ./image/cat1).png
3885 text = text.replace(crazyRegExp, writeImageTag);
3886
3887 // normal cases
3888 text = text.replace(inlineRegExp, writeImageTag);
3889
3890 // handle reference-style shortcuts: ![img text]
3891 text = text.replace(refShortcutRegExp, writeImageTag);
3892
3893 text = globals.converter._dispatch('images.after', text, options, globals);
3894 return text;
3895});
3896
3897showdown.subParser('italicsAndBold', function (text, options, globals) {
3898 'use strict';
3899
3900 text = globals.converter._dispatch('italicsAndBold.before', text, options, globals);
3901
3902 // it's faster to have 3 separate regexes for each case than have just one
3903 // because of backtracing, in some cases, it could lead to an exponential effect
3904 // called "catastrophic backtrace". Ominous!
3905
3906 function parseInside (txt, left, right) {
3907 /*
3908 if (options.simplifiedAutoLink) {
3909 txt = showdown.subParser('simplifiedAutoLinks')(txt, options, globals);
3910 }
3911 */
3912 return left + txt + right;
3913 }
3914
3915 // Parse underscores
3916 if (options.literalMidWordUnderscores) {
3917 text = text.replace(/\b___(\S[\s\S]*?)___\b/g, function (wm, txt) {
3918 return parseInside (txt, '<strong><em>', '</em></strong>');
3919 });
3920 text = text.replace(/\b__(\S[\s\S]*?)__\b/g, function (wm, txt) {
3921 return parseInside (txt, '<strong>', '</strong>');
3922 });
3923 text = text.replace(/\b_(\S[\s\S]*?)_\b/g, function (wm, txt) {
3924 return parseInside (txt, '<em>', '</em>');
3925 });
3926 } else {
3927 text = text.replace(/___(\S[\s\S]*?)___/g, function (wm, m) {
3928 return (/\S$/.test(m)) ? parseInside (m, '<strong><em>', '</em></strong>') : wm;
3929 });
3930 text = text.replace(/__(\S[\s\S]*?)__/g, function (wm, m) {
3931 return (/\S$/.test(m)) ? parseInside (m, '<strong>', '</strong>') : wm;
3932 });
3933 text = text.replace(/_([^\s_][\s\S]*?)_/g, function (wm, m) {
3934 // !/^_[^_]/.test(m) - test if it doesn't start with __ (since it seems redundant, we removed it)
3935 return (/\S$/.test(m)) ? parseInside (m, '<em>', '</em>') : wm;
3936 });
3937 }
3938
3939 // Now parse asterisks
3940 if (options.literalMidWordAsterisks) {
3941 text = text.replace(/([^*]|^)\B\*\*\*(\S[\s\S]*?)\*\*\*\B(?!\*)/g, function (wm, lead, txt) {
3942 return parseInside (txt, lead + '<strong><em>', '</em></strong>');
3943 });
3944 text = text.replace(/([^*]|^)\B\*\*(\S[\s\S]*?)\*\*\B(?!\*)/g, function (wm, lead, txt) {
3945 return parseInside (txt, lead + '<strong>', '</strong>');
3946 });
3947 text = text.replace(/([^*]|^)\B\*(\S[\s\S]*?)\*\B(?!\*)/g, function (wm, lead, txt) {
3948 return parseInside (txt, lead + '<em>', '</em>');
3949 });
3950 } else {
3951 text = text.replace(/\*\*\*(\S[\s\S]*?)\*\*\*/g, function (wm, m) {
3952 return (/\S$/.test(m)) ? parseInside (m, '<strong><em>', '</em></strong>') : wm;
3953 });
3954 text = text.replace(/\*\*(\S[\s\S]*?)\*\*/g, function (wm, m) {
3955 return (/\S$/.test(m)) ? parseInside (m, '<strong>', '</strong>') : wm;
3956 });
3957 text = text.replace(/\*([^\s*][\s\S]*?)\*/g, function (wm, m) {
3958 // !/^\*[^*]/.test(m) - test if it doesn't start with ** (since it seems redundant, we removed it)
3959 return (/\S$/.test(m)) ? parseInside (m, '<em>', '</em>') : wm;
3960 });
3961 }
3962
3963
3964 text = globals.converter._dispatch('italicsAndBold.after', text, options, globals);
3965 return text;
3966});
3967
3968/**
3969 * Form HTML ordered (numbered) and unordered (bulleted) lists.
3970 */
3971showdown.subParser('lists', function (text, options, globals) {
3972 'use strict';
3973
3974 /**
3975 * Process the contents of a single ordered or unordered list, splitting it
3976 * into individual list items.
3977 * @param {string} listStr
3978 * @param {boolean} trimTrailing
3979 * @returns {string}
3980 */
3981 function processListItems (listStr, trimTrailing) {
3982 // The $g_list_level global keeps track of when we're inside a list.
3983 // Each time we enter a list, we increment it; when we leave a list,
3984 // we decrement. If it's zero, we're not in a list anymore.
3985 //
3986 // We do this because when we're not inside a list, we want to treat
3987 // something like this:
3988 //
3989 // I recommend upgrading to version
3990 // 8. Oops, now this line is treated
3991 // as a sub-list.
3992 //
3993 // As a single paragraph, despite the fact that the second line starts
3994 // with a digit-period-space sequence.
3995 //
3996 // Whereas when we're inside a list (or sub-list), that line will be
3997 // treated as the start of a sub-list. What a kludge, huh? This is
3998 // an aspect of Markdown's syntax that's hard to parse perfectly
3999 // without resorting to mind-reading. Perhaps the solution is to
4000 // change the syntax rules such that sub-lists must start with a
4001 // starting cardinal number; e.g. "1." or "a.".
4002 globals.gListLevel++;
4003
4004 // trim trailing blank lines:
4005 listStr = listStr.replace(/\n{2,}$/, '\n');
4006
4007 // attacklab: add sentinel to emulate \z
4008 listStr += '¨0';
4009
4010 var rgx = /(\n)?(^ {0,3})([*+-]|\d+[.])[ \t]+((\[(x|X| )?])?[ \t]*[^\r]+?(\n{1,2}))(?=\n*(¨0| {0,3}([*+-]|\d+[.])[ \t]+))/gm,
4011 isParagraphed = (/\n[ \t]*\n(?!¨0)/.test(listStr));
4012
4013 // Since version 1.5, nesting sublists requires 4 spaces (or 1 tab) indentation,
4014 // which is a syntax breaking change
4015 // activating this option reverts to old behavior
4016 if (options.disableForced4SpacesIndentedSublists) {
4017 rgx = /(\n)?(^ {0,3})([*+-]|\d+[.])[ \t]+((\[(x|X| )?])?[ \t]*[^\r]+?(\n{1,2}))(?=\n*(¨0|\2([*+-]|\d+[.])[ \t]+))/gm;
4018 }
4019
4020 listStr = listStr.replace(rgx, function (wholeMatch, m1, m2, m3, m4, taskbtn, checked) {
4021 checked = (checked && checked.trim() !== '');
4022
4023 var item = showdown.subParser('outdent')(m4, options, globals),
4024 bulletStyle = '';
4025
4026 // Support for github tasklists
4027 if (taskbtn && options.tasklists) {
4028 bulletStyle = ' class="task-list-item" style="list-style-type: none;"';
4029 item = item.replace(/^[ \t]*\[(x|X| )?]/m, function () {
4030 var otp = '<input type="checkbox" disabled style="margin: 0px 0.35em 0.25em -1.6em; vertical-align: middle;"';
4031 if (checked) {
4032 otp += ' checked';
4033 }
4034 otp += '>';
4035 return otp;
4036 });
4037 }
4038
4039 // ISSUE #312
4040 // This input: - - - a
4041 // causes trouble to the parser, since it interprets it as:
4042 // <ul><li><li><li>a</li></li></li></ul>
4043 // instead of:
4044 // <ul><li>- - a</li></ul>
4045 // So, to prevent it, we will put a marker (¨A)in the beginning of the line
4046 // Kind of hackish/monkey patching, but seems more effective than overcomplicating the list parser
4047 item = item.replace(/^([-*+]|\d\.)[ \t]+[\S\n ]*/g, function (wm2) {
4048 return '¨A' + wm2;
4049 });
4050
4051 // m1 - Leading line or
4052 // Has a double return (multi paragraph) or
4053 // Has sublist
4054 if (m1 || (item.search(/\n{2,}/) > -1)) {
4055 item = showdown.subParser('githubCodeBlocks')(item, options, globals);
4056 item = showdown.subParser('blockGamut')(item, options, globals);
4057 } else {
4058 // Recursion for sub-lists:
4059 item = showdown.subParser('lists')(item, options, globals);
4060 item = item.replace(/\n$/, ''); // chomp(item)
4061 item = showdown.subParser('hashHTMLBlocks')(item, options, globals);
4062
4063 // Colapse double linebreaks
4064 item = item.replace(/\n\n+/g, '\n\n');
4065 if (isParagraphed) {
4066 item = showdown.subParser('paragraphs')(item, options, globals);
4067 } else {
4068 item = showdown.subParser('spanGamut')(item, options, globals);
4069 }
4070 }
4071
4072 // now we need to remove the marker (¨A)
4073 item = item.replace('¨A', '');
4074 // we can finally wrap the line in list item tags
4075 item = '<li' + bulletStyle + '>' + item + '</li>\n';
4076
4077 return item;
4078 });
4079
4080 // attacklab: strip sentinel
4081 listStr = listStr.replace(/¨0/g, '');
4082
4083 globals.gListLevel--;
4084
4085 if (trimTrailing) {
4086 listStr = listStr.replace(/\s+$/, '');
4087 }
4088
4089 return listStr;
4090 }
4091
4092 function styleStartNumber (list, listType) {
4093 // check if ol and starts by a number different than 1
4094 if (listType === 'ol') {
4095 var res = list.match(/^ *(\d+)\./);
4096 if (res && res[1] !== '1') {
4097 return ' start="' + res[1] + '"';
4098 }
4099 }
4100 return '';
4101 }
4102
4103 /**
4104 * Check and parse consecutive lists (better fix for issue #142)
4105 * @param {string} list
4106 * @param {string} listType
4107 * @param {boolean} trimTrailing
4108 * @returns {string}
4109 */
4110 function parseConsecutiveLists (list, listType, trimTrailing) {
4111 // check if we caught 2 or more consecutive lists by mistake
4112 // we use the counterRgx, meaning if listType is UL we look for OL and vice versa
4113 var olRgx = (options.disableForced4SpacesIndentedSublists) ? /^ ?\d+\.[ \t]/gm : /^ {0,3}\d+\.[ \t]/gm,
4114 ulRgx = (options.disableForced4SpacesIndentedSublists) ? /^ ?[*+-][ \t]/gm : /^ {0,3}[*+-][ \t]/gm,
4115 counterRxg = (listType === 'ul') ? olRgx : ulRgx,
4116 result = '';
4117
4118 if (list.search(counterRxg) !== -1) {
4119 (function parseCL (txt) {
4120 var pos = txt.search(counterRxg),
4121 style = styleStartNumber(list, listType);
4122 if (pos !== -1) {
4123 // slice
4124 result += '\n\n<' + listType + style + '>\n' + processListItems(txt.slice(0, pos), !!trimTrailing) + '</' + listType + '>\n';
4125
4126 // invert counterType and listType
4127 listType = (listType === 'ul') ? 'ol' : 'ul';
4128 counterRxg = (listType === 'ul') ? olRgx : ulRgx;
4129
4130 //recurse
4131 parseCL(txt.slice(pos));
4132 } else {
4133 result += '\n\n<' + listType + style + '>\n' + processListItems(txt, !!trimTrailing) + '</' + listType + '>\n';
4134 }
4135 })(list);
4136 } else {
4137 var style = styleStartNumber(list, listType);
4138 result = '\n\n<' + listType + style + '>\n' + processListItems(list, !!trimTrailing) + '</' + listType + '>\n';
4139 }
4140
4141 return result;
4142 }
4143
4144 /** Start of list parsing **/
4145 text = globals.converter._dispatch('lists.before', text, options, globals);
4146 // add sentinel to hack around khtml/safari bug:
4147 // http://bugs.webkit.org/show_bug.cgi?id=11231
4148 text += '¨0';
4149
4150 if (globals.gListLevel) {
4151 text = text.replace(/^(( {0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(¨0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm,
4152 function (wholeMatch, list, m2) {
4153 var listType = (m2.search(/[*+-]/g) > -1) ? 'ul' : 'ol';
4154 return parseConsecutiveLists(list, listType, true);
4155 }
4156 );
4157 } else {
4158 text = text.replace(/(\n\n|^\n?)(( {0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(¨0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm,
4159 function (wholeMatch, m1, list, m3) {
4160 var listType = (m3.search(/[*+-]/g) > -1) ? 'ul' : 'ol';
4161 return parseConsecutiveLists(list, listType, false);
4162 }
4163 );
4164 }
4165
4166 // strip sentinel
4167 text = text.replace(/¨0/, '');
4168 text = globals.converter._dispatch('lists.after', text, options, globals);
4169 return text;
4170});
4171
4172/**
4173 * Parse metadata at the top of the document
4174 */
4175showdown.subParser('metadata', function (text, options, globals) {
4176 'use strict';
4177
4178 if (!options.metadata) {
4179 return text;
4180 }
4181
4182 text = globals.converter._dispatch('metadata.before', text, options, globals);
4183
4184 function parseMetadataContents (content) {
4185 // raw is raw so it's not changed in any way
4186 globals.metadata.raw = content;
4187
4188 // escape chars forbidden in html attributes
4189 // double quotes
4190 content = content
4191 // ampersand first
4192 .replace(/&/g, '&amp;')
4193 // double quotes
4194 .replace(/"/g, '&quot;');
4195
4196 content = content.replace(/\n {4}/g, ' ');
4197 content.replace(/^([\S ]+): +([\s\S]+?)$/gm, function (wm, key, value) {
4198 globals.metadata.parsed[key] = value;
4199 return '';
4200 });
4201 }
4202
4203 text = text.replace(/^\s*«««+(\S*?)\n([\s\S]+?)\n»»»+\n/, function (wholematch, format, content) {
4204 parseMetadataContents(content);
4205 return '¨M';
4206 });
4207
4208 text = text.replace(/^\s*---+(\S*?)\n([\s\S]+?)\n---+\n/, function (wholematch, format, content) {
4209 if (format) {
4210 globals.metadata.format = format;
4211 }
4212 parseMetadataContents(content);
4213 return '¨M';
4214 });
4215
4216 text = text.replace(/¨M/g, '');
4217
4218 text = globals.converter._dispatch('metadata.after', text, options, globals);
4219 return text;
4220});
4221
4222/**
4223 * Remove one level of line-leading tabs or spaces
4224 */
4225showdown.subParser('outdent', function (text, options, globals) {
4226 'use strict';
4227 text = globals.converter._dispatch('outdent.before', text, options, globals);
4228
4229 // attacklab: hack around Konqueror 3.5.4 bug:
4230 // "----------bug".replace(/^-/g,"") == "bug"
4231 text = text.replace(/^(\t|[ ]{1,4})/gm, '¨0'); // attacklab: g_tab_width
4232
4233 // attacklab: clean up hack
4234 text = text.replace(/¨0/g, '');
4235
4236 text = globals.converter._dispatch('outdent.after', text, options, globals);
4237 return text;
4238});
4239
4240/**
4241 *
4242 */
4243showdown.subParser('paragraphs', function (text, options, globals) {
4244 'use strict';
4245
4246 text = globals.converter._dispatch('paragraphs.before', text, options, globals);
4247 // Strip leading and trailing lines:
4248 text = text.replace(/^\n+/g, '');
4249 text = text.replace(/\n+$/g, '');
4250
4251 var grafs = text.split(/\n{2,}/g),
4252 grafsOut = [],
4253 end = grafs.length; // Wrap <p> tags
4254
4255 for (var i = 0; i < end; i++) {
4256 var str = grafs[i];
4257 // if this is an HTML marker, copy it
4258 if (str.search(/¨(K|G)(\d+)\1/g) >= 0) {
4259 grafsOut.push(str);
4260
4261 // test for presence of characters to prevent empty lines being parsed
4262 // as paragraphs (resulting in undesired extra empty paragraphs)
4263 } else if (str.search(/\S/) >= 0) {
4264 str = showdown.subParser('spanGamut')(str, options, globals);
4265 str = str.replace(/^([ \t]*)/g, '<p>');
4266 str += '</p>';
4267 grafsOut.push(str);
4268 }
4269 }
4270
4271 /** Unhashify HTML blocks */
4272 end = grafsOut.length;
4273 for (i = 0; i < end; i++) {
4274 var blockText = '',
4275 grafsOutIt = grafsOut[i],
4276 codeFlag = false;
4277 // if this is a marker for an html block...
4278 // use RegExp.test instead of string.search because of QML bug
4279 while (/¨(K|G)(\d+)\1/.test(grafsOutIt)) {
4280 var delim = RegExp.$1,
4281 num = RegExp.$2;
4282
4283 if (delim === 'K') {
4284 blockText = globals.gHtmlBlocks[num];
4285 } else {
4286 // we need to check if ghBlock is a false positive
4287 if (codeFlag) {
4288 // use encoded version of all text
4289 blockText = showdown.subParser('encodeCode')(globals.ghCodeBlocks[num].text, options, globals);
4290 } else {
4291 blockText = globals.ghCodeBlocks[num].codeblock;
4292 }
4293 }
4294 blockText = blockText.replace(/\$/g, '$$$$'); // Escape any dollar signs
4295
4296 grafsOutIt = grafsOutIt.replace(/(\n\n)?¨(K|G)\d+\2(\n\n)?/, blockText);
4297 // Check if grafsOutIt is a pre->code
4298 if (/^<pre\b[^>]*>\s*<code\b[^>]*>/.test(grafsOutIt)) {
4299 codeFlag = true;
4300 }
4301 }
4302 grafsOut[i] = grafsOutIt;
4303 }
4304 text = grafsOut.join('\n');
4305 // Strip leading and trailing lines:
4306 text = text.replace(/^\n+/g, '');
4307 text = text.replace(/\n+$/g, '');
4308 return globals.converter._dispatch('paragraphs.after', text, options, globals);
4309});
4310
4311/**
4312 * Run extension
4313 */
4314showdown.subParser('runExtension', function (ext, text, options, globals) {
4315 'use strict';
4316
4317 if (ext.filter) {
4318 text = ext.filter(text, globals.converter, options);
4319
4320 } else if (ext.regex) {
4321 // TODO remove this when old extension loading mechanism is deprecated
4322 var re = ext.regex;
4323 if (!(re instanceof RegExp)) {
4324 re = new RegExp(re, 'g');
4325 }
4326 text = text.replace(re, ext.replace);
4327 }
4328
4329 return text;
4330});
4331
4332/**
4333 * These are all the transformations that occur *within* block-level
4334 * tags like paragraphs, headers, and list items.
4335 */
4336showdown.subParser('spanGamut', function (text, options, globals) {
4337 'use strict';
4338
4339 text = globals.converter._dispatch('spanGamut.before', text, options, globals);
4340 text = showdown.subParser('codeSpans')(text, options, globals);
4341 text = showdown.subParser('escapeSpecialCharsWithinTagAttributes')(text, options, globals);
4342 text = showdown.subParser('encodeBackslashEscapes')(text, options, globals);
4343
4344 // Process anchor and image tags. Images must come first,
4345 // because ![foo][f] looks like an anchor.
4346 text = showdown.subParser('images')(text, options, globals);
4347 text = showdown.subParser('anchors')(text, options, globals);
4348
4349 // Make links out of things like `<http://example.com/>`
4350 // Must come after anchors, because you can use < and >
4351 // delimiters in inline links like [this](<url>).
4352 text = showdown.subParser('autoLinks')(text, options, globals);
4353 text = showdown.subParser('simplifiedAutoLinks')(text, options, globals);
4354 text = showdown.subParser('emoji')(text, options, globals);
4355 text = showdown.subParser('underline')(text, options, globals);
4356 text = showdown.subParser('italicsAndBold')(text, options, globals);
4357 text = showdown.subParser('strikethrough')(text, options, globals);
4358 text = showdown.subParser('ellipsis')(text, options, globals);
4359
4360 // we need to hash HTML tags inside spans
4361 text = showdown.subParser('hashHTMLSpans')(text, options, globals);
4362
4363 // now we encode amps and angles
4364 text = showdown.subParser('encodeAmpsAndAngles')(text, options, globals);
4365
4366 // Do hard breaks
4367 if (options.simpleLineBreaks) {
4368 // GFM style hard breaks
4369 // only add line breaks if the text does not contain a block (special case for lists)
4370 if (!/\n\n¨K/.test(text)) {
4371 text = text.replace(/\n+/g, '<br />\n');
4372 }
4373 } else {
4374 // Vanilla hard breaks
4375 text = text.replace(/ +\n/g, '<br />\n');
4376 }
4377
4378 text = globals.converter._dispatch('spanGamut.after', text, options, globals);
4379 return text;
4380});
4381
4382showdown.subParser('strikethrough', function (text, options, globals) {
4383 'use strict';
4384
4385 function parseInside (txt) {
4386 if (options.simplifiedAutoLink) {
4387 txt = showdown.subParser('simplifiedAutoLinks')(txt, options, globals);
4388 }
4389 return '<del>' + txt + '</del>';
4390 }
4391
4392 if (options.strikethrough) {
4393 text = globals.converter._dispatch('strikethrough.before', text, options, globals);
4394 text = text.replace(/(?:~){2}([\s\S]+?)(?:~){2}/g, function (wm, txt) { return parseInside(txt); });
4395 text = globals.converter._dispatch('strikethrough.after', text, options, globals);
4396 }
4397
4398 return text;
4399});
4400
4401/**
4402 * Strips link definitions from text, stores the URLs and titles in
4403 * hash references.
4404 * Link defs are in the form: ^[id]: url "optional title"
4405 */
4406showdown.subParser('stripLinkDefinitions', function (text, options, globals) {
4407 'use strict';
4408
4409 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,
4410 base64Regex = /^ {0,3}\[([^\]]+)]:[ \t]*\n?[ \t]*<?(data:.+?\/.+?;base64,[A-Za-z0-9+/=\n]+?)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*\n?[ \t]*(?:(\n*)["|'(](.+?)["|')][ \t]*)?(?:\n\n|(?=¨0)|(?=\n\[))/gm;
4411
4412 // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
4413 text += '¨0';
4414
4415 var replaceFunc = function (wholeMatch, linkId, url, width, height, blankLines, title) {
4416
4417 // if there aren't two instances of linkId it must not be a reference link so back out
4418 linkId = linkId.toLowerCase();
4419 if (text.toLowerCase().split(linkId).length - 1 < 2) {
4420 return wholeMatch;
4421 }
4422 if (url.match(/^data:.+?\/.+?;base64,/)) {
4423 // remove newlines
4424 globals.gUrls[linkId] = url.replace(/\s/g, '');
4425 } else {
4426 globals.gUrls[linkId] = showdown.subParser('encodeAmpsAndAngles')(url, options, globals); // Link IDs are case-insensitive
4427 }
4428
4429 if (blankLines) {
4430 // Oops, found blank lines, so it's not a title.
4431 // Put back the parenthetical statement we stole.
4432 return blankLines + title;
4433
4434 } else {
4435 if (title) {
4436 globals.gTitles[linkId] = title.replace(/"|'/g, '&quot;');
4437 }
4438 if (options.parseImgDimensions && width && height) {
4439 globals.gDimensions[linkId] = {
4440 width: width,
4441 height: height
4442 };
4443 }
4444 }
4445 // Completely remove the definition from the text
4446 return '';
4447 };
4448
4449 // first we try to find base64 link references
4450 text = text.replace(base64Regex, replaceFunc);
4451
4452 text = text.replace(regex, replaceFunc);
4453
4454 // attacklab: strip sentinel
4455 text = text.replace(/¨0/, '');
4456
4457 return text;
4458});
4459
4460showdown.subParser('tables', function (text, options, globals) {
4461 'use strict';
4462
4463 if (!options.tables) {
4464 return text;
4465 }
4466
4467 var tableRgx = /^ {0,3}\|?.+\|.+\n {0,3}\|?[ \t]*:?[ \t]*(?:[-=]){2,}[ \t]*:?[ \t]*\|[ \t]*:?[ \t]*(?:[-=]){2,}[\s\S]+?(?:\n\n|¨0)/gm,
4468 //singeColTblRgx = /^ {0,3}\|.+\|\n {0,3}\|[ \t]*:?[ \t]*(?:[-=]){2,}[ \t]*:?[ \t]*\|[ \t]*\n(?: {0,3}\|.+\|\n)+(?:\n\n|¨0)/gm;
4469 singeColTblRgx = /^ {0,3}\|.+\|[ \t]*\n {0,3}\|[ \t]*:?[ \t]*(?:[-=]){2,}[ \t]*:?[ \t]*\|[ \t]*\n( {0,3}\|.+\|[ \t]*\n)*(?:\n|¨0)/gm;
4470
4471 function parseStyles (sLine) {
4472 if (/^:[ \t]*--*$/.test(sLine)) {
4473 return ' style="text-align:left;"';
4474 } else if (/^--*[ \t]*:[ \t]*$/.test(sLine)) {
4475 return ' style="text-align:right;"';
4476 } else if (/^:[ \t]*--*[ \t]*:$/.test(sLine)) {
4477 return ' style="text-align:center;"';
4478 } else {
4479 return '';
4480 }
4481 }
4482
4483 function parseHeaders (header, style) {
4484 var id = '';
4485 header = header.trim();
4486 // support both tablesHeaderId and tableHeaderId due to error in documentation so we don't break backwards compatibility
4487 if (options.tablesHeaderId || options.tableHeaderId) {
4488 id = ' id="' + header.replace(/ /g, '_').toLowerCase() + '"';
4489 }
4490 header = showdown.subParser('spanGamut')(header, options, globals);
4491
4492 return '<th' + id + style + '>' + header + '</th>\n';
4493 }
4494
4495 function parseCells (cell, style) {
4496 var subText = showdown.subParser('spanGamut')(cell, options, globals);
4497 return '<td' + style + '>' + subText + '</td>\n';
4498 }
4499
4500 function buildTable (headers, cells) {
4501 var tb = '<table>\n<thead>\n<tr>\n',
4502 tblLgn = headers.length;
4503
4504 for (var i = 0; i < tblLgn; ++i) {
4505 tb += headers[i];
4506 }
4507 tb += '</tr>\n</thead>\n<tbody>\n';
4508
4509 for (i = 0; i < cells.length; ++i) {
4510 tb += '<tr>\n';
4511 for (var ii = 0; ii < tblLgn; ++ii) {
4512 tb += cells[i][ii];
4513 }
4514 tb += '</tr>\n';
4515 }
4516 tb += '</tbody>\n</table>\n';
4517 return tb;
4518 }
4519
4520 function parseTable (rawTable) {
4521 var i, tableLines = rawTable.split('\n');
4522
4523 for (i = 0; i < tableLines.length; ++i) {
4524 // strip wrong first and last column if wrapped tables are used
4525 if (/^ {0,3}\|/.test(tableLines[i])) {
4526 tableLines[i] = tableLines[i].replace(/^ {0,3}\|/, '');
4527 }
4528 if (/\|[ \t]*$/.test(tableLines[i])) {
4529 tableLines[i] = tableLines[i].replace(/\|[ \t]*$/, '');
4530 }
4531 // parse code spans first, but we only support one line code spans
4532 tableLines[i] = showdown.subParser('codeSpans')(tableLines[i], options, globals);
4533 }
4534
4535 var rawHeaders = tableLines[0].split('|').map(function (s) { return s.trim();}),
4536 rawStyles = tableLines[1].split('|').map(function (s) { return s.trim();}),
4537 rawCells = [],
4538 headers = [],
4539 styles = [],
4540 cells = [];
4541
4542 tableLines.shift();
4543 tableLines.shift();
4544
4545 for (i = 0; i < tableLines.length; ++i) {
4546 if (tableLines[i].trim() === '') {
4547 continue;
4548 }
4549 rawCells.push(
4550 tableLines[i]
4551 .split('|')
4552 .map(function (s) {
4553 return s.trim();
4554 })
4555 );
4556 }
4557
4558 if (rawHeaders.length < rawStyles.length) {
4559 return rawTable;
4560 }
4561
4562 for (i = 0; i < rawStyles.length; ++i) {
4563 styles.push(parseStyles(rawStyles[i]));
4564 }
4565
4566 for (i = 0; i < rawHeaders.length; ++i) {
4567 if (showdown.helper.isUndefined(styles[i])) {
4568 styles[i] = '';
4569 }
4570 headers.push(parseHeaders(rawHeaders[i], styles[i]));
4571 }
4572
4573 for (i = 0; i < rawCells.length; ++i) {
4574 var row = [];
4575 for (var ii = 0; ii < headers.length; ++ii) {
4576 if (showdown.helper.isUndefined(rawCells[i][ii])) {
4577
4578 }
4579 row.push(parseCells(rawCells[i][ii], styles[ii]));
4580 }
4581 cells.push(row);
4582 }
4583
4584 return buildTable(headers, cells);
4585 }
4586
4587 text = globals.converter._dispatch('tables.before', text, options, globals);
4588
4589 // find escaped pipe characters
4590 text = text.replace(/\\(\|)/g, showdown.helper.escapeCharactersCallback);
4591
4592 // parse multi column tables
4593 text = text.replace(tableRgx, parseTable);
4594
4595 // parse one column tables
4596 text = text.replace(singeColTblRgx, parseTable);
4597
4598 text = globals.converter._dispatch('tables.after', text, options, globals);
4599
4600 return text;
4601});
4602
4603showdown.subParser('underline', function (text, options, globals) {
4604 'use strict';
4605
4606 if (!options.underline) {
4607 return text;
4608 }
4609
4610 text = globals.converter._dispatch('underline.before', text, options, globals);
4611
4612 if (options.literalMidWordUnderscores) {
4613 text = text.replace(/\b___(\S[\s\S]*?)___\b/g, function (wm, txt) {
4614 return '<u>' + txt + '</u>';
4615 });
4616 text = text.replace(/\b__(\S[\s\S]*?)__\b/g, function (wm, txt) {
4617 return '<u>' + txt + '</u>';
4618 });
4619 } else {
4620 text = text.replace(/___(\S[\s\S]*?)___/g, function (wm, m) {
4621 return (/\S$/.test(m)) ? '<u>' + m + '</u>' : wm;
4622 });
4623 text = text.replace(/__(\S[\s\S]*?)__/g, function (wm, m) {
4624 return (/\S$/.test(m)) ? '<u>' + m + '</u>' : wm;
4625 });
4626 }
4627
4628 // escape remaining underscores to prevent them being parsed by italic and bold
4629 text = text.replace(/(_)/g, showdown.helper.escapeCharactersCallback);
4630
4631 text = globals.converter._dispatch('underline.after', text, options, globals);
4632
4633 return text;
4634});
4635
4636/**
4637 * Swap back in all the special characters we've hidden.
4638 */
4639showdown.subParser('unescapeSpecialChars', function (text, options, globals) {
4640 'use strict';
4641 text = globals.converter._dispatch('unescapeSpecialChars.before', text, options, globals);
4642
4643 text = text.replace(/¨E(\d+)E/g, function (wholeMatch, m1) {
4644 var charCodeToReplace = parseInt(m1);
4645 return String.fromCharCode(charCodeToReplace);
4646 });
4647
4648 text = globals.converter._dispatch('unescapeSpecialChars.after', text, options, globals);
4649 return text;
4650});
4651
4652showdown.subParser('makeMarkdown.blockquote', function (node, globals) {
4653 'use strict';
4654
4655 var txt = '';
4656 if (node.hasChildNodes()) {
4657 var children = node.childNodes,
4658 childrenLength = children.length;
4659
4660 for (var i = 0; i < childrenLength; ++i) {
4661 var innerTxt = showdown.subParser('makeMarkdown.node')(children[i], globals);
4662
4663 if (innerTxt === '') {
4664 continue;
4665 }
4666 txt += innerTxt;
4667 }
4668 }
4669 // cleanup
4670 txt = txt.trim();
4671 txt = '> ' + txt.split('\n').join('\n> ');
4672 return txt;
4673});
4674
4675showdown.subParser('makeMarkdown.codeBlock', function (node, globals) {
4676 'use strict';
4677
4678 var lang = node.getAttribute('language'),
4679 num = node.getAttribute('precodenum');
4680 return '```' + lang + '\n' + globals.preList[num] + '\n```';
4681});
4682
4683showdown.subParser('makeMarkdown.codeSpan', function (node) {
4684 'use strict';
4685
4686 return '`' + node.innerHTML + '`';
4687});
4688
4689showdown.subParser('makeMarkdown.emphasis', function (node, globals) {
4690 'use strict';
4691
4692 var txt = '';
4693 if (node.hasChildNodes()) {
4694 txt += '*';
4695 var children = node.childNodes,
4696 childrenLength = children.length;
4697 for (var i = 0; i < childrenLength; ++i) {
4698 txt += showdown.subParser('makeMarkdown.node')(children[i], globals);
4699 }
4700 txt += '*';
4701 }
4702 return txt;
4703});
4704
4705showdown.subParser('makeMarkdown.header', function (node, globals, headerLevel) {
4706 'use strict';
4707
4708 var headerMark = new Array(headerLevel + 1).join('#'),
4709 txt = '';
4710
4711 if (node.hasChildNodes()) {
4712 txt = headerMark + ' ';
4713 var children = node.childNodes,
4714 childrenLength = children.length;
4715
4716 for (var i = 0; i < childrenLength; ++i) {
4717 txt += showdown.subParser('makeMarkdown.node')(children[i], globals);
4718 }
4719 }
4720 return txt;
4721});
4722
4723showdown.subParser('makeMarkdown.hr', function () {
4724 'use strict';
4725
4726 return '---';
4727});
4728
4729showdown.subParser('makeMarkdown.image', function (node) {
4730 'use strict';
4731
4732 var txt = '';
4733 if (node.hasAttribute('src')) {
4734 txt += '![' + node.getAttribute('alt') + '](';
4735 txt += '<' + node.getAttribute('src') + '>';
4736 if (node.hasAttribute('width') && node.hasAttribute('height')) {
4737 txt += ' =' + node.getAttribute('width') + 'x' + node.getAttribute('height');
4738 }
4739
4740 if (node.hasAttribute('title')) {
4741 txt += ' "' + node.getAttribute('title') + '"';
4742 }
4743 txt += ')';
4744 }
4745 return txt;
4746});
4747
4748showdown.subParser('makeMarkdown.links', function (node, globals) {
4749 'use strict';
4750
4751 var txt = '';
4752 if (node.hasChildNodes() && node.hasAttribute('href')) {
4753 var children = node.childNodes,
4754 childrenLength = children.length;
4755 txt = '[';
4756 for (var i = 0; i < childrenLength; ++i) {
4757 txt += showdown.subParser('makeMarkdown.node')(children[i], globals);
4758 }
4759 txt += '](';
4760 txt += '<' + node.getAttribute('href') + '>';
4761 if (node.hasAttribute('title')) {
4762 txt += ' "' + node.getAttribute('title') + '"';
4763 }
4764 txt += ')';
4765 }
4766 return txt;
4767});
4768
4769showdown.subParser('makeMarkdown.list', function (node, globals, type) {
4770 'use strict';
4771
4772 var txt = '';
4773 if (!node.hasChildNodes()) {
4774 return '';
4775 }
4776 var listItems = node.childNodes,
4777 listItemsLenght = listItems.length,
4778 listNum = node.getAttribute('start') || 1;
4779
4780 for (var i = 0; i < listItemsLenght; ++i) {
4781 if (typeof listItems[i].tagName === 'undefined' || listItems[i].tagName.toLowerCase() !== 'li') {
4782 continue;
4783 }
4784
4785 // define the bullet to use in list
4786 var bullet = '';
4787 if (type === 'ol') {
4788 bullet = listNum.toString() + '. ';
4789 } else {
4790 bullet = '- ';
4791 }
4792
4793 // parse list item
4794 txt += bullet + showdown.subParser('makeMarkdown.listItem')(listItems[i], globals);
4795 ++listNum;
4796 }
4797
4798 // add comment at the end to prevent consecutive lists to be parsed as one
4799 txt += '\n<!-- -->\n';
4800 return txt.trim();
4801});
4802
4803showdown.subParser('makeMarkdown.listItem', function (node, globals) {
4804 'use strict';
4805
4806 var listItemTxt = '';
4807
4808 var children = node.childNodes,
4809 childrenLenght = children.length;
4810
4811 for (var i = 0; i < childrenLenght; ++i) {
4812 listItemTxt += showdown.subParser('makeMarkdown.node')(children[i], globals);
4813 }
4814 // if it's only one liner, we need to add a newline at the end
4815 if (!/\n$/.test(listItemTxt)) {
4816 listItemTxt += '\n';
4817 } else {
4818 // it's multiparagraph, so we need to indent
4819 listItemTxt = listItemTxt
4820 .split('\n')
4821 .join('\n ')
4822 .replace(/^ {4}$/gm, '')
4823 .replace(/\n\n+/g, '\n\n');
4824 }
4825
4826 return listItemTxt;
4827});
4828
4829
4830
4831showdown.subParser('makeMarkdown.node', function (node, globals, spansOnly) {
4832 'use strict';
4833
4834 spansOnly = spansOnly || false;
4835
4836 var txt = '';
4837
4838 // edge case of text without wrapper paragraph
4839 if (node.nodeType === 3) {
4840 return showdown.subParser('makeMarkdown.txt')(node, globals);
4841 }
4842
4843 // HTML comment
4844 if (node.nodeType === 8) {
4845 return '<!--' + node.data + '-->\n\n';
4846 }
4847
4848 // process only node elements
4849 if (node.nodeType !== 1) {
4850 return '';
4851 }
4852
4853 var tagName = node.tagName.toLowerCase();
4854
4855 switch (tagName) {
4856
4857 //
4858 // BLOCKS
4859 //
4860 case 'h1':
4861 if (!spansOnly) { txt = showdown.subParser('makeMarkdown.header')(node, globals, 1) + '\n\n'; }
4862 break;
4863 case 'h2':
4864 if (!spansOnly) { txt = showdown.subParser('makeMarkdown.header')(node, globals, 2) + '\n\n'; }
4865 break;
4866 case 'h3':
4867 if (!spansOnly) { txt = showdown.subParser('makeMarkdown.header')(node, globals, 3) + '\n\n'; }
4868 break;
4869 case 'h4':
4870 if (!spansOnly) { txt = showdown.subParser('makeMarkdown.header')(node, globals, 4) + '\n\n'; }
4871 break;
4872 case 'h5':
4873 if (!spansOnly) { txt = showdown.subParser('makeMarkdown.header')(node, globals, 5) + '\n\n'; }
4874 break;
4875 case 'h6':
4876 if (!spansOnly) { txt = showdown.subParser('makeMarkdown.header')(node, globals, 6) + '\n\n'; }
4877 break;
4878
4879 case 'p':
4880 if (!spansOnly) { txt = showdown.subParser('makeMarkdown.paragraph')(node, globals) + '\n\n'; }
4881 break;
4882
4883 case 'blockquote':
4884 if (!spansOnly) { txt = showdown.subParser('makeMarkdown.blockquote')(node, globals) + '\n\n'; }
4885 break;
4886
4887 case 'hr':
4888 if (!spansOnly) { txt = showdown.subParser('makeMarkdown.hr')(node, globals) + '\n\n'; }
4889 break;
4890
4891 case 'ol':
4892 if (!spansOnly) { txt = showdown.subParser('makeMarkdown.list')(node, globals, 'ol') + '\n\n'; }
4893 break;
4894
4895 case 'ul':
4896 if (!spansOnly) { txt = showdown.subParser('makeMarkdown.list')(node, globals, 'ul') + '\n\n'; }
4897 break;
4898
4899 case 'precode':
4900 if (!spansOnly) { txt = showdown.subParser('makeMarkdown.codeBlock')(node, globals) + '\n\n'; }
4901 break;
4902
4903 case 'pre':
4904 if (!spansOnly) { txt = showdown.subParser('makeMarkdown.pre')(node, globals) + '\n\n'; }
4905 break;
4906
4907 case 'table':
4908 if (!spansOnly) { txt = showdown.subParser('makeMarkdown.table')(node, globals) + '\n\n'; }
4909 break;
4910
4911 //
4912 // SPANS
4913 //
4914 case 'code':
4915 txt = showdown.subParser('makeMarkdown.codeSpan')(node, globals);
4916 break;
4917
4918 case 'em':
4919 case 'i':
4920 txt = showdown.subParser('makeMarkdown.emphasis')(node, globals);
4921 break;
4922
4923 case 'strong':
4924 case 'b':
4925 txt = showdown.subParser('makeMarkdown.strong')(node, globals);
4926 break;
4927
4928 case 'del':
4929 txt = showdown.subParser('makeMarkdown.strikethrough')(node, globals);
4930 break;
4931
4932 case 'a':
4933 txt = showdown.subParser('makeMarkdown.links')(node, globals);
4934 break;
4935
4936 case 'img':
4937 txt = showdown.subParser('makeMarkdown.image')(node, globals);
4938 break;
4939
4940 default:
4941 txt = node.outerHTML + '\n\n';
4942 }
4943
4944 // common normalization
4945 // TODO eventually
4946
4947 return txt;
4948});
4949
4950showdown.subParser('makeMarkdown.paragraph', function (node, globals) {
4951 'use strict';
4952
4953 var txt = '';
4954 if (node.hasChildNodes()) {
4955 var children = node.childNodes,
4956 childrenLength = children.length;
4957 for (var i = 0; i < childrenLength; ++i) {
4958 txt += showdown.subParser('makeMarkdown.node')(children[i], globals);
4959 }
4960 }
4961
4962 // some text normalization
4963 txt = txt.trim();
4964
4965 return txt;
4966});
4967
4968showdown.subParser('makeMarkdown.pre', function (node, globals) {
4969 'use strict';
4970
4971 var num = node.getAttribute('prenum');
4972 return '<pre>' + globals.preList[num] + '</pre>';
4973});
4974
4975showdown.subParser('makeMarkdown.strikethrough', function (node, globals) {
4976 'use strict';
4977
4978 var txt = '';
4979 if (node.hasChildNodes()) {
4980 txt += '~~';
4981 var children = node.childNodes,
4982 childrenLength = children.length;
4983 for (var i = 0; i < childrenLength; ++i) {
4984 txt += showdown.subParser('makeMarkdown.node')(children[i], globals);
4985 }
4986 txt += '~~';
4987 }
4988 return txt;
4989});
4990
4991showdown.subParser('makeMarkdown.strong', function (node, globals) {
4992 'use strict';
4993
4994 var txt = '';
4995 if (node.hasChildNodes()) {
4996 txt += '**';
4997 var children = node.childNodes,
4998 childrenLength = children.length;
4999 for (var i = 0; i < childrenLength; ++i) {
5000 txt += showdown.subParser('makeMarkdown.node')(children[i], globals);
5001 }
5002 txt += '**';
5003 }
5004 return txt;
5005});
5006
5007showdown.subParser('makeMarkdown.table', function (node, globals) {
5008 'use strict';
5009
5010 var txt = '',
5011 tableArray = [[], []],
5012 headings = node.querySelectorAll('thead>tr>th'),
5013 rows = node.querySelectorAll('tbody>tr'),
5014 i, ii;
5015 for (i = 0; i < headings.length; ++i) {
5016 var headContent = showdown.subParser('makeMarkdown.tableCell')(headings[i], globals),
5017 allign = '---';
5018
5019 if (headings[i].hasAttribute('style')) {
5020 var style = headings[i].getAttribute('style').toLowerCase().replace(/\s/g, '');
5021 switch (style) {
5022 case 'text-align:left;':
5023 allign = ':---';
5024 break;
5025 case 'text-align:right;':
5026 allign = '---:';
5027 break;
5028 case 'text-align:center;':
5029 allign = ':---:';
5030 break;
5031 }
5032 }
5033 tableArray[0][i] = headContent.trim();
5034 tableArray[1][i] = allign;
5035 }
5036
5037 for (i = 0; i < rows.length; ++i) {
5038 var r = tableArray.push([]) - 1,
5039 cols = rows[i].getElementsByTagName('td');
5040
5041 for (ii = 0; ii < headings.length; ++ii) {
5042 var cellContent = ' ';
5043 if (typeof cols[ii] !== 'undefined') {
5044 cellContent = showdown.subParser('makeMarkdown.tableCell')(cols[ii], globals);
5045 }
5046 tableArray[r].push(cellContent);
5047 }
5048 }
5049
5050 var cellSpacesCount = 3;
5051 for (i = 0; i < tableArray.length; ++i) {
5052 for (ii = 0; ii < tableArray[i].length; ++ii) {
5053 var strLen = tableArray[i][ii].length;
5054 if (strLen > cellSpacesCount) {
5055 cellSpacesCount = strLen;
5056 }
5057 }
5058 }
5059
5060 for (i = 0; i < tableArray.length; ++i) {
5061 for (ii = 0; ii < tableArray[i].length; ++ii) {
5062 if (i === 1) {
5063 if (tableArray[i][ii].slice(-1) === ':') {
5064 tableArray[i][ii] = showdown.helper.padEnd(tableArray[i][ii].slice(-1), cellSpacesCount - 1, '-') + ':';
5065 } else {
5066 tableArray[i][ii] = showdown.helper.padEnd(tableArray[i][ii], cellSpacesCount, '-');
5067 }
5068 } else {
5069 tableArray[i][ii] = showdown.helper.padEnd(tableArray[i][ii], cellSpacesCount);
5070 }
5071 }
5072 txt += '| ' + tableArray[i].join(' | ') + ' |\n';
5073 }
5074
5075 return txt.trim();
5076});
5077
5078showdown.subParser('makeMarkdown.tableCell', function (node, globals) {
5079 'use strict';
5080
5081 var txt = '';
5082 if (!node.hasChildNodes()) {
5083 return '';
5084 }
5085 var children = node.childNodes,
5086 childrenLength = children.length;
5087
5088 for (var i = 0; i < childrenLength; ++i) {
5089 txt += showdown.subParser('makeMarkdown.node')(children[i], globals, true);
5090 }
5091 return txt.trim();
5092});
5093
5094showdown.subParser('makeMarkdown.txt', function (node) {
5095 'use strict';
5096
5097 var txt = node.nodeValue;
5098
5099 // multiple spaces are collapsed
5100 txt = txt.replace(/ +/g, ' ');
5101
5102 // replace the custom ¨NBSP; with a space
5103 txt = txt.replace(/¨NBSP;/g, ' ');
5104
5105 // ", <, > and & should replace escaped html entities
5106 txt = showdown.helper.unescapeHTMLEntities(txt);
5107
5108 // escape markdown magic characters
5109 // emphasis, strong and strikethrough - can appear everywhere
5110 // we also escape pipe (|) because of tables
5111 // and escape ` because of code blocks and spans
5112 txt = txt.replace(/([*_~|`])/g, '\\$1');
5113
5114 // escape > because of blockquotes
5115 txt = txt.replace(/^(\s*)>/g, '\\$1>');
5116
5117 // hash character, only troublesome at the beginning of a line because of headers
5118 txt = txt.replace(/^#/gm, '\\#');
5119
5120 // horizontal rules
5121 txt = txt.replace(/^(\s*)([-=]{3,})(\s*)$/, '$1\\$2$3');
5122
5123 // dot, because of ordered lists, only troublesome at the beginning of a line when preceded by an integer
5124 txt = txt.replace(/^( {0,3}\d+)\./gm, '$1\\.');
5125
5126 // +, * and -, at the beginning of a line becomes a list, so we need to escape them also (asterisk was already escaped)
5127 txt = txt.replace(/^( {0,3})([+-])/gm, '$1\\$2');
5128
5129 // images and links, ] followed by ( is problematic, so we escape it
5130 txt = txt.replace(/]([\s]*)\(/g, '\\]$1\\(');
5131
5132 // reference URIs must also be escaped
5133 txt = txt.replace(/^ {0,3}\[([\S \t]*?)]:/gm, '\\[$1]:');
5134
5135 return txt;
5136});
5137
5138var root = this;
5139
5140// AMD Loader
5141if (typeof define === 'function' && define.amd) {
5142 define(function () {
5143 'use strict';
5144 return showdown;
5145 });
5146
5147// CommonJS/nodeJS Loader
5148} else if (typeof module !== 'undefined' && module.exports) {
5149 module.exports = showdown;
5150
5151// Regular Browser loader
5152} else {
5153 root.showdown = showdown;
5154}
5155}).call(this);
5156
5157//# sourceMappingURL=showdown.js.map