🏠 Home 

GitHub Toggle Code Wrap

A userscript that adds a code wrap toggle button


Install this script?
// ==UserScript==
// @name        GitHub Toggle Code Wrap
// @version     1.1.11
// @description A userscript that adds a code wrap toggle button
// @license     MIT
// @author      StylishThemes
// @namespace   https://github.com/StylishThemes
// @include     https://github.com/*
// @include     https://gist.github.com/*
// @run-at      document-idle
// @grant       GM_registerMenuCommand
// @grant       GM_getValue
// @grant       GM_setValue
// @grant       GM_addStyle
// @require     https://greasyfork.org/scripts/28721-mutations/code/mutations.js?version=634242
// @icon        https://avatars3.githubusercontent.com/u/6145677?v=3&s=200
// @homepageURL https://github.com/StylishThemes/GitHub-Dark-Script
// ==/UserScript==
(() => {
"use strict";
// This code is also part of the GitHub-Dark Script
// (https://github.com/StylishThemes/GitHub-Dark-Script)
// Extracted out into a separate userscript in case users only want
// to add this functionality
// set by GM popup menu
let globalWrap = GM_getValue("github-global-code-wrap", true),
busy = false;
const wrapIcon = `
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 768 768">
<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"/>
</svg>`,
// inline code wrap css
wrapCss = {
"wrapped": "white-space: pre-wrap !important; word-break: break-all !important; overflow-wrap: break-word !important; display: block !important;",
"unwrap": "white-space: pre !important; word-break: normal !important; display: block !important;"
};
function findWrap(event) {
const target = event.target;
if (target.classList.contains("ghd-wrap-toggle")) {
toggleClasses(target);
}
}
function findSibling(node, selector) {
node = node.parentNode.firstElementChild;
while ((node = node.nextElementSibling)) {
if (node.matches(selector)) {
return node;
}
}
return null;
}
function toggleClasses(button) {
let css;
const target = findSibling(button, "code, pre, .highlight, .diff-table");
if (!target) {
console.error("Code wrap icon associated code not found", button);
return;
}
// code with line numbers
if (target.nodeName === "TABLE") {
if (!target.className.includes("wrap-table")) {
css = !globalWrap;
} else {
css = target.classList.contains("ghd-unwrap-table");
}
target.classList.toggle("ghd-wrap-table", css);
target.classList.toggle("ghd-unwrap-table", !css);
button.classList.toggle("wrapped", css);
button.classList.toggle("unwrap", !css);
} else {
css = target.getAttribute("style") || "";
if (css === "") {
css = wrapCss[globalWrap ? "unwrap" : "wrapped"];
} else {
css = wrapCss[css === wrapCss.wrapped ? "unwrap" : "wrapped"];
}
target.setAttribute("style", css);
button.classList.toggle("wrapped", css === wrapCss.wrapped);
button.classList.toggle("unwrap", css === wrapCss.wrapped);
}
}
function addCodeWrapButton(button, target) {
target.insertBefore(button.cloneNode(true), target.childNodes[0]);
target.classList.add("ghd-code-wrapper");
}
function moveMenu(codeWrap) {
const menu = $("details", codeWrap);
if (menu) {
menu.classList.add("ghd-menu");
codeWrap.parentNode.appendChild(menu);
}
}
// Add code wrap toggle
function buildCodeWrap() {
if (busy) {
return;
}
busy = true;
// add wrap code buttons
let wrapper = $$(".blob-wrapper"),
indx = wrapper ? wrapper.length : 0;
const button = document.createElement("button");
button.className = `ghd-wrap-toggle tooltipped tooltipped-sw btn btn-sm${
globalWrap ? "" : " unwrap"}`;
button.setAttribute("aria-label", "Toggle code wrap");
button.innerHTML = wrapIcon;
// Code in table with line numbers
while (indx--) {
if (!$(".ghd-wrap-toggle", wrapper[indx])) {
addCodeWrapButton(button, wrapper[indx]);
moveMenu(wrapper[indx]); // Fixes #66
}
}
// Code in markdown comments & wiki pages
wrapper = $$(`
.markdown-body pre:not(.ghd-code-wrapper),
.markdown-format pre:not(.ghd-code-wrapper)`
);
indx = wrapper ? wrapper.length : 0;
while (indx--) {
const pre = wrapper[indx];
const code = $("code", pre);
const wrap = pre.parentNode;
if (code) {
addCodeWrapButton(button, pre);
} else if (wrap.classList.contains("highlight")) {
addCodeWrapButton(button, wrap);
}
}
busy = false;
}
function init() {
document.addEventListener("click", findWrap);
$("body").classList.toggle("nowrap", !globalWrap);
buildCodeWrap();
}
function $(str, el) {
return (el || document).querySelector(str);
}
function $$(str, el) {
return [...(el || document).querySelectorAll(str)];
}
// don't initialize if GitHub Dark Script is active
if (!$("#ghd-menu")) {
GM_addStyle(`
/* icons next to a pre */
.ghd-wrap-toggle {
padding: 3px 5px;
position: absolute;
right: 3px;
top: 3px;
-moz-user-select: none;
-webkit-user-select: none;
cursor: pointer;
z-index: 20;
}
.ghd-code-wrapper:not(:hover) .ghd-wrap-toggle {
border-color: transparent !important;
background: transparent !important;
}
.ghd-menu {
margin-top: 45px;
}
/* file & diff code tables */
body .ghd-wrap-table td.blob-code-inner:not(.blob-code-hunk) {
white-space: pre-wrap !important;
word-break: break-all !important;
}
body .ghd-unwrap-table td.blob-code-inner:not(.blob-code-hunk) {
white-space: pre !important;
word-break: normal !important;
}
/* icons for non-syntax highlighted code blocks;
* see https://github.com/gjtorikian/html-proofer/blob/master/README.md
*/
.markdown-body:not(.comment-body) .ghd-wrap-toggle:not(:first-child) {
right: 3.4em;
}
.ghd-wrap-toggle svg {
height: 14px;
width: 14px;
fill: rgba(110, 110, 110, .4);
pointer-events: none;
vertical-align: text-bottom;
}
.ghd-code-wrapper:hover .ghd-wrap-toggle.unwrap svg,
.ghd-code-wrapper:hover .ghd-wrap-toggle svg {
fill: #8b0000; /* wrap disabled (red) */
}
body:not(.nowrap) .ghd-code-wrapper:hover .ghd-wrap-toggle:not(.unwrap) svg,
.ghd-code-wrapper:hover .ghd-wrap-toggle.wrapped svg {
fill: #006400; /* wrap enabled (green) */
}
.blob-wrapper, .markdown-body pre, .markdown-body .highlight,
.ghd-code-wrapper {
position: relative;
}
/* global code wrap */
body:not(.nowrap) .blob-code-inner:not(.blob-code-hunk),
body:not(.nowrap) .markdown-body pre > code,
body:not(.nowrap) .markdown-body .highlight > pre {
white-space: pre-wrap !important;
word-break: break-all !important;
overflow-wrap: break-word !important;
display: block !important;
}
td.blob-code-inner {
display: table-cell !important;
}
`);
document.addEventListener("ghmo:container", buildCodeWrap);
document.addEventListener("ghmo:preview", buildCodeWrap);
// Add GM options
GM_registerMenuCommand("Set Global Code Wrap Option", () => {
const body = $("body");
const val = prompt("Global Code Wrap (true/false):", `${globalWrap}`);
globalWrap = val.startsWith("t");
GM_setValue("github-global-code-wrap", globalWrap);
body.classList.toggle("nowrap", !globalWrap);
});
init();
}
})();