GitHub RTL Comment Blocks

A userscript that adds a button to insert RTL text blocks in comments

2017-04-14 يوللانغان نەشرى. ئەڭ يېڭى نەشرىنى كۆرۈش.

  1. // ==UserScript==
  2. // @name GitHub RTL Comment Blocks
  3. // @version 1.2.6
  4. // @description A userscript that adds a button to insert RTL text blocks in comments
  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. // @connect github.com
  13. // @require https://greatest.deepsurf.us/scripts/28721-mutations/code/mutations.js?version=188072
  14. // @require https://greatest.deepsurf.us/scripts/28239-rangy-inputs-mod-js/code/rangy-inputs-modjs.js?version=181769
  15. // @icon https://github.com/fluidicon.png
  16. // ==/UserScript==
  17. (() => {
  18. "use strict";
  19.  
  20. const icon = `
  21. <svg class="octicon" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 14 14">
  22. <path d="M14 3v8l-4-4m-7 7V6C1 6 0 5 0 3s1-3 3-3h7v2H9v12H7V2H5v12H3z"/>
  23. </svg>`,
  24.  
  25. // maybe using &#x2067; RTL text &#x2066; (isolates) is a better combo?
  26. openRTL = "&rlm;", // https://en.wikipedia.org/wiki/Right-to-left_mark
  27. closeRTL = "&lrm;", // https://en.wikipedia.org/wiki/Left-to-right_mark
  28.  
  29. regexOpen = /\u200f/ig,
  30. regexClose = /\u200e/ig,
  31. regexSplit = /(\u200f|\u200e)/ig;
  32.  
  33. GM_addStyle(`
  34. .ghu-rtl-css { direction:rtl; text-align:right; }
  35. /* delegated binding; ignore clicks on svg & path */
  36. .ghu-rtl > * { pointer-events:none; }
  37. /* override RTL on code blocks */
  38. .js-preview-body pre, .markdown-body pre,
  39. .js-preview-body code, .markdown-body code {
  40. direction:ltr;
  41. text-align:left;
  42. unicode-bidi:normal;
  43. }
  44. `);
  45.  
  46. // Add RTL button
  47. function addRtlButton() {
  48. let el, button,
  49. toolbars = $$(".toolbar-commenting"),
  50. indx = toolbars.length;
  51. if (indx) {
  52. button = document.createElement("button");
  53. button.type = "button";
  54. button.className = "ghu-rtl toolbar-item tooltipped tooltipped-n";
  55. button.setAttribute("aria-label", "RTL");
  56. button.setAttribute("tabindex", "-1");
  57. button.innerHTML = icon;
  58. while (indx--) {
  59. el = toolbars[indx];
  60. if (!$(".ghu-rtl", el)) {
  61. el.insertBefore(button.cloneNode(true), el.childNodes[0]);
  62. }
  63. }
  64. }
  65. checkRTL();
  66. }
  67.  
  68. function checkContent(el) {
  69. // check the contents, and wrap in either a span or div
  70. let indx, // useDiv,
  71. html = el.innerHTML,
  72. parts = html.split(regexSplit),
  73. len = parts.length;
  74. for (indx = 0; indx < len; indx++) {
  75. if (regexOpen.test(parts[indx])) {
  76. // check if the content contains HTML
  77. // useDiv = regexTestHTML.test(parts[indx + 1]);
  78. // parts[indx] = (useDiv ? "<div" : "<span") + " class='ghu-rtl-css'>";
  79. parts[indx] = "<div class='ghu-rtl-css'>";
  80. } else if (regexClose.test(parts[indx])) {
  81. // parts[indx] = useDiv ? "</div>" : "</span>";
  82. parts[indx] = "</div>";
  83. }
  84. }
  85. el.innerHTML = parts.join("");
  86. // remove empty paragraph wrappers (may have previously contained the mark)
  87. return el.innerHTML.replace(/<p><\/p>/g, "");
  88. }
  89.  
  90. function checkRTL() {
  91. let clone,
  92. indx = 0,
  93. div = document.createElement("div"),
  94. containers = $$(".js-preview-body, .markdown-body"),
  95. len = containers.length,
  96. // main loop
  97. loop = () => {
  98. let el, tmp,
  99. max = 0;
  100. while (max < 10 && indx < len) {
  101. if (indx > len) {
  102. return;
  103. }
  104. el = containers[indx];
  105. tmp = el.innerHTML;
  106. if (regexOpen.test(tmp) || regexClose.test(tmp)) {
  107. clone = div.cloneNode();
  108. clone.innerHTML = tmp;
  109. // now we can replace all instances
  110. el.innerHTML = checkContent(clone);
  111. max++;
  112. }
  113. indx++;
  114. }
  115. if (indx < len) {
  116. setTimeout(() => {
  117. loop();
  118. }, 200);
  119. }
  120. };
  121. loop();
  122. }
  123.  
  124. function addBindings() {
  125. window.rangyInput.init();
  126. $("body").addEventListener("click", event => {
  127. let textarea,
  128. target = event.target;
  129. if (target && target.classList.contains("ghu-rtl")) {
  130. textarea = closest(".previewable-comment-form", target);
  131. textarea = $(".comment-form-textarea", textarea);
  132. textarea.focus();
  133. // add extra white space around the tags
  134. window.rangyInput.surroundSelectedText(
  135. textarea,
  136. " " + openRTL + " ",
  137. " " + closeRTL + " "
  138. );
  139. return false;
  140. }
  141. });
  142. }
  143.  
  144. function $(selector, el) {
  145. return (el || document).querySelector(selector);
  146. }
  147.  
  148. function $$(selector, el) {
  149. return Array.from((el || document).querySelectorAll(selector));
  150. }
  151.  
  152. function closest(selector, el) {
  153. while (el && el.nodeType === 1) {
  154. if (el.matches(selector)) {
  155. return el;
  156. }
  157. el = el.parentNode;
  158. }
  159. return null;
  160. }
  161.  
  162. document.addEventListener("ghmo:container", addRtlButton);
  163. document.addEventListener("ghmo:preview", checkRTL);
  164. addBindings();
  165. addRtlButton();
  166.  
  167. })();