showdown-rails 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .rvmrc
4
+ .bundle
5
+ .config
6
+ .yardoc
7
+ Gemfile.lock
8
+ InstalledFiles
9
+ _yardoc
10
+ coverage
11
+ doc/
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,65 @@
1
+ Showdown.js LICENSE
2
+ ----
3
+
4
+ Copyright (c) 2007, John Fraser
5
+ <http://www.attacklab.net/>
6
+ All rights reserved.
7
+
8
+ Original Markdown copyright (c) 2004, John Gruber
9
+ <http://daringfireball.net/>
10
+ All rights reserved.
11
+
12
+ Redistribution and use in source and binary forms, with or without
13
+ modification, are permitted provided that the following conditions are
14
+ met:
15
+
16
+ * Redistributions of source code must retain the above copyright notice,
17
+ this list of conditions and the following disclaimer.
18
+
19
+ * Redistributions in binary form must reproduce the above copyright
20
+ notice, this list of conditions and the following disclaimer in the
21
+ documentation and/or other materials provided with the distribution.
22
+
23
+ * Neither the name "Markdown" nor the names of its contributors may
24
+ be used to endorse or promote products derived from this software
25
+ without specific prior written permission.
26
+
27
+ This software is provided by the copyright holders and contributors "as
28
+ is" and any express or implied warranties, including, but not limited
29
+ to, the implied warranties of merchantability and fitness for a
30
+ particular purpose are disclaimed. In no event shall the copyright owner
31
+ or contributors be liable for any direct, indirect, incidental, special,
32
+ exemplary, or consequential damages (including, but not limited to,
33
+ procurement of substitute goods or services; loss of use, data, or
34
+ profits; or business interruption) however caused and on any theory of
35
+ liability, whether in contract, strict liability, or tort (including
36
+ negligence or otherwise) arising in any way out of the use of this
37
+ software, even if advised of the possibility of such damage.
38
+
39
+
40
+ rails-showdown
41
+ ----
42
+
43
+
44
+ Copyright (c) 2012 Josh McArthur
45
+
46
+ MIT License
47
+
48
+ Permission is hereby granted, free of charge, to any person obtaining
49
+ a copy of this software and associated documentation files (the
50
+ "Software"), to deal in the Software without restriction, including
51
+ without limitation the rights to use, copy, modify, merge, publish,
52
+ distribute, sublicense, and/or sell copies of the Software, and to
53
+ permit persons to whom the Software is furnished to do so, subject to
54
+ the following conditions:
55
+
56
+ The above copyright notice and this permission notice shall be
57
+ included in all copies or substantial portions of the Software.
58
+
59
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
60
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
61
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
62
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
63
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
64
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
65
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,64 @@
1
+ # Showdown.js for Rails 3.1 Asset Pipeline
2
+
3
+ Showdown.js is a Javascript port of a Markdown compiler, mostly compatible with the original `markdown.pl` script.
4
+
5
+ The script enables features such as:
6
+
7
+ * Markdown text fields
8
+ * Preview of Markdown
9
+ * Deserializing Markdown content (from user input, APIs, etc.)
10
+
11
+
12
+ ## Installation
13
+
14
+ Add this line to your application's Gemfile:
15
+
16
+ gem 'rails-showdown'
17
+
18
+ And then execute:
19
+
20
+ $ bundle
21
+
22
+ Or install it yourself as:
23
+
24
+ $ gem install rails-showdown
25
+
26
+
27
+ ## Usage
28
+
29
+ 1. Include the gem in your application's `app/assets/javascripts/application.js` by adding the following line:
30
+
31
+ ```
32
+ //= require showdown
33
+ ```
34
+ 2. Turn Markdown into HTML!
35
+
36
+ ```
37
+ var converter = new Showdown.converter()
38
+ converter.makeHtml("** Markdown! **")
39
+ ```
40
+
41
+ ## License & Ownership
42
+
43
+ _I do not maintain or claim any form of ownership over showdown.js - here's what I know about the origins of showdown.js:_
44
+
45
+ * `showdown.js` was originally created by John Fraser, of [AttackLab.net](http://www.attacklab.net), however the posting of the original script seems to no longer exist
46
+ * Since then, [many people have created mirrors of the original script on Github](https://github.com/search?q=showdown&repo=&langOverride=&start_value=1&type=Everything&language=)
47
+ * One of these mirrors (https://github.com/coreyti/showdown) has added additional features (such as packaging it for [NPM](http://npmjs.org) installation), and responded to pull requests and fixes for the original script.
48
+
49
+ `showdown.js` is licensed under a BSD License, which means that it is redistributable, as long as the original license is kept intact, and extra conditions (listed in `LICENSE`) are met.
50
+
51
+ This gem is licensed under an MIT License, which also means that is is redistributable, with very little restriction.
52
+
53
+ #### To summarize:
54
+
55
+ `showdown.js` is not owned by me, but is redistributable. While the version of the script packaged with this gem is licensed under the BSD License, the remainder of the code is MIT Licensed, meaning that it is usable and redistributable by **you**, as long as you keep all licenses in place.
56
+
57
+
58
+ ## Contributing
59
+
60
+ 1. Fork it
61
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
62
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
63
+ 4. Push to the branch (`git push origin my-new-feature`)
64
+ 5. Create new Pull Request
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
@@ -0,0 +1 @@
1
+ require "showdown/rails"
@@ -0,0 +1,2 @@
1
+ require 'showdown/rails/version'
2
+ require 'showdown/rails/engine'
@@ -0,0 +1,6 @@
1
+ module Showdown
2
+ module Rails
3
+ class Engine < ::Rails::Engine
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ module Showdown
2
+ module Rails
3
+ VERSION = "0.0.3"
4
+ end
5
+ end
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/showdown/rails/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Josh McArthur"]
6
+ gem.email = ["joshua.mcarthur@gmail.com"]
7
+ gem.description = %q{Showdown.js for Rails 3.1 Asset Pipeline}
8
+ gem.summary = %q{Showdown.js for Rails 3.1 Asset Pipeline}
9
+ gem.homepage = ""
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.name = "showdown-rails"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = Showdown::Rails::VERSION
17
+
18
+ gem.add_dependency 'railties', '>= 3.1'
19
+ gem.add_dependency 'actionpack', '>= 3.1'
20
+ gem.add_development_dependency 'rails', '>= 3.1'
21
+ end
@@ -0,0 +1,1341 @@
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
+ // Handle github codeblocks prior to running HashHTML so that
135
+ // HTML contained within the codeblock gets escaped propertly
136
+ text = _DoGithubCodeBlocks(text);
137
+
138
+ // Turn block-level HTML blocks into hash entries
139
+ text = _HashHTMLBlocks(text);
140
+
141
+ // Strip link definitions, store in hashes.
142
+ text = _StripLinkDefinitions(text);
143
+
144
+ text = _RunBlockGamut(text);
145
+
146
+ text = _UnescapeSpecialChars(text);
147
+
148
+ // attacklab: Restore dollar signs
149
+ text = text.replace(/~D/g,"$$");
150
+
151
+ // attacklab: Restore tildes
152
+ text = text.replace(/~T/g,"~");
153
+
154
+ return text;
155
+ };
156
+
157
+
158
+ var _StripLinkDefinitions = function(text) {
159
+ //
160
+ // Strips link definitions from text, stores the URLs and titles in
161
+ // hash references.
162
+ //
163
+
164
+ // Link defs are in the form: ^[id]: url "optional title"
165
+
166
+ /*
167
+ var text = text.replace(/
168
+ ^[ ]{0,3}\[(.+)\]: // id = $1 attacklab: g_tab_width - 1
169
+ [ \t]*
170
+ \n? // maybe *one* newline
171
+ [ \t]*
172
+ <?(\S+?)>? // url = $2
173
+ [ \t]*
174
+ \n? // maybe one newline
175
+ [ \t]*
176
+ (?:
177
+ (\n*) // any lines skipped = $3 attacklab: lookbehind removed
178
+ ["(]
179
+ (.+?) // title = $4
180
+ [")]
181
+ [ \t]*
182
+ )? // title is optional
183
+ (?:\n+|$)
184
+ /gm,
185
+ function(){...});
186
+ */
187
+ var text = text.replace(/^[ ]{0,3}\[(.+)\]:[ \t]*\n?[ \t]*<?(\S+?)>?[ \t]*\n?[ \t]*(?:(\n*)["(](.+?)[")][ \t]*)?(?:\n+|\Z)/gm,
188
+ function (wholeMatch,m1,m2,m3,m4) {
189
+ m1 = m1.toLowerCase();
190
+ g_urls[m1] = _EncodeAmpsAndAngles(m2); // Link IDs are case-insensitive
191
+ if (m3) {
192
+ // Oops, found blank lines, so it's not a title.
193
+ // Put back the parenthetical statement we stole.
194
+ return m3+m4;
195
+ } else if (m4) {
196
+ g_titles[m1] = m4.replace(/"/g,"&quot;");
197
+ }
198
+
199
+ // Completely remove the definition from the text
200
+ return "";
201
+ }
202
+ );
203
+
204
+ return text;
205
+ }
206
+
207
+
208
+ var _HashHTMLBlocks = function(text) {
209
+ // attacklab: Double up blank lines to reduce lookaround
210
+ text = text.replace(/\n/g,"\n\n");
211
+
212
+ // Hashify HTML blocks:
213
+ // We only want to do this for block-level HTML tags, such as headers,
214
+ // lists, and tables. That's because we still want to wrap <p>s around
215
+ // "paragraphs" that are wrapped in non-block-level tags, such as anchors,
216
+ // phrase emphasis, and spans. The list of tags we're looking for is
217
+ // hard-coded:
218
+ 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";
219
+ 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";
220
+
221
+ // First, look for nested blocks, e.g.:
222
+ // <div>
223
+ // <div>
224
+ // tags for inner block must be indented.
225
+ // </div>
226
+ // </div>
227
+ //
228
+ // The outermost tags must start at the left margin for this to match, and
229
+ // the inner nested divs must be indented.
230
+ // We need to do this before the next, more liberal match, because the next
231
+ // match will start at the first `<div>` and stop at the first `</div>`.
232
+
233
+ // attacklab: This regex can be expensive when it fails.
234
+ /*
235
+ var text = text.replace(/
236
+ ( // save in $1
237
+ ^ // start of line (with /m)
238
+ <($block_tags_a) // start tag = $2
239
+ \b // word break
240
+ // attacklab: hack around khtml/pcre bug...
241
+ [^\r]*?\n // any number of lines, minimally matching
242
+ </\2> // the matching end tag
243
+ [ \t]* // trailing spaces/tabs
244
+ (?=\n+) // followed by a newline
245
+ ) // attacklab: there are sentinel newlines at end of document
246
+ /gm,function(){...}};
247
+ */
248
+ 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);
249
+
250
+ //
251
+ // Now match more liberally, simply from `\n<tag>` to `</tag>\n`
252
+ //
253
+
254
+ /*
255
+ var text = text.replace(/
256
+ ( // save in $1
257
+ ^ // start of line (with /m)
258
+ <($block_tags_b) // start tag = $2
259
+ \b // word break
260
+ // attacklab: hack around khtml/pcre bug...
261
+ [^\r]*? // any number of lines, minimally matching
262
+ .*</\2> // the matching end tag
263
+ [ \t]* // trailing spaces/tabs
264
+ (?=\n+) // followed by a newline
265
+ ) // attacklab: there are sentinel newlines at end of document
266
+ /gm,function(){...}};
267
+ */
268
+ 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);
269
+
270
+ // Special case just for <hr />. It was easier to make a special case than
271
+ // to make the other regex more complicated.
272
+
273
+ /*
274
+ text = text.replace(/
275
+ ( // save in $1
276
+ \n\n // Starting after a blank line
277
+ [ ]{0,3}
278
+ (<(hr) // start tag = $2
279
+ \b // word break
280
+ ([^<>])*? //
281
+ \/?>) // the matching end tag
282
+ [ \t]*
283
+ (?=\n{2,}) // followed by a blank line
284
+ )
285
+ /g,hashElement);
286
+ */
287
+ text = text.replace(/(\n[ ]{0,3}(<(hr)\b([^<>])*?\/?>)[ \t]*(?=\n{2,}))/g,hashElement);
288
+
289
+ // Special case for standalone HTML comments:
290
+
291
+ /*
292
+ text = text.replace(/
293
+ ( // save in $1
294
+ \n\n // Starting after a blank line
295
+ [ ]{0,3} // attacklab: g_tab_width - 1
296
+ <!
297
+ (--[^\r]*?--\s*)+
298
+ >
299
+ [ \t]*
300
+ (?=\n{2,}) // followed by a blank line
301
+ )
302
+ /g,hashElement);
303
+ */
304
+ text = text.replace(/(\n\n[ ]{0,3}<!(--[^\r]*?--\s*)+>[ \t]*(?=\n{2,}))/g,hashElement);
305
+
306
+ // PHP and ASP-style processor instructions (<?...?> and <%...%>)
307
+
308
+ /*
309
+ text = text.replace(/
310
+ (?:
311
+ \n\n // Starting after a blank line
312
+ )
313
+ ( // save in $1
314
+ [ ]{0,3} // attacklab: g_tab_width - 1
315
+ (?:
316
+ <([?%]) // $2
317
+ [^\r]*?
318
+ \2>
319
+ )
320
+ [ \t]*
321
+ (?=\n{2,}) // followed by a blank line
322
+ )
323
+ /g,hashElement);
324
+ */
325
+ text = text.replace(/(?:\n\n)([ ]{0,3}(?:<([?%])[^\r]*?\2>)[ \t]*(?=\n{2,}))/g,hashElement);
326
+
327
+ // attacklab: Undo double lines (see comment at top of this function)
328
+ text = text.replace(/\n\n/g,"\n");
329
+ return text;
330
+ }
331
+
332
+ var hashElement = function(wholeMatch,m1) {
333
+ var blockText = m1;
334
+
335
+ // Undo double lines
336
+ blockText = blockText.replace(/\n\n/g,"\n");
337
+ blockText = blockText.replace(/^\n/,"");
338
+
339
+ // strip trailing blank lines
340
+ blockText = blockText.replace(/\n+$/g,"");
341
+
342
+ // Replace the element text with a marker ("~KxK" where x is its key)
343
+ blockText = "\n\n~K" + (g_html_blocks.push(blockText)-1) + "K\n\n";
344
+
345
+ return blockText;
346
+ };
347
+
348
+ var _RunBlockGamut = function(text) {
349
+ //
350
+ // These are all the transformations that form block-level
351
+ // tags like paragraphs, headers, and list items.
352
+ //
353
+ text = _DoHeaders(text);
354
+
355
+ // Do Horizontal Rules:
356
+ var key = hashBlock("<hr />");
357
+ text = text.replace(/^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$/gm,key);
358
+ text = text.replace(/^[ ]{0,2}([ ]?\-[ ]?){3,}[ \t]*$/gm,key);
359
+ text = text.replace(/^[ ]{0,2}([ ]?\_[ ]?){3,}[ \t]*$/gm,key);
360
+
361
+ text = _DoLists(text);
362
+ text = _DoCodeBlocks(text);
363
+ text = _DoBlockQuotes(text);
364
+
365
+ // We already ran _HashHTMLBlocks() before, in Markdown(), but that
366
+ // was to escape raw HTML in the original Markdown source. This time,
367
+ // we're escaping the markup we've just created, so that we don't wrap
368
+ // <p> tags around block-level tags.
369
+ text = _HashHTMLBlocks(text);
370
+ text = _FormParagraphs(text);
371
+
372
+ return text;
373
+ };
374
+
375
+
376
+ var _RunSpanGamut = function(text) {
377
+ //
378
+ // These are all the transformations that occur *within* block-level
379
+ // tags like paragraphs, headers, and list items.
380
+ //
381
+
382
+ text = _DoCodeSpans(text);
383
+ text = _EscapeSpecialCharsWithinTagAttributes(text);
384
+ text = _EncodeBackslashEscapes(text);
385
+
386
+ // Process anchor and image tags. Images must come first,
387
+ // because ![foo][f] looks like an anchor.
388
+ text = _DoImages(text);
389
+ text = _DoAnchors(text);
390
+
391
+ // Make links out of things like `<http://example.com/>`
392
+ // Must come after _DoAnchors(), because you can use < and >
393
+ // delimiters in inline links like [this](<url>).
394
+ text = _DoAutoLinks(text);
395
+ text = _EncodeAmpsAndAngles(text);
396
+ text = _DoItalicsAndBold(text);
397
+
398
+ // Do hard breaks:
399
+ text = text.replace(/ +\n/g," <br />\n");
400
+
401
+ return text;
402
+ }
403
+
404
+ var _EscapeSpecialCharsWithinTagAttributes = function(text) {
405
+ //
406
+ // Within tags -- meaning between < and > -- encode [\ ` * _] so they
407
+ // don't conflict with their use in Markdown for code, italics and strong.
408
+ //
409
+
410
+ // Build a regex to find HTML tags and comments. See Friedl's
411
+ // "Mastering Regular Expressions", 2nd Ed., pp. 200-201.
412
+ var regex = /(<[a-z\/!$]("[^"]*"|'[^']*'|[^'">])*>|<!(--.*?--\s*)+>)/gi;
413
+
414
+ text = text.replace(regex, function(wholeMatch) {
415
+ var tag = wholeMatch.replace(/(.)<\/?code>(?=.)/g,"$1`");
416
+ tag = escapeCharacters(tag,"\\`*_");
417
+ return tag;
418
+ });
419
+
420
+ return text;
421
+ }
422
+
423
+ var _DoAnchors = function(text) {
424
+ //
425
+ // Turn Markdown link shortcuts into XHTML <a> tags.
426
+ //
427
+ //
428
+ // First, handle reference-style links: [link text] [id]
429
+ //
430
+
431
+ /*
432
+ text = text.replace(/
433
+ ( // wrap whole match in $1
434
+ \[
435
+ (
436
+ (?:
437
+ \[[^\]]*\] // allow brackets nested one level
438
+ |
439
+ [^\[] // or anything else
440
+ )*
441
+ )
442
+ \]
443
+
444
+ [ ]? // one optional space
445
+ (?:\n[ ]*)? // one optional newline followed by spaces
446
+
447
+ \[
448
+ (.*?) // id = $3
449
+ \]
450
+ )()()()() // pad remaining backreferences
451
+ /g,_DoAnchors_callback);
452
+ */
453
+ text = text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g,writeAnchorTag);
454
+
455
+ //
456
+ // Next, inline-style links: [link text](url "optional title")
457
+ //
458
+
459
+ /*
460
+ text = text.replace(/
461
+ ( // wrap whole match in $1
462
+ \[
463
+ (
464
+ (?:
465
+ \[[^\]]*\] // allow brackets nested one level
466
+ |
467
+ [^\[\]] // or anything else
468
+ )
469
+ )
470
+ \]
471
+ \( // literal paren
472
+ [ \t]*
473
+ () // no id, so leave $3 empty
474
+ <?(.*?)>? // href = $4
475
+ [ \t]*
476
+ ( // $5
477
+ (['"]) // quote char = $6
478
+ (.*?) // Title = $7
479
+ \6 // matching quote
480
+ [ \t]* // ignore any spaces/tabs between closing quote and )
481
+ )? // title is optional
482
+ \)
483
+ )
484
+ /g,writeAnchorTag);
485
+ */
486
+ text = text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\]\([ \t]*()<?(.*?)>?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,writeAnchorTag);
487
+
488
+ //
489
+ // Last, handle reference-style shortcuts: [link text]
490
+ // These must come last in case you've also got [link test][1]
491
+ // or [link test](/foo)
492
+ //
493
+
494
+ /*
495
+ text = text.replace(/
496
+ ( // wrap whole match in $1
497
+ \[
498
+ ([^\[\]]+) // link text = $2; can't contain '[' or ']'
499
+ \]
500
+ )()()()()() // pad rest of backreferences
501
+ /g, writeAnchorTag);
502
+ */
503
+ text = text.replace(/(\[([^\[\]]+)\])()()()()()/g, writeAnchorTag);
504
+
505
+ return text;
506
+ }
507
+
508
+ var writeAnchorTag = function(wholeMatch,m1,m2,m3,m4,m5,m6,m7) {
509
+ if (m7 == undefined) m7 = "";
510
+ var whole_match = m1;
511
+ var link_text = m2;
512
+ var link_id = m3.toLowerCase();
513
+ var url = m4;
514
+ var title = m7;
515
+
516
+ if (url == "") {
517
+ if (link_id == "") {
518
+ // lower-case and turn embedded newlines into spaces
519
+ link_id = link_text.toLowerCase().replace(/ ?\n/g," ");
520
+ }
521
+ url = "#"+link_id;
522
+
523
+ if (g_urls[link_id] != undefined) {
524
+ url = g_urls[link_id];
525
+ if (g_titles[link_id] != undefined) {
526
+ title = g_titles[link_id];
527
+ }
528
+ }
529
+ else {
530
+ if (whole_match.search(/\(\s*\)$/m)>-1) {
531
+ // Special case for explicit empty url
532
+ url = "";
533
+ } else {
534
+ return whole_match;
535
+ }
536
+ }
537
+ }
538
+
539
+ url = escapeCharacters(url,"*_");
540
+ var result = "<a href=\"" + url + "\"";
541
+
542
+ if (title != "") {
543
+ title = title.replace(/"/g,"&quot;");
544
+ title = escapeCharacters(title,"*_");
545
+ result += " title=\"" + title + "\"";
546
+ }
547
+
548
+ result += ">" + link_text + "</a>";
549
+
550
+ return result;
551
+ }
552
+
553
+
554
+ var _DoImages = function(text) {
555
+ //
556
+ // Turn Markdown image shortcuts into <img> tags.
557
+ //
558
+
559
+ //
560
+ // First, handle reference-style labeled images: ![alt text][id]
561
+ //
562
+
563
+ /*
564
+ text = text.replace(/
565
+ ( // wrap whole match in $1
566
+ !\[
567
+ (.*?) // alt text = $2
568
+ \]
569
+
570
+ [ ]? // one optional space
571
+ (?:\n[ ]*)? // one optional newline followed by spaces
572
+
573
+ \[
574
+ (.*?) // id = $3
575
+ \]
576
+ )()()()() // pad rest of backreferences
577
+ /g,writeImageTag);
578
+ */
579
+ text = text.replace(/(!\[(.*?)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g,writeImageTag);
580
+
581
+ //
582
+ // Next, handle inline images: ![alt text](url "optional title")
583
+ // Don't forget: encode * and _
584
+
585
+ /*
586
+ text = text.replace(/
587
+ ( // wrap whole match in $1
588
+ !\[
589
+ (.*?) // alt text = $2
590
+ \]
591
+ \s? // One optional whitespace character
592
+ \( // literal paren
593
+ [ \t]*
594
+ () // no id, so leave $3 empty
595
+ <?(\S+?)>? // src url = $4
596
+ [ \t]*
597
+ ( // $5
598
+ (['"]) // quote char = $6
599
+ (.*?) // title = $7
600
+ \6 // matching quote
601
+ [ \t]*
602
+ )? // title is optional
603
+ \)
604
+ )
605
+ /g,writeImageTag);
606
+ */
607
+ text = text.replace(/(!\[(.*?)\]\s?\([ \t]*()<?(\S+?)>?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,writeImageTag);
608
+
609
+ return text;
610
+ }
611
+
612
+ var writeImageTag = function(wholeMatch,m1,m2,m3,m4,m5,m6,m7) {
613
+ var whole_match = m1;
614
+ var alt_text = m2;
615
+ var link_id = m3.toLowerCase();
616
+ var url = m4;
617
+ var title = m7;
618
+
619
+ if (!title) title = "";
620
+
621
+ if (url == "") {
622
+ if (link_id == "") {
623
+ // lower-case and turn embedded newlines into spaces
624
+ link_id = alt_text.toLowerCase().replace(/ ?\n/g," ");
625
+ }
626
+ url = "#"+link_id;
627
+
628
+ if (g_urls[link_id] != undefined) {
629
+ url = g_urls[link_id];
630
+ if (g_titles[link_id] != undefined) {
631
+ title = g_titles[link_id];
632
+ }
633
+ }
634
+ else {
635
+ return whole_match;
636
+ }
637
+ }
638
+
639
+ alt_text = alt_text.replace(/"/g,"&quot;");
640
+ url = escapeCharacters(url,"*_");
641
+ var result = "<img src=\"" + url + "\" alt=\"" + alt_text + "\"";
642
+
643
+ // attacklab: Markdown.pl adds empty title attributes to images.
644
+ // Replicate this bug.
645
+
646
+ //if (title != "") {
647
+ title = title.replace(/"/g,"&quot;");
648
+ title = escapeCharacters(title,"*_");
649
+ result += " title=\"" + title + "\"";
650
+ //}
651
+
652
+ result += " />";
653
+
654
+ return result;
655
+ }
656
+
657
+
658
+ var _DoHeaders = function(text) {
659
+
660
+ // Setext-style headers:
661
+ // Header 1
662
+ // ========
663
+ //
664
+ // Header 2
665
+ // --------
666
+ //
667
+ text = text.replace(/^(.+)[ \t]*\n=+[ \t]*\n+/gm,
668
+ function(wholeMatch,m1){return hashBlock('<h1 id="' + headerId(m1) + '">' + _RunSpanGamut(m1) + "</h1>");});
669
+
670
+ text = text.replace(/^(.+)[ \t]*\n-+[ \t]*\n+/gm,
671
+ function(matchFound,m1){return hashBlock('<h2 id="' + headerId(m1) + '">' + _RunSpanGamut(m1) + "</h2>");});
672
+
673
+ // atx-style headers:
674
+ // # Header 1
675
+ // ## Header 2
676
+ // ## Header 2 with closing hashes ##
677
+ // ...
678
+ // ###### Header 6
679
+ //
680
+
681
+ /*
682
+ text = text.replace(/
683
+ ^(\#{1,6}) // $1 = string of #'s
684
+ [ \t]*
685
+ (.+?) // $2 = Header text
686
+ [ \t]*
687
+ \#* // optional closing #'s (not counted)
688
+ \n+
689
+ /gm, function() {...});
690
+ */
691
+
692
+ text = text.replace(/^(\#{1,6})[ \t]*(.+?)[ \t]*\#*\n+/gm,
693
+ function(wholeMatch,m1,m2) {
694
+ var h_level = m1.length;
695
+ return hashBlock("<h" + h_level + ' id="' + headerId(m2) + '">' + _RunSpanGamut(m2) + "</h" + h_level + ">");
696
+ });
697
+
698
+ function headerId(m) {
699
+ return m.replace(/[^\w]/g, '').toLowerCase();
700
+ }
701
+ return text;
702
+ }
703
+
704
+ // This declaration keeps Dojo compressor from outputting garbage:
705
+ var _ProcessListItems;
706
+
707
+ var _DoLists = function(text) {
708
+ //
709
+ // Form HTML ordered (numbered) and unordered (bulleted) lists.
710
+ //
711
+
712
+ // attacklab: add sentinel to hack around khtml/safari bug:
713
+ // http://bugs.webkit.org/show_bug.cgi?id=11231
714
+ text += "~0";
715
+
716
+ // Re-usable pattern to match any entirel ul or ol list:
717
+
718
+ /*
719
+ var whole_list = /
720
+ ( // $1 = whole list
721
+ ( // $2
722
+ [ ]{0,3} // attacklab: g_tab_width - 1
723
+ ([*+-]|\d+[.]) // $3 = first list item marker
724
+ [ \t]+
725
+ )
726
+ [^\r]+?
727
+ ( // $4
728
+ ~0 // sentinel for workaround; should be $
729
+ |
730
+ \n{2,}
731
+ (?=\S)
732
+ (?! // Negative lookahead for another list item marker
733
+ [ \t]*
734
+ (?:[*+-]|\d+[.])[ \t]+
735
+ )
736
+ )
737
+ )/g
738
+ */
739
+ var whole_list = /^(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm;
740
+
741
+ if (g_list_level) {
742
+ text = text.replace(whole_list,function(wholeMatch,m1,m2) {
743
+ var list = m1;
744
+ var list_type = (m2.search(/[*+-]/g)>-1) ? "ul" : "ol";
745
+
746
+ // Turn double returns into triple returns, so that we can make a
747
+ // paragraph for the last item in a list, if necessary:
748
+ list = list.replace(/\n{2,}/g,"\n\n\n");;
749
+ var result = _ProcessListItems(list);
750
+
751
+ // Trim any trailing whitespace, to put the closing `</$list_type>`
752
+ // up on the preceding line, to get it past the current stupid
753
+ // HTML block parser. This is a hack to work around the terrible
754
+ // hack that is the HTML block parser.
755
+ result = result.replace(/\s+$/,"");
756
+ result = "<"+list_type+">" + result + "</"+list_type+">\n";
757
+ return result;
758
+ });
759
+ } else {
760
+ whole_list = /(\n\n|^\n?)(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/g;
761
+ text = text.replace(whole_list,function(wholeMatch,m1,m2,m3) {
762
+ var runup = m1;
763
+ var list = m2;
764
+
765
+ var list_type = (m3.search(/[*+-]/g)>-1) ? "ul" : "ol";
766
+ // Turn double returns into triple returns, so that we can make a
767
+ // paragraph for the last item in a list, if necessary:
768
+ var list = list.replace(/\n{2,}/g,"\n\n\n");;
769
+ var result = _ProcessListItems(list);
770
+ result = runup + "<"+list_type+">\n" + result + "</"+list_type+">\n";
771
+ return result;
772
+ });
773
+ }
774
+
775
+ // attacklab: strip sentinel
776
+ text = text.replace(/~0/,"");
777
+
778
+ return text;
779
+ }
780
+
781
+ _ProcessListItems = function(list_str) {
782
+ //
783
+ // Process the contents of a single ordered or unordered list, splitting it
784
+ // into individual list items.
785
+ //
786
+ // The $g_list_level global keeps track of when we're inside a list.
787
+ // Each time we enter a list, we increment it; when we leave a list,
788
+ // we decrement. If it's zero, we're not in a list anymore.
789
+ //
790
+ // We do this because when we're not inside a list, we want to treat
791
+ // something like this:
792
+ //
793
+ // I recommend upgrading to version
794
+ // 8. Oops, now this line is treated
795
+ // as a sub-list.
796
+ //
797
+ // As a single paragraph, despite the fact that the second line starts
798
+ // with a digit-period-space sequence.
799
+ //
800
+ // Whereas when we're inside a list (or sub-list), that line will be
801
+ // treated as the start of a sub-list. What a kludge, huh? This is
802
+ // an aspect of Markdown's syntax that's hard to parse perfectly
803
+ // without resorting to mind-reading. Perhaps the solution is to
804
+ // change the syntax rules such that sub-lists must start with a
805
+ // starting cardinal number; e.g. "1." or "a.".
806
+
807
+ g_list_level++;
808
+
809
+ // trim trailing blank lines:
810
+ list_str = list_str.replace(/\n{2,}$/,"\n");
811
+
812
+ // attacklab: add sentinel to emulate \z
813
+ list_str += "~0";
814
+
815
+ /*
816
+ list_str = list_str.replace(/
817
+ (\n)? // leading line = $1
818
+ (^[ \t]*) // leading whitespace = $2
819
+ ([*+-]|\d+[.]) [ \t]+ // list marker = $3
820
+ ([^\r]+? // list item text = $4
821
+ (\n{1,2}))
822
+ (?= \n* (~0 | \2 ([*+-]|\d+[.]) [ \t]+))
823
+ /gm, function(){...});
824
+ */
825
+ list_str = list_str.replace(/(\n)?(^[ \t]*)([*+-]|\d+[.])[ \t]+([^\r]+?(\n{1,2}))(?=\n*(~0|\2([*+-]|\d+[.])[ \t]+))/gm,
826
+ function(wholeMatch,m1,m2,m3,m4){
827
+ var item = m4;
828
+ var leading_line = m1;
829
+ var leading_space = m2;
830
+
831
+ if (leading_line || (item.search(/\n{2,}/)>-1)) {
832
+ item = _RunBlockGamut(_Outdent(item));
833
+ }
834
+ else {
835
+ // Recursion for sub-lists:
836
+ item = _DoLists(_Outdent(item));
837
+ item = item.replace(/\n$/,""); // chomp(item)
838
+ item = _RunSpanGamut(item);
839
+ }
840
+
841
+ return "<li>" + item + "</li>\n";
842
+ }
843
+ );
844
+
845
+ // attacklab: strip sentinel
846
+ list_str = list_str.replace(/~0/g,"");
847
+
848
+ g_list_level--;
849
+ return list_str;
850
+ }
851
+
852
+
853
+ var _DoCodeBlocks = function(text) {
854
+ //
855
+ // Process Markdown `<pre><code>` blocks.
856
+ //
857
+
858
+ /*
859
+ text = text.replace(text,
860
+ /(?:\n\n|^)
861
+ ( // $1 = the code block -- one or more lines, starting with a space/tab
862
+ (?:
863
+ (?:[ ]{4}|\t) // Lines must start with a tab or a tab-width of spaces - attacklab: g_tab_width
864
+ .*\n+
865
+ )+
866
+ )
867
+ (\n*[ ]{0,3}[^ \t\n]|(?=~0)) // attacklab: g_tab_width
868
+ /g,function(){...});
869
+ */
870
+
871
+ // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
872
+ text += "~0";
873
+
874
+ text = text.replace(/(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=~0))/g,
875
+ function(wholeMatch,m1,m2) {
876
+ var codeblock = m1;
877
+ var nextChar = m2;
878
+
879
+ codeblock = _EncodeCode( _Outdent(codeblock));
880
+ codeblock = _Detab(codeblock);
881
+ codeblock = codeblock.replace(/^\n+/g,""); // trim leading newlines
882
+ codeblock = codeblock.replace(/\n+$/g,""); // trim trailing whitespace
883
+
884
+ codeblock = "<pre><code>" + codeblock + "\n</code></pre>";
885
+
886
+ return hashBlock(codeblock) + nextChar;
887
+ }
888
+ );
889
+
890
+ // attacklab: strip sentinel
891
+ text = text.replace(/~0/,"");
892
+
893
+ return text;
894
+ };
895
+
896
+ var _DoGithubCodeBlocks = function(text) {
897
+ //
898
+ // Process Github-style code blocks
899
+ // Example:
900
+ // ```ruby
901
+ // def hello_world(x)
902
+ // puts "Hello, #{x}"
903
+ // end
904
+ // ```
905
+ //
906
+
907
+
908
+ // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
909
+ text += "~0";
910
+
911
+ text = text.replace(/(?:^|\n)```(.*)\n([\s\S]*?)\n```/g,
912
+ function(wholeMatch,m1,m2) {
913
+ var language = m1;
914
+ var codeblock = m2;
915
+
916
+ codeblock = _EncodeCode(codeblock);
917
+ codeblock = _Detab(codeblock);
918
+ codeblock = codeblock.replace(/^\n+/g,""); // trim leading newlines
919
+ codeblock = codeblock.replace(/\n+$/g,""); // trim trailing whitespace
920
+
921
+ codeblock = "<pre><code" + (language ? " class=\"" + language + '"' : "") + ">" + codeblock + "\n</code></pre>";
922
+
923
+ return hashBlock(codeblock);
924
+ }
925
+ );
926
+
927
+ // attacklab: strip sentinel
928
+ text = text.replace(/~0/,"");
929
+
930
+ return text;
931
+ }
932
+
933
+ var hashBlock = function(text) {
934
+ text = text.replace(/(^\n+|\n+$)/g,"");
935
+ return "\n\n~K" + (g_html_blocks.push(text)-1) + "K\n\n";
936
+ }
937
+
938
+ var _DoCodeSpans = function(text) {
939
+ //
940
+ // * Backtick quotes are used for <code></code> spans.
941
+ //
942
+ // * You can use multiple backticks as the delimiters if you want to
943
+ // include literal backticks in the code span. So, this input:
944
+ //
945
+ // Just type ``foo `bar` baz`` at the prompt.
946
+ //
947
+ // Will translate to:
948
+ //
949
+ // <p>Just type <code>foo `bar` baz</code> at the prompt.</p>
950
+ //
951
+ // There's no arbitrary limit to the number of backticks you
952
+ // can use as delimters. If you need three consecutive backticks
953
+ // in your code, use four for delimiters, etc.
954
+ //
955
+ // * You can use spaces to get literal backticks at the edges:
956
+ //
957
+ // ... type `` `bar` `` ...
958
+ //
959
+ // Turns to:
960
+ //
961
+ // ... type <code>`bar`</code> ...
962
+ //
963
+
964
+ /*
965
+ text = text.replace(/
966
+ (^|[^\\]) // Character before opening ` can't be a backslash
967
+ (`+) // $2 = Opening run of `
968
+ ( // $3 = The code block
969
+ [^\r]*?
970
+ [^`] // attacklab: work around lack of lookbehind
971
+ )
972
+ \2 // Matching closer
973
+ (?!`)
974
+ /gm, function(){...});
975
+ */
976
+
977
+ text = text.replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm,
978
+ function(wholeMatch,m1,m2,m3,m4) {
979
+ var c = m3;
980
+ c = c.replace(/^([ \t]*)/g,""); // leading whitespace
981
+ c = c.replace(/[ \t]*$/g,""); // trailing whitespace
982
+ c = _EncodeCode(c);
983
+ return m1+"<code>"+c+"</code>";
984
+ });
985
+
986
+ return text;
987
+ }
988
+
989
+ var _EncodeCode = function(text) {
990
+ //
991
+ // Encode/escape certain characters inside Markdown code runs.
992
+ // The point is that in code, these characters are literals,
993
+ // and lose their special Markdown meanings.
994
+ //
995
+ // Encode all ampersands; HTML entities are not
996
+ // entities within a Markdown code span.
997
+ text = text.replace(/&/g,"&amp;");
998
+
999
+ // Do the angle bracket song and dance:
1000
+ text = text.replace(/</g,"&lt;");
1001
+ text = text.replace(/>/g,"&gt;");
1002
+
1003
+ // Now, escape characters that are magic in Markdown:
1004
+ text = escapeCharacters(text,"\*_{}[]\\",false);
1005
+
1006
+ // jj the line above breaks this:
1007
+ //---
1008
+
1009
+ //* Item
1010
+
1011
+ // 1. Subitem
1012
+
1013
+ // special char: *
1014
+ //---
1015
+
1016
+ return text;
1017
+ }
1018
+
1019
+
1020
+ var _DoItalicsAndBold = function(text) {
1021
+
1022
+ // <strong> must go first:
1023
+ text = text.replace(/(\*\*|__)(?=\S)([^\r]*?\S[*_]*)\1/g,
1024
+ "<strong>$2</strong>");
1025
+
1026
+ text = text.replace(/(\*|_)(?=\S)([^\r]*?\S)\1/g,
1027
+ "<em>$2</em>");
1028
+
1029
+ return text;
1030
+ }
1031
+
1032
+
1033
+ var _DoBlockQuotes = function(text) {
1034
+
1035
+ /*
1036
+ text = text.replace(/
1037
+ ( // Wrap whole match in $1
1038
+ (
1039
+ ^[ \t]*>[ \t]? // '>' at the start of a line
1040
+ .+\n // rest of the first line
1041
+ (.+\n)* // subsequent consecutive lines
1042
+ \n* // blanks
1043
+ )+
1044
+ )
1045
+ /gm, function(){...});
1046
+ */
1047
+
1048
+ text = text.replace(/((^[ \t]*>[ \t]?.+\n(.+\n)*\n*)+)/gm,
1049
+ function(wholeMatch,m1) {
1050
+ var bq = m1;
1051
+
1052
+ // attacklab: hack around Konqueror 3.5.4 bug:
1053
+ // "----------bug".replace(/^-/g,"") == "bug"
1054
+
1055
+ bq = bq.replace(/^[ \t]*>[ \t]?/gm,"~0"); // trim one level of quoting
1056
+
1057
+ // attacklab: clean up hack
1058
+ bq = bq.replace(/~0/g,"");
1059
+
1060
+ bq = bq.replace(/^[ \t]+$/gm,""); // trim whitespace-only lines
1061
+ bq = _RunBlockGamut(bq); // recurse
1062
+
1063
+ bq = bq.replace(/(^|\n)/g,"$1 ");
1064
+ // These leading spaces screw with <pre> content, so we need to fix that:
1065
+ bq = bq.replace(
1066
+ /(\s*<pre>[^\r]+?<\/pre>)/gm,
1067
+ function(wholeMatch,m1) {
1068
+ var pre = m1;
1069
+ // attacklab: hack around Konqueror 3.5.4 bug:
1070
+ pre = pre.replace(/^ /mg,"~0");
1071
+ pre = pre.replace(/~0/g,"");
1072
+ return pre;
1073
+ });
1074
+
1075
+ return hashBlock("<blockquote>\n" + bq + "\n</blockquote>");
1076
+ });
1077
+ return text;
1078
+ }
1079
+
1080
+
1081
+ var _FormParagraphs = function(text) {
1082
+ //
1083
+ // Params:
1084
+ // $text - string to process with html <p> tags
1085
+ //
1086
+
1087
+ // Strip leading and trailing lines:
1088
+ text = text.replace(/^\n+/g,"");
1089
+ text = text.replace(/\n+$/g,"");
1090
+
1091
+ var grafs = text.split(/\n{2,}/g);
1092
+ var grafsOut = new Array();
1093
+
1094
+ //
1095
+ // Wrap <p> tags.
1096
+ //
1097
+ var end = grafs.length;
1098
+ for (var i=0; i<end; i++) {
1099
+ var str = grafs[i];
1100
+
1101
+ // if this is an HTML marker, copy it
1102
+ if (str.search(/~K(\d+)K/g) >= 0) {
1103
+ grafsOut.push(str);
1104
+ }
1105
+ else if (str.search(/\S/) >= 0) {
1106
+ str = _RunSpanGamut(str);
1107
+ str = str.replace(/^([ \t]*)/g,"<p>");
1108
+ str += "</p>"
1109
+ grafsOut.push(str);
1110
+ }
1111
+
1112
+ }
1113
+
1114
+ //
1115
+ // Unhashify HTML blocks
1116
+ //
1117
+ end = grafsOut.length;
1118
+ for (var i=0; i<end; i++) {
1119
+ // if this is a marker for an html block...
1120
+ while (grafsOut[i].search(/~K(\d+)K/) >= 0) {
1121
+ var blockText = g_html_blocks[RegExp.$1];
1122
+ blockText = blockText.replace(/\$/g,"$$$$"); // Escape any dollar signs
1123
+ grafsOut[i] = grafsOut[i].replace(/~K\d+K/,blockText);
1124
+ }
1125
+ }
1126
+
1127
+ return grafsOut.join("\n\n");
1128
+ }
1129
+
1130
+
1131
+ var _EncodeAmpsAndAngles = function(text) {
1132
+ // Smart processing for ampersands and angle brackets that need to be encoded.
1133
+
1134
+ // Ampersand-encoding based entirely on Nat Irons's Amputator MT plugin:
1135
+ // http://bumppo.net/projects/amputator/
1136
+ text = text.replace(/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/g,"&amp;");
1137
+
1138
+ // Encode naked <'s
1139
+ text = text.replace(/<(?![a-z\/?\$!])/gi,"&lt;");
1140
+
1141
+ return text;
1142
+ }
1143
+
1144
+
1145
+ var _EncodeBackslashEscapes = function(text) {
1146
+ //
1147
+ // Parameter: String.
1148
+ // Returns: The string, with after processing the following backslash
1149
+ // escape sequences.
1150
+ //
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
+
1161
+ text = text.replace(/\\(\\)/g,escapeCharacters_callback);
1162
+ text = text.replace(/\\([`*_{}\[\]()>#+-.!])/g,escapeCharacters_callback);
1163
+ return text;
1164
+ }
1165
+
1166
+
1167
+ var _DoAutoLinks = function(text) {
1168
+
1169
+ text = text.replace(/<((https?|ftp|dict):[^'">\s]+)>/gi,"<a href=\"$1\">$1</a>");
1170
+
1171
+ // Email addresses: <address@domain.foo>
1172
+
1173
+ /*
1174
+ text = text.replace(/
1175
+ <
1176
+ (?:mailto:)?
1177
+ (
1178
+ [-.\w]+
1179
+ \@
1180
+ [-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+
1181
+ )
1182
+ >
1183
+ /gi, _DoAutoLinks_callback());
1184
+ */
1185
+ text = text.replace(/<(?:mailto:)?([-.\w]+\@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi,
1186
+ function(wholeMatch,m1) {
1187
+ return _EncodeEmailAddress( _UnescapeSpecialChars(m1) );
1188
+ }
1189
+ );
1190
+
1191
+ return text;
1192
+ }
1193
+
1194
+
1195
+ var _EncodeEmailAddress = function(addr) {
1196
+ //
1197
+ // Input: an email address, e.g. "foo@example.com"
1198
+ //
1199
+ // Output: the email address as a mailto link, with each character
1200
+ // of the address encoded as either a decimal or hex entity, in
1201
+ // the hopes of foiling most address harvesting spam bots. E.g.:
1202
+ //
1203
+ // <a href="&#x6D;&#97;&#105;&#108;&#x74;&#111;:&#102;&#111;&#111;&#64;&#101;
1204
+ // x&#x61;&#109;&#x70;&#108;&#x65;&#x2E;&#99;&#111;&#109;">&#102;&#111;&#111;
1205
+ // &#64;&#101;x&#x61;&#109;&#x70;&#108;&#x65;&#x2E;&#99;&#111;&#109;</a>
1206
+ //
1207
+ // Based on a filter by Matthew Wickline, posted to the BBEdit-Talk
1208
+ // mailing list: <http://tinyurl.com/yu7ue>
1209
+ //
1210
+
1211
+ // attacklab: why can't javascript speak hex?
1212
+ function char2hex(ch) {
1213
+ var hexDigits = '0123456789ABCDEF';
1214
+ var dec = ch.charCodeAt(0);
1215
+ return(hexDigits.charAt(dec>>4) + hexDigits.charAt(dec&15));
1216
+ }
1217
+
1218
+ var encode = [
1219
+ function(ch){return "&#"+ch.charCodeAt(0)+";";},
1220
+ function(ch){return "&#x"+char2hex(ch)+";";},
1221
+ function(ch){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 > .9 ? encode[2](ch) :
1236
+ r > .45 ? encode[1](ch) :
1237
+ encode[0](ch)
1238
+ );
1239
+ }
1240
+ return ch;
1241
+ });
1242
+
1243
+ addr = "<a href=\"" + addr + "\">" + addr + "</a>";
1244
+ addr = addr.replace(/">.+:/g,"\">"); // strip the mailto: from the visible part
1245
+
1246
+ return addr;
1247
+ }
1248
+
1249
+
1250
+ var _UnescapeSpecialChars = function(text) {
1251
+ //
1252
+ // Swap back in all the special characters we've hidden.
1253
+ //
1254
+ text = text.replace(/~E(\d+)E/g,
1255
+ function(wholeMatch,m1) {
1256
+ var charCodeToReplace = parseInt(m1);
1257
+ return String.fromCharCode(charCodeToReplace);
1258
+ }
1259
+ );
1260
+ return text;
1261
+ }
1262
+
1263
+
1264
+ var _Outdent = function(text) {
1265
+ //
1266
+ // Remove one level of line-leading tabs or spaces
1267
+ //
1268
+
1269
+ // attacklab: hack around Konqueror 3.5.4 bug:
1270
+ // "----------bug".replace(/^-/g,"") == "bug"
1271
+
1272
+ text = text.replace(/^(\t|[ ]{1,4})/gm,"~0"); // attacklab: g_tab_width
1273
+
1274
+ // attacklab: clean up hack
1275
+ text = text.replace(/~0/g,"")
1276
+
1277
+ return text;
1278
+ }
1279
+
1280
+ var _Detab = function(text) {
1281
+ // attacklab: Detab's completely rewritten for speed.
1282
+ // In perl we could fix it by anchoring the regexp with \G.
1283
+ // In javascript we're less fortunate.
1284
+
1285
+ // expand first n-1 tabs
1286
+ text = text.replace(/\t(?=\t)/g," "); // attacklab: g_tab_width
1287
+
1288
+ // replace the nth with two sentinels
1289
+ text = text.replace(/\t/g,"~A~B");
1290
+
1291
+ // use the sentinel to anchor our regex so it doesn't explode
1292
+ text = text.replace(/~B(.+?)~A/g,
1293
+ function(wholeMatch,m1,m2) {
1294
+ var leadingText = m1;
1295
+ var numSpaces = 4 - leadingText.length % 4; // attacklab: g_tab_width
1296
+
1297
+ // there *must* be a better way to do this:
1298
+ for (var i=0; i<numSpaces; i++) leadingText+=" ";
1299
+
1300
+ return leadingText;
1301
+ }
1302
+ );
1303
+
1304
+ // clean up sentinels
1305
+ text = text.replace(/~A/g," "); // attacklab: g_tab_width
1306
+ text = text.replace(/~B/g,"");
1307
+
1308
+ return text;
1309
+ }
1310
+
1311
+
1312
+ //
1313
+ // attacklab: Utility functions
1314
+ //
1315
+
1316
+
1317
+ var escapeCharacters = function(text, charsToEscape, afterBackslash) {
1318
+ // First we have to escape the escape characters so that
1319
+ // we can build a character class out of them
1320
+ var regexString = "([" + charsToEscape.replace(/([\[\]\\])/g,"\\$1") + "])";
1321
+
1322
+ if (afterBackslash) {
1323
+ regexString = "\\\\" + regexString;
1324
+ }
1325
+
1326
+ var regex = new RegExp(regexString,"g");
1327
+ text = text.replace(regex,escapeCharacters_callback);
1328
+
1329
+ return text;
1330
+ }
1331
+
1332
+
1333
+ var escapeCharacters_callback = function(wholeMatch,m1) {
1334
+ var charCodeToEscape = m1.charCodeAt(0);
1335
+ return "~E"+charCodeToEscape+"E";
1336
+ }
1337
+
1338
+ } // end of Showdown.converter
1339
+
1340
+ // export
1341
+ if (typeof module !== 'undefined') module.exports = Showdown;