Greasy Fork is available in English.
支持所有网站在新标签页中打开第三方网站链接(外链),在新标签页中打开符合指定规则的本站链接,解析文本链接为超链接,微信公众号文本转可点击的超链接,图片链接转图片标签,解析 Markdown 格式链接与图片标签,解析 BBCode 格式链接与图片标签
// ==UserScript==
// @name 🔗 Links Helper
// @name:zh-CN 🔗 链接助手
// @namespace https://github.com/utags/links-helper
// @homepageURL https://github.com/utags/links-helper#readme
// @supportURL https://github.com/utags/links-helper/issues
// @version 0.8.0
// @description Open external links in a new tab, open internal links matching the specified rules in a new tab, convert text to hyperlinks, convert image links to image tags(<img>), parse Markdown style links and image tags, parse BBCode style links and image tags
// @description:zh-CN 支持所有网站在新标签页中打开第三方网站链接(外链),在新标签页中打开符合指定规则的本站链接,解析文本链接为超链接,微信公众号文本转可点击的超链接,图片链接转图片标签,解析 Markdown 格式链接与图片标签,解析 BBCode 格式链接与图片标签
// @icon data:image/svg+xml;base64,PHN2ZyB3aWR0aD0nMTUnIGhlaWdodD0nMTUnIHZpZXdCb3g9JzAgMCAxNSAxNScgZmlsbD0nbm9uZScgeG1sbnM9J2h0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnJz48cGF0aCBkPSdNMyAyQzIuNDQ3NzIgMiAyIDIuNDQ3NzIgMiAzVjEyQzIgMTIuNTUyMyAyLjQ0NzcyIDEzIDMgMTNIMTJDMTIuNTUyMyAxMyAxMyAxMi41NTIzIDEzIDEyVjguNUMxMyA4LjIyMzg2IDEyLjc3NjEgOCAxMi41IDhDMTIuMjIzOSA4IDEyIDguMjIzODYgMTIgOC41VjEySDNWM0w2LjUgM0M2Ljc3NjE0IDMgNyAyLjc3NjE0IDcgMi41QzcgMi4yMjM4NiA2Ljc3NjE0IDIgNi41IDJIM1pNMTIuODUzNiAyLjE0NjQ1QzEyLjkwMTUgMi4xOTQzOSAxMi45Mzc3IDIuMjQ5NjQgMTIuOTYyMSAyLjMwODYxQzEyLjk4NjEgMi4zNjY2OSAxMi45OTk2IDIuNDMwMyAxMyAyLjQ5N0wxMyAyLjVWMi41MDA0OVY1LjVDMTMgNS43NzYxNCAxMi43NzYxIDYgMTIuNSA2QzEyLjIyMzkgNiAxMiA1Ljc3NjE0IDEyIDUuNVYzLjcwNzExTDYuODUzNTUgOC44NTM1NUM2LjY1ODI5IDkuMDQ4ODIgNi4zNDE3MSA5LjA0ODgyIDYuMTQ2NDUgOC44NTM1NUM1Ljk1MTE4IDguNjU4MjkgNS45NTExOCA4LjM0MTcxIDYuMTQ2NDUgOC4xNDY0NUwxMS4yOTI5IDNIOS41QzkuMjIzODYgMyA5IDIuNzc2MTQgOSAyLjVDOSAyLjIyMzg2IDkuMjIzODYgMiA5LjUgMkgxMi40OTk5SDEyLjVDMTIuNTY3OCAyIDEyLjYzMjQgMi4wMTM0OSAxMi42OTE0IDIuMDM3OTRDMTIuNzUwNCAyLjA2MjM0IDEyLjgwNTYgMi4wOTg1MSAxMi44NTM2IDIuMTQ2NDVaJyBmaWxsPSdjdXJyZW50Q29sb3InIGZpbGwtcnVsZT0nZXZlbm9kZCcgY2xpcC1ydWxlPSdldmVub2RkJz48L3BhdGg+PC9zdmc+
// @author Pipecraft
// @license MIT
// @match https://*/*
// @match http://*/*
// @exclude *://github.dev/*
// @exclude *://challenges.cloudflare.com/*
// @exclude *://js.stripe.com/*
// @exclude *://m.stripe.network/*
// @exclude *://www.googletagmanager.com/*
// @exclude *://googleads.g.doubleclick.net/*
// @exclude *://ep2.adtrafficquality.google/*
// @exclude *://www.google.com/recaptcha/*
// @run-at document-start
// @grant GM.getValue
// @grant GM.setValue
// @grant GM_addValueChangeListener
// @grant GM_removeValueChangeListener
// @grant GM_addElement
// @grant GM.registerMenuCommand
// ==/UserScript==
//
;(() => {
"use strict"
var __defProp = Object.defineProperty
var __getOwnPropSymbols = Object.getOwnPropertySymbols
var __hasOwnProp = Object.prototype.hasOwnProperty
var __propIsEnum = Object.prototype.propertyIsEnumerable
var __defNormalProp = (obj, key, value) =>
key in obj
? __defProp(obj, key, {
enumerable: true,
configurable: true,
writable: true,
value,
})
: (obj[key] = value)
var __spreadValues = (a, b) => {
for (var prop in b || (b = {}))
if (__hasOwnProp.call(b, prop)) __defNormalProp(a, prop, b[prop])
if (__getOwnPropSymbols)
for (var prop of __getOwnPropSymbols(b)) {
if (__propIsEnum.call(b, prop)) __defNormalProp(a, prop, b[prop])
}
return a
}
var availableLocales = ["en"]
var regexCache = /* @__PURE__ */ new Map()
function initAvailableLocales(array) {
availableLocales = array
.map((locale2) => locale2.trim().toLowerCase())
.filter(Boolean)
}
function isLocale(locale2) {
return locale2 ? availableLocales.includes(locale2.toLowerCase()) : false
}
function extractLocaleFromNavigator() {
if (typeof navigator === "undefined") {
return void 0
}
const languages = navigator.languages || [navigator.language]
for (const language of languages) {
const normalizedLang = language.toLowerCase()
const baseLang = normalizedLang.split("-")[0]
if (isLocale(normalizedLang)) {
return normalizedLang
}
if (baseLang && isLocale(baseLang)) {
return baseLang
}
}
return void 0
}
function getParameterRegex(index) {
const pattern = "\\{".concat(index, "\\}")
if (!regexCache.has(pattern)) {
regexCache.set(pattern, new RegExp(pattern, "g"))
}
return regexCache.get(pattern)
}
function initI18n(messageMaps, language) {
const validLanguage =
typeof language === "string" && language.trim() ? language.trim() : void 0
const targetLanguage = (validLanguage || getPrefferedLocale()).toLowerCase()
const baseLanguage = targetLanguage.split("-")[0]
const { mergedMessages } = resolveMessageMaps(
messageMaps,
targetLanguage,
baseLanguage
)
return function (key, ...parameters) {
const text = mergedMessages[key] || key
return parameters.length > 0 && text !== key
? interpolateParameters(text, parameters)
: text
}
}
function resolveMessageMaps(messageMaps, targetLanguage, baseLanguage) {
const normalizedMaps = Object.fromEntries(
Object.entries(messageMaps).map(([locale2, messages16]) => [
locale2.toLowerCase(),
messages16,
])
)
let mergedMessages = {}
const englishMessages = normalizedMaps.en || normalizedMaps["en-us"] || {}
mergedMessages = __spreadValues({}, englishMessages)
if (
isLocale(baseLanguage) &&
normalizedMaps[baseLanguage] &&
baseLanguage !== "en" &&
baseLanguage !== "en-us"
) {
mergedMessages = __spreadValues(
__spreadValues({}, mergedMessages),
normalizedMaps[baseLanguage]
)
}
if (
isLocale(targetLanguage) &&
normalizedMaps[targetLanguage] &&
targetLanguage !== baseLanguage
) {
mergedMessages = __spreadValues(
__spreadValues({}, mergedMessages),
normalizedMaps[targetLanguage]
)
}
return { mergedMessages }
}
function interpolateParameters(text, parameters) {
let result = text
for (const [i3, parameter] of parameters.entries()) {
const regex = getParameterRegex(i3 + 1)
result = result.replace(regex, String(parameter))
}
return result
}
function getPrefferedLocale() {
return extractLocaleFromNavigator() || "en"
}
var listeners = {}
var getValue = async (key) => {
const value = await GM.getValue(key)
return value && value !== "undefined" ? JSON.parse(value) : void 0
}
var setValue = async (key, value) => {
if (value !== void 0) {
const newValue = JSON.stringify(value)
if (listeners[key]) {
const oldValue = await GM.getValue(key)
await GM.setValue(key, newValue)
if (newValue !== oldValue) {
for (const func of listeners[key]) {
func(key, oldValue, newValue)
}
}
} else {
await GM.setValue(key, newValue)
}
}
}
var _addValueChangeListener = (key, func) => {
listeners[key] = listeners[key] || []
listeners[key].push(func)
return () => {
if (listeners[key] && listeners[key].length > 0) {
for (let i3 = listeners[key].length - 1; i3 >= 0; i3--) {
if (listeners[key][i3] === func) {
listeners[key].splice(i3, 1)
}
}
}
}
}
var addValueChangeListener = (key, func) => {
if (typeof GM_addValueChangeListener !== "function") {
console.warn("Do not support GM_addValueChangeListener!")
return _addValueChangeListener(key, func)
}
const listenerId = GM_addValueChangeListener(key, func)
return () => {
GM_removeValueChangeListener(listenerId)
}
}
var doc = document
if (typeof String.prototype.replaceAll !== "function") {
String.prototype.replaceAll = String.prototype.replace
}
var $ = (selectors, element) =>
(element || doc).querySelector(selectors) || void 0
var $$ = (selectors, element) => [
...(element || doc).querySelectorAll(selectors),
]
var getRootElement = (type) =>
type === 1
? doc.head || doc.body || doc.documentElement
: type === 2
? doc.body || doc.documentElement
: doc.documentElement
var createElement = (tagName, attributes) =>
setAttributes(doc.createElement(tagName), attributes)
var addElement = (parentNode, tagName, attributes) => {
if (typeof parentNode === "string") {
return addElement(null, parentNode, tagName)
}
if (!tagName) {
return
}
if (!parentNode) {
parentNode = /^(script|link|style|meta)$/.test(tagName)
? getRootElement(1)
: getRootElement(2)
}
if (typeof tagName === "string") {
const element = createElement(tagName, attributes)
parentNode.append(element)
return element
}
setAttributes(tagName, attributes)
parentNode.append(tagName)
return tagName
}
var addEventListener = (element, type, listener, options) => {
if (!element) {
return
}
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, options)
}
}
var removeEventListener = (element, type, listener, options) => {
if (!element) {
return
}
if (typeof type === "object") {
for (const type1 in type) {
if (Object.hasOwn(type, type1)) {
element.removeEventListener(type1, type[type1])
}
}
} else if (typeof type === "string" && typeof listener === "function") {
element.removeEventListener(type, listener, options)
}
}
var getAttribute = (element, name) =>
element && element.getAttribute ? element.getAttribute(name) : void 0
var setAttribute = (element, name, value) =>
element && element.setAttribute ? element.setAttribute(name, value) : void 0
var removeAttribute = (element, name) =>
element && element.removeAttribute ? element.removeAttribute(name) : void 0
var setAttributes = (element, attributes) => {
if (element && attributes) {
for (const name in attributes) {
if (Object.hasOwn(attributes, name)) {
const value = attributes[name]
if (value === void 0) {
continue
}
if (/^(value|textContent|innerText)$/.test(name)) {
element[name] = value
} else if (/^(innerHTML)$/.test(name)) {
element[name] = createHTML(value)
} else if (name === "style") {
setStyle(element, value, true)
} else if (/on\w+/.test(name)) {
const type = name.slice(2)
addEventListener(element, type, value)
} else {
setAttribute(element, name, value)
}
}
}
}
return element
}
var addAttribute = (element, name, value) => {
const orgValue = getAttribute(element, name)
if (!orgValue) {
setAttribute(element, name, value)
} else if (!orgValue.includes(value)) {
setAttribute(element, name, orgValue + " " + value)
}
}
var addClass = (element, className) => {
if (!element || !element.classList) {
return
}
element.classList.add(className)
}
var removeClass = (element, className) => {
if (!element || !element.classList) {
return
}
element.classList.remove(className)
}
var hasClass = (element, className) => {
if (!element || !element.classList) {
return false
}
return element.classList.contains(className)
}
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 throttle = (func, interval) => {
let timeoutId = null
let next = false
const handler = (...args) => {
if (timeoutId) {
next = true
} else {
func.apply(void 0, args)
timeoutId = setTimeout(() => {
timeoutId = null
if (next) {
next = false
handler()
}
}, interval)
}
}
return handler
}
if (typeof Object.hasOwn !== "function") {
Object.hasOwn = (instance, prop) =>
Object.prototype.hasOwnProperty.call(instance, prop)
}
var parseInt10 = (number, defaultValue) => {
if (typeof number === "number" && !Number.isNaN(number)) {
return number
}
if (typeof defaultValue !== "number") {
defaultValue = Number.NaN
}
if (!number) {
return defaultValue
}
const result = Number.parseInt(number, 10)
return Number.isNaN(result) ? defaultValue : result
}
var rootFuncArray = []
var headFuncArray = []
var bodyFuncArray = []
var headBodyObserver
var startObserveHeadBodyExists = () => {
if (headBodyObserver) {
return
}
headBodyObserver = new MutationObserver(() => {
if (doc.head && doc.body) {
headBodyObserver.disconnect()
}
if (doc.documentElement && rootFuncArray.length > 0) {
for (const func of rootFuncArray) {
func()
}
rootFuncArray.length = 0
}
if (doc.head && headFuncArray.length > 0) {
for (const func of headFuncArray) {
func()
}
headFuncArray.length = 0
}
if (doc.body && bodyFuncArray.length > 0) {
for (const func of bodyFuncArray) {
func()
}
bodyFuncArray.length = 0
}
})
headBodyObserver.observe(doc, {
childList: true,
subtree: true,
})
}
var runWhenHeadExists = (func) => {
if (!doc.head) {
headFuncArray.push(func)
startObserveHeadBodyExists()
return
}
func()
}
var runWhenBodyExists = (func) => {
if (!doc.body) {
bodyFuncArray.push(func)
startObserveHeadBodyExists()
return
}
func()
}
var escapeHTMLPolicy =
typeof trustedTypes !== "undefined" &&
typeof trustedTypes.createPolicy === "function"
? trustedTypes.createPolicy("beuEscapePolicy", {
createHTML: (string) => string,
})
: void 0
var createHTML = (html) => {
return escapeHTMLPolicy ? escapeHTMLPolicy.createHTML(html) : html
}
var addElement2 =
typeof GM_addElement === "function"
? (parentNode, tagName, attributes) => {
if (typeof parentNode === "string") {
return addElement2(null, parentNode, tagName)
}
if (!tagName) {
return
}
if (!parentNode) {
parentNode = /^(script|link|style|meta)$/.test(tagName)
? getRootElement(1)
: getRootElement(2)
}
if (typeof tagName === "string") {
let attributes2
if (attributes) {
const entries1 = []
const entries2 = []
for (const entry of Object.entries(attributes)) {
if (/^(on\w+|innerHTML)$/.test(entry[0])) {
entries2.push(entry)
} else {
entries1.push(entry)
}
}
attributes = Object.fromEntries(entries1)
attributes2 = Object.fromEntries(entries2)
}
const element = GM_addElement(null, tagName, attributes)
setAttributes(element, attributes2)
parentNode.append(element)
return element
}
setAttributes(tagName, attributes)
parentNode.append(tagName)
return tagName
}
: addElement
var addStyle = (styleText) =>
addElement2(null, "style", { textContent: styleText })
var registerMenuCommand = (name, callback, options) => {
if (globalThis !== top) {
return
}
if (typeof GM.registerMenuCommand !== "function") {
console.warn("Do not support GM.registerMenuCommand!")
return
}
return GM.registerMenuCommand(name, callback, options)
}
var style_default =
'#browser_extension_settings_container{--browser-extension-settings-background-color: #f2f2f7;--browser-extension-settings-text-color: #444444;--browser-extension-settings-link-color: #217dfc;--sb-track-color: #00000000;--sb-thumb-color: #33334480;--sb-size: 2px;--font-family: "helvetica neue", "microsoft yahei", arial, sans-serif;position:fixed;top:10px;right:30px;max-height:90%;height:600px;overflow:hidden;display:none;z-index:100000;border-radius:5px;-webkit-box-shadow:0px 10px 39px 10px rgba(62,66,66,.22);-moz-box-shadow:0px 10px 39px 10px rgba(62,66,66,.22);box-shadow:0px 10px 39px 10px rgba(62,66,66,.22) !important}#browser_extension_settings_container .browser_extension_settings_wrapper{display:flex;height:100%;overflow:hidden;background-color:var(--browser-extension-settings-background-color);font-family:var(--font-family)}#browser_extension_settings_container .browser_extension_settings_wrapper h1,#browser_extension_settings_container .browser_extension_settings_wrapper h2{border:none;color:var(--browser-extension-settings-text-color);padding:0;font-family:var(--font-family);line-height:normal;letter-spacing:normal}#browser_extension_settings_container .browser_extension_settings_wrapper h1{font-size:26px;font-weight:800;margin:18px 0}#browser_extension_settings_container .browser_extension_settings_wrapper h2{font-size:18px;font-weight:600;margin:14px 0}#browser_extension_settings_container .browser_extension_settings_wrapper footer{display:flex;justify-content:center;flex-direction:column;font-size:11px;margin:10px auto 0px;background-color:var(--browser-extension-settings-background-color);color:var(--browser-extension-settings-text-color);font-family:var(--font-family)}#browser_extension_settings_container .browser_extension_settings_wrapper footer a{color:var(--browser-extension-settings-link-color) !important;font-family:var(--font-family);text-decoration:none;padding:0}#browser_extension_settings_container .browser_extension_settings_wrapper footer p{text-align:center;padding:0;margin:2px;line-height:13px;font-size:11px;color:var(--browser-extension-settings-text-color);font-family:var(--font-family)}#browser_extension_settings_container .browser_extension_settings_wrapper a.navigation_go_previous{color:var(--browser-extension-settings-link-color);cursor:pointer;display:none}#browser_extension_settings_container .browser_extension_settings_wrapper a.navigation_go_previous::before{content:"< "}#browser_extension_settings_container .browser_extension_settings_wrapper .extension_list_container{overflow-x:auto;box-sizing:border-box;padding:10px 15px;background-color:var(--browser-extension-settings-background-color);color:var(--browser-extension-settings-text-color)}#browser_extension_settings_container .browser_extension_settings_wrapper .extension_list_container .installed_extension_list div,#browser_extension_settings_container .browser_extension_settings_wrapper .extension_list_container .related_extension_list div{background-color:#fff;font-size:14px;border-top:1px solid #ccc;padding:6px 15px 6px 15px}#browser_extension_settings_container .browser_extension_settings_wrapper .extension_list_container .installed_extension_list div a,#browser_extension_settings_container .browser_extension_settings_wrapper .extension_list_container .installed_extension_list div a:visited,#browser_extension_settings_container .browser_extension_settings_wrapper .extension_list_container .related_extension_list div a,#browser_extension_settings_container .browser_extension_settings_wrapper .extension_list_container .related_extension_list div a:visited{display:flex;justify-content:space-between;align-items:center;cursor:pointer;text-decoration:none;color:var(--browser-extension-settings-text-color);font-family:var(--font-family)}#browser_extension_settings_container .browser_extension_settings_wrapper .extension_list_container .installed_extension_list div a:hover,#browser_extension_settings_container .browser_extension_settings_wrapper .extension_list_container .installed_extension_list div a:visited:hover,#browser_extension_settings_container .browser_extension_settings_wrapper .extension_list_container .related_extension_list div a:hover,#browser_extension_settings_container .browser_extension_settings_wrapper .extension_list_container .related_extension_list div a:visited:hover{text-decoration:none;color:var(--browser-extension-settings-text-color)}#browser_extension_settings_container .browser_extension_settings_wrapper .extension_list_container .installed_extension_list div a span,#browser_extension_settings_container .browser_extension_settings_wrapper .extension_list_container .installed_extension_list div a:visited span,#browser_extension_settings_container .browser_extension_settings_wrapper .extension_list_container .related_extension_list div a span,#browser_extension_settings_container .browser_extension_settings_wrapper .extension_list_container .related_extension_list div a:visited span{margin-right:10px;line-height:24px;font-family:var(--font-family)}#browser_extension_settings_container .browser_extension_settings_wrapper .extension_list_container .installed_extension_list div.active,#browser_extension_settings_container .browser_extension_settings_wrapper .extension_list_container .installed_extension_list div:hover,#browser_extension_settings_container .browser_extension_settings_wrapper .extension_list_container .related_extension_list div.active,#browser_extension_settings_container .browser_extension_settings_wrapper .extension_list_container .related_extension_list div:hover{background-color:#e4e4e6}#browser_extension_settings_container .browser_extension_settings_wrapper .extension_list_container .installed_extension_list div.active a,#browser_extension_settings_container .browser_extension_settings_wrapper .extension_list_container .related_extension_list div.active a{cursor:default}#browser_extension_settings_container .browser_extension_settings_wrapper .extension_list_container .installed_extension_list div:first-of-type,#browser_extension_settings_container .browser_extension_settings_wrapper .extension_list_container .related_extension_list div:first-of-type{border-top:none;border-top-right-radius:10px;border-top-left-radius:10px}#browser_extension_settings_container .browser_extension_settings_wrapper .extension_list_container .installed_extension_list div:last-of-type,#browser_extension_settings_container .browser_extension_settings_wrapper .extension_list_container .related_extension_list div:last-of-type{border-bottom-right-radius:10px;border-bottom-left-radius:10px}#browser_extension_settings_container .thin_scrollbar{scrollbar-color:var(--sb-thumb-color) var(--sb-track-color);scrollbar-width:thin}#browser_extension_settings_container .thin_scrollbar::-webkit-scrollbar{width:var(--sb-size)}#browser_extension_settings_container .thin_scrollbar::-webkit-scrollbar-track{background:var(--sb-track-color);border-radius:10px}#browser_extension_settings_container .thin_scrollbar::-webkit-scrollbar-thumb{background:var(--sb-thumb-color);border-radius:10px}#browser_extension_settings_main{min-width:250px;overflow-y:auto;overflow-x:hidden;box-sizing:border-box;padding:10px 15px;background-color:var(--browser-extension-settings-background-color);color:var(--browser-extension-settings-text-color);font-family:var(--font-family)}#browser_extension_settings_main h2{text-align:center;margin:5px 0 0}#browser_extension_settings_main .option_groups{background-color:#fff;padding:6px 15px 6px 15px;border-radius:10px;display:flex;flex-direction:column;margin:10px 0 0}#browser_extension_settings_main .option_groups .action{font-size:14px;padding:6px 0 6px 0;color:var(--browser-extension-settings-link-color);cursor:pointer}#browser_extension_settings_main .bes_external_link{font-size:14px;padding:6px 0 6px 0}#browser_extension_settings_main .bes_external_link a,#browser_extension_settings_main .bes_external_link a:visited,#browser_extension_settings_main .bes_external_link a:hover{color:var(--browser-extension-settings-link-color);font-family:var(--font-family);text-decoration:none;cursor:pointer}#browser_extension_settings_main .option_groups textarea{font-size:12px;margin:10px 0 10px 0;height:100px;width:100%;border:1px solid #a9a9a9;border-radius:4px;box-sizing:border-box}#browser_extension_settings_main .switch_option,#browser_extension_settings_main .select_option{display:flex;justify-content:space-between;align-items:center;padding:6px 0 6px 0;font-size:14px}#browser_extension_settings_main .option_groups>*{border-top:1px solid #ccc}#browser_extension_settings_main .option_groups>*:first-child{border-top:none}#browser_extension_settings_main .bes_option>.bes_icon{width:24px;height:24px;margin-right:10px}#browser_extension_settings_main .bes_option>.bes_title{margin-right:10px;flex-grow:1}#browser_extension_settings_main .bes_option>.bes_select{box-sizing:border-box;background-color:#fff;height:24px;padding:0 2px 0 2px;margin:0;border-radius:6px;border:1px solid #ccc}#browser_extension_settings_main .option_groups .bes_tip{position:relative;margin:0;padding:0 15px 0 0;border:none;max-width:none;font-size:14px}#browser_extension_settings_main .option_groups .bes_tip .bes_tip_anchor{cursor:help;text-decoration:underline}#browser_extension_settings_main .option_groups .bes_tip .bes_tip_content{position:absolute;bottom:15px;left:0;background-color:#fff;color:var(--browser-extension-settings-text-color);text-align:left;padding:10px;display:none;border-radius:5px;-webkit-box-shadow:0px 10px 39px 10px rgba(62,66,66,.22);-moz-box-shadow:0px 10px 39px 10px rgba(62,66,66,.22);box-shadow:0px 10px 39px 10px rgba(62,66,66,.22) !important}#browser_extension_settings_main .option_groups .bes_tip .bes_tip_anchor:hover+.bes_tip_content,#browser_extension_settings_main .option_groups .bes_tip .bes_tip_content:hover{display:block}#browser_extension_settings_main .option_groups .bes_tip p,#browser_extension_settings_main .option_groups .bes_tip pre{margin:revert;padding:revert}#browser_extension_settings_main .option_groups .bes_tip pre{font-family:Consolas,panic sans,bitstream vera sans mono,Menlo,microsoft yahei,monospace;font-size:13px;letter-spacing:.015em;line-height:120%;white-space:pre;overflow:auto;background-color:#f5f5f5;word-break:normal;overflow-wrap:normal;padding:.5em;border:none}#browser_extension_settings_main .bes_switch_container{--button-width: 51px;--button-height: 24px;--toggle-diameter: 20px;--color-off: #e9e9eb;--color-on: #34c759;width:var(--button-width);height:var(--button-height);position:relative;padding:0;margin:0;flex:none;user-select:none}#browser_extension_settings_main input[type=checkbox]{opacity:0;width:0;height:0;position:absolute}#browser_extension_settings_main .bes_switch{width:100%;height:100%;display:block;background-color:var(--color-off);border-radius:calc(var(--button-height)/2);border:none;cursor:pointer;transition:all .2s ease-out}#browser_extension_settings_main .bes_switch::before{display:none}#browser_extension_settings_main .bes_slider{width:var(--toggle-diameter);height:var(--toggle-diameter);position:absolute;left:2px;top:calc(50% - var(--toggle-diameter)/2);border-radius:50%;background:#fff;box-shadow:0px 3px 8px rgba(0,0,0,.15),0px 3px 1px rgba(0,0,0,.06);transition:all .2s ease-out;cursor:pointer}#browser_extension_settings_main input[type=checkbox]:checked+.bes_switch{background-color:var(--color-on)}#browser_extension_settings_main input[type=checkbox]:checked+.bes_switch .bes_slider{left:calc(var(--button-width) - var(--toggle-diameter) - 2px)}#browser_extension_side_menu{min-height:80px;width:30px;opacity:0;position:fixed;top:80px;right:0;padding-top:20px;z-index:10000}#browser_extension_side_menu:hover{opacity:1}#browser_extension_side_menu button{cursor:pointer;width:24px;height:24px;padding:0;border:none;background-color:rgba(0,0,0,0);background-image:none}#browser_extension_side_menu button svg{width:24px;height:24px}#browser_extension_side_menu button:hover{opacity:70%}#browser_extension_side_menu button:active{opacity:100%}@media(max-width: 500px){#browser_extension_settings_container{right:10px}#browser_extension_settings_container .browser_extension_settings_wrapper a.navigation_go_previous{display:block}#browser_extension_settings_container .browser_extension_settings_wrapper .extension_list_container{display:none}#browser_extension_settings_container .browser_extension_settings_wrapper .extension_list_container.bes_active{display:block}#browser_extension_settings_container .browser_extension_settings_wrapper .extension_list_container.bes_active+div{display:none}}'
function createSwitch(options = {}) {
const container = createElement("label", { class: "bes_switch_container" })
const checkbox = createElement(
"input",
options.checked ? { type: "checkbox", checked: "" } : { type: "checkbox" }
)
addElement2(container, checkbox)
const switchElm = createElement("span", { class: "bes_switch" })
addElement2(switchElm, "span", { class: "bes_slider" })
addElement2(container, switchElm)
if (options.onchange) {
addEventListener(checkbox, "change", options.onchange)
}
return container
}
function createSwitchOption(icon, text, options) {
if (typeof text !== "string") {
return createSwitchOption(void 0, icon, text)
}
const div = createElement("div", { class: "switch_option bes_option" })
if (icon) {
addElement2(div, "img", { src: icon, class: "bes_icon" })
}
addElement2(div, "span", { textContent: text, class: "bes_title" })
div.append(createSwitch(options))
return div
}
var besVersion = 62
var messages = {
"settings.title": "Settings",
"settings.otherExtensions": "Other Extensions",
"settings.locale": "Language",
"settings.systemLanguage": "System Language",
"settings.displaySettingsButtonInSideMenu":
"Display Settings Button in Side Menu",
"settings.menu.settings": "\u2699\uFE0F Settings",
"settings.extensions.utags.title":
"\u{1F3F7}\uFE0F UTags - Add usertags to links",
"settings.extensions.links-helper.title": "\u{1F517} Links Helper",
"settings.extensions.v2ex.rep.title":
"V2EX.REP - \u4E13\u6CE8\u63D0\u5347 V2EX \u4E3B\u9898\u56DE\u590D\u6D4F\u89C8\u4F53\u9A8C",
"settings.extensions.v2ex.min.title":
"v2ex.min - V2EX Minimalist (\u6781\u7B80\u98CE\u683C)",
"settings.extensions.replace-ugly-avatars.title": "Replace Ugly Avatars",
"settings.extensions.more-by-pipecraft.title":
"Find more useful userscripts",
}
var en_default = messages
var messages2 = {
"settings.title": "\u8BBE\u7F6E",
"settings.otherExtensions": "\u5176\u4ED6\u6269\u5C55",
"settings.locale": "\u8BED\u8A00",
"settings.systemLanguage": "\u7CFB\u7EDF\u8BED\u8A00",
"settings.displaySettingsButtonInSideMenu":
"\u5728\u4FA7\u8FB9\u83DC\u5355\u4E2D\u663E\u793A\u8BBE\u7F6E\u6309\u94AE",
"settings.menu.settings": "\u2699\uFE0F \u8BBE\u7F6E",
"settings.extensions.utags.title":
"\u{1F3F7}\uFE0F \u5C0F\u9C7C\u6807\u7B7E (UTags) - \u4E3A\u94FE\u63A5\u6DFB\u52A0\u7528\u6237\u6807\u7B7E",
"settings.extensions.links-helper.title":
"\u{1F517} \u94FE\u63A5\u52A9\u624B",
"settings.extensions.v2ex.rep.title":
"V2EX.REP - \u4E13\u6CE8\u63D0\u5347 V2EX \u4E3B\u9898\u56DE\u590D\u6D4F\u89C8\u4F53\u9A8C",
"settings.extensions.v2ex.min.title":
"v2ex.min - V2EX \u6781\u7B80\u98CE\u683C",
"settings.extensions.replace-ugly-avatars.title":
"\u8D50\u4F60\u4E2A\u5934\u50CF\u5427",
"settings.extensions.more-by-pipecraft.title":
"\u66F4\u591A\u6709\u8DA3\u7684\u811A\u672C",
}
var zh_cn_default = messages2
var messages3 = {
"settings.title": "\u8A2D\u5B9A",
"settings.otherExtensions": "\u5176\u4ED6\u64F4\u5145\u529F\u80FD",
"settings.locale": "\u8A9E\u8A00",
"settings.systemLanguage": "\u7CFB\u7D71\u8A9E\u8A00",
"settings.displaySettingsButtonInSideMenu":
"\u5728\u5074\u908A\u9078\u55AE\u4E2D\u986F\u793A\u8A2D\u5B9A\u6309\u9215",
"settings.menu.settings": "\u2699\uFE0F \u8A2D\u5B9A",
"settings.extensions.utags.title":
"\u{1F3F7}\uFE0F \u5C0F\u9B5A\u6A19\u7C64 (UTags) - \u70BA\u9023\u7D50\u6DFB\u52A0\u7528\u6236\u6A19\u7C64",
"settings.extensions.links-helper.title":
"\u{1F517} \u9023\u7D50\u52A9\u624B",
"settings.extensions.v2ex.rep.title":
"V2EX.REP - \u4E13\u6CE8\u63D0\u5347 V2EX \u4E3B\u9898\u56DE\u590D\u6D4F\u89C8\u4F53\u9A8C",
"settings.extensions.v2ex.min.title":
"v2ex.min - V2EX \u6975\u7C21\u98A8\u683C",
"settings.extensions.replace-ugly-avatars.title":
"\u8CDC\u4F60\u500B\u982D\u50CF\u5427",
"settings.extensions.more-by-pipecraft.title":
"\u66F4\u591A\u6709\u8DA3\u7684\u8173\u672C",
}
var zh_hk_default = messages3
var messages4 = {
"settings.title": "\u8A2D\u5B9A",
"settings.otherExtensions": "\u5176\u4ED6\u64F4\u5145\u529F\u80FD",
"settings.locale": "\u8A9E\u8A00",
"settings.systemLanguage": "\u7CFB\u7D71\u8A9E\u8A00",
"settings.displaySettingsButtonInSideMenu":
"\u5728\u5074\u908A\u9078\u55AE\u4E2D\u986F\u793A\u8A2D\u5B9A\u6309\u9215",
"settings.menu.settings": "\u2699\uFE0F \u8A2D\u5B9A",
"settings.extensions.utags.title":
"\u{1F3F7}\uFE0F \u5C0F\u9B5A\u6A19\u7C64 (UTags) - \u70BA\u9023\u7D50\u65B0\u589E\u4F7F\u7528\u8005\u6A19\u7C64",
"settings.extensions.links-helper.title":
"\u{1F517} \u9023\u7D50\u52A9\u624B",
"settings.extensions.v2ex.rep.title":
"V2EX.REP - \u4E13\u6CE8\u63D0\u5347 V2EX \u4E3B\u9898\u56DE\u590D\u6D4F\u89C8\u4F53\u9A8C",
"settings.extensions.v2ex.min.title":
"v2ex.min - V2EX \u6975\u7C21\u98A8\u683C",
"settings.extensions.replace-ugly-avatars.title":
"\u66FF\u63DB\u919C\u964B\u7684\u982D\u50CF",
"settings.extensions.more-by-pipecraft.title":
"\u66F4\u591A\u6709\u8DA3\u7684\u8173\u672C",
}
var zh_tw_default = messages4
var messages5 = {
"settings.title": "\u041D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0438",
"settings.otherExtensions":
"\u0414\u0440\u0443\u0433\u0438\u0435 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043D\u0438\u044F",
"settings.locale": "\u042F\u0437\u044B\u043A",
"settings.systemLanguage":
"\u0421\u0438\u0441\u0442\u0435\u043C\u043D\u044B\u0439 \u044F\u0437\u044B\u043A",
"settings.displaySettingsButtonInSideMenu":
"\u041F\u043E\u043A\u0430\u0437\u0430\u0442\u044C \u043A\u043D\u043E\u043F\u043A\u0443 \u043D\u0430\u0441\u0442\u0440\u043E\u0435\u043A \u0432 \u0431\u043E\u043A\u043E\u0432\u043E\u043C \u043C\u0435\u043D\u044E",
"settings.menu.settings":
"\u2699\uFE0F \u041D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0438",
"settings.extensions.utags.title":
"\u{1F3F7}\uFE0F UTags - \u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044C\u0441\u043A\u0438\u0435 \u0442\u0435\u0433\u0438 \u043A \u0441\u0441\u044B\u043B\u043A\u0430\u043C",
"settings.extensions.links-helper.title":
"\u{1F517} \u041F\u043E\u043C\u043E\u0449\u043D\u0438\u043A \u0441\u0441\u044B\u043B\u043E\u043A",
"settings.extensions.v2ex.rep.title":
"V2EX.REP - \u4E13\u6CE8\u63D0\u5347 V2EX \u4E3B\u9898\u56DE\u590D\u6D4F\u89C8\u4F53\u9A8C",
"settings.extensions.v2ex.min.title":
"v2ex.min - V2EX \u041C\u0438\u043D\u0438\u043C\u0430\u043B\u0438\u0441\u0442\u0438\u0447\u043D\u044B\u0439 \u0441\u0442\u0438\u043B\u044C",
"settings.extensions.replace-ugly-avatars.title":
"\u0417\u0430\u043C\u0435\u043D\u0438\u0442\u044C \u043D\u0435\u043A\u0440\u0430\u0441\u0438\u0432\u044B\u0435 \u0430\u0432\u0430\u0442\u0430\u0440\u044B",
"settings.extensions.more-by-pipecraft.title":
"\u041D\u0430\u0439\u0442\u0438 \u0431\u043E\u043B\u044C\u0448\u0435 \u043F\u043E\u043B\u0435\u0437\u043D\u044B\u0445 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044C\u0441\u043A\u0438\u0445 \u0441\u043A\u0440\u0438\u043F\u0442\u043E\u0432",
}
var ru_default = messages5
var messages6 = {
"settings.title": "\uC124\uC815",
"settings.otherExtensions":
"\uAE30\uD0C0 \uD655\uC7A5 \uD504\uB85C\uADF8\uB7A8",
"settings.locale": "\uC5B8\uC5B4",
"settings.systemLanguage": "\uC2DC\uC2A4\uD15C \uC5B8\uC5B4",
"settings.displaySettingsButtonInSideMenu":
"\uC0AC\uC774\uB4DC \uBA54\uB274\uC5D0 \uC124\uC815 \uBC84\uD2BC \uD45C\uC2DC",
"settings.menu.settings": "\u2699\uFE0F \uC124\uC815",
"settings.extensions.utags.title":
"\u{1F3F7}\uFE0F UTags - \uB9C1\uD06C\uC5D0 \uC0AC\uC6A9\uC790 \uD0DC\uADF8 \uCD94\uAC00",
"settings.extensions.links-helper.title":
"\u{1F517} \uB9C1\uD06C \uB3C4\uC6B0\uBBF8",
"settings.extensions.v2ex.rep.title":
"V2EX.REP - \u4E13\u6CE8\u63D0\u5347 V2EX \u4E3B\u9898\u56DE\u590D\u6D4F\u89C8\u4F53\u9A8C",
"settings.extensions.v2ex.min.title":
"v2ex.min - V2EX \uBBF8\uB2C8\uBA40 \uC2A4\uD0C0\uC77C",
"settings.extensions.replace-ugly-avatars.title":
"\uBABB\uC0DD\uAE34 \uC544\uBC14\uD0C0 \uAD50\uCCB4",
"settings.extensions.more-by-pipecraft.title":
"\uB354 \uC720\uC6A9\uD55C \uC0AC\uC6A9\uC790 \uC2A4\uD06C\uB9BD\uD2B8 \uCC3E\uAE30",
}
var ko_default = messages6
var messages7 = {
"settings.title": "\u8A2D\u5B9A",
"settings.otherExtensions":
"\u305D\u306E\u4ED6\u306E\u62E1\u5F35\u6A5F\u80FD",
"settings.locale": "\u8A00\u8A9E",
"settings.systemLanguage": "\u30B7\u30B9\u30C6\u30E0\u8A00\u8A9E",
"settings.displaySettingsButtonInSideMenu":
"\u30B5\u30A4\u30C9\u30E1\u30CB\u30E5\u30FC\u306B\u8A2D\u5B9A\u30DC\u30BF\u30F3\u3092\u8868\u793A",
"settings.menu.settings": "\u2699\uFE0F \u8A2D\u5B9A",
"settings.extensions.utags.title":
"\u{1F3F7}\uFE0F UTags - \u30EA\u30F3\u30AF\u306B\u30E6\u30FC\u30B6\u30FC\u30BF\u30B0\u3092\u8FFD\u52A0",
"settings.extensions.links-helper.title":
"\u{1F517} \u30EA\u30F3\u30AF\u30D8\u30EB\u30D1\u30FC",
"settings.extensions.v2ex.rep.title":
"V2EX.REP - \u4E13\u6CE8\u63D0\u5347 V2EX \u4E3B\u9898\u56DE\u590D\u6D4F\u89C8\u4F53\u9A8C",
"settings.extensions.v2ex.min.title":
"v2ex.min - V2EX \u30DF\u30CB\u30DE\u30EB\u30B9\u30BF\u30A4\u30EB",
"settings.extensions.replace-ugly-avatars.title":
"\u919C\u3044\u30A2\u30D0\u30BF\u30FC\u3092\u7F6E\u304D\u63DB\u3048\u308B",
"settings.extensions.more-by-pipecraft.title":
"\u3088\u308A\u4FBF\u5229\u306A\u30E6\u30FC\u30B6\u30FC\u30B9\u30AF\u30EA\u30D7\u30C8\u3092\u898B\u3064\u3051\u308B",
}
var ja_default = messages7
var messages8 = {
"settings.title": "Param\xE8tres",
"settings.otherExtensions": "Autres extensions",
"settings.locale": "Langue",
"settings.systemLanguage": "Langue du syst\xE8me",
"settings.displaySettingsButtonInSideMenu":
"Afficher le bouton de param\xE8tres dans le menu lat\xE9ral",
"settings.menu.settings": "\u2699\uFE0F Param\xE8tres",
"settings.extensions.utags.title":
"\u{1F3F7}\uFE0F UTags - Ajouter des balises utilisateur aux liens",
"settings.extensions.links-helper.title": "\u{1F517} Assistant de liens",
"settings.extensions.v2ex.rep.title":
"V2EX.REP - \u4E13\u6CE8\u63D0\u5347 V2EX \u4E3B\u9898\u56DE\u590D\u6D4F\u89C8\u4F53\u9A8C",
"settings.extensions.v2ex.min.title": "v2ex.min - Style minimaliste V2EX",
"settings.extensions.replace-ugly-avatars.title":
"Remplacer les avatars laids",
"settings.extensions.more-by-pipecraft.title":
"Trouver plus de scripts utilisateur utiles",
}
var fr_default = messages8
var messages9 = {
"settings.title": "Einstellungen",
"settings.otherExtensions": "Andere Erweiterungen",
"settings.locale": "Sprache",
"settings.systemLanguage": "Systemsprache",
"settings.displaySettingsButtonInSideMenu":
"Einstellungsschaltfl\xE4che im Seitenmen\xFC anzeigen",
"settings.menu.settings": "\u2699\uFE0F Einstellungen",
"settings.extensions.utags.title":
"\u{1F3F7}\uFE0F UTags - Benutzer-Tags zu Links hinzuf\xFCgen",
"settings.extensions.links-helper.title": "\u{1F517} Link-Assistent",
"settings.extensions.v2ex.rep.title":
"V2EX.REP - \u4E13\u6CE8\u63D0\u5347 V2EX \u4E3B\u9898\u56DE\u590D\u6D4F\u89C8\u4F53\u9A8C",
"settings.extensions.v2ex.min.title":
"v2ex.min - V2EX Minimalistischer Stil",
"settings.extensions.replace-ugly-avatars.title":
"H\xE4ssliche Avatare ersetzen",
"settings.extensions.more-by-pipecraft.title":
"Weitere n\xFCtzliche Benutzerskripte finden",
}
var de_default = messages9
var messages10 = {
"settings.title": "Impostazioni",
"settings.otherExtensions": "Altre estensioni",
"settings.locale": "Lingua",
"settings.systemLanguage": "Lingua del sistema",
"settings.displaySettingsButtonInSideMenu":
"Mostra pulsante impostazioni nel menu laterale",
"settings.menu.settings": "\u2699\uFE0F Impostazioni",
"settings.extensions.utags.title":
"\u{1F3F7}\uFE0F UTags - Aggiungi tag utente ai collegamenti",
"settings.extensions.links-helper.title":
"\u{1F517} Assistente collegamenti",
"settings.extensions.v2ex.rep.title":
"V2EX.REP - \u4E13\u6CE8\u63D0\u5347 V2EX \u4E3B\u9898\u56DE\u590D\u6D4F\u89C8\u4F53\u9A8C",
"settings.extensions.v2ex.min.title": "v2ex.min - Stile minimalista V2EX",
"settings.extensions.replace-ugly-avatars.title":
"Sostituisci avatar brutti",
"settings.extensions.more-by-pipecraft.title":
"Trova pi\xF9 script utente utili",
}
var it_default = messages10
var messages11 = {
"settings.title": "Configuraci\xF3n",
"settings.otherExtensions": "Otras extensiones",
"settings.locale": "Idioma",
"settings.systemLanguage": "Idioma del sistema",
"settings.displaySettingsButtonInSideMenu":
"Mostrar bot\xF3n de configuraci\xF3n en el men\xFA lateral",
"settings.menu.settings": "\u2699\uFE0F Configuraci\xF3n",
"settings.extensions.utags.title":
"\u{1F3F7}\uFE0F UTags - Agregar etiquetas de usuario a los enlaces",
"settings.extensions.links-helper.title": "\u{1F517} Asistente de enlaces",
"settings.extensions.v2ex.rep.title":
"V2EX.REP - \u4E13\u6CE8\u63D0\u5347 V2EX \u4E3B\u9898\u56DE\u590D\u6D4F\u89C8\u4F53\u9A8C",
"settings.extensions.v2ex.min.title": "v2ex.min - Estilo minimalista V2EX",
"settings.extensions.replace-ugly-avatars.title":
"Reemplazar avatares feos",
"settings.extensions.more-by-pipecraft.title":
"Encontrar m\xE1s scripts de usuario \xFAtiles",
}
var es_default = messages11
var messages12 = {
"settings.title": "Configura\xE7\xF5es",
"settings.otherExtensions": "Outras extens\xF5es",
"settings.locale": "Idioma",
"settings.systemLanguage": "Idioma do sistema",
"settings.displaySettingsButtonInSideMenu":
"Exibir bot\xE3o de configura\xE7\xF5es no menu lateral",
"settings.menu.settings": "\u2699\uFE0F Configura\xE7\xF5es",
"settings.extensions.utags.title":
"\u{1F3F7}\uFE0F UTags - Adicionar tags de usu\xE1rio aos links",
"settings.extensions.links-helper.title": "\u{1F517} Assistente de links",
"settings.extensions.v2ex.rep.title":
"V2EX.REP - \u4E13\u6CE8\u63D0\u5347 V2EX \u4E3B\u9898\u56DE\u590D\u6D4F\u89C8\u4F53\u9A8C",
"settings.extensions.v2ex.min.title": "v2ex.min - Estilo minimalista V2EX",
"settings.extensions.replace-ugly-avatars.title":
"Substituir avatares feios",
"settings.extensions.more-by-pipecraft.title":
"Encontrar mais scripts de usu\xE1rio \xFAteis",
}
var pt_default = messages12
var messages13 = {
"settings.title": "C\xE0i \u0111\u1EB7t",
"settings.otherExtensions": "Ti\u1EC7n \xEDch m\u1EDF r\u1ED9ng kh\xE1c",
"settings.locale": "Ng\xF4n ng\u1EEF",
"settings.systemLanguage": "Ng\xF4n ng\u1EEF h\u1EC7 th\u1ED1ng",
"settings.displaySettingsButtonInSideMenu":
"Hi\u1EC3n th\u1ECB n\xFAt c\xE0i \u0111\u1EB7t trong menu b\xEAn",
"settings.menu.settings": "\u2699\uFE0F C\xE0i \u0111\u1EB7t",
"settings.extensions.utags.title":
"\u{1F3F7}\uFE0F UTags - Th\xEAm th\u1EBB ng\u01B0\u1EDDi d\xF9ng v\xE0o li\xEAn k\u1EBFt",
"settings.extensions.links-helper.title":
"\u{1F517} Tr\u1EE3 l\xFD li\xEAn k\u1EBFt",
"settings.extensions.v2ex.rep.title":
"V2EX.REP - \u4E13\u6CE8\u63D0\u5347 V2EX \u4E3B\u9898\u56DE\u590D\u6D4F\u89C8\u4F53\u9A8C",
"settings.extensions.v2ex.min.title":
"v2ex.min - Phong c\xE1ch t\u1ED1i gi\u1EA3n V2EX",
"settings.extensions.replace-ugly-avatars.title":
"Thay th\u1EBF avatar x\u1EA5u",
"settings.extensions.more-by-pipecraft.title":
"T\xECm th\xEAm script ng\u01B0\u1EDDi d\xF9ng h\u1EEFu \xEDch",
}
var vi_default = messages13
var localeMap = {
en: en_default,
"en-us": en_default,
zh: zh_cn_default,
"zh-cn": zh_cn_default,
"zh-hk": zh_hk_default,
"zh-tw": zh_tw_default,
ru: ru_default,
"ru-ru": ru_default,
ko: ko_default,
"ko-kr": ko_default,
ja: ja_default,
"ja-jp": ja_default,
fr: fr_default,
"fr-fr": fr_default,
de: de_default,
"de-de": de_default,
it: it_default,
"it-it": it_default,
es: es_default,
"es-es": es_default,
pt: pt_default,
"pt-pt": pt_default,
"pt-br": pt_default,
vi: vi_default,
"vi-vn": vi_default,
}
var localeNames = {
en: "English",
"en-us": "English (US)",
zh: "\u4E2D\u6587",
"zh-cn": "\u4E2D\u6587 (\u7B80\u4F53)",
"zh-hk": "\u4E2D\u6587 (\u9999\u6E2F)",
"zh-tw": "\u4E2D\u6587 (\u53F0\u7063)",
ru: "\u0420\u0443\u0441\u0441\u043A\u0438\u0439",
"ru-ru": "\u0420\u0443\u0441\u0441\u043A\u0438\u0439",
ko: "\uD55C\uAD6D\uC5B4",
"ko-kr": "\uD55C\uAD6D\uC5B4",
ja: "\u65E5\u672C\u8A9E",
"ja-jp": "\u65E5\u672C\u8A9E",
fr: "Fran\xE7ais",
"fr-fr": "Fran\xE7ais",
de: "Deutsch",
"de-de": "Deutsch",
it: "Italiano",
"it-it": "Italiano",
es: "Espa\xF1ol",
"es-es": "Espa\xF1ol",
pt: "Portugu\xEAs",
"pt-pt": "Portugu\xEAs",
"pt-br": "Portugu\xEAs (Brasil)",
vi: "Ti\u1EBFng Vi\u1EC7t",
"vi-vn": "Ti\u1EBFng Vi\u1EC7t",
}
var locales = Object.keys(localeMap)
initAvailableLocales(locales)
console.log("[settings] prefferedLocale:", getPrefferedLocale())
var i = initI18n(localeMap, getPrefferedLocale())
function resetI18n(locale2) {
console.log(
"[settings] prefferedLocale:",
getPrefferedLocale(),
"locale:",
locale2
)
i = initI18n(localeMap, locale2 || getPrefferedLocale())
}
var lang = navigator.language
var locale
if (lang === "zh-TW" || lang === "zh-HK") {
locale = "zh-TW"
} else if (lang.includes("zh")) {
locale = "zh-CN"
} else {
locale = "en"
}
var relatedExtensions = [
{
id: "utags",
title: i("settings.extensions.utags.title"),
url: "http://greasyfork.icu/".concat(
locale,
"/scripts/460718-utags-add-usertags-to-links"
),
},
{
id: "links-helper",
title: i("settings.extensions.links-helper.title"),
description:
"\u5728\u65B0\u6807\u7B7E\u9875\u4E2D\u6253\u5F00\u7B2C\u4E09\u65B9\u7F51\u7AD9\u94FE\u63A5\uFF0C\u56FE\u7247\u94FE\u63A5\u8F6C\u56FE\u7247\u6807\u7B7E\u7B49",
url: "http://greasyfork.icu/".concat(
locale,
"/scripts/464541-links-helper"
),
},
{
id: "v2ex.rep",
title: i("settings.extensions.v2ex.rep.title"),
url: "http://greasyfork.icu/".concat(
locale,
"/scripts/466589-v2ex-rep-%E4%B8%93%E6%B3%A8%E6%8F%90%E5%8D%87-v2ex-%E4%B8%BB%E9%A2%98%E5%9B%9E%E5%A4%8D%E6%B5%8F%E8%A7%88%E4%BD%93%E9%AA%8C"
),
},
{
id: "v2ex.min",
title: i("settings.extensions.v2ex.min.title"),
url: "http://greasyfork.icu/".concat(
locale,
"/scripts/463552-v2ex-min-v2ex-%E6%9E%81%E7%AE%80%E9%A3%8E%E6%A0%BC"
),
},
{
id: "replace-ugly-avatars",
title: i("settings.extensions.replace-ugly-avatars.title"),
url: "http://greasyfork.icu/".concat(
locale,
"/scripts/472616-replace-ugly-avatars"
),
},
{
id: "more-by-pipecraft",
title: i("settings.extensions.more-by-pipecraft.title"),
url: "http://greasyfork.icu/".concat(locale, "/users/1030884-pipecraft"),
},
]
var prefix = "browser_extension_settings_"
var randomId = String(Math.round(Math.random() * 1e4))
var settingsContainerId = prefix + "container_" + randomId
var settingsElementId = prefix + "main_" + randomId
var getSettingsElement = () => $("#" + settingsElementId)
var getSettingsStyle = () =>
style_default
.replaceAll(/browser_extension_settings_container/gm, settingsContainerId)
.replaceAll(/browser_extension_settings_main/gm, settingsElementId)
var storageKey = "settings"
var settingsOptions
var settingsTable = {}
var settings = {}
async function getSettings() {
var _a
return (_a = await getValue(storageKey)) != null ? _a : {}
}
async function saveSettingsValue(key, value) {
const settings2 = await getSettings()
settings2[key] =
settingsTable[key] && settingsTable[key].defaultValue === value
? void 0
: value
await setValue(storageKey, settings2)
}
function getSettingsValue(key) {
var _a
return Object.hasOwn(settings, key)
? settings[key]
: (_a = settingsTable[key]) == null
? void 0
: _a.defaultValue
}
var closeModal = () => {
const settingsContainer = getSettingsContainer()
if (settingsContainer) {
settingsContainer.style.display = "none"
}
removeEventListener(document, "click", onDocumentClick, true)
removeEventListener(document, "keydown", onDocumentKeyDown, true)
}
function hideSettings() {
closeModal()
}
function destroySettings() {
closeModal()
const settingsContainer = getSettingsContainer()
if (settingsContainer) {
settingsContainer.remove()
}
}
function isSettingsShown() {
const settingsContainer = getSettingsContainer()
if (settingsContainer) {
return settingsContainer.style.display === "block"
}
return false
}
var onDocumentClick = (event) => {
const target = event.target
if (
target == null ? void 0 : target.closest(".".concat(prefix, "container"))
) {
return
}
closeModal()
}
var onDocumentKeyDown = (event) => {
if (event.defaultPrevented) {
return
}
if (event.key === "Escape") {
closeModal()
event.preventDefault()
}
}
async function updateOptions() {
if (!getSettingsElement()) {
return
}
for (const key in settingsTable) {
if (Object.hasOwn(settingsTable, key)) {
const item = settingsTable[key]
const type = item.type || "switch"
switch (type) {
case "switch": {
const checkbox = $(
"#"
.concat(
settingsElementId,
' .option_groups .switch_option[data-key="'
)
.concat(key, '"] input')
)
if (checkbox) {
checkbox.checked = getSettingsValue(key)
}
break
}
case "select": {
const options = $$(
"#"
.concat(
settingsElementId,
' .option_groups .select_option[data-key="'
)
.concat(key, '"] .bes_select option')
)
for (const option of options) {
option.selected = option.value === String(getSettingsValue(key))
}
break
}
case "textarea": {
const textArea = $(
"#"
.concat(
settingsElementId,
' .option_groups textarea[data-key="'
)
.concat(key, '"]')
)
if (textArea) {
textArea.value = getSettingsValue(key)
}
break
}
default: {
break
}
}
}
}
if (typeof settingsOptions.onViewUpdate === "function") {
const settingsMain = createSettingsElement()
settingsOptions.onViewUpdate(settingsMain)
}
}
function getSettingsContainer() {
const container = $(".".concat(prefix, "container"))
if (container) {
const theVersion = parseInt10(container.dataset.besVersion, 0)
if (theVersion < besVersion) {
container.id = settingsContainerId
container.dataset.besVersion = String(besVersion)
}
return container
}
return addElement2(doc.body, "div", {
id: settingsContainerId,
class: "".concat(prefix, "container"),
"data-bes-version": besVersion,
style: "display: none;",
})
}
function getSettingsWrapper() {
const container = getSettingsContainer()
return (
$(".".concat(prefix, "wrapper"), container) ||
addElement2(container, "div", {
class: "".concat(prefix, "wrapper"),
})
)
}
function createSettingsElement() {
let settingsMain = getSettingsElement()
if (!settingsMain) {
const wrapper = getSettingsWrapper()
for (const element of $$(".".concat(prefix, "main"))) {
element.remove()
}
settingsMain = addElement2(wrapper, "div", {
id: settingsElementId,
class: "".concat(prefix, "main thin_scrollbar"),
})
if (settingsOptions.title) {
addElement2(settingsMain, "h2", { textContent: settingsOptions.title })
}
const optionGroups = []
const getOptionGroup = (index) => {
if (index > optionGroups.length) {
for (let i3 = optionGroups.length; i3 < index; i3++) {
optionGroups.push(
addElement2(settingsMain, "div", {
class: "option_groups",
})
)
}
}
return optionGroups[index - 1]
}
for (const key in settingsTable) {
if (Object.hasOwn(settingsTable, key)) {
const item = settingsTable[key]
const type = item.type || "switch"
const group = item.group || 1
const optionGroup = getOptionGroup(group)
switch (type) {
case "switch": {
const switchOption = createSwitchOption(item.icon, item.title, {
async onchange(event) {
const checkbox = event.target
if (checkbox) {
let result = true
if (typeof item.onConfirmChange === "function") {
result = item.onConfirmChange(checkbox.checked)
}
if (result) {
await saveSettingsValue(key, checkbox.checked)
} else {
checkbox.checked = !checkbox.checked
}
}
},
})
switchOption.dataset.key = key
addElement2(optionGroup, switchOption)
break
}
case "textarea": {
let timeoutId
const div = addElement2(optionGroup, "div", {
class: "bes_textarea",
})
addElement2(div, "textarea", {
"data-key": key,
placeholder: item.placeholder || "",
onkeyup(event) {
const textArea = event.target
if (timeoutId) {
clearTimeout(timeoutId)
timeoutId = void 0
}
timeoutId = setTimeout(async () => {
if (textArea) {
await saveSettingsValue(key, textArea.value.trim())
}
}, 100)
},
})
break
}
case "action": {
addElement2(optionGroup, "a", {
class: "action",
textContent: item.title,
onclick: item.onclick,
})
break
}
case "externalLink": {
const div4 = addElement2(optionGroup, "div", {
class: "bes_external_link",
})
addElement2(div4, "a", {
textContent: item.title,
href: item.url,
target: "_blank",
})
break
}
case "select": {
const div = addElement2(optionGroup, "div", {
class: "select_option bes_option",
"data-key": key,
})
if (item.icon) {
addElement2(div, "img", { src: item.icon, class: "bes_icon" })
}
addElement2(div, "span", {
textContent: item.title,
class: "bes_title",
})
const select = addElement2(div, "select", {
class: "bes_select",
async onchange() {
await saveSettingsValue(key, select.value)
},
})
for (const option of Object.entries(item.options)) {
addElement2(select, "option", {
textContent: option[0],
value: option[1],
})
}
break
}
case "tip": {
const tip = addElement2(optionGroup, "div", {
class: "bes_tip",
})
addElement2(tip, "a", {
class: "bes_tip_anchor",
textContent: item.title,
})
const tipContent = addElement2(tip, "div", {
class: "bes_tip_content",
innerHTML: createHTML(item.tipContent),
})
break
}
default: {
break
}
}
}
}
if (settingsOptions.footer) {
const footer = addElement2(settingsMain, "footer")
footer.innerHTML = createHTML(
typeof settingsOptions.footer === "string"
? settingsOptions.footer
: '<p>Made with \u2764\uFE0F by\n <a href="https://www.pipecraft.net/" target="_blank">\n Pipecraft\n </a></p>'
)
}
}
return settingsMain
}
function addCommonSettings(settingsTable2) {
let maxGroup = 0
for (const key in settingsTable2) {
if (Object.hasOwn(settingsTable2, key)) {
const item = settingsTable2[key]
const group = item.group || 1
if (group > maxGroup) {
maxGroup = group
}
}
}
settingsTable2.locale = {
title: i("settings.locale"),
type: "select",
defaultValue: "",
options: {},
group: ++maxGroup,
}
}
function handleShowSettingsUrl() {
const hashString = "#!show-settings-".concat(settingsOptions.id)
if (location.hash === hashString) {
setTimeout(showSettings, 100)
history.replaceState({}, "", location.href.replace(hashString, ""))
}
}
async function showSettings() {
const settingsContainer = getSettingsContainer()
const settingsMain = createSettingsElement()
await updateOptions()
settingsContainer.style.display = "block"
addEventListener(document, "click", onDocumentClick, true)
addEventListener(document, "keydown", onDocumentKeyDown, true)
}
var lastLocale
var resetSettingsUI = (optionsProvider) => {
lastLocale = getSettingsValue("locale") || getPrefferedLocale()
resetI18n(lastLocale)
const options = optionsProvider()
settingsOptions = options
settingsTable = options.settingsTable || {}
addCommonSettings(settingsTable)
const availableLocales3 = options.availableLocales
if (availableLocales3 == null ? void 0 : availableLocales3.length) {
initAvailableLocales(availableLocales3)
const localeSelect = settingsTable.locale
localeSelect.options = {
[i("settings.systemLanguage")]: "",
}
for (const locale2 of availableLocales3) {
const lowerCaseLocale = locale2.toLowerCase()
const displayName = localeNames[lowerCaseLocale] || locale2
localeSelect.options[displayName] = locale2
}
}
}
var initSettings = async (optionsProvider) => {
addValueChangeListener(storageKey, async () => {
settings = await getSettings()
await updateOptions()
const newLocale = getSettingsValue("locale") || getPrefferedLocale()
console.log("lastLocale:", lastLocale, "newLocale:", newLocale)
if (lastLocale !== newLocale) {
const isShown = isSettingsShown()
destroySettings()
resetI18n(newLocale)
lastLocale = newLocale
setTimeout(() => {
resetSettingsUI(optionsProvider)
}, 50)
if (isShown) {
setTimeout(showSettings, 100)
}
}
if (typeof settingsOptions.onValueChange === "function") {
settingsOptions.onValueChange()
}
})
settings = await getSettings()
resetSettingsUI(optionsProvider)
setTimeout(() => {
resetSettingsUI(optionsProvider)
}, 50)
runWhenHeadExists(() => {
addStyle(getSettingsStyle())
})
registerMenuCommand(i("settings.menu.settings"), showSettings, "o")
handleShowSettingsUrl()
}
var content_default =
".lh_selected_element{border:solid 1px red;cursor:not-allowed}a[data-lh-erased-href],a[data-lh-erased-href]:hover{cursor:default;pointer-events:none;text-decoration:none}.bes_tip_content{overflow-y:auto;max-height:300px}"
var messages14 = {
"settings.enable": "Enable",
"settings.enableCurrentSite": "Enable on current site",
"settings.enableCustomRulesForTheCurrentSite":
"Enable custom rules for the current site",
"settings.customRulesPlaceholder":
"/* Custom rules for internal URLs, matching URLs will be opened in new tabs */",
"settings.customRulesTipTitle": "Examples",
"settings.customRulesTipContent":
"<p>Custom rules for internal URLs, matching URLs will be opened in new tabs</p>\n <p>\n - One line per url pattern<br>\n - All URLs contains '/posts' or '/users/'<br>\n <pre>/posts/\n/users/</pre>\n\n - Regex is supported<br>\n <pre>^/(posts|members)/d+</pre>\n\n - '*' for all URLs<br>\n - Exclusion rules: prefix '!' to exclude matching URLs<br>\n <pre>!/posts/\n!^/users/\\d+\n!*</pre>\n </p>",
"settings.enableLinkToImgForCurrentSite":
"Enable converting image links to image tags for the current site",
"settings.enableTextToLinksForCurrentSite":
"Enable converting text links to hyperlinks for the current site",
"settings.enableTreatSubdomainsAsSameSiteForCurrentSite":
"Treat subdomains as the same site for the current site",
"settings.eraseLinks": "Erase Links",
"settings.restoreLinks": "Restore Links",
"settings.title": "\u{1F517} Links Helper",
"settings.information":
"After changing the settings, reload the page to take effect",
"settings.report": "Report and Issue...",
}
var en_default2 = messages14
var messages15 = {
"settings.enable": "\u542F\u7528\u811A\u672C",
"settings.enableCurrentSite":
"\u5728\u5F53\u524D\u7F51\u7AD9\u542F\u7528\u811A\u672C",
"settings.enableCustomRulesForTheCurrentSite":
"\u5728\u5F53\u524D\u7F51\u7AD9\u542F\u7528\u81EA\u5B9A\u4E49\u89C4\u5219",
"settings.customRulesPlaceholder":
"/* \u5185\u90E8\u94FE\u63A5\u7684\u81EA\u5B9A\u4E49\u89C4\u5219\uFF0C\u5339\u914D\u7684\u94FE\u63A5\u4F1A\u5728\u65B0\u7A97\u53E3\u6253\u5F00 */",
"settings.customRulesTipTitle": "\u793A\u4F8B",
"settings.customRulesTipContent":
"<p>\u5185\u90E8\u94FE\u63A5\u7684\u81EA\u5B9A\u4E49\u89C4\u5219\uFF0C\u5339\u914D\u7684\u94FE\u63A5\u4F1A\u5728\u65B0\u7A97\u53E3\u6253\u5F00</p>\n <p>\n - \u6BCF\u884C\u4E00\u6761\u89C4\u5219<br>\n - \u6240\u6709\u5305\u542B '/posts' \u6216 '/users/' \u7684\u94FE\u63A5<br>\n <pre>/posts/\n/users/</pre>\n\n - \u652F\u6301\u6B63\u5219\u8868\u8FBE\u5F0F<br>\n <pre>^/(posts|members)/d+</pre>\n\n - '*' \u4EE3\u8868\u5339\u914D\u6240\u6709\u94FE\u63A5<br>\n - \u6392\u9664\u89C4\u5219\uFF1A\u4EE5 '!' \u5F00\u5934\uFF0C\u5339\u914D\u5219\u6392\u9664\uFF08\u4E0D\u5728\u65B0\u7A97\u53E3\u6253\u5F00\uFF09<br>\n <pre>!/posts/\n!^/users/\\d+\n!*</pre>\n </p>",
"settings.enableLinkToImgForCurrentSite":
"\u5728\u5F53\u524D\u7F51\u7AD9\u542F\u7528\u56FE\u7247\u94FE\u63A5\u81EA\u52A8\u8F6C\u6362\u4E3A\u56FE\u7247\u6807\u7B7E",
"settings.enableTextToLinksForCurrentSite":
"\u5728\u5F53\u524D\u7F51\u7AD9\u542F\u7528\u89E3\u6790\u6587\u672C\u94FE\u63A5\u4E3A\u8D85\u94FE\u63A5",
"settings.enableTreatSubdomainsAsSameSiteForCurrentSite":
"\u5728\u5F53\u524D\u7F51\u7AD9\u542F\u7528\u5C06\u4E8C\u7EA7\u57DF\u540D\u89C6\u4E3A\u540C\u4E00\u7F51\u7AD9",
"settings.eraseLinks":
"\u53BB\u9664\u6307\u5B9A\u533A\u57DF\u7684\u94FE\u63A5",
"settings.restoreLinks": "\u6062\u590D\u53BB\u9664\u7684\u94FE\u63A5",
"settings.title": "\u{1F517} \u94FE\u63A5\u52A9\u624B",
"settings.information":
"\u66F4\u6539\u8BBE\u7F6E\u540E\uFF0C\u91CD\u65B0\u52A0\u8F7D\u9875\u9762\u5373\u53EF\u751F\u6548",
"settings.report": "\u53CD\u9988\u95EE\u9898",
}
var zh_cn_default2 = messages15
var availableLocales2 =
/** @type {const} */
["en", "zh"]
initAvailableLocales(availableLocales2)
console.log("[links-helper] prefferedLocale:", getPrefferedLocale())
var localeMap2 = {
zh: zh_cn_default2,
"zh-cn": zh_cn_default2,
en: en_default2,
}
var i2 = initI18n(localeMap2, getPrefferedLocale())
function resetI18n2(locale2) {
console.log(
"[links-helper] prefferedLocale:",
getPrefferedLocale(),
"locale:",
locale2
)
i2 = initI18n(localeMap2, locale2 || getPrefferedLocale())
}
function getAvailableLocales() {
return availableLocales2
}
var lastTarget
var handleMouseOver = (event) => {
const target = event.target
if (!target || target === lastTarget) {
return
}
if (lastTarget) {
removeClass(lastTarget, "lh_selected_element")
}
lastTarget = target
while (lastTarget && !$("a", lastTarget)) {
lastTarget = lastTarget.parentElement
}
if (lastTarget) {
addClass(lastTarget, "lh_selected_element")
}
}
var handleMouseClick = (event) => {
event.preventDefault()
event.stopPropagation()
event.stopImmediatePropagation()
if (lastTarget) {
for (const element of $$("a[href]", lastTarget)) {
const href = getAttribute(element, "href")
if (href) {
setAttribute(element, "data-lh-erased-href", href)
element.removeAttribute("href")
}
}
removeClass(lastTarget, "lh_selected_element")
}
removeEventListener(doc, "mouseover", handleMouseOver, true)
removeEventListener(doc, "click", handleMouseClick, true)
return false
}
function eraseLinks() {
addEventListener(doc, "mouseover", handleMouseOver, true)
addEventListener(doc, "click", handleMouseClick, true)
}
function restoreLinks() {
for (const element of $$("a[data-lh-erased-href]")) {
const href = getAttribute(element, "data-lh-erased-href")
setAttribute(element, "href", href)
delete element.dataset.lhErasedHref
}
}
var image_url_default =
'{\n "imgur.com": [\n "https?://imgur.com/(\\\\w+)($|\\\\?) -> https://i.imgur.com/$1.png # ex: https://imgur.com/gi2b1rj",\n "https?://imgur.com/(\\\\w+)\\\\.(\\\\w+) -> https://i.imgur.com/$1.$2 # ex: https://imgur.com/gi2b1rj.png"\n ],\n "imgur.io": [\n "https?://imgur.io/(\\\\w+)($|\\\\?) -> https://i.imgur.com/$1.png # ex: https://imgur.io/gi2b1rj",\n "https?://imgur.io/(\\\\w+)\\\\.(\\\\w+) -> https://i.imgur.com/$1.$2 # ex: https://imgur.io/gi2b1rj.png"\n ],\n "i.imgur.com": [\n "https?://i.imgur.com/(\\\\w+)($|\\\\?) -> https://i.imgur.com/$1.png"\n ],\n "camo.githubusercontent.com": [\n "https://camo.githubusercontent.com/.* # This is a img url, no need to replace value"\n ]\n}\n'
var rules = JSON.parse(image_url_default)
var cachedRules = {}
var getHostname = (url) => (/https?:\/\/([^/]+)/.exec(url) || [])[1]
var processRule = (rule, href) => {
var _a
let pattern
let replacement
const cachedRule = cachedRules[rule]
try {
if (cachedRule) {
pattern = cachedRule.pattern
replacement = cachedRule.replacement
} else {
const result = rule.replace(/ #.*/, "").split("->")
const patternString = result[0].trim()
pattern = new RegExp(
patternString.startsWith("http")
? "^" + patternString
: patternString,
"i"
)
replacement = (_a = result[1]) == null ? void 0 : _a.trim()
cachedRules[rule] = { pattern, replacement }
}
if (pattern.test(href)) {
return replacement ? href.replace(pattern, replacement) : href
}
} catch (error) {
console.error(error)
}
}
var convertImgUrl = (href) => {
if (!href) {
return
}
const hostname2 = getHostname(href)
if (Object.hasOwn(rules, hostname2)) {
for (const rule of rules[hostname2]) {
const newHref = processRule(rule, href)
if (newHref) {
return newHref
}
}
}
}
var createImgTagString = (src, text) =>
'<img src="'
.concat(src, '" title="')
.concat(text || "image", '" alt="')
.concat(
text || "image",
'" role="img" style="max-width: 100% !important; vertical-align: bottom;" loading="lazy" referrerpolicy="no-referrer" rel="noreferrer" data-lh-status="1"/>'
)
var bindOnError = () => {
for (const element of $$('img[data-lh-status="1"]')) {
setAttribute(element, "data-lh-status", "2")
addEventListener(element, "error", (event) => {
const img = event.target
const anchor = img.parentElement
img.outerHTML = createHTML(getAttribute(img, "src"))
if ((anchor == null ? void 0 : anchor.tagName) === "A") {
setStyle(anchor, "opacity: 50%;")
setAttribute(anchor, "data-message", "failed to load image")
}
})
}
}
var anchorElementToImgElement = (anchor, href, text) => {
anchor.innerHTML = createHTML(createImgTagString(href, text))
setAttribute(anchor, "target", "_blank")
addAttribute(anchor, "rel", "noopener")
addAttribute(anchor, "rel", "noreferrer")
}
var linkToImg = (anchor) => {
if (
!anchor ||
anchor.childElementCount !== 0 ||
(anchor.childNodes[0] && anchor.childNodes[0].nodeType !== 3) ||
anchor.closest("td h1,td h2,td h3,td h4,td h5")
) {
return
}
const href = anchor.href
const text = anchor.textContent || href
const newHref = convertImgUrl(href)
if (newHref) {
anchorElementToImgElement(anchor, newHref, text)
} else if (
/^https:[^?]+\.(?:jpg|jpeg|jpe|bmp|png|gif|webp|ico|svg)/i.test(href)
) {
anchorElementToImgElement(anchor, href, text)
}
}
var ignoredTags = /* @__PURE__ */ new Set([
"A",
"BUTTON",
"SVG",
"PATH",
"G",
"SCRIPT",
"STYLE",
"TEXTAREA",
"CODE",
"PRE",
"TEMPLATE",
"FILE-ATTACHMENT",
"NOSCRIPT",
"TITLE",
])
var urlPattern =
"\\b((?:https?:\\/\\/(?:[\\w-.]+\\.[a-z]{2,15}|localhost|(?:\\d{1,3}\\.){3}\\d{1,3}))(?::\\d+)?(?:\\/[\\w-/%.~+:;!@=&?#]*)?)"
var linkPattern1 = new RegExp(
"!\\[([^\\[\\]]*)\\]\\((?:\\s|<br/?>)*".concat(
urlPattern,
"(?:\\s|<br/?>)*\\)"
),
"gim"
)
var linkPattern2 = new RegExp(
"\\[([^\\[\\]]*)\\]\\((?:\\s|<br/?>)*".concat(
urlPattern,
"(?:\\s|<br/?>)*\\)"
),
"gim"
)
var linkPattern3 = new RegExp(urlPattern, "gim")
var linkPattern4 = new RegExp(
"\\[img\\](?:\\s|<br/?>)*".concat(urlPattern, "(?:\\s|<br/?>)*\\[/img\\]"),
"gim"
)
var linkPattern5 = new RegExp(
"\\[url\\](?:\\s|<br/?>)*".concat(urlPattern, "(?:\\s|<br/?>)*\\[/url\\]"),
"gim"
)
var linkPattern6 = new RegExp(
"\\[url=".concat(urlPattern, "\\]([^\\[\\]]+)\\[/url\\]"),
"gim"
)
var replaceMarkdownImgLinks = (text) => {
if (text.search(linkPattern1) >= 0) {
text = text.replaceAll(linkPattern1, (m, p1, p2) => {
return createImgTagString(convertImgUrl(p2) || p2, p1)
})
}
return text
}
var replaceMarkdownLinks = (text) => {
if (text.search(linkPattern2) >= 0) {
text = text.replaceAll(linkPattern2, (m, p1, p2) => {
return '<a href="'
.concat(p2, '">')
.concat(p1.replaceAll(/<br>$/gi, ""), "</a>")
})
}
return text
}
var replaceTextLinks = (text) => {
if (text.search(linkPattern3) >= 0) {
text = text.replaceAll(linkPattern3, (m, p1) => {
return '<a href="'.concat(p1, '">').concat(p1, "</a>")
})
}
return text
}
var replaceBBCodeImgLinks = (text) => {
if (text.search(linkPattern4) >= 0) {
text = text.replaceAll(linkPattern4, (m, p1) => {
return createImgTagString(convertImgUrl(p1) || p1, p1)
})
}
return text
}
var replaceBBCodeLinks = (text) => {
if (text.search(linkPattern5) >= 0) {
text = text.replaceAll(linkPattern5, (m, p1) => {
return '<a href="'.concat(p1, '">').concat(p1, "</a>")
})
}
if (text.search(linkPattern6) >= 0) {
text = text.replaceAll(linkPattern6, (m, p1, p2) => {
return '<a href="'.concat(p1, '">').concat(p2, "</a>")
})
}
return text
}
var textToLink = (textNode, previousText) => {
var _a, _b
const textContent = (_a = textNode.textContent) != null ? _a : ""
const parentNode = textNode.parentNode
const mergedText = previousText + textContent
if (
!parentNode ||
textNode.nodeName !== "#text" ||
textContent.trim().length === 0 ||
mergedText.trim().length < 3
) {
return
}
if (textContent.includes("://")) {
const original = textContent
let newContent = original
if (new RegExp("\\[.*]\\(", "ms").test(original)) {
newContent = replaceMarkdownImgLinks(newContent)
newContent = replaceMarkdownLinks(newContent)
}
if (/\[(img|url)]|\[url=/.test(textContent)) {
newContent = replaceBBCodeImgLinks(newContent)
newContent = replaceBBCodeLinks(newContent)
}
if (newContent === original) {
newContent = replaceTextLinks(original)
} else {
newContent = newContent.replaceAll(
new RegExp(
"(<a(?:\\s[^<>]*)?>.*?<\\/a>)|(<img(?:\\s[^<>]*)?\\/?>)|(.+?(?=(?:<a|<img))|.+$)",
"gims"
),
(m, p1, p2) => (p1 || p2 ? m : replaceTextLinks(m))
)
}
if (newContent !== original) {
const span = createElement("span")
span.innerHTML = createHTML(newContent)
textNode.after(span)
textNode.remove()
return true
}
}
const parentTextContent = (_b = parentNode.textContent) != null ? _b : ""
if (
new RegExp("\\[.*]\\(", "ms").test(mergedText) &&
(parentTextContent.search(linkPattern2) >= 0 ||
$$("img", parentNode).length > 0)
) {
const original = parentNode.innerHTML
const newContent = original
.replaceAll(new RegExp("\\[.*]\\([^[\\]()]+?\\)", "gims"), (m) =>
m
.replaceAll(
/<img[^<>]*\ssrc=['"]?(http[^'"]+)['"]?(\s[^<>]*)?>/gim,
"$1"
)
.replaceAll(
/\((?:\s|<br\/?>)*<a[^<>]*\shref=['"]?(http[^'"]+)['"]?(\s[^<>]*)?>\1<\/a>(?:\s|<br\/?>)*\)/gim,
"($1)"
)
)
.replaceAll(
new RegExp("\\[!\\[.*]\\([^()]+\\)]\\([^[\\]()]+?\\)", "gims"),
(m) =>
m
.replaceAll(
/<img[^<>]*\ssrc=['"]?(http[^'"]+)['"]?(\s[^<>]*)?>/gim,
"$1"
)
.replaceAll(
/\((?:\s|<br\/?>)*<a[^<>]*\shref=['"]?(http[^'"]+)['"]?(\s[^<>]*)?>\1<\/a>(?:\s|<br\/?>)*\)/gim,
"($1)"
)
)
if (newContent !== original) {
let newContent2 = replaceMarkdownImgLinks(newContent)
newContent2 = replaceMarkdownLinks(newContent2)
if (newContent2 !== newContent) {
parentNode.innerHTML = createHTML(newContent2)
return true
}
}
}
if (
/\[(img|url)]|\[url=/.test(textContent) &&
parentTextContent.search(/\[(img|url)[^\]]*]([^[\]]*?)\[\/\1]/) >= 0
) {
const original = parentNode.innerHTML
let before = ""
let after = original
let count = 0
while (before !== after && count < 5) {
count++
before = after
after = before.replaceAll(
/\[(img|url)[^\]]*]([^[\]]+?)\[\/\1]/gim,
(m, p1) => {
let tagsRemoved
let converted
if (p1 === "img") {
tagsRemoved = m
.replaceAll(
/<img[^<>]*\ssrc=['"]?(http[^'"]+)['"]?(\s[^<>]*)?>/gim,
"$1"
)
.replaceAll(
/\[img](?:\s|<br\/?>)*<a[^<>]*\shref=['"]?(http[^'"]+)['"]?(\s[^<>]*)?>\1<\/a>(?:\s|<br\/?>)*\[\/img]/gim,
"[img]$1[/img]"
)
converted = replaceBBCodeImgLinks(tagsRemoved)
} else {
tagsRemoved = m
.replaceAll(
/\[url](?:\s|<br\/?>)*<a[^<>]*\shref=['"]?(http[^'"]+)['"]?(\s[^<>]*)?>\1<\/a>(?:\s|<br\/?>)*\[\/url]/gim,
"[url]$1[/url]"
)
.replaceAll(
/\[url=<a[^<>]*\shref=['"]?(http[^'"]+)['"]?(\s[^<>]*)?>\1<\/a>]/gim,
"[url=$1]"
)
converted = replaceBBCodeLinks(tagsRemoved)
}
return converted === tagsRemoved ? m : converted
}
)
}
const newContent = after
if (newContent !== original) {
parentNode.innerHTML = createHTML(newContent)
return true
}
}
}
var fixAnchorTag = (anchorElement) => {
var _a
const href = anchorElement.href
const textContent = (_a = anchorElement.textContent) != null ? _a : ""
const nextSibling = anchorElement.nextSibling
if (
anchorElement.childElementCount === 0 &&
href.includes(")") &&
textContent.includes(")")
) {
const index = textContent.indexOf(")")
const removed = textContent.slice(Math.max(0, index))
anchorElement.textContent = textContent.slice(0, Math.max(0, index))
anchorElement.href = anchorElement.href.slice(
0,
Math.max(0, href.indexOf(")"))
)
if (nextSibling && nextSibling.nodeType === 3) {
nextSibling.textContent = removed + nextSibling.textContent
} else {
anchorElement.after(doc.createTextNode(removed))
}
}
}
var isCodeViewer = (element) => {
return (
hasClass(element, "diff-view") ||
hasClass(element, "diff") ||
hasClass(element, "react-code-lines") ||
hasClass(element, "virtual-blame-wrapper") ||
$('[role="code"]', element)
)
}
var scanAndConvertChildNodes = (parentNode) => {
if (
!parentNode ||
parentNode.nodeType === 8 ||
!parentNode.tagName ||
ignoredTags.has(parentNode.tagName.toUpperCase()) ||
isCodeViewer(parentNode)
) {
if (parentNode.tagName === "A") {
fixAnchorTag(parentNode)
}
return
}
let previousText = ""
for (const child of parentNode.childNodes) {
try {
if (child.nodeName === "#text") {
if (textToLink(child, previousText)) {
scanAndConvertChildNodes(parentNode)
break
}
previousText += child.textContent
} else if (child.nodeName === "BR") {
previousText += "\n"
} else {
previousText = ""
scanAndConvertChildNodes(child)
}
} catch (error) {
console.error(error)
}
}
}
var base = location.origin
var extractCanonicalId = (href) => {
try {
const u = new URL(href, base)
const p = u.pathname.toLowerCase()
let m = /^(\/t\/\d+)(?:\/|$)/.exec(p)
if (m) return m[1]
m = /^(\/t\/[^/]+\/\d+)(?:\/|$)/.exec(p)
if (m) return m[1]
m = /^(\/d\/\d+(?:-[^/]+)?)(?:\/|$)/.exec(p)
if (m) return m[1]
} catch (e) {}
return void 0
}
var getBaseDomain = (h) => {
const host2 = (h || "").toLowerCase().replace(/^www\./, "")
if (
/^\d+(?:\.\d+){3}$/.test(host2) ||
host2 === "localhost" ||
host2.includes(":")
) {
return host2
}
const parts = host2.split(".").filter(Boolean)
if (parts.length <= 2) return host2
const secondLevelDomains = /* @__PURE__ */ new Set([
"co",
"com",
"org",
"net",
"edu",
"gov",
"mil",
"ac",
])
const secondLast = parts.at(-2)
const baseSegments = secondLevelDomains.has(secondLast) ? 3 : 2
return parts.slice(-baseSegments).join(".")
}
var origin = location.origin
var host = location.host
var hostname = location.hostname
var currentUrl
var currentCanonicalId
var enableTreatSubdomainsSameSite = false
var config = {
run_at: "document_start",
}
var getSettingsTable = () => {
let groupNumber = 1
return {
enable: {
title: i2("settings.enable"),
defaultValue: true,
},
["enableCurrentSite_".concat(host)]: {
title: i2("settings.enableCurrentSite"),
defaultValue: true,
},
["enableCustomRulesForCurrentSite_".concat(host)]: {
title: i2("settings.enableCustomRulesForTheCurrentSite"),
defaultValue: false,
},
["customRulesForCurrentSite_".concat(host)]: {
title: i2("settings.enableCustomRulesForTheCurrentSite"),
defaultValue: "",
placeholder: i2("settings.customRulesPlaceholder"),
type: "textarea",
group: ++groupNumber,
},
customRulesTip: {
title: i2("settings.customRulesTipTitle"),
type: "tip",
tipContent: i2("settings.customRulesTipContent"),
group: groupNumber,
},
["enableTreatSubdomainsAsSameSiteForCurrentSite_".concat(host)]: {
title: i2("settings.enableTreatSubdomainsAsSameSiteForCurrentSite"),
defaultValue: false,
group: ++groupNumber,
},
["enableTextToLinksForCurrentSite_".concat(host)]: {
title: i2("settings.enableTextToLinksForCurrentSite"),
defaultValue: Boolean(/v2ex\.com|localhost/.test(host)),
group: ++groupNumber,
},
["enableLinkToImgForCurrentSite_".concat(host)]: {
title: i2("settings.enableLinkToImgForCurrentSite"),
defaultValue: Boolean(/v2ex\.com|localhost/.test(host)),
group: groupNumber,
},
eraseLinks: {
title: i2("settings.eraseLinks"),
type: "action",
async onclick() {
hideSettings()
eraseLinks()
},
group: ++groupNumber,
},
restoreLinks: {
title: i2("settings.restoreLinks"),
type: "action",
async onclick() {
hideSettings()
restoreLinks()
},
group: groupNumber,
},
}
}
var getWithoutOrigin = (url) => url.replace(/(^https?:\/\/[^/]+)/, "")
var currentBaseDomain = getBaseDomain(hostname)
var isSameBaseDomainWithCurrent = (a) =>
getBaseDomain(a) === currentBaseDomain
var shouldOpenInNewTab = (element) => {
var _a
const url = element.href
if (
!url ||
!/^https?:\/\//.test(url) ||
((_a = element.getAttribute("href")) == null
? void 0
: _a.startsWith("#")) ||
url === currentUrl
) {
return false
}
if (element.origin !== origin) {
if (
enableTreatSubdomainsSameSite &&
isSameBaseDomainWithCurrent(element.hostname)
) {
} else {
return true
}
}
if (getSettingsValue("enableCustomRulesForCurrentSite_".concat(host))) {
if (currentCanonicalId) {
const canonicalId = extractCanonicalId(url)
if (canonicalId && canonicalId === currentCanonicalId) {
removeAttributeAsOpenInNewTab(element)
return false
}
}
const rules2 = (
getSettingsValue("customRulesForCurrentSite_".concat(host)) || ""
).split("\n")
const hrefWithoutOrigin = getWithoutOrigin(url)
for (let rule of rules2) {
rule = rule.trim()
if (rule.length === 0) {
continue
}
const isExclude = rule.startsWith("!")
const pattern = isExclude ? rule.slice(1).trim() : rule
if (pattern.length === 0) {
continue
}
if (pattern === "*") {
return !isExclude
}
try {
const regexp = new RegExp(pattern)
if (regexp.test(hrefWithoutOrigin)) {
return !isExclude
}
} catch (error) {
console.log(error.message)
if (hrefWithoutOrigin.includes(pattern)) {
return !isExclude
}
}
}
}
}
var setAttributeAsOpenInNewTab = (element) => {
if (shouldOpenInNewTab(element)) {
setAttribute(element, "target", "_blank")
addAttribute(element, "rel", "noopener")
}
}
var removeAttributeAsOpenInNewTab = (element) => {
removeAttribute(element, "target")
removeAttribute(element, "rel")
}
function onSettingsChange() {
const locale2 = getSettingsValue("locale") || getPrefferedLocale()
resetI18n2(locale2)
enableTreatSubdomainsSameSite = Boolean(
getSettingsValue(
"enableTreatSubdomainsAsSameSiteForCurrentSite_".concat(host)
)
)
}
async function main() {
await initSettings(() => {
const settingsTable2 = getSettingsTable()
return {
id: "links-helper",
title: i2("settings.title"),
footer: "\n <p>"
.concat(
i2("settings.information"),
'</p>\n <p>\n <a href="https://github.com/utags/links-helper/issues" target="_blank">\n '
)
.concat(
i2("settings.report"),
'\n </a></p>\n <p>Made with \u2764\uFE0F by\n <a href="https://www.pipecraft.net/" target="_blank">\n Pipecraft\n </a></p>'
),
settingsTable: settingsTable2,
availableLocales: getAvailableLocales(),
async onValueChange() {
onSettingsChange()
},
onViewUpdate(settingsMainView) {
const group2 = $(".option_groups:nth-of-type(2)", settingsMainView)
if (group2) {
group2.style.display = getSettingsValue(
"enableCustomRulesForCurrentSite_".concat(host)
)
? "block"
: "none"
}
},
}
})
if (
!getSettingsValue("enable") ||
!getSettingsValue("enableCurrentSite_".concat(host))
) {
return
}
onSettingsChange()
runWhenHeadExists(() => {
addStyle(content_default)
})
addEventListener(
doc,
"click",
(event) => {
let anchorElement = event.target
if (!anchorElement) {
return
}
if (anchorElement.closest(".utags_ul")) {
if (
hasClass(anchorElement, "utags_captain_tag") ||
hasClass(anchorElement, "utags_captain_tag2")
) {
event.preventDefault()
}
return
}
while (anchorElement && anchorElement.tagName !== "A") {
anchorElement = anchorElement.parentNode
}
if (anchorElement) {
setAttributeAsOpenInNewTab(anchorElement)
if (getAttribute(anchorElement, "target") === "_blank") {
event.stopImmediatePropagation()
event.stopPropagation()
}
}
},
true
)
const scanAnchors = () => {
if (currentUrl !== location.href) {
currentUrl = location.href
currentCanonicalId = extractCanonicalId(currentUrl)
}
for (const element of $$("a")) {
if (element.__links_helper_scaned) {
continue
}
element.__links_helper_scaned = 1
try {
setAttributeAsOpenInNewTab(element)
} catch (error) {
console.error(error)
}
if (getSettingsValue("enableLinkToImgForCurrentSite_".concat(host))) {
try {
linkToImg(element)
} catch (error) {
console.error(error)
}
}
}
}
const scanNodes = throttle(() => {
if (getSettingsValue("enableTextToLinksForCurrentSite_".concat(host))) {
scanAndConvertChildNodes(doc.body)
}
scanAnchors()
bindOnError()
}, 500)
const observer = new MutationObserver((mutationsList) => {
scanNodes()
})
const startObserver = () => {
observer.observe(doc.body, {
childList: true,
subtree: true,
characterData: true,
})
}
runWhenBodyExists(() => {
startObserver()
if (getSettingsValue("enableTextToLinksForCurrentSite_".concat(host))) {
scanAndConvertChildNodes(doc.body)
}
})
scanAnchors()
}
runWhenHeadExists(async () => {
if (doc.documentElement.dataset.linksHelper === void 0) {
doc.documentElement.dataset.linksHelper = ""
await main()
}
})
})()