showdown-rails 0.0.4 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d2de1e46c86ff19fd34dc916bbfac4c00286fb2f
4
- data.tar.gz: 27cad48a4b1cce8947ca5d3b9a74b2725b0b5053
3
+ metadata.gz: 8fc5dd483f54514c93813984bd441cf47623d695
4
+ data.tar.gz: b2e0c458ffe546334a6226852841ffcddd63aa45
5
5
  SHA512:
6
- metadata.gz: 60b10f7c0bd690aa5a50e2794bd5eaf2989fe823c40083ed5da6eb338f33dd3897ad7d5f5b5fc0308bdbaf93307a1340f61237424dadee5a6f49ea9a25d6c626
7
- data.tar.gz: 1a4826cc693988807fcf65c999635616c9675bc0427f033f0145739a1a18d8bfb9058d73302e0c695a5ddb9f25178b6e6f54d7294ccd8670db3bdbb98582e21e
6
+ metadata.gz: 060fdb0ff0d9f1f402438f2c0019deafc1ffc228accf6c14fe099b02eba7f382457d41937cdfb7b2e4503cd56d6db4b951d64c007255a2bc097ed259964e2ce7
7
+ data.tar.gz: 4d1af9acf38a80c08a167633eb0edd67c948bfc5c1685c6522fad5b4632da1bd62869ddc503d8089a4ddc72b0a61b039b8ae2bd536a91a44e937c3a751c78918
data/README.md CHANGED
@@ -34,7 +34,7 @@ Or install it yourself as:
34
34
  2. Turn Markdown into HTML!
35
35
 
36
36
  ```
37
- var converter = new Showdown.converter()
37
+ var converter = new showdown.Converter()
38
38
  converter.makeHtml("** Markdown! **")
39
39
  ```
40
40
 
@@ -1,5 +1,5 @@
1
1
  module Showdown
2
2
  module Rails
3
- VERSION = "0.0.4"
3
+ VERSION = "0.1.0"
4
4
  end
5
5
  end
@@ -51,7 +51,7 @@
51
51
  //
52
52
  // var text = "Markdown *rocks*.";
53
53
  //
54
- // var converter = new Showdown.converter();
54
+ // var converter = new showdown.Converter();
55
55
  // var html = converter.makeHtml(text);
56
56
  //
57
57
  // alert(html);
@@ -60,1395 +60,2454 @@
60
60
  // file before uncommenting it.
61
61
  //
62
62
 
63
+ ;/*! showdown 25-11-2016 */
64
+ (function(){
65
+ /**
66
+ * Created by Tivie on 13-07-2015.
67
+ */
68
+
69
+ function getDefaultOpts(simple) {
70
+ 'use strict';
71
+
72
+ var defaultOptions = {
73
+ omitExtraWLInCodeBlocks: {
74
+ defaultValue: false,
75
+ describe: 'Omit the default extra whiteline added to code blocks',
76
+ type: 'boolean'
77
+ },
78
+ noHeaderId: {
79
+ defaultValue: false,
80
+ describe: 'Turn on/off generated header id',
81
+ type: 'boolean'
82
+ },
83
+ prefixHeaderId: {
84
+ defaultValue: false,
85
+ describe: 'Specify a prefix to generated header ids',
86
+ type: 'string'
87
+ },
88
+ headerLevelStart: {
89
+ defaultValue: false,
90
+ describe: 'The header blocks level start',
91
+ type: 'integer'
92
+ },
93
+ parseImgDimensions: {
94
+ defaultValue: false,
95
+ describe: 'Turn on/off image dimension parsing',
96
+ type: 'boolean'
97
+ },
98
+ simplifiedAutoLink: {
99
+ defaultValue: false,
100
+ describe: 'Turn on/off GFM autolink style',
101
+ type: 'boolean'
102
+ },
103
+ literalMidWordUnderscores: {
104
+ defaultValue: false,
105
+ describe: 'Parse midword underscores as literal underscores',
106
+ type: 'boolean'
107
+ },
108
+ strikethrough: {
109
+ defaultValue: false,
110
+ describe: 'Turn on/off strikethrough support',
111
+ type: 'boolean'
112
+ },
113
+ tables: {
114
+ defaultValue: false,
115
+ describe: 'Turn on/off tables support',
116
+ type: 'boolean'
117
+ },
118
+ tablesHeaderId: {
119
+ defaultValue: false,
120
+ describe: 'Add an id to table headers',
121
+ type: 'boolean'
122
+ },
123
+ ghCodeBlocks: {
124
+ defaultValue: true,
125
+ describe: 'Turn on/off GFM fenced code blocks support',
126
+ type: 'boolean'
127
+ },
128
+ tasklists: {
129
+ defaultValue: false,
130
+ describe: 'Turn on/off GFM tasklist support',
131
+ type: 'boolean'
132
+ },
133
+ smoothLivePreview: {
134
+ defaultValue: false,
135
+ describe: 'Prevents weird effects in live previews due to incomplete input',
136
+ type: 'boolean'
137
+ },
138
+ smartIndentationFix: {
139
+ defaultValue: false,
140
+ description: 'Tries to smartly fix indentation in es6 strings',
141
+ type: 'boolean'
142
+ },
143
+ disableForced4SpacesIndentedSublists: {
144
+ defaultValue: false,
145
+ description: 'Disables the requirement of indenting nested sublists by 4 spaces',
146
+ type: 'boolean'
147
+ }
148
+ };
149
+ if (simple === false) {
150
+ return JSON.parse(JSON.stringify(defaultOptions));
151
+ }
152
+ var ret = {};
153
+ for (var opt in defaultOptions) {
154
+ if (defaultOptions.hasOwnProperty(opt)) {
155
+ ret[opt] = defaultOptions[opt].defaultValue;
156
+ }
157
+ }
158
+ return ret;
159
+ }
63
160
 
64
- //
65
- // Showdown namespace
66
- //
67
- var Showdown = { extensions: {} };
68
-
69
- //
70
- // forEach
71
- //
72
- var forEach = Showdown.forEach = function(obj, callback) {
73
- if (typeof obj.forEach === 'function') {
74
- obj.forEach(callback);
75
- } else {
76
- var i, len = obj.length;
77
- for (i = 0; i < len; i++) {
78
- callback(obj[i], i, obj);
79
- }
80
- }
161
+ /**
162
+ * Created by Tivie on 06-01-2015.
163
+ */
164
+
165
+ // Private properties
166
+ var showdown = {},
167
+ parsers = {},
168
+ extensions = {},
169
+ globalOptions = getDefaultOpts(true),
170
+ flavor = {
171
+ github: {
172
+ omitExtraWLInCodeBlocks: true,
173
+ prefixHeaderId: 'user-content-',
174
+ simplifiedAutoLink: true,
175
+ literalMidWordUnderscores: true,
176
+ strikethrough: true,
177
+ tables: true,
178
+ tablesHeaderId: true,
179
+ ghCodeBlocks: true,
180
+ tasklists: true,
181
+ disableForced4SpacesIndentedSublists: true
182
+ },
183
+ vanilla: getDefaultOpts(true)
184
+ };
185
+
186
+ /**
187
+ * helper namespace
188
+ * @type {{}}
189
+ */
190
+ showdown.helper = {};
191
+
192
+ /**
193
+ * TODO LEGACY SUPPORT CODE
194
+ * @type {{}}
195
+ */
196
+ showdown.extensions = {};
197
+
198
+ /**
199
+ * Set a global option
200
+ * @static
201
+ * @param {string} key
202
+ * @param {*} value
203
+ * @returns {showdown}
204
+ */
205
+ showdown.setOption = function (key, value) {
206
+ 'use strict';
207
+ globalOptions[key] = value;
208
+ return this;
81
209
  };
82
210
 
83
- //
84
- // Standard extension naming
85
- //
86
- var stdExtName = function(s) {
87
- return s.replace(/[_-]||\s/g, '').toLowerCase();
211
+ /**
212
+ * Get a global option
213
+ * @static
214
+ * @param {string} key
215
+ * @returns {*}
216
+ */
217
+ showdown.getOption = function (key) {
218
+ 'use strict';
219
+ return globalOptions[key];
88
220
  };
89
221
 
90
- //
91
- // converter
92
- //
93
- // Wraps all "globals" so that the only thing
94
- // exposed is makeHtml().
95
- //
96
- Showdown.converter = function(converter_options) {
97
-
98
- //
99
- // Globals:
100
- //
101
-
102
- // Global hashes, used by various utility routines
103
- var g_urls;
104
- var g_titles;
105
- var g_html_blocks;
106
-
107
- // Used to track when we're inside an ordered or unordered list
108
- // (see _ProcessListItems() for details):
109
- var g_list_level = 0;
110
-
111
- // Global extensions
112
- var g_lang_extensions = [];
113
- var g_output_modifiers = [];
114
-
115
-
116
- //
117
- // Automatic Extension Loading (node only):
118
- //
119
-
120
- if (typeof module !== 'undefind' && typeof exports !== 'undefined' && typeof require !== 'undefind') {
121
- var fs = require('fs');
122
-
123
- if (fs) {
124
- // Search extensions folder
125
- var extensions = fs.readdirSync((__dirname || '.')+'/extensions').filter(function(file){
126
- return ~file.indexOf('.js');
127
- }).map(function(file){
128
- return file.replace(/\.js$/, '');
129
- });
130
- // Load extensions into Showdown namespace
131
- Showdown.forEach(extensions, function(ext){
132
- var name = stdExtName(ext);
133
- Showdown.extensions[name] = require('./extensions/' + ext);
134
- });
135
- }
136
- }
137
-
138
- this.makeHtml = function(text) {
139
- //
140
- // Main function. The order in which other subs are called here is
141
- // essential. Link and image substitutions need to happen before
142
- // _EscapeSpecialCharsWithinTagAttributes(), so that any *'s or _'s in the <a>
143
- // and <img> tags get encoded.
144
- //
145
-
146
- // Clear the global hashes. If we don't clear these, you get conflicts
147
- // from other articles when generating a page which contains more than
148
- // one article (e.g. an index page that shows the N most recent
149
- // articles):
150
- g_urls = {};
151
- g_titles = {};
152
- g_html_blocks = [];
153
-
154
- // attacklab: Replace ~ with ~T
155
- // This lets us use tilde as an escape char to avoid md5 hashes
156
- // The choice of character is arbitray; anything that isn't
157
- // magic in Markdown will work.
158
- text = text.replace(/~/g,"~T");
159
-
160
- // attacklab: Replace $ with ~D
161
- // RegExp interprets $ as a special character
162
- // when it's in a replacement string
163
- text = text.replace(/\$/g,"~D");
164
-
165
- // Standardize line endings
166
- text = text.replace(/\r\n/g,"\n"); // DOS to Unix
167
- text = text.replace(/\r/g,"\n"); // Mac to Unix
168
-
169
- // Make sure text begins and ends with a couple of newlines:
170
- text = "\n\n" + text + "\n\n";
171
-
172
- // Convert all tabs to spaces.
173
- text = _Detab(text);
174
-
175
- // Strip any lines consisting only of spaces and tabs.
176
- // This makes subsequent regexen easier to write, because we can
177
- // match consecutive blank lines with /\n+/ instead of something
178
- // contorted like /[ \t]*\n+/ .
179
- text = text.replace(/^[ \t]+$/mg,"");
180
-
181
- // Run language extensions
182
- Showdown.forEach(g_lang_extensions, function(x){
183
- text = _ExecuteExtension(x, text);
184
- });
185
-
186
- // Handle github codeblocks prior to running HashHTML so that
187
- // HTML contained within the codeblock gets escaped propertly
188
- text = _DoGithubCodeBlocks(text);
189
-
190
- // Turn block-level HTML blocks into hash entries
191
- text = _HashHTMLBlocks(text);
192
-
193
- // Strip link definitions, store in hashes.
194
- text = _StripLinkDefinitions(text);
195
-
196
- text = _RunBlockGamut(text);
197
-
198
- text = _UnescapeSpecialChars(text);
199
-
200
- // attacklab: Restore dollar signs
201
- text = text.replace(/~D/g,"$$");
202
-
203
- // attacklab: Restore tildes
204
- text = text.replace(/~T/g,"~");
205
-
206
- // Run output modifiers
207
- Showdown.forEach(g_output_modifiers, function(x){
208
- text = _ExecuteExtension(x, text);
209
- });
210
-
211
- return text;
222
+ /**
223
+ * Get the global options
224
+ * @static
225
+ * @returns {{}}
226
+ */
227
+ showdown.getOptions = function () {
228
+ 'use strict';
229
+ return globalOptions;
212
230
  };
213
- //
214
- // Options:
215
- //
216
231
 
217
- // Parse extensions options into separate arrays
218
- if (converter_options && converter_options.extensions) {
219
-
220
- var self = this;
221
-
222
- // Iterate over each plugin
223
- Showdown.forEach(converter_options.extensions, function(plugin){
224
-
225
- // Assume it's a bundled plugin if a string is given
226
- if (typeof plugin === 'string') {
227
- plugin = Showdown.extensions[stdExtName(plugin)];
228
- }
229
-
230
- if (typeof plugin === 'function') {
231
- // Iterate over each extension within that plugin
232
- Showdown.forEach(plugin(self), function(ext){
233
- // Sort extensions by type
234
- if (ext.type) {
235
- if (ext.type === 'language' || ext.type === 'lang') {
236
- g_lang_extensions.push(ext);
237
- } else if (ext.type === 'output' || ext.type === 'html') {
238
- g_output_modifiers.push(ext);
239
- }
240
- } else {
241
- // Assume language extension
242
- g_output_modifiers.push(ext);
243
- }
244
- });
245
- } else {
246
- throw "Extension '" + plugin + "' could not be loaded. It was either not found or is not a valid extension.";
247
- }
248
- });
249
- }
250
-
251
-
252
- var _ExecuteExtension = function(ext, text) {
253
- if (ext.regex) {
254
- var re = new RegExp(ext.regex, 'g');
255
- return text.replace(re, ext.replace);
256
- } else if (ext.filter) {
257
- return ext.filter(text);
258
- }
232
+ /**
233
+ * Reset global options to the default values
234
+ * @static
235
+ */
236
+ showdown.resetOptions = function () {
237
+ 'use strict';
238
+ globalOptions = getDefaultOpts(true);
259
239
  };
260
240
 
261
- var _StripLinkDefinitions = function(text) {
262
- //
263
- // Strips link definitions from text, stores the URLs and titles in
264
- // hash references.
265
- //
266
-
267
- // Link defs are in the form: ^[id]: url "optional title"
268
-
269
- /*
270
- var text = text.replace(/
271
- ^[ ]{0,3}\[(.+)\]: // id = $1 attacklab: g_tab_width - 1
272
- [ \t]*
273
- \n? // maybe *one* newline
274
- [ \t]*
275
- <?(\S+?)>? // url = $2
276
- [ \t]*
277
- \n? // maybe one newline
278
- [ \t]*
279
- (?:
280
- (\n*) // any lines skipped = $3 attacklab: lookbehind removed
281
- ["(]
282
- (.+?) // title = $4
283
- [")]
284
- [ \t]*
285
- )? // title is optional
286
- (?:\n+|$)
287
- /gm,
288
- function(){...});
289
- */
290
-
291
- // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
292
- text += "~0";
293
-
294
- text = text.replace(/^[ ]{0,3}\[(.+)\]:[ \t]*\n?[ \t]*<?(\S+?)>?[ \t]*\n?[ \t]*(?:(\n*)["(](.+?)[")][ \t]*)?(?:\n+|(?=~0))/gm,
295
- function (wholeMatch,m1,m2,m3,m4) {
296
- m1 = m1.toLowerCase();
297
- g_urls[m1] = _EncodeAmpsAndAngles(m2); // Link IDs are case-insensitive
298
- if (m3) {
299
- // Oops, found blank lines, so it's not a title.
300
- // Put back the parenthetical statement we stole.
301
- return m3+m4;
302
- } else if (m4) {
303
- g_titles[m1] = m4.replace(/"/g,"&quot;");
304
- }
305
-
306
- // Completely remove the definition from the text
307
- return "";
308
- }
309
- );
310
-
311
- // attacklab: strip sentinel
312
- text = text.replace(/~0/,"");
313
-
314
- return text;
315
- }
316
-
317
-
318
- var _HashHTMLBlocks = function(text) {
319
- // attacklab: Double up blank lines to reduce lookaround
320
- text = text.replace(/\n/g,"\n\n");
321
-
322
- // Hashify HTML blocks:
323
- // We only want to do this for block-level HTML tags, such as headers,
324
- // lists, and tables. That's because we still want to wrap <p>s around
325
- // "paragraphs" that are wrapped in non-block-level tags, such as anchors,
326
- // phrase emphasis, and spans. The list of tags we're looking for is
327
- // hard-coded:
328
- var block_tags_a = "p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del|style|section|header|footer|nav|article|aside";
329
- var block_tags_b = "p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|style|section|header|footer|nav|article|aside";
330
-
331
- // First, look for nested blocks, e.g.:
332
- // <div>
333
- // <div>
334
- // tags for inner block must be indented.
335
- // </div>
336
- // </div>
337
- //
338
- // The outermost tags must start at the left margin for this to match, and
339
- // the inner nested divs must be indented.
340
- // We need to do this before the next, more liberal match, because the next
341
- // match will start at the first `<div>` and stop at the first `</div>`.
342
-
343
- // attacklab: This regex can be expensive when it fails.
344
- /*
345
- var text = text.replace(/
346
- ( // save in $1
347
- ^ // start of line (with /m)
348
- <($block_tags_a) // start tag = $2
349
- \b // word break
350
- // attacklab: hack around khtml/pcre bug...
351
- [^\r]*?\n // any number of lines, minimally matching
352
- </\2> // the matching end tag
353
- [ \t]* // trailing spaces/tabs
354
- (?=\n+) // followed by a newline
355
- ) // attacklab: there are sentinel newlines at end of document
356
- /gm,function(){...}};
357
- */
358
- text = text.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del)\b[^\r]*?\n<\/\2>[ \t]*(?=\n+))/gm,hashElement);
359
-
360
- //
361
- // Now match more liberally, simply from `\n<tag>` to `</tag>\n`
362
- //
363
-
364
- /*
365
- var text = text.replace(/
366
- ( // save in $1
367
- ^ // start of line (with /m)
368
- <($block_tags_b) // start tag = $2
369
- \b // word break
370
- // attacklab: hack around khtml/pcre bug...
371
- [^\r]*? // any number of lines, minimally matching
372
- </\2> // the matching end tag
373
- [ \t]* // trailing spaces/tabs
374
- (?=\n+) // followed by a newline
375
- ) // attacklab: there are sentinel newlines at end of document
376
- /gm,function(){...}};
377
- */
378
- text = text.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|style|section|header|footer|nav|article|aside)\b[^\r]*?<\/\2>[ \t]*(?=\n+)\n)/gm,hashElement);
379
-
380
- // Special case just for <hr />. It was easier to make a special case than
381
- // to make the other regex more complicated.
382
-
383
- /*
384
- text = text.replace(/
385
- ( // save in $1
386
- \n\n // Starting after a blank line
387
- [ ]{0,3}
388
- (<(hr) // start tag = $2
389
- \b // word break
390
- ([^<>])*? //
391
- \/?>) // the matching end tag
392
- [ \t]*
393
- (?=\n{2,}) // followed by a blank line
394
- )
395
- /g,hashElement);
396
- */
397
- text = text.replace(/(\n[ ]{0,3}(<(hr)\b([^<>])*?\/?>)[ \t]*(?=\n{2,}))/g,hashElement);
398
-
399
- // Special case for standalone HTML comments:
400
-
401
- /*
402
- text = text.replace(/
403
- ( // save in $1
404
- \n\n // Starting after a blank line
405
- [ ]{0,3} // attacklab: g_tab_width - 1
406
- <!
407
- (--[^\r]*?--\s*)+
408
- >
409
- [ \t]*
410
- (?=\n{2,}) // followed by a blank line
411
- )
412
- /g,hashElement);
413
- */
414
- text = text.replace(/(\n\n[ ]{0,3}<!(--[^\r]*?--\s*)+>[ \t]*(?=\n{2,}))/g,hashElement);
415
-
416
- // PHP and ASP-style processor instructions (<?...?> and <%...%>)
417
-
418
- /*
419
- text = text.replace(/
420
- (?:
421
- \n\n // Starting after a blank line
422
- )
423
- ( // save in $1
424
- [ ]{0,3} // attacklab: g_tab_width - 1
425
- (?:
426
- <([?%]) // $2
427
- [^\r]*?
428
- \2>
429
- )
430
- [ \t]*
431
- (?=\n{2,}) // followed by a blank line
432
- )
433
- /g,hashElement);
434
- */
435
- text = text.replace(/(?:\n\n)([ ]{0,3}(?:<([?%])[^\r]*?\2>)[ \t]*(?=\n{2,}))/g,hashElement);
436
-
437
- // attacklab: Undo double lines (see comment at top of this function)
438
- text = text.replace(/\n\n/g,"\n");
439
- return text;
440
- }
441
-
442
- var hashElement = function(wholeMatch,m1) {
443
- var blockText = m1;
444
-
445
- // Undo double lines
446
- blockText = blockText.replace(/\n\n/g,"\n");
447
- blockText = blockText.replace(/^\n/,"");
448
-
449
- // strip trailing blank lines
450
- blockText = blockText.replace(/\n+$/g,"");
451
-
452
- // Replace the element text with a marker ("~KxK" where x is its key)
453
- blockText = "\n\n~K" + (g_html_blocks.push(blockText)-1) + "K\n\n";
454
-
455
- return blockText;
241
+ /**
242
+ * Set the flavor showdown should use as default
243
+ * @param {string} name
244
+ */
245
+ showdown.setFlavor = function (name) {
246
+ 'use strict';
247
+ if (flavor.hasOwnProperty(name)) {
248
+ var preset = flavor[name];
249
+ for (var option in preset) {
250
+ if (preset.hasOwnProperty(option)) {
251
+ globalOptions[option] = preset[option];
252
+ }
253
+ }
254
+ }
456
255
  };
457
256
 
458
- var _RunBlockGamut = function(text) {
459
- //
460
- // These are all the transformations that form block-level
461
- // tags like paragraphs, headers, and list items.
462
- //
463
- text = _DoHeaders(text);
464
-
465
- // Do Horizontal Rules:
466
- var key = hashBlock("<hr />");
467
- text = text.replace(/^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$/gm,key);
468
- text = text.replace(/^[ ]{0,2}([ ]?\-[ ]?){3,}[ \t]*$/gm,key);
469
- text = text.replace(/^[ ]{0,2}([ ]?\_[ ]?){3,}[ \t]*$/gm,key);
470
-
471
- text = _DoLists(text);
472
- text = _DoCodeBlocks(text);
473
- text = _DoBlockQuotes(text);
474
-
475
- // We already ran _HashHTMLBlocks() before, in Markdown(), but that
476
- // was to escape raw HTML in the original Markdown source. This time,
477
- // we're escaping the markup we've just created, so that we don't wrap
478
- // <p> tags around block-level tags.
479
- text = _HashHTMLBlocks(text);
480
- text = _FormParagraphs(text);
481
-
482
- return text;
257
+ /**
258
+ * Get the default options
259
+ * @static
260
+ * @param {boolean} [simple=true]
261
+ * @returns {{}}
262
+ */
263
+ showdown.getDefaultOptions = function (simple) {
264
+ 'use strict';
265
+ return getDefaultOpts(simple);
483
266
  };
484
267
 
485
-
486
- var _RunSpanGamut = function(text) {
487
- //
488
- // These are all the transformations that occur *within* block-level
489
- // tags like paragraphs, headers, and list items.
490
- //
491
-
492
- text = _DoCodeSpans(text);
493
- text = _EscapeSpecialCharsWithinTagAttributes(text);
494
- text = _EncodeBackslashEscapes(text);
495
-
496
- // Process anchor and image tags. Images must come first,
497
- // because ![foo][f] looks like an anchor.
498
- text = _DoImages(text);
499
- text = _DoAnchors(text);
500
-
501
- // Make links out of things like `<http://example.com/>`
502
- // Must come after _DoAnchors(), because you can use < and >
503
- // delimiters in inline links like [this](<url>).
504
- text = _DoAutoLinks(text);
505
- text = _EncodeAmpsAndAngles(text);
506
- text = _DoItalicsAndBold(text);
507
-
508
- // Do hard breaks:
509
- text = text.replace(/ +\n/g," <br />\n");
510
-
511
- return text;
512
- }
513
-
514
- var _EscapeSpecialCharsWithinTagAttributes = function(text) {
515
- //
516
- // Within tags -- meaning between < and > -- encode [\ ` * _] so they
517
- // don't conflict with their use in Markdown for code, italics and strong.
518
- //
519
-
520
- // Build a regex to find HTML tags and comments. See Friedl's
521
- // "Mastering Regular Expressions", 2nd Ed., pp. 200-201.
522
- var regex = /(<[a-z\/!$]("[^"]*"|'[^']*'|[^'">])*>|<!(--.*?--\s*)+>)/gi;
523
-
524
- text = text.replace(regex, function(wholeMatch) {
525
- var tag = wholeMatch.replace(/(.)<\/?code>(?=.)/g,"$1`");
526
- tag = escapeCharacters(tag,"\\`*_");
527
- return tag;
528
- });
529
-
530
- return text;
531
- }
532
-
533
- var _DoAnchors = function(text) {
534
- //
535
- // Turn Markdown link shortcuts into XHTML <a> tags.
536
- //
537
- //
538
- // First, handle reference-style links: [link text] [id]
539
- //
540
-
541
- /*
542
- text = text.replace(/
543
- ( // wrap whole match in $1
544
- \[
545
- (
546
- (?:
547
- \[[^\]]*\] // allow brackets nested one level
548
- |
549
- [^\[] // or anything else
550
- )*
551
- )
552
- \]
553
-
554
- [ ]? // one optional space
555
- (?:\n[ ]*)? // one optional newline followed by spaces
556
-
557
- \[
558
- (.*?) // id = $3
559
- \]
560
- )()()()() // pad remaining backreferences
561
- /g,_DoAnchors_callback);
562
- */
563
- text = text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g,writeAnchorTag);
564
-
565
- //
566
- // Next, inline-style links: [link text](url "optional title")
567
- //
568
-
569
- /*
570
- text = text.replace(/
571
- ( // wrap whole match in $1
572
- \[
573
- (
574
- (?:
575
- \[[^\]]*\] // allow brackets nested one level
576
- |
577
- [^\[\]] // or anything else
578
- )
579
- )
580
- \]
581
- \( // literal paren
582
- [ \t]*
583
- () // no id, so leave $3 empty
584
- <?(.*?)>? // href = $4
585
- [ \t]*
586
- ( // $5
587
- (['"]) // quote char = $6
588
- (.*?) // Title = $7
589
- \6 // matching quote
590
- [ \t]* // ignore any spaces/tabs between closing quote and )
591
- )? // title is optional
592
- \)
593
- )
594
- /g,writeAnchorTag);
595
- */
596
- text = text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\]\([ \t]*()<?(.*?(?:\(.*?\).*?)?)>?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,writeAnchorTag);
597
-
598
- //
599
- // Last, handle reference-style shortcuts: [link text]
600
- // These must come last in case you've also got [link test][1]
601
- // or [link test](/foo)
602
- //
603
-
604
- /*
605
- text = text.replace(/
606
- ( // wrap whole match in $1
607
- \[
608
- ([^\[\]]+) // link text = $2; can't contain '[' or ']'
609
- \]
610
- )()()()()() // pad rest of backreferences
611
- /g, writeAnchorTag);
612
- */
613
- text = text.replace(/(\[([^\[\]]+)\])()()()()()/g, writeAnchorTag);
614
-
615
- return text;
616
- }
617
-
618
- var writeAnchorTag = function(wholeMatch,m1,m2,m3,m4,m5,m6,m7) {
619
- if (m7 == undefined) m7 = "";
620
- var whole_match = m1;
621
- var link_text = m2;
622
- var link_id = m3.toLowerCase();
623
- var url = m4;
624
- var title = m7;
625
-
626
- if (url == "") {
627
- if (link_id == "") {
628
- // lower-case and turn embedded newlines into spaces
629
- link_id = link_text.toLowerCase().replace(/ ?\n/g," ");
630
- }
631
- url = "#"+link_id;
632
-
633
- if (g_urls[link_id] != undefined) {
634
- url = g_urls[link_id];
635
- if (g_titles[link_id] != undefined) {
636
- title = g_titles[link_id];
637
- }
638
- }
639
- else {
640
- if (whole_match.search(/\(\s*\)$/m)>-1) {
641
- // Special case for explicit empty url
642
- url = "";
643
- } else {
644
- return whole_match;
645
- }
646
- }
647
- }
648
-
649
- url = escapeCharacters(url,"*_");
650
- var result = "<a href=\"" + url + "\"";
651
-
652
- if (title != "") {
653
- title = title.replace(/"/g,"&quot;");
654
- title = escapeCharacters(title,"*_");
655
- result += " title=\"" + title + "\"";
656
- }
657
-
658
- result += ">" + link_text + "</a>";
659
-
660
- return result;
661
- }
662
-
663
-
664
- var _DoImages = function(text) {
665
- //
666
- // Turn Markdown image shortcuts into <img> tags.
667
- //
668
-
669
- //
670
- // First, handle reference-style labeled images: ![alt text][id]
671
- //
672
-
673
- /*
674
- text = text.replace(/
675
- ( // wrap whole match in $1
676
- !\[
677
- (.*?) // alt text = $2
678
- \]
679
-
680
- [ ]? // one optional space
681
- (?:\n[ ]*)? // one optional newline followed by spaces
682
-
683
- \[
684
- (.*?) // id = $3
685
- \]
686
- )()()()() // pad rest of backreferences
687
- /g,writeImageTag);
688
- */
689
- text = text.replace(/(!\[(.*?)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g,writeImageTag);
690
-
691
- //
692
- // Next, handle inline images: ![alt text](url "optional title")
693
- // Don't forget: encode * and _
694
-
695
- /*
696
- text = text.replace(/
697
- ( // wrap whole match in $1
698
- !\[
699
- (.*?) // alt text = $2
700
- \]
701
- \s? // One optional whitespace character
702
- \( // literal paren
703
- [ \t]*
704
- () // no id, so leave $3 empty
705
- <?(\S+?)>? // src url = $4
706
- [ \t]*
707
- ( // $5
708
- (['"]) // quote char = $6
709
- (.*?) // title = $7
710
- \6 // matching quote
711
- [ \t]*
712
- )? // title is optional
713
- \)
714
- )
715
- /g,writeImageTag);
716
- */
717
- text = text.replace(/(!\[(.*?)\]\s?\([ \t]*()<?(\S+?)>?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,writeImageTag);
718
-
719
- return text;
720
- }
721
-
722
- var writeImageTag = function(wholeMatch,m1,m2,m3,m4,m5,m6,m7) {
723
- var whole_match = m1;
724
- var alt_text = m2;
725
- var link_id = m3.toLowerCase();
726
- var url = m4;
727
- var title = m7;
728
-
729
- if (!title) title = "";
730
-
731
- if (url == "") {
732
- if (link_id == "") {
733
- // lower-case and turn embedded newlines into spaces
734
- link_id = alt_text.toLowerCase().replace(/ ?\n/g," ");
735
- }
736
- url = "#"+link_id;
737
-
738
- if (g_urls[link_id] != undefined) {
739
- url = g_urls[link_id];
740
- if (g_titles[link_id] != undefined) {
741
- title = g_titles[link_id];
742
- }
743
- }
744
- else {
745
- return whole_match;
746
- }
747
- }
748
-
749
- alt_text = alt_text.replace(/"/g,"&quot;");
750
- url = escapeCharacters(url,"*_");
751
- var result = "<img src=\"" + url + "\" alt=\"" + alt_text + "\"";
752
-
753
- // attacklab: Markdown.pl adds empty title attributes to images.
754
- // Replicate this bug.
755
-
756
- //if (title != "") {
757
- title = title.replace(/"/g,"&quot;");
758
- title = escapeCharacters(title,"*_");
759
- result += " title=\"" + title + "\"";
760
- //}
761
-
762
- result += " />";
763
-
764
- return result;
765
- }
766
-
767
-
768
- var _DoHeaders = function(text) {
769
-
770
- // Setext-style headers:
771
- // Header 1
772
- // ========
773
- //
774
- // Header 2
775
- // --------
776
- //
777
- text = text.replace(/^(.+)[ \t]*\n=+[ \t]*\n+/gm,
778
- function(wholeMatch,m1){return hashBlock('<h1 id="' + headerId(m1) + '">' + _RunSpanGamut(m1) + "</h1>");});
779
-
780
- text = text.replace(/^(.+)[ \t]*\n-+[ \t]*\n+/gm,
781
- function(matchFound,m1){return hashBlock('<h2 id="' + headerId(m1) + '">' + _RunSpanGamut(m1) + "</h2>");});
782
-
783
- // atx-style headers:
784
- // # Header 1
785
- // ## Header 2
786
- // ## Header 2 with closing hashes ##
787
- // ...
788
- // ###### Header 6
789
- //
790
-
791
- /*
792
- text = text.replace(/
793
- ^(\#{1,6}) // $1 = string of #'s
794
- [ \t]*
795
- (.+?) // $2 = Header text
796
- [ \t]*
797
- \#* // optional closing #'s (not counted)
798
- \n+
799
- /gm, function() {...});
800
- */
801
-
802
- text = text.replace(/^(\#{1,6})[ \t]*(.+?)[ \t]*\#*\n+/gm,
803
- function(wholeMatch,m1,m2) {
804
- var h_level = m1.length;
805
- return hashBlock("<h" + h_level + ' id="' + headerId(m2) + '">' + _RunSpanGamut(m2) + "</h" + h_level + ">");
806
- });
807
-
808
- function headerId(m) {
809
- return m.replace(/[^\w]/g, '').toLowerCase();
810
- }
811
- return text;
812
- }
813
-
814
- // This declaration keeps Dojo compressor from outputting garbage:
815
- var _ProcessListItems;
816
-
817
- var _DoLists = function(text) {
818
- //
819
- // Form HTML ordered (numbered) and unordered (bulleted) lists.
820
- //
821
-
822
- // attacklab: add sentinel to hack around khtml/safari bug:
823
- // http://bugs.webkit.org/show_bug.cgi?id=11231
824
- text += "~0";
825
-
826
- // Re-usable pattern to match any entirel ul or ol list:
827
-
828
- /*
829
- var whole_list = /
830
- ( // $1 = whole list
831
- ( // $2
832
- [ ]{0,3} // attacklab: g_tab_width - 1
833
- ([*+-]|\d+[.]) // $3 = first list item marker
834
- [ \t]+
835
- )
836
- [^\r]+?
837
- ( // $4
838
- ~0 // sentinel for workaround; should be $
839
- |
840
- \n{2,}
841
- (?=\S)
842
- (?! // Negative lookahead for another list item marker
843
- [ \t]*
844
- (?:[*+-]|\d+[.])[ \t]+
845
- )
846
- )
847
- )/g
848
- */
849
- var whole_list = /^(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm;
850
-
851
- if (g_list_level) {
852
- text = text.replace(whole_list,function(wholeMatch,m1,m2) {
853
- var list = m1;
854
- var list_type = (m2.search(/[*+-]/g)>-1) ? "ul" : "ol";
855
-
856
- // Turn double returns into triple returns, so that we can make a
857
- // paragraph for the last item in a list, if necessary:
858
- list = list.replace(/\n{2,}/g,"\n\n\n");;
859
- var result = _ProcessListItems(list);
860
-
861
- // Trim any trailing whitespace, to put the closing `</$list_type>`
862
- // up on the preceding line, to get it past the current stupid
863
- // HTML block parser. This is a hack to work around the terrible
864
- // hack that is the HTML block parser.
865
- result = result.replace(/\s+$/,"");
866
- result = "<"+list_type+">" + result + "</"+list_type+">\n";
867
- return result;
868
- });
869
- } else {
870
- whole_list = /(\n\n|^\n?)(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/g;
871
- text = text.replace(whole_list,function(wholeMatch,m1,m2,m3) {
872
- var runup = m1;
873
- var list = m2;
874
-
875
- var list_type = (m3.search(/[*+-]/g)>-1) ? "ul" : "ol";
876
- // Turn double returns into triple returns, so that we can make a
877
- // paragraph for the last item in a list, if necessary:
878
- var list = list.replace(/\n{2,}/g,"\n\n\n");;
879
- var result = _ProcessListItems(list);
880
- result = runup + "<"+list_type+">\n" + result + "</"+list_type+">\n";
881
- return result;
882
- });
883
- }
884
-
885
- // attacklab: strip sentinel
886
- text = text.replace(/~0/,"");
887
-
888
- return text;
889
- }
890
-
891
- _ProcessListItems = function(list_str) {
892
- //
893
- // Process the contents of a single ordered or unordered list, splitting it
894
- // into individual list items.
895
- //
896
- // The $g_list_level global keeps track of when we're inside a list.
897
- // Each time we enter a list, we increment it; when we leave a list,
898
- // we decrement. If it's zero, we're not in a list anymore.
899
- //
900
- // We do this because when we're not inside a list, we want to treat
901
- // something like this:
902
- //
903
- // I recommend upgrading to version
904
- // 8. Oops, now this line is treated
905
- // as a sub-list.
906
- //
907
- // As a single paragraph, despite the fact that the second line starts
908
- // with a digit-period-space sequence.
909
- //
910
- // Whereas when we're inside a list (or sub-list), that line will be
911
- // treated as the start of a sub-list. What a kludge, huh? This is
912
- // an aspect of Markdown's syntax that's hard to parse perfectly
913
- // without resorting to mind-reading. Perhaps the solution is to
914
- // change the syntax rules such that sub-lists must start with a
915
- // starting cardinal number; e.g. "1." or "a.".
916
-
917
- g_list_level++;
918
-
919
- // trim trailing blank lines:
920
- list_str = list_str.replace(/\n{2,}$/,"\n");
921
-
922
- // attacklab: add sentinel to emulate \z
923
- list_str += "~0";
924
-
925
- /*
926
- list_str = list_str.replace(/
927
- (\n)? // leading line = $1
928
- (^[ \t]*) // leading whitespace = $2
929
- ([*+-]|\d+[.]) [ \t]+ // list marker = $3
930
- ([^\r]+? // list item text = $4
931
- (\n{1,2}))
932
- (?= \n* (~0 | \2 ([*+-]|\d+[.]) [ \t]+))
933
- /gm, function(){...});
934
- */
935
- list_str = list_str.replace(/(\n)?(^[ \t]*)([*+-]|\d+[.])[ \t]+([^\r]+?(\n{1,2}))(?=\n*(~0|\2([*+-]|\d+[.])[ \t]+))/gm,
936
- function(wholeMatch,m1,m2,m3,m4){
937
- var item = m4;
938
- var leading_line = m1;
939
- var leading_space = m2;
940
-
941
- if (leading_line || (item.search(/\n{2,}/)>-1)) {
942
- item = _RunBlockGamut(_Outdent(item));
943
- }
944
- else {
945
- // Recursion for sub-lists:
946
- item = _DoLists(_Outdent(item));
947
- item = item.replace(/\n$/,""); // chomp(item)
948
- item = _RunSpanGamut(item);
949
- }
950
-
951
- return "<li>" + item + "</li>\n";
952
- }
953
- );
954
-
955
- // attacklab: strip sentinel
956
- list_str = list_str.replace(/~0/g,"");
957
-
958
- g_list_level--;
959
- return list_str;
960
- }
961
-
962
-
963
- var _DoCodeBlocks = function(text) {
964
- //
965
- // Process Markdown `<pre><code>` blocks.
966
- //
967
-
968
- /*
969
- text = text.replace(text,
970
- /(?:\n\n|^)
971
- ( // $1 = the code block -- one or more lines, starting with a space/tab
972
- (?:
973
- (?:[ ]{4}|\t) // Lines must start with a tab or a tab-width of spaces - attacklab: g_tab_width
974
- .*\n+
975
- )+
976
- )
977
- (\n*[ ]{0,3}[^ \t\n]|(?=~0)) // attacklab: g_tab_width
978
- /g,function(){...});
979
- */
980
-
981
- // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
982
- text += "~0";
983
-
984
- text = text.replace(/(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=~0))/g,
985
- function(wholeMatch,m1,m2) {
986
- var codeblock = m1;
987
- var nextChar = m2;
988
-
989
- codeblock = _EncodeCode( _Outdent(codeblock));
990
- codeblock = _Detab(codeblock);
991
- codeblock = codeblock.replace(/^\n+/g,""); // trim leading newlines
992
- codeblock = codeblock.replace(/\n+$/g,""); // trim trailing whitespace
993
-
994
- codeblock = "<pre><code>" + codeblock + "\n</code></pre>";
995
-
996
- return hashBlock(codeblock) + nextChar;
997
- }
998
- );
999
-
1000
- // attacklab: strip sentinel
1001
- text = text.replace(/~0/,"");
1002
-
1003
- return text;
268
+ /**
269
+ * Get or set a subParser
270
+ *
271
+ * subParser(name) - Get a registered subParser
272
+ * subParser(name, func) - Register a subParser
273
+ * @static
274
+ * @param {string} name
275
+ * @param {function} [func]
276
+ * @returns {*}
277
+ */
278
+ showdown.subParser = function (name, func) {
279
+ 'use strict';
280
+ if (showdown.helper.isString(name)) {
281
+ if (typeof func !== 'undefined') {
282
+ parsers[name] = func;
283
+ } else {
284
+ if (parsers.hasOwnProperty(name)) {
285
+ return parsers[name];
286
+ } else {
287
+ throw Error('SubParser named ' + name + ' not registered!');
288
+ }
289
+ }
290
+ }
1004
291
  };
1005
292
 
1006
- var _DoGithubCodeBlocks = function(text) {
1007
- //
1008
- // Process Github-style code blocks
1009
- // Example:
1010
- // ```ruby
1011
- // def hello_world(x)
1012
- // puts "Hello, #{x}"
1013
- // end
1014
- // ```
1015
- //
1016
-
1017
-
1018
- // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
1019
- text += "~0";
1020
-
1021
- text = text.replace(/(?:^|\n)```(.*)\n([\s\S]*?)\n```/g,
1022
- function(wholeMatch,m1,m2) {
1023
- var language = m1;
1024
- var codeblock = m2;
1025
-
1026
- codeblock = _EncodeCode(codeblock);
1027
- codeblock = _Detab(codeblock);
1028
- codeblock = codeblock.replace(/^\n+/g,""); // trim leading newlines
1029
- codeblock = codeblock.replace(/\n+$/g,""); // trim trailing whitespace
1030
-
1031
- codeblock = "<pre><code" + (language ? " class=\"" + language + '"' : "") + ">" + codeblock + "\n</code></pre>";
1032
-
1033
- return hashBlock(codeblock);
1034
- }
1035
- );
1036
-
1037
- // attacklab: strip sentinel
1038
- text = text.replace(/~0/,"");
1039
-
1040
- return text;
1041
- }
1042
-
1043
- var hashBlock = function(text) {
1044
- text = text.replace(/(^\n+|\n+$)/g,"");
1045
- return "\n\n~K" + (g_html_blocks.push(text)-1) + "K\n\n";
1046
- }
1047
-
1048
- var _DoCodeSpans = function(text) {
1049
- //
1050
- // * Backtick quotes are used for <code></code> spans.
1051
- //
1052
- // * You can use multiple backticks as the delimiters if you want to
1053
- // include literal backticks in the code span. So, this input:
1054
- //
1055
- // Just type ``foo `bar` baz`` at the prompt.
1056
- //
1057
- // Will translate to:
1058
- //
1059
- // <p>Just type <code>foo `bar` baz</code> at the prompt.</p>
1060
- //
1061
- // There's no arbitrary limit to the number of backticks you
1062
- // can use as delimters. If you need three consecutive backticks
1063
- // in your code, use four for delimiters, etc.
1064
- //
1065
- // * You can use spaces to get literal backticks at the edges:
1066
- //
1067
- // ... type `` `bar` `` ...
1068
- //
1069
- // Turns to:
1070
- //
1071
- // ... type <code>`bar`</code> ...
1072
- //
1073
-
1074
- /*
1075
- text = text.replace(/
1076
- (^|[^\\]) // Character before opening ` can't be a backslash
1077
- (`+) // $2 = Opening run of `
1078
- ( // $3 = The code block
1079
- [^\r]*?
1080
- [^`] // attacklab: work around lack of lookbehind
1081
- )
1082
- \2 // Matching closer
1083
- (?!`)
1084
- /gm, function(){...});
1085
- */
1086
-
1087
- text = text.replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm,
1088
- function(wholeMatch,m1,m2,m3,m4) {
1089
- var c = m3;
1090
- c = c.replace(/^([ \t]*)/g,""); // leading whitespace
1091
- c = c.replace(/[ \t]*$/g,""); // trailing whitespace
1092
- c = _EncodeCode(c);
1093
- return m1+"<code>"+c+"</code>";
1094
- });
1095
-
1096
- return text;
1097
- }
1098
-
1099
- var _EncodeCode = function(text) {
1100
- //
1101
- // Encode/escape certain characters inside Markdown code runs.
1102
- // The point is that in code, these characters are literals,
1103
- // and lose their special Markdown meanings.
1104
- //
1105
- // Encode all ampersands; HTML entities are not
1106
- // entities within a Markdown code span.
1107
- text = text.replace(/&/g,"&amp;");
1108
-
1109
- // Do the angle bracket song and dance:
1110
- text = text.replace(/</g,"&lt;");
1111
- text = text.replace(/>/g,"&gt;");
1112
-
1113
- // Now, escape characters that are magic in Markdown:
1114
- text = escapeCharacters(text,"\*_{}[]\\",false);
1115
-
1116
- // jj the line above breaks this:
1117
- //---
1118
-
1119
- //* Item
1120
-
1121
- // 1. Subitem
1122
-
1123
- // special char: *
1124
- //---
1125
-
1126
- return text;
1127
- }
1128
-
1129
-
1130
- var _DoItalicsAndBold = function(text) {
1131
-
1132
- // <strong> must go first:
1133
- text = text.replace(/(\*\*|__)(?=\S)([^\r]*?\S[*_]*)\1/g,
1134
- "<strong>$2</strong>");
1135
-
1136
- text = text.replace(/(\*|_)(?=\S)([^\r]*?\S)\1/g,
1137
- "<em>$2</em>");
1138
-
1139
- return text;
1140
- }
1141
-
1142
-
1143
- var _DoBlockQuotes = function(text) {
1144
-
1145
- /*
1146
- text = text.replace(/
1147
- ( // Wrap whole match in $1
1148
- (
1149
- ^[ \t]*>[ \t]? // '>' at the start of a line
1150
- .+\n // rest of the first line
1151
- (.+\n)* // subsequent consecutive lines
1152
- \n* // blanks
1153
- )+
1154
- )
1155
- /gm, function(){...});
1156
- */
1157
-
1158
- text = text.replace(/((^[ \t]*>[ \t]?.+\n(.+\n)*\n*)+)/gm,
1159
- function(wholeMatch,m1) {
1160
- var bq = m1;
1161
-
1162
- // attacklab: hack around Konqueror 3.5.4 bug:
1163
- // "----------bug".replace(/^-/g,"") == "bug"
1164
-
1165
- bq = bq.replace(/^[ \t]*>[ \t]?/gm,"~0"); // trim one level of quoting
1166
-
1167
- // attacklab: clean up hack
1168
- bq = bq.replace(/~0/g,"");
1169
-
1170
- bq = bq.replace(/^[ \t]+$/gm,""); // trim whitespace-only lines
1171
- bq = _RunBlockGamut(bq); // recurse
1172
-
1173
- bq = bq.replace(/(^|\n)/g,"$1 ");
1174
- // These leading spaces screw with <pre> content, so we need to fix that:
1175
- bq = bq.replace(
1176
- /(\s*<pre>[^\r]+?<\/pre>)/gm,
1177
- function(wholeMatch,m1) {
1178
- var pre = m1;
1179
- // attacklab: hack around Konqueror 3.5.4 bug:
1180
- pre = pre.replace(/^ /mg,"~0");
1181
- pre = pre.replace(/~0/g,"");
1182
- return pre;
1183
- });
1184
-
1185
- return hashBlock("<blockquote>\n" + bq + "\n</blockquote>");
1186
- });
1187
- return text;
1188
- }
1189
-
1190
-
1191
- var _FormParagraphs = function(text) {
1192
- //
1193
- // Params:
1194
- // $text - string to process with html <p> tags
1195
- //
1196
-
1197
- // Strip leading and trailing lines:
1198
- text = text.replace(/^\n+/g,"");
1199
- text = text.replace(/\n+$/g,"");
1200
-
1201
- var grafs = text.split(/\n{2,}/g);
1202
- var grafsOut = [];
1203
-
1204
- //
1205
- // Wrap <p> tags.
1206
- //
1207
- var end = grafs.length;
1208
- for (var i=0; i<end; i++) {
1209
- var str = grafs[i];
1210
-
1211
- // if this is an HTML marker, copy it
1212
- if (str.search(/~K(\d+)K/g) >= 0) {
1213
- grafsOut.push(str);
1214
- }
1215
- else if (str.search(/\S/) >= 0) {
1216
- str = _RunSpanGamut(str);
1217
- str = str.replace(/^([ \t]*)/g,"<p>");
1218
- str += "</p>"
1219
- grafsOut.push(str);
1220
- }
1221
-
1222
- }
1223
-
1224
- //
1225
- // Unhashify HTML blocks
1226
- //
1227
- end = grafsOut.length;
1228
- for (var i=0; i<end; i++) {
1229
- // if this is a marker for an html block...
1230
- while (grafsOut[i].search(/~K(\d+)K/) >= 0) {
1231
- var blockText = g_html_blocks[RegExp.$1];
1232
- blockText = blockText.replace(/\$/g,"$$$$"); // Escape any dollar signs
1233
- grafsOut[i] = grafsOut[i].replace(/~K\d+K/,blockText);
1234
- }
1235
- }
1236
-
1237
- return grafsOut.join("\n\n");
1238
- }
1239
-
1240
-
1241
- var _EncodeAmpsAndAngles = function(text) {
1242
- // Smart processing for ampersands and angle brackets that need to be encoded.
1243
-
1244
- // Ampersand-encoding based entirely on Nat Irons's Amputator MT plugin:
1245
- // http://bumppo.net/projects/amputator/
1246
- text = text.replace(/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/g,"&amp;");
1247
-
1248
- // Encode naked <'s
1249
- text = text.replace(/<(?![a-z\/?\$!])/gi,"&lt;");
1250
-
1251
- return text;
1252
- }
1253
-
1254
-
1255
- var _EncodeBackslashEscapes = function(text) {
1256
- //
1257
- // Parameter: String.
1258
- // Returns: The string, with after processing the following backslash
1259
- // escape sequences.
1260
- //
1261
-
1262
- // attacklab: The polite way to do this is with the new
1263
- // escapeCharacters() function:
1264
- //
1265
- // text = escapeCharacters(text,"\\",true);
1266
- // text = escapeCharacters(text,"`*_{}[]()>#+-.!",true);
1267
- //
1268
- // ...but we're sidestepping its use of the (slow) RegExp constructor
1269
- // as an optimization for Firefox. This function gets called a LOT.
1270
-
1271
- text = text.replace(/\\(\\)/g,escapeCharacters_callback);
1272
- text = text.replace(/\\([`*_{}\[\]()>#+-.!])/g,escapeCharacters_callback);
1273
- return text;
1274
- }
1275
-
1276
-
1277
- var _DoAutoLinks = function(text) {
293
+ /**
294
+ * Gets or registers an extension
295
+ * @static
296
+ * @param {string} name
297
+ * @param {object|function=} ext
298
+ * @returns {*}
299
+ */
300
+ showdown.extension = function (name, ext) {
301
+ 'use strict';
302
+
303
+ if (!showdown.helper.isString(name)) {
304
+ throw Error('Extension \'name\' must be a string');
305
+ }
306
+
307
+ name = showdown.helper.stdExtName(name);
308
+
309
+ // Getter
310
+ if (showdown.helper.isUndefined(ext)) {
311
+ if (!extensions.hasOwnProperty(name)) {
312
+ throw Error('Extension named ' + name + ' is not registered!');
313
+ }
314
+ return extensions[name];
315
+
316
+ // Setter
317
+ } else {
318
+ // Expand extension if it's wrapped in a function
319
+ if (typeof ext === 'function') {
320
+ ext = ext();
321
+ }
322
+
323
+ // Ensure extension is an array
324
+ if (!showdown.helper.isArray(ext)) {
325
+ ext = [ext];
326
+ }
327
+
328
+ var validExtension = validate(ext, name);
329
+
330
+ if (validExtension.valid) {
331
+ extensions[name] = ext;
332
+ } else {
333
+ throw Error(validExtension.error);
334
+ }
335
+ }
336
+ };
1278
337
 
1279
- text = text.replace(/<((https?|ftp|dict):[^'">\s]+)>/gi,"<a href=\"$1\">$1</a>");
338
+ /**
339
+ * Gets all extensions registered
340
+ * @returns {{}}
341
+ */
342
+ showdown.getAllExtensions = function () {
343
+ 'use strict';
344
+ return extensions;
345
+ };
1280
346
 
1281
- // Email addresses: <address@domain.foo>
347
+ /**
348
+ * Remove an extension
349
+ * @param {string} name
350
+ */
351
+ showdown.removeExtension = function (name) {
352
+ 'use strict';
353
+ delete extensions[name];
354
+ };
1282
355
 
1283
- /*
1284
- text = text.replace(/
1285
- <
1286
- (?:mailto:)?
1287
- (
1288
- [-.\w]+
1289
- \@
1290
- [-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+
1291
- )
1292
- >
1293
- /gi, _DoAutoLinks_callback());
1294
- */
1295
- text = text.replace(/<(?:mailto:)?([-.\w]+\@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi,
1296
- function(wholeMatch,m1) {
1297
- return _EncodeEmailAddress( _UnescapeSpecialChars(m1) );
1298
- }
1299
- );
356
+ /**
357
+ * Removes all extensions
358
+ */
359
+ showdown.resetExtensions = function () {
360
+ 'use strict';
361
+ extensions = {};
362
+ };
1300
363
 
1301
- return text;
364
+ /**
365
+ * Validate extension
366
+ * @param {array} extension
367
+ * @param {string} name
368
+ * @returns {{valid: boolean, error: string}}
369
+ */
370
+ function validate(extension, name) {
371
+ 'use strict';
372
+
373
+ var errMsg = (name) ? 'Error in ' + name + ' extension->' : 'Error in unnamed extension',
374
+ ret = {
375
+ valid: true,
376
+ error: ''
377
+ };
378
+
379
+ if (!showdown.helper.isArray(extension)) {
380
+ extension = [extension];
381
+ }
382
+
383
+ for (var i = 0; i < extension.length; ++i) {
384
+ var baseMsg = errMsg + ' sub-extension ' + i + ': ',
385
+ ext = extension[i];
386
+ if (typeof ext !== 'object') {
387
+ ret.valid = false;
388
+ ret.error = baseMsg + 'must be an object, but ' + typeof ext + ' given';
389
+ return ret;
390
+ }
391
+
392
+ if (!showdown.helper.isString(ext.type)) {
393
+ ret.valid = false;
394
+ ret.error = baseMsg + 'property "type" must be a string, but ' + typeof ext.type + ' given';
395
+ return ret;
396
+ }
397
+
398
+ var type = ext.type = ext.type.toLowerCase();
399
+
400
+ // normalize extension type
401
+ if (type === 'language') {
402
+ type = ext.type = 'lang';
403
+ }
404
+
405
+ if (type === 'html') {
406
+ type = ext.type = 'output';
407
+ }
408
+
409
+ if (type !== 'lang' && type !== 'output' && type !== 'listener') {
410
+ ret.valid = false;
411
+ ret.error = baseMsg + 'type ' + type + ' is not recognized. Valid values: "lang/language", "output/html" or "listener"';
412
+ return ret;
413
+ }
414
+
415
+ if (type === 'listener') {
416
+ if (showdown.helper.isUndefined(ext.listeners)) {
417
+ ret.valid = false;
418
+ ret.error = baseMsg + '. Extensions of type "listener" must have a property called "listeners"';
419
+ return ret;
420
+ }
421
+ } else {
422
+ if (showdown.helper.isUndefined(ext.filter) && showdown.helper.isUndefined(ext.regex)) {
423
+ ret.valid = false;
424
+ ret.error = baseMsg + type + ' extensions must define either a "regex" property or a "filter" method';
425
+ return ret;
426
+ }
427
+ }
428
+
429
+ if (ext.listeners) {
430
+ if (typeof ext.listeners !== 'object') {
431
+ ret.valid = false;
432
+ ret.error = baseMsg + '"listeners" property must be an object but ' + typeof ext.listeners + ' given';
433
+ return ret;
434
+ }
435
+ for (var ln in ext.listeners) {
436
+ if (ext.listeners.hasOwnProperty(ln)) {
437
+ if (typeof ext.listeners[ln] !== 'function') {
438
+ ret.valid = false;
439
+ ret.error = baseMsg + '"listeners" property must be an hash of [event name]: [callback]. listeners.' + ln +
440
+ ' must be a function but ' + typeof ext.listeners[ln] + ' given';
441
+ return ret;
442
+ }
443
+ }
444
+ }
445
+ }
446
+
447
+ if (ext.filter) {
448
+ if (typeof ext.filter !== 'function') {
449
+ ret.valid = false;
450
+ ret.error = baseMsg + '"filter" must be a function, but ' + typeof ext.filter + ' given';
451
+ return ret;
452
+ }
453
+ } else if (ext.regex) {
454
+ if (showdown.helper.isString(ext.regex)) {
455
+ ext.regex = new RegExp(ext.regex, 'g');
456
+ }
457
+ if (!ext.regex instanceof RegExp) {
458
+ ret.valid = false;
459
+ ret.error = baseMsg + '"regex" property must either be a string or a RegExp object, but ' + typeof ext.regex + ' given';
460
+ return ret;
461
+ }
462
+ if (showdown.helper.isUndefined(ext.replace)) {
463
+ ret.valid = false;
464
+ ret.error = baseMsg + '"regex" extensions must implement a replace string or function';
465
+ return ret;
466
+ }
467
+ }
468
+ }
469
+ return ret;
1302
470
  }
1303
471
 
472
+ /**
473
+ * Validate extension
474
+ * @param {object} ext
475
+ * @returns {boolean}
476
+ */
477
+ showdown.validateExtension = function (ext) {
478
+ 'use strict';
479
+
480
+ var validateExtension = validate(ext, null);
481
+ if (!validateExtension.valid) {
482
+ console.warn(validateExtension.error);
483
+ return false;
484
+ }
485
+ return true;
486
+ };
1304
487
 
1305
- var _EncodeEmailAddress = function(addr) {
1306
- //
1307
- // Input: an email address, e.g. "foo@example.com"
1308
- //
1309
- // Output: the email address as a mailto link, with each character
1310
- // of the address encoded as either a decimal or hex entity, in
1311
- // the hopes of foiling most address harvesting spam bots. E.g.:
1312
- //
1313
- // <a href="&#x6D;&#97;&#105;&#108;&#x74;&#111;:&#102;&#111;&#111;&#64;&#101;
1314
- // x&#x61;&#109;&#x70;&#108;&#x65;&#x2E;&#99;&#111;&#109;">&#102;&#111;&#111;
1315
- // &#64;&#101;x&#x61;&#109;&#x70;&#108;&#x65;&#x2E;&#99;&#111;&#109;</a>
1316
- //
1317
- // Based on a filter by Matthew Wickline, posted to the BBEdit-Talk
1318
- // mailing list: <http://tinyurl.com/yu7ue>
1319
- //
488
+ /**
489
+ * showdownjs helper functions
490
+ */
1320
491
 
1321
- var encode = [
1322
- function(ch){return "&#"+ch.charCodeAt(0)+";";},
1323
- function(ch){return "&#x"+ch.charCodeAt(0).toString(16)+";";},
1324
- function(ch){return ch;}
1325
- ];
1326
-
1327
- addr = "mailto:" + addr;
1328
-
1329
- addr = addr.replace(/./g, function(ch) {
1330
- if (ch == "@") {
1331
- // this *must* be encoded. I insist.
1332
- ch = encode[Math.floor(Math.random()*2)](ch);
1333
- } else if (ch !=":") {
1334
- // leave ':' alone (to spot mailto: later)
1335
- var r = Math.random();
1336
- // roughly 10% raw, 45% hex, 45% dec
1337
- ch = (
1338
- r > .9 ? encode[2](ch) :
1339
- r > .45 ? encode[1](ch) :
1340
- encode[0](ch)
1341
- );
1342
- }
1343
- return ch;
1344
- });
1345
-
1346
- addr = "<a href=\"" + addr + "\">" + addr + "</a>";
1347
- addr = addr.replace(/">.+:/g,"\">"); // strip the mailto: from the visible part
1348
-
1349
- return addr;
492
+ if (!showdown.hasOwnProperty('helper')) {
493
+ showdown.helper = {};
1350
494
  }
1351
495
 
496
+ /**
497
+ * Check if var is string
498
+ * @static
499
+ * @param {string} a
500
+ * @returns {boolean}
501
+ */
502
+ showdown.helper.isString = function isString(a) {
503
+ 'use strict';
504
+ return (typeof a === 'string' || a instanceof String);
505
+ };
1352
506
 
1353
- var _UnescapeSpecialChars = function(text) {
1354
- //
1355
- // Swap back in all the special characters we've hidden.
1356
- //
1357
- text = text.replace(/~E(\d+)E/g,
1358
- function(wholeMatch,m1) {
1359
- var charCodeToReplace = parseInt(m1);
1360
- return String.fromCharCode(charCodeToReplace);
1361
- }
1362
- );
1363
- return text;
1364
- }
1365
-
507
+ /**
508
+ * Check if var is a function
509
+ * @static
510
+ * @param {string} a
511
+ * @returns {boolean}
512
+ */
513
+ showdown.helper.isFunction = function isFunction(a) {
514
+ 'use strict';
515
+ var getType = {};
516
+ return a && getType.toString.call(a) === '[object Function]';
517
+ };
1366
518
 
1367
- var _Outdent = function(text) {
1368
- //
1369
- // Remove one level of line-leading tabs or spaces
1370
- //
519
+ /**
520
+ * ForEach helper function
521
+ * @static
522
+ * @param {*} obj
523
+ * @param {function} callback
524
+ */
525
+ showdown.helper.forEach = function forEach(obj, callback) {
526
+ 'use strict';
527
+ if (typeof obj.forEach === 'function') {
528
+ obj.forEach(callback);
529
+ } else {
530
+ for (var i = 0; i < obj.length; i++) {
531
+ callback(obj[i], i, obj);
532
+ }
533
+ }
534
+ };
1371
535
 
1372
- // attacklab: hack around Konqueror 3.5.4 bug:
1373
- // "----------bug".replace(/^-/g,"") == "bug"
536
+ /**
537
+ * isArray helper function
538
+ * @static
539
+ * @param {*} a
540
+ * @returns {boolean}
541
+ */
542
+ showdown.helper.isArray = function isArray(a) {
543
+ 'use strict';
544
+ return a.constructor === Array;
545
+ };
1374
546
 
1375
- text = text.replace(/^(\t|[ ]{1,4})/gm,"~0"); // attacklab: g_tab_width
547
+ /**
548
+ * Check if value is undefined
549
+ * @static
550
+ * @param {*} value The value to check.
551
+ * @returns {boolean} Returns `true` if `value` is `undefined`, else `false`.
552
+ */
553
+ showdown.helper.isUndefined = function isUndefined(value) {
554
+ 'use strict';
555
+ return typeof value === 'undefined';
556
+ };
1376
557
 
1377
- // attacklab: clean up hack
1378
- text = text.replace(/~0/g,"")
558
+ /**
559
+ * Standardidize extension name
560
+ * @static
561
+ * @param {string} s extension name
562
+ * @returns {string}
563
+ */
564
+ showdown.helper.stdExtName = function (s) {
565
+ 'use strict';
566
+ return s.replace(/[_-]||\s/g, '').toLowerCase();
567
+ };
1379
568
 
1380
- return text;
569
+ function escapeCharactersCallback(wholeMatch, m1) {
570
+ 'use strict';
571
+ var charCodeToEscape = m1.charCodeAt(0);
572
+ return '~E' + charCodeToEscape + 'E';
1381
573
  }
1382
574
 
1383
- var _Detab = function(text) {
1384
- // attacklab: Detab's completely rewritten for speed.
1385
- // In perl we could fix it by anchoring the regexp with \G.
1386
- // In javascript we're less fortunate.
1387
-
1388
- // expand first n-1 tabs
1389
- text = text.replace(/\t(?=\t)/g," "); // attacklab: g_tab_width
1390
-
1391
- // replace the nth with two sentinels
1392
- text = text.replace(/\t/g,"~A~B");
1393
-
1394
- // use the sentinel to anchor our regex so it doesn't explode
1395
- text = text.replace(/~B(.+?)~A/g,
1396
- function(wholeMatch,m1,m2) {
1397
- var leadingText = m1;
1398
- var numSpaces = 4 - leadingText.length % 4; // attacklab: g_tab_width
575
+ /**
576
+ * Callback used to escape characters when passing through String.replace
577
+ * @static
578
+ * @param {string} wholeMatch
579
+ * @param {string} m1
580
+ * @returns {string}
581
+ */
582
+ showdown.helper.escapeCharactersCallback = escapeCharactersCallback;
583
+
584
+ /**
585
+ * Escape characters in a string
586
+ * @static
587
+ * @param {string} text
588
+ * @param {string} charsToEscape
589
+ * @param {boolean} afterBackslash
590
+ * @returns {XML|string|void|*}
591
+ */
592
+ showdown.helper.escapeCharacters = function escapeCharacters(text, charsToEscape, afterBackslash) {
593
+ 'use strict';
594
+ // First we have to escape the escape characters so that
595
+ // we can build a character class out of them
596
+ var regexString = '([' + charsToEscape.replace(/([\[\]\\])/g, '\\$1') + '])';
597
+
598
+ if (afterBackslash) {
599
+ regexString = '\\\\' + regexString;
600
+ }
601
+
602
+ var regex = new RegExp(regexString, 'g');
603
+ text = text.replace(regex, escapeCharactersCallback);
604
+
605
+ return text;
606
+ };
1399
607
 
1400
- // there *must* be a better way to do this:
1401
- for (var i=0; i<numSpaces; i++) leadingText+=" ";
608
+ var rgxFindMatchPos = function (str, left, right, flags) {
609
+ 'use strict';
610
+ var f = flags || '',
611
+ g = f.indexOf('g') > -1,
612
+ x = new RegExp(left + '|' + right, 'g' + f.replace(/g/g, '')),
613
+ l = new RegExp(left, f.replace(/g/g, '')),
614
+ pos = [],
615
+ t, s, m, start, end;
616
+
617
+ do {
618
+ t = 0;
619
+ while ((m = x.exec(str))) {
620
+ if (l.test(m[0])) {
621
+ if (!(t++)) {
622
+ s = x.lastIndex;
623
+ start = s - m[0].length;
624
+ }
625
+ } else if (t) {
626
+ if (!--t) {
627
+ end = m.index + m[0].length;
628
+ var obj = {
629
+ left: {start: start, end: s},
630
+ match: {start: s, end: m.index},
631
+ right: {start: m.index, end: end},
632
+ wholeMatch: {start: start, end: end}
633
+ };
634
+ pos.push(obj);
635
+ if (!g) {
636
+ return pos;
637
+ }
638
+ }
639
+ }
640
+ }
641
+ } while (t && (x.lastIndex = s));
642
+
643
+ return pos;
644
+ };
1402
645
 
1403
- return leadingText;
1404
- }
1405
- );
646
+ /**
647
+ * matchRecursiveRegExp
648
+ *
649
+ * (c) 2007 Steven Levithan <stevenlevithan.com>
650
+ * MIT License
651
+ *
652
+ * Accepts a string to search, a left and right format delimiter
653
+ * as regex patterns, and optional regex flags. Returns an array
654
+ * of matches, allowing nested instances of left/right delimiters.
655
+ * Use the "g" flag to return all matches, otherwise only the
656
+ * first is returned. Be careful to ensure that the left and
657
+ * right format delimiters produce mutually exclusive matches.
658
+ * Backreferences are not supported within the right delimiter
659
+ * due to how it is internally combined with the left delimiter.
660
+ * When matching strings whose format delimiters are unbalanced
661
+ * to the left or right, the output is intentionally as a
662
+ * conventional regex library with recursion support would
663
+ * produce, e.g. "<<x>" and "<x>>" both produce ["x"] when using
664
+ * "<" and ">" as the delimiters (both strings contain a single,
665
+ * balanced instance of "<x>").
666
+ *
667
+ * examples:
668
+ * matchRecursiveRegExp("test", "\\(", "\\)")
669
+ * returns: []
670
+ * matchRecursiveRegExp("<t<<e>><s>>t<>", "<", ">", "g")
671
+ * returns: ["t<<e>><s>", ""]
672
+ * matchRecursiveRegExp("<div id=\"x\">test</div>", "<div\\b[^>]*>", "</div>", "gi")
673
+ * returns: ["test"]
674
+ */
675
+ showdown.helper.matchRecursiveRegExp = function (str, left, right, flags) {
676
+ 'use strict';
677
+
678
+ var matchPos = rgxFindMatchPos (str, left, right, flags),
679
+ results = [];
680
+
681
+ for (var i = 0; i < matchPos.length; ++i) {
682
+ results.push([
683
+ str.slice(matchPos[i].wholeMatch.start, matchPos[i].wholeMatch.end),
684
+ str.slice(matchPos[i].match.start, matchPos[i].match.end),
685
+ str.slice(matchPos[i].left.start, matchPos[i].left.end),
686
+ str.slice(matchPos[i].right.start, matchPos[i].right.end)
687
+ ]);
688
+ }
689
+ return results;
690
+ };
1406
691
 
1407
- // clean up sentinels
1408
- text = text.replace(/~A/g," "); // attacklab: g_tab_width
1409
- text = text.replace(/~B/g,"");
692
+ /**
693
+ *
694
+ * @param {string} str
695
+ * @param {string|function} replacement
696
+ * @param {string} left
697
+ * @param {string} right
698
+ * @param {string} flags
699
+ * @returns {string}
700
+ */
701
+ showdown.helper.replaceRecursiveRegExp = function (str, replacement, left, right, flags) {
702
+ 'use strict';
703
+
704
+ if (!showdown.helper.isFunction(replacement)) {
705
+ var repStr = replacement;
706
+ replacement = function () {
707
+ return repStr;
708
+ };
709
+ }
710
+
711
+ var matchPos = rgxFindMatchPos(str, left, right, flags),
712
+ finalStr = str,
713
+ lng = matchPos.length;
714
+
715
+ if (lng > 0) {
716
+ var bits = [];
717
+ if (matchPos[0].wholeMatch.start !== 0) {
718
+ bits.push(str.slice(0, matchPos[0].wholeMatch.start));
719
+ }
720
+ for (var i = 0; i < lng; ++i) {
721
+ bits.push(
722
+ replacement(
723
+ str.slice(matchPos[i].wholeMatch.start, matchPos[i].wholeMatch.end),
724
+ str.slice(matchPos[i].match.start, matchPos[i].match.end),
725
+ str.slice(matchPos[i].left.start, matchPos[i].left.end),
726
+ str.slice(matchPos[i].right.start, matchPos[i].right.end)
727
+ )
728
+ );
729
+ if (i < lng - 1) {
730
+ bits.push(str.slice(matchPos[i].wholeMatch.end, matchPos[i + 1].wholeMatch.start));
731
+ }
732
+ }
733
+ if (matchPos[lng - 1].wholeMatch.end < str.length) {
734
+ bits.push(str.slice(matchPos[lng - 1].wholeMatch.end));
735
+ }
736
+ finalStr = bits.join('');
737
+ }
738
+ return finalStr;
739
+ };
1410
740
 
1411
- return text;
741
+ /**
742
+ * POLYFILLS
743
+ */
744
+ if (showdown.helper.isUndefined(console)) {
745
+ console = {
746
+ warn: function (msg) {
747
+ 'use strict';
748
+ alert(msg);
749
+ },
750
+ log: function (msg) {
751
+ 'use strict';
752
+ alert(msg);
753
+ },
754
+ error: function (msg) {
755
+ 'use strict';
756
+ throw msg;
757
+ }
758
+ };
1412
759
  }
1413
760
 
761
+ /**
762
+ * Created by Estevao on 31-05-2015.
763
+ */
764
+
765
+ /**
766
+ * Showdown Converter class
767
+ * @class
768
+ * @param {object} [converterOptions]
769
+ * @returns {Converter}
770
+ */
771
+ showdown.Converter = function (converterOptions) {
772
+ 'use strict';
773
+
774
+ var
775
+ /**
776
+ * Options used by this converter
777
+ * @private
778
+ * @type {{}}
779
+ */
780
+ options = {},
781
+
782
+ /**
783
+ * Language extensions used by this converter
784
+ * @private
785
+ * @type {Array}
786
+ */
787
+ langExtensions = [],
788
+
789
+ /**
790
+ * Output modifiers extensions used by this converter
791
+ * @private
792
+ * @type {Array}
793
+ */
794
+ outputModifiers = [],
795
+
796
+ /**
797
+ * Event listeners
798
+ * @private
799
+ * @type {{}}
800
+ */
801
+ listeners = {};
802
+
803
+ _constructor();
804
+
805
+ /**
806
+ * Converter constructor
807
+ * @private
808
+ */
809
+ function _constructor() {
810
+ converterOptions = converterOptions || {};
811
+
812
+ for (var gOpt in globalOptions) {
813
+ if (globalOptions.hasOwnProperty(gOpt)) {
814
+ options[gOpt] = globalOptions[gOpt];
815
+ }
816
+ }
817
+
818
+ // Merge options
819
+ if (typeof converterOptions === 'object') {
820
+ for (var opt in converterOptions) {
821
+ if (converterOptions.hasOwnProperty(opt)) {
822
+ options[opt] = converterOptions[opt];
823
+ }
824
+ }
825
+ } else {
826
+ throw Error('Converter expects the passed parameter to be an object, but ' + typeof converterOptions +
827
+ ' was passed instead.');
828
+ }
829
+
830
+ if (options.extensions) {
831
+ showdown.helper.forEach(options.extensions, _parseExtension);
832
+ }
833
+ }
834
+
835
+ /**
836
+ * Parse extension
837
+ * @param {*} ext
838
+ * @param {string} [name='']
839
+ * @private
840
+ */
841
+ function _parseExtension(ext, name) {
842
+
843
+ name = name || null;
844
+ // If it's a string, the extension was previously loaded
845
+ if (showdown.helper.isString(ext)) {
846
+ ext = showdown.helper.stdExtName(ext);
847
+ name = ext;
848
+
849
+ // LEGACY_SUPPORT CODE
850
+ if (showdown.extensions[ext]) {
851
+ console.warn('DEPRECATION WARNING: ' + ext + ' is an old extension that uses a deprecated loading method.' +
852
+ 'Please inform the developer that the extension should be updated!');
853
+ legacyExtensionLoading(showdown.extensions[ext], ext);
854
+ return;
855
+ // END LEGACY SUPPORT CODE
856
+
857
+ } else if (!showdown.helper.isUndefined(extensions[ext])) {
858
+ ext = extensions[ext];
859
+
860
+ } else {
861
+ throw Error('Extension "' + ext + '" could not be loaded. It was either not found or is not a valid extension.');
862
+ }
863
+ }
864
+
865
+ if (typeof ext === 'function') {
866
+ ext = ext();
867
+ }
868
+
869
+ if (!showdown.helper.isArray(ext)) {
870
+ ext = [ext];
871
+ }
872
+
873
+ var validExt = validate(ext, name);
874
+ if (!validExt.valid) {
875
+ throw Error(validExt.error);
876
+ }
877
+
878
+ for (var i = 0; i < ext.length; ++i) {
879
+ switch (ext[i].type) {
880
+
881
+ case 'lang':
882
+ langExtensions.push(ext[i]);
883
+ break;
884
+
885
+ case 'output':
886
+ outputModifiers.push(ext[i]);
887
+ break;
888
+ }
889
+ if (ext[i].hasOwnProperty(listeners)) {
890
+ for (var ln in ext[i].listeners) {
891
+ if (ext[i].listeners.hasOwnProperty(ln)) {
892
+ listen(ln, ext[i].listeners[ln]);
893
+ }
894
+ }
895
+ }
896
+ }
897
+
898
+ }
899
+
900
+ /**
901
+ * LEGACY_SUPPORT
902
+ * @param {*} ext
903
+ * @param {string} name
904
+ */
905
+ function legacyExtensionLoading(ext, name) {
906
+ if (typeof ext === 'function') {
907
+ ext = ext(new showdown.Converter());
908
+ }
909
+ if (!showdown.helper.isArray(ext)) {
910
+ ext = [ext];
911
+ }
912
+ var valid = validate(ext, name);
913
+
914
+ if (!valid.valid) {
915
+ throw Error(valid.error);
916
+ }
917
+
918
+ for (var i = 0; i < ext.length; ++i) {
919
+ switch (ext[i].type) {
920
+ case 'lang':
921
+ langExtensions.push(ext[i]);
922
+ break;
923
+ case 'output':
924
+ outputModifiers.push(ext[i]);
925
+ break;
926
+ default:// should never reach here
927
+ throw Error('Extension loader error: Type unrecognized!!!');
928
+ }
929
+ }
930
+ }
931
+
932
+ /**
933
+ * Listen to an event
934
+ * @param {string} name
935
+ * @param {function} callback
936
+ */
937
+ function listen(name, callback) {
938
+ if (!showdown.helper.isString(name)) {
939
+ throw Error('Invalid argument in converter.listen() method: name must be a string, but ' + typeof name + ' given');
940
+ }
941
+
942
+ if (typeof callback !== 'function') {
943
+ throw Error('Invalid argument in converter.listen() method: callback must be a function, but ' + typeof callback + ' given');
944
+ }
945
+
946
+ if (!listeners.hasOwnProperty(name)) {
947
+ listeners[name] = [];
948
+ }
949
+ listeners[name].push(callback);
950
+ }
951
+
952
+ function rTrimInputText(text) {
953
+ var rsp = text.match(/^\s*/)[0].length,
954
+ rgx = new RegExp('^\\s{0,' + rsp + '}', 'gm');
955
+ return text.replace(rgx, '');
956
+ }
957
+
958
+ /**
959
+ * Dispatch an event
960
+ * @private
961
+ * @param {string} evtName Event name
962
+ * @param {string} text Text
963
+ * @param {{}} options Converter Options
964
+ * @param {{}} globals
965
+ * @returns {string}
966
+ */
967
+ this._dispatch = function dispatch (evtName, text, options, globals) {
968
+ if (listeners.hasOwnProperty(evtName)) {
969
+ for (var ei = 0; ei < listeners[evtName].length; ++ei) {
970
+ var nText = listeners[evtName][ei](evtName, text, this, options, globals);
971
+ if (nText && typeof nText !== 'undefined') {
972
+ text = nText;
973
+ }
974
+ }
975
+ }
976
+ return text;
977
+ };
978
+
979
+ /**
980
+ * Listen to an event
981
+ * @param {string} name
982
+ * @param {function} callback
983
+ * @returns {showdown.Converter}
984
+ */
985
+ this.listen = function (name, callback) {
986
+ listen(name, callback);
987
+ return this;
988
+ };
989
+
990
+ /**
991
+ * Converts a markdown string into HTML
992
+ * @param {string} text
993
+ * @returns {*}
994
+ */
995
+ this.makeHtml = function (text) {
996
+ //check if text is not falsy
997
+ if (!text) {
998
+ return text;
999
+ }
1000
+
1001
+ var globals = {
1002
+ gHtmlBlocks: [],
1003
+ gHtmlMdBlocks: [],
1004
+ gHtmlSpans: [],
1005
+ gUrls: {},
1006
+ gTitles: {},
1007
+ gDimensions: {},
1008
+ gListLevel: 0,
1009
+ hashLinkCounts: {},
1010
+ langExtensions: langExtensions,
1011
+ outputModifiers: outputModifiers,
1012
+ converter: this,
1013
+ ghCodeBlocks: []
1014
+ };
1015
+
1016
+ // attacklab: Replace ~ with ~T
1017
+ // This lets us use tilde as an escape char to avoid md5 hashes
1018
+ // The choice of character is arbitrary; anything that isn't
1019
+ // magic in Markdown will work.
1020
+ text = text.replace(/~/g, '~T');
1021
+
1022
+ // attacklab: Replace $ with ~D
1023
+ // RegExp interprets $ as a special character
1024
+ // when it's in a replacement string
1025
+ text = text.replace(/\$/g, '~D');
1026
+
1027
+ // Standardize line endings
1028
+ text = text.replace(/\r\n/g, '\n'); // DOS to Unix
1029
+ text = text.replace(/\r/g, '\n'); // Mac to Unix
1030
+
1031
+ if (options.smartIndentationFix) {
1032
+ text = rTrimInputText(text);
1033
+ }
1034
+
1035
+ // Make sure text begins and ends with a couple of newlines:
1036
+ text = '\n\n' + text + '\n\n';
1037
+
1038
+ // detab
1039
+ text = showdown.subParser('detab')(text, options, globals);
1040
+
1041
+ // stripBlankLines
1042
+ text = showdown.subParser('stripBlankLines')(text, options, globals);
1043
+
1044
+ //run languageExtensions
1045
+ showdown.helper.forEach(langExtensions, function (ext) {
1046
+ text = showdown.subParser('runExtension')(ext, text, options, globals);
1047
+ });
1414
1048
 
1415
- //
1416
- // attacklab: Utility functions
1417
- //
1418
-
1419
-
1420
- var escapeCharacters = function(text, charsToEscape, afterBackslash) {
1421
- // First we have to escape the escape characters so that
1422
- // we can build a character class out of them
1423
- var regexString = "([" + charsToEscape.replace(/([\[\]\\])/g,"\\$1") + "])";
1049
+ // run the sub parsers
1050
+ text = showdown.subParser('hashPreCodeTags')(text, options, globals);
1051
+ text = showdown.subParser('githubCodeBlocks')(text, options, globals);
1052
+ text = showdown.subParser('hashHTMLBlocks')(text, options, globals);
1053
+ text = showdown.subParser('hashHTMLSpans')(text, options, globals);
1054
+ text = showdown.subParser('stripLinkDefinitions')(text, options, globals);
1055
+ text = showdown.subParser('blockGamut')(text, options, globals);
1056
+ text = showdown.subParser('unhashHTMLSpans')(text, options, globals);
1057
+ text = showdown.subParser('unescapeSpecialChars')(text, options, globals);
1058
+
1059
+ // attacklab: Restore dollar signs
1060
+ text = text.replace(/~D/g, '$$');
1061
+
1062
+ // attacklab: Restore tildes
1063
+ text = text.replace(/~T/g, '~');
1064
+
1065
+ // Run output modifiers
1066
+ showdown.helper.forEach(outputModifiers, function (ext) {
1067
+ text = showdown.subParser('runExtension')(ext, text, options, globals);
1068
+ });
1424
1069
 
1425
- if (afterBackslash) {
1426
- regexString = "\\\\" + regexString;
1427
- }
1070
+ return text;
1071
+ };
1072
+
1073
+ /**
1074
+ * Set an option of this Converter instance
1075
+ * @param {string} key
1076
+ * @param {*} value
1077
+ */
1078
+ this.setOption = function (key, value) {
1079
+ options[key] = value;
1080
+ };
1081
+
1082
+ /**
1083
+ * Get the option of this Converter instance
1084
+ * @param {string} key
1085
+ * @returns {*}
1086
+ */
1087
+ this.getOption = function (key) {
1088
+ return options[key];
1089
+ };
1090
+
1091
+ /**
1092
+ * Get the options of this Converter instance
1093
+ * @returns {{}}
1094
+ */
1095
+ this.getOptions = function () {
1096
+ return options;
1097
+ };
1098
+
1099
+ /**
1100
+ * Add extension to THIS converter
1101
+ * @param {{}} extension
1102
+ * @param {string} [name=null]
1103
+ */
1104
+ this.addExtension = function (extension, name) {
1105
+ name = name || null;
1106
+ _parseExtension(extension, name);
1107
+ };
1108
+
1109
+ /**
1110
+ * Use a global registered extension with THIS converter
1111
+ * @param {string} extensionName Name of the previously registered extension
1112
+ */
1113
+ this.useExtension = function (extensionName) {
1114
+ _parseExtension(extensionName);
1115
+ };
1116
+
1117
+ /**
1118
+ * Set the flavor THIS converter should use
1119
+ * @param {string} name
1120
+ */
1121
+ this.setFlavor = function (name) {
1122
+ if (flavor.hasOwnProperty(name)) {
1123
+ var preset = flavor[name];
1124
+ for (var option in preset) {
1125
+ if (preset.hasOwnProperty(option)) {
1126
+ options[option] = preset[option];
1127
+ }
1128
+ }
1129
+ }
1130
+ };
1131
+
1132
+ /**
1133
+ * Remove an extension from THIS converter.
1134
+ * Note: This is a costly operation. It's better to initialize a new converter
1135
+ * and specify the extensions you wish to use
1136
+ * @param {Array} extension
1137
+ */
1138
+ this.removeExtension = function (extension) {
1139
+ if (!showdown.helper.isArray(extension)) {
1140
+ extension = [extension];
1141
+ }
1142
+ for (var a = 0; a < extension.length; ++a) {
1143
+ var ext = extension[a];
1144
+ for (var i = 0; i < langExtensions.length; ++i) {
1145
+ if (langExtensions[i] === ext) {
1146
+ langExtensions[i].splice(i, 1);
1147
+ }
1148
+ }
1149
+ for (var ii = 0; ii < outputModifiers.length; ++i) {
1150
+ if (outputModifiers[ii] === ext) {
1151
+ outputModifiers[ii].splice(i, 1);
1152
+ }
1153
+ }
1154
+ }
1155
+ };
1156
+
1157
+ /**
1158
+ * Get all extension of THIS converter
1159
+ * @returns {{language: Array, output: Array}}
1160
+ */
1161
+ this.getAllExtensions = function () {
1162
+ return {
1163
+ language: langExtensions,
1164
+ output: outputModifiers
1165
+ };
1166
+ };
1167
+ };
1428
1168
 
1429
- var regex = new RegExp(regexString,"g");
1430
- text = text.replace(regex,escapeCharacters_callback);
1169
+ /**
1170
+ * Turn Markdown link shortcuts into XHTML <a> tags.
1171
+ */
1172
+ showdown.subParser('anchors', function (text, options, globals) {
1173
+ 'use strict';
1174
+
1175
+ text = globals.converter._dispatch('anchors.before', text, options, globals);
1176
+
1177
+ var writeAnchorTag = function (wholeMatch, m1, m2, m3, m4, m5, m6, m7) {
1178
+ if (showdown.helper.isUndefined(m7)) {
1179
+ m7 = '';
1180
+ }
1181
+ wholeMatch = m1;
1182
+ var linkText = m2,
1183
+ linkId = m3.toLowerCase(),
1184
+ url = m4,
1185
+ title = m7;
1186
+
1187
+ if (!url) {
1188
+ if (!linkId) {
1189
+ // lower-case and turn embedded newlines into spaces
1190
+ linkId = linkText.toLowerCase().replace(/ ?\n/g, ' ');
1191
+ }
1192
+ url = '#' + linkId;
1193
+
1194
+ if (!showdown.helper.isUndefined(globals.gUrls[linkId])) {
1195
+ url = globals.gUrls[linkId];
1196
+ if (!showdown.helper.isUndefined(globals.gTitles[linkId])) {
1197
+ title = globals.gTitles[linkId];
1198
+ }
1199
+ } else {
1200
+ if (wholeMatch.search(/\(\s*\)$/m) > -1) {
1201
+ // Special case for explicit empty url
1202
+ url = '';
1203
+ } else {
1204
+ return wholeMatch;
1205
+ }
1206
+ }
1207
+ }
1208
+
1209
+ url = showdown.helper.escapeCharacters(url, '*_', false);
1210
+ var result = '<a href="' + url + '"';
1211
+
1212
+ if (title !== '' && title !== null) {
1213
+ title = title.replace(/"/g, '&quot;');
1214
+ title = showdown.helper.escapeCharacters(title, '*_', false);
1215
+ result += ' title="' + title + '"';
1216
+ }
1217
+
1218
+ result += '>' + linkText + '</a>';
1219
+
1220
+ return result;
1221
+ };
1222
+
1223
+ // First, handle reference-style links: [link text] [id]
1224
+ text = text.replace(/(\[((?:\[[^\]]*]|[^\[\]])*)][ ]?(?:\n[ ]*)?\[(.*?)])()()()()/g, writeAnchorTag);
1225
+
1226
+ // Next, inline-style links: [link text](url "optional title")
1227
+ text = text.replace(/(\[((?:\[[^\]]*]|[^\[\]])*)]\([ \t]*()<?(.*?(?:\(.*?\).*?)?)>?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,
1228
+ writeAnchorTag);
1229
+
1230
+ // Last, handle reference-style shortcuts: [link text]
1231
+ // These must come last in case you've also got [link test][1]
1232
+ // or [link test](/foo)
1233
+ text = text.replace(/(\[([^\[\]]+)])()()()()()/g, writeAnchorTag);
1234
+
1235
+ text = globals.converter._dispatch('anchors.after', text, options, globals);
1236
+ return text;
1237
+ });
1238
+
1239
+ showdown.subParser('autoLinks', function (text, options, globals) {
1240
+ 'use strict';
1241
+
1242
+ text = globals.converter._dispatch('autoLinks.before', text, options, globals);
1243
+
1244
+ var simpleURLRegex = /\b(((https?|ftp|dict):\/\/|www\.)[^'">\s]+\.[^'">\s]+)(?=\s|$)(?!["<>])/gi,
1245
+ delimUrlRegex = /<(((https?|ftp|dict):\/\/|www\.)[^'">\s]+)>/gi,
1246
+ simpleMailRegex = /(?:^|\s)([A-Za-z0-9!#$%&'*+-/=?^_`\{|}~\.]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)(?:$|\s)/gi,
1247
+ delimMailRegex = /<(?:mailto:)?([-.\w]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi;
1248
+
1249
+ text = text.replace(delimUrlRegex, replaceLink);
1250
+ text = text.replace(delimMailRegex, replaceMail);
1251
+ // simpleURLRegex = /\b(((https?|ftp|dict):\/\/|www\.)[-.+~:?#@!$&'()*,;=[\]\w]+)\b/gi,
1252
+ // Email addresses: <address@domain.foo>
1253
+
1254
+ if (options.simplifiedAutoLink) {
1255
+ text = text.replace(simpleURLRegex, replaceLink);
1256
+ text = text.replace(simpleMailRegex, replaceMail);
1257
+ }
1258
+
1259
+ function replaceLink(wm, link) {
1260
+ var lnkTxt = link;
1261
+ if (/^www\./i.test(link)) {
1262
+ link = link.replace(/^www\./i, 'http://www.');
1263
+ }
1264
+ return '<a href="' + link + '">' + lnkTxt + '</a>';
1265
+ }
1266
+
1267
+ function replaceMail(wholeMatch, m1) {
1268
+ var unescapedStr = showdown.subParser('unescapeSpecialChars')(m1);
1269
+ return showdown.subParser('encodeEmailAddress')(unescapedStr);
1270
+ }
1271
+
1272
+ text = globals.converter._dispatch('autoLinks.after', text, options, globals);
1273
+
1274
+ return text;
1275
+ });
1276
+
1277
+ /**
1278
+ * These are all the transformations that form block-level
1279
+ * tags like paragraphs, headers, and list items.
1280
+ */
1281
+ showdown.subParser('blockGamut', function (text, options, globals) {
1282
+ 'use strict';
1283
+
1284
+ text = globals.converter._dispatch('blockGamut.before', text, options, globals);
1285
+
1286
+ // we parse blockquotes first so that we can have headings and hrs
1287
+ // inside blockquotes
1288
+ text = showdown.subParser('blockQuotes')(text, options, globals);
1289
+ text = showdown.subParser('headers')(text, options, globals);
1290
+
1291
+ // Do Horizontal Rules:
1292
+ var key = showdown.subParser('hashBlock')('<hr />', options, globals);
1293
+ text = text.replace(/^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$/gm, key);
1294
+ text = text.replace(/^[ ]{0,2}([ ]?\-[ ]?){3,}[ \t]*$/gm, key);
1295
+ text = text.replace(/^[ ]{0,2}([ ]?_[ ]?){3,}[ \t]*$/gm, key);
1296
+
1297
+ text = showdown.subParser('lists')(text, options, globals);
1298
+ text = showdown.subParser('codeBlocks')(text, options, globals);
1299
+ text = showdown.subParser('tables')(text, options, globals);
1300
+
1301
+ // We already ran _HashHTMLBlocks() before, in Markdown(), but that
1302
+ // was to escape raw HTML in the original Markdown source. This time,
1303
+ // we're escaping the markup we've just created, so that we don't wrap
1304
+ // <p> tags around block-level tags.
1305
+ text = showdown.subParser('hashHTMLBlocks')(text, options, globals);
1306
+ text = showdown.subParser('paragraphs')(text, options, globals);
1307
+
1308
+ text = globals.converter._dispatch('blockGamut.after', text, options, globals);
1309
+
1310
+ return text;
1311
+ });
1312
+
1313
+ showdown.subParser('blockQuotes', function (text, options, globals) {
1314
+ 'use strict';
1315
+
1316
+ text = globals.converter._dispatch('blockQuotes.before', text, options, globals);
1431
1317
 
1432
- return text;
1433
- }
1318
+ text = text.replace(/((^ {0,3}>[ \t]?.+\n(.+\n)*\n*)+)/gm, function (wholeMatch, m1) {
1319
+ var bq = m1;
1320
+
1321
+ // attacklab: hack around Konqueror 3.5.4 bug:
1322
+ // "----------bug".replace(/^-/g,"") == "bug"
1323
+ bq = bq.replace(/^[ \t]*>[ \t]?/gm, '~0'); // trim one level of quoting
1324
+
1325
+ // attacklab: clean up hack
1326
+ bq = bq.replace(/~0/g, '');
1327
+
1328
+ bq = bq.replace(/^[ \t]+$/gm, ''); // trim whitespace-only lines
1329
+ bq = showdown.subParser('githubCodeBlocks')(bq, options, globals);
1330
+ bq = showdown.subParser('blockGamut')(bq, options, globals); // recurse
1331
+
1332
+ bq = bq.replace(/(^|\n)/g, '$1 ');
1333
+ // These leading spaces screw with <pre> content, so we need to fix that:
1334
+ bq = bq.replace(/(\s*<pre>[^\r]+?<\/pre>)/gm, function (wholeMatch, m1) {
1335
+ var pre = m1;
1336
+ // attacklab: hack around Konqueror 3.5.4 bug:
1337
+ pre = pre.replace(/^ /mg, '~0');
1338
+ pre = pre.replace(/~0/g, '');
1339
+ return pre;
1340
+ });
1434
1341
 
1342
+ return showdown.subParser('hashBlock')('<blockquote>\n' + bq + '\n</blockquote>', options, globals);
1343
+ });
1344
+
1345
+ text = globals.converter._dispatch('blockQuotes.after', text, options, globals);
1346
+ return text;
1347
+ });
1348
+
1349
+ /**
1350
+ * Process Markdown `<pre><code>` blocks.
1351
+ */
1352
+ showdown.subParser('codeBlocks', function (text, options, globals) {
1353
+ 'use strict';
1354
+
1355
+ text = globals.converter._dispatch('codeBlocks.before', text, options, globals);
1356
+
1357
+ // sentinel workarounds for lack of \A and \Z, safari\khtml bug
1358
+ text += '~0';
1359
+
1360
+ var pattern = /(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=~0))/g;
1361
+ text = text.replace(pattern, function (wholeMatch, m1, m2) {
1362
+ var codeblock = m1,
1363
+ nextChar = m2,
1364
+ end = '\n';
1365
+
1366
+ codeblock = showdown.subParser('outdent')(codeblock);
1367
+ codeblock = showdown.subParser('encodeCode')(codeblock);
1368
+ codeblock = showdown.subParser('detab')(codeblock);
1369
+ codeblock = codeblock.replace(/^\n+/g, ''); // trim leading newlines
1370
+ codeblock = codeblock.replace(/\n+$/g, ''); // trim trailing newlines
1371
+
1372
+ if (options.omitExtraWLInCodeBlocks) {
1373
+ end = '';
1374
+ }
1375
+
1376
+ codeblock = '<pre><code>' + codeblock + end + '</code></pre>';
1377
+
1378
+ return showdown.subParser('hashBlock')(codeblock, options, globals) + nextChar;
1379
+ });
1380
+
1381
+ // strip sentinel
1382
+ text = text.replace(/~0/, '');
1383
+
1384
+ text = globals.converter._dispatch('codeBlocks.after', text, options, globals);
1385
+ return text;
1386
+ });
1387
+
1388
+ /**
1389
+ *
1390
+ * * Backtick quotes are used for <code></code> spans.
1391
+ *
1392
+ * * You can use multiple backticks as the delimiters if you want to
1393
+ * include literal backticks in the code span. So, this input:
1394
+ *
1395
+ * Just type ``foo `bar` baz`` at the prompt.
1396
+ *
1397
+ * Will translate to:
1398
+ *
1399
+ * <p>Just type <code>foo `bar` baz</code> at the prompt.</p>
1400
+ *
1401
+ * There's no arbitrary limit to the number of backticks you
1402
+ * can use as delimters. If you need three consecutive backticks
1403
+ * in your code, use four for delimiters, etc.
1404
+ *
1405
+ * * You can use spaces to get literal backticks at the edges:
1406
+ *
1407
+ * ... type `` `bar` `` ...
1408
+ *
1409
+ * Turns to:
1410
+ *
1411
+ * ... type <code>`bar`</code> ...
1412
+ */
1413
+ showdown.subParser('codeSpans', function (text, options, globals) {
1414
+ 'use strict';
1415
+
1416
+ text = globals.converter._dispatch('codeSpans.before', text, options, globals);
1417
+
1418
+ /*
1419
+ text = text.replace(/
1420
+ (^|[^\\]) // Character before opening ` can't be a backslash
1421
+ (`+) // $2 = Opening run of `
1422
+ ( // $3 = The code block
1423
+ [^\r]*?
1424
+ [^`] // attacklab: work around lack of lookbehind
1425
+ )
1426
+ \2 // Matching closer
1427
+ (?!`)
1428
+ /gm, function(){...});
1429
+ */
1430
+
1431
+ if (typeof(text) === 'undefined') {
1432
+ text = '';
1433
+ }
1434
+ text = text.replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm,
1435
+ function (wholeMatch, m1, m2, m3) {
1436
+ var c = m3;
1437
+ c = c.replace(/^([ \t]*)/g, ''); // leading whitespace
1438
+ c = c.replace(/[ \t]*$/g, ''); // trailing whitespace
1439
+ c = showdown.subParser('encodeCode')(c);
1440
+ return m1 + '<code>' + c + '</code>';
1441
+ }
1442
+ );
1443
+
1444
+ text = globals.converter._dispatch('codeSpans.after', text, options, globals);
1445
+ return text;
1446
+ });
1447
+
1448
+ /**
1449
+ * Convert all tabs to spaces
1450
+ */
1451
+ showdown.subParser('detab', function (text) {
1452
+ 'use strict';
1453
+
1454
+ // expand first n-1 tabs
1455
+ text = text.replace(/\t(?=\t)/g, ' '); // g_tab_width
1456
+
1457
+ // replace the nth with two sentinels
1458
+ text = text.replace(/\t/g, '~A~B');
1459
+
1460
+ // use the sentinel to anchor our regex so it doesn't explode
1461
+ text = text.replace(/~B(.+?)~A/g, function (wholeMatch, m1) {
1462
+ var leadingText = m1,
1463
+ numSpaces = 4 - leadingText.length % 4; // g_tab_width
1464
+
1465
+ // there *must* be a better way to do this:
1466
+ for (var i = 0; i < numSpaces; i++) {
1467
+ leadingText += ' ';
1468
+ }
1469
+
1470
+ return leadingText;
1471
+ });
1472
+
1473
+ // clean up sentinels
1474
+ text = text.replace(/~A/g, ' '); // g_tab_width
1475
+ text = text.replace(/~B/g, '');
1476
+
1477
+ return text;
1478
+
1479
+ });
1480
+
1481
+ /**
1482
+ * Smart processing for ampersands and angle brackets that need to be encoded.
1483
+ */
1484
+ showdown.subParser('encodeAmpsAndAngles', function (text) {
1485
+ 'use strict';
1486
+ // Ampersand-encoding based entirely on Nat Irons's Amputator MT plugin:
1487
+ // http://bumppo.net/projects/amputator/
1488
+ text = text.replace(/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/g, '&amp;');
1489
+
1490
+ // Encode naked <'s
1491
+ text = text.replace(/<(?![a-z\/?\$!])/gi, '&lt;');
1492
+
1493
+ return text;
1494
+ });
1495
+
1496
+ /**
1497
+ * Returns the string, with after processing the following backslash escape sequences.
1498
+ *
1499
+ * attacklab: The polite way to do this is with the new escapeCharacters() function:
1500
+ *
1501
+ * text = escapeCharacters(text,"\\",true);
1502
+ * text = escapeCharacters(text,"`*_{}[]()>#+-.!",true);
1503
+ *
1504
+ * ...but we're sidestepping its use of the (slow) RegExp constructor
1505
+ * as an optimization for Firefox. This function gets called a LOT.
1506
+ */
1507
+ showdown.subParser('encodeBackslashEscapes', function (text) {
1508
+ 'use strict';
1509
+ text = text.replace(/\\(\\)/g, showdown.helper.escapeCharactersCallback);
1510
+ text = text.replace(/\\([`*_{}\[\]()>#+-.!])/g, showdown.helper.escapeCharactersCallback);
1511
+ return text;
1512
+ });
1513
+
1514
+ /**
1515
+ * Encode/escape certain characters inside Markdown code runs.
1516
+ * The point is that in code, these characters are literals,
1517
+ * and lose their special Markdown meanings.
1518
+ */
1519
+ showdown.subParser('encodeCode', function (text) {
1520
+ 'use strict';
1521
+
1522
+ // Encode all ampersands; HTML entities are not
1523
+ // entities within a Markdown code span.
1524
+ text = text.replace(/&/g, '&amp;');
1525
+
1526
+ // Do the angle bracket song and dance:
1527
+ text = text.replace(/</g, '&lt;');
1528
+ text = text.replace(/>/g, '&gt;');
1529
+
1530
+ // Now, escape characters that are magic in Markdown:
1531
+ text = showdown.helper.escapeCharacters(text, '*_{}[]\\', false);
1532
+
1533
+ // jj the line above breaks this:
1534
+ //---
1535
+ //* Item
1536
+ // 1. Subitem
1537
+ // special char: *
1538
+ // ---
1539
+
1540
+ return text;
1541
+ });
1542
+
1543
+ /**
1544
+ * Input: an email address, e.g. "foo@example.com"
1545
+ *
1546
+ * Output: the email address as a mailto link, with each character
1547
+ * of the address encoded as either a decimal or hex entity, in
1548
+ * the hopes of foiling most address harvesting spam bots. E.g.:
1549
+ *
1550
+ * <a href="&#x6D;&#97;&#105;&#108;&#x74;&#111;:&#102;&#111;&#111;&#64;&#101;
1551
+ * x&#x61;&#109;&#x70;&#108;&#x65;&#x2E;&#99;&#111;&#109;">&#102;&#111;&#111;
1552
+ * &#64;&#101;x&#x61;&#109;&#x70;&#108;&#x65;&#x2E;&#99;&#111;&#109;</a>
1553
+ *
1554
+ * Based on a filter by Matthew Wickline, posted to the BBEdit-Talk
1555
+ * mailing list: <http://tinyurl.com/yu7ue>
1556
+ *
1557
+ */
1558
+ showdown.subParser('encodeEmailAddress', function (addr) {
1559
+ 'use strict';
1560
+
1561
+ var encode = [
1562
+ function (ch) {
1563
+ return '&#' + ch.charCodeAt(0) + ';';
1564
+ },
1565
+ function (ch) {
1566
+ return '&#x' + ch.charCodeAt(0).toString(16) + ';';
1567
+ },
1568
+ function (ch) {
1569
+ return ch;
1570
+ }
1571
+ ];
1572
+
1573
+ addr = 'mailto:' + addr;
1574
+
1575
+ addr = addr.replace(/./g, function (ch) {
1576
+ if (ch === '@') {
1577
+ // this *must* be encoded. I insist.
1578
+ ch = encode[Math.floor(Math.random() * 2)](ch);
1579
+ } else if (ch !== ':') {
1580
+ // leave ':' alone (to spot mailto: later)
1581
+ var r = Math.random();
1582
+ // roughly 10% raw, 45% hex, 45% dec
1583
+ ch = (
1584
+ r > 0.9 ? encode[2](ch) : r > 0.45 ? encode[1](ch) : encode[0](ch)
1585
+ );
1586
+ }
1587
+ return ch;
1588
+ });
1589
+
1590
+ addr = '<a href="' + addr + '">' + addr + '</a>';
1591
+ addr = addr.replace(/">.+:/g, '">'); // strip the mailto: from the visible part
1592
+
1593
+ return addr;
1594
+ });
1595
+
1596
+ /**
1597
+ * Within tags -- meaning between < and > -- encode [\ ` * _] so they
1598
+ * don't conflict with their use in Markdown for code, italics and strong.
1599
+ */
1600
+ showdown.subParser('escapeSpecialCharsWithinTagAttributes', function (text) {
1601
+ 'use strict';
1602
+
1603
+ // Build a regex to find HTML tags and comments. See Friedl's
1604
+ // "Mastering Regular Expressions", 2nd Ed., pp. 200-201.
1605
+ var regex = /(<[a-z\/!$]("[^"]*"|'[^']*'|[^'">])*>|<!(--.*?--\s*)+>)/gi;
1606
+
1607
+ text = text.replace(regex, function (wholeMatch) {
1608
+ var tag = wholeMatch.replace(/(.)<\/?code>(?=.)/g, '$1`');
1609
+ tag = showdown.helper.escapeCharacters(tag, '\\`*_', false);
1610
+ return tag;
1611
+ });
1612
+
1613
+ return text;
1614
+ });
1615
+
1616
+ /**
1617
+ * Handle github codeblocks prior to running HashHTML so that
1618
+ * HTML contained within the codeblock gets escaped properly
1619
+ * Example:
1620
+ * ```ruby
1621
+ * def hello_world(x)
1622
+ * puts "Hello, #{x}"
1623
+ * end
1624
+ * ```
1625
+ */
1626
+ showdown.subParser('githubCodeBlocks', function (text, options, globals) {
1627
+ 'use strict';
1628
+
1629
+ // early exit if option is not enabled
1630
+ if (!options.ghCodeBlocks) {
1631
+ return text;
1632
+ }
1633
+
1634
+ text = globals.converter._dispatch('githubCodeBlocks.before', text, options, globals);
1635
+
1636
+ text += '~0';
1637
+
1638
+ text = text.replace(/(?:^|\n)```(.*)\n([\s\S]*?)\n```/g, function (wholeMatch, language, codeblock) {
1639
+ var end = (options.omitExtraWLInCodeBlocks) ? '' : '\n';
1640
+
1641
+ // First parse the github code block
1642
+ codeblock = showdown.subParser('encodeCode')(codeblock);
1643
+ codeblock = showdown.subParser('detab')(codeblock);
1644
+ codeblock = codeblock.replace(/^\n+/g, ''); // trim leading newlines
1645
+ codeblock = codeblock.replace(/\n+$/g, ''); // trim trailing whitespace
1646
+
1647
+ codeblock = '<pre><code' + (language ? ' class="' + language + ' language-' + language + '"' : '') + '>' + codeblock + end + '</code></pre>';
1648
+
1649
+ codeblock = showdown.subParser('hashBlock')(codeblock, options, globals);
1650
+
1651
+ // Since GHCodeblocks can be false positives, we need to
1652
+ // store the primitive text and the parsed text in a global var,
1653
+ // and then return a token
1654
+ return '\n\n~G' + (globals.ghCodeBlocks.push({text: wholeMatch, codeblock: codeblock}) - 1) + 'G\n\n';
1655
+ });
1656
+
1657
+ // attacklab: strip sentinel
1658
+ text = text.replace(/~0/, '');
1659
+
1660
+ return globals.converter._dispatch('githubCodeBlocks.after', text, options, globals);
1661
+ });
1662
+
1663
+ showdown.subParser('hashBlock', function (text, options, globals) {
1664
+ 'use strict';
1665
+ text = text.replace(/(^\n+|\n+$)/g, '');
1666
+ return '\n\n~K' + (globals.gHtmlBlocks.push(text) - 1) + 'K\n\n';
1667
+ });
1668
+
1669
+ showdown.subParser('hashElement', function (text, options, globals) {
1670
+ 'use strict';
1671
+
1672
+ return function (wholeMatch, m1) {
1673
+ var blockText = m1;
1674
+
1675
+ // Undo double lines
1676
+ blockText = blockText.replace(/\n\n/g, '\n');
1677
+ blockText = blockText.replace(/^\n/, '');
1678
+
1679
+ // strip trailing blank lines
1680
+ blockText = blockText.replace(/\n+$/g, '');
1681
+
1682
+ // Replace the element text with a marker ("~KxK" where x is its key)
1683
+ blockText = '\n\n~K' + (globals.gHtmlBlocks.push(blockText) - 1) + 'K\n\n';
1684
+
1685
+ return blockText;
1686
+ };
1687
+ });
1688
+
1689
+ showdown.subParser('hashHTMLBlocks', function (text, options, globals) {
1690
+ 'use strict';
1691
+
1692
+ var blockTags = [
1693
+ 'pre',
1694
+ 'div',
1695
+ 'h1',
1696
+ 'h2',
1697
+ 'h3',
1698
+ 'h4',
1699
+ 'h5',
1700
+ 'h6',
1701
+ 'blockquote',
1702
+ 'table',
1703
+ 'dl',
1704
+ 'ol',
1705
+ 'ul',
1706
+ 'script',
1707
+ 'noscript',
1708
+ 'form',
1709
+ 'fieldset',
1710
+ 'iframe',
1711
+ 'math',
1712
+ 'style',
1713
+ 'section',
1714
+ 'header',
1715
+ 'footer',
1716
+ 'nav',
1717
+ 'article',
1718
+ 'aside',
1719
+ 'address',
1720
+ 'audio',
1721
+ 'canvas',
1722
+ 'figure',
1723
+ 'hgroup',
1724
+ 'output',
1725
+ 'video',
1726
+ 'p'
1727
+ ],
1728
+ repFunc = function (wholeMatch, match, left, right) {
1729
+ var txt = wholeMatch;
1730
+ // check if this html element is marked as markdown
1731
+ // if so, it's contents should be parsed as markdown
1732
+ if (left.search(/\bmarkdown\b/) !== -1) {
1733
+ txt = left + globals.converter.makeHtml(match) + right;
1734
+ }
1735
+ return '\n\n~K' + (globals.gHtmlBlocks.push(txt) - 1) + 'K\n\n';
1736
+ };
1737
+
1738
+ for (var i = 0; i < blockTags.length; ++i) {
1739
+ text = showdown.helper.replaceRecursiveRegExp(text, repFunc, '^ {0,3}<' + blockTags[i] + '\\b[^>]*>', '</' + blockTags[i] + '>', 'gim');
1740
+ }
1741
+
1742
+ // HR SPECIAL CASE
1743
+ text = text.replace(/(\n {0,3}(<(hr)\b([^<>])*?\/?>)[ \t]*(?=\n{2,}))/g,
1744
+ showdown.subParser('hashElement')(text, options, globals));
1745
+
1746
+ // Special case for standalone HTML comments
1747
+ text = showdown.helper.replaceRecursiveRegExp(text, function (txt) {
1748
+ return '\n\n~K' + (globals.gHtmlBlocks.push(txt) - 1) + 'K\n\n';
1749
+ }, '^ {0,3}<!--', '-->', 'gm');
1750
+
1751
+ // PHP and ASP-style processor instructions (<?...?> and <%...%>)
1752
+ text = text.replace(/(?:\n\n)( {0,3}(?:<([?%])[^\r]*?\2>)[ \t]*(?=\n{2,}))/g,
1753
+ showdown.subParser('hashElement')(text, options, globals));
1754
+
1755
+ return text;
1756
+ });
1757
+
1758
+ /**
1759
+ * Hash span elements that should not be parsed as markdown
1760
+ */
1761
+ showdown.subParser('hashHTMLSpans', function (text, config, globals) {
1762
+ 'use strict';
1763
+
1764
+ var matches = showdown.helper.matchRecursiveRegExp(text, '<code\\b[^>]*>', '</code>', 'gi');
1765
+
1766
+ for (var i = 0; i < matches.length; ++i) {
1767
+ text = text.replace(matches[i][0], '~L' + (globals.gHtmlSpans.push(matches[i][0]) - 1) + 'L');
1768
+ }
1769
+ return text;
1770
+ });
1771
+
1772
+ /**
1773
+ * Unhash HTML spans
1774
+ */
1775
+ showdown.subParser('unhashHTMLSpans', function (text, config, globals) {
1776
+ 'use strict';
1777
+
1778
+ for (var i = 0; i < globals.gHtmlSpans.length; ++i) {
1779
+ text = text.replace('~L' + i + 'L', globals.gHtmlSpans[i]);
1780
+ }
1781
+
1782
+ return text;
1783
+ });
1784
+
1785
+ /**
1786
+ * Hash span elements that should not be parsed as markdown
1787
+ */
1788
+ showdown.subParser('hashPreCodeTags', function (text, config, globals) {
1789
+ 'use strict';
1790
+
1791
+ var repFunc = function (wholeMatch, match, left, right) {
1792
+ // encode html entities
1793
+ var codeblock = left + showdown.subParser('encodeCode')(match) + right;
1794
+ return '\n\n~G' + (globals.ghCodeBlocks.push({text: wholeMatch, codeblock: codeblock}) - 1) + 'G\n\n';
1795
+ };
1796
+
1797
+ text = showdown.helper.replaceRecursiveRegExp(text, repFunc, '^ {0,3}<pre\\b[^>]*>\\s*<code\\b[^>]*>', '^ {0,3}</code>\\s*</pre>', 'gim');
1798
+ return text;
1799
+ });
1800
+
1801
+ showdown.subParser('headers', function (text, options, globals) {
1802
+ 'use strict';
1803
+
1804
+ text = globals.converter._dispatch('headers.before', text, options, globals);
1805
+
1806
+ var prefixHeader = options.prefixHeaderId,
1807
+ headerLevelStart = (isNaN(parseInt(options.headerLevelStart))) ? 1 : parseInt(options.headerLevelStart),
1808
+
1809
+ // Set text-style headers:
1810
+ // Header 1
1811
+ // ========
1812
+ //
1813
+ // Header 2
1814
+ // --------
1815
+ //
1816
+ setextRegexH1 = (options.smoothLivePreview) ? /^(.+)[ \t]*\n={2,}[ \t]*\n+/gm : /^(.+)[ \t]*\n=+[ \t]*\n+/gm,
1817
+ setextRegexH2 = (options.smoothLivePreview) ? /^(.+)[ \t]*\n-{2,}[ \t]*\n+/gm : /^(.+)[ \t]*\n-+[ \t]*\n+/gm;
1818
+
1819
+ text = text.replace(setextRegexH1, function (wholeMatch, m1) {
1820
+
1821
+ var spanGamut = showdown.subParser('spanGamut')(m1, options, globals),
1822
+ hID = (options.noHeaderId) ? '' : ' id="' + headerId(m1) + '"',
1823
+ hLevel = headerLevelStart,
1824
+ hashBlock = '<h' + hLevel + hID + '>' + spanGamut + '</h' + hLevel + '>';
1825
+ return showdown.subParser('hashBlock')(hashBlock, options, globals);
1826
+ });
1827
+
1828
+ text = text.replace(setextRegexH2, function (matchFound, m1) {
1829
+ var spanGamut = showdown.subParser('spanGamut')(m1, options, globals),
1830
+ hID = (options.noHeaderId) ? '' : ' id="' + headerId(m1) + '"',
1831
+ hLevel = headerLevelStart + 1,
1832
+ hashBlock = '<h' + hLevel + hID + '>' + spanGamut + '</h' + hLevel + '>';
1833
+ return showdown.subParser('hashBlock')(hashBlock, options, globals);
1834
+ });
1835
+
1836
+ // atx-style headers:
1837
+ // # Header 1
1838
+ // ## Header 2
1839
+ // ## Header 2 with closing hashes ##
1840
+ // ...
1841
+ // ###### Header 6
1842
+ //
1843
+ text = text.replace(/^(#{1,6})[ \t]*(.+?)[ \t]*#*\n+/gm, function (wholeMatch, m1, m2) {
1844
+ var span = showdown.subParser('spanGamut')(m2, options, globals),
1845
+ hID = (options.noHeaderId) ? '' : ' id="' + headerId(m2) + '"',
1846
+ hLevel = headerLevelStart - 1 + m1.length,
1847
+ header = '<h' + hLevel + hID + '>' + span + '</h' + hLevel + '>';
1848
+
1849
+ return showdown.subParser('hashBlock')(header, options, globals);
1850
+ });
1851
+
1852
+ function headerId(m) {
1853
+ var title, escapedId = m.replace(/[^\w]/g, '').toLowerCase();
1854
+
1855
+ if (globals.hashLinkCounts[escapedId]) {
1856
+ title = escapedId + '-' + (globals.hashLinkCounts[escapedId]++);
1857
+ } else {
1858
+ title = escapedId;
1859
+ globals.hashLinkCounts[escapedId] = 1;
1860
+ }
1861
+
1862
+ // Prefix id to prevent causing inadvertent pre-existing style matches.
1863
+ if (prefixHeader === true) {
1864
+ prefixHeader = 'section';
1865
+ }
1866
+
1867
+ if (showdown.helper.isString(prefixHeader)) {
1868
+ return prefixHeader + title;
1869
+ }
1870
+ return title;
1871
+ }
1872
+
1873
+ text = globals.converter._dispatch('headers.after', text, options, globals);
1874
+ return text;
1875
+ });
1876
+
1877
+ /**
1878
+ * Turn Markdown image shortcuts into <img> tags.
1879
+ */
1880
+ showdown.subParser('images', function (text, options, globals) {
1881
+ 'use strict';
1882
+
1883
+ text = globals.converter._dispatch('images.before', text, options, globals);
1884
+
1885
+ var inlineRegExp = /!\[(.*?)]\s?\([ \t]*()<?(\S+?)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*(?:(['"])(.*?)\6[ \t]*)?\)/g,
1886
+ referenceRegExp = /!\[([^\]]*?)] ?(?:\n *)?\[(.*?)]()()()()()/g;
1887
+
1888
+ function writeImageTag (wholeMatch, altText, linkId, url, width, height, m5, title) {
1889
+
1890
+ var gUrls = globals.gUrls,
1891
+ gTitles = globals.gTitles,
1892
+ gDims = globals.gDimensions;
1893
+
1894
+ linkId = linkId.toLowerCase();
1895
+
1896
+ if (!title) {
1897
+ title = '';
1898
+ }
1899
+
1900
+ if (url === '' || url === null) {
1901
+ if (linkId === '' || linkId === null) {
1902
+ // lower-case and turn embedded newlines into spaces
1903
+ linkId = altText.toLowerCase().replace(/ ?\n/g, ' ');
1904
+ }
1905
+ url = '#' + linkId;
1906
+
1907
+ if (!showdown.helper.isUndefined(gUrls[linkId])) {
1908
+ url = gUrls[linkId];
1909
+ if (!showdown.helper.isUndefined(gTitles[linkId])) {
1910
+ title = gTitles[linkId];
1911
+ }
1912
+ if (!showdown.helper.isUndefined(gDims[linkId])) {
1913
+ width = gDims[linkId].width;
1914
+ height = gDims[linkId].height;
1915
+ }
1916
+ } else {
1917
+ return wholeMatch;
1918
+ }
1919
+ }
1920
+
1921
+ altText = altText.replace(/"/g, '&quot;');
1922
+ altText = showdown.helper.escapeCharacters(altText, '*_', false);
1923
+ url = showdown.helper.escapeCharacters(url, '*_', false);
1924
+ var result = '<img src="' + url + '" alt="' + altText + '"';
1925
+
1926
+ if (title) {
1927
+ title = title.replace(/"/g, '&quot;');
1928
+ title = showdown.helper.escapeCharacters(title, '*_', false);
1929
+ result += ' title="' + title + '"';
1930
+ }
1931
+
1932
+ if (width && height) {
1933
+ width = (width === '*') ? 'auto' : width;
1934
+ height = (height === '*') ? 'auto' : height;
1935
+
1936
+ result += ' width="' + width + '"';
1937
+ result += ' height="' + height + '"';
1938
+ }
1939
+
1940
+ result += ' />';
1941
+
1942
+ return result;
1943
+ }
1944
+
1945
+ // First, handle reference-style labeled images: ![alt text][id]
1946
+ text = text.replace(referenceRegExp, writeImageTag);
1947
+
1948
+ // Next, handle inline images: ![alt text](url =<width>x<height> "optional title")
1949
+ text = text.replace(inlineRegExp, writeImageTag);
1950
+
1951
+ text = globals.converter._dispatch('images.after', text, options, globals);
1952
+ return text;
1953
+ });
1954
+
1955
+ showdown.subParser('italicsAndBold', function (text, options, globals) {
1956
+ 'use strict';
1957
+
1958
+ text = globals.converter._dispatch('italicsAndBold.before', text, options, globals);
1959
+
1960
+ if (options.literalMidWordUnderscores) {
1961
+ //underscores
1962
+ // Since we are consuming a \s character, we need to add it
1963
+ text = text.replace(/(^|\s|>|\b)__(?=\S)([\s\S]+?)__(?=\b|<|\s|$)/gm, '$1<strong>$2</strong>');
1964
+ text = text.replace(/(^|\s|>|\b)_(?=\S)([\s\S]+?)_(?=\b|<|\s|$)/gm, '$1<em>$2</em>');
1965
+ //asterisks
1966
+ text = text.replace(/(\*\*)(?=\S)([^\r]*?\S[*]*)\1/g, '<strong>$2</strong>');
1967
+ text = text.replace(/(\*)(?=\S)([^\r]*?\S)\1/g, '<em>$2</em>');
1968
+
1969
+ } else {
1970
+ // <strong> must go first:
1971
+ text = text.replace(/(\*\*|__)(?=\S)([^\r]*?\S[*_]*)\1/g, '<strong>$2</strong>');
1972
+ text = text.replace(/(\*|_)(?=\S)([^\r]*?\S)\1/g, '<em>$2</em>');
1973
+ }
1974
+
1975
+ text = globals.converter._dispatch('italicsAndBold.after', text, options, globals);
1976
+ return text;
1977
+ });
1978
+
1979
+ /**
1980
+ * Form HTML ordered (numbered) and unordered (bulleted) lists.
1981
+ */
1982
+ showdown.subParser('lists', function (text, options, globals) {
1983
+ 'use strict';
1984
+
1985
+ text = globals.converter._dispatch('lists.before', text, options, globals);
1986
+ /**
1987
+ * Process the contents of a single ordered or unordered list, splitting it
1988
+ * into individual list items.
1989
+ * @param {string} listStr
1990
+ * @param {boolean} trimTrailing
1991
+ * @returns {string}
1992
+ */
1993
+ function processListItems (listStr, trimTrailing) {
1994
+ // The $g_list_level global keeps track of when we're inside a list.
1995
+ // Each time we enter a list, we increment it; when we leave a list,
1996
+ // we decrement. If it's zero, we're not in a list anymore.
1997
+ //
1998
+ // We do this because when we're not inside a list, we want to treat
1999
+ // something like this:
2000
+ //
2001
+ // I recommend upgrading to version
2002
+ // 8. Oops, now this line is treated
2003
+ // as a sub-list.
2004
+ //
2005
+ // As a single paragraph, despite the fact that the second line starts
2006
+ // with a digit-period-space sequence.
2007
+ //
2008
+ // Whereas when we're inside a list (or sub-list), that line will be
2009
+ // treated as the start of a sub-list. What a kludge, huh? This is
2010
+ // an aspect of Markdown's syntax that's hard to parse perfectly
2011
+ // without resorting to mind-reading. Perhaps the solution is to
2012
+ // change the syntax rules such that sub-lists must start with a
2013
+ // starting cardinal number; e.g. "1." or "a.".
2014
+ globals.gListLevel++;
2015
+
2016
+ // trim trailing blank lines:
2017
+ listStr = listStr.replace(/\n{2,}$/, '\n');
2018
+
2019
+ // attacklab: add sentinel to emulate \z
2020
+ listStr += '~0';
2021
+
2022
+ var rgx = /(\n)?(^ {0,3})([*+-]|\d+[.])[ \t]+((\[(x|X| )?])?[ \t]*[^\r]+?(\n{1,2}))(?=\n*(~0| {0,3}([*+-]|\d+[.])[ \t]+))/gm,
2023
+ isParagraphed = (/\n[ \t]*\n(?!~0)/.test(listStr));
2024
+
2025
+ // Since version 1.5, nesting sublists requires 4 spaces (or 1 tab) indentation,
2026
+ // which is a syntax breaking change
2027
+ // activating this option reverts to old behavior
2028
+ if (options.disableForced4SpacesIndentedSublists) {
2029
+ rgx = /(\n)?(^ {0,3})([*+-]|\d+[.])[ \t]+((\[(x|X| )?])?[ \t]*[^\r]+?(\n{1,2}))(?=\n*(~0|\2([*+-]|\d+[.])[ \t]+))/gm;
2030
+ }
2031
+
2032
+ listStr = listStr.replace(rgx, function (wholeMatch, m1, m2, m3, m4, taskbtn, checked) {
2033
+ checked = (checked && checked.trim() !== '');
2034
+
2035
+ var item = showdown.subParser('outdent')(m4, options, globals),
2036
+ bulletStyle = '';
2037
+
2038
+ // Support for github tasklists
2039
+ if (taskbtn && options.tasklists) {
2040
+ bulletStyle = ' class="task-list-item" style="list-style-type: none;"';
2041
+ item = item.replace(/^[ \t]*\[(x|X| )?]/m, function () {
2042
+ var otp = '<input type="checkbox" disabled style="margin: 0px 0.35em 0.25em -1.6em; vertical-align: middle;"';
2043
+ if (checked) {
2044
+ otp += ' checked';
2045
+ }
2046
+ otp += '>';
2047
+ return otp;
2048
+ });
2049
+ }
2050
+
2051
+ // m1 - Leading line or
2052
+ // Has a double return (multi paragraph) or
2053
+ // Has sublist
2054
+ if (m1 || (item.search(/\n{2,}/) > -1)) {
2055
+ item = showdown.subParser('githubCodeBlocks')(item, options, globals);
2056
+ item = showdown.subParser('blockGamut')(item, options, globals);
2057
+ } else {
2058
+ // Recursion for sub-lists:
2059
+ item = showdown.subParser('lists')(item, options, globals);
2060
+ item = item.replace(/\n$/, ''); // chomp(item)
2061
+ if (isParagraphed) {
2062
+ item = showdown.subParser('paragraphs')(item, options, globals);
2063
+ } else {
2064
+ item = showdown.subParser('spanGamut')(item, options, globals);
2065
+ }
2066
+ }
2067
+ item = '<li' + bulletStyle + '>' + item + '</li>\n';
2068
+ return item;
2069
+ });
1435
2070
 
1436
- var escapeCharacters_callback = function(wholeMatch,m1) {
1437
- var charCodeToEscape = m1.charCodeAt(0);
1438
- return "~E"+charCodeToEscape+"E";
2071
+ // attacklab: strip sentinel
2072
+ listStr = listStr.replace(/~0/g, '');
2073
+
2074
+ globals.gListLevel--;
2075
+
2076
+ if (trimTrailing) {
2077
+ listStr = listStr.replace(/\s+$/, '');
2078
+ }
2079
+
2080
+ return listStr;
2081
+ }
2082
+
2083
+ /**
2084
+ * Check and parse consecutive lists (better fix for issue #142)
2085
+ * @param {string} list
2086
+ * @param {string} listType
2087
+ * @param {boolean} trimTrailing
2088
+ * @returns {string}
2089
+ */
2090
+ function parseConsecutiveLists(list, listType, trimTrailing) {
2091
+ // check if we caught 2 or more consecutive lists by mistake
2092
+ // we use the counterRgx, meaning if listType is UL we look for OL and vice versa
2093
+ var olRgx = (options.disableForced4SpacesIndentedSublists) ? /^ ?\d+\.[ \t]/gm : /^ {0,3}\d+\.[ \t]/gm,
2094
+ ulRgx = (options.disableForced4SpacesIndentedSublists) ? /^ ?[*+-][ \t]/gm : /^ {0,3}[*+-][ \t]/gm,
2095
+ counterRxg = (listType === 'ul') ? olRgx : ulRgx,
2096
+ result = '';
2097
+
2098
+ if (list.search(counterRxg) !== -1) {
2099
+ (function parseCL(txt) {
2100
+ var pos = txt.search(counterRxg);
2101
+ if (pos !== -1) {
2102
+ // slice
2103
+ result += '\n<' + listType + '>\n' + processListItems(txt.slice(0, pos), !!trimTrailing) + '</' + listType + '>\n';
2104
+
2105
+ // invert counterType and listType
2106
+ listType = (listType === 'ul') ? 'ol' : 'ul';
2107
+ counterRxg = (listType === 'ul') ? olRgx : ulRgx;
2108
+
2109
+ //recurse
2110
+ parseCL(txt.slice(pos));
2111
+ } else {
2112
+ result += '\n<' + listType + '>\n' + processListItems(txt, !!trimTrailing) + '</' + listType + '>\n';
2113
+ }
2114
+ })(list);
2115
+ } else {
2116
+ result = '\n<' + listType + '>\n' + processListItems(list, !!trimTrailing) + '</' + listType + '>\n';
2117
+ }
2118
+
2119
+ return result;
2120
+ }
2121
+
2122
+ // add sentinel to hack around khtml/safari bug:
2123
+ // http://bugs.webkit.org/show_bug.cgi?id=11231
2124
+ text += '~0';
2125
+
2126
+ if (globals.gListLevel) {
2127
+ text = text.replace(/^(( {0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm,
2128
+ function (wholeMatch, list, m2) {
2129
+ var listType = (m2.search(/[*+-]/g) > -1) ? 'ul' : 'ol';
2130
+ return parseConsecutiveLists(list, listType, true);
2131
+ }
2132
+ );
2133
+ } else {
2134
+ text = text.replace(/(\n\n|^\n?)(( {0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm,
2135
+ function (wholeMatch, m1, list, m3) {
2136
+ var listType = (m3.search(/[*+-]/g) > -1) ? 'ul' : 'ol';
2137
+ return parseConsecutiveLists(list, listType, false);
2138
+ }
2139
+ );
2140
+ }
2141
+
2142
+ // strip sentinel
2143
+ text = text.replace(/~0/, '');
2144
+
2145
+ text = globals.converter._dispatch('lists.after', text, options, globals);
2146
+ return text;
2147
+ });
2148
+
2149
+ /**
2150
+ * Remove one level of line-leading tabs or spaces
2151
+ */
2152
+ showdown.subParser('outdent', function (text) {
2153
+ 'use strict';
2154
+
2155
+ // attacklab: hack around Konqueror 3.5.4 bug:
2156
+ // "----------bug".replace(/^-/g,"") == "bug"
2157
+ text = text.replace(/^(\t|[ ]{1,4})/gm, '~0'); // attacklab: g_tab_width
2158
+
2159
+ // attacklab: clean up hack
2160
+ text = text.replace(/~0/g, '');
2161
+
2162
+ return text;
2163
+ });
2164
+
2165
+ /**
2166
+ *
2167
+ */
2168
+ showdown.subParser('paragraphs', function (text, options, globals) {
2169
+ 'use strict';
2170
+
2171
+ text = globals.converter._dispatch('paragraphs.before', text, options, globals);
2172
+ // Strip leading and trailing lines:
2173
+ text = text.replace(/^\n+/g, '');
2174
+ text = text.replace(/\n+$/g, '');
2175
+
2176
+ var grafs = text.split(/\n{2,}/g),
2177
+ grafsOut = [],
2178
+ end = grafs.length; // Wrap <p> tags
2179
+
2180
+ for (var i = 0; i < end; i++) {
2181
+ var str = grafs[i];
2182
+ // if this is an HTML marker, copy it
2183
+ if (str.search(/~(K|G)(\d+)\1/g) >= 0) {
2184
+ grafsOut.push(str);
2185
+ } else {
2186
+ str = showdown.subParser('spanGamut')(str, options, globals);
2187
+ str = str.replace(/^([ \t]*)/g, '<p>');
2188
+ str += '</p>';
2189
+ grafsOut.push(str);
2190
+ }
2191
+ }
2192
+
2193
+ /** Unhashify HTML blocks */
2194
+ end = grafsOut.length;
2195
+ for (i = 0; i < end; i++) {
2196
+ var blockText = '',
2197
+ grafsOutIt = grafsOut[i],
2198
+ codeFlag = false;
2199
+ // if this is a marker for an html block...
2200
+ while (grafsOutIt.search(/~(K|G)(\d+)\1/) >= 0) {
2201
+ var delim = RegExp.$1,
2202
+ num = RegExp.$2;
2203
+
2204
+ if (delim === 'K') {
2205
+ blockText = globals.gHtmlBlocks[num];
2206
+ } else {
2207
+ // we need to check if ghBlock is a false positive
2208
+ if (codeFlag) {
2209
+ // use encoded version of all text
2210
+ blockText = showdown.subParser('encodeCode')(globals.ghCodeBlocks[num].text);
2211
+ } else {
2212
+ blockText = globals.ghCodeBlocks[num].codeblock;
2213
+ }
2214
+ }
2215
+ blockText = blockText.replace(/\$/g, '$$$$'); // Escape any dollar signs
2216
+
2217
+ grafsOutIt = grafsOutIt.replace(/(\n\n)?~(K|G)\d+\2(\n\n)?/, blockText);
2218
+ // Check if grafsOutIt is a pre->code
2219
+ if (/^<pre\b[^>]*>\s*<code\b[^>]*>/.test(grafsOutIt)) {
2220
+ codeFlag = true;
2221
+ }
2222
+ }
2223
+ grafsOut[i] = grafsOutIt;
2224
+ }
2225
+ text = grafsOut.join('\n');
2226
+ // Strip leading and trailing lines:
2227
+ text = text.replace(/^\n+/g, '');
2228
+ text = text.replace(/\n+$/g, '');
2229
+ return globals.converter._dispatch('paragraphs.after', text, options, globals);
2230
+ });
2231
+
2232
+ /**
2233
+ * Run extension
2234
+ */
2235
+ showdown.subParser('runExtension', function (ext, text, options, globals) {
2236
+ 'use strict';
2237
+
2238
+ if (ext.filter) {
2239
+ text = ext.filter(text, globals.converter, options);
2240
+
2241
+ } else if (ext.regex) {
2242
+ // TODO remove this when old extension loading mechanism is deprecated
2243
+ var re = ext.regex;
2244
+ if (!re instanceof RegExp) {
2245
+ re = new RegExp(re, 'g');
2246
+ }
2247
+ text = text.replace(re, ext.replace);
2248
+ }
2249
+
2250
+ return text;
2251
+ });
2252
+
2253
+ /**
2254
+ * These are all the transformations that occur *within* block-level
2255
+ * tags like paragraphs, headers, and list items.
2256
+ */
2257
+ showdown.subParser('spanGamut', function (text, options, globals) {
2258
+ 'use strict';
2259
+
2260
+ text = globals.converter._dispatch('spanGamut.before', text, options, globals);
2261
+ text = showdown.subParser('codeSpans')(text, options, globals);
2262
+ text = showdown.subParser('escapeSpecialCharsWithinTagAttributes')(text, options, globals);
2263
+ text = showdown.subParser('encodeBackslashEscapes')(text, options, globals);
2264
+
2265
+ // Process anchor and image tags. Images must come first,
2266
+ // because ![foo][f] looks like an anchor.
2267
+ text = showdown.subParser('images')(text, options, globals);
2268
+ text = showdown.subParser('anchors')(text, options, globals);
2269
+
2270
+ // Make links out of things like `<http://example.com/>`
2271
+ // Must come after _DoAnchors(), because you can use < and >
2272
+ // delimiters in inline links like [this](<url>).
2273
+ text = showdown.subParser('autoLinks')(text, options, globals);
2274
+ text = showdown.subParser('encodeAmpsAndAngles')(text, options, globals);
2275
+ text = showdown.subParser('italicsAndBold')(text, options, globals);
2276
+ text = showdown.subParser('strikethrough')(text, options, globals);
2277
+
2278
+ // Do hard breaks:
2279
+ text = text.replace(/ +\n/g, ' <br />\n');
2280
+
2281
+ text = globals.converter._dispatch('spanGamut.after', text, options, globals);
2282
+ return text;
2283
+ });
2284
+
2285
+ showdown.subParser('strikethrough', function (text, options, globals) {
2286
+ 'use strict';
2287
+
2288
+ if (options.strikethrough) {
2289
+ text = globals.converter._dispatch('strikethrough.before', text, options, globals);
2290
+ text = text.replace(/(?:~T){2}([\s\S]+?)(?:~T){2}/g, '<del>$1</del>');
2291
+ text = globals.converter._dispatch('strikethrough.after', text, options, globals);
2292
+ }
2293
+
2294
+ return text;
2295
+ });
2296
+
2297
+ /**
2298
+ * Strip any lines consisting only of spaces and tabs.
2299
+ * This makes subsequent regexs easier to write, because we can
2300
+ * match consecutive blank lines with /\n+/ instead of something
2301
+ * contorted like /[ \t]*\n+/
2302
+ */
2303
+ showdown.subParser('stripBlankLines', function (text) {
2304
+ 'use strict';
2305
+ return text.replace(/^[ \t]+$/mg, '');
2306
+ });
2307
+
2308
+ /**
2309
+ * Strips link definitions from text, stores the URLs and titles in
2310
+ * hash references.
2311
+ * Link defs are in the form: ^[id]: url "optional title"
2312
+ */
2313
+ showdown.subParser('stripLinkDefinitions', function (text, options, globals) {
2314
+ 'use strict';
2315
+
2316
+ 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;
2317
+
2318
+ // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
2319
+ text += '~0';
2320
+
2321
+ text = text.replace(regex, function (wholeMatch, linkId, url, width, height, blankLines, title) {
2322
+ linkId = linkId.toLowerCase();
2323
+ globals.gUrls[linkId] = showdown.subParser('encodeAmpsAndAngles')(url); // Link IDs are case-insensitive
2324
+
2325
+ if (blankLines) {
2326
+ // Oops, found blank lines, so it's not a title.
2327
+ // Put back the parenthetical statement we stole.
2328
+ return blankLines + title;
2329
+
2330
+ } else {
2331
+ if (title) {
2332
+ globals.gTitles[linkId] = title.replace(/"|'/g, '&quot;');
2333
+ }
2334
+ if (options.parseImgDimensions && width && height) {
2335
+ globals.gDimensions[linkId] = {
2336
+ width: width,
2337
+ height: height
2338
+ };
2339
+ }
2340
+ }
2341
+ // Completely remove the definition from the text
2342
+ return '';
2343
+ });
2344
+
2345
+ // attacklab: strip sentinel
2346
+ text = text.replace(/~0/, '');
2347
+
2348
+ return text;
2349
+ });
2350
+
2351
+ showdown.subParser('tables', function (text, options, globals) {
2352
+ 'use strict';
2353
+
2354
+ if (!options.tables) {
2355
+ return text;
2356
+ }
2357
+
2358
+ var tableRgx = /^ {0,3}\|?.+\|.+\n[ \t]{0,3}\|?[ \t]*:?[ \t]*(?:-|=){2,}[ \t]*:?[ \t]*\|[ \t]*:?[ \t]*(?:-|=){2,}[\s\S]+?(?:\n\n|~0)/gm;
2359
+
2360
+ function parseStyles(sLine) {
2361
+ if (/^:[ \t]*--*$/.test(sLine)) {
2362
+ return ' style="text-align:left;"';
2363
+ } else if (/^--*[ \t]*:[ \t]*$/.test(sLine)) {
2364
+ return ' style="text-align:right;"';
2365
+ } else if (/^:[ \t]*--*[ \t]*:$/.test(sLine)) {
2366
+ return ' style="text-align:center;"';
2367
+ } else {
2368
+ return '';
2369
+ }
2370
+ }
2371
+
2372
+ function parseHeaders(header, style) {
2373
+ var id = '';
2374
+ header = header.trim();
2375
+ if (options.tableHeaderId) {
2376
+ id = ' id="' + header.replace(/ /g, '_').toLowerCase() + '"';
2377
+ }
2378
+ header = showdown.subParser('spanGamut')(header, options, globals);
2379
+
2380
+ return '<th' + id + style + '>' + header + '</th>\n';
2381
+ }
2382
+
2383
+ function parseCells(cell, style) {
2384
+ var subText = showdown.subParser('spanGamut')(cell, options, globals);
2385
+ return '<td' + style + '>' + subText + '</td>\n';
2386
+ }
2387
+
2388
+ function buildTable(headers, cells) {
2389
+ var tb = '<table>\n<thead>\n<tr>\n',
2390
+ tblLgn = headers.length;
2391
+
2392
+ for (var i = 0; i < tblLgn; ++i) {
2393
+ tb += headers[i];
2394
+ }
2395
+ tb += '</tr>\n</thead>\n<tbody>\n';
2396
+
2397
+ for (i = 0; i < cells.length; ++i) {
2398
+ tb += '<tr>\n';
2399
+ for (var ii = 0; ii < tblLgn; ++ii) {
2400
+ tb += cells[i][ii];
2401
+ }
2402
+ tb += '</tr>\n';
2403
+ }
2404
+ tb += '</tbody>\n</table>\n';
2405
+ return tb;
2406
+ }
2407
+
2408
+ text = globals.converter._dispatch('tables.before', text, options, globals);
2409
+
2410
+ text = text.replace(tableRgx, function (rawTable) {
2411
+
2412
+ var i, tableLines = rawTable.split('\n');
2413
+
2414
+ // strip wrong first and last column if wrapped tables are used
2415
+ for (i = 0; i < tableLines.length; ++i) {
2416
+ if (/^ {0,3}\|/.test(tableLines[i])) {
2417
+ tableLines[i] = tableLines[i].replace(/^ {0,3}\|/, '');
2418
+ }
2419
+ if (/\|[ \t]*$/.test(tableLines[i])) {
2420
+ tableLines[i] = tableLines[i].replace(/\|[ \t]*$/, '');
2421
+ }
2422
+ }
2423
+
2424
+ var rawHeaders = tableLines[0].split('|').map(function (s) { return s.trim();}),
2425
+ rawStyles = tableLines[1].split('|').map(function (s) { return s.trim();}),
2426
+ rawCells = [],
2427
+ headers = [],
2428
+ styles = [],
2429
+ cells = [];
2430
+
2431
+ tableLines.shift();
2432
+ tableLines.shift();
2433
+
2434
+ for (i = 0; i < tableLines.length; ++i) {
2435
+ if (tableLines[i].trim() === '') {
2436
+ continue;
2437
+ }
2438
+ rawCells.push(
2439
+ tableLines[i]
2440
+ .split('|')
2441
+ .map(function (s) {
2442
+ return s.trim();
2443
+ })
2444
+ );
2445
+ }
2446
+
2447
+ if (rawHeaders.length < rawStyles.length) {
2448
+ return rawTable;
2449
+ }
2450
+
2451
+ for (i = 0; i < rawStyles.length; ++i) {
2452
+ styles.push(parseStyles(rawStyles[i]));
2453
+ }
2454
+
2455
+ for (i = 0; i < rawHeaders.length; ++i) {
2456
+ if (showdown.helper.isUndefined(styles[i])) {
2457
+ styles[i] = '';
2458
+ }
2459
+ headers.push(parseHeaders(rawHeaders[i], styles[i]));
2460
+ }
2461
+
2462
+ for (i = 0; i < rawCells.length; ++i) {
2463
+ var row = [];
2464
+ for (var ii = 0; ii < headers.length; ++ii) {
2465
+ if (showdown.helper.isUndefined(rawCells[i][ii])) {
2466
+
2467
+ }
2468
+ row.push(parseCells(rawCells[i][ii], styles[ii]));
2469
+ }
2470
+ cells.push(row);
2471
+ }
2472
+
2473
+ return buildTable(headers, cells);
2474
+ });
2475
+
2476
+ text = globals.converter._dispatch('tables.after', text, options, globals);
2477
+
2478
+ return text;
2479
+ });
2480
+
2481
+ /**
2482
+ * Swap back in all the special characters we've hidden.
2483
+ */
2484
+ showdown.subParser('unescapeSpecialChars', function (text) {
2485
+ 'use strict';
2486
+
2487
+ text = text.replace(/~E(\d+)E/g, function (wholeMatch, m1) {
2488
+ var charCodeToReplace = parseInt(m1);
2489
+ return String.fromCharCode(charCodeToReplace);
2490
+ });
2491
+ return text;
2492
+ });
2493
+
2494
+ var root = this;
2495
+
2496
+ // CommonJS/nodeJS Loader
2497
+ if (typeof module !== 'undefined' && module.exports) {
2498
+ module.exports = showdown;
2499
+
2500
+ // AMD Loader
2501
+ } else if (typeof define === 'function' && define.amd) {
2502
+ define(function () {
2503
+ 'use strict';
2504
+ return showdown;
2505
+ });
2506
+
2507
+ // Regular Browser loader
2508
+ } else {
2509
+ root.showdown = showdown;
1439
2510
  }
2511
+ }).call(this);
1440
2512
 
1441
- } // end of Showdown.converter
1442
-
1443
-
1444
- // export
1445
- if (typeof module !== 'undefined') module.exports = Showdown;
1446
-
1447
- // stolen from AMD branch of underscore
1448
- // AMD define happens at the end for compatibility with AMD loaders
1449
- // that don't enforce next-turn semantics on modules.
1450
- if (typeof define === 'function' && define.amd) {
1451
- define('showdown', function() {
1452
- return Showdown;
1453
- });
1454
- }
2513
+ //# sourceMappingURL=showdown.js.map