Hover Alternative Front-Ends

Pops up a floating div when you hover over a link, containing alternative front-ends!

Dovrai installare un'estensione come Tampermonkey, Greasemonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Userscripts per installare questo script.

Dovrai installare un'estensione come ad esempio Tampermonkey per installare questo script.

Dovrai installare un gestore di script utente per installare questo script.

(Ho già un gestore di script utente, lasciamelo installare!)

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

(Ho già un gestore di stile utente, lasciamelo installare!)

// ==UserScript==
// @name           Hover Alternative Front-Ends
// @namespace      HAFE
// @description    Pops up a floating div when you hover over a link, containing alternative front-ends!
// @homepageURL    https://greasyfork.org/en/scripts/437920-hover-alternative-front-ends
// @match          *://*/*
// @grant          none
// @run-at         document-idle
// @version        1.2.3
// ==/UserScript==

// This script is a fork of 'Hover Preview':
// https://greasyfork.org/en/scripts/8042-hover-preview

const focusReactionTime = 300;
const unfocusReactionTime = 1500;

const domains = {
  youtube: ["youtube.com","youtu.be","youtube-nocookie.com"],
  twitter: ["twitter.com","twimg.com"],
  reddit: ["reddit.com","i.redd.it"],
  instagram: ["instagram.com"],
  imgur: ["imgur.com"],
  medium: ["medium.com"],
  wikipedia: ["en.wikipedia.org"]
};

const youtubeFrontends = [{
  name: "Invidious(Snopyta)",
  address: "invidious.snopyta.org"
},{
  name: "Invidious(Yewtube)",
  address: "yewtu.be"
},{
  name: "Invidious(Puffyan)",
  address: "vid.puffyan.us"
},{
  name: "Invidious(Seth)",
  address: "invidious.sethforprivacy.com"
},{
  name: "CloudTube",
  address: "tube.cadence.moe"
},{
  name: "Piped",
  address: "piped.kavin.rocks",
}];

const twitterFrontends = [{
  name: "Nitter",
  address: "nitter.net"
},{
  name: "Nitter(Snopyta)",
  address: "nitter.snopyta.org"
},{
  name: "Nitter(Puss)",
  address: "nitter.pussthecat.org"
},{
  name: "Nitter(42l)",
  address: "nitter.42l.fr"
},{
  name: "Nitter(Seth)",
  address: "nitter.sethforprivacy.com"
},{
  name: "Nitter(Action Sack)",
  address: "nitter.actionsack.com"
}];

const redditFrontends = [{
  name: "Teddit",
  address: "teddit.net"
},{
  name: "Teddit(Puss)",
  address: "teddit.pussthecat.org"
},{
  name: "Teddit(Seth)",
  address: "teddit.sethforprivacy.com"
},{
  name: "Libreddit",
  address: "libredd.it"
},{
  name: "Libreddit(Spike)",
  address: "libreddit.spike.codes"
},{
  name: "Libreddit(Puss)",
  address: "libreddit.pussthecat.org"
}];

const instagramFrontends = [{
  name: "Bibliogram",
  address: "bibliogram.art"
},{
  name: "Bibliogram(Snopyta)",
  address: "bibliogram.snopyta.org"
},{
  name: "Bibliogram(Puss)",
  address: "bibliogram.pussthecat.org"
},{
  name: "Bibliogram(Action Sack)",
  address: "bib.actionsack.com"
},{
  name: "Bibliogram(Hamster)",
  address: "bibliogram.hamster.dance"
}];

const imgurFrontends = [{
  name: "Imgin",
  address: "imgin.voidnet.tech"
},{
  name: "Ringu(Action Sack)",
  address: "i.actionsack.com"
},{
  name: "Kageurufu",
  address: "imgur.kageurufu.net"
}];

const mediumFrontends = [{
  name: "Scribe",
  address: "scribe.rip"
},{
  name: "Scribe(NixNet)",
  address: "scribe.nixnet.services"
}];

const wikipediaFrontends = [{
  name: "Wikiless",
  address: "wikiless.org"
},{
  name: "Wikiless(Seth)",
  address: "wikiless.sethforprivacy.com"
},{
  name: "Infogalactic",
  address: "infogalactic.com"
}];

var focus = undefined;
var lastFocus = undefined;
var timer = null;

var hafePopup;
var hafeFrame;

var isOverPopup = false;

function checkFocus() {
  if (focus) {
    // if (focus == lastFocus) {
      // User has definitely been here a while
      showHAFEWindow(focus);
    // } else {
    // }
    // lastFocus = focus;
  }
}

function aMouseOver(evt) {
  if (evt.currentTarget.tagName !== "A") {
          alert(decodeURIComponent("not link"));
    return;
  }
  if (!focus) {
    focus = evt.currentTarget;
    // setTimeout('checkFocus();',focusReactionTime);
    // Hack to bring the popup back immediately if we've gone back to the same link.
    if (hafeFrame && focus.href && hafeFrame.href == focus.href) {
      showHAFEWindow(focus,evt);
    } else {
      if (timer) {
        clearTimeout(timer);
      }
      timer = setTimeout(checkFocus,focusReactionTime);
    }
  } else {
    window.status = "Already focused on a link wtf!";
  }
}

function aMouseOut(evt) {
  if (evt.currentTarget.tagName !== "A") {
    return;
  }
  focus = undefined;
  if (timer) {
    clearTimeout(timer);
  }
  // TESTING: Don't hide the popup if mouse is currently over the popup!
  timer = setTimeout(clearPopup,unfocusReactionTime);
}

function clearPopup(e) {
  if (isOverPopup || focus)
    return;
  if (hafePopup) {
    // hafePopup.parentNode.removeChild(hafePopup);
    // hafePopup = undefined; // eww cache it!
    hafePopup.style.display = 'none';
  }
}

// DONE: If the user clicks a link, this isn't really a hover, so we should not
// activate and just let the user's click be processed!
function aClick(evt) {
  focus = undefined;
}

function createPopup() {
  // Create frame
  hafePopup = document.createElement('DIV');
  /** Seems style does not work for Konqueror this way. **/
  hafePopup.innerHTML =
    "<STYLE type='text/css'> .hafediv { background-color: #21242C; margin: 0px; padding: 2px; border: 1px solid dodgerblue; border-radius: 4px; text-align: center; box-sizing: border-box; } .hafediv a { font-family: Helvetica; font-size: 14px; color: white; text-decoration: none; padding: 0 5px; box-shadow: inset 0 0 0 0 #21242C; transition: all 0.4s ease-in-out 0s; border-radius: 3px; box-sizing: border-box; } .hafediv a:hover { box-shadow: inset 0 300px 0 0 dodgerblue; color: white; } </STYLE>"
    +
    "<DIV class='hafediv' width='" + (window.innerWidth * 0.75) + "' height='" + (window.innerHeight*0.75) + "' src='about:blank'></DIV>";
  hafePopup.addEventListener("mouseover", function(evt) { isOverPopup=true; }, false);
  hafePopup.addEventListener("mouseout", function(evt) { isOverPopup=false; setTimeout(clearPopup,unfocusReactionTime); }, false);
  document.documentElement.appendChild(hafePopup);
  hafePopup.style.position = "absolute";
  hafePopup.style.zIndex = "10000";
  hafeFrame = hafePopup.getElementsByTagName('DIV')[0];
}

function insertLink(linkAddress, linkText) {
  var newLink = document.createElement('a');
  newLink.href = linkAddress
  newLink.textContent = linkText
  hafeFrame.append(newLink);
  hafeFrame.append(document.createTextNode(" "));
}

function insertLinks(link) {
  hafeFrame.innerHTML = "";

  var linkHost = link.href.match(/^https?\:\/\/([^\/?#]+)(?:[\/?#]|$)/i);
  var linkPath = link.href.replace(linkHost[0], "");

  for (var domain in domains) {
    for (var target=0; target < domains[domain].length; target++) {
      if (linkHost[0].includes(domains[domain][target])) {
        switch (domain) {
          case "youtube":
          if (linkHost[1].includes("studio")) {
            return false;
          } else {
            for (var index=0; index < youtubeFrontends.length; index++) {
              insertLink(link.href.replace(linkHost[1], youtubeFrontends[index].address), youtubeFrontends[index].name);
            }
          }
          break;

          case "twitter":
          if (linkHost[1].includes("pbs") || linkHost[1].includes("video")) {
            for (var index=0; index < twitterFrontends.length; index++) {
              insertLink(linkHost[0].replace(linkHost[1], twitterFrontends[index].address) + "pic/" + encodeURIComponent(link.href), twitterFrontends[index].name);
            }
          } else {
            for (var index=0; index < twitterFrontends.length; index++) {
              insertLink(link.href.replace(linkHost[1], twitterFrontends[index].address), twitterFrontends[index].name);
            }
          }
          break;

          case "reddit":
          if (domains[domain][target] === "i.redd.it") {
            for (var index=0; index < redditFrontends.length; index++) {
              if (redditFrontends[index].name.includes("Libreddit")) {
                insertLink(linkHost[0].replace(linkHost[1], redditFrontends[index].address) + "img/" + linkPath, redditFrontends[index].name);
              }
            }
          } else {
            for (var index=0; index < redditFrontends.length; index++) {
              insertLink(link.href.replace(linkHost[1], redditFrontends[index].address), redditFrontends[index].name);
            }
          }
          break;

          case "instagram":
          const instagramPaths = /\b(?:tv|reels?|u|p)\b/i;
          const instagramIgnore = /\b(?:stories|accounts|explore|topics)\b/i;
          var linkFolders = linkPath.split(/(?:\?|\/)+/);

          if (linkHost[1].includes("about") || linkHost[1].includes("help") || linkFolders[0].match(instagramIgnore)) {
            return false;
          } else if (linkFolders.length > 0 && linkFolders[0].match(instagramPaths)) {
            for (var index=0; index < instagramFrontends.length; index++) {
              insertLink(link.href.replace(linkHost[1], instagramFrontends[index].address), instagramFrontends[index].name);
            }
          } else if (linkFolders.length > 1 && linkFolders[1].match(instagramPaths)) {
            for (var index=0; index < instagramFrontends.length; index++) {
              insertLink(linkHost[0].replace(linkHost[1], instagramFrontends[index].address) + linkPath.replace(linkFolders[0] + "/", ""), instagramFrontends[index].name);
            }
          } else {
            for (var index=0; index < instagramFrontends.length; index++) {
              insertLink(link.href.replace(linkHost[1], instagramFrontends[index].address + "/u"), instagramFrontends[index].name);
            }
          }
          break;

          case "imgur":
          for (var index=0; index < imgurFrontends.length; index++) {
            insertLink(link.href.replace(linkHost[1], imgurFrontends[index].address), imgurFrontends[index].name);
          }
          break;

          case "medium":
          for (var index=0; index < mediumFrontends.length; index++) {
            insertLink(link.href.replace(linkHost[1], mediumFrontends[index].address), mediumFrontends[index].name);
          }
          break;

          case "wikipedia":
          for (var index=0; index < wikipediaFrontends.length; index++) {
            if (wikipediaFrontends[index].name.includes("Infogalactic")) {
              insertLink(linkHost[0].replace(linkHost[1], wikipediaFrontends[index].address) + linkPath.replace("wiki", "info"), wikipediaFrontends[index].name);
            } else {
              insertLink(link.href.replace(linkHost[1], wikipediaFrontends[index].address), wikipediaFrontends[index].name);
            }
          }
          break;

          default:
          alert(decodeURIComponent(link.href));
        }
        return true;
      }
    }
  }
  return false;
}

function showHAFEWindow(link,evt) {
  if (!hafeFrame) {
    createPopup();
  }
  if (insertLinks(link)) {
    hafePopup.style.display = '';
    hafePopup.style.top = ((link.getBoundingClientRect().top + window.scrollY) - hafePopup.getBoundingClientRect().height) + "px";
    hafePopup.style.left = link.getBoundingClientRect().left + "px";
  } else {
    hafePopup.style.display = 'none';
  }
}

function init() {
  for (var i=0; i < document.links.length; i++) {
    var link = document.links[i];
    /** Apparently deprecated. **/
    // link.onmouseover = aMouseOver;
    // link.onmouseout = aMouseOut;
    /** The new way: **/
    link.addEventListener("mouseover", aMouseOver, false);
    link.addEventListener("mouseout", aMouseOut, false);
    link.addEventListener("click", aClick, false);
    //addEvents(link);
    // link.addEventListener("mousemove", function(evt) { locate(evt); }, true);
  }
}

init();

var observer = new MutationObserver(init);

observer.observe(document.body, { subtree: true, childList: true });
//observer.disconnect();

// window.document.checkFocus = checkFocus;