// ==UserScript==
// @name GitHub Download ZIP
// @version 0.2.8
// @description A userscript adds download links so that downloaded filenames include the SHA
// @license MIT
// @author Rob Garrison
// @namespace https://github.com/Mottie
// @match https://github.com/*
// @run-at document-idle
// @grant GM_addStyle
// @grant GM.addStyle
// @grant GM_xmlhttpRequest
// @grant GM.xmlHttpRequest
// @connect api.github.com
// @connect assets-cdn.github.com
// @require https://greasemonkey.github.io/gm4-polyfill/gm4-polyfill.js?updated=20180103
// @require https://greasyfork.org/scripts/28721-mutations/code/mutations.js?version=1108163
// @icon https://github.githubassets.com/pinned-octocat.svg
// @updateURL https://raw.githubusercontent.com/Mottie/Github-userscripts/master/github-download-zip.user.js
// @downloadURL https://raw.githubusercontent.com/Mottie/Github-userscripts/master/github-download-zip.user.js
// @supportURL https://github.com/Mottie/GitHub-userscripts/issues
// ==/UserScript==
/* global GM */
(() => {
"use strict";
GM.addStyle(`
.ghdz-releases { width:100% !important; padding:10px; }
.ghdz-releases summary { text-align:left; padding-left:16px; }
.ghdz-files.select-menu-modal { width:100%; border:0; box-shadow:none !important; margin-bottom:0; }
.ghdz-file { text-align:left; padding-left:16px; }
.commit-links-cell { min-width: 375px; width: auto; }
`);
const zipIcon = `
`;
const span = document.createElement("span");
span.innerHTML = zipIcon;
const link = document.createElement("a");
link.className = "btn btn-outline BtnGroup-item tooltipped tooltipped-s ghdz-btn";
link.setAttribute("aria-label", "Download ZIP");
link.innerHTML = zipIcon;
const div = document.createElement("details");
div.className = "select-menu get-repo-btn ghdz-releases";
div.innerHTML = `
Latest Release Files
`;
function buildURL(part) {
const [, user, repo] = window.location.pathname.split("/");
return `https://api.github.com/repos/${user}/${repo}/zipball/${part}`;
}
function buildLink(url, text) {
return `
${text}
`;
}
function buildReleases(result, container) {
let html = `
${
typeof result === "string" ? result : "No release files or assets found"
}
`;
// Example page with release files & assets:
// https://github.com/Maximus5/ConEmu/releases
if (
Array.isArray(result) &&
result.length &&
Object.keys(result[0] || {}).length
) {
html = "";
const last = result[0];
if (last.assets) {
last.assets.forEach(release => {
const url = release.browser_download_url;
html += buildLink(url, url.split("/").slice(-1));
});
}
html += buildLink(last.zipball_url, "Source code (zip)");
html += buildLink(last.tarball_url, "Source code (tar.gz)");
}
$(".ghdz-files", container).innerHTML = html;
}
function getReleases(container) {
if ($(".ghdz-file", container)) {
// Already loaded
return;
}
const [, user, repo] = window.location.pathname.split("/");
GM.xmlHttpRequest({
method : "GET",
url : `https://api.github.com/repos/${user}/${repo}/releases`,
onload : response => {
if (response.status !== 200) {
buildReleases(response.message, container);
return console.error(response);
}
let json = false;
try {
json = JSON.parse(response.responseText);
} catch (err) {
return console.error(response);
}
if (json) {
buildReleases(json, container);
}
}
});
}
function addBindings() {
document.addEventListener("click", function(event) {
const target = event.target.closest("details");
if (target && target.classList.contains("ghdz-releases")) {
getReleases(target);
}
});
}
function updateLinks() {
// Branch dropdown on main repo page
const branch = $("summary[data-hotkey='w'] span");
// Download link in "Clone or Download" dropdown
const downloadLink = $("a[data-ga-click*='download zip']");
// Repo commits page
const commits = $(".commits-listing");
if (downloadLink && branch && !$(".ghdz-releases", downloadLink.parentElement)) {
const branchName = branch.textContent.indexOf("…") > -1
// Branch selector is showing trucated text; title has full text
? branch.parentNode.title
: branch.textContent;
downloadLink.href = buildURL(branchName.trim());
downloadLink.appendChild(span.cloneNode(true));
downloadLink.after(div.cloneNode(true));
downloadLink.parentElement.classList.add("flex-wrap");
}
// Branch doesn't matter when you're using the SHA (first 7 values)
if (commits) {
[...document.querySelectorAll(".commit-group .commit .commit-links-group")].forEach(group => {
if (!$(".ghdz-btn", group)) {
const sha = $(".sha", group).textContent.trim();
const a = link.cloneNode(true);
a.href = buildURL(sha);
group.appendChild(a);
}
});
}
}
function $(selector, el) {
return (el || document).querySelector(selector);
}
// DOM targets - to detect GitHub dynamic ajax page loading
document.addEventListener("pjax:end", updateLinks);
updateLinks();
addBindings();
})();