// ==UserScript== // @name GitHub Dark Script // @version 2.5.9 // @description GitHub Dark in userscript form, with a settings panel // @license MIT // @author StylishThemes // @namespace https://github.com/StylishThemes // @include /^https?://((blog|gist|guides|help|raw|status|developer)\.)?github\.com/((?!generated_pages\/preview).)*$/ // @include https://*.githubusercontent.com/* // @include https://*graphql-explorer.githubapp.com/* // @run-at document-start // @inject-into content // @grant GM.addStyle // @grant GM_addStyle // @grant GM.getValue // @grant GM_getValue // @grant GM.setValue // @grant GM_setValue // @grant GM.info // @grant GM_info // @grant GM.xmlHttpRequest // @grant GM_xmlhttpRequest // @grant GM.registerMenuCommand // @grant GM_registerMenuCommand // @connect githubusercontent.com // @connect raw.githubusercontent.com // @require https://greasemonkey.github.io/gm4-polyfill/gm4-polyfill.js // @require https://greasyfork.org/scripts/15563-jscolor/code/jscolor.js?version=106439 // @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 // @downloadURL https://update.greasyfork.icu/scripts/15562/GitHub%20Dark%20Script.user.js // @updateURL https://update.greasyfork.icu/scripts/15562/GitHub%20Dark%20Script.meta.js // ==/UserScript== /* global jscolor */ (async () => { "use strict"; const version = GM.info.script.version, // delay until package.json allowed to load delay = 8.64e7, // 24 hours in milliseconds // Keyboard shortcut to open ghd panel (only a two key combo coded) keyboardOpen = "g+0", keyboardToggle = "g+-", // keyboard shortcut delay from first to second letter keyboardDelay = 1000, // base urls to fetch style and package.json root = "https://raw.githubusercontent.com/StylishThemes/GitHub-Dark/master/", defaults = { attach: "scroll", color: "#4183C4", enable: true, font: "Menlo", image: "url('data:image/png;base64,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')", tab: 0, // 0 is disabled theme: "Twilight", // GitHub themeCm: "Twilight", // CodeMirror themeJp: "Twilight", // Jupyter type: "tiled", wrap: false, // toggle buttons enableCodeWrap: true, enableMonospace: true, // diff toggle + accordion mode modeDiffToggle: "1", // internal variables date: 0, version: 0, rawCss: "", cssgithub: "", csscodemirror: "", cssjupyter: "", processedCss: "" }, // extract style & theme name regex = /\/\*! [^*]+ \*\//, themesXref = { github: { placeholder: "syntax-theme", folder: "themes/github/" }, codemirror: { placeholder: "syntax-codemirror", folder: "themes/codemirror/" }, jupyter: { placeholder: "syntax-jupyter", folder: "themes/jupyter/" } }, // available theme names themes = { github: { "Ambiance": "ambiance", "Chaos": "chaos", "Clouds Midnight": "clouds-midnight", "Cobalt": "cobalt", "GitHub Dark": "github-dark", "Idle Fingers": "idle-fingers", "Kr Theme": "kr-theme", "Merbivore": "merbivore", "Merbivore Soft": "merbivore-soft", "Mono Industrial": "mono-industrial", "Mono Industrial Clear": "mono-industrial-clear", "Monokai": "monokai", "Monokai Spacegray Eighties": "monokai-spacegray-eighties", "Obsidian": "obsidian", "One Dark": "one-dark", "Pastel on Dark": "pastel-on-dark", "Railscasts": "railscasts", "Solarized Dark": "solarized-dark", "Terminal": "terminal", "Tomorrow Night": "tomorrow-night", "Tomorrow Night Blue": "tomorrow-night-blue", "Tomorrow Night Bright": "tomorrow-night-bright", "Tomorrow Night Eigthies": "tomorrow-night-eighties", "Twilight": "twilight", "Vibrant Ink": "vibrant-ink" }, // CodeMirror themes codemirror: { "Ambiance": "ambiance", "Base16 Ocean Dark": "base16-ocean-dark", "Cobalt": "cobalt", "Dracula": "dracula", "Material": "material", "Monokai": "monokai", "Monokai Spacegray Eighties": "monokai-spacegray-eighties", "One Dark": "one-dark", "Pastel on Dark": "pastel-on-dark", "Railscasts": "railscasts", "Solarized Dark": "solarized-dark", "Tomorrow Night Bright": "tomorrow-night-bright", "Tomorrow Night Eigthies": "tomorrow-night-eighties", "Twilight": "twilight", "Vibrant Ink": "vibrant-ink" }, // Jupyter (pygments) themes jupyter: { "Base16 Ocean Dark": "base16-ocean", "Dracula": "dracula", "GitHub Dark": "github-dark", "Idle Fingers": "idle-fingers", "Monokai": "monokai", "Monokai Spacegray Eighties": "monokai-spacegray-eighties", "Obsidian": "obsidian", "Pastel on Dark": "pastel-on-dark", "Railscasts": "railscasts", "Solarized Dark": "solarized-dark", "Tomorrow Night": "tomorrow-night", "Tomorrow Night Blue": "tomorrow-night-blue", "Tomorrow Night Bright": "tomorrow-night-bright", "Tomorrow Night Eigthies": "tomorrow-night-eighties", "Twilight": "twilight" } }, type = { tiled: ` background-repeat: repeat !important; background-size: auto !important; background-position: left top !important; `, fit: ` background-repeat: no-repeat !important; background-size: cover !important; background-position: center top !important; ` }, 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; ` }, // https://github.com/StylishThemes/GitHub-code-wrap/blob/master/github-code-wrap.css wrapCodeCss = ` /* GitHub: Enable wrapping of long code lines */ .blob-code-inner:not(.blob-code-hunk), .markdown-body pre > code, .markdown-body .highlight > pre { ${wrapCss.wrapped} } td.blob-code-inner { display: table-cell !important; } `, wrapIcon = ` `, monospaceIcon = ` `, fileIcon = ` `, $style = make({ el: "style", cl4ss: "ghd-style" }); let timer, picker; // jscolor picker let isInitialized = "pending"; // prevent mutationObserver from going nuts let isUpdating = false; // set when css code to test is pasted into the settings panel let testing = false; const debug = true; let data = {}; function updatePanel() { if (!isInitialized || !$("#ghd-settings-inner")) { return } // prevent multiple change events from processing isUpdating = true; let temp; const body = $("body"); const panel = $("#ghd-settings-inner"); $(".ghd-attach", panel).value = data.attach || defaults.attach; $(".ghd-font", panel).value = data.font || defaults.font; $(".ghd-image", panel).value = data.image || defaults.image; $(".ghd-tab", panel).value = data.tab || defaults.tab; $(".ghd-theme", panel).value = data.theme || defaults.theme; $(".ghd-themecm", panel).value = data.themeCm || defaults.themeCm; $(".ghd-themejp", panel).value = data.themeJp || defaults.themeJp; $(".ghd-type", panel).value = data.type || defaults.type; $(".ghd-enable", panel).checked = isBool("enable"); $(".ghd-wrap", panel).checked = isBool("wrap"); $(".ghd-codewrap-checkbox", panel).checked = isBool("enableCodeWrap"); $(".ghd-monospace-checkbox", panel).checked = isBool("enableMonospace"); const el = $(".ghd-diff-select", panel); temp = `${data.modeDiffToggle || defaults.modeDiffToggle}`; el.value = temp; toggleClass(el, "enabled", temp !== "0"); // update version tooltip $(".ghd-versions", panel).setAttribute("aria-label", getVersionTooltip()); temp = data.color || defaults.color; $(".ghd-color").value = temp; // update swatch color & color picker value $("#ghd-swatch").style.backgroundColor = temp; if (picker) { picker.fromString(temp); } $style.disabled = !data.enable; toggleClass(body, "ghd-disabled", !data.enable); toggleClass(body, "nowrap", !data.wrap); if (data.enableCodeWrap !== data.lastCW || data.enableMonospace !== data.lastMS || data.modeDiffToggle !== data.lastDT) { data.lastCW = data.enableCodeWrap; data.lastMS = data.enableMonospace; data.lastDT = data.modeDiffToggle; updateToggles(); } isUpdating = false; } async function getStoredValues(init) { data = await GM.getValue("data", defaults); try { data = JSON.parse(data); if (!Object.keys(data).length || ({}).toString.call(data) !== "[object Object]") { throw new Error(); } } catch (err) { // compat data = await GM.getValue("data", defaults); } if (debug) { if (init) { console.info("GitHub-Dark Script initializing!"); } console.info("Retrieved stored values", data); } } async function setStoredValues(reset) { data.processedCss = $style.textContent; await GM.setValue("data", JSON.stringify(reset ? defaults : data)); updatePanel(); if (debug) { console.info(`${reset ? "Resetting" : "Saving"} current values`, data); } } // modified from http://stackoverflow.com/a/5624139/145346 function hexToRgb(hex) { const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); return result ? [ parseInt(result[1], 16), parseInt(result[2], 16), parseInt(result[3], 16) ].join(", ") : ""; } // convert version "1.2.3" into "001002003" for easier comparison function convertVersion(val) { let index; const parts = val ? val.split(".") : ""; let str = ""; const len = parts.length; for (index = 0; index < len; index++) { str += (`000${parts[index]}`).slice(-3); } if (debug) { console.info(`Converted version "${val}" to "${str}" for easy comparison`); } return val ? str : val; } function checkVersion() { if (debug) { console.info("Fetching package.json"); } GM.xmlHttpRequest({ method: "GET", url: `${root}package.json`, onload: response => { const pkg = JSON.parse(response.responseText); // save last loaded date, so package.json is only loaded once a day data.date = new Date().getTime(); const ver = convertVersion(pkg.version); // if new available, load it & parse if (ver > data.version) { if (data.version !== 0 && debug) { console.info(`Updating from ${data.version} to ${ver}`); } data.version = ver; fetchAndApplyStyle(); } else { addSavedStyle(); } // save new date/version GM.setValue("data", JSON.stringify(data)); } }); } function fetchAndApplyStyle() { if (debug) { console.info(`Fetching ${root}github-dark.css`); } GM.xmlHttpRequest({ method: "GET", url: `${root}github-dark.css`, onload: response => { data.rawCss = response.responseText; processStyle(); } }); } // load syntax highlighting theme function fetchAndApplyTheme(name, group) { if (!data.enable) { if (debug) { console.info("Disabled: stop theme processing"); } return; } if (data[`last${group}`] === name && data[`css${group}`] !== "") { return applyTheme(name, group); } const themeUrl = `${root}${themesXref[group].folder}${themes[group][name]}.min.css`; if (debug) { console.info(`Fetching ${group} ${name} theme`, themeUrl); } GM.xmlHttpRequest({ method: "GET", url: themeUrl, onload: response => { let theme = response.responseText; if (response.status === 200 && theme) { data[`css${group}`] = theme; data[`last${group}`] = name; applyTheme(name, group); } else { theme = data[`css${group}`]; console.error(`Failed to load ${group} theme file: "${name}"`); console.info(`Falling back to previous ${group} theme of ${theme.substring(0, theme.indexOf("*/") + 2)}`); } } }); } async function applyTheme(_name, group) { let theme, css; if (debug) { theme = (data[`css${group}`] || "").match(regex); console.info(`Adding syntax ${group} theme "${theme}" to css`); } css = data.processedCss || ""; css = css.replace( `/*[[${themesXref[group].placeholder}]]*/`, data[`css${group}`] || "" ); applyStyle(css); await setStoredValues(); isUpdating = false; } function setTabSize() { return data.tab > 0 ? `pre, .highlight, .diff-table, .tab-size { tab-size: ${data.tab} !important; -moz-tab-size: ${data.tab} !important; }` : ""; } function processStyle() { const url = (data.image || "").startsWith("url") ? data.image : (data.image === "none" ? "none" : `url('${data.image}')`); if (!data.enable) { if (debug) { console.info("Disabled: stop processing"); } return; } if (debug) { console.info("Processing set styles"); } const processed = (data.rawCss || "") // remove moz-document wrapper .replace(/@-moz-document regexp\((.*)\) \{(\n|\r)+/, "") // replace background image; if no "url" at start, then use "none" .replace(/\/\*\[\[bg-choice\]\]\*\/ url\(.*\)/, url) // Add tiled or fit window size css .replace("/*[[bg-options]]*/", type[data.type || "tiled"]) // set scroll or fixed background image .replace("/*[[bg-attachment]]*/ fixed", data.attach || "scroll") // replace base-color .replace(/\/\*\[\[base-color\]\]\*\/ #\w{3,6}/g, data.color || "#4183C4") // replace base-color-rgb .replace(/\/\*\[\[base-color-rgb\]\]\*\//g, hexToRgb(data.color || "#4183c4")) // add font choice .replace("/*[[font-choice]]*/", data.font || "Menlo") // add tab size .replace(/\/\*\[\[tab-size\]\]\*\//g, setTabSize()) // code wrap css .replace("/*[[code-wrap]]*/", data.wrap ? wrapCodeCss : "") // remove default syntax .replace(/\s+\/\* grunt build - remove start[\s\S]+grunt build - remove end \*\/$/m, ""); data.processedCss = processed; fetchAndApplyTheme(data.theme, "github"); fetchAndApplyTheme(data.themeCm, "codemirror"); fetchAndApplyTheme(data.themeJp, "jupyter"); } function applyStyle(css) { if (debug) { console.info("Applying style", `"${(css || "").match(regex)}"`); } $style.textContent = css || ""; } function addSavedStyle() { if (debug) { console.info("Adding previously saved style"); } // apply already processed css to prevent FOUC $style.textContent = data.processedCss; } function updateStyle() { isUpdating = true; if (debug) { console.info("Updating user settings"); } const body = $("body"), panel = $("#ghd-settings-inner"); data.attach = $(".ghd-attach", panel).value; // get hex value directly data.color = picker.toHEXString(); data.enable = $(".ghd-enable", panel).checked; data.font = $(".ghd-font", panel).value; data.image = $(".ghd-image", panel).value; data.tab = $(".ghd-tab", panel).value; data.theme = $(".ghd-theme", panel).value; data.themeCm = $(".ghd-themecm", panel).value; data.themeJp = $(".ghd-themejp", panel).value; data.type = $(".ghd-type", panel).value; data.wrap = $(".ghd-wrap", panel).checked; data.enableCodeWrap = $(".ghd-codewrap-checkbox", panel).checked; data.enableMonospace = $(".ghd-monospace-checkbox", panel).checked; data.modeDiffToggle = $(".ghd-diff-select", panel).value; $style.disabled = !data.enable; toggleClass(body, "ghd-disabled", !data.enable); toggleClass(body, "nowrap", !data.wrap); if (testing) { processStyle(); testing = false; } else { fetchAndApplyStyle(); } isUpdating = false; } // user can force GitHub-dark update async function forceUpdate(css) { if (css) { // add raw css directly for style testing data.rawCss = css; processStyle(); } else { // clear saved date data.version = 0; data.cssgithub = ""; data.csscodemirror = ""; data.cssjupyter = ""; await GM.setValue("data", JSON.stringify(data)); closePanel("forced"); } } function getVersionTooltip() { let indx; const ver = []; // convert stored css version from "001014049" into "1.14.49" for tooltip const parts = String(data.version).match(/\d{3}/g); const len = parts && parts.length || 0; for (indx = 0; indx < len; indx++) { ver.push(parseInt(parts[indx])); } return `Script v${version}\nCSS ${(ver.length ? `v${ver.join(".")}` : "unknown")}`; } function buildOptions(group) { let options = ""; Object.keys(themes[group]).forEach(theme => { options += ``; }); return options; } async function buildSettings() { if (debug) { console.info("Adding settings panel & GitHub Dark link to profile dropdown"); } // Script-specific CSS GM.addStyle(` #ghd-menu:hover { cursor:pointer } #ghd-settings { position:fixed; z-index:65535; top:0; bottom:0; left:0; right:0; opacity:0; visibility:hidden; } #ghd-settings.in { opacity:1; visibility:visible; background:rgba(0,0,0,.5); } #ghd-settings-inner { position:fixed; left:50%; top:50%; transform:translate(-50%,-50%); width:25rem; box-shadow:0 .5rem 1rem #111; color:#c0c0c0 } #ghd-settings label { margin-left:.5rem; position:relative; top:-1px } #ghd-settings-close { height:1rem; width:1rem; fill:#666; float:right; cursor:pointer } #ghd-settings-close:hover { fill:#ccc } #ghd-settings .ghd-right { float:right; padding:5px; } #ghd-settings p { line-height:22px; } #ghd-settings .form-select, #ghd-settings input[type="text"] { height:30px; min-height:30px; } #ghd-swatch { width:25px; height:22px; display:inline-block; margin:3px 10px; border-radius:4px; } #ghd-settings input, #ghd-settings select { border:#555 1px solid; } #ghd-settings .ghd-attach, #ghd-settings .ghd-diff-select { padding-right:25px; } #ghd-settings input[type="checkbox"] { margin-top:3px; width: 16px !important; height: 16px !important; border-radius: 3px !important; } #ghd-settings .boxed-group-inner { padding:0; } #ghd-settings .ghd-footer { padding:10px; border-top:#555 solid 1px; } #ghd-settings .ghd-settings-wrapper { max-height:60vh; overflow-y:auto; padding:4px 10px; } #ghd-settings .ghd-tab { width:6em; } #ghd-settings .octicon { vertical-align:text-bottom !important; } #ghd-settings .ghd-paste-area { position:absolute; bottom:50px; top:37px; left:2px; right:2px; width:396px !important; height:-moz-calc(100% - 85px); resize:none !important; border-style:solid; z-index:0; } /* code wrap toggle: https://gist.github.com/silverwind/6c1701f56e62204cc42b 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 */ .ghd-wrap-table .blob-code-inner:not(.blob-code-hunk) { white-space:pre-wrap !important; word-break:break-all !important; } .ghd-unwrap-table .blob-code-inner:not(.blob-code-hunk) { white-space:pre !important; word-break:normal !important; } .ghd-wrap-toggle > *, .ghd-monospace > *, .ghd-file-toggle > * { pointer-events:none; vertical-align:middle !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); vertical-align:text-bottom; } /* wrap disabled (red) */ .ghd-code-wrapper:hover .ghd-wrap-toggle.unwrap svg, .ghd-code-wrapper:hover .ghd-wrap-toggle svg { fill:#8b0000; } /* wrap enabled (green) */ body:not(.nowrap) .ghd-code-wrapper:hover .ghd-wrap-toggle:not(.unwrap) svg, .ghd-code-wrapper:hover .ghd-wrap-toggle.wrapped svg { fill:#006400; } .markdown-body pre, .markdown-body .highlight, .ghd-code-wrapper { position:relative; } /* monospace font toggle */ .ghd-monospace-font { font-family:"${data.font}", Menlo, Inconsolata, "Droid Mono", monospace !important; font-size:1em !important; } /* file collapsed icon */ .Details--on .ghd-file-toggle svg { -webkit-transform:rotate(90deg); transform:rotate(90deg); } `); const opts = buildOptions("github"); const optscm = buildOptions("codemirror"); const optsjp = buildOptions("jupyter"); const ver = getVersionTooltip(); // circle-question-mark icon const icon = ` `; // Settings panel markup make({ el: "div", appendTo: "body", attr: {id: "ghd-settings"}, html: `