diff --git a/LICENSE b/LICENSE
index e90216b..633331b 100644
--- a/LICENSE
+++ b/LICENSE
@@ -186,7 +186,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
- Copyright 2020-2021 Giacomo Ferretti
+ Copyright 2020-2022 Giacomo Ferretti
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/README.md b/README.md
index 2b299b6..1b1bbbe 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,27 @@
# Forks Diff Counter for GitHub
-## Download
+## Installation
-Get the extension on the [chrome web store](https://chrome.google.com/webstore/detail/eencojgimolmahmdfpnfbcldppmlokfg).
+### Chrome Web Store
+
+Get the extension on the [Chrome Web Store](https://chrome.google.com/webstore/detail/eencojgimolmahmdfpnfbcldppmlokfg).
+
+### Firefox Browser Add-ons
+
+Get the extension on the [Firefox Browser Add-ons](https://addons.mozilla.org/en-US/firefox/addon/forks-diff-counter-for-github/).
+
+### Unpacked
+
+#### Google Chrome
+
+1. Download the repo by using `git clone https://github.com/giacomoferretti/forks-diff-chrome`
+2. Visit `chrome://extensions/` in Google Chrome
+3. Enable "Developer mode"
+4. Click on "Load unpacked"
+5. Select the cloned folder
+
+When updating the plugin, remember to reload it.
## How to pack
-Use the `pack.sh` script to generate the zip file.
\ No newline at end of file
+Use the `pack.sh` script to generate the zip file.
diff --git a/js/inject.js b/js/inject.js
index 0caaaf4..f67d675 100644
--- a/js/inject.js
+++ b/js/inject.js
@@ -1,14 +1,19 @@
/**
- * Because we are running in an "isolated world" and need to hook 'replaceState',
+ * Because we are running in an "isolated world" and need to hook 'replaceState',
* we need to inject our script into the page.
*
* https://developer.chrome.com/docs/extensions/mv3/content_scripts/#isolated_world
*/
(function () {
- var s = document.createElement('script');
- s.src = chrome.runtime.getURL('js/main.js');
- s.onload = function() {
- this.remove();
- };
- (document.head || document.documentElement).appendChild(s);
+ // ...chrome pls...
+ if (typeof browser === "undefined") {
+ var browser = chrome;
+ }
+
+ const s = document.createElement("script");
+ s.src = browser.runtime.getURL("js/main.js");
+ s.onload = function () {
+ this.remove();
+ };
+ (document.head || document.documentElement).appendChild(s);
})();
diff --git a/js/main.js b/js/main.js
index 2a8a96c..41678a2 100644
--- a/js/main.js
+++ b/js/main.js
@@ -1,167 +1,152 @@
-'use strict';
-
-const forksDiff = (function() {
- // Icons
- const starIcon = ' ';
- const forkIcon = ' ';
- const loadingIcon = ' ';
-
- // Regex
- const isEvenRegex = /
[\s]+This branch is even/;
- const commitsAheadRegex = /
[\s]+This branch is ([0-9]*) commits? ahead/;
- const commitsBehindRegex = /
[\s]+This branch is ([0-9]*) commits? behind/;
- const commitsFullRegex = /
[\s]+This branch is ([0-9]*) commits? ahead, ([0-9]*) commit/;
- const starsRegex = /([0-9]+) users? starred this repository/;
- const githubUrlRegex = /https?:\/\/github\.com\/.*\/network\/members/;
-
- function appendSpace(e) {
- e.appendChild(document.createTextNode(' '));
- }
-
- function processRepo(repoElement) {
- // Add spinner
- const spinner = document.createElement('span');
- spinner.innerHTML = loadingIcon;
- repoElement.appendChild(spinner);
-
- // Extract repo URL
- const repoUrl = repoElement.getElementsByTagName('a')[2].href;
-
- // Prepare request
- const request = new XMLHttpRequest();
- request.addEventListener('load', function() {
- repoElement.removeChild(spinner);
-
- const body = this.responseText;
-
- let commitsAhead = 0;
- let commitsBehind = 0;
-
- // Check if repo is even
- if (isEvenRegex.test(body)) {
- const evenSpan = document.createElement('span');
- evenSpan.className = 'text-gray';
- evenSpan.appendChild(document.createTextNode('is even'));
- repoElement.appendChild(evenSpan);
- } else {
- // Extract ahead and behind
- let commitHistoryData = body.match(commitsFullRegex);
- if (commitHistoryData != null) {
- commitsAhead = parseInt(commitHistoryData[1]);
- commitsBehind = parseInt(commitHistoryData[2]);
- } else {
- // Extract ahead
- commitHistoryData = body.match(commitsAheadRegex);
- if (commitHistoryData != null) {
- commitsAhead = parseInt(commitHistoryData[1]);
- }
-
- // Extract behind
- commitHistoryData = body.match(commitsBehindRegex);
- if (commitHistoryData != null) {
- commitsBehind = parseInt(commitHistoryData[1]);
- }
- }
-
- // Add ahead commits
- if (commitsAhead != 0) {
- appendSpace(repoElement);
- const commitsAheadText = document.createElement('span');
- commitsAheadText.className = 'cadd';
- commitsAheadText.appendChild(document.createTextNode('+' + commitsAhead));
- repoElement.appendChild(commitsAheadText);
- }
-
- // Add behind commits
- if (commitsBehind != 0) {
- appendSpace(repoElement);
- const commitsBehindCounter = document.createElement('span');
- commitsBehindCounter.className = 'cdel';
- commitsBehindCounter.appendChild(document.createTextNode('-' + commitsBehind));
- repoElement.appendChild(commitsBehindCounter);
- }
- }
-
- // Read stars
- appendSpace(repoElement);
- const stars = body.match(starsRegex);
- const starIndicator = document.createElement('span');
- starIndicator.innerHTML = starIcon + ' ' + stars[1];
- repoElement.appendChild(starIndicator);
- });
-
- // Send request
- request.open('GET', repoUrl);
- request.send();
- }
-
- function mainButtonAction(e) {
- // Disable button
- e.target.setAttribute('class', 'btn btn-sm disabled');
- e.target.removeEventListener('click', mainButtonAction);
-
- // Iterate through repos
- const repos = network.children;
- for (let i = 0; i < repos.length; i++) {
- if (repos[i].getElementsByClassName('network-tree').length === 0) continue; // Skip original
-
- processRepo(repos[i]);
+"use strict";
+
+const forksDiff = (function () {
+ // Icons
+ const starIcon =
+ '
';
+ const forkIcon =
+ '
';
+ const loadingIcon =
+ '
';
+
+ // Regex
+ const diffRegex =
+ /
[\S\s]*?This branch is[\S\s]*?((?
[0-9]*) commits? ahead[\S\s]*?(?[0-9]*) commits? behind|(?[0-9]*) commits? behind|(?[0-9]*) commits? ahead)/;
+ const starsRegex = /([0-9]+) users? starred this repository/;
+ const githubUrlRegex = /https?:\/\/github\.com\/.*\/network\/members/;
+
+ const queue = [];
+ const parallelNum = 3;
+
+ const addSpan = (parent, text, className) => {
+ const span = document.createElement("span");
+ span.className = className;
+ span.appendChild(document.createTextNode(text));
+ parent.appendChild(span);
+ };
+
+ const processRepo = () => {
+ const currentRepo = queue.shift();
+
+ // Add spinner
+ const spinner = document.createElement("span");
+ spinner.innerHTML = loadingIcon;
+ currentRepo.appendChild(spinner);
+
+ const request = new XMLHttpRequest();
+ request.addEventListener("load", function () {
+ if (this.status == 429) {
+ // Rate limited :(
+ console.log(this.getResponseHeader("Retry-After"));
+ return;
+ }
+
+ const body = this.responseText;
+ currentRepo.removeChild(spinner);
+
+ // Read diff
+ const diffRegexResult = diffRegex.exec(body);
+ if (!diffRegexResult) {
+ addSpan(currentRepo, "is even", "text-gray");
+ } else {
+ const {
+ groups: { a1, a2, b, c },
+ } = diffRegexResult;
+
+ if (a1 && a2) {
+ addSpan(currentRepo, "+" + a1, "cadd");
+ addSpan(currentRepo, " ");
+ addSpan(currentRepo, "-" + a2, "cdel");
+ } else if (b) {
+ addSpan(currentRepo, "-" + b, "cdel");
+ } else if (c) {
+ addSpan(currentRepo, "+" + c, "cadd");
}
+ }
+
+ // Read stars
+ addSpan(currentRepo, " ");
+ const stars = body.match(starsRegex);
+ const starIndicator = document.createElement("span");
+ starIndicator.innerHTML = starIcon + " " + stars[1];
+ currentRepo.appendChild(starIndicator);
+
+ // Process next repo
+ if (queue.length > 0) {
+ processRepo();
+ }
+ });
+
+ // Send request
+ request.open("GET", currentRepo.getElementsByTagName("a")[2].href);
+ request.send();
+ };
+
+ const mainButtonAction = (e) => {
+ // Disable button
+ e.target.className = "btn ml-2 float-right disabled";
+ e.target.removeEventListener("click", mainButtonAction);
+
+ // Iterate through repos
+ const repos = network.children;
+ for (let i = 0; i < repos.length; i++) {
+ if (repos[i].getElementsByClassName("network-tree").length === 0)
+ continue; // Skip original
+
+ queue.push(repos[i]);
}
- function addButton() {
- const network = document.getElementById('network');
-
- // Check if we have at least one div.repo, if not we are on Network page and not Forks page
- if (network === null) return;
- if (network.querySelector('div.repo') === null) return;
-
- const mainButton = document.createElement('button');
- mainButton.className = 'btn btn-sm';
- mainButton.style.float = 'right';
- mainButton.innerHTML = forkIcon + ' Load diff';
- mainButton.addEventListener('click', mainButtonAction);
- network.insertBefore(mainButton, network.childNodes[0]);
- }
-
- function addReplaceStateEventListener() {
- // https://gist.github.com/rudiedirkx/fd568b08d7bffd6bd372
- const _wr = function(type) {
- const orig = history[type];
- return function() {
- const rv = orig.apply(this, arguments);
- const e = new Event(type);
- e.arguments = arguments;
- window.dispatchEvent(e);
- return rv;
- };
- };
- history.pushState = _wr('pushState');
- history.replaceState = _wr('replaceState');
+ // Start
+ for (let i = parallelNum - 1; i >= 0; i--) {
+ processRepo();
}
+ };
+
+ const addButton = () => {
+ const network = document.getElementById("network");
+
+ // Check if we have at least one div.repo, if not we are on Network page and not Forks page
+ if (network === null) return;
+ if (network.querySelector("div.repo") === null) return;
+
+ const mainButton = document.createElement("button");
+ mainButton.className = "btn ml-2 float-right";
+ mainButton.innerHTML = forkIcon + " Load diff";
+ mainButton.addEventListener("click", mainButtonAction);
+ network.insertBefore(mainButton, network.childNodes[0]);
+ };
+
+ const addReplaceStateEventListener = () => {
+ // https://gist.github.com/rudiedirkx/fd568b08d7bffd6bd372
+ const _wr = (type) => {
+ const orig = history[type];
+ return () => {
+ const rv = orig.apply(this, arguments);
+ const e = new Event(type);
+ e.arguments = arguments;
+ window.dispatchEvent(e);
+ return rv;
+ };
+ };
+ history.pushState = _wr("pushState");
+ history.replaceState = _wr("replaceState");
+ };
- function replaceStateListener() {
- function action() {
- addButton();
- document.removeEventListener('pjax:end', action);
- }
-
- if (githubUrlRegex.test(location.href)) {
- document.addEventListener('pjax:end', action);
- }
+ const replaceStateListener = () => {
+ if (githubUrlRegex.test(location.href)) {
+ addButton();
}
+ };
- return {
- addButton: addButton,
- addReplaceStateEventListener: addReplaceStateEventListener,
- replaceStateListener: replaceStateListener,
- };
+ return {
+ addButton: addButton,
+ addReplaceStateEventListener: addReplaceStateEventListener,
+ replaceStateListener: replaceStateListener,
+ };
})();
-(function() {
- // Add 'replaceState' listener
- forksDiff.addReplaceStateEventListener();
- window.addEventListener('replaceState', forksDiff.replaceStateListener);
-
- forksDiff.addButton();
+(function () {
+ // Add 'replaceState' listener
+ forksDiff.addReplaceStateEventListener();
+ window.addEventListener("replaceState", forksDiff.replaceStateListener);
})();
diff --git a/manifest.json b/manifest.json
index dccd6c9..182952d 100644
--- a/manifest.json
+++ b/manifest.json
@@ -1,28 +1,27 @@
{
- "manifest_version": 2,
- "name": "__MSG_appName__",
- "version": "1.1.1",
- "description": "__MSG_appDescription__",
- "default_locale": "en",
- "icons": {
- "16": "icons/icon.16.png",
- "32": "icons/icon.32.png",
- "48": "icons/icon.48.png",
- "64": "icons/icon.64.png",
- "128": "icons/icon.128.png"
- },
- "content_scripts": [
- {
- "matches": [
- "*://github.com/*"
- ],
- "js": [
- "js/inject.js"
- ],
- "run_at": "document_end"
- }
- ],
- "web_accessible_resources": [
- "js/main.js"
- ]
+ "manifest_version": 3,
+ "name": "__MSG_appName__",
+ "version": "1.2.0",
+ "description": "__MSG_appDescription__",
+ "default_locale": "en",
+ "icons": {
+ "16": "icons/icon.16.png",
+ "32": "icons/icon.32.png",
+ "48": "icons/icon.48.png",
+ "64": "icons/icon.64.png",
+ "128": "icons/icon.128.png"
+ },
+ "content_scripts": [
+ {
+ "matches": ["*://github.com/*"],
+ "js": ["js/inject.js"],
+ "run_at": "document_end"
+ }
+ ],
+ "web_accessible_resources": [
+ {
+ "resources": ["js/main.js"],
+ "matches": ["*://github.com/*"]
+ }
+ ]
}
diff --git a/pack.sh b/pack.sh
index ae5c014..97dc655 100755
--- a/pack.sh
+++ b/pack.sh
@@ -1,6 +1,6 @@
#!/usr/bin/env bash
-VERSION="1.1.1"
+VERSION="1.2.0-chrome"
ICONS_SIZES="16 32 48 64 128"
OUTPUT_FOLDER=".build"