Greasy Fork is available in English.

GitHub Toggle Code Wrap

A userscript that adds a code wrap toggle button

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

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

  1. // ==UserScript==
  2. // @name GitHub Toggle Code Wrap
  3. // @version 1.1.6
  4. // @description A userscript that adds a code wrap toggle button
  5. // @license MIT
  6. // @author StylishThemes
  7. // @namespace https://github.com/StylishThemes
  8. // @include https://github.com/*
  9. // @include https://gist.github.com/*
  10. // @run-at document-idle
  11. // @grant GM_registerMenuCommand
  12. // @grant GM_getValue
  13. // @grant GM_setValue
  14. // @grant GM_addStyle
  15. // @require https://greatest.deepsurf.us/scripts/28721-mutations/code/mutations.js?version=189706
  16. // @icon https://avatars3.githubusercontent.com/u/6145677?v=3&s=200
  17. // ==/UserScript==
  18. /* jshint esnext:true, unused:true */
  19. (() => {
  20. "use strict";
  21. /*
  22. This code is also part of the GitHub-Dark Script
  23. (https://github.com/StylishThemes/GitHub-Dark-Script)
  24. Extracted out into a separate userscript in case users only want
  25. to add this functionality
  26. */
  27. // set by GM popup menu
  28. let globalWrap = GM_getValue("github-global-code-wrap", true),
  29. busy = false;
  30.  
  31. const wrapIcon = `
  32. <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 768 768">
  33. <path d="M544.5 352.5q52.5 0 90 37.5t37.5 90-37.5 90-90 37.5H480V672l-96-96 96-96v64.5h72q25.5 0 45-19.5t19.5-45-19.5-45-45-19.5H127.5v-63h417zm96-192v63h-513v-63h513zm-513 447v-63h192v63h-192z"/>
  34. </svg>`,
  35.  
  36. // inline code wrap css
  37. wrapCss = {
  38. "wrapped": "white-space: pre-wrap !important; word-break: break-all !important; overflow-wrap: break-word !important; display: block !important;",
  39. "unwrap": "white-space: pre !important; word-break: normal !important; display: block !important;"
  40. };
  41.  
  42. function findWrap(event) {
  43. const target = event.target;
  44. if (target.classList.contains("ghd-wrap-toggle")) {
  45. toggleClasses(target);
  46. }
  47. }
  48.  
  49. function findSibling(node, selector) {
  50. node = node.parentNode.firstElementChild;
  51. while ((node = node.nextElementSibling)) {
  52. if (node.matches(selector)) {
  53. return node;
  54. }
  55. }
  56. return null;
  57. }
  58.  
  59. function toggleClasses(button) {
  60. let css,
  61. target = findSibling(button, "code, pre, .highlight, .diff-table");
  62. if (!target) {
  63. console.error("Code wrap icon associated code not found", button);
  64. return;
  65. }
  66. // code with line numbers
  67. if (target.nodeName === "TABLE") {
  68. if (target.className.indexOf("wrap-table") < 0) {
  69. css = !globalWrap;
  70. } else {
  71. css = target.classList.contains("ghd-unwrap-table");
  72. }
  73. target.classList.toggle("ghd-wrap-table", css);
  74. target.classList.toggle("ghd-unwrap-table", !css);
  75. button.classList.toggle("wrapped", css);
  76. button.classList.toggle("unwrap", !css);
  77. } else {
  78. css = target.getAttribute("style") || "";
  79. if (css === "") {
  80. css = wrapCss[globalWrap ? "unwrap" : "wrapped"];
  81. } else {
  82. css = wrapCss[css === wrapCss.wrapped ? "unwrap" : "wrapped"];
  83. }
  84. target.setAttribute("style", css);
  85. button.classList.toggle("wrapped", css === wrapCss.wrapped);
  86. button.classList.toggle("unwrap", css === wrapCss.wrapped);
  87. }
  88. }
  89.  
  90. function addCodeWrapButton(button, target) {
  91. target.insertBefore(button.cloneNode(true), target.childNodes[0]);
  92. target.classList.add("ghd-code-wrapper");
  93. }
  94.  
  95. // Add code wrap toggle
  96. function buildCodeWrap() {
  97. if (busy) {
  98. return;
  99. }
  100. busy = true;
  101.  
  102. // add wrap code buttons
  103. let wrapper = $$(".blob-wrapper"),
  104. indx = wrapper ? wrapper.length : 0;
  105. const button = document.createElement("button");
  106. button.className = "ghd-wrap-toggle tooltipped tooltipped-sw btn btn-sm" +
  107. (globalWrap ? "" : " unwrap");
  108. button.setAttribute("aria-label", "Toggle code wrap");
  109. button.innerHTML = wrapIcon;
  110.  
  111. // Code in table with line numbers
  112. while (indx--) {
  113. if (!$(".ghd-wrap-toggle", wrapper[indx])) {
  114. addCodeWrapButton(button, wrapper[indx]);
  115. }
  116. }
  117.  
  118. // Code in markdown comments & wiki pages
  119. wrapper = $$(`
  120. .markdown-body pre:not(.ghd-code-wrapper),
  121. .markdown-format pre:not(.ghd-code-wrapper)`
  122. );
  123. indx = wrapper ? wrapper.length : 0;
  124. while (indx--) {
  125. const pre = wrapper[indx];
  126. const code = $("code", pre);
  127. const wrap = pre.parentNode;
  128. if (code) {
  129. addCodeWrapButton(button, pre);
  130. } else if (wrap.classList.contains("highlight")) {
  131. addCodeWrapButton(button, wrap);
  132. }
  133. }
  134. busy = false;
  135. }
  136.  
  137. function init() {
  138. document.addEventListener("click", findWrap);
  139. $("body").classList.toggle("nowrap", !globalWrap);
  140. buildCodeWrap();
  141. }
  142.  
  143. function $(str, el) {
  144. return (el || document).querySelector(str);
  145. }
  146.  
  147. function $$(str, el) {
  148. return [...(el || document).querySelectorAll(str)];
  149. }
  150.  
  151. // don't initialize if GitHub Dark Script is active
  152. if (!$("#ghd-menu")) {
  153. GM_addStyle(`
  154. /* icons next to a pre */
  155. .ghd-wrap-toggle {
  156. padding: 3px 5px;
  157. position: absolute;
  158. right: 3px;
  159. top: 3px;
  160. -moz-user-select: none;
  161. -webkit-user-select: none;
  162. cursor: pointer;
  163. z-index: 20;
  164. }
  165. .ghd-code-wrapper:not(:hover) .ghd-wrap-toggle {
  166. border-color: transparent !important;
  167. background: transparent !important;
  168. }
  169. /* file & diff code tables */
  170. body .ghd-wrap-table td.blob-code-inner {
  171. white-space: pre-wrap !important;
  172. word-break: break-all !important;
  173. }
  174. body .ghd-unwrap-table td.blob-code-inner {
  175. white-space: pre !important;
  176. word-break: normal !important;
  177. }
  178. /* icons for non-syntax highlighted code blocks;
  179. * see https://github.com/gjtorikian/html-proofer/blob/master/README.md
  180. */
  181. .markdown-body:not(.comment-body) .ghd-wrap-toggle:not(:first-child) {
  182. right: 3.4em;
  183. }
  184. .ghd-wrap-toggle svg {
  185. height: 14px;
  186. width: 14px;
  187. fill: rgba(110, 110, 110, .4);
  188. pointer-events: none;
  189. vertical-align: text-bottom;
  190. }
  191. .ghd-code-wrapper:hover .ghd-wrap-toggle.unwrap svg,
  192. .ghd-code-wrapper:hover .ghd-wrap-toggle svg {
  193. fill: #8b0000; /* wrap disabled (red) */
  194. }
  195. body:not(.nowrap) .ghd-code-wrapper:hover .ghd-wrap-toggle:not(.unwrap) svg,
  196. .ghd-code-wrapper:hover .ghd-wrap-toggle.wrapped svg {
  197. fill: #006400; /* wrap enabled (green) */
  198. }
  199. .blob-wrapper, .markdown-body pre, .markdown-body .highlight,
  200. .ghd-code-wrapper {
  201. position: relative;
  202. }
  203. /* global code wrap */
  204. body:not(.nowrap) .blob-code-inner,
  205. body:not(.nowrap) .markdown-body pre > code,
  206. body:not(.nowrap) .markdown-body .highlight > pre {
  207. white-space: pre-wrap !important;
  208. word-break: break-all !important;
  209. overflow-wrap: break-word !important;
  210. display: block !important;
  211. }
  212. td.blob-code-inner {
  213. display: table-cell !important;
  214. }
  215. `);
  216.  
  217. document.addEventListener("ghmo:container", buildCodeWrap);
  218. document.addEventListener("ghmo:preview", buildCodeWrap);
  219.  
  220. // Add GM options
  221. GM_registerMenuCommand("Set Global Code Wrap Option", () => {
  222. const body = $("body"),
  223. val = prompt("Global Code Wrap (true/false):", "" + globalWrap);
  224. globalWrap = /^t/.test(val);
  225. GM_setValue("github-global-code-wrap", globalWrap);
  226. body.classList.toggle("nowrap", !globalWrap);
  227. });
  228.  
  229. init();
  230. }
  231.  
  232. })();