', klass)
258 | }
259 | image.src = href
260 | }
261 |
262 | function fillFaceboxFromAjax(href, klass) {
263 | $.get(href, function(data) { $.facebox.reveal(data, klass) })
264 | }
265 |
266 | function skipOverlay() {
267 | return $.facebox.settings.overlay == false || $.facebox.settings.opacity === null
268 | }
269 |
270 | function showOverlay() {
271 | if (skipOverlay()) return
272 |
273 | if ($('#facebox_overlay').length == 0)
274 | $("body").append('')
275 |
276 | $('#facebox_overlay').hide().addClass("facebox_overlayBG")
277 | .css('opacity', $.facebox.settings.opacity)
278 | .click(function() { $(document).trigger('close.facebox') })
279 | .fadeIn(200)
280 | return false
281 | }
282 |
283 | function hideOverlay() {
284 | if (skipOverlay()) return
285 |
286 | $('#facebox_overlay').fadeOut(200, function(){
287 | $("#facebox_overlay").removeClass("facebox_overlayBG")
288 | $("#facebox_overlay").addClass("facebox_hide")
289 | $("#facebox_overlay").remove()
290 | })
291 |
292 | return false
293 | }
294 |
295 | /*
296 | * Bindings
297 | */
298 |
299 | $(document).bind('close.facebox', function() {
300 | $(document).unbind('keydown.facebox')
301 | $('#facebox').fadeOut(function() {
302 | $('#facebox .content').removeClass().addClass('content')
303 | $('#facebox .loading').remove()
304 | $(document).trigger('afterClose.facebox')
305 | })
306 | hideOverlay()
307 | })
308 |
309 | })(jQuery);
310 |
--------------------------------------------------------------------------------
/javascript/showdown.js:
--------------------------------------------------------------------------------
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 | //
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 | //
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 | // GitHub Flavored Markdown modifications by Tekkub
66 | // http://github.github.com/github-flavored-markdown/
67 | //
68 | // Modifications are tagged with "GFM"
69 | // **************************************************
70 |
71 | //
72 | // Showdown namespace
73 | //
74 | var Showdown = {};
75 |
76 | //
77 | // converter
78 | //
79 | // Wraps all "globals" so that the only thing
80 | // exposed is makeHtml().
81 | //
82 | Showdown.converter = function() {
83 |
84 | //
85 | // Globals:
86 | //
87 |
88 | // Global hashes, used by various utility routines
89 | var g_urls;
90 | var g_titles;
91 | var g_html_blocks;
92 |
93 | // Used to track when we're inside an ordered or unordered list
94 | // (see _ProcessListItems() for details):
95 | var g_list_level = 0;
96 |
97 |
98 | this.makeHtml = function(text) {
99 | //
100 | // Main function. The order in which other subs are called here is
101 | // essential. Link and image substitutions need to happen before
102 | // _EscapeSpecialCharsWithinTagAttributes(), so that any *'s or _'s in the
103 | // and tags get encoded.
104 | //
105 |
106 | // Clear the global hashes. If we don't clear these, you get conflicts
107 | // from other articles when generating a page which contains more than
108 | // one article (e.g. an index page that shows the N most recent
109 | // articles):
110 | g_urls = new Array();
111 | g_titles = new Array();
112 | g_html_blocks = new Array();
113 |
114 | // attacklab: Replace ~ with ~T
115 | // This lets us use tilde as an escape char to avoid md5 hashes
116 | // The choice of character is arbitray; anything that isn't
117 | // magic in Markdown will work.
118 | text = text.replace(/~/g,"~T");
119 |
120 | // attacklab: Replace $ with ~D
121 | // RegExp interprets $ as a special character
122 | // when it's in a replacement string
123 | text = text.replace(/\$/g,"~D");
124 |
125 | // Standardize line endings
126 | text = text.replace(/\r\n/g,"\n"); // DOS to Unix
127 | text = text.replace(/\r/g,"\n"); // Mac to Unix
128 |
129 | // Make sure text begins and ends with a couple of newlines:
130 | text = "\n\n" + text + "\n\n";
131 |
132 | // Convert all tabs to spaces.
133 | text = _Detab(text);
134 |
135 | // Strip any lines consisting only of spaces and tabs.
136 | // This makes subsequent regexen easier to write, because we can
137 | // match consecutive blank lines with /\n+/ instead of something
138 | // contorted like /[ \t]*\n+/ .
139 | text = text.replace(/^[ \t]+$/mg,"");
140 |
141 | // Turn block-level HTML blocks into hash entries
142 | text = _HashHTMLBlocks(text);
143 |
144 | // Strip link definitions, store in hashes.
145 | text = _StripLinkDefinitions(text);
146 |
147 | text = _RunBlockGamut(text);
148 |
149 | text = _UnescapeSpecialChars(text);
150 |
151 | // attacklab: Restore dollar signs
152 | text = text.replace(/~D/g,"$$");
153 |
154 | // attacklab: Restore tildes
155 | text = text.replace(/~T/g,"~");
156 |
157 | // ** GFM ** Auto-link URLs and emails
158 | text = text.replace(/https?\:\/\/[^"\s\<\>]*[^.,;'">\:\s\<\>\)\]\!]/g, function(wholeMatch,matchIndex){
159 | var left = text.slice(0, matchIndex), right = text.slice(matchIndex)
160 | if (left.match(/<[^>]+$/) && right.match(/^[^>]*>/)) {return wholeMatch}
161 | return "" + wholeMatch + "";
162 | });
163 | text = text.replace(/[a-z0-9_\-+=.]+@[a-z0-9\-]+(\.[a-z0-9-]+)+/ig, function(wholeMatch){return "" + wholeMatch + "";});
164 |
165 | // ** GFM ** Auto-link sha1 if GitHub.nameWithOwner is defined
166 | text = text.replace(/[a-f0-9]{40}/ig, function(wholeMatch,matchIndex){
167 | if (typeof(GitHub) == "undefined" || typeof(GitHub.nameWithOwner) == "undefined") {return wholeMatch;}
168 | var left = text.slice(0, matchIndex), right = text.slice(matchIndex)
169 | if (left.match(/@$/) || (left.match(/<[^>]+$/) && right.match(/^[^>]*>/))) {return wholeMatch;}
170 | return "" + wholeMatch.substring(0,7) + "";
171 | });
172 |
173 | // ** GFM ** Auto-link user@sha1 if GitHub.nameWithOwner is defined
174 | text = text.replace(/([a-z0-9_\-+=.]+)@([a-f0-9]{40})/ig, function(wholeMatch,username,sha,matchIndex){
175 | if (typeof(GitHub) == "undefined" || typeof(GitHub.nameWithOwner) == "undefined") {return wholeMatch;}
176 | GitHub.repoName = GitHub.repoName || _GetRepoName()
177 | var left = text.slice(0, matchIndex), right = text.slice(matchIndex)
178 | if (left.match(/\/$/) || (left.match(/<[^>]+$/) && right.match(/^[^>]*>/))) {return wholeMatch;}
179 | return "" + username + "@" + sha.substring(0,7) + "";
180 | });
181 |
182 | // ** GFM ** Auto-link user/repo@sha1
183 | text = text.replace(/([a-z0-9_\-+=.]+\/[a-z0-9_\-+=.]+)@([a-f0-9]{40})/ig, function(wholeMatch,repo,sha){
184 | return "" + repo + "@" + sha.substring(0,7) + "";
185 | });
186 |
187 | // ** GFM ** Auto-link #issue if GitHub.nameWithOwner is defined
188 | text = text.replace(/#([0-9]+)/ig, function(wholeMatch,issue,matchIndex){
189 | if (typeof(GitHub) == "undefined" || typeof(GitHub.nameWithOwner) == "undefined") {return wholeMatch;}
190 | var left = text.slice(0, matchIndex), right = text.slice(matchIndex)
191 | if (left == "" || left.match(/[a-z0-9_\-+=.]$/) || (left.match(/<[^>]+$/) && right.match(/^[^>]*>/))) {return wholeMatch;}
192 | return "" + wholeMatch + "";
193 | });
194 |
195 | // ** GFM ** Auto-link user#issue if GitHub.nameWithOwner is defined
196 | text = text.replace(/([a-z0-9_\-+=.]+)#([0-9]+)/ig, function(wholeMatch,username,issue,matchIndex){
197 | if (typeof(GitHub) == "undefined" || typeof(GitHub.nameWithOwner) == "undefined") {return wholeMatch;}
198 | GitHub.repoName = GitHub.repoName || _GetRepoName()
199 | var left = text.slice(0, matchIndex), right = text.slice(matchIndex)
200 | if (left.match(/\/$/) || (left.match(/<[^>]+$/) && right.match(/^[^>]*>/))) {return wholeMatch;}
201 | return "" + wholeMatch + "";
202 | });
203 |
204 | // ** GFM ** Auto-link user/repo#issue
205 | text = text.replace(/([a-z0-9_\-+=.]+\/[a-z0-9_\-+=.]+)#([0-9]+)/ig, function(wholeMatch,repo,issue){
206 | return "" + wholeMatch + "";
207 | });
208 |
209 | return text;
210 | }
211 |
212 |
213 | var _GetRepoName = function() {
214 | return GitHub.nameWithOwner.match(/^.+\/(.+)$/)[1]
215 | }
216 |
217 | var _StripLinkDefinitions = function(text) {
218 | //
219 | // Strips link definitions from text, stores the URLs and titles in
220 | // hash references.
221 | //
222 |
223 | // Link defs are in the form: ^[id]: url "optional title"
224 |
225 | /*
226 | var text = text.replace(/
227 | ^[ ]{0,3}\[(.+)\]: // id = $1 attacklab: g_tab_width - 1
228 | [ \t]*
229 | \n? // maybe *one* newline
230 | [ \t]*
231 | (\S+?)>? // url = $2
232 | [ \t]*
233 | \n? // maybe one newline
234 | [ \t]*
235 | (?:
236 | (\n*) // any lines skipped = $3 attacklab: lookbehind removed
237 | ["(]
238 | (.+?) // title = $4
239 | [")]
240 | [ \t]*
241 | )? // title is optional
242 | (?:\n+|$)
243 | /gm,
244 | function(){...});
245 | */
246 | var text = text.replace(/^[ ]{0,3}\[(.+)\]:[ \t]*\n?[ \t]*(\S+?)>?[ \t]*\n?[ \t]*(?:(\n*)["(](.+?)[")][ \t]*)?(?:\n+|\Z)/gm,
247 | function (wholeMatch,m1,m2,m3,m4) {
248 | m1 = m1.toLowerCase();
249 | g_urls[m1] = _EncodeAmpsAndAngles(m2); // Link IDs are case-insensitive
250 | if (m3) {
251 | // Oops, found blank lines, so it's not a title.
252 | // Put back the parenthetical statement we stole.
253 | return m3+m4;
254 | } else if (m4) {
255 | g_titles[m1] = m4.replace(/"/g,""");
256 | }
257 |
258 | // Completely remove the definition from the text
259 | return "";
260 | }
261 | );
262 |
263 | return text;
264 | }
265 |
266 |
267 | var _HashHTMLBlocks = function(text) {
268 | // attacklab: Double up blank lines to reduce lookaround
269 | text = text.replace(/\n/g,"\n\n");
270 |
271 | // Hashify HTML blocks:
272 | // We only want to do this for block-level HTML tags, such as headers,
273 | // lists, and tables. That's because we still want to wrap
s around
274 | // "paragraphs" that are wrapped in non-block-level tags, such as anchors,
275 | // phrase emphasis, and spans. The list of tags we're looking for is
276 | // hard-coded:
277 | var block_tags_a = "p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del"
278 | var block_tags_b = "p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math"
279 |
280 | // First, look for nested blocks, e.g.:
281 | //
282 | //
283 | // tags for inner block must be indented.
284 | //
285 | //
286 | //
287 | // The outermost tags must start at the left margin for this to match, and
288 | // the inner nested divs must be indented.
289 | // We need to do this before the next, more liberal match, because the next
290 | // match will start at the first `
` and stop at the first `
`.
291 |
292 | // attacklab: This regex can be expensive when it fails.
293 | /*
294 | var text = text.replace(/
295 | ( // save in $1
296 | ^ // start of line (with /m)
297 | <($block_tags_a) // start tag = $2
298 | \b // word break
299 | // attacklab: hack around khtml/pcre bug...
300 | [^\r]*?\n // any number of lines, minimally matching
301 | \2> // the matching end tag
302 | [ \t]* // trailing spaces/tabs
303 | (?=\n+) // followed by a newline
304 | ) // attacklab: there are sentinel newlines at end of document
305 | /gm,function(){...}};
306 | */
307 | 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);
308 |
309 | //
310 | // Now match more liberally, simply from `\n` to `\n`
311 | //
312 |
313 | /*
314 | var text = text.replace(/
315 | ( // save in $1
316 | ^ // start of line (with /m)
317 | <($block_tags_b) // start tag = $2
318 | \b // word break
319 | // attacklab: hack around khtml/pcre bug...
320 | [^\r]*? // any number of lines, minimally matching
321 | .*\2> // the matching end tag
322 | [ \t]* // trailing spaces/tabs
323 | (?=\n+) // followed by a newline
324 | ) // attacklab: there are sentinel newlines at end of document
325 | /gm,function(){...}};
326 | */
327 | 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);
328 |
329 | // Special case just for . It was easier to make a special case than
330 | // to make the other regex more complicated.
331 |
332 | /*
333 | text = text.replace(/
334 | ( // save in $1
335 | \n\n // Starting after a blank line
336 | [ ]{0,3}
337 | (<(hr) // start tag = $2
338 | \b // word break
339 | ([^<>])*? //
340 | \/?>) // the matching end tag
341 | [ \t]*
342 | (?=\n{2,}) // followed by a blank line
343 | )
344 | /g,hashElement);
345 | */
346 | text = text.replace(/(\n[ ]{0,3}(<(hr)\b([^<>])*?\/?>)[ \t]*(?=\n{2,}))/g,hashElement);
347 |
348 | // Special case for standalone HTML comments:
349 |
350 | /*
351 | text = text.replace(/
352 | ( // save in $1
353 | \n\n // Starting after a blank line
354 | [ ]{0,3} // attacklab: g_tab_width - 1
355 |
358 | [ \t]*
359 | (?=\n{2,}) // followed by a blank line
360 | )
361 | /g,hashElement);
362 | */
363 | text = text.replace(/(\n\n[ ]{0,3}[ \t]*(?=\n{2,}))/g,hashElement);
364 |
365 | // PHP and ASP-style processor instructions (...?> and <%...%>)
366 |
367 | /*
368 | text = text.replace(/
369 | (?:
370 | \n\n // Starting after a blank line
371 | )
372 | ( // save in $1
373 | [ ]{0,3} // attacklab: g_tab_width - 1
374 | (?:
375 | <([?%]) // $2
376 | [^\r]*?
377 | \2>
378 | )
379 | [ \t]*
380 | (?=\n{2,}) // followed by a blank line
381 | )
382 | /g,hashElement);
383 | */
384 | text = text.replace(/(?:\n\n)([ ]{0,3}(?:<([?%])[^\r]*?\2>)[ \t]*(?=\n{2,}))/g,hashElement);
385 |
386 | // attacklab: Undo double lines (see comment at top of this function)
387 | text = text.replace(/\n\n/g,"\n");
388 | return text;
389 | }
390 |
391 | var hashElement = function(wholeMatch,m1) {
392 | var blockText = m1;
393 |
394 | // Undo double lines
395 | blockText = blockText.replace(/\n\n/g,"\n");
396 | blockText = blockText.replace(/^\n/,"");
397 |
398 | // strip trailing blank lines
399 | blockText = blockText.replace(/\n+$/g,"");
400 |
401 | // Replace the element text with a marker ("~KxK" where x is its key)
402 | blockText = "\n\n~K" + (g_html_blocks.push(blockText)-1) + "K\n\n";
403 |
404 | return blockText;
405 | };
406 |
407 | var _RunBlockGamut = function(text) {
408 | //
409 | // These are all the transformations that form block-level
410 | // tags like paragraphs, headers, and list items.
411 | //
412 | text = _DoHeaders(text);
413 |
414 | // Do Horizontal Rules:
415 | var key = hashBlock("");
416 | text = text.replace(/^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$/gm,key);
417 | text = text.replace(/^[ ]{0,2}([ ]?\-[ ]?){3,}[ \t]*$/gm,key);
418 | text = text.replace(/^[ ]{0,2}([ ]?\_[ ]?){3,}[ \t]*$/gm,key);
419 |
420 | text = _DoLists(text);
421 | text = _DoCodeBlocks(text);
422 | text = _DoBlockQuotes(text);
423 |
424 | // We already ran _HashHTMLBlocks() before, in Markdown(), but that
425 | // was to escape raw HTML in the original Markdown source. This time,
426 | // we're escaping the markup we've just created, so that we don't wrap
427 | //
");});
731 |
732 | // atx-style headers:
733 | // # Header 1
734 | // ## Header 2
735 | // ## Header 2 with closing hashes ##
736 | // ...
737 | // ###### Header 6
738 | //
739 |
740 | /*
741 | text = text.replace(/
742 | ^(\#{1,6}) // $1 = string of #'s
743 | [ \t]*
744 | (.+?) // $2 = Header text
745 | [ \t]*
746 | \#* // optional closing #'s (not counted)
747 | \n+
748 | /gm, function() {...});
749 | */
750 |
751 | text = text.replace(/^(\#{1,6})[ \t]*(.+?)[ \t]*\#*\n+/gm,
752 | function(wholeMatch,m1,m2) {
753 | var h_level = m1.length;
754 | return hashBlock("" + _RunSpanGamut(m2) + "");
755 | });
756 |
757 | return text;
758 | }
759 |
760 | // This declaration keeps Dojo compressor from outputting garbage:
761 | var _ProcessListItems;
762 |
763 | var _DoLists = function(text) {
764 | //
765 | // Form HTML ordered (numbered) and unordered (bulleted) lists.
766 | //
767 |
768 | // attacklab: add sentinel to hack around khtml/safari bug:
769 | // http://bugs.webkit.org/show_bug.cgi?id=11231
770 | text += "~0";
771 |
772 | // Re-usable pattern to match any entirel ul or ol list:
773 |
774 | /*
775 | var whole_list = /
776 | ( // $1 = whole list
777 | ( // $2
778 | [ ]{0,3} // attacklab: g_tab_width - 1
779 | ([*+-]|\d+[.]) // $3 = first list item marker
780 | [ \t]+
781 | )
782 | [^\r]+?
783 | ( // $4
784 | ~0 // sentinel for workaround; should be $
785 | |
786 | \n{2,}
787 | (?=\S)
788 | (?! // Negative lookahead for another list item marker
789 | [ \t]*
790 | (?:[*+-]|\d+[.])[ \t]+
791 | )
792 | )
793 | )/g
794 | */
795 | var whole_list = /^(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm;
796 |
797 | if (g_list_level) {
798 | text = text.replace(whole_list,function(wholeMatch,m1,m2) {
799 | var list = m1;
800 | var list_type = (m2.search(/[*+-]/g)>-1) ? "ul" : "ol";
801 |
802 | // Turn double returns into triple returns, so that we can make a
803 | // paragraph for the last item in a list, if necessary:
804 | list = list.replace(/\n{2,}/g,"\n\n\n");;
805 | var result = _ProcessListItems(list);
806 |
807 | // Trim any trailing whitespace, to put the closing `$list_type>`
808 | // up on the preceding line, to get it past the current stupid
809 | // HTML block parser. This is a hack to work around the terrible
810 | // hack that is the HTML block parser.
811 | result = result.replace(/\s+$/,"");
812 | result = "<"+list_type+">" + result + ""+list_type+">\n";
813 | return result;
814 | });
815 | } else {
816 | whole_list = /(\n\n|^\n?)(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/g;
817 | text = text.replace(whole_list,function(wholeMatch,m1,m2,m3) {
818 | var runup = m1;
819 | var list = m2;
820 |
821 | var list_type = (m3.search(/[*+-]/g)>-1) ? "ul" : "ol";
822 | // Turn double returns into triple returns, so that we can make a
823 | // paragraph for the last item in a list, if necessary:
824 | var list = list.replace(/\n{2,}/g,"\n\n\n");;
825 | var result = _ProcessListItems(list);
826 | result = runup + "<"+list_type+">\n" + result + ""+list_type+">\n";
827 | return result;
828 | });
829 | }
830 |
831 | // attacklab: strip sentinel
832 | text = text.replace(/~0/,"");
833 |
834 | return text;
835 | }
836 |
837 | _ProcessListItems = function(list_str) {
838 | //
839 | // Process the contents of a single ordered or unordered list, splitting it
840 | // into individual list items.
841 | //
842 | // The $g_list_level global keeps track of when we're inside a list.
843 | // Each time we enter a list, we increment it; when we leave a list,
844 | // we decrement. If it's zero, we're not in a list anymore.
845 | //
846 | // We do this because when we're not inside a list, we want to treat
847 | // something like this:
848 | //
849 | // I recommend upgrading to version
850 | // 8. Oops, now this line is treated
851 | // as a sub-list.
852 | //
853 | // As a single paragraph, despite the fact that the second line starts
854 | // with a digit-period-space sequence.
855 | //
856 | // Whereas when we're inside a list (or sub-list), that line will be
857 | // treated as the start of a sub-list. What a kludge, huh? This is
858 | // an aspect of Markdown's syntax that's hard to parse perfectly
859 | // without resorting to mind-reading. Perhaps the solution is to
860 | // change the syntax rules such that sub-lists must start with a
861 | // starting cardinal number; e.g. "1." or "a.".
862 |
863 | g_list_level++;
864 |
865 | // trim trailing blank lines:
866 | list_str = list_str.replace(/\n{2,}$/,"\n");
867 |
868 | // attacklab: add sentinel to emulate \z
869 | list_str += "~0";
870 |
871 | /*
872 | list_str = list_str.replace(/
873 | (\n)? // leading line = $1
874 | (^[ \t]*) // leading whitespace = $2
875 | ([*+-]|\d+[.]) [ \t]+ // list marker = $3
876 | ([^\r]+? // list item text = $4
877 | (\n{1,2}))
878 | (?= \n* (~0 | \2 ([*+-]|\d+[.]) [ \t]+))
879 | /gm, function(){...});
880 | */
881 | list_str = list_str.replace(/(\n)?(^[ \t]*)([*+-]|\d+[.])[ \t]+([^\r]+?(\n{1,2}))(?=\n*(~0|\2([*+-]|\d+[.])[ \t]+))/gm,
882 | function(wholeMatch,m1,m2,m3,m4){
883 | var item = m4;
884 | var leading_line = m1;
885 | var leading_space = m2;
886 |
887 | if (leading_line || (item.search(/\n{2,}/)>-1)) {
888 | item = _RunBlockGamut(_Outdent(item));
889 | }
890 | else {
891 | // Recursion for sub-lists:
892 | item = _DoLists(_Outdent(item));
893 | item = item.replace(/\n$/,""); // chomp(item)
894 | item = _RunSpanGamut(item);
895 | }
896 |
897 | return "