bhf 0.4.7 → 0.4.8

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