Skip to content

Commit

Permalink
Merge pull request #6 from oe/feat/download-at-folderlist
Browse files Browse the repository at this point in the history
feat: 1. make file icon in file list  can be clicked to download file
  • Loading branch information
oe authored Jun 30, 2022
2 parents bb4a908 + e8b364c commit 7770e5c
Show file tree
Hide file tree
Showing 7 changed files with 181 additions and 214 deletions.
6 changes: 2 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ If you have any issues with this script, please create an issue on [Github](http

## Features
* **download github source code online**: allow you to download whole repo, a sub-folder of a repo or a single file online without `git clone` locally
* **copy source code content by one click**: allow you to copy source code in repo's single file view
* **seamless integration**: seamless integrated with Github, and works great with [octotree](https://github.com/ovity/octotree).
* **seamless integration**: seamless integrated with Github(click file icon in file list to download), and works great with [octotree](https://github.com/ovity/octotree).

![Download Github screenshot](./screenshot.png)

Expand Down Expand Up @@ -50,8 +49,7 @@ This script use [Downgit](https://downgit.evecalm.com/)([sourcecode](https://git
如果你使用中遇到任何问题, 欢迎在[Github](https://github.com/oe/download-git-userscript/issues) 上提交 issue
## 功能特性
* **在线下载Github仓库源码**: 你可以在线下载整个仓库、仓库的某个文件夹、单个文件的代码, 无需在机器上使用`git clone`命令下载完整仓库
* **一键复制源代码**: 在浏览单个文件时, 可以快速一键复制源码
* **无缝集成**: 与 GitHub 无缝集成, 看起来就像是原生功能, 与 Github 增强扩展 [octotree](https://github.com/ovity/octotree) 也能无缝配合
* **无缝集成**: 与 GitHub 无缝集成(点击文件列表左侧图标即可直接下载), 看起来就像是原生功能, 与 Github 增强扩展 [octotree](https://github.com/ovity/octotree) 也能无缝配合


![Download Github screenshot](./screenshot.png)
Expand Down
2 changes: 1 addition & 1 deletion dist/download-git-userscript.meta.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// ==UserScript==
// @name Download github repo sub-folder
// @version 0.3.0
// @version 0.4.0
// @author Saiya
// @description download github sub-folder via one click, copy the single file's source code easily
// @supportURL https://github.com/oe/download-git-userscript/issues
Expand Down
265 changes: 125 additions & 140 deletions dist/download-git-userscript.user.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// ==UserScript==
// @name Download github repo sub-folder
// @version 0.3.0
// @version 0.4.0
// @author Saiya
// @description download github sub-folder via one click, copy the single file's source code easily
// @supportURL https://github.com/oe/download-git-userscript/issues
Expand Down Expand Up @@ -101,7 +101,7 @@
/******/
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = 1);
/******/ return __webpack_require__(__webpack_require__.s = 0);
/******/ })
/************************************************************************/
/******/ ([
Expand All @@ -110,8 +110,126 @@

"use strict";

var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const utils = __importStar(__webpack_require__(1));
(function () {
const DOWNLOAD_BTN_ID = 'xiu-download-btn';
const STYLE_ELEMENT_ID = 'xiu-style-element';
main();
// observePageChange()
document.addEventListener('DOMSubtreeModified', onBodyChanged);
function main() {
if (!utils.isRepo())
return;
addDownloadBtn();
addDownload2FileList();
}
let tid = 0;
function onBodyChanged() {
clearTimeout(tid);
// @ts-ignore
tid = setTimeout(addDownloadBtn, 100);
}
function addDownloadBtn() {
let $navi = document.querySelector('.application-main .file-navigation');
if (!$navi)
return;
const downloadBtn = getDownloadBtn($navi);
if ($navi.contains(downloadBtn))
return;
$navi.appendChild(downloadBtn);
}
function getDownloadBtn($fileNavi) {
let downloadBtn = document.getElementById(DOWNLOAD_BTN_ID);
if (!downloadBtn) {
downloadBtn = document.createElement('a');
downloadBtn.id = DOWNLOAD_BTN_ID;
}
downloadBtn.className = 'btn d-none d-md-block ml-2';
downloadBtn.target = '_blank';
let url = '';
if (utils.isRepoRootDir()) {
const link = $fileNavi.querySelector('get-repo a[href$=".zip"]');
url = link.href;
}
else {
url = utils.getDownloadRedirectUrl(utils.getCurrentUrlPath());
}
downloadBtn.textContent = 'Download';
downloadBtn.href = url;
return downloadBtn;
}
function addDownload2FileList() {
if (document.getElementById(STYLE_ELEMENT_ID))
return;
const style = document.createElement('style');
style.id = STYLE_ELEMENT_ID;
const styleContent = `
.Box .Box-row > [role="gridcell"]:first-child:after {
position: absolute;
left: 20px;
top: 10px;
opacity: 0.6;
pointer-events: none;
content: '↓';
font-size: 0.8em;
}
.Box .Box-row > [role="gridcell"]:first-child > svg {
cursor: pointer;
}
`;
style.textContent = styleContent;
document.head.appendChild(style);
addEvent2FileIcon();
}
function addEvent2FileIcon() {
document.documentElement.addEventListener('click', (e) => {
var _a, _b, _c, _d;
// @ts-ignore
const target = (e.target && e.target.ownerSVGElement || e.target);
if (!target || (target.tagName || '').toLowerCase() !== 'svg')
return;
console.log(target);
const label = target.getAttribute('aria-label') || '';
if (!['Directory', 'File'].includes(label))
return;
const url = (_d = (_c = (_b = (_a = target.parentElement) === null || _a === void 0 ? void 0 : _a.nextElementSibling) === null || _b === void 0 ? void 0 : _b.querySelector) === null || _c === void 0 ? void 0 : _c.call(_b, 'a')) === null || _d === void 0 ? void 0 : _d.href;
if (!url)
return;
utils.openLink(utils.getDownloadRedirectUrl(url));
});
}
})();


/***/ }),
/* 1 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

Object.defineProperty(exports, "__esModule", { value: true });
exports.openLink = exports.getCurrentUrlPath = exports.getRawBtn = exports.getUrlTextResponse = exports.isTextBasedSinglePage = exports.isRepoRootDir = exports.isPrivateRepo = exports.isRepo = exports.isGist = void 0;
exports.getDownloadRedirectUrl = exports.openLink = exports.getCurrentUrlPath = exports.getRawBtn = exports.getUrlTextResponse = exports.isTextBasedSinglePage = exports.isRepoRootDir = exports.isPrivateRepo = exports.isRepo = exports.isGist = void 0;
/**
* is gist website
*/
Expand Down Expand Up @@ -187,143 +305,10 @@ function openLink(url) {
link.click();
}
exports.openLink = openLink;


/***/ }),
/* 1 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const utils = __importStar(__webpack_require__(0));
const utils_1 = __webpack_require__(0);
(function () {
const DOWNLOAD_BTN_ID = 'xiu-download-btn';
main();
observePageChange();
function main() {
if (!utils.isRepo())
return;
addDownloadBtn();
addCopyTextBtn();
}
function observePageChange() {
// Select the node that will be observed for mutations
let targetNode;
if (utils.isGist()) {
targetNode = document.querySelector('#gist-pjax-container');
}
else {
// to deal with octree, it append elements as siblings of #js-repo-pjax-container
// which is inside of child of .application-main
targetNode = document.querySelector('#js-repo-pjax-container');
}
if (!targetNode)
return;
let tid = 0;
// Callback function to execute when mutations are observed
const callback = function (mutationsList) {
clearTimeout(tid);
// if download button exists, and textContent just been changed
// e.g. translated by browser
if (mutationsList.some(mutation => {
if (mutation.type === 'childList' &&
mutation.target.id === DOWNLOAD_BTN_ID &&
mutation.target.textContent !== 'Download')
return true;
}))
return;
tid = setTimeout(main, 200);
};
// Create an observer instance linked to the callback function
const observer = new MutationObserver(callback);
// Start observing the target node for configured mutations
observer.observe(targetNode, { childList: true, subtree: true });
}
function addDownloadBtn() {
let $navi = document.querySelector('.repository-content .file-navigation');
if ($navi) {
const downloadBtn = getDownloadBtn($navi);
if ($navi.contains(downloadBtn))
return;
$navi.appendChild(downloadBtn);
return;
}
const moreBtn = document.querySelector('#blob-more-options-details');
if (!moreBtn)
return;
$navi = moreBtn.parentElement;
const downloadBtn = getDownloadBtn($navi);
if ($navi.contains(downloadBtn))
return;
moreBtn.insertAdjacentElement('afterend', downloadBtn);
}
function getDownloadBtn($fileNavi) {
let downloadBtn = document.getElementById(DOWNLOAD_BTN_ID);
if (!downloadBtn) {
downloadBtn = document.createElement('a');
downloadBtn.id = DOWNLOAD_BTN_ID;
}
downloadBtn.className = 'btn d-none d-md-block ml-2';
downloadBtn.target = '_blank';
let url = '';
if (utils.isRepoRootDir()) {
const link = $fileNavi.querySelector('get-repo a[href$=".zip"]');
url = link.href;
}
else {
url = `https://downgit.evecalm.com/#/home?url=${encodeURIComponent(utils.getCurrentUrlPath())}`;
}
downloadBtn.textContent = 'Download';
downloadBtn.href = url;
return downloadBtn;
}
function addCopyTextBtn() {
if (!utils.isTextBasedSinglePage() || document.getElementById('xiu-copy-btn')) {
return;
}
const rawBtn = utils_1.getRawBtn();
// <a href="/Kyome22/IronKeyboard/raw/master/Keyboard/Line1.swift" id="raw-url" role="button" class="btn btn-sm BtnGroup-item ">Raw</a>
const copyBtn = document.createElement('a');
copyBtn.setAttribute('role', 'button');
copyBtn.className = 'btn btn-sm BtnGroup-item';
copyBtn.href = '#';
copyBtn.id = 'xiu-copy-btn';
copyBtn.textContent = 'Copy';
copyBtn.onclick = async (e) => {
e.preventDefault();
try {
const text = await utils.getUrlTextResponse(rawBtn.href);
// @ts-ignore
GM_setClipboard(text);
}
catch (error) {
console.warn(error);
}
};
rawBtn.insertAdjacentElement('beforebegin', copyBtn);
}
})();
function getDownloadRedirectUrl(url) {
return `https://downgit.evecalm.com/#/home?url=${encodeURIComponent(url)}`;
}
exports.getDownloadRedirectUrl = getDownloadRedirectUrl;


/***/ })
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "download-git-userscript",
"version": "0.3.0",
"version": "0.4.0",
"scripts": {
"dev": "cross-env NODE_ENV=development webpack-dev-server --config-name main --host 127.0.0.1 --watch-poll",
"lint": "eslint .",
Expand Down
Binary file modified screenshot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 7770e5c

Please sign in to comment.