Hover Alternative Front-Ends

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

Tendrás que instalar una extensión para tu navegador como Tampermonkey, Greasemonkey o Violentmonkey si quieres utilizar este script.

Necesitarás instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Userscripts para instalar este script.

Necesitará instalar una extensión como Tampermonkey para instalar este script.

Necesitarás instalar una extensión para administrar scripts de usuario si quieres instalar este script.

(Ya tengo un administrador de scripts de usuario, déjame instalarlo)

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

(Ya tengo un administrador de estilos de usuario, déjame instalarlo)

// ==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;