sc-docs 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,29 @@
1
+
2
+ // Koala - Formatters - HTML - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
3
+ (function() {
4
+
5
+ /**
6
+ * Escapes html entities.
7
+ *
8
+ * @param {string} str
9
+ * @return {string}
10
+ * @api private
11
+ */
12
+
13
+ function escape(str) {
14
+ return String(str)
15
+ .replace(/&/g, '&amp;')
16
+ .replace(/"/g, '&quot;')
17
+ .replace(/</g, '&lt;')
18
+ .replace(/>/g, '&gt;');
19
+ }
20
+
21
+ // --- Formatter
22
+
23
+ Koala.formatters.HTML = new Koala.Formatter(function(key, val){
24
+ return key === null
25
+ ? escape(val)
26
+ : '<span class="' + key + '">' + escape(val) + '</span>';
27
+ });
28
+
29
+ })();
@@ -0,0 +1,36 @@
1
+
2
+ // Koala - Grammars - JavaScript - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
3
+
4
+ /**
5
+ * Module dependencies.
6
+ */
7
+
8
+ // --- Grammar
9
+
10
+ Koala.lexers['js'] = new Koala.Lexer({
11
+ 'this': 'this',
12
+ 'number float': /^(\d+\.\d+)/,
13
+ 'number integer': /^(\d+)/,
14
+ 'comment': /^(\/\/[^\n]*)/,
15
+ 'keyword': /^(YES|NO|null|break|continue|do|for|import|new|void|case|default|else|function|in|return|typeof|while|delete|export|if|else|label|switch|var|with|catch|class|const|debugger|enum|extends|throw|try|finally|super|NaN)\b/,
16
+ 'string': /^("(.*?)"|'(.*?)')/,
17
+ 'class': /^([A-Z]\w*)/,
18
+ 'variable': /^([a-z_]\w*)/,
19
+ 'multiline comment': function(str){
20
+ var buf = '/*'
21
+ if (str.charAt(0) !== '/' ||
22
+ str.charAt(1) !== '*')
23
+ return
24
+ str = str.substr(2)
25
+ while (true)
26
+ if (str.charAt(0) === '*' &&
27
+ str.charAt(1) === '/')
28
+ break
29
+ else
30
+ buf += str.charAt(0),
31
+ str = str.substr(1)
32
+ str = str.substr(2)
33
+ return buf + '*/'
34
+ },
35
+ 'regexp': /^(\/(.+?)\/[a-z]*)/
36
+ });
@@ -0,0 +1,39 @@
1
+
2
+ // Koala - Grammars - Ruby - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
3
+
4
+ /**
5
+ * Module dependencies.
6
+ */
7
+
8
+ // --- Grammar
9
+
10
+ Koala.lexers['rb'] = new Koala.Lexer({
11
+ 'this': 'self',
12
+ 'number float': /^(\d+\.\d+)/,
13
+ 'number integer': /^(\d+)/,
14
+ 'comment': /^(#[^\n]*)/,
15
+ 'keyword': /^(return|defined|alias|and|begin|BEGIN|break|case|class|def|do|else|if|END|end|ensure|false|true|for|in|module|next|nil|not|or|redo|rescue|retry|return|super|then|when|true|undef|unless|until|while|yield)\b/,
16
+ 'string': /^("(.*?)"|'(.*?)')/,
17
+ 'symbol string': /^(\:(\w+|".*?"|'.*?'))/,
18
+ 'class': /^((\:\:)?[A-Z]\w*)/,
19
+ 'global variable': /^(\$[!@&`'+~=\/\w\\,;<>*$?:"\-]+)/,
20
+ 'class variable': /^(@@\w+)/,
21
+ 'inst variable': /^(@\w+)/,
22
+ 'variable': /^([a-z_]\w*)/,
23
+ 'regexp': /^(\/(.*?)\/[a-z]*)/,
24
+ 'multiline comment': function(str){
25
+ var buf = '=begin'
26
+ if (str.indexOf('=begin') !== 0)
27
+ return
28
+ str = str.substr(6)
29
+ while (true)
30
+ if (str.indexOf('=end') === 0)
31
+ break
32
+ else
33
+ buf += str.charAt(0),
34
+ str = str.substr(1)
35
+ str = str.substr(4)
36
+ return buf + '=end'
37
+ }
38
+ });
39
+
@@ -0,0 +1,65 @@
1
+
2
+ // Koala - Lexer - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
3
+ Koala.Lexer = (function() {
4
+ /**
5
+ * Initialize with hash of _rules_.
6
+ */
7
+
8
+ function Lexer(rules) {
9
+ this.rules = rules
10
+ }
11
+
12
+ Lexer.prototype = {
13
+
14
+ /**
15
+ * Scan the given _str_ returning an
16
+ * array of tokens.
17
+ *
18
+ * @param {string} str
19
+ * @return {array}
20
+ * @api public
21
+ */
22
+
23
+ scan: function(str) {
24
+ var rule, token, fn,
25
+ str = String(str),
26
+ tokens = []
27
+ while (str.length) {
28
+ for (var key in this.rules)
29
+ if (this.rules.hasOwnProperty(key))
30
+ switch ((rule = this.rules[key]).constructor) {
31
+ case Function:
32
+ var buf
33
+ if ((buf = rule.call(str, str)) != null)
34
+ str = str.substr(buf.length),
35
+ token = [key, buf]
36
+ break
37
+ case String:
38
+ if (str.indexOf(rule) === 0)
39
+ str = str.substr(rule.length),
40
+ token = [key, rule]
41
+ break
42
+ case Array:
43
+ fn = rule[1], rule = rule[0]
44
+ case RegExp:
45
+ if (str.match(rule))
46
+ str = str.substr(RegExp.$1.length),
47
+ token = [key, fn ? fn(RegExp.$1) : RegExp.$1],
48
+ fn = null
49
+ break
50
+ default:
51
+ throw new TypeError("rule `" + key + "' must be a String, RegExp, or Array")
52
+ }
53
+ if (token)
54
+ tokens.push(token),
55
+ token = null
56
+ else
57
+ tokens.push([null, str.charAt(0)]),
58
+ str = str.substr(1)
59
+ }
60
+ return tokens
61
+ }
62
+ }
63
+
64
+ return Lexer;
65
+ })();
@@ -0,0 +1,1312 @@
1
+ //
2
+ // showdown.js -- A javascript port of Markdown.
3
+ //
4
+ // Copyright (c) 2007 John Fraser.
5
+ //
6
+ // Original Markdown Copyright (c) 2004-2005 John Gruber
7
+ // <http://daringfireball.net/projects/markdown/>
8
+ //
9
+ // Redistributable under a BSD-style open source license.
10
+ // See license.txt for more information.
11
+ //
12
+ // The full source distribution is at:
13
+ //
14
+ // A A L
15
+ // T C A
16
+ // T K B
17
+ //
18
+ // <http://www.attacklab.net/>
19
+ //
20
+
21
+ //
22
+ // Wherever possible, Showdown is a straight, line-by-line port
23
+ // of the Perl version of Markdown.
24
+ //
25
+ // This is not a normal parser design; it's basically just a
26
+ // series of string substitutions. It's hard to read and
27
+ // maintain this way, but keeping Showdown close to the original
28
+ // design makes it easier to port new features.
29
+ //
30
+ // More importantly, Showdown behaves like markdown.pl in most
31
+ // edge cases. So web applications can do client-side preview
32
+ // in Javascript, and then build identical HTML on the server.
33
+ //
34
+ // This port needs the new RegExp functionality of ECMA 262,
35
+ // 3rd Edition (i.e. Javascript 1.5). Most modern web browsers
36
+ // should do fine. Even with the new regular expression features,
37
+ // We do a lot of work to emulate Perl's regex functionality.
38
+ // The tricky changes in this file mostly have the "attacklab:"
39
+ // label. Major or self-explanatory changes don't.
40
+ //
41
+ // Smart diff tools like Araxis Merge will be able to match up
42
+ // this file with markdown.pl in a useful way. A little tweaking
43
+ // helps: in a copy of markdown.pl, replace "#" with "//" and
44
+ // replace "$text" with "text". Be sure to ignore whitespace
45
+ // and line endings.
46
+ //
47
+
48
+
49
+ //
50
+ // Showdown usage:
51
+ //
52
+ // var text = "Markdown *rocks*.";
53
+ //
54
+ // var converter = new Showdown.converter();
55
+ // var html = converter.makeHtml(text);
56
+ //
57
+ // alert(html);
58
+ //
59
+ // Note: move the sample code to the bottom of this
60
+ // file before uncommenting it.
61
+ //
62
+
63
+
64
+ //
65
+ // Showdown namespace
66
+ //
67
+ var Showdown = {};
68
+
69
+ //
70
+ // converter
71
+ //
72
+ // Wraps all "globals" so that the only thing
73
+ // exposed is makeHtml().
74
+ //
75
+ Showdown.converter = function() {
76
+
77
+ //
78
+ // Globals:
79
+ //
80
+
81
+ // Global hashes, used by various utility routines
82
+ var g_urls;
83
+ var g_titles;
84
+ var g_html_blocks;
85
+
86
+ // Used to track when we're inside an ordered or unordered list
87
+ // (see _ProcessListItems() for details):
88
+ var g_list_level = 0;
89
+
90
+
91
+ this.makeHtml = function(text) {
92
+ //
93
+ // Main function. The order in which other subs are called here is
94
+ // essential. Link and image substitutions need to happen before
95
+ // _EscapeSpecialCharsWithinTagAttributes(), so that any *'s or _'s in the <a>
96
+ // and <img> tags get encoded.
97
+ //
98
+
99
+ // Clear the global hashes. If we don't clear these, you get conflicts
100
+ // from other articles when generating a page which contains more than
101
+ // one article (e.g. an index page that shows the N most recent
102
+ // articles):
103
+ g_urls = new Array();
104
+ g_titles = new Array();
105
+ g_html_blocks = new Array();
106
+
107
+ // attacklab: Replace ~ with ~T
108
+ // This lets us use tilde as an escape char to avoid md5 hashes
109
+ // The choice of character is arbitray; anything that isn't
110
+ // magic in Markdown will work.
111
+ text = text.replace(/~/g,"~T");
112
+
113
+ // attacklab: Replace $ with ~D
114
+ // RegExp interprets $ as a special character
115
+ // when it's in a replacement string
116
+ text = text.replace(/\$/g,"~D");
117
+
118
+ // Standardize line endings
119
+ text = text.replace(/\r\n/g,"\n"); // DOS to Unix
120
+ text = text.replace(/\r/g,"\n"); // Mac to Unix
121
+
122
+ // Make sure text begins and ends with a couple of newlines:
123
+ text = "\n\n" + text + "\n\n";
124
+
125
+ // Convert all tabs to spaces.
126
+ text = _Detab(text);
127
+
128
+ // Strip any lines consisting only of spaces and tabs.
129
+ // This makes subsequent regexen easier to write, because we can
130
+ // match consecutive blank lines with /\n+/ instead of something
131
+ // contorted like /[ \t]*\n+/ .
132
+ text = text.replace(/^[ \t]+$/mg,"");
133
+
134
+ // Turn block-level HTML blocks into hash entries
135
+ text = _HashHTMLBlocks(text);
136
+
137
+ // Strip link definitions, store in hashes.
138
+ text = _StripLinkDefinitions(text);
139
+
140
+ text = _RunBlockGamut(text);
141
+
142
+ text = _UnescapeSpecialChars(text);
143
+
144
+ // attacklab: Restore dollar signs
145
+ text = text.replace(/~D/g,"$$");
146
+
147
+ // attacklab: Restore tildes
148
+ text = text.replace(/~T/g,"~");
149
+
150
+ return text;
151
+ }
152
+
153
+
154
+ var _StripLinkDefinitions = function(text) {
155
+ //
156
+ // Strips link definitions from text, stores the URLs and titles in
157
+ // hash references.
158
+ //
159
+
160
+ // Link defs are in the form: ^[id]: url "optional title"
161
+
162
+ /*
163
+ var text = text.replace(/
164
+ ^[ ]{0,3}\[(.+)\]: // id = $1 attacklab: g_tab_width - 1
165
+ [ \t]*
166
+ \n? // maybe *one* newline
167
+ [ \t]*
168
+ <?(\S+?)>? // url = $2
169
+ [ \t]*
170
+ \n? // maybe one newline
171
+ [ \t]*
172
+ (?:
173
+ (\n*) // any lines skipped = $3 attacklab: lookbehind removed
174
+ ["(]
175
+ (.+?) // title = $4
176
+ [")]
177
+ [ \t]*
178
+ )? // title is optional
179
+ (?:\n+|$)
180
+ /gm,
181
+ function(){...});
182
+ */
183
+ var text = text.replace(/^[ ]{0,3}\[(.+)\]:[ \t]*\n?[ \t]*<?(\S+?)>?[ \t]*\n?[ \t]*(?:(\n*)["(](.+?)[")][ \t]*)?(?:\n+|\Z)/gm,
184
+ function (wholeMatch,m1,m2,m3,m4) {
185
+ m1 = m1.toLowerCase();
186
+ g_urls[m1] = _EncodeAmpsAndAngles(m2); // Link IDs are case-insensitive
187
+ if (m3) {
188
+ // Oops, found blank lines, so it's not a title.
189
+ // Put back the parenthetical statement we stole.
190
+ return m3+m4;
191
+ } else if (m4) {
192
+ g_titles[m1] = m4.replace(/"/g,"&quot;");
193
+ }
194
+
195
+ // Completely remove the definition from the text
196
+ return "";
197
+ }
198
+ );
199
+
200
+ return text;
201
+ }
202
+
203
+
204
+ var _HashHTMLBlocks = function(text) {
205
+ // attacklab: Double up blank lines to reduce lookaround
206
+ text = text.replace(/\n/g,"\n\n");
207
+
208
+ // Hashify HTML blocks:
209
+ // We only want to do this for block-level HTML tags, such as headers,
210
+ // lists, and tables. That's because we still want to wrap <p>s around
211
+ // "paragraphs" that are wrapped in non-block-level tags, such as anchors,
212
+ // phrase emphasis, and spans. The list of tags we're looking for is
213
+ // hard-coded:
214
+ var block_tags_a = "p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del"
215
+ var block_tags_b = "p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math"
216
+
217
+ // First, look for nested blocks, e.g.:
218
+ // <div>
219
+ // <div>
220
+ // tags for inner block must be indented.
221
+ // </div>
222
+ // </div>
223
+ //
224
+ // The outermost tags must start at the left margin for this to match, and
225
+ // the inner nested divs must be indented.
226
+ // We need to do this before the next, more liberal match, because the next
227
+ // match will start at the first `<div>` and stop at the first `</div>`.
228
+
229
+ // attacklab: This regex can be expensive when it fails.
230
+ /*
231
+ var text = text.replace(/
232
+ ( // save in $1
233
+ ^ // start of line (with /m)
234
+ <($block_tags_a) // start tag = $2
235
+ \b // word break
236
+ // attacklab: hack around khtml/pcre bug...
237
+ [^\r]*?\n // any number of lines, minimally matching
238
+ </\2> // the matching end tag
239
+ [ \t]* // trailing spaces/tabs
240
+ (?=\n+) // followed by a newline
241
+ ) // attacklab: there are sentinel newlines at end of document
242
+ /gm,function(){...}};
243
+ */
244
+ 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);
245
+
246
+ //
247
+ // Now match more liberally, simply from `\n<tag>` to `</tag>\n`
248
+ //
249
+
250
+ /*
251
+ var text = text.replace(/
252
+ ( // save in $1
253
+ ^ // start of line (with /m)
254
+ <($block_tags_b) // start tag = $2
255
+ \b // word break
256
+ // attacklab: hack around khtml/pcre bug...
257
+ [^\r]*? // any number of lines, minimally matching
258
+ .*</\2> // the matching end tag
259
+ [ \t]* // trailing spaces/tabs
260
+ (?=\n+) // followed by a newline
261
+ ) // attacklab: there are sentinel newlines at end of document
262
+ /gm,function(){...}};
263
+ */
264
+ text = text.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math)\b[^\r]*?.*<\/\2>[ \t]*(?=\n+)\n)/gm,hashElement);
265
+
266
+ // Special case just for <hr />. It was easier to make a special case than
267
+ // to make the other regex more complicated.
268
+
269
+ /*
270
+ text = text.replace(/
271
+ ( // save in $1
272
+ \n\n // Starting after a blank line
273
+ [ ]{0,3}
274
+ (<(hr) // start tag = $2
275
+ \b // word break
276
+ ([^<>])*? //
277
+ \/?>) // the matching end tag
278
+ [ \t]*
279
+ (?=\n{2,}) // followed by a blank line
280
+ )
281
+ /g,hashElement);
282
+ */
283
+ text = text.replace(/(\n[ ]{0,3}(<(hr)\b([^<>])*?\/?>)[ \t]*(?=\n{2,}))/g,hashElement);
284
+
285
+ // Special case for standalone HTML comments:
286
+
287
+ /*
288
+ text = text.replace(/
289
+ ( // save in $1
290
+ \n\n // Starting after a blank line
291
+ [ ]{0,3} // attacklab: g_tab_width - 1
292
+ <!
293
+ (--[^\r]*?--\s*)+
294
+ >
295
+ [ \t]*
296
+ (?=\n{2,}) // followed by a blank line
297
+ )
298
+ /g,hashElement);
299
+ */
300
+ text = text.replace(/(\n\n[ ]{0,3}<!(--[^\r]*?--\s*)+>[ \t]*(?=\n{2,}))/g,hashElement);
301
+
302
+ // PHP and ASP-style processor instructions (<?...?> and <%...%>)
303
+
304
+ /*
305
+ text = text.replace(/
306
+ (?:
307
+ \n\n // Starting after a blank line
308
+ )
309
+ ( // save in $1
310
+ [ ]{0,3} // attacklab: g_tab_width - 1
311
+ (?:
312
+ <([?%]) // $2
313
+ [^\r]*?
314
+ \2>
315
+ )
316
+ [ \t]*
317
+ (?=\n{2,}) // followed by a blank line
318
+ )
319
+ /g,hashElement);
320
+ */
321
+ text = text.replace(/(?:\n\n)([ ]{0,3}(?:<([?%])[^\r]*?\2>)[ \t]*(?=\n{2,}))/g,hashElement);
322
+
323
+ // attacklab: Undo double lines (see comment at top of this function)
324
+ text = text.replace(/\n\n/g,"\n");
325
+ return text;
326
+ }
327
+
328
+ var hashElement = function(wholeMatch,m1) {
329
+ var blockText = m1;
330
+
331
+ // Undo double lines
332
+ blockText = blockText.replace(/\n\n/g,"\n");
333
+ blockText = blockText.replace(/^\n/,"");
334
+
335
+ // strip trailing blank lines
336
+ blockText = blockText.replace(/\n+$/g,"");
337
+
338
+ // Replace the element text with a marker ("~KxK" where x is its key)
339
+ blockText = "\n\n~K" + (g_html_blocks.push(blockText)-1) + "K\n\n";
340
+
341
+ return blockText;
342
+ };
343
+
344
+ var _RunBlockGamut = function(text) {
345
+ //
346
+ // These are all the transformations that form block-level
347
+ // tags like paragraphs, headers, and list items.
348
+ //
349
+ text = _DoHeaders(text);
350
+
351
+ // Do Horizontal Rules:
352
+ var key = hashBlock("<hr />");
353
+ text = text.replace(/^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$/gm,key);
354
+ text = text.replace(/^[ ]{0,2}([ ]?\-[ ]?){3,}[ \t]*$/gm,key);
355
+ text = text.replace(/^[ ]{0,2}([ ]?\_[ ]?){3,}[ \t]*$/gm,key);
356
+
357
+ text = _DoLists(text);
358
+ text = _DoCodeBlocks(text);
359
+ text = _DoBlockQuotes(text);
360
+
361
+ // We already ran _HashHTMLBlocks() before, in Markdown(), but that
362
+ // was to escape raw HTML in the original Markdown source. This time,
363
+ // we're escaping the markup we've just created, so that we don't wrap
364
+ // <p> tags around block-level tags.
365
+ text = _HashHTMLBlocks(text);
366
+ text = _FormParagraphs(text);
367
+
368
+ return text;
369
+ }
370
+
371
+
372
+ var _RunSpanGamut = function(text) {
373
+ //
374
+ // These are all the transformations that occur *within* block-level
375
+ // tags like paragraphs, headers, and list items.
376
+ //
377
+
378
+ // SC HACK: REPLACE ANYTHING THAT LOOKS LIKE AN SC VARIABLE WITH A CODE SPAN
379
+ // Addressing two cases:
380
+ // lowerThenUpper
381
+ // Upper.SomethingElse
382
+ //
383
+ // We cannot reliably do UpperSomethingElse: that could be a name. Like McDonalds.
384
+ text = text.replace(/(^|\s)([a-z][a-z0-9_]*[A-Z][A-Za-z0-9_]+)/g, "$1`$2`");
385
+ text = text.replace(/(^|\s)([A-Z][A-Za-z0-9]*[0._][A-Za-z0-9_.("'$)]+)/g, "$1`$2`")
386
+ text = text.replace(/(\s|$)(YES|NO)([\s.;,\/]|$)/, "$1`$2`$3");
387
+
388
+ text = _DoCodeSpans(text);
389
+ text = _EscapeSpecialCharsWithinTagAttributes(text);
390
+ text = _EncodeBackslashEscapes(text);
391
+
392
+ // Process anchor and image tags. Images must come first,
393
+ // because ![foo][f] looks like an anchor.
394
+ text = _DoImages(text);
395
+ text = _DoAnchors(text);
396
+
397
+ // Make links out of things like `<http://example.com/>`
398
+ // Must come after _DoAnchors(), because you can use < and >
399
+ // delimiters in inline links like [this](<url>).
400
+ text = _DoAutoLinks(text);
401
+ text = _EncodeAmpsAndAngles(text);
402
+ text = _DoItalicsAndBold(text);
403
+
404
+ // Do hard breaks:
405
+ text = text.replace(/ +\n/g," <br />\n");
406
+
407
+ return text;
408
+ }
409
+
410
+ var _EscapeSpecialCharsWithinTagAttributes = function(text) {
411
+ //
412
+ // Within tags -- meaning between < and > -- encode [\ ` * _] so they
413
+ // don't conflict with their use in Markdown for code, italics and strong.
414
+ //
415
+
416
+ // Build a regex to find HTML tags and comments. See Friedl's
417
+ // "Mastering Regular Expressions", 2nd Ed., pp. 200-201.
418
+ var regex = /(<[a-z\/!$]("[^"]*"|'[^']*'|[^'">])*>|<!(--.*?--\s*)+>)/gi;
419
+
420
+ text = text.replace(regex, function(wholeMatch) {
421
+ var tag = wholeMatch.replace(/(.)<\/?code>(?=.)/g,"$1`");
422
+ tag = escapeCharacters(tag,"\\`*_");
423
+ return tag;
424
+ });
425
+
426
+ return text;
427
+ }
428
+
429
+ var _DoAnchors = function(text) {
430
+ //
431
+ // Turn Markdown link shortcuts into XHTML <a> tags.
432
+ //
433
+ //
434
+ // First, handle reference-style links: [link text] [id]
435
+ //
436
+
437
+ /*
438
+ text = text.replace(/
439
+ ( // wrap whole match in $1
440
+ \[
441
+ (
442
+ (?:
443
+ \[[^\]]*\] // allow brackets nested one level
444
+ |
445
+ [^\[] // or anything else
446
+ )*
447
+ )
448
+ \]
449
+
450
+ [ ]? // one optional space
451
+ (?:\n[ ]*)? // one optional newline followed by spaces
452
+
453
+ \[
454
+ (.*?) // id = $3
455
+ \]
456
+ )()()()() // pad remaining backreferences
457
+ /g,_DoAnchors_callback);
458
+ */
459
+ text = text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g,writeAnchorTag);
460
+
461
+ //
462
+ // Next, inline-style links: [link text](url "optional title")
463
+ //
464
+
465
+ /*
466
+ text = text.replace(/
467
+ ( // wrap whole match in $1
468
+ \[
469
+ (
470
+ (?:
471
+ \[[^\]]*\] // allow brackets nested one level
472
+ |
473
+ [^\[\]] // or anything else
474
+ )
475
+ )
476
+ \]
477
+ \( // literal paren
478
+ [ \t]*
479
+ () // no id, so leave $3 empty
480
+ <?(.*?)>? // href = $4
481
+ [ \t]*
482
+ ( // $5
483
+ (['"]) // quote char = $6
484
+ (.*?) // Title = $7
485
+ \6 // matching quote
486
+ [ \t]* // ignore any spaces/tabs between closing quote and )
487
+ )? // title is optional
488
+ \)
489
+ )
490
+ /g,writeAnchorTag);
491
+ */
492
+ text = text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\]\([ \t]*()<?(.*?)>?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,writeAnchorTag);
493
+
494
+ //
495
+ // Last, handle reference-style shortcuts: [link text]
496
+ // These must come last in case you've also got [link test][1]
497
+ // or [link test](/foo)
498
+ //
499
+
500
+ /*
501
+ text = text.replace(/
502
+ ( // wrap whole match in $1
503
+ \[
504
+ ([^\[\]]+) // link text = $2; can't contain '[' or ']'
505
+ \]
506
+ )()()()()() // pad rest of backreferences
507
+ /g, writeAnchorTag);
508
+ */
509
+ text = text.replace(/(\[([^\[\]]+)\])()()()()()/g, writeAnchorTag);
510
+
511
+ return text;
512
+ }
513
+
514
+ var writeAnchorTag = function(wholeMatch,m1,m2,m3,m4,m5,m6,m7) {
515
+ if (m7 == undefined) m7 = "";
516
+ var whole_match = m1;
517
+ var link_text = m2;
518
+ var link_id = m3.toLowerCase();
519
+ var url = m4;
520
+ var title = m7;
521
+
522
+ if (url == "") {
523
+ if (link_id == "") {
524
+ // lower-case and turn embedded newlines into spaces
525
+ link_id = link_text.toLowerCase().replace(/ ?\n/g," ");
526
+ }
527
+ url = "#"+link_id;
528
+
529
+ if (g_urls[link_id] != undefined) {
530
+ url = g_urls[link_id];
531
+ if (g_titles[link_id] != undefined) {
532
+ title = g_titles[link_id];
533
+ }
534
+ }
535
+ else {
536
+ if (whole_match.search(/\(\s*\)$/m)>-1) {
537
+ // Special case for explicit empty url
538
+ url = "";
539
+ } else {
540
+ return whole_match;
541
+ }
542
+ }
543
+ }
544
+
545
+ url = escapeCharacters(url,"*_");
546
+ var result = "<a href=\"" + url + "\"";
547
+
548
+ if (title != "") {
549
+ title = title.replace(/"/g,"&quot;");
550
+ title = escapeCharacters(title,"*_");
551
+ result += " title=\"" + title + "\"";
552
+ }
553
+
554
+ result += ">" + link_text + "</a>";
555
+
556
+ return result;
557
+ }
558
+
559
+
560
+ var _DoImages = function(text) {
561
+ //
562
+ // Turn Markdown image shortcuts into <img> tags.
563
+ //
564
+
565
+ //
566
+ // First, handle reference-style labeled images: ![alt text][id]
567
+ //
568
+
569
+ /*
570
+ text = text.replace(/
571
+ ( // wrap whole match in $1
572
+ !\[
573
+ (.*?) // alt text = $2
574
+ \]
575
+
576
+ [ ]? // one optional space
577
+ (?:\n[ ]*)? // one optional newline followed by spaces
578
+
579
+ \[
580
+ (.*?) // id = $3
581
+ \]
582
+ )()()()() // pad rest of backreferences
583
+ /g,writeImageTag);
584
+ */
585
+ text = text.replace(/(!\[(.*?)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g,writeImageTag);
586
+
587
+ //
588
+ // Next, handle inline images: ![alt text](url "optional title")
589
+ // Don't forget: encode * and _
590
+
591
+ /*
592
+ text = text.replace(/
593
+ ( // wrap whole match in $1
594
+ !\[
595
+ (.*?) // alt text = $2
596
+ \]
597
+ \s? // One optional whitespace character
598
+ \( // literal paren
599
+ [ \t]*
600
+ () // no id, so leave $3 empty
601
+ <?(\S+?)>? // src url = $4
602
+ [ \t]*
603
+ ( // $5
604
+ (['"]) // quote char = $6
605
+ (.*?) // title = $7
606
+ \6 // matching quote
607
+ [ \t]*
608
+ )? // title is optional
609
+ \)
610
+ )
611
+ /g,writeImageTag);
612
+ */
613
+ text = text.replace(/(!\[(.*?)\]\s?\([ \t]*()<?(\S+?)>?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,writeImageTag);
614
+
615
+ return text;
616
+ }
617
+
618
+ var writeImageTag = function(wholeMatch,m1,m2,m3,m4,m5,m6,m7) {
619
+ var whole_match = m1;
620
+ var alt_text = m2;
621
+ var link_id = m3.toLowerCase();
622
+ var url = m4;
623
+ var title = m7;
624
+
625
+ if (!title) title = "";
626
+
627
+ if (url == "") {
628
+ if (link_id == "") {
629
+ // lower-case and turn embedded newlines into spaces
630
+ link_id = alt_text.toLowerCase().replace(/ ?\n/g," ");
631
+ }
632
+ url = "#"+link_id;
633
+
634
+ if (g_urls[link_id] != undefined) {
635
+ url = g_urls[link_id];
636
+ if (g_titles[link_id] != undefined) {
637
+ title = g_titles[link_id];
638
+ }
639
+ }
640
+ else {
641
+ return whole_match;
642
+ }
643
+ }
644
+
645
+ alt_text = alt_text.replace(/"/g,"&quot;");
646
+ url = escapeCharacters(url,"*_");
647
+ var result = "<img src=\"" + url + "\" alt=\"" + alt_text + "\"";
648
+
649
+ // attacklab: Markdown.pl adds empty title attributes to images.
650
+ // Replicate this bug.
651
+
652
+ //if (title != "") {
653
+ title = title.replace(/"/g,"&quot;");
654
+ title = escapeCharacters(title,"*_");
655
+ result += " title=\"" + title + "\"";
656
+ //}
657
+
658
+ result += " />";
659
+
660
+ return result;
661
+ }
662
+
663
+
664
+ var _DoHeaders = function(text) {
665
+
666
+ // Setext-style headers:
667
+ // Header 1
668
+ // ========
669
+ //
670
+ // Header 2
671
+ // --------
672
+ //
673
+ text = text.replace(/^(.+)[ \t]*\n=+[ \t]*\n+/gm,
674
+ function(wholeMatch,m1){return hashBlock('<h1 id="' + headerId(m1) + '">' + _RunSpanGamut(m1) + "</h1>");});
675
+
676
+ text = text.replace(/^(.+)[ \t]*\n-+[ \t]*\n+/gm,
677
+ function(matchFound,m1){return hashBlock('<h2 id="' + headerId(m1) + '">' + _RunSpanGamut(m1) + "</h2>");});
678
+
679
+ // atx-style headers:
680
+ // # Header 1
681
+ // ## Header 2
682
+ // ## Header 2 with closing hashes ##
683
+ // ...
684
+ // ###### Header 6
685
+ //
686
+
687
+ /*
688
+ text = text.replace(/
689
+ ^(\#{1,6}) // $1 = string of #'s
690
+ [ \t]*
691
+ (.+?) // $2 = Header text
692
+ [ \t]*
693
+ \#* // optional closing #'s (not counted)
694
+ \n+
695
+ /gm, function() {...});
696
+ */
697
+
698
+ text = text.replace(/^(\#{1,6})[ \t]*(.+?)[ \t]*\#*\n+/gm,
699
+ function(wholeMatch,m1,m2) {
700
+ var h_level = m1.length;
701
+ return hashBlock("<h" + h_level + ' id="' + headerId(m2) + '">' + _RunSpanGamut(m2) + "</h" + h_level + ">");
702
+ });
703
+
704
+ function headerId(m) {
705
+ return m.replace(/[^\w]/g, '').toLowerCase();
706
+ }
707
+ return text;
708
+ }
709
+
710
+ // This declaration keeps Dojo compressor from outputting garbage:
711
+ var _ProcessListItems;
712
+
713
+ var _DoLists = function(text) {
714
+ //
715
+ // Form HTML ordered (numbered) and unordered (bulleted) lists.
716
+ //
717
+
718
+ // attacklab: add sentinel to hack around khtml/safari bug:
719
+ // http://bugs.webkit.org/show_bug.cgi?id=11231
720
+ text += "~0";
721
+
722
+ // Re-usable pattern to match any entirel ul or ol list:
723
+
724
+ /*
725
+ var whole_list = /
726
+ ( // $1 = whole list
727
+ ( // $2
728
+ [ ]{0,3} // attacklab: g_tab_width - 1
729
+ ([*+-]|\d+[.]) // $3 = first list item marker
730
+ [ \t]+
731
+ )
732
+ [^\r]+?
733
+ ( // $4
734
+ ~0 // sentinel for workaround; should be $
735
+ |
736
+ \n{2,}
737
+ (?=\S)
738
+ (?! // Negative lookahead for another list item marker
739
+ [ \t]*
740
+ (?:[*+-]|\d+[.])[ \t]+
741
+ )
742
+ )
743
+ )/g
744
+ */
745
+ var whole_list = /^(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm;
746
+
747
+ if (g_list_level) {
748
+ text = text.replace(whole_list,function(wholeMatch,m1,m2) {
749
+ var list = m1;
750
+ var list_type = (m2.search(/[*+-]/g)>-1) ? "ul" : "ol";
751
+
752
+ // Turn double returns into triple returns, so that we can make a
753
+ // paragraph for the last item in a list, if necessary:
754
+ list = list.replace(/\n{2,}/g,"\n\n\n");;
755
+ var result = _ProcessListItems(list);
756
+
757
+ // Trim any trailing whitespace, to put the closing `</$list_type>`
758
+ // up on the preceding line, to get it past the current stupid
759
+ // HTML block parser. This is a hack to work around the terrible
760
+ // hack that is the HTML block parser.
761
+ result = result.replace(/\s+$/,"");
762
+ result = "<"+list_type+">" + result + "</"+list_type+">\n";
763
+ return result;
764
+ });
765
+ } else {
766
+ whole_list = /(\n\n|^\n?)(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/g;
767
+ text = text.replace(whole_list,function(wholeMatch,m1,m2,m3) {
768
+ var runup = m1;
769
+ var list = m2;
770
+
771
+ var list_type = (m3.search(/[*+-]/g)>-1) ? "ul" : "ol";
772
+ // Turn double returns into triple returns, so that we can make a
773
+ // paragraph for the last item in a list, if necessary:
774
+ var list = list.replace(/\n{2,}/g,"\n\n\n");;
775
+ var result = _ProcessListItems(list);
776
+ result = runup + "<"+list_type+">\n" + result + "</"+list_type+">\n";
777
+ return result;
778
+ });
779
+ }
780
+
781
+ // attacklab: strip sentinel
782
+ text = text.replace(/~0/,"");
783
+
784
+ return text;
785
+ }
786
+
787
+ _ProcessListItems = function(list_str) {
788
+ //
789
+ // Process the contents of a single ordered or unordered list, splitting it
790
+ // into individual list items.
791
+ //
792
+ // The $g_list_level global keeps track of when we're inside a list.
793
+ // Each time we enter a list, we increment it; when we leave a list,
794
+ // we decrement. If it's zero, we're not in a list anymore.
795
+ //
796
+ // We do this because when we're not inside a list, we want to treat
797
+ // something like this:
798
+ //
799
+ // I recommend upgrading to version
800
+ // 8. Oops, now this line is treated
801
+ // as a sub-list.
802
+ //
803
+ // As a single paragraph, despite the fact that the second line starts
804
+ // with a digit-period-space sequence.
805
+ //
806
+ // Whereas when we're inside a list (or sub-list), that line will be
807
+ // treated as the start of a sub-list. What a kludge, huh? This is
808
+ // an aspect of Markdown's syntax that's hard to parse perfectly
809
+ // without resorting to mind-reading. Perhaps the solution is to
810
+ // change the syntax rules such that sub-lists must start with a
811
+ // starting cardinal number; e.g. "1." or "a.".
812
+
813
+ g_list_level++;
814
+
815
+ // trim trailing blank lines:
816
+ list_str = list_str.replace(/\n{2,}$/,"\n");
817
+
818
+ // attacklab: add sentinel to emulate \z
819
+ list_str += "~0";
820
+
821
+ /*
822
+ list_str = list_str.replace(/
823
+ (\n)? // leading line = $1
824
+ (^[ \t]*) // leading whitespace = $2
825
+ ([*+-]|\d+[.]) [ \t]+ // list marker = $3
826
+ ([^\r]+? // list item text = $4
827
+ (\n{1,2}))
828
+ (?= \n* (~0 | \2 ([*+-]|\d+[.]) [ \t]+))
829
+ /gm, function(){...});
830
+ */
831
+ list_str = list_str.replace(/(\n)?(^[ \t]*)([*+-]|\d+[.])[ \t]+([^\r]+?(\n{1,2}))(?=\n*(~0|\2([*+-]|\d+[.])[ \t]+))/gm,
832
+ function(wholeMatch,m1,m2,m3,m4){
833
+ var item = m4;
834
+ var leading_line = m1;
835
+ var leading_space = m2;
836
+
837
+ if (leading_line || (item.search(/\n{2,}/)>-1)) {
838
+ item = _RunBlockGamut(_Outdent(item));
839
+ }
840
+ else {
841
+ // Recursion for sub-lists:
842
+ item = _DoLists(_Outdent(item));
843
+ item = item.replace(/\n$/,""); // chomp(item)
844
+ item = _RunSpanGamut(item);
845
+ }
846
+
847
+ return "<li>" + item + "</li>\n";
848
+ }
849
+ );
850
+
851
+ // attacklab: strip sentinel
852
+ list_str = list_str.replace(/~0/g,"");
853
+
854
+ g_list_level--;
855
+ return list_str;
856
+ }
857
+
858
+
859
+ var _DoCodeBlocks = function(text) {
860
+ //
861
+ // Process Markdown `<pre><code>` blocks.
862
+ //
863
+
864
+ /*
865
+ text = text.replace(text,
866
+ /(?:\n\n|^)
867
+ ( // $1 = the code block -- one or more lines, starting with a space/tab
868
+ (?:
869
+ (?:[ ]{4}|\t) // Lines must start with a tab or a tab-width of spaces - attacklab: g_tab_width
870
+ .*\n+
871
+ )+
872
+ )
873
+ (\n*[ ]{0,3}[^ \t\n]|(?=~0)) // attacklab: g_tab_width
874
+ /g,function(){...});
875
+ */
876
+
877
+ // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
878
+ text += "~0";
879
+
880
+ text = text.replace(/(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=~0))/g,
881
+ function(wholeMatch,m1,m2) {
882
+ var codeblock = m1;
883
+ var nextChar = m2;
884
+
885
+ codeblock = _EncodeCode( _Outdent(codeblock));
886
+ codeblock = _Detab(codeblock);
887
+ codeblock = codeblock.replace(/^\n+/g,""); // trim leading newlines
888
+ codeblock = codeblock.replace(/\n+$/g,""); // trim trailing whitespace
889
+
890
+ codeblock = "<pre><code>" + codeblock + "\n</code></pre>";
891
+
892
+ return hashBlock(codeblock) + nextChar;
893
+ }
894
+ );
895
+
896
+ // attacklab: strip sentinel
897
+ text = text.replace(/~0/,"");
898
+
899
+ return text;
900
+ }
901
+
902
+ var hashBlock = function(text) {
903
+ text = text.replace(/(^\n+|\n+$)/g,"");
904
+ return "\n\n~K" + (g_html_blocks.push(text)-1) + "K\n\n";
905
+ }
906
+
907
+
908
+ var _DoCodeSpans = function(text) {
909
+ //
910
+ // * Backtick quotes are used for <code></code> spans.
911
+ //
912
+ // * You can use multiple backticks as the delimiters if you want to
913
+ // include literal backticks in the code span. So, this input:
914
+ //
915
+ // Just type ``foo `bar` baz`` at the prompt.
916
+ //
917
+ // Will translate to:
918
+ //
919
+ // <p>Just type <code>foo `bar` baz</code> at the prompt.</p>
920
+ //
921
+ // There's no arbitrary limit to the number of backticks you
922
+ // can use as delimters. If you need three consecutive backticks
923
+ // in your code, use four for delimiters, etc.
924
+ //
925
+ // * You can use spaces to get literal backticks at the edges:
926
+ //
927
+ // ... type `` `bar` `` ...
928
+ //
929
+ // Turns to:
930
+ //
931
+ // ... type <code>`bar`</code> ...
932
+ //
933
+
934
+ /*
935
+ text = text.replace(/
936
+ (^|[^\\]) // Character before opening ` can't be a backslash
937
+ (`+) // $2 = Opening run of `
938
+ ( // $3 = The code block
939
+ [^\r]*?
940
+ [^`] // attacklab: work around lack of lookbehind
941
+ )
942
+ \2 // Matching closer
943
+ (?!`)
944
+ /gm, function(){...});
945
+ */
946
+
947
+ text = text.replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm,
948
+ function(wholeMatch,m1,m2,m3,m4) {
949
+ var c = m3;
950
+ c = c.replace(/^([ \t]*)/g,""); // leading whitespace
951
+ c = c.replace(/[ \t]*$/g,""); // trailing whitespace
952
+ c = _EncodeCode(c);
953
+ return m1+"<code>"+c+"</code>";
954
+ });
955
+
956
+ return text;
957
+ }
958
+
959
+
960
+ var _EncodeCode = function(text) {
961
+ //
962
+ // Encode/escape certain characters inside Markdown code runs.
963
+ // The point is that in code, these characters are literals,
964
+ // and lose their special Markdown meanings.
965
+ //
966
+ // Encode all ampersands; HTML entities are not
967
+ // entities within a Markdown code span.
968
+ text = text.replace(/&/g,"&amp;");
969
+
970
+ // Do the angle bracket song and dance:
971
+ text = text.replace(/</g,"&lt;");
972
+ text = text.replace(/>/g,"&gt;");
973
+
974
+ // Now, escape characters that are magic in Markdown:
975
+ text = escapeCharacters(text,"\*_{}[]\\",false);
976
+
977
+ // jj the line above breaks this:
978
+ //---
979
+
980
+ //* Item
981
+
982
+ // 1. Subitem
983
+
984
+ // special char: *
985
+ //---
986
+
987
+ return text;
988
+ }
989
+
990
+
991
+ var _DoItalicsAndBold = function(text) {
992
+
993
+ // <strong> must go first:
994
+ text = text.replace(/(^|\s)(\*\*|__)(?=\S)([^\n\r]*?\S[*_]*)\2/g,
995
+ "<strong>$1$3</strong>");
996
+
997
+ text = text.replace(/(^|\s)(\*|_)(?=\S)([^\r]*?\S)\2/g,
998
+ "<em>$1$3</em>");
999
+
1000
+ return text;
1001
+ }
1002
+
1003
+
1004
+ var _DoBlockQuotes = function(text) {
1005
+
1006
+ /*
1007
+ text = text.replace(/
1008
+ ( // Wrap whole match in $1
1009
+ (
1010
+ ^[ \t]*>[ \t]? // '>' at the start of a line
1011
+ .+\n // rest of the first line
1012
+ (.+\n)* // subsequent consecutive lines
1013
+ \n* // blanks
1014
+ )+
1015
+ )
1016
+ /gm, function(){...});
1017
+ */
1018
+
1019
+ text = text.replace(/((^[ \t]*>[ \t]?.+\n(.+\n)*\n*)+)/gm,
1020
+ function(wholeMatch,m1) {
1021
+ var bq = m1;
1022
+
1023
+ // attacklab: hack around Konqueror 3.5.4 bug:
1024
+ // "----------bug".replace(/^-/g,"") == "bug"
1025
+
1026
+ bq = bq.replace(/^[ \t]*>[ \t]?/gm,"~0"); // trim one level of quoting
1027
+
1028
+ // attacklab: clean up hack
1029
+ bq = bq.replace(/~0/g,"");
1030
+
1031
+ bq = bq.replace(/^[ \t]+$/gm,""); // trim whitespace-only lines
1032
+ bq = _RunBlockGamut(bq); // recurse
1033
+
1034
+ bq = bq.replace(/(^|\n)/g,"$1 ");
1035
+ // These leading spaces screw with <pre> content, so we need to fix that:
1036
+ bq = bq.replace(
1037
+ /(\s*<pre>[^\r]+?<\/pre>)/gm,
1038
+ function(wholeMatch,m1) {
1039
+ var pre = m1;
1040
+ // attacklab: hack around Konqueror 3.5.4 bug:
1041
+ pre = pre.replace(/^ /mg,"~0");
1042
+ pre = pre.replace(/~0/g,"");
1043
+ return pre;
1044
+ });
1045
+
1046
+ return hashBlock("<blockquote>\n" + bq + "\n</blockquote>");
1047
+ });
1048
+ return text;
1049
+ }
1050
+
1051
+
1052
+ var _FormParagraphs = function(text) {
1053
+ //
1054
+ // Params:
1055
+ // $text - string to process with html <p> tags
1056
+ //
1057
+
1058
+ // Strip leading and trailing lines:
1059
+ text = text.replace(/^\n+/g,"");
1060
+ text = text.replace(/\n+$/g,"");
1061
+
1062
+ var grafs = text.split(/\n{2,}/g);
1063
+ var grafsOut = new Array();
1064
+
1065
+ //
1066
+ // Wrap <p> tags.
1067
+ //
1068
+ var end = grafs.length;
1069
+ for (var i=0; i<end; i++) {
1070
+ var str = grafs[i];
1071
+
1072
+ // if this is an HTML marker, copy it
1073
+ if (str.search(/~K(\d+)K/g) >= 0) {
1074
+ grafsOut.push(str);
1075
+ }
1076
+ else if (str.search(/\S/) >= 0) {
1077
+ str = _RunSpanGamut(str);
1078
+ str = str.replace(/^([ \t]*)/g,"<p>");
1079
+ str += "</p>"
1080
+ grafsOut.push(str);
1081
+ }
1082
+
1083
+ }
1084
+
1085
+ //
1086
+ // Unhashify HTML blocks
1087
+ //
1088
+ end = grafsOut.length;
1089
+ for (var i=0; i<end; i++) {
1090
+ // if this is a marker for an html block...
1091
+ while (grafsOut[i].search(/~K(\d+)K/) >= 0) {
1092
+ var blockText = g_html_blocks[RegExp.$1];
1093
+ blockText = blockText.replace(/\$/g,"$$$$"); // Escape any dollar signs
1094
+ grafsOut[i] = grafsOut[i].replace(/~K\d+K/,blockText);
1095
+ }
1096
+ }
1097
+
1098
+ return grafsOut.join("\n\n");
1099
+ }
1100
+
1101
+
1102
+ var _EncodeAmpsAndAngles = function(text) {
1103
+ // Smart processing for ampersands and angle brackets that need to be encoded.
1104
+
1105
+ // Ampersand-encoding based entirely on Nat Irons's Amputator MT plugin:
1106
+ // http://bumppo.net/projects/amputator/
1107
+ text = text.replace(/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/g,"&amp;");
1108
+
1109
+ // Encode naked <'s
1110
+ text = text.replace(/<(?![a-z\/?\$!])/gi,"&lt;");
1111
+
1112
+ return text;
1113
+ }
1114
+
1115
+
1116
+ var _EncodeBackslashEscapes = function(text) {
1117
+ //
1118
+ // Parameter: String.
1119
+ // Returns: The string, with after processing the following backslash
1120
+ // escape sequences.
1121
+ //
1122
+
1123
+ // attacklab: The polite way to do this is with the new
1124
+ // escapeCharacters() function:
1125
+ //
1126
+ // text = escapeCharacters(text,"\\",true);
1127
+ // text = escapeCharacters(text,"`*_{}[]()>#+-.!",true);
1128
+ //
1129
+ // ...but we're sidestepping its use of the (slow) RegExp constructor
1130
+ // as an optimization for Firefox. This function gets called a LOT.
1131
+
1132
+ text = text.replace(/\\(\\)/g,escapeCharacters_callback);
1133
+ text = text.replace(/\\([`*_{}\[\]()>#+-.!])/g,escapeCharacters_callback);
1134
+ return text;
1135
+ }
1136
+
1137
+
1138
+ var _DoAutoLinks = function(text) {
1139
+
1140
+ text = text.replace(/<((https?|ftp|dict):[^'">\s]+)>/gi,"<a href=\"$1\">$1</a>");
1141
+
1142
+ // Email addresses: <address@domain.foo>
1143
+
1144
+ /*
1145
+ text = text.replace(/
1146
+ <
1147
+ (?:mailto:)?
1148
+ (
1149
+ [-.\w]+
1150
+ \@
1151
+ [-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+
1152
+ )
1153
+ >
1154
+ /gi, _DoAutoLinks_callback());
1155
+ */
1156
+ text = text.replace(/<(?:mailto:)?([-.\w]+\@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi,
1157
+ function(wholeMatch,m1) {
1158
+ return _EncodeEmailAddress( _UnescapeSpecialChars(m1) );
1159
+ }
1160
+ );
1161
+
1162
+ return text;
1163
+ }
1164
+
1165
+
1166
+ var _EncodeEmailAddress = function(addr) {
1167
+ //
1168
+ // Input: an email address, e.g. "foo@example.com"
1169
+ //
1170
+ // Output: the email address as a mailto link, with each character
1171
+ // of the address encoded as either a decimal or hex entity, in
1172
+ // the hopes of foiling most address harvesting spam bots. E.g.:
1173
+ //
1174
+ // <a href="&#x6D;&#97;&#105;&#108;&#x74;&#111;:&#102;&#111;&#111;&#64;&#101;
1175
+ // x&#x61;&#109;&#x70;&#108;&#x65;&#x2E;&#99;&#111;&#109;">&#102;&#111;&#111;
1176
+ // &#64;&#101;x&#x61;&#109;&#x70;&#108;&#x65;&#x2E;&#99;&#111;&#109;</a>
1177
+ //
1178
+ // Based on a filter by Matthew Wickline, posted to the BBEdit-Talk
1179
+ // mailing list: <http://tinyurl.com/yu7ue>
1180
+ //
1181
+
1182
+ // attacklab: why can't javascript speak hex?
1183
+ function char2hex(ch) {
1184
+ var hexDigits = '0123456789ABCDEF';
1185
+ var dec = ch.charCodeAt(0);
1186
+ return(hexDigits.charAt(dec>>4) + hexDigits.charAt(dec&15));
1187
+ }
1188
+
1189
+ var encode = [
1190
+ function(ch){return "&#"+ch.charCodeAt(0)+";";},
1191
+ function(ch){return "&#x"+char2hex(ch)+";";},
1192
+ function(ch){return ch;}
1193
+ ];
1194
+
1195
+ addr = "mailto:" + addr;
1196
+
1197
+ addr = addr.replace(/./g, function(ch) {
1198
+ if (ch == "@") {
1199
+ // this *must* be encoded. I insist.
1200
+ ch = encode[Math.floor(Math.random()*2)](ch);
1201
+ } else if (ch !=":") {
1202
+ // leave ':' alone (to spot mailto: later)
1203
+ var r = Math.random();
1204
+ // roughly 10% raw, 45% hex, 45% dec
1205
+ ch = (
1206
+ r > .9 ? encode[2](ch) :
1207
+ r > .45 ? encode[1](ch) :
1208
+ encode[0](ch)
1209
+ );
1210
+ }
1211
+ return ch;
1212
+ });
1213
+
1214
+ addr = "<a href=\"" + addr + "\">" + addr + "</a>";
1215
+ addr = addr.replace(/">.+:/g,"\">"); // strip the mailto: from the visible part
1216
+
1217
+ return addr;
1218
+ }
1219
+
1220
+
1221
+ var _UnescapeSpecialChars = function(text) {
1222
+ //
1223
+ // Swap back in all the special characters we've hidden.
1224
+ //
1225
+ text = text.replace(/~E(\d+)E/g,
1226
+ function(wholeMatch,m1) {
1227
+ var charCodeToReplace = parseInt(m1);
1228
+ return String.fromCharCode(charCodeToReplace);
1229
+ }
1230
+ );
1231
+ return text;
1232
+ }
1233
+
1234
+
1235
+ var _Outdent = function(text) {
1236
+ //
1237
+ // Remove one level of line-leading tabs or spaces
1238
+ //
1239
+
1240
+ // attacklab: hack around Konqueror 3.5.4 bug:
1241
+ // "----------bug".replace(/^-/g,"") == "bug"
1242
+
1243
+ text = text.replace(/^(\t|[ ]{1,4})/gm,"~0"); // attacklab: g_tab_width
1244
+
1245
+ // attacklab: clean up hack
1246
+ text = text.replace(/~0/g,"")
1247
+
1248
+ return text;
1249
+ }
1250
+
1251
+ var _Detab = function(text) {
1252
+ // attacklab: Detab's completely rewritten for speed.
1253
+ // In perl we could fix it by anchoring the regexp with \G.
1254
+ // In javascript we're less fortunate.
1255
+
1256
+ // expand first n-1 tabs
1257
+ text = text.replace(/\t(?=\t)/g," "); // attacklab: g_tab_width
1258
+
1259
+ // replace the nth with two sentinels
1260
+ text = text.replace(/\t/g,"~A~B");
1261
+
1262
+ // use the sentinel to anchor our regex so it doesn't explode
1263
+ text = text.replace(/~B(.+?)~A/g,
1264
+ function(wholeMatch,m1,m2) {
1265
+ var leadingText = m1;
1266
+ var numSpaces = 4 - leadingText.length % 4; // attacklab: g_tab_width
1267
+
1268
+ // there *must* be a better way to do this:
1269
+ for (var i=0; i<numSpaces; i++) leadingText+=" ";
1270
+
1271
+ return leadingText;
1272
+ }
1273
+ );
1274
+
1275
+ // clean up sentinels
1276
+ text = text.replace(/~A/g," "); // attacklab: g_tab_width
1277
+ text = text.replace(/~B/g,"");
1278
+
1279
+ return text;
1280
+ }
1281
+
1282
+
1283
+ //
1284
+ // attacklab: Utility functions
1285
+ //
1286
+
1287
+
1288
+ var escapeCharacters = function(text, charsToEscape, afterBackslash) {
1289
+ // First we have to escape the escape characters so that
1290
+ // we can build a character class out of them
1291
+ var regexString = "([" + charsToEscape.replace(/([\[\]\\])/g,"\\$1") + "])";
1292
+
1293
+ if (afterBackslash) {
1294
+ regexString = "\\\\" + regexString;
1295
+ }
1296
+
1297
+ var regex = new RegExp(regexString,"g");
1298
+ text = text.replace(regex,escapeCharacters_callback);
1299
+
1300
+ return text;
1301
+ }
1302
+
1303
+
1304
+ var escapeCharacters_callback = function(wholeMatch,m1) {
1305
+ var charCodeToEscape = m1.charCodeAt(0);
1306
+ return "~E"+charCodeToEscape+"E";
1307
+ }
1308
+
1309
+ } // end of Showdown.converter
1310
+
1311
+ // export
1312
+ if (typeof exports != 'undefined') exports.Showdown = Showdown;