Github Reply Comments

Easy reply to Github comments

当前为 2019-04-24 提交的版本,查看 最新版本

  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.1
  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(".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 text = newComment.value.length > 0 ? "\n" : "";
  122. text += String.format('[**@{0}**]({1}/{0}) commented on [{2}]({3} "{4} - Replied by Github Reply Comments"):\n{5}\n\n',
  123. comment.querySelector(".author").textContent,
  124. location.origin,
  125. timestamp.firstElementChild.getAttribute("title"),
  126. timestamp.href,
  127. timestamp.firstElementChild.getAttribute("datetime"),
  128. commentText);
  129.  
  130. newComment.value += text;
  131. newComment.setSelectionRange(newComment.value.length, newComment.value.length);
  132. newComment.focus();
  133. });
  134.  
  135. var svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
  136. svg.classList.add("octicon", "octicon-mail-reply");
  137. svg.setAttribute("height", "16");
  138. svg.setAttribute("width", "16");
  139. reply.appendChild(svg);
  140. var path = document.createElementNS("http://www.w3.org/2000/svg", "path");
  141. 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");
  142. svg.appendChild(path);
  143.  
  144. actions.appendChild(reply);
  145. });
  146. }
  147.  
  148. // init;
  149. addReplyButtons();
  150.  
  151. // on pjax;
  152. document.addEventListener('pjax:end', addReplyButtons);
  153.  
  154. })();