Github Find Active Forks

Find the most active forks of a GitHub repository.

Bu betiği kurabilmeniz için Tampermonkey, Greasemonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

You will need to install an extension such as Tampermonkey to install this script.

Bu betiği kurabilmeniz için Tampermonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği kurabilmeniz için Tampermonkey ya da Userscripts gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

You will need to install an extension such as Tampermonkey to install this script.

Bu komut dosyasını yüklemek için bir kullanıcı komut dosyası yöneticisi uzantısı yüklemeniz gerekecek.

(Zaten bir kullanıcı komut dosyası yöneticim var, kurmama izin verin!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(Zateb bir user-style yöneticim var, yükleyeyim!)

// ==UserScript==
// @name         Github Find Active Forks
// @namespace    http://tampermonkey.net/
// @version      1.5.0
// @copyright    2025, Asriel (https://greasyfork.org/de/users/1375984-asriel-aac)
// @description  Find the most active forks of a GitHub repository.
// @author       Asriel
// @match        *://github.com/*
// @icon         https://github.githubassets.com/favicons/favicon-dark.png
// @grant        GM_addStyle
// @run-at       document-end
// @license MIT
// @namespace https://greasyfork.org/users/448067
// ==/UserScript==

// Securely define CSS styles directly within the script
GM_addStyle(`
  .table { width: 100%; border-collapse: collapse; }
  .table th, .table td { border: 1px solid #ddd; padding: 8px; text-align: left; }
  .table th { background-color: #f2f2f2; }
  .avatar { border-radius: 50%; }
`);

// Load latest, more secure versions of external libraries
const loadScript = (url) => {
  return new Promise((resolve) => {
    let script = document.createElement("script");
    script.src = url;
    script.onload = resolve;
    document.head.appendChild(script);
  });
};

// Define secure versions of required libraries
const scripts = [
  "https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.4/jquery.min.js",
  "https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/5.3.0/js/bootstrap.bundle.min.js",
  "https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.4/moment.min.js",
  "https://cdnjs.cloudflare.com/ajax/libs/jquery-footable/3.1.6/footable.min.js"
];

// Load scripts sequentially before executing main function
Promise.all(scripts.map(loadScript)).then(() => {

  const SIZE_KILO = 1024;
  const UNITS = ["Bytes", "KB", "MB", "GB", "TB", "PB"];

  const fetchData = async (url) => {
    try {
      let response = await fetch(url);
      if (!response.ok) throw new Error(`HTTP error! Status: ${response.status}`);
      return await response.json();
    } catch (error) {
      console.error("Fetch error:", error);
      return null;
    }
  };

  const getHumanFileSize = (size) => {
    if (size <= 0) return { size: "0", unit: UNITS[0] };
    size *= SIZE_KILO;
    const order = Math.floor(Math.log(size) / Math.log(SIZE_KILO));
    return { size: (size / Math.pow(SIZE_KILO, order)).toFixed(2), unit: UNITS[order] };
  };

  const createTable = (rJson, cJson) => {
    const humanSize = getHumanFileSize(cJson?.size ?? -1);
    const rowsData = [{
      repoName: `<img src="${cJson.owner.avatar_url}" width="24" height="24" class="avatar">
                 <a href="${cJson.html_url}">${cJson.full_name}</a>`,
      repoStars: cJson?.stargazers_count ?? -1,
      repoForks: cJson?.forks_count ?? -1,
      repoOpenIssue: cJson?.open_issues_count ?? -1,
      repoSize: humanSize,
      repoModified: Number(moment(cJson?.pushed_at ?? "NULL").format("x"))
    }];

    rJson.forEach((v) => {
      const humanSize = getHumanFileSize(v?.size ?? -1);
      rowsData.push({
        repoName: `<img src="${v.owner.avatar_url}" width="24" height="24" class="avatar">
                   <a href="${v.html_url}">${v.full_name}</a>`,
        repoStars: v?.stargazers_count ?? -1,
        repoForks: v?.forks_count ?? -1,
        repoOpenIssue: v?.open_issues_count ?? -1,
        repoSize: humanSize,
        repoModified: Number(moment(v?.pushed_at ?? "NULL").format("x"))
      });
    });

    jQuery(() => {
      $(".table").footable({
        columns: [
          { name: "repoName", title: "Repo" },
          { name: "repoStars", title: "Stars", breakpoints: "xs", type: "number" },
          { name: "repoForks", title: "Forks", breakpoints: "xs", type: "number" },
          { name: "repoOpenIssue", title: "Open Issues", breakpoints: "xs", type: "number" },
          {
            name: "repoSize",
            title: "Size",
            breakpoints: "xs",
            type: "object",
            formatter: (value) => value ? `${value.size} ${value.unit}` : "-",
            sortValue: (value) => value ? value.size * Math.pow(SIZE_KILO, UNITS.indexOf(value.unit)) : 0
          },
          {
            name: "repoModified",
            title: "Last Push",
            type: "date",
            breakpoints: "xs sm md",
            formatter: (value) => moment().to(moment(value, "YYYYMMDD"))
          }
        ],
        rows: rowsData
      });
    });
  };

  const loadMain = async () => {
    const pathComponents = window.location.pathname.split("/");
    if (pathComponents.length >= 3) {
      const user = pathComponents[1];
      const repo = pathComponents[2];
      const divForks = document.querySelector('div[id="network"]');
      if (!divForks) return;

      divForks.innerHTML = `<table class="table" data-paging="true" data-sorting="true"></table>`;
      const repoData = await fetchData(`https://api.github.com/repos/${user}/${repo}`);
      const forksData = await fetchData(`https://api.github.com/repos/${user}/${repo}/forks?sort=newest&per_page=100`);
      if (repoData && forksData) createTable(forksData, repoData);
    }
  };

  document.addEventListener("turbo:render", () => {
    if (location.pathname.endsWith("/network/members")) loadMain();
  });

  if (location.pathname.endsWith("/network/members")) loadMain();

});