Github Reply Comments

Easy reply to Github comments

目前为 2019-12-09 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name Github Reply Comments
  3. // @namespace https://github.com/jerone/UserScripts
  4. // @description Easy reply to Github comments
  5. // @author jerone
  6. // @copyright 2016+, jerone (http://jeroenvanwarmerdam.nl)
  7. // @license CC-BY-NC-SA-4.0; https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode
  8. // @license GPL-3.0-or-later; http://www.gnu.org/licenses/gpl-3.0.txt
  9. // @homepage https://github.com/jerone/UserScripts/tree/master/Github_Reply_Comments
  10. // @homepageURL https://github.com/jerone/UserScripts/tree/master/Github_Reply_Comments
  11. // @supportURL https://github.com/jerone/UserScripts/issues
  12. // @contributionURL https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=VCYMHWQ7ZMBKW
  13. // @version 1.0.4
  14. // @icon https://github.githubassets.com/pinned-octocat.svg
  15. // @grant none
  16. // @include https://github.com/*
  17. // @include https://gist.github.com/*
  18. // @require https://unpkg.com/turndown@5.0.3/dist/turndown.js
  19. // @require https://unpkg.com/turndown-plugin-gfm@1.0.2/dist/turndown-plugin-gfm.js
  20. // @require https://unpkg.com/turndown-plugin-github-code-snippet@1.0.2/turndown-plugin-github-code-snippet.js
  21. // ==/UserScript==
  22.  
  23. (function () {
  24.  
  25. String.format = function (string) {
  26. var args = Array.prototype.slice.call(arguments, 1, arguments.length);
  27. return string.replace(/{(\d+)}/g, function (match, number) {
  28. return typeof args[number] !== "undefined" ? args[number] : match;
  29. });
  30. };
  31.  
  32. var turndownService = new TurndownService({ headingStyle: 'atx', codeBlockStyle: 'fenced', hr: '***' });
  33. turndownService.use(turndownPluginGfm.gfm);
  34. turndownService.use(turndownPluginGithubCodeSnippet);
  35.  
  36. function getCommentTextarea(replyBtn) {
  37. var newComment = replyBtn;
  38. while (newComment && !newComment.classList.contains("js-quote-selection-container")) {
  39. newComment = newComment.parentNode;
  40. }
  41.  
  42. var inlineComment = newComment.querySelector(".js-inline-comment-form-container");
  43. if (inlineComment) {
  44. inlineComment.classList.add("open");
  45. }
  46.  
  47. var textareas = newComment.querySelectorAll(":scope > :not(.last-review-thread) .comment-form-textarea");
  48. return textareas[textareas.length - 1];
  49. }
  50.  
  51. function getCommentMarkdown(comment) {
  52. var commentText = "";
  53.  
  54. // Use raw comment when available.
  55. var commentForm = comment.querySelector(".comment-form-textarea");
  56. if (commentForm) {
  57. commentText = commentForm.value;
  58. }
  59.  
  60. // Convert comment HTML to markdown.
  61. if (!commentText) {
  62. // Clone it, so we can alter the HTML a bit, without modifing the page.
  63. var commentBody = comment.querySelector(".comment-body").cloneNode(true);
  64.  
  65. // Remove 'Toggle code wrap' buttons from https://greasyfork.org/en/scripts/18789-github-toggle-code-wrap
  66. Array.prototype.forEach.call(commentBody.querySelectorAll(".ghd-wrap-toggle"), function (ghd) {
  67. ghd.remove();
  68. });
  69.  
  70. // GitHub add an extra new line, which is converted by Turndown.
  71. Array.prototype.forEach.call(commentBody.querySelectorAll("pre code"), function (pre) {
  72. pre.innerHTML = pre.innerHTML.replace(/\n$/g, '');
  73. });
  74.  
  75. commentText = turndownService.turndown(commentBody.innerHTML);
  76. }
  77.  
  78. return commentText;
  79. }
  80.  
  81. function addReplyButtons() {
  82. Array.prototype.forEach.call(document.querySelectorAll(".comment, .review-comment"), function (comment) {
  83. var oldReply = comment.querySelector(".GithubReplyComments, .GithubCommentEnhancerReply");
  84. if (oldReply) {
  85. oldReply.parentNode.removeChild(oldReply);
  86. }
  87.  
  88. var header = comment.querySelector(":scope > :not(.minimized-comment) .timeline-comment-header"),
  89. actions = comment.querySelector(":scope > :not(.minimized-comment) .timeline-comment-actions");
  90.  
  91. if (!header) {
  92. header = actions;
  93. }
  94.  
  95. if (!actions) {
  96. if (!header) {
  97. return;
  98. }
  99. actions = document.createElement("div");
  100. actions.classList.add("timeline-comment-actions");
  101. header.insertBefore(actions, header.firstElementChild);
  102. }
  103.  
  104. var reply = document.createElement("button");
  105. reply.setAttribute("type", "button");
  106. reply.setAttribute("title", "Reply to this comment");
  107. reply.setAttribute("aria-label", "Reply to this comment");
  108. reply.classList.add("GithubReplyComments", "btn-link", "timeline-comment-action", "tooltipped", "tooltipped-ne");
  109. reply.addEventListener("click", function (e) {
  110. e.preventDefault();
  111.  
  112. var timestamp = comment.querySelector(".js-timestamp, .timestamp");
  113.  
  114. var commentText = getCommentMarkdown(comment);
  115. commentText = commentText.trim().split("\n").map(function (line) {
  116. return "> " + line;
  117. }).join("\n");
  118.  
  119. var newComment = getCommentTextarea(this);
  120.  
  121. var author = comment.querySelector(".author");
  122. var authorLink = location.origin + (author.getAttribute("href") || "/" + author.textContent);
  123.  
  124. var text = newComment.value.length > 0 ? "\n" : "";
  125. text += String.format('[**@{0}**]({1}) commented on [{2}]({3} "{4} - Replied by Github Reply Comments"):\n{5}\n\n',
  126. author.textContent,
  127. authorLink,
  128. timestamp.firstElementChild.getAttribute("title"),
  129. timestamp.href,
  130. timestamp.firstElementChild.getAttribute("datetime"),
  131. commentText);
  132.  
  133. newComment.value += text;
  134. newComment.setSelectionRange(newComment.value.length, newComment.value.length);
  135. newComment.focus();
  136. });
  137.  
  138. var svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
  139. svg.classList.add("octicon", "octicon-mail-reply");
  140. svg.setAttribute("height", "16");
  141. svg.setAttribute("width", "16");
  142. reply.appendChild(svg);
  143. var path = document.createElementNS("http://www.w3.org/2000/svg", "path");
  144. path.setAttribute("d", "M6 2.5l-6 4.5 6 4.5v-3c1.73 0 5.14 0.95 6 4.38 0-4.55-3.06-7.05-6-7.38v-3z");
  145. svg.appendChild(path);
  146.  
  147. actions.appendChild(reply);
  148. });
  149. }
  150.  
  151. // init;
  152. addReplyButtons();
  153.  
  154. // on pjax;
  155. document.addEventListener('pjax:end', addReplyButtons);
  156.  
  157. })();