fogbugz-markdown

Automatically reinterpret comments as markdown.

  1. // ==UserScript==
  2. // @name fogbugz-markdown
  3. // @version 1.4
  4. // @namespace ohnopub.net
  5. // @description Automatically reinterpret comments as markdown.
  6. // @include http://*.fogbugz.com/*
  7. // @include https://*.fogbugz.com/*
  8. // @require https://code.jquery.com/jquery-1.10.2.min.js
  9. // @require https://cdnjs.cloudflare.com/ajax/libs/marked/0.3.1/marked.min.js
  10. // ==/UserScript==
  11.  
  12. this.jQuery = jQuery.noConflict(true);
  13. (function (jQuery) {
  14. /* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions */
  15. /* Also http://imgur.com/MzceH02 */
  16. var stringEscapeForRegExp = function (string){
  17. return string.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1");
  18. }
  19.  
  20. jQuery(document).ready(function () {
  21. var markedOptions = {
  22. renderer: new marked.Renderer()
  23. };
  24.  
  25. var markedRendererLink = markedOptions.renderer.link;
  26. var linkBugRegExp = new RegExp('^([a-z0-9]{1,12}://' + stringEscapeForRegExp(window.location.hostname) + '/)?/?(default\\.asp)?\\?([0-9]+)$');
  27. markedOptions.renderer.link = function (href, title, text) {
  28. /* Add back the onmouseover="b1(ix, this);" to each bug link */
  29. var matches = linkBugRegExp.exec(href);
  30. if (matches)
  31. return jQuery('<span/>').append(jQuery('<a/>', {'class': 'markdown-bugref', href: matches[0], onmouseover: 'b1(' + JSON.stringify(matches[3]|0) + ', this);', text: text, title: title})).html();
  32. return markedRendererLink.apply(this, arguments);
  33. };
  34.  
  35. jQuery('.bugevent.detailed .body, .pseudobugevent.detailed .body').each(function (i, elem) {
  36. var obj = jQuery(elem);
  37. /*
  38. * Detect preformatted (rich text) by text nodes with only
  39. * whitespace followed by an element containing text nodes
  40. * (empty or not, e.g. <p></p>).
  41. */
  42. var c = obj.contents();
  43. while (c.length && c[0].data && /^\s*$/.test(c[0].data))
  44. c.pop();
  45. /*
  46. * We have skipped the whitespace text nodes, now to see
  47. * if the first element is empty or not. Rich text is
  48. * mostly in <p/> elements and would have <span/>s
  49. * probably for formatting. Plain text only has <br/> if
  50. * anything. In plain text, we might thus encounter an
  51. * empty element *or* a non-empty text node but in rich
  52. * text that can never happen. Thus, if it’s not a text
  53. * node and not an empty element, assume it’s preformatted
  54. * rich text and abort.
  55. */
  56. if (c.length && !c[0].data && !jQuery(c[0]).is(':empty'))
  57. return;
  58.  
  59. /*
  60. * Make buglinks into something that will survive the
  61. * markdown.
  62. */
  63. obj.find('a').each(function (i, elem) {
  64. var obj = jQuery(elem);
  65. if (linkBugRegExp.exec(obj.attr('href')))
  66. obj.text('[' + obj.text() + '](' + obj.attr('href') + ')');
  67. });
  68. obj.html(marked(obj.text(), markedOptions));
  69. });
  70.  
  71. /* style blockquote */
  72. jQuery('head').append(
  73. jQuery('<style/>', {
  74. text: 'blockquote {margin-left: 0; border-left: 0.25em solid #aaaaaa; border-left-color: rgba(127, 127, 127, 0.5); padding-left: 1.75em;}',
  75. type: 'text/css'
  76. }));
  77. });
  78. })(jQuery);