Hacker News Apps Switcher

Open Hacker News links on the favorite apps

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

You will need to install an extension such as Tampermonkey to install this 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                 Hacker News Apps Switcher
// @name:zh-CN           Hacker News 网站切换器
// @namespace            https://www.pipecraft.net/
// @homepage             https://github.com/dev-topics-only/hacker-news-apps-switcher#readme
// @supportURL           https://github.com/dev-topics-only/hacker-news-apps-switcher/issues
// @version              0.0.4
// @description          Open Hacker News links on the favorite apps
// @description:zh-CN    选择其他 HN 网站打开 Hacker News 链接
// @icon                 https://icons.pipecraft.net/favicons/64/news.ycombinator.com/favicon.ico
// @author               Pipecraft
// @license              MIT
// @match                https://*/*
// @match                http://*/*
// @grant                GM_setValue
// @grant                GM_getValue
// @grant                GM_addValueChangeListener
// ==/UserScript==
//
;(() => {
  "use strict"
  var doc = document
  var $ = (element, selectors) =>
    element && typeof element === "object"
      ? element.querySelector(selectors)
      : doc.querySelector(element)
  var $$ = (element, selectors) =>
    element && typeof element === "object"
      ? [...element.querySelectorAll(selectors)]
      : [...doc.querySelectorAll(element)]
  var createElement = (tagName, attributes) => {
    const element = doc.createElement(tagName)
    if (attributes) {
      for (const name in attributes) {
        if (Object.hasOwn(attributes, name)) {
          const value = attributes[name]
          if (name === "textContent") {
            element[name] = value
          } else if (name === "style") {
            setStyle(element, value)
          } else {
            setAttribute(element, name, value)
          }
        }
      }
    }
    return element
  }
  var addEventListener = (element, type, listener, options) => {
    if (!element) {
      return () => 0
    }
    if (typeof type === "object") {
      const removers = []
      for (const type1 in type) {
        if (Object.hasOwn(type, type1)) {
          element.addEventListener(type1, type[type1])
          removers.push(() => element.removeEventListener(type1, type[type1]))
        }
      }
      return () => {
        for (const remover of removers) remover()
      }
    }
    if (typeof type === "string" && typeof listener === "function") {
      element.addEventListener(type, listener, options)
      return () => {
        element.removeEventListener(type, listener, options)
      }
    }
  }
  var getAttribute = (element, name) =>
    element ? element.getAttribute(name) : null
  var setAttribute = (element, name, value) =>
    element ? element.setAttribute(name, value) : void 0
  var setStyle = (element, values, overwrite) => {
    if (!element) {
      return
    }
    const style = element.style
    if (typeof values === "string") {
      style.cssText = overwrite ? values : style.cssText + ";" + values
      return
    }
    if (overwrite) {
      style.cssText = ""
    }
    for (const key in values) {
      if (Object.hasOwn(values, key)) {
        style[key] = values[key].replace("!important", "")
      }
    }
  }
  var toStyleMap = (styleText) => {
    styleText = noStyleSpace(styleText)
    const map = {}
    const keyValues = styleText.split("}")
    for (const keyValue of keyValues) {
      const kv = keyValue.split("{")
      if (kv[0] && kv[1]) {
        map[kv[0]] = kv[1]
      }
    }
    return map
  }
  var noStyleSpace = (text) => text.replace(/\s*([^\w-!])\s*/gm, "$1")
  var createSetStyle = (styleText) => {
    const styleMap = toStyleMap(styleText)
    return (element, value, overwrite) => {
      if (typeof value === "object") {
        setStyle(element, value, overwrite)
      } else if (typeof value === "string") {
        const key = noStyleSpace(value)
        const value2 = styleMap[key]
        setStyle(element, value2 || value, overwrite)
      }
    }
  }
  if (typeof Object.hasOwn !== "function") {
    Object.hasOwn = (instance, prop) =>
      Object.prototype.hasOwnProperty.call(instance, prop)
  }
  var content_default =
    ".hnas_wrapper {  display: inline-block;}.hnas_wrapper > div.hnas_tooltip {  min-width: 250px;  display: none;  position: absolute;  top: 0px;  left: 0px;  box-sizing: border-box;  padding: 10px 15px;  background-color: white;  z-index: 100000;  border-radius: 5px;  -webkit-box-shadow: 0px 10px 39px 10px rgba(62, 66, 66, 0.22);  -moz-box-shadow: 0px 10px 39px 10px rgba(62, 66, 66, 0.22);  box-shadow: 0px 10px 39px 10px rgba(62, 66, 66, 0.22);}.hnas_wrapper > div.hnas_tooltip > div {  display: flex;  flex-direction: column;}.hnas_wrapper > div.hnas_tooltip > div > a {  text-decoration: none;  color: black;  padding: 5px;  border-radius: 5px;  border: none;  font-weight: normal;  font-size: 1rem;  line-height: 1.25rem;}.hnas_wrapper > div.hnas_tooltip > div > a:hover {  text-decoration: underline;  color: black !important;  background-color: #f3f4f6;}"
  var apps = [
    "https://news.ycombinator.com/item?id=1234",
    "https://hn.svelte.dev/item/1234",
    "https://hn-redesign.vercel.app/items/1234",
    "https://insin.github.io/react-hn/#/story/1234",
    "https://lotusreader.netlify.app/item/1234",
    "https://hackernewsmobile.com/#/comments/1234",
    "https://hackerweb.app/#/item/1234",
    "https://hn.premii.com/#/comments/1234",
    "https://whnex.com/items/1234",
    "https://hack.ernews.info/comments-for/1234",
    "https://hacker-news.news/post/1234",
    "Close",
  ]
  var setStyle2 = createSetStyle(content_default)
  var tooltip = null
  function toSiteName(url) {
    return /\/([^/]+)\//.exec(url)[1]
  }
  var handler = (event) => {
    let target = event.target
    const tooltip2 = $(".hnas_tooltip")
    if (tooltip2) {
      while (target !== tooltip2 && target) {
        target = target.parentNode
      }
      if (target === tooltip2) {
        event.preventDefault()
        return
      }
      tooltip2.style.display = "none"
    }
    document.removeEventListener("click", handler)
  }
  function displayTooltip(id, wrapper) {
    if (!tooltip) {
      tooltip = createElement("div")
      setStyle2(tooltip, ".hnas_wrapper > div.hnas_tooltip")
      setAttribute(tooltip, "class", "hnas_tooltip")
      const list = createElement("div")
      setStyle2(list, ".hnas_wrapper > div.hnas_tooltip > div")
      for (const app of apps) {
        const link = createElement("a")
        setStyle2(link, ".hnas_wrapper > div.hnas_tooltip > div > a")
        link.dataset.hnas_link = "1"
        if (app === "Close") {
          link.innerHTML = "Close"
          setStyle2(link, "color: #217dfc; cursor: pointer;")
        } else {
          setAttribute(link, "href", app)
          setAttribute(link, "target", "_blank")
          link.innerHTML = toSiteName(app)
        }
        addEventListener(link, {
          click(event) {
            const tooltip2 = $(".hnas_tooltip")
            if (tooltip2) {
              tooltip2.style.display = "none"
            }
            document.removeEventListener("click", handler)
            if (link.innerHTML === "Close") {
              event.preventDefault()
            }
          },
          mouseover() {
            setStyle2(
              link,
              "text-decoration: underline; background-color: #f3f4f6; color: black !important;"
            )
            if (app === "Close") {
              setStyle2(link, "color: #217dfc; cursor: pointer;")
            }
          },
          mouseout() {
            setStyle2(link, ".hnas_wrapper > div.hnas_tooltip > div > a", true)
            if (app === "Close") {
              setStyle2(link, "color: #217dfc; cursor: pointer;")
            }
          },
        })
        list.append(link)
      }
      tooltip.append(list)
    }
    if (tooltip.style.display === "block" && tooltip.parentNode === wrapper) {
      return
    }
    for (const link of $$(tooltip, "div a")) {
      const href = getAttribute(link, "href")
      if (href) {
        setAttribute(link, "href", href.replace(/\d+/, id))
      }
    }
    const linkElement = wrapper.previousSibling
    const width = linkElement.offsetWidth
    const height = linkElement.offsetHeight
    const top = linkElement.offsetTop
    const left = linkElement.offsetLeft
    wrapper.append(tooltip)
    setStyle2(tooltip, {
      display: "block",
      top: String(top + height) + "px",
      left: String(left) + "px",
      width: String(width) + "px",
    })
    document.removeEventListener("click", handler)
    setTimeout(() => {
      addEventListener(document, "click", handler)
    }, 100)
  }
  function updateLinks() {
    const links = $$(
      'a[href^="https://news.ycombinator.com/item?id="],a[href^="http://news.ycombinator.com/item?id="]'
    )
    for (const link of links) {
      if (link.dataset.hnas_binded || link.dataset.hnas_link) {
        continue
      }
      link.dataset.hnas_binded = "1"
      const wrapper = createElement("span")
      setAttribute(wrapper, "class", "hnas_wrapper")
      link.after(wrapper)
      const id = /id=(\d+)/.exec(getAttribute(link, "href"))[1]
      if (id) {
        addEventListener(link, "click", (event) => {
          event.preventDefault()
          displayTooltip(id, wrapper)
        })
      }
    }
  }
  function main() {
    if (!document.body) {
      return
    }
    setInterval(updateLinks, 1e3)
    updateLinks()
  }
  main()
})()