// ==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(), 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: "https://greasyfork.org/".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: "https://greasyfork.org/".concat( locale, "/scripts/464541-links-helper" ), }, { id: "v2ex.rep", title: i("settings.extensions.v2ex.rep.title"), url: "https://greasyfork.org/".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: "https://greasyfork.org/".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: "https://greasyfork.org/".concat( locale, "/scripts/472616-replace-ugly-avatars" ), }, { id: "more-by-pipecraft", title: i("settings.extensions.more-by-pipecraft.title"), url: "https://greasyfork.org/".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 : '

Made with \u2764\uFE0F by\n \n Pipecraft\n

' ) } } 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": "

Custom rules for internal URLs, matching URLs will be opened in new tabs

\n

\n - One line per url pattern
\n - All URLs contains '/posts' or '/users/'
\n

/posts/\n/users/
\n\n - Regex is supported
\n
^/(posts|members)/d+
\n\n - '*' for all URLs
\n - Exclusion rules: prefix '!' to exclude matching URLs
\n
!/posts/\n!^/users/\\d+\n!*
\n

", "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": "

\u5185\u90E8\u94FE\u63A5\u7684\u81EA\u5B9A\u4E49\u89C4\u5219\uFF0C\u5339\u914D\u7684\u94FE\u63A5\u4F1A\u5728\u65B0\u7A97\u53E3\u6253\u5F00

\n

\n - \u6BCF\u884C\u4E00\u6761\u89C4\u5219
\n - \u6240\u6709\u5305\u542B '/posts' \u6216 '/users/' \u7684\u94FE\u63A5
\n

/posts/\n/users/
\n\n - \u652F\u6301\u6B63\u5219\u8868\u8FBE\u5F0F
\n
^/(posts|members)/d+
\n\n - '*' \u4EE3\u8868\u5339\u914D\u6240\u6709\u94FE\u63A5
\n - \u6392\u9664\u89C4\u5219\uFF1A\u4EE5 '!' \u5F00\u5934\uFF0C\u5339\u914D\u5219\u6392\u9664\uFF08\u4E0D\u5728\u65B0\u7A97\u53E3\u6253\u5F00\uFF09
\n
!/posts/\n!^/users/\\d+\n!*
\n

", "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) => '')
      .concat(
        text || ' ) 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|
)*".concat( urlPattern, "(?:\\s|
)*\\)" ), "gim" ) var linkPattern2 = new RegExp( "\\[([^\\[\\]]*)\\]\\((?:\\s|
)*".concat( urlPattern, "(?:\\s|
)*\\)" ), "gim" ) var linkPattern3 = new RegExp(urlPattern, "gim") var linkPattern4 = new RegExp( "\\[img\\](?:\\s|
)*".concat(urlPattern, "(?:\\s|
)*\\[/img\\]"), "gim" ) var linkPattern5 = new RegExp( "\\[url\\](?:\\s|
)*".concat(urlPattern, "(?:\\s|
)*\\[/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 '') .concat(p1.replaceAll(/
$/gi, ""), "
") }) } return text } var replaceTextLinks = (text) => { if (text.search(linkPattern3) >= 0) { text = text.replaceAll(linkPattern3, (m, p1) => { return '').concat(p1, "") }) } 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 '').concat(p1, "") }) } if (text.search(linkPattern6) >= 0) { text = text.replaceAll(linkPattern6, (m, p1, p2) => { return '').concat(p2, "") }) } 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>)|(]*)?\\/?>)|(.+?(?=(?: (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( /]*\ssrc=['"]?(http[^'"]+)['"]?(\s[^<>]*)?>/gim, "$1" ) .replaceAll( /\((?:\s|)*]*\shref=['"]?(http[^'"]+)['"]?(\s[^<>]*)?>\1<\/a>(?:\s|)*\)/gim, "($1)" ) ) .replaceAll( new RegExp("\\[!\\[.*]\\([^()]+\\)]\\([^[\\]()]+?\\)", "gims"), (m) => m .replaceAll( /]*\ssrc=['"]?(http[^'"]+)['"]?(\s[^<>]*)?>/gim, "$1" ) .replaceAll( /\((?:\s|)*]*\shref=['"]?(http[^'"]+)['"]?(\s[^<>]*)?>\1<\/a>(?:\s|)*\)/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( /]*\ssrc=['"]?(http[^'"]+)['"]?(\s[^<>]*)?>/gim, "$1" ) .replaceAll( /\[img](?:\s|)*]*\shref=['"]?(http[^'"]+)['"]?(\s[^<>]*)?>\1<\/a>(?:\s|)*\[\/img]/gim, "[img]$1[/img]" ) converted = replaceBBCodeImgLinks(tagsRemoved) } else { tagsRemoved = m .replaceAll( /\[url](?:\s|)*]*\shref=['"]?(http[^'"]+)['"]?(\s[^<>]*)?>\1<\/a>(?:\s|)*\[\/url]/gim, "[url]$1[/url]" ) .replaceAll( /\[url=]*\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

" .concat( i2("settings.information"), '

\n

\n \n ' ) .concat( i2("settings.report"), '\n

\n

Made with \u2764\uFE0F by\n \n Pipecraft\n

' ), 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() } }) })()