forked from utags/browser-extension-starter
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
85085d0
commit 4ab92a1
Showing
2 changed files
with
265 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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() | ||
})() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters