Github Comment Enhancer

Enhances Github comments

目前为 2014-05-09 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @id Github_Comment_Enhancer@https://github.com/jerone/UserScripts
  3. // @name Github Comment Enhancer
  4. // @namespace https://github.com/jerone/UserScripts
  5. // @description Enhances Github comments
  6. // @author jerone
  7. // @homepage https://github.com/jerone/UserScripts/tree/master/Github_Comment_Enhancer
  8. // @homepageURL https://github.com/jerone/UserScripts/tree/master/Github_Comment_Enhancer
  9. // @version 1.2
  10. // @grant none
  11. // @run-at document-end
  12. // @include https://github.com/*/*/issues/*
  13. // @include https://github.com/*/*/pull/*
  14. // @include https://github.com/*/*/commit/*
  15. // ==/UserScript==
  16.  
  17. (function() {
  18.  
  19. // Source: https://github.com/gollum/gollum/blob/9c714e768748db4560bc017cacef4afa0c751a63/lib/gollum/public/gollum/javascript/editor/langs/markdown.js
  20. var MarkDown = {
  21. "function-bold": {
  22. search: /^(\s*)([\s\S]*?)(\s*)$/g,
  23. replace: "$1**$2**$3"
  24. },
  25. "function-italic": {
  26. search: /^(\s*)([\s\S]*?)(\s*)$/g,
  27. replace: "$1_$2_$3"
  28. },
  29. "function-strikethrough": {
  30. search: /^(\s*)([\s\S]*?)(\s*)$/g,
  31. replace: "$1~~$2~~$3"
  32. },
  33.  
  34. "function-h1": {
  35. search: /(.+)([\n]?)/g,
  36. replace: "# $1$2",
  37. forceNewline: true
  38. },
  39. "function-h2": {
  40. search: /(.+)([\n]?)/g,
  41. replace: "## $1$2",
  42. forceNewline: true
  43. },
  44. "function-h3": {
  45. search: /(.+)([\n]?)/g,
  46. replace: "### $1$2",
  47. forceNewline: true
  48. },
  49. "function-h4": {
  50. search: /(.+)([\n]?)/g,
  51. replace: "#### $1$2",
  52. forceNewline: true
  53. },
  54. "function-h5": {
  55. search: /(.+)([\n]?)/g,
  56. replace: "##### $1$2",
  57. forceNewline: true
  58. },
  59. "function-h6": {
  60. search: /(.+)([\n]?)/g,
  61. replace: "###### $1$2",
  62. forceNewline: true
  63. },
  64.  
  65. "function-link": {
  66. exec: function(txt, selText, next) {
  67. var isUrl = selText && /(?:https?:\/\/)|(?:www\.)/.test(selText.trim()),
  68. href = window.prompt("Link href:", isUrl ? selText.trim() : ""),
  69. text = window.prompt("Link text:", isUrl ? "" : selText.trim());
  70. if (href) {
  71. next("[" + (text || href) + "](" + href + ")");
  72. }
  73. }
  74. },
  75. "function-image": {
  76. exec: function(txt, selText, next) {
  77. var isUrl = selText && /(?:https?:\/\/)|(?:www\.)/.test(selText.trim()),
  78. href = window.prompt("Image href:", isUrl ? selText.trim() : ""),
  79. text = window.prompt("Image text:", isUrl ? "" : selText.trim());
  80. if (href) {
  81. next("![" + (text || href) + "](" + href + ")");
  82. }
  83. }
  84. },
  85.  
  86. "function-ul": {
  87. search: /(.+)([\n]?)/g,
  88. replace: "* $1$2",
  89. forceNewline: true
  90. },
  91. "function-ol": {
  92. exec: function(txt, selText, next) {
  93. var repText = "";
  94. if (!selText) {
  95. repText = "1. ";
  96. } else {
  97. var lines = selText.split("\n"),
  98. hasContent = /[\w]+/;
  99. for (var i = 0; i < lines.length; i++) {
  100. if (hasContent.test(lines[i])) {
  101. repText += (i + 1).toString() + ". " + lines[i] + "\n";
  102. }
  103. }
  104. }
  105. next(repText);
  106. }
  107. },
  108. "function-checklist": {
  109. search: /(.+)([\n]?)/g,
  110. replace: "* [ ] $1$2",
  111. forceNewline: true
  112. },
  113.  
  114. "function-code": {
  115. exec: function(txt, selText, next) {
  116. var rt = selText.indexOf("\n") > -1 ? "$1\n```\n$2\n```$3" : "$1`$2`$3";
  117. next(selText.replace(/^(\s*)([\s\S]*?)(\s*)$/g, rt));
  118. }
  119. },
  120. "function-blockquote": {
  121. search: /(.+)([\n]?)/g,
  122. replace: "> $1$2",
  123. forceNewline: true
  124. },
  125. "function-hr": {
  126. append: "\n***\n",
  127. forceNewline: true
  128. },
  129. "function-table": {
  130. append: '\n\
  131. | Header | Header | Header |\n\
  132. | :--- | :---: | ---: |\n\
  133. | Cell | Cell | Cell |\n\
  134. | Cell | Cell | Cell |\n\
  135. ',
  136. forceNewline: true
  137. }
  138. };
  139.  
  140. Array.forEach(document.querySelectorAll(".comment-form-textarea"), function(commentForm) {
  141. var gollumEditor = document.createElement("div");
  142. gollumEditor.innerHTML =
  143. '<div class="active" id="gollum-editor-function-bar" style="border:0 none;">' +
  144. ' <div id="gollum-editor-function-buttons">' +
  145. ' <div class="button-group">' +
  146. ' <a href="#" id="function-bold" class="minibutton function-button" title="Bold" tabindex="-1">' +
  147. ' <b>B</b>' +
  148. ' </a>' +
  149. ' <a href="#" id="function-italic" class="minibutton function-button" title="Italic" tabindex="-1">' +
  150. ' <em>i</em>' +
  151. ' </a>' +
  152. ' <a href="#" id="function-strikethrough" class="minibutton function-button" title="Strikethrough" tabindex="-1">' +
  153. ' <s>S</s>' +
  154. ' </a>' +
  155. ' </div>' +
  156.  
  157. ' <div class="button-group">' +
  158. ' <div class="select-menu js-menu-container js-select-menu js-composer-assignee-picker">' +
  159. ' <span aria-haspopup="true" title="Headers" role="button" class="minibutton select-menu-button icon-only js-menu-target" style="padding:0 18px 0 7px; width:auto; border-bottom-right-radius:3px; border-top-right-radius:3px;">' +
  160. ' <b>h#</b>' +
  161. ' </span>' +
  162. ' <div aria-hidden="false" class="select-menu-modal-holder js-menu-content js-navigation-container js-active-navigation-container" style="top: 26px;">' +
  163. ' <div class="select-menu-modal" style="width:auto;">' +
  164. ' <div class="select-menu-header">' +
  165. ' <span class="select-menu-title">Choose header</span>' +
  166. ' <span class="octicon octicon-remove-close js-menu-close"></span>' +
  167. ' </div>' +
  168. ' <div class="button-group">' +
  169. ' <a href="#" id="function-h1" class="minibutton function-button js-menu-close" title="Header 1" tabindex="-1">' +
  170. ' <b>h1</b>' +
  171. ' </a>' +
  172. ' <a href="#" id="function-h2" class="minibutton function-button js-menu-close" title="Header 2" tabindex="-1">' +
  173. ' <b>h2</b>' +
  174. ' </a>' +
  175. ' <a href="#" id="function-h3" class="minibutton function-button js-menu-close" title="Header 3" tabindex="-1">' +
  176. ' <b>h3</b>' +
  177. ' </a>' +
  178. ' <a href="#" id="function-h4" class="minibutton function-button js-menu-close" title="Header 4" tabindex="-1">' +
  179. ' <b>h4</b>' +
  180. ' </a>' +
  181. ' <a href="#" id="function-h5" class="minibutton function-button js-menu-close" title="Header 5" tabindex="-1">' +
  182. ' <b>h5</b>' +
  183. ' </a>' +
  184. ' <a href="#" id="function-h6" class="minibutton function-button js-menu-close" title="Header 6" tabindex="-1">' +
  185. ' <b>h6</b>' +
  186. ' </a>' +
  187. ' </div>' +
  188. ' </div>' +
  189. ' </div>' +
  190. ' </div>' +
  191. ' </div>' +
  192.  
  193. ' <div class="button-group">' +
  194. ' <a href="#" id="function-link" class="minibutton function-button" title="Link" tabindex="-1">' +
  195. ' <span class="octicon octicon-link"></span>' +
  196. ' </a>' +
  197. ' <a href="#" id="function-image" class="minibutton function-button" title="Image" tabindex="-1">' +
  198. ' <span class="octicon octicon-file-media"></span>' +
  199. ' </a>' +
  200. ' </div>' +
  201. ' <div class="button-group">' +
  202. ' <a href="#" id="function-ul" class="minibutton function-button" title="Unordered List" tabindex="-1">' +
  203. ' <span class="octicon octicon-list-unordered"></span>' +
  204. ' </a>' +
  205. ' <a href="#" id="function-ol" class="minibutton function-button" title="Ordered List" tabindex="-1">' +
  206. ' <span class="octicon octicon-list-ordered"></span>' +
  207. ' </a>' +
  208. ' <a href="#" id="function-checklist" class="minibutton function-button" title="Task List" tabindex="-1">' +
  209. ' <span class="octicon octicon-checklist"></span>' +
  210. ' </a>' +
  211. ' </div>' +
  212.  
  213. ' <div class="button-group">' +
  214. ' <a href="#" id="function-code" class="minibutton function-button" title="Code" tabindex="-1">' +
  215. ' <span class="octicon octicon-code"></span>' +
  216. ' </a>' +
  217. ' <a href="#" id="function-blockquote" class="minibutton function-button" title="Blockquote" tabindex="-1">' +
  218. ' <span class="octicon octicon-quote"></span>' +
  219. ' </a>' +
  220. ' <a href="#" id="function-hr" class="minibutton function-button" title="Horizontal Rule" tabindex="-1">' +
  221. ' <span class="octicon octicon-horizontal-rule"></span>' +
  222. ' </a>' +
  223. ' <a href="#" id="function-table" class="minibutton function-button" title="Table" tabindex="-1">' +
  224. ' <span class="octicon octicon-three-bars"></span>' +
  225. ' </a>' +
  226. ' </div>' +
  227.  
  228. //' <a href="#" id="function-help" class="minibutton function-button" title="Help" tabindex="-1">' +
  229. //' <span class="octicon octicon-question"></span>' +
  230. //' </a>' +
  231. ' </div>' +
  232. '</div>';
  233. commentForm.parentNode.insertBefore(gollumEditor, commentForm.parentNode.firstChild);
  234.  
  235. Array.forEach(gollumEditor.querySelectorAll(".function-button"), function(button) {
  236. button.addEventListener("click", function(e) {
  237. e.preventDefault();
  238.  
  239. executeAction(MarkDown[this.id], commentForm);
  240.  
  241. return false;
  242. });
  243. });
  244.  
  245. });
  246.  
  247. // Source: https://github.com/gollum/gollum/blob/9c714e768748db4560bc017cacef4afa0c751a63/lib/gollum/public/gollum/javascript/editor/gollum.editor.js#L516
  248. function executeAction(definitionObject, commentForm) {
  249. var txt = commentForm.value,
  250. selPos = {
  251. start: commentForm.selectionStart,
  252. end: commentForm.selectionEnd
  253. },
  254. selText = txt.substring(selPos.start, selPos.end),
  255. repText = selText,
  256. reselect = true,
  257. cursor = null;
  258.  
  259. // execute replacement function;
  260. if (definitionObject.exec) {
  261. definitionObject.exec(txt, selText, function(repText) {
  262. replaceFieldSelection(commentForm, repText);
  263. });
  264. return;
  265. }
  266.  
  267. // execute a search;
  268. var searchExp = new RegExp(definitionObject.search || /([^\n]+)/gi);
  269.  
  270. // replace text;
  271. if (definitionObject.replace) {
  272. var rt = definitionObject.replace;
  273. repText = repText.replace(searchExp, rt);
  274. repText = repText.replace(/\$[\d]/g, "");
  275. if (repText === "") {
  276. cursor = rt.indexOf("$1");
  277. repText = rt.replace(/\$[\d]/g, "");
  278. if (cursor === -1) {
  279. cursor = Math.floor(rt.length / 2);
  280. }
  281. }
  282. }
  283.  
  284. // append if necessary;
  285. if (definitionObject.append) {
  286. if (repText === selText) {
  287. reselect = false;
  288. }
  289. repText += definitionObject.append;
  290. }
  291.  
  292. if (repText) {
  293. if (definitionObject.forceNewline === true && (selPos.start > 0 && txt.substr(Math.max(0, selPos.start - 1), 1) !== "\n")) {
  294. repText = "\n" + repText;
  295. }
  296. replaceFieldSelection(commentForm, repText, reselect, cursor);
  297. }
  298. }
  299.  
  300. // Source: https://github.com/gollum/gollum/blob/9c714e768748db4560bc017cacef4afa0c751a63/lib/gollum/public/gollum/javascript/editor/gollum.editor.js#L708
  301. function replaceFieldSelection(commentForm, replaceText, reselect, cursorOffset) {
  302. var txt = commentForm.value,
  303. selPos = {
  304. start: commentForm.selectionStart,
  305. end: commentForm.selectionEnd
  306. };
  307.  
  308. var selectNew = true;
  309. if (reselect === false) {
  310. selectNew = false;
  311. }
  312.  
  313. var scrollTop = null;
  314. if (commentForm.scrollTop) {
  315. scrollTop = commentForm.scrollTop;
  316. }
  317.  
  318. commentForm.value = txt.substring(0, selPos.start) + replaceText + txt.substring(selPos.end);
  319. commentForm.focus();
  320.  
  321. if (selectNew) {
  322. if (cursorOffset) {
  323. commentForm.setSelectionRange(selPos.start + cursorOffset, selPos.start + cursorOffset);
  324. } else {
  325. commentForm.setSelectionRange(selPos.start, selPos.start + replaceText.length);
  326. }
  327. }
  328.  
  329. if (scrollTop) {
  330. commentForm.scrollTop = scrollTop;
  331. }
  332. }
  333.  
  334. })();