// ==UserScript== // @id Github_Comment_Enhancer@https://github.com/jerone/UserScripts // @name Github Comment Enhancer // @namespace https://github.com/jerone/UserScripts // @description Enhances Github comments // @author jerone // @homepage https://github.com/jerone/UserScripts/tree/master/Github_Comment_Enhancer // @homepageURL https://github.com/jerone/UserScripts/tree/master/Github_Comment_Enhancer // @version 1.2 // @grant none // @run-at document-end // @include https://github.com/*/*/issues/* // @include https://github.com/*/*/pull/* // @include https://github.com/*/*/commit/* // @downloadURL none // ==/UserScript== (function() { // Source: https://github.com/gollum/gollum/blob/9c714e768748db4560bc017cacef4afa0c751a63/lib/gollum/public/gollum/javascript/editor/langs/markdown.js var MarkDown = { "function-bold": { search: /^(\s*)([\s\S]*?)(\s*)$/g, replace: "$1**$2**$3" }, "function-italic": { search: /^(\s*)([\s\S]*?)(\s*)$/g, replace: "$1_$2_$3" }, "function-strikethrough": { search: /^(\s*)([\s\S]*?)(\s*)$/g, replace: "$1~~$2~~$3" }, "function-h1": { search: /(.+)([\n]?)/g, replace: "# $1$2", forceNewline: true }, "function-h2": { search: /(.+)([\n]?)/g, replace: "## $1$2", forceNewline: true }, "function-h3": { search: /(.+)([\n]?)/g, replace: "### $1$2", forceNewline: true }, "function-h4": { search: /(.+)([\n]?)/g, replace: "#### $1$2", forceNewline: true }, "function-h5": { search: /(.+)([\n]?)/g, replace: "##### $1$2", forceNewline: true }, "function-h6": { search: /(.+)([\n]?)/g, replace: "###### $1$2", forceNewline: true }, "function-link": { exec: function(txt, selText, next) { var isUrl = selText && /(?:https?:\/\/)|(?:www\.)/.test(selText.trim()), href = window.prompt("Link href:", isUrl ? selText.trim() : ""), text = window.prompt("Link text:", isUrl ? "" : selText.trim()); if (href) { next("[" + (text || href) + "](" + href + ")"); } } }, "function-image": { exec: function(txt, selText, next) { var isUrl = selText && /(?:https?:\/\/)|(?:www\.)/.test(selText.trim()), href = window.prompt("Image href:", isUrl ? selText.trim() : ""), text = window.prompt("Image text:", isUrl ? "" : selText.trim()); if (href) { next("![" + (text || href) + "](" + href + ")"); } } }, "function-ul": { search: /(.+)([\n]?)/g, replace: "* $1$2", forceNewline: true }, "function-ol": { exec: function(txt, selText, next) { var repText = ""; if (!selText) { repText = "1. "; } else { var lines = selText.split("\n"), hasContent = /[\w]+/; for (var i = 0; i < lines.length; i++) { if (hasContent.test(lines[i])) { repText += (i + 1).toString() + ". " + lines[i] + "\n"; } } } next(repText); } }, "function-checklist": { search: /(.+)([\n]?)/g, replace: "* [ ] $1$2", forceNewline: true }, "function-code": { exec: function(txt, selText, next) { var rt = selText.indexOf("\n") > -1 ? "$1\n```\n$2\n```$3" : "$1`$2`$3"; next(selText.replace(/^(\s*)([\s\S]*?)(\s*)$/g, rt)); } }, "function-blockquote": { search: /(.+)([\n]?)/g, replace: "> $1$2", forceNewline: true }, "function-hr": { append: "\n***\n", forceNewline: true }, "function-table": { append: '\n\ | Header | Header | Header |\n\ | :--- | :---: | ---: |\n\ | Cell | Cell | Cell |\n\ | Cell | Cell | Cell |\n\ ', forceNewline: true } }; Array.forEach(document.querySelectorAll(".comment-form-textarea"), function(commentForm) { var gollumEditor = document.createElement("div"); gollumEditor.innerHTML = '
' + '
' + ' ' + '
' + '
' + ' ' + ' h#' + ' ' + '
' + '
' + '
' + ' Choose header' + ' ' + '
' + ' ' + '
' + '
' + '
' + '
' + '
' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + '
' + '
' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + '
' + ' ' + //' ' + //' ' + //' ' + '
' + '
'; commentForm.parentNode.insertBefore(gollumEditor, commentForm.parentNode.firstChild); Array.forEach(gollumEditor.querySelectorAll(".function-button"), function(button) { button.addEventListener("click", function(e) { e.preventDefault(); executeAction(MarkDown[this.id], commentForm); return false; }); }); }); // Source: https://github.com/gollum/gollum/blob/9c714e768748db4560bc017cacef4afa0c751a63/lib/gollum/public/gollum/javascript/editor/gollum.editor.js#L516 function executeAction(definitionObject, commentForm) { var txt = commentForm.value, selPos = { start: commentForm.selectionStart, end: commentForm.selectionEnd }, selText = txt.substring(selPos.start, selPos.end), repText = selText, reselect = true, cursor = null; // execute replacement function; if (definitionObject.exec) { definitionObject.exec(txt, selText, function(repText) { replaceFieldSelection(commentForm, repText); }); return; } // execute a search; var searchExp = new RegExp(definitionObject.search || /([^\n]+)/gi); // replace text; if (definitionObject.replace) { var rt = definitionObject.replace; repText = repText.replace(searchExp, rt); repText = repText.replace(/\$[\d]/g, ""); if (repText === "") { cursor = rt.indexOf("$1"); repText = rt.replace(/\$[\d]/g, ""); if (cursor === -1) { cursor = Math.floor(rt.length / 2); } } } // append if necessary; if (definitionObject.append) { if (repText === selText) { reselect = false; } repText += definitionObject.append; } if (repText) { if (definitionObject.forceNewline === true && (selPos.start > 0 && txt.substr(Math.max(0, selPos.start - 1), 1) !== "\n")) { repText = "\n" + repText; } replaceFieldSelection(commentForm, repText, reselect, cursor); } } // Source: https://github.com/gollum/gollum/blob/9c714e768748db4560bc017cacef4afa0c751a63/lib/gollum/public/gollum/javascript/editor/gollum.editor.js#L708 function replaceFieldSelection(commentForm, replaceText, reselect, cursorOffset) { var txt = commentForm.value, selPos = { start: commentForm.selectionStart, end: commentForm.selectionEnd }; var selectNew = true; if (reselect === false) { selectNew = false; } var scrollTop = null; if (commentForm.scrollTop) { scrollTop = commentForm.scrollTop; } commentForm.value = txt.substring(0, selPos.start) + replaceText + txt.substring(selPos.end); commentForm.focus(); if (selectNew) { if (cursorOffset) { commentForm.setSelectionRange(selPos.start + cursorOffset, selPos.start + cursorOffset); } else { commentForm.setSelectionRange(selPos.start, selPos.start + replaceText.length); } } if (scrollTop) { commentForm.scrollTop = scrollTop; } } })();