GitHub Indent Comment Blocks

A userscript that allows you to indent & outdent blocks in the comment editor

当前为 2017-04-14 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name GitHub Indent Comment Blocks
  3. // @version 1.0.3
  4. // @description A userscript that allows you to indent & outdent blocks in the comment editor
  5. // @license https://creativecommons.org/licenses/by-sa/4.0/
  6. // @author Rob Garrison
  7. // @namespace https://github.com/Mottie
  8. // @include https://github.com/*
  9. // @include https://gist.github.com/*
  10. // @run-at document-idle
  11. // @grant GM_addStyle
  12. // @grant GM_getValue
  13. // @grant GM_setValue
  14. // @grant GM_registerMenuCommand
  15. // @connect github.com
  16. // @require https://greasyfork.org/scripts/28721-mutations/code/mutations.js?version=188072
  17. // @require https://greasyfork.org/scripts/28239-rangy-inputs-mod-js/code/rangy-inputs-modjs.js?version=181769
  18. // @icon https://github.com/fluidicon.png
  19. // ==/UserScript==
  20. (() => {
  21. "use strict";
  22.  
  23. let spaceSize = GM_getValue("space-size", 2);
  24.  
  25. const icons = {
  26. indent: `
  27. <svg class="octicon" xmlns="http://www.w3.org/2000/svg" width="12" height="16" viewBox="0 0 12 16">
  28. <path d="M12 13c0 .6 0 1-.9 1H.9c-.9 0-.9-.4-.9-1s0-1 .9-1h10.2c.88 0 .88.4.88 1zM.92 4h10.2C12 4 12 3.6 12 3s0-1-.9-1H.92c-.9 0-.9.4-.9 1s0 1 .9 1zM11.5 7h-5C6 7 6 7.4 6 8s0 1 .5 1h5c.5 0 .5-.4.5-1s0-1-.5-1zm-7 1L0 5v6z"/>
  29. </svg>`,
  30. outdent: `
  31. <svg class="octicon" xmlns="http://www.w3.org/2000/svg" width="12" height="16" viewBox="0 0 12 16">
  32. <path d="M12 13c0 .6 0 1-.9 1H.9c-.9 0-.9-.4-.9-1s0-1 .9-1h10.2c.88 0 .88.4.88 1zM.92 4h10.2C12 4 12 3.6 12 3s0-1-.9-1H.92c-.9 0-.9.4-.9 1s0 1 .9 1zm10.7 3H6.4c-.46 0-.4.4-.4 1s-.06 1 .4 1h5.2c.47 0 .4-.4.4-1s.07-1-.4-1zM0 8l4.5-3v6z"/>
  33. </svg>`
  34. };
  35.  
  36. GM_addStyle(".ghio-in-outdent * { pointer-events:none; }");
  37.  
  38. // Add indent & outdent buttons
  39. function addButtons() {
  40. createButton("Outdent");
  41. createButton("Indent");
  42. }
  43.  
  44. function createButton(name) {
  45. const toolbars = $$(".toolbar-commenting"),
  46. nam = name.toLowerCase(),
  47. button = document.createElement("button");
  48. let el,
  49. indx = toolbars.length;
  50. if (indx) {
  51. button.type = "button";
  52. button.className = `ghio-${nam.toLowerCase()} ghio-in-outdent toolbar-item tooltipped tooltipped-n`;
  53. button.setAttribute("aria-label", `${name} Selected Text`);
  54. button.setAttribute("tabindex", "-1");
  55. button.innerHTML = icons[nam.toLowerCase()];
  56. while (indx--) {
  57. el = toolbars[indx];
  58. if (!$(`.ghio-${nam.toLowerCase()}`, el)) {
  59. el.insertBefore(button.cloneNode(true), el.childNodes[0]);
  60. }
  61. }
  62. }
  63. }
  64.  
  65. function indent(text) {
  66. let result = [],
  67. block = new Array(parseInt(spaceSize, 10) + 1).join(" ");
  68. (text || "").split(/\r*\n/).forEach(line => {
  69. result.push(block + line);
  70. });
  71. return result.join("\n");
  72. }
  73.  
  74. function outdent(text) {
  75. let regex = new RegExp(`^(\x20{1,${spaceSize}}|\xA0{1,${spaceSize}}|\x09)`),
  76. result = [];
  77. (text || "").split(/\r*\n/).forEach(line => {
  78. result.push(line.replace(regex, ""));
  79. });
  80. return result.join("\n");
  81. }
  82.  
  83. function addBindings() {
  84. window.rangyInput.init();
  85. saveTabSize();
  86. $("body").addEventListener("click", event => {
  87. let textarea,
  88. target = event.target;
  89. if (target && target.classList.contains("ghio-in-outdent")) {
  90. textarea = closest(".previewable-comment-form", target);
  91. textarea = $(".comment-form-textarea", textarea);
  92. textarea.focus();
  93. setTimeout(() => {
  94. window.rangyInput.indentSelectedText(
  95. textarea,
  96. target.classList.contains("ghio-indent") ? indent : outdent
  97. );
  98. }, 100);
  99. return false;
  100. }
  101. });
  102. // Add Tab & Shift + Tab
  103. $("body").addEventListener("keydown", event => {
  104. if (event.key === "Tab") {
  105. let target = event.target;
  106. if (target && target.classList.contains("comment-form-textarea")) {
  107. event.preventDefault();
  108. target.focus();
  109. setTimeout(() => {
  110. window.rangyInput.indentSelectedText(
  111. target,
  112. // shift + tab = outdent
  113. event.getModifierState("Shift") ? outdent : indent
  114. );
  115. }, 100);
  116. }
  117. }
  118. });
  119. }
  120.  
  121. function saveTabSize() {
  122. let $el = $(".gh-indent-size");
  123. if (!$el) {
  124. $el = document.createElement("style");
  125. $el.setAttribute("rel", "stylesheet");
  126. $el.className = "gh-indent-size";
  127. document.querySelector("head").appendChild($el);
  128. }
  129. $el.innerHTML = `.comment-form-textarea { tab-size:${spaceSize}; }`;
  130. }
  131.  
  132. function $(selector, el) {
  133. return (el || document).querySelector(selector);
  134. }
  135.  
  136. function $$(selector, el) {
  137. return Array.from((el || document).querySelectorAll(selector));
  138. }
  139.  
  140. function closest(selector, el) {
  141. while (el && el.nodeType === 1) {
  142. if (el.matches(selector)) {
  143. return el;
  144. }
  145. el = el.parentNode;
  146. }
  147. return null;
  148. }
  149.  
  150. // Add GM options
  151. GM_registerMenuCommand(
  152. "Indent or outdent size",
  153. () => {
  154. const spaces = GM_getValue("indentOutdentSize", spaceSize);
  155. let val = prompt("Enter number of spaces to indent or outdent:", spaces);
  156. if (val !== null && typeof val === "string") {
  157. spaceSize = val;
  158. GM_setValue("space-size", val);
  159. saveTabSize();
  160. }
  161. }
  162. );
  163.  
  164. document.addEventListener("ghmo:container", addButtons);
  165. addBindings();
  166. addButtons();
  167. })();