Advertisement
Guest User

Untitled

a guest
Nov 23rd, 2011
131
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  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. // The full source distribution is at:
  10. //
  11. //              A A L
  12. //              T C A
  13. //              T K B
  14. //
  15. //   <http://www.attacklab.net/>
  16. //
  17.  
  18. //
  19. // Wherever possible, Showdown is a straight, line-by-line port
  20. // of the Perl version of Markdown.
  21. //
  22. // This is not a normal parser design; it's basically just a
  23. // series of string substitutions.  It's hard to read and
  24. // maintain this way,  but keeping Showdown close to the original
  25. // design makes it easier to port new features.
  26. //
  27. // More importantly, Showdown behaves like markdown.pl in most
  28. // edge cases.  So web applications can do client-side preview
  29. // in Javascript, and then build identical HTML on the server.
  30. //
  31. // This port needs the new RegExp functionality of ECMA 262,
  32. // 3rd Edition (i.e. Javascript 1.5).  Most modern web browsers
  33. // should do fine.  Even with the new regular expression features,
  34. // We do a lot of work to emulate Perl's regex functionality.
  35. // The tricky changes in this file mostly have the "attacklab:"
  36. // label.  Major or self-explanatory changes don't.
  37. //
  38. // Smart diff tools like Araxis Merge will be able to match up
  39. // this file with markdown.pl in a useful way.  A little tweaking
  40. // helps: in a copy of markdown.pl, replace "#" with "//" and
  41. // replace "$text" with "text".  Be sure to ignore whitespace
  42. // and line endings.
  43. //
  44.  
  45. /*jslint browser:true, white:false, onevar:false, nomen:false, regexp:false, undef:false,
  46.          eqeqeq:false, plusplus:false, bitwise:false */
  47. /*globals window */
  48.  
  49. //
  50. // Showdown usage:
  51. //
  52. //   var text = "Markdown *rocks*.";
  53. //
  54. //   var converter = new Attacklab.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. // Attacklab namespace
  66. //
  67. window['Attacklab'] = window['Attacklab'] || {};
  68.  
  69. //
  70. // Showdown namespace
  71. //
  72. window['Attacklab']['showdown'] = window['Attacklab']['showdown'] || {};
  73.  
  74. /**
  75.  * Markdown converter
  76.  *
  77.  * Wraps all 'globals' so that the only public function is `makeHtml()`
  78.  *
  79.  * @constructor
  80.  */
  81. window['Attacklab']['showdown']['converter'] = function () {
  82.  
  83. //
  84. // Globals:
  85. //
  86.  
  87. // Global hashes, used by various utility routines
  88. var g_urls;
  89. var g_titles;
  90. var g_html_blocks;
  91.  
  92. // Used to track when we're inside an ordered or unordered list
  93. // (see _ProcessListItems() for details):
  94. var g_list_level = 0;
  95.  
  96. this['makeHtml'] = function(text) {
  97. //
  98. // Main function. The order in which other subs are called here is
  99. // essential. Link and image substitutions need to happen before
  100. // _EscapeSpecialCharsWithinTagAttributes(), so that any *'s or _'s in the <a>
  101. // and <img> tags get encoded.
  102. //
  103.  
  104.     // Clear the global hashes. If we don't clear these, you get conflicts
  105.     // from other articles when generating a page which contains more than
  106.     // one article (e.g. an index page that shows the N most recent
  107.     // articles):
  108.     g_urls = [];
  109.     g_titles = [];
  110.     g_html_blocks = [];
  111.  
  112.     // attacklab: Replace ~ with ~T
  113.     // This lets us use tilde as an escape char to avoid md5 hashes
  114.     // The choice of character is arbitray; anything that isn't
  115.     // magic in Markdown will work.
  116.     text = text.replace(/~/g,"~T");
  117.  
  118.     // attacklab: Replace $ with ~D
  119.     // RegExp interprets $ as a special character
  120.     // when it's in a replacement string
  121.     text = text.replace(/\$/g,"~D");
  122.  
  123.     // Standardize line endings
  124.     text = text.replace(/\r\n/g,"\n"); // DOS to Unix
  125.     text = text.replace(/\r/g,"\n"); // Mac to Unix
  126.  
  127.     // Make sure text begins and ends with a couple of newlines:
  128.     text = "\n\n" + text + "\n\n";
  129.  
  130.     // Convert all tabs to spaces.
  131.     text = _Detab(text);
  132.  
  133.     // Strip any lines consisting only of spaces and tabs.
  134.     // This makes subsequent regexen easier to write, because we can
  135.     // match consecutive blank lines with /\n+/ instead of something
  136.     // contorted like /[ \t]*\n+/ .
  137.     text = text.replace(/^[ \t]+$/mg,"");
  138.  
  139.     // Turn block-level HTML blocks into hash entries
  140.     text = _HashHTMLBlocks(text);
  141.  
  142.     // Strip link definitions, store in hashes.
  143.     text = _StripLinkDefinitions(text);
  144.  
  145.     text = _RunBlockGamut(text);
  146.  
  147.     text = _UnescapeSpecialChars(text);
  148.  
  149.     // attacklab: Restore dollar signs
  150.     text = text.replace(/~D/g,"$$");
  151.  
  152.     // attacklab: Restore tildes
  153.     text = text.replace(/~T/g,"~");
  154.  
  155.     return text;
  156. };
  157.  
  158. var _StripLinkDefinitions = function(orig) {
  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 = orig.replace(/^[ ]{0,3}\[(.+)\]:[ \t]*\n?[ \t]*<?(\S+?)>?[ \t]*\n?[ \t]*(?:(\n*)["(](.+?)[")][ \t]*)?(?:\n+)/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. var _HashHTMLBlocks = function(text) {
  208.     // attacklab: Double up blank lines to reduce lookaround
  209.     text = text.replace(/\n/g,"\n\n");
  210.  
  211.     // Hashify HTML blocks:
  212.     // We only want to do this for block-level HTML tags, such as headers,
  213.     // lists, and tables. That's because we still want to wrap <p>s around
  214.     // "paragraphs" that are wrapped in non-block-level tags, such as anchors,
  215.     // phrase emphasis, and spans. The list of tags we're looking for is
  216.     // hard-coded:
  217.     var block_tags_a = "p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del";
  218.     var block_tags_b = "p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math";
  219.  
  220.     // First, look for nested blocks, e.g.:
  221.     //   <div>
  222.     //     <div>
  223.     //     tags for inner block must be indented.
  224.     //     </div>
  225.     //   </div>
  226.     //
  227.     // The outermost tags must start at the left margin for this to match, and
  228.     // the inner nested divs must be indented.
  229.     // We need to do this before the next, more liberal match, because the next
  230.     // match will start at the first `<div>` and stop at the first `</div>`.
  231.  
  232.     // attacklab: This regex can be expensive when it fails.
  233.     /*
  234.         var text = text.replace(/
  235.         (                       // save in $1
  236.             ^                   // start of line  (with /m)
  237.             <($block_tags_a)    // start tag = $2
  238.             \b                  // word break
  239.                                 // attacklab: hack around khtml/pcre bug...
  240.             [^\r]*?\n           // any number of lines, minimally matching
  241.             </\2>               // the matching end tag
  242.             [ \t]*              // trailing spaces/tabs
  243.             (?=\n+)             // followed by a newline
  244.         )                       // attacklab: there are sentinel newlines at end of document
  245.         /gm,function(){...}};
  246.     */
  247.     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);
  248.  
  249.     //
  250.     // Now match more liberally, simply from `\n<tag>` to `</tag>\n`
  251.     //
  252.  
  253.     /*
  254.         var text = text.replace(/
  255.         (                       // save in $1
  256.             ^                   // start of line  (with /m)
  257.             <($block_tags_b)    // start tag = $2
  258.             \b                  // word break
  259.                                 // attacklab: hack around khtml/pcre bug...
  260.             [^\r]*?             // any number of lines, minimally matching
  261.             .*</\2>             // the matching end tag
  262.             [ \t]*              // trailing spaces/tabs
  263.             (?=\n+)             // followed by a newline
  264.         )                       // attacklab: there are sentinel newlines at end of document
  265.         /gm,function(){...}};
  266.     */
  267.     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);
  268.  
  269.     // Special case just for <hr />. It was easier to make a special case than
  270.     // to make the other regex more complicated.
  271.  
  272.     /*
  273.         text = text.replace(/
  274.         (                       // save in $1
  275.             \n\n                // Starting after a blank line
  276.             [ ]{0,3}
  277.             (<(hr)              // start tag = $2
  278.             \b                  // word break
  279.             ([^<>])*?           //
  280.             \/?>)               // the matching end tag
  281.             [ \t]*
  282.             (?=\n{2,})          // followed by a blank line
  283.         )
  284.         /g,hashElement);
  285.     */
  286.     text = text.replace(/(\n[ ]{0,3}(<(hr)\b([^<>])*?\/?>)[ \t]*(?=\n{2,}))/g,hashElement);
  287.  
  288.     // Special case for standalone HTML comments:
  289.  
  290.     /*
  291.         text = text.replace(/
  292.         (                       // save in $1
  293.             \n\n                // Starting after a blank line
  294.             [ ]{0,3}            // attacklab: g_tab_width - 1
  295.             <!
  296.             (--[^\r]*?--\s*)+
  297.             >
  298.             [ \t]*
  299.             (?=\n{2,})          // followed by a blank line
  300.         )
  301.         /g,hashElement);
  302.     */
  303.     text = text.replace(/(\n\n[ ]{0,3}<!(--[^\r]*?--\s*)+>[ \t]*(?=\n{2,}))/g,hashElement);
  304.  
  305.     // PHP and ASP-style processor instructions (<?...?> and <%...%>)
  306.  
  307.     /*
  308.         text = text.replace(/
  309.         (?:
  310.             \n\n                // Starting after a blank line
  311.         )
  312.         (                       // save in $1
  313.             [ ]{0,3}            // attacklab: g_tab_width - 1
  314.             (?:
  315.                 <([?%])         // $2
  316.                 [^\r]*?
  317.                 \2>
  318.             )
  319.             [ \t]*
  320.             (?=\n{2,})          // followed by a blank line
  321.         )
  322.         /g,hashElement);
  323.     */
  324.     text = text.replace(/(?:\n\n)([ ]{0,3}(?:<([?%])[^\r]*?\2>)[ \t]*(?=\n{2,}))/g,hashElement);
  325.  
  326.     // attacklab: Undo double lines (see comment at top of this function)
  327.     text = text.replace(/\n\n/g,"\n");
  328.     return text;
  329. };
  330.  
  331. var hashElement = function(wholeMatch,m1) {
  332.     var blockText = m1;
  333.  
  334.     // Undo double lines
  335.     blockText = blockText.replace(/\n\n/g,"\n");
  336.     blockText = blockText.replace(/^\n/,"");
  337.  
  338.     // strip trailing blank lines
  339.     blockText = blockText.replace(/\n+$/g,"");
  340.  
  341.     // Replace the element text with a marker ("~KxK" where x is its key)
  342.     blockText = "\n\n~K" + (g_html_blocks.push(blockText)-1) + "K\n\n";
  343.  
  344.     return blockText;
  345. };
  346.  
  347. var _RunBlockGamut = function(text) {
  348. //
  349. // These are all the transformations that form block-level
  350. // tags like paragraphs, headers, and list items.
  351. //
  352.     text = _DoHeaders(text);
  353.  
  354.     // Do Horizontal Rules:
  355.     var key = hashBlock("<hr />");
  356.     text = text.replace(/^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$/gm,key);
  357.     text = text.replace(/^[ ]{0,2}([ ]?-[ ]?){3,}[ \t]*$/gm,key);
  358.     text = text.replace(/^[ ]{0,2}([ ]?_[ ]?){3,}[ \t]*$/gm,key);
  359.  
  360.     text = _DoLists(text);
  361.     text = _DoCodeBlocks(text);
  362.     text = _DoBlockQuotes(text);
  363.  
  364.     // We already ran _HashHTMLBlocks() before, in Markdown(), but that
  365.     // was to escape raw HTML in the original Markdown source. This time,
  366.     // we're escaping the markup we've just created, so that we don't wrap
  367.     // <p> tags around block-level tags.
  368.     text = _HashHTMLBlocks(text);
  369.     text = _FormParagraphs(text);
  370.  
  371.     return text;
  372. };
  373.  
  374.  
  375. var _RunSpanGamut = function(text) {
  376. //
  377. // These are all the transformations that occur *within* block-level
  378. // tags like paragraphs, headers, and list items.
  379. //
  380.  
  381.     text = _DoCodeSpans(text);
  382.     text = _EscapeSpecialCharsWithinTagAttributes(text);
  383.     text = _EncodeBackslashEscapes(text);
  384.  
  385.     // Process anchor and image tags. Images must come first,
  386.     // because ![foo][f] looks like an anchor.
  387.     text = _DoAutoLinks(text); /** SV: moved this up to avoid autolinks breaking regular links */
  388.     text = _DoImages(text);
  389.     text = _DoAnchors(text);
  390.     text = _DoSmileys(text);
  391.  
  392.     // Make links out of things like `<http://example.com/>`
  393.     // Must come after _DoAnchors(), because you can use < and >
  394.     // delimiters in inline links like [this](<url>).
  395.     text = _EncodeAmpsAndAngles(text);
  396.     text = _DoItalicsAndBold(text);
  397.  
  398.     // Do hard breaks:
  399.     text = text.replace(/\n/g," <br>\n"); /** SV: don't need trailing spaces for line breaks */
  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) {
  510.         m7 = "";
  511.     }
  512.     var whole_match = m1;
  513.     var link_text   = m2;
  514.     var link_id  = m3.toLowerCase();
  515.     var url     = m4;
  516.     var title   = m7;
  517.  
  518.     if (url == "") {
  519.         if (link_id == "") {
  520.             // lower-case and turn embedded newlines into spaces
  521.             link_id = link_text.toLowerCase().replace(/ ?\n/g," ");
  522.         }
  523.         url = "#"+link_id;
  524.  
  525.         if (g_urls[link_id] !== undefined) {
  526.             url = g_urls[link_id];
  527.             if (g_titles[link_id] !== undefined) {
  528.                 title = g_titles[link_id];
  529.             }
  530.         }
  531.         else {
  532.             if (whole_match.search(/\(\s*\)$/m)>-1) {
  533.                 // Special case for explicit empty url
  534.                 url = "";
  535.             } else {
  536.                 return whole_match;
  537.             }
  538.         }
  539.     }
  540.  
  541.     url = escapeCharacters(url,"*_");
  542.     var result = "<a href=\"" + url + "\"";
  543.  
  544.     if (title != "") {
  545.         title = title.replace(/"/g,"&quot;");
  546.         title = escapeCharacters(title,"*_");
  547.         result +=  " title=\"" + title + "\"";
  548.     }
  549.  
  550.     result += ">" + link_text + "</a>";
  551.  
  552.     return result;
  553. };
  554.  
  555.  
  556. var _DoImages = function(text) {
  557. //
  558. // Turn Markdown image shortcuts into <img> tags.
  559. //
  560.  
  561.     //
  562.     // First, handle reference-style labeled images: ![alt text][id]
  563.     //
  564.  
  565.     /*
  566.         text = text.replace(/
  567.         (                       // wrap whole match in $1
  568.             !\[
  569.             (.*?)               // alt text = $2
  570.             \]
  571.  
  572.             [ ]?                // one optional space
  573.             (?:\n[ ]*)?         // one optional newline followed by spaces
  574.  
  575.             \[
  576.             (.*?)               // id = $3
  577.             \]
  578.         )()()()()               // pad rest of backreferences
  579.         /g,writeImageTag);
  580.     */
  581.     text = text.replace(/(!\[(.*?)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g,writeImageTag);
  582.  
  583.     //
  584.     // Next, handle inline images:  ![alt text](url "optional title")
  585.     // Don't forget: encode * and _
  586.  
  587.     /*
  588.         text = text.replace(/
  589.         (                       // wrap whole match in $1
  590.             !\[
  591.             (.*?)               // alt text = $2
  592.             \]
  593.             \s?                 // One optional whitespace character
  594.             \(                  // literal paren
  595.             [ \t]*
  596.             ()                  // no id, so leave $3 empty
  597.             <?(\S+?)>?          // src url = $4
  598.             [ \t]*
  599.             (                   // $5
  600.                 (['"])          // quote char = $6
  601.                 (.*?)           // title = $7
  602.                 \6              // matching quote
  603.                 [ \t]*
  604.             )?                  // title is optional
  605.         \)
  606.         )
  607.         /g,writeImageTag);
  608.     */
  609.     text = text.replace(/(!\[(.*?)\]\s?\([ \t]*()<?(\S+?)>?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,writeImageTag);
  610.  
  611.     return text;
  612. };
  613.  
  614. var writeImageTag = function(wholeMatch,m1,m2,m3,m4,m5,m6,m7) {
  615.     var whole_match = m1;
  616.     var alt_text   = m2;
  617.     var link_id  = m3.toLowerCase();
  618.     var url     = m4;
  619.     var title   = m7;
  620.  
  621.     if (!title) {
  622.         title = "";
  623.     }
  624.  
  625.     if (url == "") {
  626.         if (link_id == "") {
  627.             // lower-case and turn embedded newlines into spaces
  628.             link_id = alt_text.toLowerCase().replace(/ ?\n/g," ");
  629.         }
  630.         url = "#"+link_id;
  631.  
  632.         if (g_urls[link_id] !== undefined) {
  633.             url = g_urls[link_id];
  634.             if (g_titles[link_id] !== undefined) {
  635.                 title = g_titles[link_id];
  636.             }
  637.         }
  638.         else {
  639.             return whole_match;
  640.         }
  641.     }
  642.  
  643.     alt_text = alt_text.replace(/"/g,"&quot;");
  644.     url = escapeCharacters(url,"*_");
  645.     var result = "<img src=\"" + url + "\" alt=\"" + alt_text + "\"";
  646.  
  647.     // attacklab: Markdown.pl adds empty title attributes to images.
  648.     // Replicate this bug.
  649.  
  650.     //if (title != "") {
  651.         title = title.replace(/"/g,"&quot;");
  652.         title = escapeCharacters(title,"*_");
  653.         result +=  " title=\"" + title + "\"";
  654.     //}
  655.  
  656.     result += " />";
  657.  
  658.     return result;
  659. };
  660.  
  661.  
  662. var _DoHeaders = function(text) {
  663.     /** SV: limit headers to H2..6 */
  664.  
  665.     // Setext-style headers:
  666.     //  Header 1
  667.     //  ========
  668.     //
  669.     //  Header 2
  670.     //  --------
  671.     //
  672.     text = text.replace(/^(.+)[ \t]*\n=+[ \t]*\n+/gm,
  673.         function(wholeMatch,m1){return hashBlock("<h2>" + _RunSpanGamut(m1) + "</h2>");});
  674.  
  675.     text = text.replace(/^(.+)[ \t]*\n-+[ \t]*\n+/gm,
  676.         function(matchFound,m1){return hashBlock("<h3>" + _RunSpanGamut(m1) + "</h3>");});
  677.  
  678.     // atx-style headers:
  679.     //  # Header 1
  680.     //  ## Header 2
  681.     //  ## Header 2 with closing hashes ##
  682.     //  ...
  683.     //  ###### Header 6
  684.     //
  685.  
  686.     /*
  687.         text = text.replace(/
  688.             ^(\#{1,6})              // $1 = string of #'s
  689.             [ \t]*
  690.             (.+?)                   // $2 = Header text
  691.             [ \t]*
  692.             \#*                     // optional closing #'s (not counted)
  693.             \n+
  694.         /gm, function() {...});
  695.     */
  696.  
  697.     /** SV: limit headers to H2..6 */
  698.     text = text.replace(/^(\#{1,5})[ \t]*(.+?)[ \t]*\#*\n+/gm,
  699.         function(wholeMatch,m1,m2) {
  700.             var h_level = m1.length + 1;
  701.             return hashBlock("<h" + h_level + ">" + _RunSpanGamut(m2) + "</h" + h_level + ">");
  702.         });
  703.  
  704.     return text;
  705. };
  706.  
  707. // This declaration keeps Dojo compressor from outputting garbage:
  708. var _ProcessListItems;
  709.  
  710. var _DoLists = function(text) {
  711. //
  712. // Form HTML ordered (numbered) and unordered (bulleted) lists.
  713. //
  714.  
  715.     // attacklab: add sentinel to hack around khtml/safari bug:
  716.     // http://bugs.webkit.org/show_bug.cgi?id=11231
  717.     text += "~0";
  718.  
  719.     // Re-usable pattern to match any entirel ul or ol list:
  720.  
  721.     /*
  722.         var whole_list = /
  723.         (                                   // $1 = whole list
  724.             (                               // $2
  725.                 [ ]{0,3}                    // attacklab: g_tab_width - 1
  726.                 ([*+-]|\d+[.])              // $3 = first list item marker
  727.                 [ \t]+
  728.             )
  729.             [^\r]+?
  730.             (                               // $4
  731.                 ~0                          // sentinel for workaround; should be $
  732.             |
  733.                 \n{2,}
  734.                 (?=\S)
  735.                 (?!                         // Negative lookahead for another list item marker
  736.                     [ \t]*
  737.                     (?:[*+-]|\d+[.])[ \t]+
  738.                 )
  739.             )
  740.         )/g
  741.     */
  742.     var whole_list = /^(([ ]{0,3}([*+\-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+\-]|\d+[.])[ \t]+)))/gm;
  743.  
  744.     if (g_list_level) {
  745.         text = text.replace(whole_list,function(wholeMatch,m1,m2) {
  746.             var list = m1;
  747.             var list_type = (m2.search(/[*+\-]/g)>-1) ? "ul" : "ol";
  748.  
  749.             // Turn double returns into triple returns, so that we can make a
  750.             // paragraph for the last item in a list, if necessary:
  751.             list = list.replace(/\n{2,}/g,"\n\n\n");
  752.             var result = _ProcessListItems(list);
  753.  
  754.             // Trim any trailing whitespace, to put the closing `</$list_type>`
  755.             // up on the preceding line, to get it past the current stupid
  756.             // HTML block parser. This is a hack to work around the terrible
  757.             // hack that is the HTML block parser.
  758.             result = result.replace(/\s+$/,"");
  759.             result = "<"+list_type+">" + result + "</"+list_type+">\n";
  760.             return result;
  761.         });
  762.     } else {
  763.         whole_list = /(\n\n|^\n?)(([ ]{0,3}([*+\-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+\-]|\d+[.])[ \t]+)))/g;
  764.         text = text.replace(whole_list,function(wholeMatch,m1,m2,m3) {
  765.             var runup = m1;
  766.             var list = m2;
  767.  
  768.             var list_type = (m3.search(/[*+\-]/g)>-1) ? "ul" : "ol";
  769.             // Turn double returns into triple returns, so that we can make a
  770.             // paragraph for the last item in a list, if necessary:
  771.             list = list.replace(/\n{2,}/g,"\n\n\n");
  772.             var result = _ProcessListItems(list);
  773.             result = runup + "<"+list_type+">\n" + result + "</"+list_type+">\n";
  774.             return result;
  775.         });
  776.     }
  777.  
  778.     // attacklab: strip sentinel
  779.     text = text.replace(/~0/,"");
  780.  
  781.     return text;
  782. };
  783.  
  784. _ProcessListItems = function(list_str) {
  785. //
  786. //  Process the contents of a single ordered or unordered list, splitting it
  787. //  into individual list items.
  788. //
  789.     // The $g_list_level global keeps track of when we're inside a list.
  790.     // Each time we enter a list, we increment it; when we leave a list,
  791.     // we decrement. If it's zero, we're not in a list anymore.
  792.     //
  793.     // We do this because when we're not inside a list, we want to treat
  794.     // something like this:
  795.     //
  796.     //    I recommend upgrading to version
  797.     //    8. Oops, now this line is treated
  798.     //    as a sub-list.
  799.     //
  800.     // As a single paragraph, despite the fact that the second line starts
  801.     // with a digit-period-space sequence.
  802.     //
  803.     // Whereas when we're inside a list (or sub-list), that line will be
  804.     // treated as the start of a sub-list. What a kludge, huh? This is
  805.     // an aspect of Markdown's syntax that's hard to parse perfectly
  806.     // without resorting to mind-reading. Perhaps the solution is to
  807.     // change the syntax rules such that sub-lists must start with a
  808.     // starting cardinal number; e.g. "1." or "a.".
  809.  
  810.     g_list_level++;
  811.  
  812.     // trim trailing blank lines:
  813.     list_str = list_str.replace(/\n{2,}$/,"\n");
  814.  
  815.     // attacklab: add sentinel to emulate \z
  816.     list_str += "~0";
  817.  
  818.     /*
  819.         list_str = list_str.replace(/
  820.             (\n)?                           // leading line = $1
  821.             (^[ \t]*)                       // leading whitespace = $2
  822.             ([*+-]|\d+[.]) [ \t]+           // list marker = $3
  823.             ([^\r]+?                        // list item text   = $4
  824.             (\n{1,2}))
  825.             (?= \n* (~0 | \2 ([*+-]|\d+[.]) [ \t]+))
  826.         /gm, function(){...});
  827.     */
  828.     list_str = list_str.replace(/(\n)?(^[ \t]*)([*+\-]|\d+[.])[ \t]+([^\r]+?(\n{1,2}))(?=\n*(~0|\2([*+\-]|\d+[.])[ \t]+))/gm,
  829.         function(wholeMatch,m1,m2,m3,m4){
  830.             var item = m4;
  831.             var leading_line = m1;
  832.             var leading_space = m2;
  833.  
  834.             if (leading_line || (item.search(/\n{2,}/)>-1)) {
  835.                 item = _RunBlockGamut(_Outdent(item));
  836.             }
  837.             else {
  838.                 // Recursion for sub-lists:
  839.                 item = _DoLists(_Outdent(item));
  840.                 item = item.replace(/\n$/,""); // chomp(item)
  841.                 item = _RunSpanGamut(item);
  842.             }
  843.  
  844.             return  "<li>" + item + "</li>\n";
  845.         }
  846.     );
  847.  
  848.     // attacklab: strip sentinel
  849.     list_str = list_str.replace(/~0/g,"");
  850.  
  851.     g_list_level--;
  852.     return list_str;
  853. };
  854.  
  855.  
  856. var _DoCodeBlocks = function(text) {
  857. //
  858. //  Process Markdown `<pre><code>` blocks.
  859. //
  860.  
  861.     /*
  862.         text = text.replace(text,
  863.             /(?:\n\n|^)
  864.             (                               // $1 = the code block -- one or more lines, starting with a space/tab
  865.                 (?:
  866.                     (?:[ ]{4}|\t)           // Lines must start with a tab or a tab-width of spaces - attacklab: g_tab_width
  867.                     .*\n+
  868.                 )+
  869.             )
  870.             (\n*[ ]{0,3}[^ \t\n]|(?=~0))    // attacklab: g_tab_width
  871.         /g,function(){...});
  872.     */
  873.  
  874.     // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
  875.     text += "~0";
  876.  
  877.     text = text.replace(/(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=~0))/g,
  878.         function(wholeMatch,m1,m2) {
  879.             var codeblock = m1;
  880.             var nextChar = m2;
  881.  
  882.             codeblock = _EncodeCode( _Outdent(codeblock));
  883.             codeblock = _Detab(codeblock);
  884.             codeblock = codeblock.replace(/^\n+/g,""); // trim leading newlines
  885.             codeblock = codeblock.replace(/\n+$/g,""); // trim trailing whitespace
  886.  
  887.             codeblock = "<pre><code>" + codeblock + "\n</code></pre>";
  888.  
  889.             return hashBlock(codeblock) + nextChar;
  890.         }
  891.     );
  892.  
  893.     // attacklab: strip sentinel
  894.     text = text.replace(/~0/,"");
  895.  
  896.     return text;
  897. };
  898.  
  899. var hashBlock = function(text) {
  900.     text = text.replace(/(^\n+|\n+$)/g,"");
  901.     return "\n\n~K" + (g_html_blocks.push(text)-1) + "K\n\n";
  902. };
  903.  
  904.  
  905. var _DoCodeSpans = function(text) {
  906. //
  907. //   *  Backtick quotes are used for <code></code> spans.
  908. //
  909. //   *  You can use multiple backticks as the delimiters if you want to
  910. //   include literal backticks in the code span. So, this input:
  911. //
  912. //       Just type ``foo `bar` baz`` at the prompt.
  913. //
  914. //     Will translate to:
  915. //
  916. //       <p>Just type <code>foo `bar` baz</code> at the prompt.</p>
  917. //
  918. //  There's no arbitrary limit to the number of backticks you
  919. //  can use as delimters. If you need three consecutive backticks
  920. //  in your code, use four for delimiters, etc.
  921. //
  922. //  *  You can use spaces to get literal backticks at the edges:
  923. //
  924. //       ... type `` `bar` `` ...
  925. //
  926. //     Turns to:
  927. //
  928. //       ... type <code>`bar`</code> ...
  929. //
  930.  
  931.     /*
  932.         text = text.replace(/
  933.             (^|[^\\])                   // Character before opening ` can't be a backslash
  934.             (`+)                        // $2 = Opening run of `
  935.             (                           // $3 = The code block
  936.                 [^\r]*?
  937.                 [^`]                    // attacklab: work around lack of lookbehind
  938.             )
  939.             \2                          // Matching closer
  940.             (?!`)
  941.         /gm, function(){...});
  942.     */
  943.  
  944.     text = text.replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm,
  945.         function(wholeMatch,m1,m2,m3,m4) {
  946.             var c = m3;
  947.             c = c.replace(/^([ \t]*)/g,""); // leading whitespace
  948.             c = c.replace(/[ \t]*$/g,"");   // trailing whitespace
  949.             c = _EncodeCode(c);
  950.             return m1+"<code>"+c+"</code>";
  951.         });
  952.  
  953.     return text;
  954. };
  955.  
  956.  
  957. var _EncodeCode = function(text) {
  958. //
  959. // Encode/escape certain characters inside Markdown code runs.
  960. // The point is that in code, these characters are literals,
  961. // and lose their special Markdown meanings.
  962. //
  963.     // Encode all ampersands; HTML entities are not
  964.     // entities within a Markdown code span.
  965.     text = text.replace(/&/g,"&amp;");
  966.  
  967.     // Do the angle bracket song and dance:
  968.     text = text.replace(/</g,"&lt;");
  969.     text = text.replace(/>/g,"&gt;");
  970.  
  971.     // Now, escape characters that are magic in Markdown:
  972.     text = escapeCharacters(text,'*_{}[]\\',false);
  973.  
  974. // jj the line above breaks this:
  975. //---
  976.  
  977. //* Item
  978.  
  979. //   1. Subitem
  980.  
  981. //            special char: *
  982. //---
  983.  
  984.     return text;
  985. };
  986.  
  987.  
  988. var _DoItalicsAndBold = function(text) {
  989.  
  990.     // <strong> must go first:
  991.     text = text.replace(/(\*\*|__)(?=\S)([^\r]*?\S[\*_]*)\1/g,
  992.         "<strong>$2</strong>");
  993.  
  994.     text = text.replace(/(\*|_)(?=\S)([^\r]*?\S)\1/g,
  995.         "<em>$2</em>");
  996.  
  997.     return text;
  998. };
  999.  
  1000.  
  1001. var _DoBlockQuotes = function(text) {
  1002.  
  1003.     /*
  1004.         text = text.replace(/
  1005.         (                               // Wrap whole match in $1
  1006.             (
  1007.                 ^[ \t]*>[ \t]?          // '>' at the start of a line
  1008.                 .+\n                    // rest of the first line
  1009.                 (.+\n)*                 // subsequent consecutive lines
  1010.                 \n*                     // blanks
  1011.             )+
  1012.         )
  1013.         /gm, function(){...});
  1014.     */
  1015.  
  1016.     text = text.replace(/((^[ \t]*>[ \t]?.+\n(.+\n)*\n*)+)/gm,
  1017.         function(wholeMatch,m1) {
  1018.             var bq = m1;
  1019.  
  1020.             // attacklab: hack around Konqueror 3.5.4 bug:
  1021.             // "----------bug".replace(/^-/g,"") == "bug"
  1022.  
  1023.             bq = bq.replace(/^[ \t]*>[ \t]?/gm,"~0");   // trim one level of quoting
  1024.  
  1025.             // attacklab: clean up hack
  1026.             bq = bq.replace(/~0/g,"");
  1027.  
  1028.             bq = bq.replace(/^[ \t]+$/gm,"");       // trim whitespace-only lines
  1029.             bq = _RunBlockGamut(bq);                // recurse
  1030.  
  1031.             bq = bq.replace(/(^|\n)/g,"$1  ");
  1032.             // These leading spaces screw with <pre> content, so we need to fix that:
  1033.             bq = bq.replace(
  1034.                     /(\s*<pre>[^\r]+?<\/pre>)/gm,
  1035.                 function(wholeMatch,m1) {
  1036.                     var pre = m1;
  1037.                     // attacklab: hack around Konqueror 3.5.4 bug:
  1038.                     pre = pre.replace(/^[ ]{2}/mg,"~0");
  1039.                     pre = pre.replace(/~0/g,"");
  1040.                     return pre;
  1041.                 });
  1042.  
  1043.             return hashBlock("<blockquote>\n" + bq + "\n</blockquote>");
  1044.         });
  1045.     return text;
  1046. };
  1047.  
  1048.  
  1049. var _FormParagraphs = function(text) {
  1050. //
  1051. //  Params:
  1052. //    $text - string to process with html <p> tags
  1053. //
  1054.     var i;
  1055.  
  1056.     // Strip leading and trailing lines:
  1057.     text = text.replace(/^\n+/g,"");
  1058.     text = text.replace(/\n+$/g,"");
  1059.  
  1060.     var grafs = text.split(/\n{2,}/g);
  1061.     var grafsOut = [];
  1062.  
  1063.     //
  1064.     // Wrap <p> tags.
  1065.     //
  1066.     var end = grafs.length;
  1067.     for (i=0; i<end; i++) {
  1068.         var str = grafs[i];
  1069.  
  1070.         // if this is an HTML marker, copy it
  1071.         if (str.search(/~K(\d+)K/g) >= 0) {
  1072.             grafsOut.push(str);
  1073.         }
  1074.         else if (str.search(/\S/) >= 0) {
  1075.             str = _RunSpanGamut(str);
  1076.             str = str.replace(/^([ \t]*)/g,"<p>");
  1077.             str += "</p>";
  1078.             grafsOut.push(str);
  1079.         }
  1080.  
  1081.     }
  1082.  
  1083.     //
  1084.     // Unhashify HTML blocks
  1085.     //
  1086.     end = grafsOut.length;
  1087.     for (i=0; i<end; i++) {
  1088.         // if this is a marker for an html block...
  1089.         var results;
  1090.  
  1091.         while ((results = /~K(\d+)K/.exec(grafsOut[i])) !== null) {
  1092.             var blockText = g_html_blocks[results[1]];
  1093.             blockText = blockText.replace(/\$/g,"$$$$"); // Escape any dollar signs
  1094.             grafsOut[i] = grafsOut[i].replace(/~K\d+K/,blockText);
  1095.         }
  1096.     }
  1097.  
  1098.     return grafsOut.join("\n\n");
  1099. };
  1100.  
  1101.  
  1102. var _EncodeAmpsAndAngles = function(text) {
  1103. // Smart processing for ampersands and angle brackets that need to be encoded.
  1104.  
  1105.     // Ampersand-encoding based entirely on Nat Irons's Amputator MT plugin:
  1106.     //   http://bumppo.net/projects/amputator/
  1107.     text = text.replace(/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/g,"&amp;");
  1108.  
  1109.     // Encode naked <'s
  1110.     text = text.replace(/<(?![a-z\/?\$!])/gi,"&lt;");
  1111.  
  1112.     return text;
  1113. };
  1114.  
  1115.  
  1116. var _EncodeBackslashEscapes = function(text) {
  1117. //
  1118. //   Parameter:  String.
  1119. //   Returns:   The string, with after processing the following backslash
  1120. //             escape sequences.
  1121. //
  1122.  
  1123.     // attacklab: The polite way to do this is with the new
  1124.     // escapeCharacters() function:
  1125.     //
  1126.     //  text = escapeCharacters(text,"\\",true);
  1127.     //  text = escapeCharacters(text,"`*_{}[]()>#+-.!",true);
  1128.     //
  1129.     // ...but we're sidestepping its use of the (slow) RegExp constructor
  1130.     // as an optimization for Firefox.  This function gets called a LOT.
  1131.  
  1132.     text = text.replace(/\\(\\)/g,escapeCharacters_callback);
  1133.     text = text.replace(/\\([`*_{}\[\]()>#+-.!])/g,escapeCharacters_callback);
  1134.     return text;
  1135. };
  1136.  
  1137.  
  1138. var _DoAutoLinks = function(text) {
  1139.     /** SV: brackets optional for links */
  1140.     text = text.replace(/<?((https?|ftp|dict):[^'">\s]+)>?/gi,"<a href=\"$1\">$1</a>");
  1141.  
  1142.     // Email addresses: <address@domain.foo>
  1143.  
  1144.     /*
  1145.         text = text.replace(/
  1146.             <
  1147.             (?:mailto:)?
  1148.             (
  1149.                 [-.\w]+
  1150.                 \@
  1151.                 [-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+
  1152.             )
  1153.             >
  1154.         /gi, _DoAutoLinks_callback());
  1155.     */
  1156.     text = text.replace(/<(?:mailto:)?([\-.\w]+\@[\-a-z0-9]+(\.[\-a-z0-9]+)*\.[a-z]+)>/gi,
  1157.         function(wholeMatch,m1) {
  1158.             return _EncodeEmailAddress( _UnescapeSpecialChars(m1) );
  1159.         }
  1160.     );
  1161.  
  1162.     return text;
  1163. };
  1164.  
  1165.  
  1166. var _EncodeEmailAddress = function(addr) {
  1167. //
  1168. //  Input: an email address, e.g. "foo@example.com"
  1169. //
  1170. //  Output: the email address as a mailto link, with each character
  1171. //  of the address encoded as either a decimal or hex entity, in
  1172. //  the hopes of foiling most address harvesting spam bots. E.g.:
  1173. //
  1174. //  <a href="&#x6D;&#97;&#105;&#108;&#x74;&#111;:&#102;&#111;&#111;&#64;&#101;
  1175. //     x&#x61;&#109;&#x70;&#108;&#x65;&#x2E;&#99;&#111;&#109;">&#102;&#111;&#111;
  1176. //     &#64;&#101;x&#x61;&#109;&#x70;&#108;&#x65;&#x2E;&#99;&#111;&#109;</a>
  1177. //
  1178. //  Based on a filter by Matthew Wickline, posted to the BBEdit-Talk
  1179. //  mailing list: <http://tinyurl.com/yu7ue>
  1180. //
  1181.  
  1182.     // attacklab: why can't javascript speak hex?
  1183.     function char2hex(ch) {
  1184.         var hexDigits = '0123456789ABCDEF';
  1185.         var dec = ch.charCodeAt(0);
  1186.         return(hexDigits.charAt(dec>>4) + hexDigits.charAt(dec&15));
  1187.     }
  1188.  
  1189.     var encode = [
  1190.         function(ch){return "&#"+ch.charCodeAt(0)+";";},
  1191.         function(ch){return "&#x"+char2hex(ch)+";";},
  1192.         function(ch){return ch;}
  1193.     ];
  1194.  
  1195.     addr = "mailto:" + addr;
  1196.  
  1197.     addr = addr.replace(/./g, function(ch) {
  1198.         if (ch == "@") {
  1199.             // this *must* be encoded. I insist.
  1200.             ch = encode[Math.floor(Math.random()*2)](ch);
  1201.         } else if (ch !=":") {
  1202.             // leave ':' alone (to spot mailto: later)
  1203.             var r = Math.random();
  1204.             // roughly 10% raw, 45% hex, 45% dec
  1205.             ch =  (
  1206.                     r > 0.9  ?   encode[2](ch)   :
  1207.                     r > 0.45 ?   encode[1](ch)   :
  1208.                                 encode[0](ch)
  1209.                 );
  1210.         }
  1211.         return ch;
  1212.     });
  1213.  
  1214.     addr = "<a href=\"" + addr + "\">" + addr + "</a>";
  1215.     addr = addr.replace(/">.+:/g,"\">"); // strip the mailto: from the visible part
  1216.  
  1217.     return addr;
  1218. };
  1219.  
  1220.  
  1221. var _UnescapeSpecialChars = function(text) {
  1222. //
  1223. // Swap back in all the special characters we've hidden.
  1224. //
  1225.     text = text.replace(/~E(\d+)E/g,
  1226.         function(wholeMatch,m1) {
  1227.             var charCodeToReplace = parseInt(m1, 10);
  1228.             return String.fromCharCode(charCodeToReplace);
  1229.         }
  1230.     );
  1231.     return text;
  1232. };
  1233.  
  1234.  
  1235. var _Outdent = function(text) {
  1236. //
  1237. // Remove one level of line-leading tabs or spaces
  1238. //
  1239.  
  1240.     // attacklab: hack around Konqueror 3.5.4 bug:
  1241.     // "----------bug".replace(/^-/g,"") == "bug"
  1242.  
  1243.     text = text.replace(/^(\t|[ ]{1,4})/gm,"~0"); // attacklab: g_tab_width
  1244.  
  1245.     // attacklab: clean up hack
  1246.     text = text.replace(/~0/g,"");
  1247.  
  1248.     return text;
  1249. };
  1250.  
  1251. var _Detab = function(text) {
  1252. // attacklab: Detab's completely rewritten for speed.
  1253. // In perl we could fix it by anchoring the regexp with \G.
  1254. // In javascript we're less fortunate.
  1255.  
  1256.     // expand first n-1 tabs
  1257.     text = text.replace(/\t(?=\t)/g,"    "); // attacklab: g_tab_width
  1258.  
  1259.     // replace the nth with two sentinels
  1260.     text = text.replace(/\t/g,"~A~B");
  1261.  
  1262.     // use the sentinel to anchor our regex so it doesn't explode
  1263.     text = text.replace(/~B(.+?)~A/g,
  1264.         function(wholeMatch,m1,m2) {
  1265.             var leadingText = m1;
  1266.             var numSpaces = 4 - leadingText.length % 4;  // attacklab: g_tab_width
  1267.  
  1268.             // there *must* be a better way to do this:
  1269.             for (var i=0; i<numSpaces; i++) {
  1270.                 leadingText += " ";
  1271.             }
  1272.  
  1273.             return leadingText;
  1274.         }
  1275.     );
  1276.  
  1277.     // clean up sentinels
  1278.     text = text.replace(/~A/g,"    ");  // attacklab: g_tab_width
  1279.     text = text.replace(/~B/g,"");
  1280.  
  1281.     return text;
  1282. };
  1283.  
  1284.  
  1285. //
  1286. //  attacklab: Utility functions
  1287. //
  1288.  
  1289.  
  1290. var escapeCharacters = function(text, charsToEscape, afterBackslash) {
  1291.     // First we have to escape the escape characters so that
  1292.     // we can build a character class out of them
  1293.     var regexString = "([" + charsToEscape.replace(/([\[\]\\])/g,"\\$1") + "])";
  1294.  
  1295.     if (afterBackslash) {
  1296.         regexString = "\\\\" + regexString;
  1297.     }
  1298.  
  1299.     var regex = new RegExp(regexString,"g");
  1300.     text = text.replace(regex,escapeCharacters_callback);
  1301.  
  1302.     return text;
  1303. };
  1304.  
  1305.  
  1306. var escapeCharacters_callback = function(wholeMatch,m1) {
  1307.     var charCodeToEscape = m1.charCodeAt(0);
  1308.     return "~E"+charCodeToEscape+"E";
  1309. };
  1310.  
  1311. }; // end of Attacklab.showdown.converter
  1312.  
  1313.  
  1314. // Version 0.9 used the Showdown namespace instead of Attacklab.showdown
  1315. // The old namespace is deprecated, but we'll support it for now:
  1316. window['Showdown'] = window['Attacklab']['showdown'];
  1317.  
  1318. // If anyone's interested, tell the world that this file's been loaded
  1319. if (window['Attacklab']['fileLoaded']) {
  1320.     window['Attacklab']['fileLoaded']("showdown.js");
  1321. }
  1322.  
  1323.  
  1324. var smilies = [];
  1325.  
  1326. smilies['(finger)'] = ['Finger','images/emoticon-00173-middlefinger.png','images/emoticon-00173-middlefinger.gif'];
  1327. smilies['(bandit)'] = ['Bandit','images/emoticon-00174-bandit.png','images/emoticon-00174-bandit.gif'];
  1328. smilies['(drunk)'] = ['Drunk','images/emoticon-00175-drunk.png','images/emoticon-00175-drunk.gif'];
  1329. smilies['(smoking)'] = ['Smoking','images/emoticon-00176-smoke.png','images/emoticon-00176-smoke.gif'];
  1330. smilies['(toivo)'] = ['Toivo','images/emoticon-00177-toivo.png','images/emoticon-00177-toivo.gif'];
  1331. smilies['(rock)'] = ['Rock','images/emoticon-00178-rock.png','images/emoticon-00178-rock.gif'];
  1332. smilies['(headbang)'] = ['Headbang','images/emoticon-00179-headbang.png','images/emoticon-00179-headbang.gif'];
  1333. smilies['(bug)'] = ['Bug','images/emoticon-00180-bug.png','images/emoticon-00180-bug.gif'];
  1334. smilies['(fubar)'] = ['Fubar','images/emoticon-00181-fubar.png','images/emoticon-00181-fubar.gif'];
  1335. smilies['(poolparty)'] = ['Poolparty','images/emoticon-00182-poolparty.png','images/emoticon-00182-poolparty.gif'];
  1336. smilies['(swear)'] = ['Swearing','images/emoticon-00183-swear.png','images/emoticon-00183-swear.gif'];
  1337. smilies['(tmi)'] = ['TMI','images/emoticon-00184-tmi.png','images/emoticon-00184-tmi.gif'];
  1338. smilies['(heidy)'] = ['Heidy','images/emoticon-00185-heidy.png','images/emoticon-00185-heidy.gif'];
  1339. smilies['(mooning)'] = ['Mooning','images/emoticon-00172-mooning.png','images/emoticon-00172-mooning.gif'];
  1340. smilies['(highfive)'] = ['High Five','images/highfive.png','images/highfive.png'];
  1341. smilies['(facepalm)'] = ['Face Palm','images/facepalm.png','images/facepalm.png'];
  1342. smilies['(fingers)'] = ['Fingers Crossed','images/fingerscrossed.png','images/fingerscrossed.png'];
  1343. smilies['(lalala)'] = ['Lalala','images/lalala.png','images/lalala.png'];
  1344. smilies['(waiting)'] = ['Waiting','images/waiting.png','images/waiting.png'];
  1345. smilies['(tumbleweed)'] = ['Tumbleweed','images/tumbleweed.png','images/tumbleweed.png'];
  1346. smilies['(wfh)'] = ['Working From Home','images/wfh.png','images/wfh.png'];
  1347. smilies[':)'] = ['Smile','images/emoticon-00100-smile.png','images/emoticon-00100-smile.gif'];
  1348. smilies[':('] = ['Sad Smile','images/emoticon-00101-sadsmile.png','images/emoticon-00101-sadsmile.gif'];
  1349. smilies[':D'] = ['Big Smile','images/emoticon-00102-bigsmile.png','images/emoticon-00102-bigsmile.gif'];
  1350. smilies['8)'] = ['Cool','images/emoticon-00103-cool.png','images/emoticon-00103-cool.gif'];
  1351. smilies[':o'] = ['Wink','images/emoticon-00105-wink.png','images/emoticon-00105-wink.gif'];
  1352. smilies[';('] = ['Crying','images/emoticon-00106-crying.png','images/emoticon-00106-crying.gif'];
  1353. smilies['(sweat)'] = ['Sweating','images/emoticon-00107-sweating.png','images/emoticon-00107-sweating.gif'];
  1354. smilies[':|'] = ['Speechless','images/emoticon-00108-speechless.png','images/emoticon-00108-speechless.gif'];
  1355. smilies[':*'] = ['Kiss','images/emoticon-00109-kiss.png','images/emoticon-00109-kiss.gif'];
  1356. smilies[':P'] = ['Tongue Out','images/emoticon-00110-tongueout.png','images/emoticon-00110-tongueout.gif'];
  1357. smilies['(blush)'] = ['Blush','images/emoticon-00111-blush.png','images/emoticon-00111-blush.gif'];
  1358. smilies[':^)'] = ['Wondering','images/emoticon-00112-wondering.png','images/emoticon-00112-wondering.gif'];
  1359. smilies['|-)'] = ['Sleepy','images/emoticon-00113-sleepy.png','images/emoticon-00113-sleepy.gif'];
  1360. smilies['|('] = ['Dull','images/emoticon-00114-dull.png','images/emoticon-00114-dull.gif'];
  1361. smilies['(inlove)'] = ['In love','images/emoticon-00115-inlove.png','images/emoticon-00115-inlove.gif'];
  1362. smilies[']:)'] = ['Evil grin','images/emoticon-00116-evilgrin.png','images/emoticon-00116-evilgrin.gif'];
  1363. smilies['(talk)'] = ['Talking','images/emoticon-00117-talking.png','images/emoticon-00117-talking.gif'];
  1364. smilies['(yawn)'] = ['Yawn','images/emoticon-00118-yawn.png','images/emoticon-00118-yawn.gif'];
  1365. smilies['(puke)'] = ['Puke','images/emoticon-00119-puke.png','images/emoticon-00119-puke.gif'];
  1366. smilies['(doh)'] = ['Doh!','images/emoticon-00120-doh.png','images/emoticon-00120-doh.gif'];
  1367. smilies[':@'] = ['Angry','images/emoticon-00121-angry.png','images/emoticon-00121-angry.gif'];
  1368. smilies['(wasntme)'] = ['It wasnt me','images/emoticon-00122-itwasntme.png','images/emoticon-00122-itwasntme.gif'];
  1369. smilies['(party)'] = ['Party!!!','images/emoticon-00123-party.png','images/emoticon-00123-party.gif'];
  1370. smilies[':S'] = ['Worried','images/emoticon-00124-worried.png','images/emoticon-00124-worried.gif'];
  1371. smilies['(mm)'] = ['Mmm...','images/emoticon-00125-mmm.png','images/emoticon-00125-mmm.gif'];
  1372. smilies['8-|'] = ['Nerd','images/emoticon-00126-nerd.png','images/emoticon-00126-nerd.gif'];
  1373. smilies[':x'] = ['Lips Sealed','images/emoticon-00127-lipssealed.png','images/emoticon-00127-lipssealed.gif'];
  1374. smilies['(hi)'] = ['Hi','images/emoticon-00128-hi.png','images/emoticon-00128-hi.gif'];
  1375. smilies['(call)'] = ['Call','images/emoticon-00129-call.png','images/emoticon-00129-call.gif'];
  1376. smilies['(devil)'] = ['Devil','images/emoticon-00130-devil.png','images/emoticon-00130-devil.gif'];
  1377. smilies['(angel)'] = ['Angel','images/emoticon-00131-angel.png','images/emoticon-00131-angel.gif'];
  1378. smilies['(envy)'] = ['Envy','images/emoticon-00132-envy.png','images/emoticon-00132-envy.gif'];
  1379. smilies['(wait)'] = ['Wait','images/emoticon-00133-wait.png','images/emoticon-00133-wait.gif'];
  1380. smilies['(bear)'] = ['Bear','images/emoticon-00134-bear.png','images/emoticon-00134-bear.gif'];
  1381. smilies['(makeup)'] = ['Make-up','images/emoticon-00135-makeup.png','images/emoticon-00135-makeup.gif'];
  1382. smilies['(giggle)'] = ['Covered Laugh','images/emoticon-00136-giggle.png','images/emoticon-00136-giggle.gif'];
  1383. smilies['(clap)'] = ['Clapping Hands','images/emoticon-00137-clapping.png','images/emoticon-00137-clapping.gif'];
  1384. smilies['(think)'] = ['Thinking','images/emoticon-00138-thinking.png','images/emoticon-00138-thinking.gif'];
  1385. smilies['(bow)'] = ['Bow','images/emoticon-00139-bow.png','images/emoticon-00139-bow.gif'];
  1386. smilies['(rofl)'] = ['Rolling on the floor laughing','images/emoticon-00140-rofl.png','images/emoticon-00140-rofl.gif'];
  1387. smilies['(whew)'] = ['Whew','images/emoticon-00141-whew.png','images/emoticon-00141-whew.gif'];
  1388. smilies['(happy)'] = ['Happy','images/emoticon-00142-happy.png','images/emoticon-00142-happy.gif'];
  1389. smilies['(smirk)'] = ['Smirking','images/emoticon-00143-smirk.png','images/emoticon-00143-smirk.gif'];
  1390. smilies['(nod)'] = ['Nodding','images/emoticon-00144-nod.png','images/emoticon-00144-nod.gif'];
  1391. smilies['(shake)'] = ['Shaking','images/emoticon-00145-shake.png','images/emoticon-00145-shake.gif'];
  1392. smilies['(punch)'] = ['Punch','images/emoticon-00146-punch.png','images/emoticon-00146-punch.gif'];
  1393. smilies['(emo)'] = ['Emo','images/emoticon-00147-emo.png','images/emoticon-00147-emo.gif'];
  1394. smilies['(y)'] = ['Yes','images/emoticon-00148-yes.png','images/emoticon-00148-yes.gif'];
  1395. smilies['(n)'] = ['No','images/emoticon-00149-no.png','images/emoticon-00149-no.gif'];
  1396. smilies['(handshake)'] = ['Shaking Hands','images/emoticon-00150-handshake.png','images/emoticon-00150-handshake.gif'];
  1397. smilies['(skype)'] = ['Skype','images/emoticon-00151-skype.png','images/emoticon-00151-skype.gif'];
  1398. smilies['(h)'] = ['Heart','images/emoticon-00152-heart.png','images/emoticon-00152-heart.gif'];
  1399. smilies['(u)'] = ['Broken heart','images/emoticon-00153-brokenheart.png','images/emoticon-00153-brokenheart.gif'];
  1400. smilies['(e)'] = ['Mail','images/emoticon-00154-mail.png','images/emoticon-00154-mail.gif'];
  1401. smilies['(f)'] = ['Flower','images/emoticon-00155-flower.png','images/emoticon-00155-flower.gif'];
  1402. smilies['(rain)'] = ['Rain','images/emoticon-00156-rain.png','images/emoticon-00156-rain.gif'];
  1403. smilies['(sun)'] = ['Sun','images/emoticon-00157-sun.png','images/emoticon-00157-sun.gif'];
  1404. smilies['(o)'] = ['Time','images/emoticon-00158-time.png','images/emoticon-00158-time.gif'];
  1405. smilies['(music)'] = ['Music','images/emoticon-00159-music.png','images/emoticon-00159-music.gif'];
  1406. smilies['(~)'] = ['Movie','images/emoticon-00160-movie.png','images/emoticon-00160-movie.gif'];
  1407. smilies['(mp)'] = ['Phone','images/emoticon-00161-phone.png','images/emoticon-00161-phone.gif'];
  1408. smilies['(coffee)'] = ['Coffee','images/emoticon-00162-coffee.png','images/emoticon-00162-coffee.gif'];
  1409. smilies['(pizza)'] = ['Pizza','images/emoticon-00163-pizza.png','images/emoticon-00163-pizza.gif'];
  1410. smilies['(cash)'] = ['Cash','images/emoticon-00164-cash.png','images/emoticon-00164-cash.gif'];
  1411. smilies['(muscle)'] = ['Muscle','images/emoticon-00165-muscle.png','images/emoticon-00165-muscle.gif'];
  1412. smilies['(^)'] = ['Cake','images/emoticon-00166-cake.png','images/emoticon-00166-cake.gif'];
  1413. smilies['(beer)'] = ['Beer','images/emoticon-00167-beer.png','images/emoticon-00167-beer.gif'];
  1414. smilies['(d)'] = ['Drink','images/emoticon-00168-drink.png','images/emoticon-00168-drink.gif'];
  1415. smilies['(dance)'] = ['Dance','images/emoticon-00169-dance.png','images/emoticon-00169-dance.gif'];
  1416. smilies['(ninja)'] = ['Ninja','images/emoticon-00170-ninja.png','images/emoticon-00170-ninja.gif'];
  1417. smilies['(*)'] = ['Star','images/emoticon-00171-star.png','images/emoticon-00171-star.gif'];
  1418.  
  1419.  
  1420. var _DoSmileys = function(text) {
  1421.  
  1422.     for (i in smilies) {
  1423.         var prepex = escapeRegExp(i);
  1424.         var rex = new RegExp('^'+prepex);
  1425.         text = text.replace(rex,'<img src="./qa-plugin/smilies/'+smilies[i][1]+'"/>');
  1426.         var rex = new RegExp('(.)('+prepex+')','g');
  1427.         text = text.replace(rex,replacer);
  1428.     }
  1429.  
  1430.     return text;
  1431. }
  1432.  
  1433. function escapeRegExp(str) {
  1434.   return str.replace(/[-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
  1435. }
  1436. function replacer(str, p1, p2, offset, s)  
  1437. {  
  1438.   if(p1 == '\\') return p2;
  1439.   return p1+'<img src="./qa-plugin/smilies/'+smilies[p2][1]+'"/>';  
  1440. }  
  1441.  
  1442.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement