Mercurial > hg > nodescore
comparison www/p/lib/js/showdown.js @ 42:49c94f63b8b0
css for nexus 7 and associated files- archive m.a added - remove later
author | tzara <rc-web@kiben.net> |
---|---|
date | Tue, 04 Sep 2012 07:25:49 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
41:56767c69b7c4 | 42:49c94f63b8b0 |
---|---|
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,"""); | |
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,"""); | |
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:  | |
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,"""); | |
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,"""); | |
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,"&"); | |
998 | |
999 // Do the angle bracket song and dance: | |
1000 text = text.replace(/</g,"<"); | |
1001 text = text.replace(/>/g,">"); | |
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,"&"); | |
1137 | |
1138 // Encode naked <'s | |
1139 text = text.replace(/<(?![a-z\/?\$!])/gi,"<"); | |
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="mailto:foo@e | |
1204 // xample.com">foo | |
1205 // @example.com</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; |