// ==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 // @copyright 2014+, jerone (http://jeroenvanwarmerdam.nl) // @license GNU GPLv3 // @homepage https://github.com/jerone/UserScripts/tree/master/Github_Comment_Enhancer // @homepageURL https://github.com/jerone/UserScripts/tree/master/Github_Comment_Enhancer // @version 1.5 // @grant none // @run-at document-end // @include https://github.com/*/*/issues/* // @include https://github.com/*/*/pull/* // @include https://github.com/*/*/commit/* // @include https://github.com/*/*/wiki/* // @include https://gist.github.com/* // @downloadURL none // ==/UserScript== /* global unsafeWindow */ (function() { String.format = function(string) { var args = Array.prototype.slice.call(arguments, 1, arguments.length); return string.replace(/{(\d+)}/g, function(match, number) { return typeof args[number] !== "undefined" ? args[number] : match; }); }; // 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, commentForm, next) { var selTxt = selText.trim(), isUrl = selTxt && /(?:https?:\/\/)|(?:www\.)/.test(selTxt), href = window.prompt("Link href:", isUrl ? selTxt : ""), text = window.prompt("Link text:", isUrl ? "" : selTxt); if (href) { next(String.format("[{0}]({1}){2}", text || href, href, (/\s+$/.test(selText) ? " " : ""))); } } }, "function-image": { exec: function(txt, selText, commentForm, next) { var selTxt = selText.trim(), isUrl = selTxt && /(?:https?:\/\/)|(?:www\.)/.test(selTxt), href = window.prompt("Image href:", isUrl ? selTxt : ""), text = window.prompt("Image text:", isUrl ? "" : selTxt); if (href) { next(String.format("![{0}]({1}){2}", text || href, href, (/\s+$/.test(selText) ? " " : ""))); } } }, "function-ul": { search: /(.+)([\n]?)/g, replace: "* $1$2", forceNewline: true }, "function-ol": { exec: function(txt, selText, commentForm, 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 += String.format("$0. $1\n", i + 1, lines[i]); } } } next(repText); } }, "function-checklist": { search: /(.+)([\n]?)/g, replace: "* [ ] $1$2", forceNewline: true }, "function-code": { exec: function(txt, selText, commentForm, 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" + "| Head | Head | Head |\n" + "| :--- | :--: | ---: |\n" + "| Cell | Cell | Cell |\n" + "| Cell | Cell | Cell |\n", forceNewline: true }, "function-clear": { exec: function(txt, selText, commentForm, next) { commentForm.value = ""; next(""); } }, "function-snippets-useragent": { exec: function(txt, selText, commentForm, next) { next("`" + navigator.userAgent + "`"); } }, "function-snippets-contributing": { exec: function(txt, selText, commentForm, next) { next("Please, always consider reviewing the [guidelines for contributing](../blob/master/CONTRIBUTING.md) to this repository."); } } }; var editorHTML = (function() { return '
' + '
' + ' ' + ' B' + ' ' + ' ' + ' i' + ' ' + ' ' + ' S' + ' ' + '
' + '
' + '
' + ' ' + ' h#' + ' ' + '
' + '
' + '
' + ' Choose header' + ' ' + '
' + ' ' + '
' + '
' + '
' + '
' + '
' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + '
' + '
' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + '
' + '
' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + '
' + '
' + '
' + ' ' + ' ' + ' ' + '
' + '
' + '
' + ' Snippets' + ' ' + '
' + '
' + '
' + ' ' + '
' + '
' + ' ' + '
' + '
' + '
' + '
' + '
' + '
' + ' ' + ' ' + ' ' + '
'; })(); // 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, commentForm, 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(); // Gist Github requires that the comment form change event be triggered to update the preview; unsafeWindow.$(commentForm).trigger("change"); 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; } } function isWiki() { return /\/wiki\//.test(location.href); } var functionButtonClick = function(e) { e.preventDefault(); executeAction(MarkDown[this.id], this.commentForm); return false; }; function addToolbar() { if (isWiki()) { // Override existing language with improved & missing functions and remove existing click events; unsafeWindow.$.GollumEditor.defineLanguage("markdown", MarkDown); unsafeWindow.$(".function-button:not(#function-help)").unbind("click"); // Remove existing click events when changing languages; document.getElementById("wiki_format").addEventListener("change", function() { unsafeWindow.$(".function-button:not(#function-help)").unbind("click"); Array.forEach(document.querySelectorAll(".comment-form-textarea .function-button"), function(button) { button.removeEventListener("click", functionButtonClick); }); }); } Array.forEach(document.querySelectorAll(".comment-form-textarea,.js-comment-field"), function(commentForm) { if (commentForm.classList.contains("GithubCommentEnhancer")) { return; } commentForm.classList.add("GithubCommentEnhancer"); var gollumEditor; if (isWiki()) { gollumEditor = document.getElementById("gollum-editor-function-bar"); var temp = document.createElement("div"); temp.innerHTML = editorHTML; temp.firstChild.appendChild(document.getElementById("function-help")); // restore the help button; gollumEditor.replaceChild(temp.querySelector("#gollum-editor-function-buttons"), document.getElementById("gollum-editor-function-buttons")); Array.forEach(temp.children, function(elm) { elm.style.position = "absolute"; elm.style.right = "30px"; elm.style.top = "0"; commentForm.parentNode.insertBefore(elm, commentForm); }); temp = null; } else { gollumEditor = document.createElement("div"); gollumEditor.innerHTML = editorHTML; gollumEditor.id = "gollum-editor-function-bar"; gollumEditor.style.border = "0 none"; gollumEditor.style.height = "26px"; gollumEditor.style.margin = "10px 0"; gollumEditor.style.paddingBottom = "10px"; gollumEditor.classList.add("active"); commentForm.parentNode.insertBefore(gollumEditor, commentForm); } Array.forEach(gollumEditor.parentNode.querySelectorAll(".function-button"), function(button) { if (button.classList.contains("minibutton")) { button.style.padding = "0px"; button.style.textAlign = "center"; button.style.width = "30px"; button.firstElementChild.style.marginRight = "0px"; } button.commentForm = commentForm; // remove event listener doesn't accept `bind`; button.addEventListener("click", functionButtonClick); }); }); } // init; addToolbar(); // on pjax; unsafeWindow.$(document).on("pjax:success", addToolbar); // on page update; unsafeWindow.$.pageUpdate(function() { window.setTimeout(function() { addToolbar(); }, 1); }); })();