From 4ab92a13a30677971938ad4a23e67d2b77b71da8 Mon Sep 17 00:00:00 2001 From: pipecraft Date: Thu, 30 Mar 2023 02:07:49 +0800 Subject: [PATCH] 0.0.2 --- build/userscript-prod/index.js | 264 +++++++++++++++++++++++++++++++++ package.json | 2 +- 2 files changed, 265 insertions(+), 1 deletion(-) create mode 100644 build/userscript-prod/index.js diff --git a/build/userscript-prod/index.js b/build/userscript-prod/index.js new file mode 100644 index 0000000..5255f2f --- /dev/null +++ b/build/userscript-prod/index.js @@ -0,0 +1,264 @@ +// ==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.2 +// @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 toCamelCase = function (text) { + return text.replace(/^([A-Z])|[\s-_](\w)/g, function (match, p1, p2) { + if (p2) return p2.toUpperCase() + return p1.toLowerCase() + }) + } + var $ = (element, selectors) => + typeof element === "object" + ? element.querySelector(selectors) + : doc.querySelector(element) + var $$ = (element, selectors) => + typeof element === "object" + ? [...element.querySelectorAll(selectors)] + : [...doc.querySelectorAll(element)] + var createElement = doc.createElement.bind(doc) + var addEventListener = (element, type, listener) => { + if (typeof type === "object") { + for (const type1 in type) { + if (Object.hasOwn(type, type1)) { + element.addEventListener(type1, type[type1]) + } + } + } else if (typeof type === "string" && typeof listener === "function") { + element.addEventListener(type, listener) + } + } + var getAttribute = (element, name) => element.getAttribute(name) + var setAttribute = (element, name, value) => element.setAttribute(name, value) + var setStyle = (element, values, overwrite) => { + const style = element.style + if (overwrite) { + if (typeof values === "string") { + style.cssText = values + return + } + style.cssText = "" + } + if (typeof values === "string") { + values = toStyleKeyValues(values) + } + for (const key in values) { + if (Object.hasOwn(values, key)) { + style[key] = values[key].replace("!important", "") + } + } + } + var toStyleKeyValues = (styleText) => { + const result = {} + const keyValues = styleText.split(/\s*;\s*/) + for (const keyValue of keyValues) { + const kv = keyValue.split(/\s*:\s*/) + const key = toCamelCase(kv[0]) + if (key) { + result[key] = kv[1] + } + } + return result + } + 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 style_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 > ul { list-style: none; padding: 0; margin: 0;}.hnas_wrapper > div.hnas_tooltip > ul > li { display: block;}.hnas_wrapper > div.hnas_tooltip > ul > li > a { text-decoration: none; color: black; padding: 5px; border-radius: 5px; display: flex; font-size: 1rem; line-height: 1.25rem;}.hnas_wrapper > div.hnas_tooltip > ul > li > a:hover { text-decoration: underline; color: black !important; background-color: #f3f4f6;}" + + var addValueChangeListener = GM_addValueChangeListener + + var apps = [ + "https://news.ycombinator.com/item?id=1234", + "https://hn.svelte.dev/item/1234", + // https://github.com/rocktimsaikia/hackernews-redesign + "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(style_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 ul = createElement("ul") + setStyle2(ul, ".hnas_wrapper > div.hnas_tooltip > ul") + for (const app of apps) { + const li = createElement("li") + setStyle2(li, "display: block;") + const link = createElement("a") + setStyle2(link, ".hnas_wrapper > div.hnas_tooltip > ul > li > 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 > ul > li > a", + true + ) + if (app === "Close") { + setStyle2(link, "color: #217dfc; cursor: pointer;") + } + }, + }) + li.append(link) + ul.append(li) + } + tooltip.append(ul) + } + if (tooltip.style.display === "block" && tooltip.parentNode === wrapper) { + return + } + for (const link of $$(tooltip, "ul li 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: top + height + "px", + left: left + "px", + width: 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() +})() diff --git a/package.json b/package.json index 84954bc..5bcc382 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "hacker-news-apps-switcher", "displayName": "Hacker News Apps Switcher", "displayName:zh-CN": "Hacker News 网站切换器", - "version": "0.0.1", + "version": "0.0.2", "description": "Open Hacker News links on the favorite apps", "description:zh-CN": "选择其他 HN 网站打开 Hacker News 链接", "author": "Pipecraft",