NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name GitHub Dark Script // @version 2.4.11 // @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 // ==/UserScript== /* global GM, jscolor */ /* jshint esnext:true, unused:true */ (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", "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", "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", "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, .markdown-body pre > code, .markdown-body .highlight > pre { ${wrapCss.wrapped} } td.blob-code-inner { display: table-cell !important; } `, wrapIcon = ` <svg xmlns="http://www.w3.org/2000/svg" width="768" height="768" 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> `, monospaceIcon = ` <svg class="octicon" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 32 32"> <path d="M5.91 7.31v8.41c0 .66.05 1.09.14 1.31.09.21.23.37.41.48.18.11.52.16 1.02.16v.41H2.41v-.41c.5 0 .86-.05 1.03-.14.16-.11.3-.27.41-.5.11-.23.16-.66.16-1.3V11.7c0-1.14-.04-1.87-.11-2.2-.04-.26-.13-.42-.24-.53-.11-.1-.27-.14-.46-.14-.21 0-.48.05-.77.18l-.18-.41 3.14-1.28h.52v-.01zm-.95-5.46c.32 0 .59.11.82.34.23.23.34.5.34.82 0 .32-.11.59-.34.82-.23.22-.51.34-.82.34-.32 0-.59-.11-.82-.34s-.36-.5-.36-.82c0-.32.11-.59.34-.82.24-.23.51-.34.84-.34zm19.636 19.006h-3.39v-1.64h5.39v9.8h3.43v1.66h-9.18v-1.66h3.77v-8.16h-.02zm.7-6.44c.21 0 .43.04.63.13.18.09.36.2.5.34s.25.3.34.5c.07.18.13.39.13.61 0 .22-.04.41-.13.61s-.19.36-.34.5-.3.25-.5.32c-.2.09-.39.13-.62.13-.21 0-.43-.04-.61-.12-.19-.07-.35-.19-.5-.34-.14-.14-.25-.3-.34-.5-.07-.2-.13-.39-.13-.61s.04-.43.13-.61c.07-.18.2-.36.34-.5s.31-.25.5-.34c.17-.09.39-.12.6-.12zM2 30L27.82 2H30L4.14 30H2z"/> </svg> `, fileIcon = ` <svg class="octicon" xmlns="http://www.w3.org/2000/svg" width="10" height="6.5" viewBox="0 0 10 6.5"> <path d="M0 1.5L1.5 0l3.5 3.7L8.5.0 10 1.5 5 6.5 0 1.5z"/> </svg> `, $style = make({ el: "style", cl4ss: "ghd-style" }); let timer, picker, // jscolor picker isInitialized = "pending", // prevent mutationObserver from going nuts isUpdating = false, // set when css code to test is pasted into the settings panel testing = false, // debug = true, data = {}; function updatePanel() { if (!isInitialized || !$("#ghd-settings-inner")) { return; } // prevent multiple change events from processing isUpdating = true; let temp, el, body = $("body"), 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"); 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.log("GitHub-Dark Script initializing!"); } console.log("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.log((reset ? "Resetting" : "Saving") + " current values", data); } } // modified from http://stackoverflow.com/a/5624139/145346 function hexToRgb(hex) { let 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, parts = val ? val.split(".") : "", str = "", len = parts.length; for (index = 0; index < len; index++) { str += ("000" + parts[index]).slice(-3); } if (debug) { console.log(`Converted version "${val}" to "${str}" for easy comparison`); } return val ? str : val; } function checkVersion() { if (debug) { console.log("Fetching package.json"); } GM.xmlHttpRequest({ method: "GET", url: root + "package.json", onload: response => { let pkg = JSON.parse(response.responseText); // save last loaded date, so package.json is only loaded once a day data.date = new Date().getTime(); let ver = convertVersion(pkg.version); // if new available, load it & parse if (ver > data.version) { if (data.version !== 0 && debug) { console.log(`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.log(`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.log("Disabled: stop theme processing"); } return; } if (data["last" + group] === name && data["css" + group] !== "") { return applyTheme(name, group); } let themeUrl = `${root}${themesXref[group].folder}${themes[group][name]}.min.css`; if (debug) { console.log(`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.log(`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.log(`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() { let url = /^url/.test(data.image || "") ? data.image : (data.image === "none" ? "none" : "url('" + data.image + "')"); if (!data.enable) { if (debug) { console.log("Disabled: stop processing"); } return; } if (debug) { console.log("Processing set styles"); } let 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 to end of file(.*(\n|\r))+\}$/m, ""); // see https://github.com/StylishThemes/GitHub-Dark/issues/275 if (/firefox/i.test(navigator.userAgent)) { processed = processed // line in github-dark.css = "select, input:not(.btn-link), textarea" .replace("select, input:not(.btn-link)", "input { color:#eee !important; } select") .replace(/input\[type="checkbox"\][\s\S]+?}/gm, ""); } data.processedCss = processed; fetchAndApplyTheme(data.theme, "github"); fetchAndApplyTheme(data.themeCm, "codemirror"); fetchAndApplyTheme(data.themeJp, "jupyter"); } function applyStyle(css) { if (debug) { console.log("Applying style", `"${(css || "").match(regex)}"`); } $style.textContent = css || ""; } function addSavedStyle() { if (debug) { console.log("Adding previously saved style"); } // apply already processed css to prevent FOUC $style.textContent = data.processedCss; } function updateStyle() { isUpdating = true; if (debug) { console.log("Updating user settings"); } let 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, ver = [], // convert stored css version from "001014049" into "1.14.49" for tooltip parts = String(data.version).match(/\d{3}/g), len = parts && parts.length || 0; for (indx = 0; indx < len; indx++) { ver.push(parseInt(parts[indx], 10)); } return `Script v${version}\nCSS ${(ver.length ? "v" + ver.join(".") : "unknown")}`; } function buildOptions(group) { let options = ""; Object.keys(themes[group]).forEach(theme => { options += `<option value="${theme}">${theme}</option>`; }); return options; } async function buildSettings() { if (debug) { console.log("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; } /* file & diff code tables */ .ghd-wrap-table .blob-code-inner { white-space:pre-wrap !important; word-break:break-all !important; } .ghd-unwrap-table .blob-code-inner { 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); } `); let icon, opts = buildOptions("github"), optscm = buildOptions("codemirror"), optsjp = buildOptions("jupyter"), ver = getVersionTooltip(); // circle-question-mark icon icon = ` <svg class="octicon" xmlns="http://www.w3.org/2000/svg" height="16" width="14" viewBox="0 0 16 14"> <path d="M6 10h2v2H6V10z m4-3.5c0 2.14-2 2.5-2 2.5H6c0-0.55 0.45-1 1-1h0.5c0.28 0 0.5-0.22 0.5-0.5v-1c0-0.28-0.22-0.5-0.5-0.5h-1c-0.28 0-0.5 0.22-0.5 0.5v0.5H4c0-1.5 1.5-3 3-3s3 1 3 2.5zM7 2.3c3.14 0 5.7 2.56 5.7 5.7S10.14 13.7 7 13.7 1.3 11.14 1.3 8s2.56-5.7 5.7-5.7m0-1.3C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7S10.86 1 7 1z" /> </svg> `; // Settings panel markup make({ el: "div", appendTo: "body", attr: { id: "ghd-settings" }, html: ` <div id="ghd-settings-inner" class="boxed-group"> <h3>GitHub-Dark Settings <a href="https://github.com/StylishThemes/GitHub-Dark-Script/wiki" class="tooltipped tooltipped-e" aria-label="See documentation">${icon}</a> <svg id="ghd-settings-close" xmlns="http://www.w3.org/2000/svg" width="768" height="768" viewBox="160 160 608 608"><path d="M686.2 286.8L507.7 465.3l178.5 178.5-45 45-178.5-178.5-178.5 178.5-45-45 178.5-178.5-178.5-178.5 45-45 178.5 178.5 178.5-178.5z"/></svg> </h3> <div class="boxed-group-inner"> <form> <div class="ghd-settings-wrapper"> <p class="ghd-checkbox"> <label>Enable GitHub-Dark<input class="ghd-enable ghd-right" type="checkbox"></label> </p> <p> <label>Color:</label> <input class="ghd-color ghd-right" type="text" value="#4183C4"> <span id="ghd-swatch" class="ghd-right"></span> </p> <h4>Background</h4> <p> <label>Image:</label> <input class="ghd-image ghd-right" type="text"> <a href="https://github.com/StylishThemes/GitHub-Dark/wiki/Image" class="tooltipped tooltipped-e" aria-label="Click to learn about GitHub's Content Security Policy and how to add a custom image">${icon}</a> </p> <p> <label>Image type:</label> <select class="ghd-type ghd-right form-select"> <option value="tiled">Tiled</option> <option value="fit">Fit window</option> </select> </p> <p> <label>Image attachment:</label> <select class="ghd-attach ghd-right form-select"> <option value="scroll">Scroll</option> <option value="fixed">Fixed</option> </select> </p> <h4>Code</h4> <p><label>GitHub Theme:</label> <select class="ghd-theme ghd-right form-select">${opts}</select></p> <p><label>CodeMirror Theme:</label> <select class="ghd-themecm ghd-right form-select">${optscm}</select></p> <p><label>Jupyter Theme:</label> <select class="ghd-themejp ghd-right form-select">${optsjp}</select></p> <p> <label>Font Name:</label> <input class="ghd-font ghd-right" type="text"> <a href="http://www.cssfontstack.com/" class="tooltipped tooltipped-e" aria-label="Add a system installed (monospaced) font name; this script will not load external fonts!">${icon}</a> </p> <p> <label>Tab Size:</label> <select class="ghd-tab ghd-right form-select"> <option value="0">disable</option> <option value="1">1</option> <option value="2">2</option> <option value="3">3</option> <option value="4">4</option> <option value="5">5</option> <option value="6">6</option> <option value="7">7</option> <option value="8">8</option> </select> </p> <p class="ghd-checkbox"> <label>Wrap<input class="ghd-wrap ghd-right" type="checkbox"></label> </p> <h4>Toggles</h4> <p class="ghd-checkbox"> <label>Code Wrap<input class="ghd-codewrap-checkbox ghd-right" type="checkbox"></label> </p> <p class="ghd-checkbox"> <label>Comment Monospace Font<input class="ghd-monospace-checkbox ghd-right" type="checkbox"></label> </p> <p class="ghd-range"> <label>Diff File Collapse</label> <select class="ghd-diff-select ghd-right form-select"> <option value="0">Disabled</option> <option value="1">Enabled</option> <option value="2">Accordion</option> </select> </p> </div> <div class="ghd-footer"> <div class="BtnGroup"> <a href="#" class="ghd-update btn btn-sm tooltipped tooltipped-n tooltipped-multiline" aria-label="Update style if the newest release is not loading; the page will reload!">Force Update Style</a> <a href="#" class="ghd-textarea-toggle btn btn-sm tooltipped tooltipped-n" aria-label="Paste CSS update"> <svg xmlns="http://www.w3.org/2000/svg" width="8" height="8" viewBox="0 0 16 16" fill="#eee"> <path d="M15 11 1 11 8 3z"/> </svg> </a> <div class="ghd-paste-area-content" aria-hidden="true" style="display:none"> <textarea class="ghd-paste-area" placeholder="Paste GitHub-Dark Style here!"></textarea> </div> </div> <a href="#" class="ghd-reset btn btn-sm btn-danger tooltipped tooltipped-n" aria-label="Reset to defaults; there is no undo!">Reset All Settings</a> <span class="ghd-versions ghd-right tooltipped tooltipped-n" aria-label="${ver}"> <svg class="ghd-info" xmlns="http://www.w3.org/2000/svg" width="16px" height="16px" viewBox="0 0 24 24"> <path fill="#444" d="M12,9c0.82,0,1.5-0.68,1.5-1.5S12.82,6,12,6s-1.5,0.68-1.5,1.5S11.18,9,12,9z M12,1.5 C6.211,1.5,1.5,6.211,1.5,12S6.211,22.5,12,22.5S22.5,17.789,22.5,12S17.789,1.5,12,1.5z M12,19.5c-4.148,0-7.5-3.352-7.5-7.5 S7.852,4.5,12,4.5s7.5,3.352,7.5,7.5S16.148,19.5,12,19.5z M13.5,12c0-0.75-0.75-1.5-1.5-1.5s-0.75,0-1.5,0S9,11.25,9,12h1.5 c0,0,0,3.75,0,4.5S11.25,18,12,18s0.75,0,1.5,0s1.5-0.75,1.5-1.5h-1.5C13.5,16.5,13.5,12.75,13.5,12z"/> </svg> </span> </div> </form> </div> </div> ` }); updateToggles(); } function addCodeWrapButton(button, target) { target.insertBefore(button.cloneNode(true), target.childNodes[0]); target.classList.add("ghd-code-wrapper"); } // Add code wrap toggle function buildCodeWrap() { // mutation events happen quick, so we still add an update flag isUpdating = true; let icon = make({ el: "button", cl4ss: "ghd-wrap-toggle tooltipped tooltipped-sw btn btn-sm" + (data.wrap ? "" : " unwrap"), attr: { "aria-label": "Toggle code wrap" }, html: wrapIcon }); $$(".blob-wrapper").forEach(el => { if (el && !$(".ghd-wrap-toggle", el)) { addCodeWrapButton(icon, el); } }); $$(` .markdown-body pre:not(.ghd-code-wrapper), .markdown-format pre:not(.ghd-code-wrapper)` ).forEach(pre => { const code = $("code", pre); const wrap = pre.parentNode; if (code) { addCodeWrapButton(icon, pre); } else if (wrap.classList.contains("highlight")) { addCodeWrapButton(icon, wrap); } }); isUpdating = false; } // Add monospace font toggle function addMonospaceToggle() { isUpdating = true; let button = make({ el: "button", cl4ss: "ghd-monospace toolbar-item tooltipped tooltipped-n", attr: { "type": "button", "aria-label": "Toggle monospaced font", "tabindex": "-1" }, html: monospaceIcon }); $$(".toolbar-commenting").forEach(el => { if (el && !$(".ghd-monospace", el)) { // prepend el.insertBefore(button.cloneNode(true), el.childNodes[0]); } }); isUpdating = false; } // Add file diffs toggle function addFileToggle() { isUpdating = true; let firstButton, button = make({ el: "button", cl4ss: "ghd-file-toggle btn btn-sm tooltipped tooltipped-n", attr: { "type": "button", "aria-label": "Click to Expand or Collapse file", "tabindex": "-1" }, html: fileIcon }); $$("#files .file-actions").forEach(el => { if (el && !$(".ghd-file-toggle", el)) { // hide GitHub toggle view button el.querySelector(".js-details-target").style.display = "none"; el.appendChild(button.cloneNode(true)); } }); firstButton = $(".ghd-file-toggle"); // accordion mode = start with all but first entry collapsed if (firstButton && data.modeDiffToggle === "2") { toggleFile({ target: firstButton }, true); } isUpdating = false; } // Add toggle buttons after page updates function updateToggles() { if (isUpdating) { return; } clearTimeout(timer); timer = setTimeout(() => { if (data.enableCodeWrap) { buildCodeWrap(); } else { removeAll(".ghd-wrap-toggle"); toggleClass($$(".Details--on"), "Details--on", false); } if (data.enableMonospace) { addMonospaceToggle(); } else { removeAll(".ghd-monospace"); toggleClass($$(".ghd-monospace-font"), "ghd-monospace-font", false); } if (data.modeDiffToggle !== "0") { addFileToggle(); } else { removeAll(".ghd-file-toggle"); toggleClass($$(".Details--on"), "Details--on", false); } }, 200); } function makeRow(vals, str) { return make({ el: "tr", cl4ss: "ghd-shortcut", html: `<td class="keys"><kbd>${vals[0]}</kbd> <kbd>${vals[1]}</kbd></td><td>${str}</td>` }); } // add keyboard shortcut to help menu (press "?") function buildShortcut() { let row, el = $(".keyboard-mappings tbody"); if (el && !$(".ghd-shortcut")) { row = makeRow(keyboardOpen.split("+"), "GitHub-Dark: open settings"); el.appendChild(row); row = makeRow(keyboardToggle.split("+"), "GitHub-Dark: toggle style"); el.appendChild(row); } } function findSibling(node, selector) { node = node.parentNode.firstElementChild; while ((node = node.nextElementSibling)) { if (node.matches(selector)) { return node; } } return null; } function toggleCodeWrap(el) { let css, overallWrap = data.wrap, target = findSibling(el, ".highlight, .diff-table, code, pre"); if (!target) { if (debug) { console.log("Code wrap icon associated code not found", el); } return; } // code with line numbers if (target.nodeName === "TABLE") { if (target.className.indexOf("wrap-table") < 0) { css = !overallWrap; } else { css = target.classList.contains("ghd-unwrap-table"); } toggleClass(target, "ghd-wrap-table", css); toggleClass(target, "ghd-unwrap-table", !css); toggleClass(el, "wrapped", css); toggleClass(el, "unwrap", !css); } else { css = target.getAttribute("style") || ""; if (css === "") { css = wrapCss[overallWrap ? "unwrap" : "wrapped"]; } else { css = wrapCss[css === wrapCss.wrapped ? "unwrap" : "wrapped"]; } target.setAttribute("style", css); toggleClass(el, "wrapped", css === wrapCss.wrapped); toggleClass(el, "unwrap", css === wrapCss.unwrap); } } function toggleMonospace(el) { let tmp = el.closest(".previewable-comment-form"), // single comment textarea = $(".comment-form-textarea", tmp); if (textarea) { toggleClass(textarea, "ghd-monospace-font"); textarea.focus(); tmp = textarea.classList.contains("ghd-monospace-font"); toggleClass(el, "ghd-icon-active", tmp); } } function toggleSibs(target, state) { // oddly, when a "Details--on" class is applied, the content is hidden const isCollapsed = state || target.classList.contains("Details--on"), toggles = $$(".file"); let el, indx = toggles.length; while (indx--) { el = toggles[indx]; if (el !== target) { el.classList.toggle("Details--on", isCollapsed); } } } function toggleFile(event, init) { isUpdating = true; let el = event.target.closest(".file"); if (el && data.modeDiffToggle === "2") { if (!init) { el.classList.toggle("Details--on"); } toggleSibs(el, true); } else if (el) { el.classList.toggle("Details--on"); // shift+click toggle all files! if (event.shiftKey) { toggleSibs(el); } } isUpdating = false; document.activeElement.blur(); // move current open panel to the top if (el && !el.classList.contains("Details--on")) { location.hash = el.id; } } function bindEvents() { let el, menu, lastKey, panel = $("#ghd-settings-inner"), swatch = $("#ghd-swatch", panel); // finish initialization $("#ghd-settings-inner .ghd-enable").checked = data.enable; toggleClass($("body"), "ghd-disabled", !data.enable); // Create our menu entry menu = make({ el: "a", cl4ss: "dropdown-item", html: "GitHub Dark Settings", attr: { id: "ghd-menu" } }); // .header changed to .Header 22 Aug 2017 el = $$(` .header .dropdown-item[href="/settings/profile"], .header .dropdown-item[data-ga-click*="go to profile"], .Header .dropdown-item[href="/settings/profile"], .Header .dropdown-item[data-ga-click*="go to profile"]` ); // get last found item - gists only have the "go to profile" item; // GitHub has both el = el[el.length - 1]; if (el) { // insert after el.parentNode.insertBefore(menu, el.nextSibling); on($("#ghd-menu"), "click", () => { openPanel(); }); } on(document, "keypress keydown", event => { clearTimeout(timer); // use "g+o" to open up ghd options panel let openKeys = keyboardOpen.split("+"), toggleKeys = keyboardToggle.split("+"), key = event.key.toLowerCase(), panelVisible = $("#ghd-settings").classList.contains("in"); // press escape to close the panel if (key === "escape" && panelVisible) { closePanel(); return; } // use event.which from keypress for shortcuts // prevent opening panel while typing "go" in comments if (event.type === "keydown" || /(input|textarea)/i.test(document.activeElement.nodeName)) { return; } if (lastKey === openKeys[0] && key === openKeys[1]) { if (panelVisible) { closePanel(); } else { openPanel(); } } if (lastKey === toggleKeys[0] && key === toggleKeys[1]) { toggleStyle(); } lastKey = key; timer = setTimeout(() => { lastKey = null; }, keyboardDelay); // add shortcut to help menu if (key === "?") { // table doesn't exist until user presses "?" setTimeout(() => { buildShortcut(); }, 300); } }); // add ghd-settings panel bindings on($$("#ghd-settings, #ghd-settings-close"), "click keyup", event => { // press escape to close settings if (event.type === "keyup" && event.which !== 27) { return; } closePanel(); }); on(panel, "click", event => { event.stopPropagation(); }); on($(".ghd-reset", panel), "click", async event => { event.preventDefault(); isUpdating = true; // pass true to reset values await setStoredValues(true); // add reset values back to data await getStoredValues(); // add reset values to panel updatePanel(); // update style updateStyle(); }); on($$("input[type='text']", panel), "focus", function() { // select all text when focused this.select(); }); on($$("select, input", panel), "change", () => { if (!isUpdating) { updateStyle(); } }); on($(".ghd-update", panel), "click", async event => { event.preventDefault(); await forceUpdate(); }); on($(".ghd-textarea-toggle", panel), "click", function(event) { event.preventDefault(); let hidden, el; this.classList.remove("selected"); el = next(this, ".ghd-paste-area-content"); if (el) { hidden = el.style.display === "none"; el.style.display = hidden ? "" : "none"; if (el.style.display !== "none") { el.classList.add("selected"); el = $("textarea", el); el.focus(); el.select(); } } return false; }); on($(".ghd-paste-area-content", panel), "paste", async event => { let toggle = $(".ghd-textarea-toggle", panel), textarea = event.target; setTimeout(async () => { textarea.parentNode.style.display = "none"; toggle.classList.remove("selected"); testing = true; await forceUpdate(textarea.value); }, 200); }); // Toggles on($("body"), "click", event => { let target = event.target; if (data.enableCodeWrap && target.classList.contains("ghd-wrap-toggle")) { // **** CODE WRAP TOGGLE **** event.stopPropagation(); toggleCodeWrap(target); } else if (data.enableMonospace && target.classList.contains("ghd-monospace")) { // **** MONOSPACE FONT TOGGLE **** event.stopPropagation(); toggleMonospace(target); return false; } else if (data.modeDiffToggle !== "0" && target.classList.contains("ghd-file-toggle")) { // **** CODE DIFF COLLAPSE TOGGLE **** event.stopPropagation(); toggleFile(event); } }); // style color picker picker = new jscolor($(".ghd-color", panel)); picker.zIndex = 65536; picker.hash = true; picker.backgroundColor = "#333"; picker.padding = 0; picker.borderWidth = 0; picker.borderColor = "#444"; picker.onFineChange = () => { swatch.style.backgroundColor = "#" + picker; }; } function openPanel() { // Don't show options panel on page that's missing main styles (e.g. help) if ($(".modal-backdrop")) { $(".modal-backdrop").click(); } updatePanel(); $("#ghd-settings").classList.add("in"); } function closePanel(flag) { $("#ghd-settings").classList.remove("in"); picker.hide(); if (flag === "forced") { // forced update; partial re-initialization init(); } else { // apply changes when the panel is closed updateStyle(); } } function toggleStyle() { let isEnabled = !data.enable; data.enable = isEnabled; $("#ghd-settings-inner .ghd-enable").checked = isEnabled; // add processedCss back into style (emptied when disabled) if (isEnabled) { // data.processedCss is empty when ghd is disabled on init if (!data.processedCss) { processStyle(); } else { addSavedStyle(); } } $style.disabled = !isEnabled; } async function init() { if (!document.head) { return; } document.head.parentNode.insertBefore($style, document.head.nextSibling); await getStoredValues(true); $style.disabled = !data.enable; data.lastgithub = data.themeGH; data.lastcodemirror = data.themeCM; data.lastjupyter = data.themeJP; data.lastCW = data.enableCodeWrap; data.lastMS = data.enableMonospace; data.lastDT = data.modeDiffToggle; if (!data.processedCss) { fetchAndApplyStyle(); } else { // only load package.json once a day, or after a forced update if ((new Date().getTime() > data.date + delay) || data.version === 0) { // get package.json from GitHub-Dark & compare versions // load new script if a newer one is available checkVersion(); } else { addSavedStyle(); } } isInitialized = false; } // add style at document-start await init(); async function buildOnLoad() { if (isInitialized === "pending") { // init after DOM loaded on .atom pages await init(); } // add panel even if you're not logged in - open panel using keyboard // shortcut just not on githubusercontent pages (no settings panel needed) if (window.location.host.indexOf("githubusercontent.com") < 0) { buildSettings(); // add event binding on document ready bindEvents(); on(document, "ghmo:container", updateToggles); on(document, "ghmo:comments", updateToggles); on(document, "ghmo:diff", updateToggles); on(document, "ghmo:preview", updateToggles); } isInitialized = true; } if (document.readyState === "loading") { on(document, "DOMContentLoaded", buildOnLoad); } else { buildOnLoad(); } /* utility functions */ function isBool(name) { let val = data[name]; return typeof val === "boolean" ? val : defaults[name]; } function $(str, el) { return (el || document).querySelector(str); } function $$(str, el) { return [...(el || document).querySelectorAll(str)]; } function next(el, selector) { while ((el = el.nextElementSibling)) { if (el && el.matches(selector)) { return el; } } return null; } function make(obj) { let key, el = document.createElement(obj.el); if (obj.cl4ss) { el.className = obj.cl4ss; } if (obj.html) { el.innerHTML = obj.html; } if (obj.attr) { for (key in obj.attr) { if (obj.attr.hasOwnProperty(key)) { el.setAttribute(key, obj.attr[key]); } } } if (obj.appendTo) { $(obj.appendTo).appendChild(el); } return el; } function removeAll(selector) { isUpdating = true; $$(selector).forEach(el => { el.parentNode.removeChild(el); }); isUpdating = false; } function on(els, name, callback) { els = Array.isArray(els) ? els : [els]; let events = name.split(/\s+/); els.forEach(el => { events.forEach(ev => { el.addEventListener(ev, callback); }); }); } function toggleClass(els, cl4ss, flag) { els = Array.isArray(els) ? els : [els]; els.forEach(el => { if (el) { if (typeof flag === "undefined") { flag = !el.classList.contains(cl4ss); } el.classList.toggle(cl4ss, flag); } }); } // Add GM options // await GM.registerMenuCommand("GitHub Dark Script debug logging", async () => { // let val = prompt( // "Toggle GitHub Dark Script debug log (true/false):", // "" + debug // ); // if (val) { // debug = /^t/.test(val); // await GM.setValue("debug", debug); // } // }); })();