Github Repository Tab Creator

Template Script to Create your own Repository Tab

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

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

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name        Github Repository Tab Creator
// @namespace   Violentmonkey Scripts
// @match       *://github.com/*/*
// @grant       none
// @version     1.0.1
// @author      Der_Floh
// @description Template Script to Create your own Repository Tab
// @license     MIT
// @icon        https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png
// @homepageURL https://greasyfork.org/de/scripts/468808-github-repository-tab-creator
// @supportURL  https://greasyfork.org/de/scripts/468808-github-repository-tab-creator/feedback
// ==/UserScript==

// jshint esversion: 8
console.info(`%cRunning: ${GM_info.script.name} v${GM_info.script.version}`, 'color: DodgerBlue');



const name1 = "EXAMPLE-NAME"; // <-- Name for the tab (has to be something)
const icon1 = "https://somewebsite/EXAMPLE-ICON.png"; // <-- icon to use for the tab (possible: image link, svg string, html element, empty for no icon)
createTabElem(name1, icon1, (event) => {
  const currentLocation = window.location.toString();
  const subpage = "EXAMPLE-SUBPAGE"; // <-- Subpage to navigate to (for example https://github.com/username/repositoryname/SUBPAGE)
  const link = `/${getUsername(currentLocation)}/${getRepositoryname(currentLocation)}/${subpage}`;
  window.location.href = link;
});

// Working Example:
/*const name2 = "Stars"
const icon2 = '<svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-star d-inline-block mr-2"><path d="M8 .25a.75.75 0 0 1 .673.418l1.882 3.815 4.21.612a.75.75 0 0 1 .416 1.279l-3.046 2.97.719 4.192a.751.751 0 0 1-1.088.791L8 12.347l-3.766 1.98a.75.75 0 0 1-1.088-.79l.72-4.194L.818 6.374a.75.75 0 0 1 .416-1.28l4.21-.611L7.327.668A.75.75 0 0 1 8 .25Zm0 2.445L6.615 5.5a.75.75 0 0 1-.564.41l-3.097.45 2.24 2.184a.75.75 0 0 1 .216.664l-.528 3.084 2.769-1.456a.75.75 0 0 1 .698 0l2.77 1.456-.53-3.084a.75.75 0 0 1 .216-.664l2.24-2.183-3.096-.45a.75.75 0 0 1-.564-.41L8 2.694Z"></path></svg>';
createTabElem(name2, icon2, (event) => {
  const currentLocation = window.location.toString();
  const subpage = "stargazers";
  const link = `/${getUsername(currentLocation)}/${getRepositoryname(currentLocation)}/${subpage}`;
  window.location.href = link;
});*/



// | ------------+-------------------+------------ |
// | ------------|  Given Functions  |------------ |
// V ------------+-------------------+------------ V

/**
 * Gets the username of a github url
 * @param {string} url - The url from which to get the username
 * @returns {string} The username of the given github url
 */
function getUsername(url) {
  const parsedUrl = new URL(url);
  const pathSegments = parsedUrl.pathname.split('/');

  if (pathSegments.length >= 3)
    return pathSegments[1];

  return null;
}

/**
 * Gets the repositoryname of a github url
 * @param {string} url - The url from which to get the repositoryname
 * @returns {string} The repositoryname of the given github url
 */
function getRepositoryname(url) {
  const parsedUrl = new URL(url);
  const pathSegments = parsedUrl.pathname.split('/');

  if (pathSegments.length >= 3)
    return pathSegments[2];

  return null;
}

/**
 * Adds a new Tab to a Repository Page
 * @param {string} name - The name of the tab
 * @param {Element} svg - The svg element that specifies the icon of the tab
 * @param {Function} callback - function that specifies what should happen
 * @returns {Element} The created Tab
 */
async function createTabElem(name, svg, callback) {
  const isStargazers = await handleLocalStorage(name);
  const menubar = document.querySelector('ul[class="UnderlineNav-body list-style-none"]');
  const id = menubar.querySelectorAll('li[class="d-inline-flex"]').length;
  let li = document.createElement("li");
  li.dataset.dataViewComponent = true;
  const a = document.createElement("a");
  a.id = name.toLowerCase() + "-tab";
  a.dataset.tabItem = `i${id}${name}-tab`;
  a.dataset.pjax = "#repo-content-pjax-container";
  a.dataset.turboFrame = "repo-content-turbo-frame";
  a.dataset.hotkey = "g c";
  a.dataset.analyticsEvent = '{"category":"Underline navbar","action":"Click tab","label":"Settings","target":"UNDERLINE_NAV.TAB"}';
  a.dataset.viewComponent = "true";
  a.classList.add("UnderlineNav-item", "no-wrap", "js-responsive-underlinenav-item", "js-selected-navigation-item");
  a.addEventListener('click', (event) => {
    event.preventDefault();
    localStorage.setItem(name.toLowerCase(), true);
    callback(event);
  });
  if (isStargazers)
    a.setAttribute("aria-current", "page");

  if (svg && svg != "") {
    let svgElem;
    if (isElement(svg)) {
      svgElem = svg.cloneNode(true);
    } else if (svg.startsWith("http")) {
      svgElem = document.createElement("img");
      svgElem.src = svg;
    } else {
      svgElem = document.createElement("div");
      svgElem.innerHTML = svg;
      svgElem.style.display = "flex";
      svgElem.style.justifyContent = "center";
      svgElem.style.alignItems = "center";
    }
    svgElem.style.maxWidth = "16px";
    svgElem.style.maxHeight = "16px";
    a.appendChild(svgElem);
  }
  const span1 = document.createElement("span");
  span1.dataset.content = name;
  span1.textContent = name;
  a.appendChild(span1);
  const span2 = document.createElement("span");
  span2.id = "code-repo-tab-count";
  span2.dataset.pjaxReplace = "";
  span2.dataset.turboReplace = "";
  span2.title = "Not available";
  span2.dataset.viewComponent = true;
  span2.classList.add("Counter");
  a.appendChild(span2);
  li.appendChild(a);
  menubar.appendChild(li);
  return li;
}

/**
 * Handles the current selected tab inside the localStorage
 * @param {string} tabName - The tabname to handle
 * @returns {boolean} True if the tab is selected
 */
async function handleLocalStorage(tabName) {
  const isStargazers = localStorage.getItem(tabName.toLowerCase()) == "true";
  if (isStargazers)
    localStorage.removeItem(tabName.toLowerCase());

  window.addEventListener("load", () => {
    if (!isStargazers)
      return;
    const tab = document.getElementById(tabName.toLowerCase() + "-tab");
    const intervalId = setInterval(() => {
      if (tab.getAttribute("aria-current") == "page")
        clearInterval(intervalId);
      if (isStargazers)
        tab.setAttribute("aria-current", "page");
    }, 10);
  });

  return isStargazers;
}

/**
 * Checks wether a variable is an Element
 * @param {object} variable - The variable to check
 * @returns {boolean} True if variable is an Element
 */
function isElement(variable) {
  return variable instanceof Element;
}