Greasy Fork 支持简体中文。

Markdown textarea

Add Markdown convert button to textarea

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