赐你个头像吧

🔃 换掉别人的头像与昵称

  1. // ==UserScript==
  2. // @name Replace Ugly Avatars
  3. // @name:zh-CN 赐你个头像吧
  4. // @namespace https://github.com/utags/replace-ugly-avatars
  5. // @homepageURL https://github.com/utags/replace-ugly-avatars#readme
  6. // @supportURL https://github.com/utags/replace-ugly-avatars/issues
  7. // @version 0.5.3
  8. // @description 🔃 Replace specified user's avatar (profile photo) and username (nickname)
  9. // @description:zh-CN 🔃 换掉别人的头像与昵称
  10. // @icon data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='%230d6efd' class='bi bi-arrow-repeat' viewBox='0 0 16 16'%3E %3Cpath d='M11.534 7h3.932a.25.25 0 0 1 .192.41l-1.966 2.36a.25.25 0 0 1-.384 0l-1.966-2.36a.25.25 0 0 1 .192-.41zm-11 2h3.932a.25.25 0 0 0 .192-.41L2.692 6.23a.25.25 0 0 0-.384 0L.342 8.59A.25.25 0 0 0 .534 9z'/%3E %3Cpath fill-rule='evenodd' d='M8 3c-1.552 0-2.94.707-3.857 1.818a.5.5 0 1 1-.771-.636A6.002 6.002 0 0 1 13.917 7H12.9A5.002 5.002 0 0 0 8 3zM3.1 9a5.002 5.002 0 0 0 8.757 2.182.5.5 0 1 1 .771.636A6.002 6.002 0 0 1 2.083 9H3.1z'/%3E %3C/svg%3E
  11. // @author Pipecraft
  12. // @license MIT
  13. // @match https://*.v2ex.com/*
  14. // @match https://*.v2ex.co/*
  15. // @match https://linux.do/*
  16. // @match https://v2hot.pipecraft.net/*
  17. // @run-at document-start
  18. // @grant GM.getValue
  19. // @grant GM.setValue
  20. // @grant GM.deleteValue
  21. // @grant GM_addValueChangeListener
  22. // @grant GM_removeValueChangeListener
  23. // @grant GM_addElement
  24. // @grant GM.registerMenuCommand
  25. // ==/UserScript==
  26. //
  27. ;(() => {
  28. "use strict"
  29. var listeners = {}
  30. var getValue = async (key) => {
  31. const value = await GM.getValue(key)
  32. return value && value !== "undefined" ? JSON.parse(value) : void 0
  33. }
  34. var setValue = async (key, value) => {
  35. if (value !== void 0) {
  36. const newValue = JSON.stringify(value)
  37. if (listeners[key]) {
  38. const oldValue = await GM.getValue(key)
  39. await GM.setValue(key, newValue)
  40. if (newValue !== oldValue) {
  41. for (const func of listeners[key]) {
  42. func(key, oldValue, newValue)
  43. }
  44. }
  45. } else {
  46. await GM.setValue(key, newValue)
  47. }
  48. }
  49. }
  50. var deleteValue = async (key) => GM.deleteValue(key)
  51. var _addValueChangeListener = (key, func) => {
  52. listeners[key] = listeners[key] || []
  53. listeners[key].push(func)
  54. return () => {
  55. if (listeners[key] && listeners[key].length > 0) {
  56. for (let i3 = listeners[key].length - 1; i3 >= 0; i3--) {
  57. if (listeners[key][i3] === func) {
  58. listeners[key].splice(i3, 1)
  59. }
  60. }
  61. }
  62. }
  63. }
  64. var addValueChangeListener = (key, func) => {
  65. if (typeof GM_addValueChangeListener !== "function") {
  66. console.warn("Do not support GM_addValueChangeListener!")
  67. return _addValueChangeListener(key, func)
  68. }
  69. const listenerId = GM_addValueChangeListener(key, func)
  70. return () => {
  71. GM_removeValueChangeListener(listenerId)
  72. }
  73. }
  74. var doc = document
  75. if (typeof String.prototype.replaceAll !== "function") {
  76. String.prototype.replaceAll = String.prototype.replace
  77. }
  78. var $ = (selectors, element) => (element || doc).querySelector(selectors)
  79. var $$ = (selectors, element) => [
  80. ...(element || doc).querySelectorAll(selectors),
  81. ]
  82. var getRootElement = (type) =>
  83. type === 1
  84. ? doc.head || doc.body || doc.documentElement
  85. : type === 2
  86. ? doc.body || doc.documentElement
  87. : doc.documentElement
  88. var createElement = (tagName, attributes) =>
  89. setAttributes(doc.createElement(tagName), attributes)
  90. var addElement = (parentNode, tagName, attributes) => {
  91. if (typeof parentNode === "string") {
  92. return addElement(null, parentNode, tagName)
  93. }
  94. if (!tagName) {
  95. return
  96. }
  97. if (!parentNode) {
  98. parentNode = /^(script|link|style|meta)$/.test(tagName)
  99. ? getRootElement(1)
  100. : getRootElement(2)
  101. }
  102. if (typeof tagName === "string") {
  103. const element = createElement(tagName, attributes)
  104. parentNode.append(element)
  105. return element
  106. }
  107. setAttributes(tagName, attributes)
  108. parentNode.append(tagName)
  109. return tagName
  110. }
  111. var addEventListener = (element, type, listener, options) => {
  112. if (!element) {
  113. return
  114. }
  115. if (typeof type === "object") {
  116. for (const type1 in type) {
  117. if (Object.hasOwn(type, type1)) {
  118. element.addEventListener(type1, type[type1])
  119. }
  120. }
  121. } else if (typeof type === "string" && typeof listener === "function") {
  122. element.addEventListener(type, listener, options)
  123. }
  124. }
  125. var removeEventListener = (element, type, listener, options) => {
  126. if (!element) {
  127. return
  128. }
  129. if (typeof type === "object") {
  130. for (const type1 in type) {
  131. if (Object.hasOwn(type, type1)) {
  132. element.removeEventListener(type1, type[type1])
  133. }
  134. }
  135. } else if (typeof type === "string" && typeof listener === "function") {
  136. element.removeEventListener(type, listener, options)
  137. }
  138. }
  139. var getAttribute = (element, name) =>
  140. element ? element.getAttribute(name) : null
  141. var setAttribute = (element, name, value) =>
  142. element ? element.setAttribute(name, value) : void 0
  143. var setAttributes = (element, attributes) => {
  144. if (element && attributes) {
  145. for (const name in attributes) {
  146. if (Object.hasOwn(attributes, name)) {
  147. const value = attributes[name]
  148. if (value === void 0) {
  149. continue
  150. }
  151. if (/^(value|textContent|innerText)$/.test(name)) {
  152. element[name] = value
  153. } else if (/^(innerHTML)$/.test(name)) {
  154. element[name] = createHTML(value)
  155. } else if (name === "style") {
  156. setStyle(element, value, true)
  157. } else if (/on\w+/.test(name)) {
  158. const type = name.slice(2)
  159. addEventListener(element, type, value)
  160. } else {
  161. setAttribute(element, name, value)
  162. }
  163. }
  164. }
  165. }
  166. return element
  167. }
  168. var addClass = (element, className) => {
  169. if (!element || !element.classList) {
  170. return
  171. }
  172. element.classList.add(className)
  173. }
  174. var removeClass = (element, className) => {
  175. if (!element || !element.classList) {
  176. return
  177. }
  178. element.classList.remove(className)
  179. }
  180. var setStyle = (element, values, overwrite) => {
  181. if (!element) {
  182. return
  183. }
  184. const style = element.style
  185. if (typeof values === "string") {
  186. style.cssText = overwrite ? values : style.cssText + ";" + values
  187. return
  188. }
  189. if (overwrite) {
  190. style.cssText = ""
  191. }
  192. for (const key in values) {
  193. if (Object.hasOwn(values, key)) {
  194. style[key] = values[key].replace("!important", "")
  195. }
  196. }
  197. }
  198. var throttle = (func, interval) => {
  199. let timeoutId = null
  200. let next = false
  201. const handler = (...args) => {
  202. if (timeoutId) {
  203. next = true
  204. } else {
  205. func.apply(void 0, args)
  206. timeoutId = setTimeout(() => {
  207. timeoutId = null
  208. if (next) {
  209. next = false
  210. handler()
  211. }
  212. }, interval)
  213. }
  214. }
  215. return handler
  216. }
  217. if (typeof Object.hasOwn !== "function") {
  218. Object.hasOwn = (instance, prop) =>
  219. Object.prototype.hasOwnProperty.call(instance, prop)
  220. }
  221. var getOffsetPosition = (element, referElement) => {
  222. const position = { top: 0, left: 0 }
  223. referElement = referElement || doc.body
  224. while (element && element !== referElement) {
  225. position.top += element.offsetTop
  226. position.left += element.offsetLeft
  227. element = element.offsetParent
  228. }
  229. return position
  230. }
  231. var runOnceCache = {}
  232. var runOnce = async (key, func) => {
  233. if (Object.hasOwn(runOnceCache, key)) {
  234. return runOnceCache[key]
  235. }
  236. const result = await func()
  237. if (key) {
  238. runOnceCache[key] = result
  239. }
  240. return result
  241. }
  242. var sleep = async (time) => {
  243. return new Promise((resolve) => {
  244. setTimeout(() => {
  245. resolve(1)
  246. }, time)
  247. })
  248. }
  249. var parseInt10 = (number, defaultValue) => {
  250. if (typeof number === "number" && !Number.isNaN(number)) {
  251. return number
  252. }
  253. if (typeof defaultValue !== "number") {
  254. defaultValue = Number.NaN
  255. }
  256. if (!number) {
  257. return defaultValue
  258. }
  259. const result = Number.parseInt(number, 10)
  260. return Number.isNaN(result) ? defaultValue : result
  261. }
  262. var rootFuncArray = []
  263. var headFuncArray = []
  264. var bodyFuncArray = []
  265. var headBodyObserver
  266. var startObserveHeadBodyExists = () => {
  267. if (headBodyObserver) {
  268. return
  269. }
  270. headBodyObserver = new MutationObserver(() => {
  271. if (doc.head && doc.body) {
  272. headBodyObserver.disconnect()
  273. }
  274. if (doc.documentElement && rootFuncArray.length > 0) {
  275. for (const func of rootFuncArray) {
  276. func()
  277. }
  278. rootFuncArray.length = 0
  279. }
  280. if (doc.head && headFuncArray.length > 0) {
  281. for (const func of headFuncArray) {
  282. func()
  283. }
  284. headFuncArray.length = 0
  285. }
  286. if (doc.body && bodyFuncArray.length > 0) {
  287. for (const func of bodyFuncArray) {
  288. func()
  289. }
  290. bodyFuncArray.length = 0
  291. }
  292. })
  293. headBodyObserver.observe(doc, {
  294. childList: true,
  295. subtree: true,
  296. })
  297. }
  298. var runWhenHeadExists = (func) => {
  299. if (!doc.head) {
  300. headFuncArray.push(func)
  301. startObserveHeadBodyExists()
  302. return
  303. }
  304. func()
  305. }
  306. var runWhenDomReady = (func) => {
  307. if (doc.readyState === "interactive" || doc.readyState === "complete") {
  308. return func()
  309. }
  310. const handler = () => {
  311. if (doc.readyState === "interactive" || doc.readyState === "complete") {
  312. func()
  313. removeEventListener(doc, "readystatechange", handler)
  314. }
  315. }
  316. addEventListener(doc, "readystatechange", handler)
  317. }
  318. var escapeHTMLPolicy =
  319. typeof trustedTypes !== "undefined" &&
  320. typeof trustedTypes.createPolicy === "function"
  321. ? trustedTypes.createPolicy("beuEscapePolicy", {
  322. createHTML: (string) => string,
  323. })
  324. : void 0
  325. var createHTML = (html) => {
  326. return escapeHTMLPolicy ? escapeHTMLPolicy.createHTML(html) : html
  327. }
  328. var addElement2 =
  329. typeof GM_addElement === "function"
  330. ? (parentNode, tagName, attributes) => {
  331. if (typeof parentNode === "string") {
  332. return addElement2(null, parentNode, tagName)
  333. }
  334. if (!tagName) {
  335. return
  336. }
  337. if (!parentNode) {
  338. parentNode = /^(script|link|style|meta)$/.test(tagName)
  339. ? getRootElement(1)
  340. : getRootElement(2)
  341. }
  342. if (typeof tagName === "string") {
  343. let attributes2
  344. if (attributes) {
  345. const entries1 = []
  346. const entries2 = []
  347. for (const entry of Object.entries(attributes)) {
  348. if (/^(on\w+|innerHTML)$/.test(entry[0])) {
  349. entries2.push(entry)
  350. } else {
  351. entries1.push(entry)
  352. }
  353. }
  354. attributes = Object.fromEntries(entries1)
  355. attributes2 = Object.fromEntries(entries2)
  356. }
  357. const element = GM_addElement(null, tagName, attributes)
  358. setAttributes(element, attributes2)
  359. parentNode.append(element)
  360. return element
  361. }
  362. setAttributes(tagName, attributes)
  363. parentNode.append(tagName)
  364. return tagName
  365. }
  366. : addElement
  367. var addStyle = (styleText) =>
  368. addElement2(null, "style", { textContent: styleText })
  369. var registerMenuCommand = (name, callback, accessKey) => {
  370. if (window !== top) {
  371. return
  372. }
  373. if (typeof GM.registerMenuCommand !== "function") {
  374. console.warn("Do not support GM.registerMenuCommand!")
  375. return
  376. }
  377. GM.registerMenuCommand(name, callback, accessKey)
  378. }
  379. var style_default =
  380. '#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 .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 .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 .switch::before{display:none}#browser_extension_settings_main .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+.switch{background-color:var(--color-on)}#browser_extension_settings_main input[type=checkbox]:checked+.switch .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 .extension_list_container{display:none}#browser_extension_settings_container .extension_list_container.bes_active{display:block}#browser_extension_settings_container .extension_list_container.bes_active+div{display:none}#browser_extension_settings_main a.navigation_go_previous{display:block}}'
  381. function createSwitch(options = {}) {
  382. const container = createElement("label", { class: "container" })
  383. const checkbox = createElement(
  384. "input",
  385. options.checked ? { type: "checkbox", checked: "" } : { type: "checkbox" }
  386. )
  387. addElement2(container, checkbox)
  388. const switchElm = createElement("span", { class: "switch" })
  389. addElement2(switchElm, "span", { class: "slider" })
  390. addElement2(container, switchElm)
  391. if (options.onchange) {
  392. addEventListener(checkbox, "change", options.onchange)
  393. }
  394. return container
  395. }
  396. function createSwitchOption(icon, text, options) {
  397. if (typeof text !== "string") {
  398. return createSwitchOption(void 0, icon, text)
  399. }
  400. const div = createElement("div", { class: "switch_option bes_option" })
  401. if (icon) {
  402. addElement2(div, "img", { src: icon, class: "bes_icon" })
  403. }
  404. addElement2(div, "span", { textContent: text, class: "bes_title" })
  405. div.append(createSwitch(options))
  406. return div
  407. }
  408. var besVersion = 51
  409. var openButton =
  410. '<svg viewBox="0 0 60.2601318359375 84.8134765625" version="1.1" xmlns="http://www.w3.org/2000/svg" class=" glyph-box" style="height: 9.62969px; width: 6.84191px;"><g transform="matrix(1 0 0 1 -6.194965820312518 77.63671875)"><path d="M66.4551-35.2539C66.4551-36.4746 65.9668-37.5977 65.0391-38.4766L26.3672-76.3672C25.4883-77.1973 24.4141-77.6367 23.1445-77.6367C20.6543-77.6367 18.7012-75.7324 18.7012-73.1934C18.7012-71.9727 19.1895-70.8496 19.9707-70.0195L55.5176-35.2539L19.9707-0.488281C19.1895 0.341797 18.7012 1.41602 18.7012 2.68555C18.7012 5.22461 20.6543 7.12891 23.1445 7.12891C24.4141 7.12891 25.4883 6.68945 26.3672 5.81055L65.0391-32.0312C65.9668-32.959 66.4551-34.0332 66.4551-35.2539Z"></path></g></svg>'
  411. var openInNewTabButton =
  412. '<svg viewBox="0 0 72.127685546875 72.2177734375" version="1.1" xmlns="http://www.w3.org/2000/svg" class=" glyph-box" style="height: 8.19958px; width: 8.18935px;"><g transform="matrix(1 0 0 1 -12.451127929687573 71.3388671875)"><path d="M84.5703-17.334L84.5215-66.4551C84.5215-69.2383 82.7148-71.1914 79.7852-71.1914L30.6641-71.1914C27.9297-71.1914 26.0742-69.0918 26.0742-66.748C26.0742-64.4043 28.1738-62.4023 30.4688-62.4023L47.4609-62.4023L71.2891-63.1836L62.207-55.2246L13.8184-6.73828C12.9395-5.85938 12.4512-4.73633 12.4512-3.66211C12.4512-1.31836 14.5508 0.878906 16.9922 0.878906C18.1152 0.878906 19.1895 0.488281 20.0684-0.439453L68.5547-48.877L76.6113-58.0078L75.7324-35.2051L75.7324-17.1387C75.7324-14.8438 77.7344-12.6953 80.127-12.6953C82.4707-12.6953 84.5703-14.6973 84.5703-17.334Z"></path></g></svg>'
  413. var settingButton =
  414. '<svg viewBox="0 0 16 16" version="1.1">\n<path d="M8 0a8.2 8.2 0 0 1 .701.031C9.444.095 9.99.645 10.16 1.29l.288 1.107c.018.066.079.158.212.224.231.114.454.243.668.386.123.082.233.09.299.071l1.103-.303c.644-.176 1.392.021 1.82.63.27.385.506.792.704 1.218.315.675.111 1.422-.364 1.891l-.814.806c-.049.048-.098.147-.088.294.016.257.016.515 0 .772-.01.147.038.246.088.294l.814.806c.475.469.679 1.216.364 1.891a7.977 7.977 0 0 1-.704 1.217c-.428.61-1.176.807-1.82.63l-1.102-.302c-.067-.019-.177-.011-.3.071a5.909 5.909 0 0 1-.668.386c-.133.066-.194.158-.211.224l-.29 1.106c-.168.646-.715 1.196-1.458 1.26a8.006 8.006 0 0 1-1.402 0c-.743-.064-1.289-.614-1.458-1.26l-.289-1.106c-.018-.066-.079-.158-.212-.224a5.738 5.738 0 0 1-.668-.386c-.123-.082-.233-.09-.299-.071l-1.103.303c-.644.176-1.392-.021-1.82-.63a8.12 8.12 0 0 1-.704-1.218c-.315-.675-.111-1.422.363-1.891l.815-.806c.05-.048.098-.147.088-.294a6.214 6.214 0 0 1 0-.772c.01-.147-.038-.246-.088-.294l-.815-.806C.635 6.045.431 5.298.746 4.623a7.92 7.92 0 0 1 .704-1.217c.428-.61 1.176-.807 1.82-.63l1.102.302c.067.019.177.011.3-.071.214-.143.437-.272.668-.386.133-.066.194-.158.211-.224l.29-1.106C6.009.645 6.556.095 7.299.03 7.53.01 7.764 0 8 0Zm-.571 1.525c-.036.003-.108.036-.137.146l-.289 1.105c-.147.561-.549.967-.998 1.189-.173.086-.34.183-.5.29-.417.278-.97.423-1.529.27l-1.103-.303c-.109-.03-.175.016-.195.045-.22.312-.412.644-.573.99-.014.031-.021.11.059.19l.815.806c.411.406.562.957.53 1.456a4.709 4.709 0 0 0 0 .582c.032.499-.119 1.05-.53 1.456l-.815.806c-.081.08-.073.159-.059.19.162.346.353.677.573.989.02.03.085.076.195.046l1.102-.303c.56-.153 1.113-.008 1.53.27.161.107.328.204.501.29.447.222.85.629.997 1.189l.289 1.105c.029.109.101.143.137.146a6.6 6.6 0 0 0 1.142 0c.036-.003.108-.036.137-.146l.289-1.105c.147-.561.549-.967.998-1.189.173-.086.34-.183.5-.29.417-.278.97-.423 1.529-.27l1.103.303c.109.029.175-.016.195-.045.22-.313.411-.644.573-.99.014-.031.021-.11-.059-.19l-.815-.806c-.411-.406-.562-.957-.53-1.456a4.709 4.709 0 0 0 0-.582c-.032-.499.119-1.05.53-1.456l.815-.806c.081-.08.073-.159.059-.19a6.464 6.464 0 0 0-.573-.989c-.02-.03-.085-.076-.195-.046l-1.102.303c-.56.153-1.113.008-1.53-.27a4.44 4.44 0 0 0-.501-.29c-.447-.222-.85-.629-.997-1.189l-.289-1.105c-.029-.11-.101-.143-.137-.146a6.6 6.6 0 0 0-1.142 0ZM11 8a3 3 0 1 1-6 0 3 3 0 0 1 6 0ZM9.5 8a1.5 1.5 0 1 0-3.001.001A1.5 1.5 0 0 0 9.5 8Z"></path>\n</svg>'
  415. function initI18n(messageMaps, language) {
  416. language = (language || navigator.language).toLowerCase()
  417. const language2 = language.slice(0, 2)
  418. let messagesDefault
  419. let messagesLocal
  420. for (const entry of Object.entries(messageMaps)) {
  421. const langs = new Set(
  422. entry[0]
  423. .toLowerCase()
  424. .split(",")
  425. .map((v) => v.trim())
  426. )
  427. const value = entry[1]
  428. if (langs.has(language)) {
  429. messagesLocal = value
  430. }
  431. if (langs.has(language2) && !messagesLocal) {
  432. messagesLocal = value
  433. }
  434. if (langs.has("en")) {
  435. messagesDefault = value
  436. }
  437. if (langs.has("en-us") && !messagesDefault) {
  438. messagesDefault = value
  439. }
  440. }
  441. if (!messagesLocal) {
  442. messagesLocal = {}
  443. }
  444. if (!messagesDefault || messagesDefault === messagesLocal) {
  445. messagesDefault = {}
  446. }
  447. return function (key, ...parameters) {
  448. let text = messagesLocal[key] || messagesDefault[key] || key
  449. if (parameters && parameters.length > 0 && text !== key) {
  450. for (let i3 = 0; i3 < parameters.length; i3++) {
  451. text = text.replaceAll(
  452. new RegExp("\\{".concat(i3 + 1, "\\}"), "g"),
  453. String(parameters[i3])
  454. )
  455. }
  456. }
  457. return text
  458. }
  459. }
  460. var messages = {
  461. "settings.title": "Settings",
  462. "settings.otherExtensions": "Other Extensions",
  463. "settings.displaySettingsButtonInSideMenu":
  464. "Display Settings Button in Side Menu",
  465. "settings.menu.settings": "\u2699\uFE0F Settings",
  466. "settings.extensions.utags.title":
  467. "\u{1F3F7}\uFE0F UTags - Add usertags to links",
  468. "settings.extensions.links-helper.title": "\u{1F517} Links Helper",
  469. "settings.extensions.v2ex.rep.title":
  470. "V2EX.REP - \u4E13\u6CE8\u63D0\u5347 V2EX \u4E3B\u9898\u56DE\u590D\u6D4F\u89C8\u4F53\u9A8C",
  471. "settings.extensions.v2ex.min.title":
  472. "v2ex.min - V2EX Minimalist (\u6781\u7B80\u98CE\u683C)",
  473. "settings.extensions.replace-ugly-avatars.title": "Replace Ugly Avatars",
  474. "settings.extensions.more-by-pipecraft.title":
  475. "Find more useful userscripts",
  476. }
  477. var en_default = messages
  478. var messages2 = {
  479. "settings.title": "\u8BBE\u7F6E",
  480. "settings.otherExtensions": "\u5176\u4ED6\u6269\u5C55",
  481. "settings.displaySettingsButtonInSideMenu":
  482. "\u5728\u4FA7\u8FB9\u680F\u83DC\u5355\u4E2D\u663E\u793A\u8BBE\u7F6E\u6309\u94AE",
  483. "settings.menu.settings": "\u2699\uFE0F \u8BBE\u7F6E",
  484. "settings.extensions.utags.title":
  485. "\u{1F3F7}\uFE0F \u5C0F\u9C7C\u6807\u7B7E (UTags) - \u4E3A\u94FE\u63A5\u6DFB\u52A0\u7528\u6237\u6807\u7B7E",
  486. "settings.extensions.links-helper.title":
  487. "\u{1F517} \u94FE\u63A5\u52A9\u624B",
  488. "settings.extensions.v2ex.rep.title":
  489. "V2EX.REP - \u4E13\u6CE8\u63D0\u5347 V2EX \u4E3B\u9898\u56DE\u590D\u6D4F\u89C8\u4F53\u9A8C",
  490. "settings.extensions.v2ex.min.title":
  491. "v2ex.min - V2EX \u6781\u7B80\u98CE\u683C",
  492. "settings.extensions.replace-ugly-avatars.title":
  493. "\u8D50\u4F60\u4E2A\u5934\u50CF\u5427",
  494. "settings.extensions.more-by-pipecraft.title":
  495. "\u66F4\u591A\u6709\u8DA3\u7684\u811A\u672C",
  496. }
  497. var zh_cn_default = messages2
  498. var i = initI18n({
  499. "en,en-US": en_default,
  500. "zh,zh-CN": zh_cn_default,
  501. })
  502. var lang = navigator.language
  503. var locale
  504. if (lang === "zh-TW" || lang === "zh-HK") {
  505. locale = "zh-TW"
  506. } else if (lang.includes("zh")) {
  507. locale = "zh-CN"
  508. } else {
  509. locale = "en"
  510. }
  511. var relatedExtensions = [
  512. {
  513. id: "utags",
  514. title: i("settings.extensions.utags.title"),
  515. url: "https://gf.qytechs.cn/".concat(
  516. locale,
  517. "/scripts/460718-utags-add-usertags-to-links"
  518. ),
  519. },
  520. {
  521. id: "links-helper",
  522. title: i("settings.extensions.links-helper.title"),
  523. description:
  524. "\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",
  525. url: "https://gf.qytechs.cn/".concat(
  526. locale,
  527. "/scripts/464541-links-helper"
  528. ),
  529. },
  530. {
  531. id: "v2ex.rep",
  532. title: i("settings.extensions.v2ex.rep.title"),
  533. url: "https://gf.qytechs.cn/".concat(
  534. locale,
  535. "/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"
  536. ),
  537. },
  538. {
  539. id: "v2ex.min",
  540. title: i("settings.extensions.v2ex.min.title"),
  541. url: "https://gf.qytechs.cn/".concat(
  542. locale,
  543. "/scripts/463552-v2ex-min-v2ex-%E6%9E%81%E7%AE%80%E9%A3%8E%E6%A0%BC"
  544. ),
  545. },
  546. {
  547. id: "replace-ugly-avatars",
  548. title: i("settings.extensions.replace-ugly-avatars.title"),
  549. url: "https://gf.qytechs.cn/".concat(
  550. locale,
  551. "/scripts/472616-replace-ugly-avatars"
  552. ),
  553. },
  554. {
  555. id: "more-by-pipecraft",
  556. title: i("settings.extensions.more-by-pipecraft.title"),
  557. url: "https://gf.qytechs.cn/".concat(locale, "/users/1030884-pipecraft"),
  558. },
  559. ]
  560. var getInstalledExtesionList = () => {
  561. return $(".extension_list_container .installed_extension_list")
  562. }
  563. var getRelatedExtesionList = () => {
  564. return $(".extension_list_container .related_extension_list")
  565. }
  566. var isInstalledExtension = (id) => {
  567. const list = getInstalledExtesionList()
  568. if (!list) {
  569. return false
  570. }
  571. const installed = $('[data-extension-id="'.concat(id, '"]'), list)
  572. return Boolean(installed)
  573. }
  574. var addCurrentExtension = (extension) => {
  575. const list = getInstalledExtesionList()
  576. if (!list) {
  577. return
  578. }
  579. if (isInstalledExtension(extension.id)) {
  580. return
  581. }
  582. const element = createInstalledExtension(extension)
  583. list.append(element)
  584. const list2 = getRelatedExtesionList()
  585. if (list2) {
  586. updateRelatedExtensions(list2)
  587. }
  588. }
  589. var activeExtension = (id) => {
  590. const list = getInstalledExtesionList()
  591. if (!list) {
  592. return false
  593. }
  594. for (const element of $$(".active", list)) {
  595. removeClass(element, "active")
  596. }
  597. const installed = $('[data-extension-id="'.concat(id, '"]'), list)
  598. if (installed) {
  599. addClass(installed, "active")
  600. }
  601. }
  602. var activeExtensionList = () => {
  603. const extensionListContainer = $(".extension_list_container")
  604. if (extensionListContainer) {
  605. addClass(extensionListContainer, "bes_active")
  606. }
  607. }
  608. var deactiveExtensionList = () => {
  609. const extensionListContainer = $(".extension_list_container")
  610. if (extensionListContainer) {
  611. removeClass(extensionListContainer, "bes_active")
  612. }
  613. }
  614. var createInstalledExtension = (installedExtension) => {
  615. const div = createElement("div", {
  616. class: "installed_extension",
  617. "data-extension-id": installedExtension.id,
  618. })
  619. const a = addElement2(div, "a", {
  620. onclick: installedExtension.onclick,
  621. })
  622. addElement2(a, "span", {
  623. textContent: installedExtension.title,
  624. })
  625. const svg = addElement2(a, "svg")
  626. svg.outerHTML = createHTML(openButton)
  627. return div
  628. }
  629. var updateRelatedExtensions = (container) => {
  630. const relatedExtensionElements = $$("[data-extension-id]", container)
  631. if (relatedExtensionElements.length > 0) {
  632. for (const relatedExtensionElement of relatedExtensionElements) {
  633. if (
  634. isInstalledExtension(
  635. relatedExtensionElement.dataset.extensionId || "noid"
  636. )
  637. ) {
  638. relatedExtensionElement.remove()
  639. }
  640. }
  641. } else {
  642. container.innerHTML = createHTML("")
  643. }
  644. for (const relatedExtension of relatedExtensions) {
  645. if (
  646. isInstalledExtension(relatedExtension.id) ||
  647. $('[data-extension-id="'.concat(relatedExtension.id, '"]'), container)
  648. ) {
  649. continue
  650. }
  651. if ($$("[data-extension-id]", container).length >= 4) {
  652. return
  653. }
  654. const div4 = addElement2(container, "div", {
  655. class: "related_extension",
  656. "data-extension-id": relatedExtension.id,
  657. })
  658. const a = addElement2(div4, "a", {
  659. href: relatedExtension.url,
  660. target: "_blank",
  661. })
  662. addElement2(a, "span", {
  663. textContent: relatedExtension.title,
  664. })
  665. const svg = addElement2(a, "svg")
  666. svg.outerHTML = createHTML(openInNewTabButton)
  667. }
  668. }
  669. function createExtensionList(installedExtensions) {
  670. const div = createElement("div", {
  671. class: "extension_list_container thin_scrollbar",
  672. })
  673. addElement2(div, "h1", { textContent: i("settings.title") })
  674. const div2 = addElement2(div, "div", {
  675. class: "installed_extension_list",
  676. })
  677. for (const installedExtension of installedExtensions) {
  678. if (isInstalledExtension(installedExtension.id)) {
  679. continue
  680. }
  681. const element = createInstalledExtension(installedExtension)
  682. div2.append(element)
  683. }
  684. addElement2(div, "h2", { textContent: i("settings.otherExtensions") })
  685. const div3 = addElement2(div, "div", {
  686. class: "related_extension_list",
  687. })
  688. updateRelatedExtensions(div3)
  689. return div
  690. }
  691. var prefix = "browser_extension_settings_"
  692. var randomId = String(Math.round(Math.random() * 1e4))
  693. var settingsContainerId = prefix + "container_" + randomId
  694. var settingsElementId = prefix + "main_" + randomId
  695. var getSettingsElement = () => $("#" + settingsElementId)
  696. var getSettingsStyle = () =>
  697. style_default
  698. .replaceAll(/browser_extension_settings_container/gm, settingsContainerId)
  699. .replaceAll(/browser_extension_settings_main/gm, settingsElementId)
  700. var storageKey = "settings"
  701. var settingsOptions
  702. var settingsTable = {}
  703. var settings = {}
  704. async function getSettings() {
  705. var _a
  706. return (_a = await getValue(storageKey)) != null ? _a : {}
  707. }
  708. async function saveSettingsValue(key, value) {
  709. const settings2 = await getSettings()
  710. settings2[key] =
  711. settingsTable[key] && settingsTable[key].defaultValue === value
  712. ? void 0
  713. : value
  714. await setValue(storageKey, settings2)
  715. }
  716. async function saveSettingsValues(values) {
  717. const settings2 = await getSettings()
  718. for (const key in values) {
  719. if (Object.hasOwn(values, key)) {
  720. const value = values[key]
  721. settings2[key] =
  722. settingsTable[key] && settingsTable[key].defaultValue === value
  723. ? void 0
  724. : value
  725. }
  726. }
  727. await setValue(storageKey, settings2)
  728. }
  729. function getSettingsValue(key) {
  730. var _a
  731. return Object.hasOwn(settings, key)
  732. ? settings[key]
  733. : (_a = settingsTable[key]) == null
  734. ? void 0
  735. : _a.defaultValue
  736. }
  737. var closeModal = () => {
  738. const settingsContainer = getSettingsContainer()
  739. if (settingsContainer) {
  740. settingsContainer.style.display = "none"
  741. }
  742. removeEventListener(document, "click", onDocumentClick, true)
  743. removeEventListener(document, "keydown", onDocumentKeyDown, true)
  744. }
  745. var onDocumentClick = (event) => {
  746. const target = event.target
  747. if (
  748. target == null ? void 0 : target.closest(".".concat(prefix, "container"))
  749. ) {
  750. return
  751. }
  752. closeModal()
  753. }
  754. var onDocumentKeyDown = (event) => {
  755. if (event.defaultPrevented) {
  756. return
  757. }
  758. if (event.key === "Escape") {
  759. closeModal()
  760. event.preventDefault()
  761. }
  762. }
  763. async function updateOptions() {
  764. if (!getSettingsElement()) {
  765. return
  766. }
  767. for (const key in settingsTable) {
  768. if (Object.hasOwn(settingsTable, key)) {
  769. const item = settingsTable[key]
  770. const type = item.type || "switch"
  771. switch (type) {
  772. case "switch": {
  773. const checkbox = $(
  774. "#"
  775. .concat(
  776. settingsElementId,
  777. ' .option_groups .switch_option[data-key="'
  778. )
  779. .concat(key, '"] input')
  780. )
  781. if (checkbox) {
  782. checkbox.checked = getSettingsValue(key)
  783. }
  784. break
  785. }
  786. case "select": {
  787. const options = $$(
  788. "#"
  789. .concat(
  790. settingsElementId,
  791. ' .option_groups .select_option[data-key="'
  792. )
  793. .concat(key, '"] .bes_select option')
  794. )
  795. for (const option of options) {
  796. option.selected = option.value === String(getSettingsValue(key))
  797. }
  798. break
  799. }
  800. case "textarea": {
  801. const textArea = $(
  802. "#"
  803. .concat(
  804. settingsElementId,
  805. ' .option_groups textarea[data-key="'
  806. )
  807. .concat(key, '"]')
  808. )
  809. if (textArea) {
  810. textArea.value = getSettingsValue(key)
  811. }
  812. break
  813. }
  814. default: {
  815. break
  816. }
  817. }
  818. }
  819. }
  820. if (typeof settingsOptions.onViewUpdate === "function") {
  821. const settingsMain = createSettingsElement()
  822. settingsOptions.onViewUpdate(settingsMain)
  823. }
  824. }
  825. function getSettingsContainer() {
  826. const container = $(".".concat(prefix, "container"))
  827. if (container) {
  828. const theVersion = parseInt10(container.dataset.besVersion, 0)
  829. if (theVersion < besVersion) {
  830. container.id = settingsContainerId
  831. container.dataset.besVersion = String(besVersion)
  832. }
  833. return container
  834. }
  835. return addElement2(doc.body, "div", {
  836. id: settingsContainerId,
  837. class: "".concat(prefix, "container"),
  838. "data-bes-version": besVersion,
  839. style: "display: none;",
  840. })
  841. }
  842. function getSettingsWrapper() {
  843. const container = getSettingsContainer()
  844. return (
  845. $(".".concat(prefix, "wrapper"), container) ||
  846. addElement2(container, "div", {
  847. class: "".concat(prefix, "wrapper"),
  848. })
  849. )
  850. }
  851. function initExtensionList() {
  852. const wrapper = getSettingsWrapper()
  853. if (!$(".extension_list_container", wrapper)) {
  854. const list = createExtensionList([])
  855. wrapper.append(list)
  856. }
  857. addCurrentExtension({
  858. id: settingsOptions.id,
  859. title: settingsOptions.title,
  860. onclick: showSettings,
  861. })
  862. }
  863. function createSettingsElement() {
  864. let settingsMain = getSettingsElement()
  865. if (!settingsMain) {
  866. const wrapper = getSettingsWrapper()
  867. for (const element of $$(".".concat(prefix, "main"))) {
  868. element.remove()
  869. }
  870. settingsMain = addElement2(wrapper, "div", {
  871. id: settingsElementId,
  872. class: "".concat(prefix, "main thin_scrollbar"),
  873. })
  874. addElement2(settingsMain, "a", {
  875. textContent: "Settings",
  876. class: "navigation_go_previous",
  877. onclick() {
  878. activeExtensionList()
  879. },
  880. })
  881. if (settingsOptions.title) {
  882. addElement2(settingsMain, "h2", { textContent: settingsOptions.title })
  883. }
  884. const optionGroups = []
  885. const getOptionGroup = (index) => {
  886. if (index > optionGroups.length) {
  887. for (let i3 = optionGroups.length; i3 < index; i3++) {
  888. optionGroups.push(
  889. addElement2(settingsMain, "div", {
  890. class: "option_groups",
  891. })
  892. )
  893. }
  894. }
  895. return optionGroups[index - 1]
  896. }
  897. for (const key in settingsTable) {
  898. if (Object.hasOwn(settingsTable, key)) {
  899. const item = settingsTable[key]
  900. const type = item.type || "switch"
  901. const group = item.group || 1
  902. const optionGroup = getOptionGroup(group)
  903. switch (type) {
  904. case "switch": {
  905. const switchOption = createSwitchOption(item.icon, item.title, {
  906. async onchange(event) {
  907. const checkbox = event.target
  908. if (checkbox) {
  909. let result = true
  910. if (typeof item.onConfirmChange === "function") {
  911. result = item.onConfirmChange(checkbox.checked)
  912. }
  913. if (result) {
  914. await saveSettingsValue(key, checkbox.checked)
  915. } else {
  916. checkbox.checked = !checkbox.checked
  917. }
  918. }
  919. },
  920. })
  921. switchOption.dataset.key = key
  922. addElement2(optionGroup, switchOption)
  923. break
  924. }
  925. case "textarea": {
  926. let timeoutId
  927. const div = addElement2(optionGroup, "div", {
  928. class: "bes_textarea",
  929. })
  930. addElement2(div, "textarea", {
  931. "data-key": key,
  932. placeholder: item.placeholder || "",
  933. onkeyup(event) {
  934. const textArea = event.target
  935. if (timeoutId) {
  936. clearTimeout(timeoutId)
  937. timeoutId = void 0
  938. }
  939. timeoutId = setTimeout(async () => {
  940. if (textArea) {
  941. await saveSettingsValue(key, textArea.value.trim())
  942. }
  943. }, 100)
  944. },
  945. })
  946. break
  947. }
  948. case "action": {
  949. addElement2(optionGroup, "a", {
  950. class: "action",
  951. textContent: item.title,
  952. onclick: item.onclick,
  953. })
  954. break
  955. }
  956. case "externalLink": {
  957. const div4 = addElement2(optionGroup, "div", {
  958. class: "bes_external_link",
  959. })
  960. addElement2(div4, "a", {
  961. textContent: item.title,
  962. href: item.url,
  963. target: "_blank",
  964. })
  965. break
  966. }
  967. case "select": {
  968. const div = addElement2(optionGroup, "div", {
  969. class: "select_option bes_option",
  970. "data-key": key,
  971. })
  972. if (item.icon) {
  973. addElement2(div, "img", { src: item.icon, class: "bes_icon" })
  974. }
  975. addElement2(div, "span", {
  976. textContent: item.title,
  977. class: "bes_title",
  978. })
  979. const select = addElement2(div, "select", {
  980. class: "bes_select",
  981. async onchange() {
  982. await saveSettingsValue(key, select.value)
  983. },
  984. })
  985. for (const option of Object.entries(item.options)) {
  986. addElement2(select, "option", {
  987. textContent: option[0],
  988. value: option[1],
  989. })
  990. }
  991. break
  992. }
  993. case "tip": {
  994. const tip = addElement2(optionGroup, "div", {
  995. class: "bes_tip",
  996. })
  997. addElement2(tip, "a", {
  998. class: "bes_tip_anchor",
  999. textContent: item.title,
  1000. })
  1001. const tipContent = addElement2(tip, "div", {
  1002. class: "bes_tip_content",
  1003. innerHTML: createHTML(item.tipContent),
  1004. })
  1005. break
  1006. }
  1007. }
  1008. }
  1009. }
  1010. if (settingsOptions.footer) {
  1011. const footer = addElement2(settingsMain, "footer")
  1012. footer.innerHTML = createHTML(
  1013. typeof settingsOptions.footer === "string"
  1014. ? settingsOptions.footer
  1015. : '<p>Made with \u2764\uFE0F by\n <a href="https://www.pipecraft.net/" target="_blank">\n Pipecraft\n </a></p>'
  1016. )
  1017. }
  1018. }
  1019. return settingsMain
  1020. }
  1021. function addSideMenu() {
  1022. if (!getSettingsValue("displaySettingsButtonInSideMenu")) {
  1023. return
  1024. }
  1025. const menu =
  1026. $("#browser_extension_side_menu") ||
  1027. addElement2(doc.body, "div", {
  1028. id: "browser_extension_side_menu",
  1029. "data-bes-version": besVersion,
  1030. })
  1031. const button = $("button[data-bes-version]", menu)
  1032. if (button) {
  1033. const theVersion = parseInt10(button.dataset.besVersion, 0)
  1034. if (theVersion >= besVersion) {
  1035. return
  1036. }
  1037. button.remove()
  1038. }
  1039. addElement2(menu, "button", {
  1040. type: "button",
  1041. "data-bes-version": besVersion,
  1042. title: i("settings.menu.settings"),
  1043. onclick() {
  1044. setTimeout(showSettings, 1)
  1045. },
  1046. innerHTML: settingButton,
  1047. })
  1048. }
  1049. function addCommonSettings(settingsTable3) {
  1050. let maxGroup = 0
  1051. for (const key in settingsTable3) {
  1052. if (Object.hasOwn(settingsTable3, key)) {
  1053. const item = settingsTable3[key]
  1054. const group = item.group || 1
  1055. if (group > maxGroup) {
  1056. maxGroup = group
  1057. }
  1058. }
  1059. }
  1060. settingsTable3.displaySettingsButtonInSideMenu = {
  1061. title: i("settings.displaySettingsButtonInSideMenu"),
  1062. defaultValue: !(
  1063. typeof GM === "object" && typeof GM.registerMenuCommand === "function"
  1064. ),
  1065. group: maxGroup + 1,
  1066. }
  1067. }
  1068. function handleShowSettingsUrl() {
  1069. if (location.hash === "#bes-show-settings") {
  1070. setTimeout(showSettings, 100)
  1071. }
  1072. }
  1073. async function showSettings() {
  1074. const settingsContainer = getSettingsContainer()
  1075. const settingsMain = createSettingsElement()
  1076. await updateOptions()
  1077. settingsContainer.style.display = "block"
  1078. addEventListener(document, "click", onDocumentClick, true)
  1079. addEventListener(document, "keydown", onDocumentKeyDown, true)
  1080. activeExtension(settingsOptions.id)
  1081. deactiveExtensionList()
  1082. }
  1083. var initSettings = async (options) => {
  1084. settingsOptions = options
  1085. settingsTable = options.settingsTable || {}
  1086. addCommonSettings(settingsTable)
  1087. addValueChangeListener(storageKey, async () => {
  1088. settings = await getSettings()
  1089. await updateOptions()
  1090. addSideMenu()
  1091. if (typeof options.onValueChange === "function") {
  1092. options.onValueChange()
  1093. }
  1094. })
  1095. settings = await getSettings()
  1096. runWhenHeadExists(() => {
  1097. addStyle(getSettingsStyle())
  1098. })
  1099. runWhenDomReady(() => {
  1100. initExtensionList()
  1101. addSideMenu()
  1102. })
  1103. registerMenuCommand(i("settings.menu.settings"), showSettings, "o")
  1104. handleShowSettingsUrl()
  1105. }
  1106. var content_default =
  1107. '#rua_container .change_button{position:absolute;box-sizing:border-box;width:20px;height:20px;padding:1px;border:1px solid;cursor:pointer;color:#0d6efd;z-index:10001}#rua_container .change_button.advanced{color:#00008b;display:none}#rua_container .change_button.hide{display:none}#rua_container .change_button:active,#rua_container .change_button.active{opacity:50%;transition:all .2s}#rua_container:hover .change_button{display:block !important}img.rua_fadeout{opacity:10%;transition:all 1s ease-out}[data-replace-ugly-avatars*="v2ex.co"] #Main .header .fr a img{width:73px;height:73px}[data-replace-ugly-avatars*="v2ex.co"] td[width="48"] img{width:48px;height:48px}'
  1108. function getRandomInt(min, max) {
  1109. min = Math.ceil(min)
  1110. max = Math.floor(max)
  1111. return Math.floor(Math.random() * (max - min)) + min
  1112. }
  1113. var cachedData
  1114. function getRamdomAvatar() {
  1115. if (cachedData && cachedData.length > 0) {
  1116. let avatar = cachedData[getRandomInt(0, cachedData.length)]
  1117. avatar = encodeURIComponent(avatar).replaceAll("%2F", "/")
  1118. return "https://wsrv.nl/?url=cdn.jsdelivr.net/gh/gfriends/gfriends@master/Content/".concat(
  1119. avatar,
  1120. "&w=96&h=96&dpr=2&fit=cover&a=focal&fpy=0.35&output=webp"
  1121. )
  1122. }
  1123. setTimeout(initRamdomAvatar)
  1124. }
  1125. var retryCount = 0
  1126. async function fetchRamdomAvatar() {
  1127. const url =
  1128. "https://cdn.jsdelivr.net/gh/utags/random-avatars@2025021816/public/gfriends-".concat(
  1129. getRandomInt(1, 101),
  1130. ".json"
  1131. )
  1132. try {
  1133. const response = await fetch(url)
  1134. if (response.status === 200) {
  1135. return await response.json()
  1136. }
  1137. } catch (error) {
  1138. console.error(error)
  1139. retryCount++
  1140. if (retryCount < 3) {
  1141. await sleep(1e3)
  1142. return fetchRamdomAvatar()
  1143. }
  1144. }
  1145. }
  1146. var storageKey2 = "gfriendsData"
  1147. async function initRamdomAvatar() {
  1148. if (cachedData && cachedData.length > 0) {
  1149. return
  1150. }
  1151. cachedData = await getValue(storageKey2)
  1152. if (cachedData) {
  1153. setTimeout(async () => {
  1154. const data = await fetchRamdomAvatar()
  1155. if (data) {
  1156. await setValue(storageKey2, data)
  1157. }
  1158. }, 1e3 * 60)
  1159. } else {
  1160. const data = await fetchRamdomAvatar()
  1161. if (data) {
  1162. cachedData = data
  1163. await setValue(storageKey2, data)
  1164. }
  1165. }
  1166. }
  1167. var cachedData2
  1168. function getRamdomAvatar2() {
  1169. if (cachedData2 && cachedData2.length > 0) {
  1170. const avatar = cachedData2[getRandomInt(0, cachedData2.length)]
  1171. return "https://cdn.jsdelivr.net/gh/utags/ugly-avatar-generated@main/".concat(
  1172. avatar
  1173. )
  1174. }
  1175. setTimeout(initRamdomAvatar2)
  1176. }
  1177. var retryCount2 = 0
  1178. async function fetchRamdomAvatar2() {
  1179. const url =
  1180. "https://cdn.jsdelivr.net/gh/utags/random-avatars@2025021816/public/ugly-avatar/ugly-avatar-".concat(
  1181. getRandomInt(1, 11),
  1182. ".json"
  1183. )
  1184. try {
  1185. const response = await fetch(url)
  1186. if (response.status === 200) {
  1187. return await response.json()
  1188. }
  1189. } catch (error) {
  1190. console.error(error)
  1191. retryCount2++
  1192. if (retryCount2 < 3) {
  1193. await sleep(1e3)
  1194. return fetchRamdomAvatar2()
  1195. }
  1196. }
  1197. }
  1198. var storageKey3 = "uglyAvatarData"
  1199. async function initRamdomAvatar2() {
  1200. if (cachedData2 && cachedData2.length > 0) {
  1201. return
  1202. }
  1203. cachedData2 = await getValue(storageKey3)
  1204. if (cachedData2) {
  1205. setTimeout(async () => {
  1206. const data = await fetchRamdomAvatar2()
  1207. if (data) {
  1208. await setValue(storageKey3, data)
  1209. }
  1210. }, 1e3 * 60)
  1211. } else {
  1212. const data = await fetchRamdomAvatar2()
  1213. if (data) {
  1214. cachedData2 = data
  1215. await setValue(storageKey3, data)
  1216. }
  1217. }
  1218. }
  1219. var styles = [
  1220. "adventurer",
  1221. "adventurer-neutral",
  1222. "avataaars",
  1223. "avataaars-neutral",
  1224. "big-ears",
  1225. "big-ears-neutral",
  1226. "big-smile",
  1227. "bottts",
  1228. "bottts-neutral",
  1229. "croodles",
  1230. "croodles-neutral",
  1231. "fun-emoji",
  1232. "icons",
  1233. "identicon",
  1234. "initials",
  1235. "lorelei",
  1236. "lorelei-neutral",
  1237. "micah",
  1238. "miniavs",
  1239. "notionists",
  1240. "notionists-neutral",
  1241. "open-peeps",
  1242. "personas",
  1243. "pixel-art",
  1244. "pixel-art-neutral",
  1245. "shapes",
  1246. "thumbs",
  1247. "ugly-avatar",
  1248. "gfriends",
  1249. ]
  1250. var allAvatarStyleList = styles
  1251. function getRandomFlipParameter(style) {
  1252. if (style === "initials" || style === "identicon") {
  1253. return ""
  1254. }
  1255. const values = [false, false, false, false, true]
  1256. const value = values[getRandomInt(0, values.length)]
  1257. return value ? "&flip=true" : ""
  1258. }
  1259. function getRandomRadiusParameter(style) {
  1260. const values = [0, 0, 0, 10, 10, 10, 20, 20, 30, 50]
  1261. const value = values[getRandomInt(0, values.length)]
  1262. return value ? "&radius=" + value : ""
  1263. }
  1264. function getRandomBackgroundColorParameter(style) {
  1265. const values = [
  1266. "",
  1267. "",
  1268. "",
  1269. "",
  1270. "",
  1271. "",
  1272. "",
  1273. "",
  1274. "",
  1275. "ffffff",
  1276. "b6e3f4",
  1277. "c0aede",
  1278. "d1d4f9",
  1279. "ffd5dc",
  1280. "ffdfbf",
  1281. ]
  1282. let value = values[getRandomInt(0, values.length)]
  1283. if ((style === "initials" || style === "icons") && value === "ffffff") {
  1284. value = ""
  1285. }
  1286. return value ? "&backgroundColor=" + value : ""
  1287. }
  1288. function getRandomAvatar(prefix2, styleList) {
  1289. const styles2 =
  1290. !styleList || styleList.length === 0 ? allAvatarStyleList : styleList
  1291. const randomStyle = styles2[getRandomInt(0, styles2.length)]
  1292. if (randomStyle === "ugly-avatar") {
  1293. return getRamdomAvatar2()
  1294. }
  1295. if (randomStyle === "gfriends") {
  1296. return getRamdomAvatar()
  1297. }
  1298. return (
  1299. "https://api.dicebear.com/6.x/"
  1300. .concat(randomStyle, "/svg?seed=")
  1301. .concat(prefix2, ".")
  1302. .concat(Date.now()) +
  1303. getRandomFlipParameter(randomStyle) +
  1304. getRandomRadiusParameter(randomStyle) +
  1305. getRandomBackgroundColorParameter(randomStyle)
  1306. )
  1307. }
  1308. var changeIcon =
  1309. '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-repeat" viewBox="0 0 16 16">\n<path d="M11.534 7h3.932a.25.25 0 0 1 .192.41l-1.966 2.36a.25.25 0 0 1-.384 0l-1.966-2.36a.25.25 0 0 1 .192-.41zm-11 2h3.932a.25.25 0 0 0 .192-.41L2.692 6.23a.25.25 0 0 0-.384 0L.342 8.59A.25.25 0 0 0 .534 9z"/>\n<path fill-rule="evenodd" d="M8 3c-1.552 0-2.94.707-3.857 1.818a.5.5 0 1 1-.771-.636A6.002 6.002 0 0 1 13.917 7H12.9A5.002 5.002 0 0 0 8 3zM3.1 9a5.002 5.002 0 0 0 8.757 2.182.5.5 0 1 1 .771.636A6.002 6.002 0 0 1 2.083 9H3.1z"/>\n</svg>'
  1310. var messages3 = {
  1311. "settings.enableCurrentSite": "Enable on current site",
  1312. "settings.autoReplaceAll": "Automatically replace all avatars",
  1313. "settings.autoReplaceAll.confirm":
  1314. "Are you sure you want to automatically replace all avatars?",
  1315. "settings.clearData": "Clear the replaced avatar data",
  1316. "settings.clearData.confirm":
  1317. "Are you sure you want to delete all replaced avatar data?",
  1318. "settings.clearData.done": "Done!",
  1319. "settings.title": "Replace Ugly Avatars",
  1320. "settings.information":
  1321. "After changing the settings, reload the page to take effect",
  1322. "settings.report": "Report and Issue...",
  1323. "alert.needsSelectOneAavatar":
  1324. "At least one avatar style needs to be enabled",
  1325. "prompt.enterAvatarLink": "Please enter the avatar link",
  1326. }
  1327. var en_default2 = messages3
  1328. var messages4 = {
  1329. "settings.enableCurrentSite":
  1330. "\u5728\u5F53\u524D\u7F51\u7AD9\u542F\u7528\u811A\u672C",
  1331. "settings.autoReplaceAll":
  1332. "\u81EA\u52A8\u66FF\u6362\u5168\u90E8\u5934\u50CF",
  1333. "settings.autoReplaceAll.confirm":
  1334. "\u786E\u5B9A\u8981\u81EA\u52A8\u66FF\u6362\u5168\u90E8\u5934\u50CF\u5417\uFF1F",
  1335. "settings.clearData":
  1336. "\u6E05\u7A7A\u88AB\u66FF\u6362\u7684\u5934\u50CF\u6570\u636E",
  1337. "settings.clearData.confirm":
  1338. "\u786E\u5B9A\u8981\u5220\u9664\u6240\u6709\u88AB\u66FF\u6362\u7684\u5934\u50CF\u6570\u636E\u5417\uFF1F",
  1339. "settings.clearData.done": "\u5220\u9664\u5B8C\u6BD5!",
  1340. "settings.title": "\u8D50\u4F60\u4E2A\u5934\u50CF\u5427",
  1341. "settings.information":
  1342. "\u66F4\u6539\u8BBE\u7F6E\u540E\uFF0C\u91CD\u65B0\u52A0\u8F7D\u9875\u9762\u5373\u53EF\u751F\u6548",
  1343. "settings.report": "\u53CD\u9988\u95EE\u9898",
  1344. "alert.needsSelectOneAavatar":
  1345. "\u81F3\u5C11\u9700\u8981\u542F\u7528\u4E00\u79CD\u5934\u50CF\u98CE\u683C",
  1346. "prompt.enterAvatarLink": "\u8BF7\u8F93\u5165\u5934\u50CF\u94FE\u63A5",
  1347. }
  1348. var zh_cn_default2 = messages4
  1349. var i2 = initI18n({
  1350. "en,en-US": en_default2,
  1351. "zh,zh-CN": zh_cn_default2,
  1352. })
  1353. var site = {
  1354. matches: /^linux\.do$/,
  1355. getAvatarElements() {
  1356. return $$(
  1357. [
  1358. 'img[src^="https://linux.do/user_avatar/linux.do/"]',
  1359. 'img[src^="/user_avatar/linux.do/"]',
  1360. 'img[src^="https://cdn.linux.do/user_avatar/"]',
  1361. 'img[src^="https://linux.do/letter_avatar_proxy/"]',
  1362. 'img[src^="/letter_avatar_proxy/"]',
  1363. 'img[src^="https://cdn.linux.do/letter_avatar_proxy/"]',
  1364. 'img[src^="https://cdn.linux.do/letter_avatar/"]',
  1365. "img[data-rua-org-src]",
  1366. ].join(",")
  1367. )
  1368. },
  1369. getUserName(element) {
  1370. const src =
  1371. getAttribute(element, "data-rua-org-src") ||
  1372. getAttribute(element, "src")
  1373. if (!src) {
  1374. return
  1375. }
  1376. if (src.includes("letter_avatar_proxy")) {
  1377. const name2 = src.replace(
  1378. /.*\/letter_avatar_proxy\/v4\/letter\/(\w\/[^/]+)\/.*/,
  1379. "$1"
  1380. )
  1381. return name2.toLowerCase()
  1382. }
  1383. if (src.includes("letter_avatar/")) {
  1384. const name2 = src.replace(/.*\/letter_avatar\/(\w+)\/.*/, "$1")
  1385. return name2.toLowerCase()
  1386. }
  1387. const name = src.replace(/.*\/user_avatar\/linux\.do\/([^/]+)\/.*/, "$1")
  1388. return name.toLowerCase()
  1389. },
  1390. }
  1391. var linux_do_default = site
  1392. var site2 = {
  1393. matches: /v2ex\.com$|^v2hot\.|v2ex\.co$/,
  1394. getAvatarElements() {
  1395. return $$('.avatar,a[href*="/member/"] img')
  1396. },
  1397. getUserName,
  1398. }
  1399. function getUserName(element) {
  1400. if (!element) {
  1401. return
  1402. }
  1403. const userNameElement = $('a[href*="/member/"]', element)
  1404. if (userNameElement) {
  1405. const userName = (/member\/(\w+)/.exec(userNameElement.href) || [])[1]
  1406. if (userName) {
  1407. return userName.toLowerCase()
  1408. }
  1409. return
  1410. }
  1411. return getUserName(element.parentElement)
  1412. }
  1413. var v2ex_com_default = site2
  1414. var sites = [
  1415. //
  1416. v2ex_com_default,
  1417. linux_do_default,
  1418. ]
  1419. var defaultSite = {
  1420. matches: /.*/,
  1421. getAvatarElements() {
  1422. return []
  1423. },
  1424. getUserName(element) {
  1425. return ""
  1426. },
  1427. }
  1428. function matchedSite(hostname2) {
  1429. for (const s of sites) {
  1430. if (s.matches.test(hostname2)) {
  1431. return s
  1432. }
  1433. }
  1434. return defaultSite
  1435. }
  1436. var hostname = location.hostname
  1437. var currentSite = matchedSite(hostname)
  1438. var host = location.host
  1439. var storageKey4 = host.includes("v2ex")
  1440. ? "avatar:v2ex.com"
  1441. : "avatar:".concat(host)
  1442. async function saveAvatar(userName, src) {
  1443. const values = (await getValue(storageKey4)) || {}
  1444. values[userName] = src
  1445. await setValue(storageKey4, values)
  1446. }
  1447. async function saveAvatars(newValues) {
  1448. let values = (await getValue(storageKey4)) || {}
  1449. values = Object.assign(values, newValues)
  1450. await setValue(storageKey4, values)
  1451. }
  1452. async function clearAvatarData() {
  1453. await deleteValue(storageKey4)
  1454. }
  1455. var cachedValues = {}
  1456. async function reloadCachedValues() {
  1457. cachedValues = (await getValue(storageKey4)) || {}
  1458. }
  1459. function getChangedAavatar(userName) {
  1460. return cachedValues[userName]
  1461. }
  1462. async function initStorage(options) {
  1463. addValueChangeListener(storageKey4, async () => {
  1464. await reloadCachedValues()
  1465. if (options && typeof options.avatarValueChangeListener === "function") {
  1466. options.avatarValueChangeListener()
  1467. }
  1468. })
  1469. await reloadCachedValues()
  1470. console.log(
  1471. "The number of avatars that have been replaced:",
  1472. Object.keys(cachedValues).length
  1473. )
  1474. }
  1475. var host2 = location.host
  1476. var suffix = host2.includes("v2ex") ? "" : "_" + host2
  1477. var isEnabledByDefault = () => {
  1478. if (host2.includes("xxxxxxxx")) {
  1479. return false
  1480. }
  1481. return true
  1482. }
  1483. var settingsTable2 = {
  1484. ["enableCurrentSite_".concat(host2)]: {
  1485. title: i2("settings.enableCurrentSite"),
  1486. defaultValue: isEnabledByDefault(),
  1487. },
  1488. ["style-adventurer".concat(suffix)]: {
  1489. title: "Adventurer",
  1490. icon: "https://api.dicebear.com/6.x/adventurer/svg?seed=JD",
  1491. defaultValue: true,
  1492. group: 2,
  1493. },
  1494. ["style-adventurer-neutral".concat(suffix)]: {
  1495. title: "Adventurer Neutral",
  1496. icon: "https://api.dicebear.com/6.x/adventurer-neutral/svg?seed=JD",
  1497. defaultValue: false,
  1498. group: 2,
  1499. },
  1500. ["style-avataaars".concat(suffix)]: {
  1501. title: "Avataaars",
  1502. icon: "https://api.dicebear.com/6.x/avataaars/svg?seed=JD",
  1503. defaultValue: false,
  1504. group: 2,
  1505. },
  1506. ["style-avataaars-neutral".concat(suffix)]: {
  1507. title: "Avataaars Neutral",
  1508. icon: "https://api.dicebear.com/6.x/avataaars-neutral/svg?seed=JD",
  1509. defaultValue: false,
  1510. group: 2,
  1511. },
  1512. ["style-big-ears".concat(suffix)]: {
  1513. title: "Big Ears",
  1514. icon: "https://api.dicebear.com/6.x/big-ears/svg?seed=JD",
  1515. defaultValue: false,
  1516. group: 2,
  1517. },
  1518. ["style-big-ears-neutral".concat(suffix)]: {
  1519. title: "Big Ears Neutral",
  1520. icon: "https://api.dicebear.com/6.x/big-ears-neutral/svg?seed=JD",
  1521. defaultValue: false,
  1522. group: 2,
  1523. },
  1524. ["style-big-smile".concat(suffix)]: {
  1525. title: "Big Smile",
  1526. icon: "https://api.dicebear.com/6.x/big-smile/svg?seed=JD",
  1527. defaultValue: false,
  1528. group: 2,
  1529. },
  1530. ["style-bottts".concat(suffix)]: {
  1531. title: "Bottts",
  1532. icon: "https://api.dicebear.com/6.x/bottts/svg?seed=JD",
  1533. defaultValue: false,
  1534. group: 2,
  1535. },
  1536. ["style-bottts-neutral".concat(suffix)]: {
  1537. title: "Bottts Neutral",
  1538. icon: "https://api.dicebear.com/6.x/bottts-neutral/svg?seed=JD",
  1539. defaultValue: false,
  1540. group: 2,
  1541. },
  1542. ["style-croodles".concat(suffix)]: {
  1543. title: "Croodles",
  1544. icon: "https://api.dicebear.com/6.x/croodles/svg?seed=JD",
  1545. defaultValue: false,
  1546. group: 2,
  1547. },
  1548. ["style-croodles-neutral".concat(suffix)]: {
  1549. title: "Croodles Neutral",
  1550. icon: "https://api.dicebear.com/6.x/croodles-neutral/svg?seed=JD",
  1551. defaultValue: false,
  1552. group: 2,
  1553. },
  1554. ["style-fun-emoji".concat(suffix)]: {
  1555. title: "Fun Emoji",
  1556. icon: "https://api.dicebear.com/6.x/fun-emoji/svg?seed=JD",
  1557. defaultValue: false,
  1558. group: 2,
  1559. },
  1560. ["style-icons".concat(suffix)]: {
  1561. title: "Icons",
  1562. icon: "https://api.dicebear.com/6.x/icons/svg?seed=JD",
  1563. defaultValue: false,
  1564. group: 2,
  1565. },
  1566. ["style-identicon".concat(suffix)]: {
  1567. title: "Identicon",
  1568. icon: "https://api.dicebear.com/6.x/identicon/svg?seed=JD",
  1569. defaultValue: false,
  1570. group: 2,
  1571. },
  1572. ["style-initials".concat(suffix)]: {
  1573. title: "Initials",
  1574. icon: "https://api.dicebear.com/6.x/initials/svg?seed=JD",
  1575. defaultValue: false,
  1576. group: 2,
  1577. },
  1578. ["style-lorelei".concat(suffix)]: {
  1579. title: "Lorelei",
  1580. icon: "https://api.dicebear.com/6.x/lorelei/svg?seed=JD",
  1581. defaultValue: false,
  1582. group: 2,
  1583. },
  1584. ["style-lorelei-neutral".concat(suffix)]: {
  1585. title: "Lorelei Neutral",
  1586. icon: "https://api.dicebear.com/6.x/lorelei-neutral/svg?seed=JD",
  1587. defaultValue: false,
  1588. group: 2,
  1589. },
  1590. ["style-micah".concat(suffix)]: {
  1591. title: "Micah",
  1592. icon: "https://api.dicebear.com/6.x/micah/svg?seed=JD",
  1593. defaultValue: false,
  1594. group: 2,
  1595. },
  1596. ["style-miniavs".concat(suffix)]: {
  1597. title: "Miniavs",
  1598. icon: "https://api.dicebear.com/6.x/miniavs/svg?seed=JD",
  1599. defaultValue: false,
  1600. group: 2,
  1601. },
  1602. ["style-notionists".concat(suffix)]: {
  1603. title: "Notionists",
  1604. icon: "https://api.dicebear.com/6.x/notionists/svg?seed=JD",
  1605. defaultValue: false,
  1606. group: 2,
  1607. },
  1608. ["style-notionists-neutral".concat(suffix)]: {
  1609. title: "Notionists Neutral",
  1610. icon: "https://api.dicebear.com/6.x/notionists-neutral/svg?seed=JD",
  1611. defaultValue: false,
  1612. group: 2,
  1613. },
  1614. ["style-open-peeps".concat(suffix)]: {
  1615. title: "Open Peeps",
  1616. icon: "https://api.dicebear.com/6.x/open-peeps/svg?seed=JD",
  1617. defaultValue: false,
  1618. group: 2,
  1619. },
  1620. ["style-personas".concat(suffix)]: {
  1621. title: "Personas",
  1622. icon: "https://api.dicebear.com/6.x/personas/svg?seed=JD",
  1623. defaultValue: false,
  1624. group: 2,
  1625. },
  1626. ["style-pixel-art".concat(suffix)]: {
  1627. title: "Pixel Art",
  1628. icon: "https://api.dicebear.com/6.x/pixel-art/svg?seed=JD",
  1629. defaultValue: false,
  1630. group: 2,
  1631. },
  1632. ["style-pixel-art-neutral".concat(suffix)]: {
  1633. title: "Pixel Art Neutral",
  1634. icon: "https://api.dicebear.com/6.x/pixel-art-neutral/svg?seed=JD",
  1635. defaultValue: false,
  1636. group: 2,
  1637. },
  1638. ["style-shapes".concat(suffix)]: {
  1639. title: "Shapes",
  1640. icon: "https://api.dicebear.com/6.x/shapes/svg?seed=JD",
  1641. defaultValue: false,
  1642. group: 2,
  1643. },
  1644. ["style-thumbs".concat(suffix)]: {
  1645. title: "Thumbs",
  1646. icon: "https://api.dicebear.com/6.x/thumbs/svg?seed=JD",
  1647. defaultValue: false,
  1648. group: 2,
  1649. },
  1650. ["style-ugly-avatar".concat(suffix)]: {
  1651. title: "Ugly Avatar",
  1652. icon: "https://cdn.jsdelivr.net/gh/utags/ugly-avatar-generated@main/svg/00/0010afd433ff844eb3da1d22515a96f8.svg",
  1653. defaultValue: false,
  1654. group: 2,
  1655. },
  1656. ["style-gfriends".concat(suffix)]: {
  1657. title: "Japan Girl Friends (NSFW)",
  1658. icon: "https://wsrv.nl/?url=cdn.jsdelivr.net/gh/gfriends/gfriends@master/Content/8-Honnaka/%E8%91%89%E6%9C%88%E3%81%BF%E3%82%8A%E3%81%82.jpg%3Ft%3D1644908887&w=96&h=96&dpr=2&fit=cover&a=focal&fpy=0.35&output=webp",
  1659. defaultValue: false,
  1660. group: 2,
  1661. },
  1662. ["autoReplaceAll".concat(suffix)]: {
  1663. title: i2("settings.autoReplaceAll"),
  1664. defaultValue: false,
  1665. onConfirmChange(checked) {
  1666. if (checked) {
  1667. return confirm(i2("settings.autoReplaceAll.confirm"))
  1668. }
  1669. return true
  1670. },
  1671. group: 3,
  1672. },
  1673. clearData: {
  1674. title: i2("settings.clearData"),
  1675. type: "action",
  1676. async onclick() {
  1677. if (confirm(i2("settings.clearData.confirm"))) {
  1678. await clearAvatarData()
  1679. setTimeout(() => {
  1680. alert(i2("settings.clearData.done"))
  1681. })
  1682. }
  1683. },
  1684. group: 4,
  1685. },
  1686. }
  1687. var avatarStyleList = []
  1688. function updateAvatarStyleList() {
  1689. avatarStyleList = allAvatarStyleList.filter((style) =>
  1690. getSettingsValue("style-".concat(style).concat(suffix))
  1691. )
  1692. if (avatarStyleList.length === 0 && !doc.hidden) {
  1693. setTimeout(async () => {
  1694. alert(i2("alert.needsSelectOneAavatar"))
  1695. await saveSettingsValues({
  1696. ["style-adventurer".concat(suffix)]: true,
  1697. })
  1698. const firstStyleOption = $(
  1699. '.browser_extension_settings_container [data-key="style-adventurer'.concat(
  1700. suffix,
  1701. '"]'
  1702. )
  1703. )
  1704. if (firstStyleOption) {
  1705. firstStyleOption.scrollIntoView({ block: "nearest" })
  1706. }
  1707. }, 200)
  1708. }
  1709. if (getSettingsValue("style-ugly-avatar".concat(suffix))) {
  1710. setTimeout(initRamdomAvatar2)
  1711. }
  1712. if (getSettingsValue("style-gfriends".concat(suffix))) {
  1713. setTimeout(initRamdomAvatar)
  1714. }
  1715. }
  1716. var lastValueOfEnableCurrentSite = true
  1717. var lastValueOfAutoReplaceAll = false
  1718. async function onSettingsChange() {
  1719. if (getSettingsValue("enableCurrentSite_".concat(host2))) {
  1720. if (!lastValueOfEnableCurrentSite) {
  1721. if ($("#rua_tyle")) {
  1722. scanAvatars()
  1723. } else {
  1724. await main()
  1725. }
  1726. }
  1727. } else if (lastValueOfEnableCurrentSite) {
  1728. for (const element of $$("img[data-rua-org-src]")) {
  1729. if (
  1730. element.dataset.ruaOrgSrc &&
  1731. element.src !== element.dataset.ruaOrgSrc
  1732. ) {
  1733. element.src = element.dataset.ruaOrgSrc
  1734. }
  1735. }
  1736. }
  1737. lastValueOfEnableCurrentSite = getSettingsValue(
  1738. "enableCurrentSite_".concat(host2)
  1739. )
  1740. if (
  1741. getSettingsValue("autoReplaceAll".concat(suffix)) &&
  1742. !lastValueOfAutoReplaceAll &&
  1743. !doc.hidden
  1744. ) {
  1745. lastValueOfAutoReplaceAll = true
  1746. scanAvatars()
  1747. }
  1748. lastValueOfAutoReplaceAll = getSettingsValue(
  1749. "autoReplaceAll".concat(suffix)
  1750. )
  1751. updateAvatarStyleList()
  1752. }
  1753. function isAvatar(element) {
  1754. if (!element || element.tagName !== "IMG") {
  1755. return false
  1756. }
  1757. if (element.dataset.ruaUserName) {
  1758. return true
  1759. }
  1760. return false
  1761. }
  1762. var currentTarget
  1763. function addChangeButton(element) {
  1764. currentTarget = element
  1765. const container =
  1766. $("#rua_container") ||
  1767. addElement2(doc.body, "div", {
  1768. id: "rua_container",
  1769. })
  1770. const changeButton =
  1771. $(".change_button.quick", container) ||
  1772. addElement2(container, "button", {
  1773. innerHTML: changeIcon,
  1774. class: "change_button quick",
  1775. async onclick() {
  1776. addClass(changeButton, "active")
  1777. setTimeout(() => {
  1778. removeClass(changeButton, "active")
  1779. }, 200)
  1780. const userName = currentTarget.dataset.ruaUserName || "noname"
  1781. const avatarUrl = getRandomAvatar(userName, avatarStyleList)
  1782. if (avatarUrl) {
  1783. changeAvatar(currentTarget, avatarUrl, true)
  1784. await saveAvatar(userName, avatarUrl)
  1785. }
  1786. },
  1787. })
  1788. const changeButton2 =
  1789. $(".change_button.advanced", container) ||
  1790. addElement2(container, "button", {
  1791. innerHTML: changeIcon,
  1792. class: "change_button advanced",
  1793. async onclick() {
  1794. addClass(changeButton2, "active")
  1795. setTimeout(() => {
  1796. removeClass(changeButton2, "active")
  1797. }, 200)
  1798. const userName = currentTarget.dataset.ruaUserName || "noname"
  1799. const avatarUrl = prompt(i2("prompt.enterAvatarLink"), "")
  1800. if (avatarUrl) {
  1801. changeAvatar(currentTarget, avatarUrl, true)
  1802. await saveAvatar(userName, avatarUrl)
  1803. }
  1804. },
  1805. })
  1806. removeClass(changeButton, "hide")
  1807. removeClass(changeButton2, "hide")
  1808. const pos = getOffsetPosition(element)
  1809. const leftOffset =
  1810. element.clientWidth - changeButton.clientWidth > 20
  1811. ? element.clientWidth - changeButton.clientWidth
  1812. : element.clientWidth - 1
  1813. changeButton.style.top = pos.top + "px"
  1814. changeButton.style.left = pos.left + leftOffset + "px"
  1815. changeButton2.style.top = pos.top + changeButton.clientHeight + "px"
  1816. changeButton2.style.left = pos.left + leftOffset + "px"
  1817. const mouseoutHandler = () => {
  1818. addClass(changeButton, "hide")
  1819. addClass(changeButton2, "hide")
  1820. removeEventListener(element, "mouseout", mouseoutHandler)
  1821. }
  1822. addEventListener(element, "mouseout", mouseoutHandler)
  1823. }
  1824. function changeAvatar(element, src, animation = false) {
  1825. if (element.ruaLoading) {
  1826. return
  1827. }
  1828. if (!element.dataset.ruaOrgSrc) {
  1829. const orgSrc = element.dataset.src || element.src
  1830. element.dataset.ruaOrgSrc = orgSrc
  1831. }
  1832. element.ruaLoading = true
  1833. const imgOnloadHandler = () => {
  1834. if (element.src !== src) {
  1835. return
  1836. }
  1837. element.ruaLoading = false
  1838. removeClass(element, "rua_fadeout")
  1839. removeEventListener(element, "load", imgOnloadHandler)
  1840. removeEventListener(element, "error", imgOnloadHandler)
  1841. }
  1842. if (animation) {
  1843. addClass(element, "rua_fadeout")
  1844. } else {
  1845. element.src =
  1846. "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"
  1847. }
  1848. addEventListener(element, "load", imgOnloadHandler)
  1849. addEventListener(element, "error", imgOnloadHandler)
  1850. element.src = src
  1851. }
  1852. var scanAvatars = throttle(async () => {
  1853. if (doc.hidden || !getSettingsValue("enableCurrentSite_".concat(host2))) {
  1854. return
  1855. }
  1856. const newValues = {}
  1857. const avatars = currentSite.getAvatarElements()
  1858. for (const avatar of avatars) {
  1859. let userName = avatar.dataset.ruaUserName
  1860. if (!userName) {
  1861. userName = currentSite.getUserName(avatar)
  1862. if (!userName) {
  1863. console.error("Can't get username", avatar, userName)
  1864. continue
  1865. }
  1866. avatar.dataset.ruaUserName = userName
  1867. setAttributes(avatar, {
  1868. loading: "lazy",
  1869. decoding: "async",
  1870. referrerpolicy: "no-referrer",
  1871. rel: "noreferrer",
  1872. })
  1873. }
  1874. const newAvatarSrc = getChangedAavatar(userName)
  1875. if (newAvatarSrc && avatar.src !== newAvatarSrc) {
  1876. changeAvatar(avatar, newAvatarSrc)
  1877. } else if (!newAvatarSrc) {
  1878. if (
  1879. avatar.dataset.ruaOrgSrc &&
  1880. avatar.src !== avatar.dataset.ruaOrgSrc
  1881. ) {
  1882. avatar.src = avatar.dataset.ruaOrgSrc
  1883. }
  1884. if (lastValueOfAutoReplaceAll && Object.keys(newValues).length < 3) {
  1885. const avatarUrl = getRandomAvatar(userName, avatarStyleList)
  1886. if (avatarUrl) {
  1887. newValues[userName] = avatarUrl
  1888. } else {
  1889. setTimeout(scanAvatars, 100)
  1890. }
  1891. }
  1892. }
  1893. }
  1894. if (lastValueOfAutoReplaceAll && Object.keys(newValues).length > 0) {
  1895. await saveAvatars(newValues)
  1896. }
  1897. }, 300)
  1898. async function main() {
  1899. await runOnce("main", async () => {
  1900. await initSettings({
  1901. id: "replace-ugly-avatars",
  1902. title: i2("settings.title"),
  1903. footer: "\n <p>"
  1904. .concat(
  1905. i2("settings.information"),
  1906. '</p>\n <p>\n <a href="https://github.com/utags/replace-ugly-avatars/issues" target="_blank">\n '
  1907. )
  1908. .concat(
  1909. i2("settings.report"),
  1910. '\n </a></p>\n <p>Made with \u2764\uFE0F by\n <a href="https://www.pipecraft.net/" target="_blank">\n Pipecraft\n </a></p>'
  1911. ),
  1912. settingsTable: settingsTable2,
  1913. async onValueChange() {
  1914. await onSettingsChange()
  1915. },
  1916. })
  1917. })
  1918. lastValueOfEnableCurrentSite = getSettingsValue(
  1919. "enableCurrentSite_".concat(host2)
  1920. )
  1921. lastValueOfAutoReplaceAll = getSettingsValue(
  1922. "autoReplaceAll".concat(suffix)
  1923. )
  1924. if (!getSettingsValue("enableCurrentSite_".concat(host2))) {
  1925. return
  1926. }
  1927. updateAvatarStyleList()
  1928. runWhenHeadExists(() => {
  1929. addElement2("style", {
  1930. textContent: content_default,
  1931. id: "rua_tyle",
  1932. })
  1933. })
  1934. addEventListener(doc, "mouseover", (event) => {
  1935. const target = event.target
  1936. if (!isAvatar(target)) {
  1937. return
  1938. }
  1939. addChangeButton(target)
  1940. })
  1941. addEventListener(doc, "visibilitychange", () => {
  1942. if (!doc.hidden) {
  1943. scanAvatars()
  1944. }
  1945. })
  1946. await initStorage({
  1947. avatarValueChangeListener() {
  1948. scanAvatars()
  1949. },
  1950. })
  1951. if ($("img")) {
  1952. scanAvatars()
  1953. }
  1954. const observer = new MutationObserver(() => {
  1955. scanAvatars()
  1956. })
  1957. observer.observe(doc, {
  1958. childList: true,
  1959. subtree: true,
  1960. })
  1961. }
  1962. runWhenHeadExists(async () => {
  1963. if (doc.documentElement.dataset.replaceUglyAvatars === void 0) {
  1964. doc.documentElement.dataset.replaceUglyAvatars = "".concat(host2)
  1965. await main()
  1966. }
  1967. })
  1968. })()

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址