CSDN优化

支持PC和手机端、屏蔽广告、优化浏览体验、重定向拦截的Url、自动展开全文、自动展开代码块、全文居中、允许复制内容、去除复制内容的小尾巴、自定义屏蔽元素等

  1. // ==UserScript==
  2. // @name CSDN优化
  3. // @namespace https://github.com/WhiteSevs/TamperMonkeyScript
  4. // @version 2025.7.12
  5. // @author WhiteSevs
  6. // @description 支持PC和手机端、屏蔽广告、优化浏览体验、重定向拦截的Url、自动展开全文、自动展开代码块、全文居中、允许复制内容、去除复制内容的小尾巴、自定义屏蔽元素等
  7. // @license GPL-3.0-only
  8. // @icon data:image/png;base64,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
  9. // @supportURL https://github.com/WhiteSevs/TamperMonkeyScript/issues
  10. // @match *://*.csdn.net/*
  11. // @require https://fastly.jsdelivr.net/gh/WhiteSevs/TamperMonkeyScript@86be74b83fca4fa47521cded28377b35e1d7d2ac/lib/CoverUMD/index.js
  12. // @require https://fastly.jsdelivr.net/npm/@whitesev/utils@2.7.0/dist/index.umd.js
  13. // @require https://fastly.jsdelivr.net/npm/@whitesev/domutils@1.5.11/dist/index.umd.js
  14. // @require https://fastly.jsdelivr.net/npm/@whitesev/pops@2.1.13/dist/index.umd.js
  15. // @require https://fastly.jsdelivr.net/npm/qmsg@1.3.8/dist/index.umd.js
  16. // @connect blog.csdn.net
  17. // @connect mp-action.csdn.net
  18. // @grant GM_deleteValue
  19. // @grant GM_getResourceText
  20. // @grant GM_getValue
  21. // @grant GM_info
  22. // @grant GM_registerMenuCommand
  23. // @grant GM_setValue
  24. // @grant GM_unregisterMenuCommand
  25. // @grant GM_xmlhttpRequest
  26. // @grant unsafeWindow
  27. // @run-at document-start
  28. // ==/UserScript==
  29.  
  30. (function (Qmsg, DOMUtils, Utils, pops) {
  31. 'use strict';
  32.  
  33. var _GM_deleteValue = /* @__PURE__ */ (() => typeof GM_deleteValue != "undefined" ? GM_deleteValue : void 0)();
  34. var _GM_getResourceText = /* @__PURE__ */ (() => typeof GM_getResourceText != "undefined" ? GM_getResourceText : void 0)();
  35. var _GM_getValue = /* @__PURE__ */ (() => typeof GM_getValue != "undefined" ? GM_getValue : void 0)();
  36. var _GM_info = /* @__PURE__ */ (() => typeof GM_info != "undefined" ? GM_info : void 0)();
  37. var _GM_registerMenuCommand = /* @__PURE__ */ (() => typeof GM_registerMenuCommand != "undefined" ? GM_registerMenuCommand : void 0)();
  38. var _GM_setValue = /* @__PURE__ */ (() => typeof GM_setValue != "undefined" ? GM_setValue : void 0)();
  39. var _GM_unregisterMenuCommand = /* @__PURE__ */ (() => typeof GM_unregisterMenuCommand != "undefined" ? GM_unregisterMenuCommand : void 0)();
  40. var _GM_xmlhttpRequest = /* @__PURE__ */ (() => typeof GM_xmlhttpRequest != "undefined" ? GM_xmlhttpRequest : void 0)();
  41. var _unsafeWindow = /* @__PURE__ */ (() => typeof unsafeWindow != "undefined" ? unsafeWindow : void 0)();
  42. var _monkeyWindow = /* @__PURE__ */ (() => window)();
  43. const CommonUtil = {
  44. /**
  45. * 移除元素(未出现也可以等待出现)
  46. * @param selector 元素选择器
  47. */
  48. waitRemove(...args) {
  49. args.forEach((selector) => {
  50. if (typeof selector !== "string") {
  51. return;
  52. }
  53. utils.waitNodeList(selector).then((nodeList) => {
  54. nodeList.forEach(($el) => $el.remove());
  55. });
  56. });
  57. },
  58. /**
  59. * 添加屏蔽CSS
  60. * @param args
  61. * @example
  62. * addBlockCSS("")
  63. * addBlockCSS("","")
  64. * addBlockCSS(["",""])
  65. */
  66. addBlockCSS(...args) {
  67. let selectorList = [];
  68. if (args.length === 0) {
  69. return;
  70. }
  71. if (args.length === 1 && typeof args[0] === "string" && args[0].trim() === "") {
  72. return;
  73. }
  74. args.forEach((selector) => {
  75. if (Array.isArray(selector)) {
  76. selectorList = selectorList.concat(selector);
  77. } else {
  78. selectorList.push(selector);
  79. }
  80. });
  81. return addStyle(`${selectorList.join(",\n")}{display: none !important;}`);
  82. },
  83. /**
  84. * 设置GM_getResourceText的style内容
  85. * @param resourceMapData 资源数据
  86. * @example
  87. * setGMResourceCSS({
  88. * keyName: "ViewerCSS",
  89. * url: "https://example.com/example.css",
  90. * })
  91. */
  92. setGMResourceCSS(resourceMapData) {
  93. let cssText = typeof _GM_getResourceText === "function" ? _GM_getResourceText(resourceMapData.keyName) : null;
  94. if (typeof cssText === "string" && cssText) {
  95. addStyle(cssText);
  96. } else {
  97. CommonUtil.loadStyleLink(resourceMapData.url);
  98. }
  99. },
  100. /**
  101. * 添加<link>标签
  102. * @param url
  103. * @example
  104. * loadStyleLink("https://example.com/example.css")
  105. */
  106. async loadStyleLink(url) {
  107. let $link = document.createElement("link");
  108. $link.rel = "stylesheet";
  109. $link.type = "text/css";
  110. $link.href = url;
  111. DOMUtils.ready(() => {
  112. document.head.appendChild($link);
  113. });
  114. },
  115. /**
  116. * 添加<script>标签
  117. * @param url
  118. * @example
  119. * loadStyleLink("https://example.com/example.js")
  120. */
  121. async loadScript(url) {
  122. let $script = document.createElement("script");
  123. $script.src = url;
  124. return new Promise((resolve) => {
  125. $script.onload = () => {
  126. resolve(null);
  127. };
  128. (document.head || document.documentElement).appendChild($script);
  129. });
  130. },
  131. /**
  132. * 将url修复,例如只有search的链接修复为完整的链接
  133. *
  134. * 注意:不包括http转https
  135. * @param url 需要修复的链接
  136. * @example
  137. * 修复前:`/xxx/xxx?ss=ssss`
  138. * 修复后:`https://xxx.xxx.xxx/xxx/xxx?ss=ssss`
  139. * @example
  140. * 修复前:`//xxx/xxx?ss=ssss`
  141. * 修复后:`https://xxx.xxx.xxx/xxx/xxx?ss=ssss`
  142. * @example
  143. * 修复前:`https://xxx.xxx.xxx/xxx/xxx?ss=ssss`
  144. * 修复后:`https://xxx.xxx.xxx/xxx/xxx?ss=ssss`
  145. * @example
  146. * 修复前:`xxx/xxx?ss=ssss`
  147. * 修复后:`https://xxx.xxx.xxx/xxx/xxx?ss=ssss`
  148. */
  149. fixUrl(url) {
  150. url = url.trim();
  151. if (url.match(/^http(s|):\/\//i)) {
  152. return url;
  153. } else {
  154. if (!url.startsWith("/")) {
  155. url += "/";
  156. }
  157. url = window.location.origin + url;
  158. return url;
  159. }
  160. },
  161. /**
  162. * http转https
  163. * @param url 需要修复的链接
  164. * @example
  165. * 修复前:
  166. * 修复后:
  167. * @example
  168. * 修复前:
  169. * 修复后:
  170. */
  171. fixHttps(url) {
  172. if (url.startsWith("https://")) {
  173. return url;
  174. }
  175. if (!url.startsWith("http://")) {
  176. return url;
  177. }
  178. let urlInstance = new URL(url);
  179. urlInstance.protocol = "https:";
  180. return urlInstance.toString();
  181. },
  182. /**
  183. * 禁止页面滚动,默认锁定html和body
  184. * @example
  185. * lockScroll();
  186. * @example
  187. * lockScroll(document.body);
  188. */
  189. lockScroll(...args) {
  190. let $hidden = document.createElement("style");
  191. $hidden.innerHTML = /*css*/
  192. `
  193. .pops-overflow-hidden-important {
  194. overflow: hidden !important;
  195. }
  196. `;
  197. let $elList = [document.documentElement, document.body].concat(
  198. ...args || []
  199. );
  200. $elList.forEach(($el) => {
  201. $el.classList.add("pops-overflow-hidden-important");
  202. });
  203. (document.head || document.documentElement).appendChild($hidden);
  204. return {
  205. /**
  206. * 解除锁定
  207. */
  208. recovery() {
  209. $elList.forEach(($el) => {
  210. $el.classList.remove("pops-overflow-hidden-important");
  211. });
  212. $hidden.remove();
  213. }
  214. };
  215. },
  216. /**
  217. * 获取剪贴板文本
  218. */
  219. async getClipboardText() {
  220. function readClipboardText(resolve) {
  221. navigator.clipboard.readText().then((clipboardText) => {
  222. resolve(clipboardText);
  223. }).catch((error) => {
  224. log.error("读取剪贴板内容失败👉", error);
  225. resolve("");
  226. });
  227. }
  228. function requestPermissionsWithClipboard(resolve) {
  229. navigator.permissions.query({
  230. // @ts-ignore
  231. name: "clipboard-read"
  232. }).then((permissionStatus) => {
  233. readClipboardText(resolve);
  234. }).catch((error) => {
  235. log.error(
  236. "申请剪贴板权限失败,尝试直接读取👉",
  237. error.message ?? error.name ?? error.stack
  238. );
  239. readClipboardText(resolve);
  240. });
  241. }
  242. function checkClipboardApi() {
  243. if (typeof navigator?.clipboard?.readText !== "function") {
  244. return false;
  245. }
  246. if (typeof navigator?.permissions?.query !== "function") {
  247. return false;
  248. }
  249. return true;
  250. }
  251. return new Promise((resolve) => {
  252. if (!checkClipboardApi()) {
  253. resolve("");
  254. return;
  255. }
  256. if (document.hasFocus()) {
  257. requestPermissionsWithClipboard(resolve);
  258. } else {
  259. window.addEventListener(
  260. "focus",
  261. () => {
  262. requestPermissionsWithClipboard(resolve);
  263. },
  264. {
  265. once: true
  266. }
  267. );
  268. }
  269. });
  270. },
  271. /**
  272. * html转义
  273. * @param unsafe
  274. */
  275. escapeHtml(unsafe) {
  276. return unsafe.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;").replace(/©/g, "&copy;").replace(/®/g, "&reg;").replace(/™/g, "&trade;").replace(/→/g, "&rarr;").replace(/←/g, "&larr;").replace(/↑/g, "&uarr;").replace(/↓/g, "&darr;").replace(/—/g, "&mdash;").replace(/–/g, "&ndash;").replace(/…/g, "&hellip;").replace(/ /g, "&nbsp;").replace(/\r\n/g, "<br>").replace(/\r/g, "<br>").replace(/\n/g, "<br>").replace(/\t/g, "&nbsp;&nbsp;&nbsp;&nbsp;");
  277. }
  278. };
  279. const PanelSettingConfig = {
  280. /** Toast位置 */
  281. qmsg_config_position: {
  282. key: "qmsg-config-position",
  283. defaultValue: "bottom"
  284. },
  285. /** 最多显示的数量 */
  286. qmsg_config_maxnums: {
  287. key: "qmsg-config-maxnums",
  288. defaultValue: 3
  289. },
  290. /** 逆序弹出 */
  291. qmsg_config_showreverse: {
  292. key: "qmsg-config-showreverse",
  293. defaultValue: false
  294. }
  295. };
  296. const utils = Utils.noConflict();
  297. const domUtils = DOMUtils.noConflict();
  298. const __pops = pops;
  299. const log = new utils.Log(
  300. _GM_info,
  301. _unsafeWindow.console || _monkeyWindow.console
  302. );
  303. let SCRIPT_NAME = _GM_info?.script?.name || void 0;
  304. pops.config.Utils.AnyTouch();
  305. const DEBUG = false;
  306. log.config({
  307. debug: DEBUG,
  308. logMaxCount: 1e3,
  309. autoClearConsole: true,
  310. tag: true
  311. });
  312. Qmsg.config(
  313. Object.defineProperties(
  314. {
  315. html: true,
  316. autoClose: true,
  317. showClose: false
  318. },
  319. {
  320. position: {
  321. get() {
  322. return Panel.getValue(
  323. PanelSettingConfig.qmsg_config_position.key,
  324. PanelSettingConfig.qmsg_config_position.defaultValue
  325. );
  326. }
  327. },
  328. maxNums: {
  329. get() {
  330. return Panel.getValue(
  331. PanelSettingConfig.qmsg_config_maxnums.key,
  332. PanelSettingConfig.qmsg_config_maxnums.defaultValue
  333. );
  334. }
  335. },
  336. showReverse: {
  337. get() {
  338. return Panel.getValue(
  339. PanelSettingConfig.qmsg_config_showreverse.key,
  340. PanelSettingConfig.qmsg_config_showreverse.defaultValue
  341. );
  342. }
  343. },
  344. zIndex: {
  345. get() {
  346. let maxZIndex = Utils.getMaxZIndex();
  347. let popsMaxZIndex = pops.config.InstanceUtils.getPopsMaxZIndex().zIndex;
  348. return Utils.getMaxValue(maxZIndex, popsMaxZIndex) + 100;
  349. }
  350. }
  351. }
  352. )
  353. );
  354. __pops.GlobalConfig.setGlobalConfig({
  355. zIndex: () => {
  356. let maxZIndex = Utils.getMaxZIndex(void 0, void 0, ($ele) => {
  357. if ($ele?.classList?.contains("qmsg-shadow-container")) {
  358. return false;
  359. }
  360. if ($ele?.closest("qmsg") && $ele.getRootNode() instanceof ShadowRoot) {
  361. return false;
  362. }
  363. });
  364. let popsMaxZIndex = pops.config.InstanceUtils.getPopsMaxZIndex().zIndex;
  365. return Utils.getMaxValue(maxZIndex, popsMaxZIndex) + 100;
  366. },
  367. mask: {
  368. // 开启遮罩层
  369. enable: true,
  370. // 取消点击遮罩层的事件
  371. clickEvent: {
  372. toClose: false,
  373. toHide: false
  374. }
  375. }
  376. });
  377. const GM_Menu = new utils.GM_Menu({
  378. GM_getValue: _GM_getValue,
  379. GM_setValue: _GM_setValue,
  380. GM_registerMenuCommand: _GM_registerMenuCommand,
  381. GM_unregisterMenuCommand: _GM_unregisterMenuCommand
  382. });
  383. const httpx = new utils.Httpx({
  384. xmlHttpRequest: _GM_xmlhttpRequest,
  385. logDetails: DEBUG
  386. });
  387. httpx.interceptors.request.use((data) => {
  388. return data;
  389. });
  390. httpx.interceptors.response.use(void 0, (data) => {
  391. log.error("拦截器-请求错误", data);
  392. if (data.type === "onabort") {
  393. Qmsg.warning("请求取消", { consoleLogContent: true });
  394. } else if (data.type === "onerror") {
  395. Qmsg.error("请求异常", { consoleLogContent: true });
  396. } else if (data.type === "ontimeout") {
  397. Qmsg.error("请求超时", { consoleLogContent: true });
  398. } else {
  399. Qmsg.error("其它错误", { consoleLogContent: true });
  400. }
  401. return data;
  402. });
  403. ({
  404. Object: {
  405. defineProperty: _unsafeWindow.Object.defineProperty
  406. },
  407. Function: {
  408. apply: _unsafeWindow.Function.prototype.apply,
  409. call: _unsafeWindow.Function.prototype.call
  410. },
  411. Element: {
  412. appendChild: _unsafeWindow.Element.prototype.appendChild
  413. },
  414. setTimeout: _unsafeWindow.setTimeout
  415. });
  416. const addStyle = utils.addStyle.bind(utils);
  417. const $ = document.querySelector.bind(document);
  418. const $$ = document.querySelectorAll.bind(document);
  419. new utils.GM_Cookie();
  420. const KEY = "GM_Panel";
  421. const ATTRIBUTE_INIT = "data-init";
  422. const ATTRIBUTE_KEY = "data-key";
  423. const ATTRIBUTE_DEFAULT_VALUE = "data-default-value";
  424. const ATTRIBUTE_INIT_MORE_VALUE = "data-init-more-value";
  425. const PROPS_STORAGE_API = "data-storage-api";
  426. const PanelUISize = {
  427. /**
  428. * 一般设置界面的尺寸
  429. */
  430. setting: {
  431. get width() {
  432. if (window.innerWidth < 550) {
  433. return "88vw";
  434. } else if (window.innerWidth < 700) {
  435. return "550px";
  436. } else {
  437. return "700px";
  438. }
  439. },
  440. get height() {
  441. if (window.innerHeight < 450) {
  442. return "70vh";
  443. } else if (window.innerHeight < 550) {
  444. return "450px";
  445. } else {
  446. return "550px";
  447. }
  448. }
  449. }
  450. };
  451. class StorageUtils {
  452. /** 存储的键名 */
  453. storageKey;
  454. listenerData;
  455. /**
  456. * 存储的键名,可以是多层的,如:a.b.c
  457. *
  458. * 那就是
  459. * {
  460. * "a": {
  461. * "b": {
  462. * "c": {
  463. * ...你的数据
  464. * }
  465. * }
  466. * }
  467. * }
  468. * @param key
  469. */
  470. constructor(key) {
  471. if (typeof key === "string") {
  472. let trimKey = key.trim();
  473. if (trimKey == "") {
  474. throw new Error("key参数不能为空字符串");
  475. }
  476. this.storageKey = trimKey;
  477. } else {
  478. throw new Error("key参数类型错误,必须是字符串");
  479. }
  480. this.listenerData = new Utils.Dictionary();
  481. }
  482. /**
  483. * 获取本地值
  484. */
  485. getLocalValue() {
  486. let localValue = _GM_getValue(this.storageKey);
  487. if (localValue == null) {
  488. localValue = {};
  489. this.setLocalValue(localValue);
  490. }
  491. return localValue;
  492. }
  493. /**
  494. * 设置本地值
  495. * @param value
  496. */
  497. setLocalValue(value) {
  498. _GM_setValue(this.storageKey, value);
  499. }
  500. /**
  501. * 设置值
  502. * @param key 键
  503. * @param value 值
  504. */
  505. set(key, value) {
  506. let oldValue = this.get(key);
  507. let localValue = this.getLocalValue();
  508. Reflect.set(localValue, key, value);
  509. this.setLocalValue(localValue);
  510. this.triggerValueChangeListener(key, oldValue, value);
  511. }
  512. /**
  513. * 获取值
  514. * @param key 键
  515. * @param defaultValue 默认值
  516. */
  517. get(key, defaultValue) {
  518. let localValue = this.getLocalValue();
  519. return Reflect.get(localValue, key) ?? defaultValue;
  520. }
  521. /**
  522. * 获取所有值
  523. */
  524. getAll() {
  525. let localValue = this.getLocalValue();
  526. return localValue;
  527. }
  528. /**
  529. * 删除值
  530. * @param key 键
  531. */
  532. delete(key) {
  533. let oldValue = this.get(key);
  534. let localValue = this.getLocalValue();
  535. Reflect.deleteProperty(localValue, key);
  536. this.setLocalValue(localValue);
  537. this.triggerValueChangeListener(key, oldValue, void 0);
  538. }
  539. /**
  540. * 判断是否存在该值
  541. */
  542. has(key) {
  543. let localValue = this.getLocalValue();
  544. return Reflect.has(localValue, key);
  545. }
  546. /**
  547. * 获取所有键
  548. */
  549. keys() {
  550. let localValue = this.getLocalValue();
  551. return Reflect.ownKeys(localValue);
  552. }
  553. /**
  554. * 获取所有值
  555. */
  556. values() {
  557. let localValue = this.getLocalValue();
  558. return Reflect.ownKeys(localValue).map(
  559. (key) => Reflect.get(localValue, key)
  560. );
  561. }
  562. /**
  563. * 清空所有值
  564. */
  565. clear() {
  566. _GM_deleteValue(this.storageKey);
  567. }
  568. /**
  569. * 监听值改变
  570. * + .set
  571. * + .delete
  572. * @param key 监听的键
  573. * @param callback 值改变的回调函数
  574. */
  575. addValueChangeListener(key, callback) {
  576. let listenerId = Math.random();
  577. let listenerData = this.listenerData.get(key) || [];
  578. listenerData.push({
  579. id: listenerId,
  580. key,
  581. callback
  582. });
  583. this.listenerData.set(key, listenerData);
  584. return listenerId;
  585. }
  586. /**
  587. * 移除监听
  588. * @param listenerId 监听的id或键名
  589. */
  590. removeValueChangeListener(listenerId) {
  591. let flag = false;
  592. for (const [key, listenerData] of this.listenerData.entries()) {
  593. for (let index = 0; index < listenerData.length; index++) {
  594. const value = listenerData[index];
  595. if (typeof listenerId === "string" && value.key === listenerId || typeof listenerId === "number" && value.id === listenerId) {
  596. listenerData.splice(index, 1);
  597. index--;
  598. flag = true;
  599. }
  600. }
  601. this.listenerData.set(key, listenerData);
  602. }
  603. return flag;
  604. }
  605. /**
  606. * 主动触发监听器
  607. * @param key 键
  608. * @param oldValue (可选)旧值
  609. * @param newValue (可选)新值
  610. */
  611. triggerValueChangeListener(key, oldValue, newValue) {
  612. if (!this.listenerData.has(key)) {
  613. return;
  614. }
  615. let listenerData = this.listenerData.get(key);
  616. for (let index = 0; index < listenerData.length; index++) {
  617. const data = listenerData[index];
  618. if (typeof data.callback === "function") {
  619. let value = this.get(key);
  620. let __newValue;
  621. let __oldValue;
  622. if (typeof oldValue !== "undefined" && arguments.length >= 2) {
  623. __oldValue = oldValue;
  624. } else {
  625. __oldValue = value;
  626. }
  627. if (typeof newValue !== "undefined" && arguments.length > 2) {
  628. __newValue = newValue;
  629. } else {
  630. __newValue = value;
  631. }
  632. data.callback(key, __oldValue, __newValue);
  633. }
  634. }
  635. }
  636. }
  637. const PopsPanelStorageApi = new StorageUtils(KEY);
  638. const PanelContent = {
  639. $data: {
  640. /**
  641. * @private
  642. */
  643. __contentConfig: null,
  644. get contentConfig() {
  645. if (this.__contentConfig == null) {
  646. this.__contentConfig = new utils.Dictionary();
  647. }
  648. return this.__contentConfig;
  649. }
  650. },
  651. /**
  652. * 设置所有配置项,用于初始化默认的值
  653. *
  654. * 如果是第一组添加的话,那么它默认就是设置菜单打开的配置
  655. * @param configList 配置项
  656. */
  657. addContentConfig(configList) {
  658. if (!Array.isArray(configList)) {
  659. configList = [configList];
  660. }
  661. let index = this.$data.contentConfig.keys().length;
  662. this.$data.contentConfig.set(index, configList);
  663. },
  664. /**
  665. * 获取所有的配置内容,用于初始化默认的值
  666. */
  667. getAllContentConfig() {
  668. return this.$data.contentConfig.values().flat();
  669. },
  670. /**
  671. * 获取配置内容
  672. * @param index 配置索引
  673. */
  674. getConfig(index = 0) {
  675. return this.$data.contentConfig.get(index) ?? [];
  676. },
  677. /**
  678. * 获取默认左侧底部的配置项
  679. */
  680. getDefaultBottomContentConfig() {
  681. return [
  682. {
  683. id: "script-version",
  684. title: `版本:${_GM_info?.script?.version || "未知"}`,
  685. isBottom: true,
  686. forms: [],
  687. clickFirstCallback(event, rightHeaderElement, rightContainerElement) {
  688. let supportURL = _GM_info?.script?.supportURL || _GM_info?.script?.namespace;
  689. if (typeof supportURL === "string" && utils.isNotNull(supportURL)) {
  690. window.open(supportURL, "_blank");
  691. }
  692. return false;
  693. }
  694. }
  695. ];
  696. }
  697. };
  698. const PanelMenu = {
  699. $data: {
  700. __menuOption: [
  701. {
  702. key: "show_pops_panel_setting",
  703. text: "⚙ 设置",
  704. autoReload: false,
  705. isStoreValue: false,
  706. showText(text) {
  707. return text;
  708. },
  709. callback: () => {
  710. Panel.showPanel(PanelContent.getConfig(0));
  711. }
  712. }
  713. ],
  714. get menuOption() {
  715. return this.__menuOption;
  716. }
  717. },
  718. init() {
  719. this.initExtensionsMenu();
  720. },
  721. /**
  722. * 初始化菜单项
  723. */
  724. initExtensionsMenu() {
  725. if (!Panel.isTopWindow()) {
  726. return;
  727. }
  728. GM_Menu.add(this.$data.menuOption);
  729. },
  730. /**
  731. * 添加菜单项
  732. * @param option 菜单配置
  733. */
  734. addMenuOption(option) {
  735. if (!Array.isArray(option)) {
  736. option = [option];
  737. }
  738. this.$data.menuOption.push(...option);
  739. },
  740. /**
  741. * 更新菜单项
  742. * @param option 菜单配置
  743. */
  744. updateMenuOption(option) {
  745. if (!Array.isArray(option)) {
  746. option = [option];
  747. }
  748. option.forEach((optionItem) => {
  749. let findIndex = this.$data.menuOption.findIndex((it) => {
  750. return it.key === optionItem.key;
  751. });
  752. if (findIndex !== -1) {
  753. this.$data.menuOption[findIndex] = optionItem;
  754. }
  755. });
  756. },
  757. /**
  758. * 获取菜单项
  759. * @param [index=0] 索引
  760. */
  761. getMenuOption(index = 0) {
  762. return this.$data.menuOption[index];
  763. },
  764. /**
  765. * 删除菜单项
  766. * @param [index=0] 索引
  767. */
  768. deleteMenuOption(index = 0) {
  769. this.$data.menuOption.splice(index, 1);
  770. }
  771. };
  772. const Panel = {
  773. /** 数据 */
  774. $data: {
  775. /**
  776. * @private
  777. */
  778. __configDefaultValueData: null,
  779. /**
  780. * @private
  781. */
  782. __onceExecMenuData: null,
  783. /**
  784. * @private
  785. */
  786. __onceExecData: null,
  787. /**
  788. * @private
  789. */
  790. __panelConfig: {},
  791. $panel: null,
  792. /**
  793. * 菜单项的默认值
  794. */
  795. get configDefaultValueData() {
  796. if (this.__configDefaultValueData == null) {
  797. this.__configDefaultValueData = new utils.Dictionary();
  798. }
  799. return this.__configDefaultValueData;
  800. },
  801. /**
  802. * 成功只执行了一次的项
  803. */
  804. get onceExecMenuData() {
  805. if (this.__onceExecMenuData == null) {
  806. this.__onceExecMenuData = new utils.Dictionary();
  807. }
  808. return this.__onceExecMenuData;
  809. },
  810. /**
  811. * 成功只执行了一次的项
  812. */
  813. get onceExecData() {
  814. if (this.__onceExecData == null) {
  815. this.__onceExecData = new utils.Dictionary();
  816. }
  817. return this.__onceExecData;
  818. },
  819. /** 脚本名,一般用在设置的标题上 */
  820. get scriptName() {
  821. return SCRIPT_NAME;
  822. },
  823. /**
  824. * pops.panel的默认配置
  825. */
  826. get panelConfig() {
  827. return this.__panelConfig;
  828. },
  829. set panelConfig(value) {
  830. this.__panelConfig = value;
  831. },
  832. /** 菜单项的总值在本地数据配置的键名 */
  833. key: KEY,
  834. /** 菜单项在attributes上配置的菜单键 */
  835. attributeKeyName: ATTRIBUTE_KEY,
  836. /** 菜单项在attributes上配置的菜单默认值 */
  837. attributeDefaultValueName: ATTRIBUTE_DEFAULT_VALUE
  838. },
  839. init() {
  840. this.initContentDefaultValue();
  841. PanelMenu.init();
  842. },
  843. /** 判断是否是顶层窗口 */
  844. isTopWindow() {
  845. return _unsafeWindow.top === _unsafeWindow.self;
  846. },
  847. /** 初始化菜单项的默认值保存到本地数据中 */
  848. initContentDefaultValue() {
  849. const initDefaultValue = (config) => {
  850. if (!config.attributes) {
  851. return;
  852. }
  853. if (config.type === "button" || config.type === "forms" || config.type === "deepMenu") {
  854. return;
  855. }
  856. let needInitConfig = {};
  857. let key = config.attributes[ATTRIBUTE_KEY];
  858. if (key != null) {
  859. needInitConfig[key] = config.attributes[ATTRIBUTE_DEFAULT_VALUE];
  860. }
  861. let __attr_init__ = config.attributes[ATTRIBUTE_INIT];
  862. if (typeof __attr_init__ === "function") {
  863. let __attr_result__ = __attr_init__();
  864. if (typeof __attr_result__ === "boolean" && !__attr_result__) {
  865. return;
  866. }
  867. }
  868. let initMoreValue = config.attributes[ATTRIBUTE_INIT_MORE_VALUE];
  869. if (initMoreValue && typeof initMoreValue === "object") {
  870. Object.assign(needInitConfig, initMoreValue);
  871. }
  872. let needInitConfigList = Object.keys(needInitConfig);
  873. if (!needInitConfigList.length) {
  874. log.warn(["请先配置键", config]);
  875. return;
  876. }
  877. needInitConfigList.forEach((__key) => {
  878. let __defaultValue = needInitConfig[__key];
  879. this.setDefaultValue(__key, __defaultValue);
  880. });
  881. };
  882. const loopInitDefaultValue = (configList) => {
  883. for (let index = 0; index < configList.length; index++) {
  884. let configItem = configList[index];
  885. initDefaultValue(configItem);
  886. let childForms = configItem.forms;
  887. if (childForms && Array.isArray(childForms)) {
  888. loopInitDefaultValue(childForms);
  889. }
  890. }
  891. };
  892. const contentConfigList = [...PanelContent.getAllContentConfig()];
  893. for (let index = 0; index < contentConfigList.length; index++) {
  894. let leftContentConfigItem = contentConfigList[index];
  895. if (!leftContentConfigItem.forms) {
  896. continue;
  897. }
  898. const rightContentConfigList = leftContentConfigItem.forms;
  899. if (rightContentConfigList && Array.isArray(rightContentConfigList)) {
  900. loopInitDefaultValue(rightContentConfigList);
  901. }
  902. }
  903. },
  904. /**
  905. * 设置初始化使用的默认值
  906. */
  907. setDefaultValue(key, defaultValue) {
  908. if (this.$data.configDefaultValueData.has(key)) {
  909. log.warn("请检查该key(已存在): " + key);
  910. }
  911. this.$data.configDefaultValueData.set(key, defaultValue);
  912. },
  913. /**
  914. * 设置值
  915. * @param key 键
  916. * @param value 值
  917. */
  918. setValue(key, value) {
  919. PopsPanelStorageApi.set(key, value);
  920. },
  921. /**
  922. * 获取值
  923. * @param key 键
  924. * @param defaultValue 默认值
  925. */
  926. getValue(key, defaultValue) {
  927. let localValue = PopsPanelStorageApi.get(key);
  928. if (localValue == null) {
  929. if (this.$data.configDefaultValueData.has(key)) {
  930. return this.$data.configDefaultValueData.get(key);
  931. }
  932. return defaultValue;
  933. }
  934. return localValue;
  935. },
  936. /**
  937. * 删除值
  938. * @param key 键
  939. */
  940. deleteValue(key) {
  941. PopsPanelStorageApi.delete(key);
  942. },
  943. /**
  944. * 判断该键是否存在
  945. * @param key 键
  946. */
  947. hasKey(key) {
  948. return PopsPanelStorageApi.has(key);
  949. },
  950. /**
  951. * 监听调用setValue、deleteValue
  952. * @param key 需要监听的键
  953. * @param callback
  954. */
  955. addValueChangeListener(key, callback) {
  956. let listenerId = PopsPanelStorageApi.addValueChangeListener(
  957. key,
  958. (__key, __newValue, __oldValue) => {
  959. callback(key, __oldValue, __newValue);
  960. }
  961. );
  962. return listenerId;
  963. },
  964. /**
  965. * 移除监听
  966. * @param listenerId 监听的id
  967. */
  968. removeValueChangeListener(listenerId) {
  969. PopsPanelStorageApi.removeValueChangeListener(listenerId);
  970. },
  971. /**
  972. * 主动触发菜单值改变的回调
  973. * @param key 菜单键
  974. * @param newValue 想要触发的新值,默认使用当前值
  975. * @param oldValue 想要触发的旧值,默认使用当前值
  976. */
  977. triggerMenuValueChange(key, newValue, oldValue) {
  978. PopsPanelStorageApi.triggerValueChangeListener(key, oldValue, newValue);
  979. },
  980. /**
  981. * 移除已执行的仅执行一次的菜单
  982. * @param key 键
  983. */
  984. deleteExecMenuOnce(key) {
  985. this.$data.onceExecMenuData.delete(key);
  986. let flag = PopsPanelStorageApi.removeValueChangeListener(key);
  987. return flag;
  988. },
  989. /**
  990. * 移除已执行的仅执行一次的菜单
  991. * @param key 键
  992. */
  993. deleteOnceExec(key) {
  994. this.$data.onceExecData.delete(key);
  995. },
  996. /**
  997. * 执行菜单
  998. *
  999. * @param queryKey 键|键数组
  1000. * @param callback 执行的回调函数
  1001. * @param checkExec 判断是否执行回调
  1002. *
  1003. * (默认)如果想要每个菜单是`与`关系,即每个菜单都判断为开启,那么就判断它们的值&就行
  1004. *
  1005. * 如果想要任意菜单存在true再执行,那么判断它们的值|就行
  1006. *
  1007. * + 返回值都为`true`,执行回调,如果回调返回了<style>元素,该元素会在监听到值改变时被移除掉
  1008. * + 返回值有一个为`false`,则不执行回调,且移除之前回调函数返回的<style>元素
  1009. * @param once 是否只执行一次,默认true
  1010. *
  1011. * + true (默认)只执行一次,且会监听键的值改变
  1012. * + false 不会监听键的值改变
  1013. */
  1014. exec(queryKey, callback, checkExec, once = true) {
  1015. const that = this;
  1016. let queryKeyFn;
  1017. if (typeof queryKey === "string" || Array.isArray(queryKey)) {
  1018. queryKeyFn = () => queryKey;
  1019. } else {
  1020. queryKeyFn = queryKey;
  1021. }
  1022. let isArrayKey = false;
  1023. let queryKeyResult = queryKeyFn();
  1024. let keyList = [];
  1025. if (Array.isArray(queryKeyResult)) {
  1026. isArrayKey = true;
  1027. keyList = queryKeyResult;
  1028. } else {
  1029. keyList.push(queryKeyResult);
  1030. }
  1031. let findNotInDataKey = keyList.find(
  1032. (it) => !this.$data.configDefaultValueData.has(it)
  1033. );
  1034. if (findNotInDataKey) {
  1035. log.warn(`${findNotInDataKey} 键不存在`);
  1036. return;
  1037. }
  1038. let storageKey = JSON.stringify(keyList);
  1039. if (once) {
  1040. if (this.$data.onceExecMenuData.has(storageKey)) {
  1041. return;
  1042. }
  1043. this.$data.onceExecMenuData.set(storageKey, 1);
  1044. }
  1045. let storeValueList = [];
  1046. let listenerIdList = [];
  1047. let dynamicAddStyleNodeCallback = (value, $style) => {
  1048. let dynamicResultList = [];
  1049. if (!Array.isArray($style)) {
  1050. $style = [$style];
  1051. }
  1052. $style.forEach(($styleItem) => {
  1053. if ($styleItem == null) {
  1054. return;
  1055. }
  1056. if ($styleItem instanceof HTMLStyleElement) {
  1057. dynamicResultList.push($styleItem);
  1058. return;
  1059. }
  1060. });
  1061. {
  1062. storeValueList = storeValueList.concat(dynamicResultList);
  1063. }
  1064. };
  1065. let getMenuValue = (key) => {
  1066. let value = this.getValue(key);
  1067. return value;
  1068. };
  1069. let clearBeforeStoreValue = () => {
  1070. for (let index = 0; index < storeValueList.length; index++) {
  1071. let $css = storeValueList[index];
  1072. $css.remove();
  1073. storeValueList.splice(index, 1);
  1074. index--;
  1075. }
  1076. };
  1077. let checkMenuExec = () => {
  1078. let flag = false;
  1079. if (typeof checkExec === "function") {
  1080. flag = checkExec(keyList);
  1081. } else {
  1082. flag = keyList.every((key) => getMenuValue(key));
  1083. }
  1084. return flag;
  1085. };
  1086. let valueChangeCallback = (valueOption) => {
  1087. let execFlag = checkMenuExec();
  1088. let resultList = [];
  1089. if (execFlag) {
  1090. let valueList = keyList.map((key) => this.getValue(key));
  1091. let callbackResult = callback({
  1092. value: isArrayKey ? valueList : valueList[0],
  1093. addStyleElement: (...args) => {
  1094. return dynamicAddStyleNodeCallback(true, ...args);
  1095. }
  1096. });
  1097. if (!Array.isArray(callbackResult)) {
  1098. callbackResult = [callbackResult];
  1099. }
  1100. callbackResult.forEach((it) => {
  1101. if (it == null) {
  1102. return;
  1103. }
  1104. if (it instanceof HTMLStyleElement) {
  1105. resultList.push(it);
  1106. return;
  1107. }
  1108. });
  1109. }
  1110. clearBeforeStoreValue();
  1111. storeValueList = [...resultList];
  1112. };
  1113. once && keyList.forEach((key) => {
  1114. let listenerId = this.addValueChangeListener(
  1115. key,
  1116. (key2, newValue, oldValue) => {
  1117. valueChangeCallback();
  1118. }
  1119. );
  1120. listenerIdList.push(listenerId);
  1121. });
  1122. valueChangeCallback();
  1123. let result = {
  1124. /**
  1125. * 清空菜单执行情况
  1126. *
  1127. * + 清空存储的元素列表
  1128. * + 清空值改变的监听器
  1129. * + 清空存储的一次执行的键
  1130. */
  1131. clear() {
  1132. this.clearStoreStyleElements();
  1133. this.removeValueChangeListener();
  1134. once && that.$data.onceExecMenuData.delete(storageKey);
  1135. },
  1136. /**
  1137. * 清空存储的元素列表
  1138. */
  1139. clearStoreStyleElements: () => {
  1140. return clearBeforeStoreValue();
  1141. },
  1142. /**
  1143. * 移除值改变的监听器
  1144. */
  1145. removeValueChangeListener: () => {
  1146. listenerIdList.forEach((listenerId) => {
  1147. this.removeValueChangeListener(listenerId);
  1148. });
  1149. }
  1150. };
  1151. return result;
  1152. },
  1153. /**
  1154. * 自动判断菜单是否启用,然后执行回调
  1155. * @param key
  1156. * @param callback 回调
  1157. * @param [isReverse=false] 逆反判断菜单启用
  1158. */
  1159. execMenu(key, callback, isReverse = false) {
  1160. return this.exec(
  1161. key,
  1162. (option) => {
  1163. return callback(option);
  1164. },
  1165. (keyList) => {
  1166. let execFlag = keyList.every((__key__) => {
  1167. let flag = !!this.getValue(__key__);
  1168. isReverse && (flag = !flag);
  1169. return flag;
  1170. });
  1171. return execFlag;
  1172. },
  1173. false
  1174. );
  1175. },
  1176. /**
  1177. * 自动判断菜单是否启用,然后执行回调,只会执行一次
  1178. *
  1179. * 它会自动监听值改变(设置中的修改),改变后如果未执行,则执行一次
  1180. * @param key
  1181. * @param callback 回调
  1182. * @param getValueFn 自定义处理获取当前值,值true是启用并执行回调,值false是不执行回调
  1183. * @param handleValueChangeFn 自定义处理值改变时的回调,值true是启用并执行回调,值false是不执行回调
  1184. */
  1185. execMenuOnce(key, callback) {
  1186. return this.exec(
  1187. key,
  1188. callback,
  1189. (keyList) => {
  1190. let execFlag = keyList.every((__key__) => {
  1191. let flag = !!this.getValue(__key__);
  1192. return flag;
  1193. });
  1194. return execFlag;
  1195. },
  1196. true
  1197. );
  1198. },
  1199. /**
  1200. * 根据key执行一次
  1201. * @param key 键
  1202. * @param callback 回调
  1203. */
  1204. onceExec(key, callback) {
  1205. if (typeof key !== "string") {
  1206. throw new TypeError("key 必须是字符串");
  1207. }
  1208. if (this.$data.onceExecData.has(key)) {
  1209. return;
  1210. }
  1211. callback();
  1212. this.$data.onceExecData.set(key, 1);
  1213. },
  1214. /**
  1215. * 显示设置面板
  1216. * @param content 显示的内容配置
  1217. * @param [title] 标题
  1218. * @param [preventDefaultContentConfig=false] 是否阻止默认添加内容配置(版本号)
  1219. */
  1220. showPanel(content, title = `${SCRIPT_NAME}-设置`, preventDefaultContentConfig = false) {
  1221. let notHasBottomVersionContentConfig = content.some((it) => {
  1222. let isBottom = typeof it.isBottom === "function" ? it.isBottom() : Boolean(it.isBottom);
  1223. return !isBottom && it.id !== "script-version";
  1224. });
  1225. if (!preventDefaultContentConfig && notHasBottomVersionContentConfig) {
  1226. content.push(...PanelContent.getDefaultBottomContentConfig());
  1227. }
  1228. let $panel = __pops.panel({
  1229. ...{
  1230. title: {
  1231. text: title,
  1232. position: "center",
  1233. html: false,
  1234. style: ""
  1235. },
  1236. content,
  1237. btn: {
  1238. close: {
  1239. enable: true,
  1240. callback: (details, event) => {
  1241. details.close();
  1242. this.$data.$panel = null;
  1243. }
  1244. }
  1245. },
  1246. mask: {
  1247. enable: true,
  1248. clickEvent: {
  1249. toClose: true,
  1250. toHide: false
  1251. },
  1252. clickCallBack: (originalRun, config) => {
  1253. originalRun();
  1254. this.$data.$panel = null;
  1255. }
  1256. },
  1257. width: PanelUISize.setting.width,
  1258. height: PanelUISize.setting.height,
  1259. drag: true,
  1260. only: true
  1261. },
  1262. ...this.$data.panelConfig
  1263. });
  1264. this.$data.$panel = $panel;
  1265. }
  1266. };
  1267. const CSDNRouter = {
  1268. /**
  1269. * 判断是否是华为云联盟
  1270. * + huaweicloud.csdn.net
  1271. */
  1272. isHuaWeiCloudBlog() {
  1273. return Boolean(/huaweicloud.csdn.net/i.test(window.location.origin));
  1274. },
  1275. /**
  1276. * 判断是否是博客
  1277. * + blog.csdn.net
  1278. */
  1279. isBlog() {
  1280. return Boolean(/blog.csdn.net/i.test(window.location.origin));
  1281. },
  1282. /**
  1283. * 博客帖子
  1284. */
  1285. isBlogArticle() {
  1286. return this.isBlog() && window.location.pathname.includes("/article/details/");
  1287. },
  1288. /**
  1289. * 判断是否是文库
  1290. * + wenku.csdn.net
  1291. */
  1292. isWenKu() {
  1293. return Boolean(/wenku.csdn.net/i.test(window.location.origin));
  1294. },
  1295. /**
  1296. * 判断是否是链接
  1297. * + link.csdn.net
  1298. */
  1299. isLink() {
  1300. return window.location.hostname === "link.csdn.net";
  1301. },
  1302. /**
  1303. * 判断是否是搜索
  1304. * + so.csdn.net
  1305. */
  1306. isSo() {
  1307. return window.location.hostname === "so.csdn.net";
  1308. },
  1309. /**
  1310. * 判断是否是C知道
  1311. * + so.csdn.net/know
  1312. * + /chat
  1313. * + /so/ai
  1314. */
  1315. isSoCKnow() {
  1316. return this.isSo() && (window.location.pathname.startsWith("/chat") || window.location.pathname.startsWith("/so/ai"));
  1317. },
  1318. /**
  1319. * 判断是否是资源页面
  1320. * + download.csdn.net
  1321. */
  1322. isDownload() {
  1323. return window.location.hostname === "download.csdn.net";
  1324. }
  1325. };
  1326. const ShieldCSS$4 = "/* 底部免费抽xxx奖品广告 */\r\ndiv.siderbar-box,\r\n/* 华为开发者联盟加入社区 */\r\ndiv.user-desc.user-desc-fix {\r\n display: none !important;\r\n}\r\n";
  1327. const CSDNHuaWeiCloud = {
  1328. init() {
  1329. addStyle(ShieldCSS$4);
  1330. Panel.execMenuOnce(
  1331. "csdn-hua-wei-cloud-shieldCloudDeveloperTaskChallengeEvent",
  1332. () => {
  1333. return this.shieldCloudDeveloperTaskChallengeEvent();
  1334. }
  1335. );
  1336. Panel.execMenuOnce("csdn-hua-wei-cloud-autoExpandContent", () => {
  1337. return this.autoExpandContent();
  1338. });
  1339. Panel.execMenuOnce(
  1340. "csdn-hua-wei-cloud-shieldLeftFloatingButton",
  1341. () => {
  1342. return this.shieldLeftFloatingButton();
  1343. }
  1344. );
  1345. Panel.execMenuOnce("csdn-hua-wei-cloud-blockRightColumn", () => {
  1346. return this.blockRightColumn();
  1347. });
  1348. Panel.execMenuOnce(
  1349. "csdn-hua-wei-cloud-blockRecommendedContentAtTheBottom",
  1350. () => {
  1351. return this.blockRecommendedContentAtTheBottom();
  1352. }
  1353. );
  1354. Panel.execMenuOnce(
  1355. "csdn-hua-wei-cloud-shieldTheBottomForMoreRecommendations",
  1356. () => {
  1357. return this.shieldTheBottomForMoreRecommendations();
  1358. }
  1359. );
  1360. },
  1361. /**
  1362. * 自动展开内容
  1363. */
  1364. autoExpandContent() {
  1365. log.info("自动展开全文");
  1366. return [
  1367. CommonUtil.addBlockCSS("div.article-show-more"),
  1368. addStyle(`
  1369. /* 自动展开全文 */
  1370. .main-content .user-article{
  1371. height: auto !important;
  1372. overflow: auto !important;
  1373. }
  1374. `)
  1375. ];
  1376. },
  1377. /**
  1378. * 屏蔽云开发者任务挑战活动
  1379. */
  1380. shieldCloudDeveloperTaskChallengeEvent() {
  1381. log.info("屏蔽云开发者任务挑战活动");
  1382. return CommonUtil.addBlockCSS(".luck-draw-modal-warp");
  1383. },
  1384. /**
  1385. * 屏蔽左侧悬浮按钮
  1386. */
  1387. shieldLeftFloatingButton() {
  1388. log.info("屏蔽左侧悬浮按钮,包括当前阅读量、点赞按钮、评论按钮、分享按钮");
  1389. return CommonUtil.addBlockCSS("div.toolbar-wrapper.article-interact-bar");
  1390. },
  1391. /**
  1392. * 屏蔽右侧栏
  1393. */
  1394. blockRightColumn() {
  1395. log.info("屏蔽右侧栏,包括相关产品-活动日历-运营活动-热门标签");
  1396. return CommonUtil.addBlockCSS("div.page-home-right.dp-aside-right");
  1397. },
  1398. /**
  1399. * 屏蔽底部推荐内容
  1400. */
  1401. blockRecommendedContentAtTheBottom() {
  1402. log.info("屏蔽底部推荐内容");
  1403. return CommonUtil.addBlockCSS("div.recommend-card-box");
  1404. },
  1405. /**
  1406. * 屏蔽底部更多推荐
  1407. */
  1408. shieldTheBottomForMoreRecommendations() {
  1409. log.info("屏蔽底部更多推荐");
  1410. return CommonUtil.addBlockCSS("div.more-article");
  1411. }
  1412. };
  1413. const BlogArticleCenterCSS = '#mainBox main {\r\n width: inherit !important;\r\n}\r\n/* 当文章向下滚动时,触发左侧信息悬浮 */\r\naside.blog_container_aside[style*="position: fixed;"] {\r\n display: none !important;\r\n}\r\n\r\n@media (min-width: 1320px) and (max-width: 1380px) {\r\n .nodata .container {\r\n width: 900px !important;\r\n }\r\n\r\n .nodata .container main {\r\n width: 900px;\r\n }\r\n\r\n .nodata .container main #pcCommentBox pre > ol.hljs-ln {\r\n width: 490px !important;\r\n }\r\n\r\n .nodata .container main .articleConDownSource {\r\n width: 500px;\r\n }\r\n}\r\n\r\n@media screen and (max-width: 1320px) {\r\n .nodata .container {\r\n width: 760px !important;\r\n }\r\n\r\n .nodata .container main {\r\n width: 760px;\r\n }\r\n\r\n .nodata .container main #pcCommentBox pre > ol.hljs-ln {\r\n width: 490px !important;\r\n }\r\n\r\n .nodata .container main .toolbox-list .tool-reward {\r\n display: none;\r\n }\r\n\r\n .nodata\r\n .container\r\n main\r\n .more-toolbox-new\r\n .toolbox-left\r\n .profile-box\r\n .profile-name {\r\n max-width: 128px;\r\n }\r\n\r\n .nodata .container main .articleConDownSource {\r\n width: 420px;\r\n }\r\n}\r\n\r\n@media screen and (min-width: 1380px) {\r\n .nodata .container {\r\n width: 1010px !important;\r\n }\r\n\r\n .nodata .container main {\r\n width: 1010px;\r\n }\r\n\r\n .nodata .container main #pcCommentBox pre > ol.hljs-ln {\r\n width: 490px !important;\r\n }\r\n\r\n .nodata .container main .articleConDownSource {\r\n width: 560px;\r\n }\r\n}\r\n\r\n@media (min-width: 1550px) and (max-width: 1700px) {\r\n .nodata .container {\r\n width: 820px !important;\r\n }\r\n\r\n .nodata .container main {\r\n width: 820px;\r\n }\r\n\r\n .nodata .container main #pcCommentBox pre > ol.hljs-ln {\r\n width: 690px !important;\r\n }\r\n\r\n .nodata .container main .articleConDownSource {\r\n width: 500px;\r\n }\r\n}\r\n\r\n@media screen and (min-width: 1700px) {\r\n .nodata .container {\r\n width: 1010px !important;\r\n }\r\n\r\n .nodata .container main {\r\n width: 1010px;\r\n }\r\n\r\n .nodata .container main #pcCommentBox pre > ol.hljs-ln {\r\n width: 690px !important;\r\n }\r\n\r\n .nodata .container main .articleConDownSource {\r\n width: 560px;\r\n }\r\n}\r\n';
  1414. const CSDNBlogArticleRightToolBar = {
  1415. init() {
  1416. Panel.exec(
  1417. "csdn-blog-rightToolbarEnable",
  1418. () => {
  1419. return this.shieldRightToolbar();
  1420. },
  1421. (keyList) => !Panel.getValue(keyList[0]),
  1422. true
  1423. );
  1424. Panel.execMenuOnce("csdn-blog-rightToolbarCreativeCenter", () => {
  1425. return this.shieldCreativeCenter();
  1426. });
  1427. Panel.execMenuOnce("csdn-blog-rightToolbarShowOrSidebar", () => {
  1428. return this.shieldShowOrSidebar();
  1429. });
  1430. Panel.execMenuOnce("csdn-blog-rightToolbarBeginnerGuidance", () => {
  1431. return this.shieldBeginnerGuidance();
  1432. });
  1433. Panel.execMenuOnce("csdn-blog-rightToolbarCustomerService", () => {
  1434. return this.shieldCustomerService();
  1435. });
  1436. Panel.execMenuOnce("csdn-blog-rightToolbarReport", () => {
  1437. return this.shieldReport();
  1438. });
  1439. Panel.execMenuOnce("csdn-blog-rightToolbarBackToTop", () => {
  1440. return this.shieldBackToTop();
  1441. });
  1442. this.initRightToolbarOffset();
  1443. domUtils.ready(() => {
  1444. Panel.execMenuOnce("csdn-blog-addGotoRecommandButton", () => {
  1445. this.addGotoRecommandButton();
  1446. });
  1447. });
  1448. },
  1449. /**
  1450. * 【添加】前往评论按钮,在返回顶部的下面
  1451. */
  1452. addGotoRecommandButton() {
  1453. log.info("【添加】前往评论按钮,在返回顶部的上面");
  1454. let gotoRecommandNode = document.createElement("a");
  1455. gotoRecommandNode.className = "option-box";
  1456. gotoRecommandNode.setAttribute("data-type", "gorecommand");
  1457. gotoRecommandNode.innerHTML = /*html*/
  1458. `
  1459. <img src="https://g.csdnimg.cn/side-toolbar/3.6/images/customer.png" alt="" srcset="">
  1460. <span class="show-txt" style="opacity:100;">前往<br>评论</span>
  1461. `;
  1462. gotoRecommandNode.addEventListener("click", function() {
  1463. let toolbarBoxElement = document.querySelector("#toolBarBox");
  1464. if (!toolbarBoxElement || !toolbarBoxElement.getClientRects().length) {
  1465. let $pcCommentBox = $("#pcCommentBox");
  1466. if ($pcCommentBox && $pcCommentBox.getClientRects().length) {
  1467. toolbarBoxElement = $pcCommentBox;
  1468. } else {
  1469. log.error("评论区处于隐藏状态");
  1470. return;
  1471. }
  1472. }
  1473. log.info("滚动到评论");
  1474. let toolbarBoxOffsetTop = toolbarBoxElement.getBoundingClientRect().top + window.scrollY;
  1475. let csdnToolBarElement = document.querySelector(
  1476. "#csdn-toolbar"
  1477. );
  1478. let csdnToolBarStyles = window.getComputedStyle(csdnToolBarElement);
  1479. let csdnToolBarHeight = csdnToolBarElement.clientHeight - parseFloat(csdnToolBarStyles.paddingTop) - parseFloat(csdnToolBarStyles.paddingBottom);
  1480. window.scrollTo({
  1481. top: toolbarBoxOffsetTop - csdnToolBarHeight - 8,
  1482. left: 0,
  1483. behavior: "smooth"
  1484. });
  1485. });
  1486. utils.waitNode(".csdn-side-toolbar").then(() => {
  1487. let targetElement = document.querySelector(
  1488. ".csdn-side-toolbar a:nth-last-child(2)"
  1489. );
  1490. targetElement.parentElement.insertBefore(
  1491. gotoRecommandNode,
  1492. targetElement.nextSibling
  1493. );
  1494. });
  1495. },
  1496. /**
  1497. * 初始化右侧工具栏的偏移(top、right)
  1498. */
  1499. initRightToolbarOffset() {
  1500. log.info("初始化右侧工具栏的偏移(top、right)");
  1501. addStyle(
  1502. /*css*/
  1503. `
  1504. .csdn-side-toolbar{
  1505. left: unset !important;
  1506. }
  1507. `
  1508. );
  1509. utils.waitNode(".csdn-side-toolbar").then(($sideToolbar) => {
  1510. domUtils.css($sideToolbar, {
  1511. top: parseInt(Panel.getValue("csdn-blog-rightToolbarTopOffset")) + "px",
  1512. right: parseInt(Panel.getValue("csdn-blog-rightToolbarRightOffset")) + "px"
  1513. });
  1514. });
  1515. },
  1516. /**
  1517. * 屏蔽右侧工具栏
  1518. */
  1519. shieldRightToolbar() {
  1520. log.info("屏蔽右侧工具栏");
  1521. return CommonUtil.addBlockCSS(`div.csdn-side-toolbar`);
  1522. },
  1523. /**
  1524. * 【屏蔽】创作中心
  1525. */
  1526. shieldCreativeCenter() {
  1527. log.info("【屏蔽】创作中心");
  1528. return CommonUtil.addBlockCSS(
  1529. ".csdn-side-toolbar .sidetool-writeguide-box"
  1530. );
  1531. },
  1532. /**
  1533. * 【屏蔽】显示/隐藏侧栏
  1534. */
  1535. shieldShowOrSidebar() {
  1536. log.info("【屏蔽】显示/隐藏侧栏");
  1537. return CommonUtil.addBlockCSS(".csdn-side-toolbar a.sidecolumn");
  1538. },
  1539. /**
  1540. * 【屏蔽】新手引导
  1541. */
  1542. shieldBeginnerGuidance() {
  1543. log.info("【屏蔽】新手引导");
  1544. return CommonUtil.addBlockCSS(
  1545. '.csdn-side-toolbar a.option-box[data-type="guide"]'
  1546. );
  1547. },
  1548. /**
  1549. * 【屏蔽】客服
  1550. */
  1551. shieldCustomerService() {
  1552. log.info("【屏蔽】客服");
  1553. return CommonUtil.addBlockCSS(
  1554. '.csdn-side-toolbar a.option-box[data-type="cs"]'
  1555. );
  1556. },
  1557. /**
  1558. * 【屏蔽】举报
  1559. */
  1560. shieldReport() {
  1561. log.info("【屏蔽】举报");
  1562. return CommonUtil.addBlockCSS(
  1563. '.csdn-side-toolbar a.option-box[data-type="report"]'
  1564. );
  1565. },
  1566. /**
  1567. * 【屏蔽】返回顶部
  1568. */
  1569. shieldBackToTop() {
  1570. log.info("【屏蔽】返回顶部");
  1571. return CommonUtil.addBlockCSS(
  1572. '.csdn-side-toolbar a.option-box[data-type="gotop"]'
  1573. );
  1574. }
  1575. };
  1576. const CSDNBlogArticle = {
  1577. init() {
  1578. CSDNBlogArticleRightToolBar.init();
  1579. Panel.execMenuOnce("csdn-blog-articleCenter", () => {
  1580. return this.articleCenter();
  1581. });
  1582. Panel.execMenuOnce("csdn-blog-shieldLoginDialog", () => {
  1583. return this.shieldLoginDialog();
  1584. });
  1585. Panel.execMenuOnce("csdn-blog-autoExpandContent", () => {
  1586. return this.autoExpandContent();
  1587. });
  1588. Panel.execMenuOnce("csdn-blog-autoExpandCodeContent", () => {
  1589. return this.autoExpandCodeContent();
  1590. });
  1591. Panel.exec(
  1592. "csdn-blog-blockComment",
  1593. () => {
  1594. return this.blockComment();
  1595. },
  1596. (keyList) => !Panel.getValue(keyList[0]),
  1597. true
  1598. );
  1599. Panel.exec(
  1600. "csdn-blog-bottomRecommendArticleEnable",
  1601. () => {
  1602. return this.shieldBottomRecommendArticle();
  1603. },
  1604. (keyList) => !Panel.getValue(keyList[0]),
  1605. true
  1606. );
  1607. Panel.execMenuOnce("csdn-blog-shieldBottomSkillTree", () => {
  1608. return this.shieldBottomSkillTree();
  1609. });
  1610. Panel.execMenuOnce("csdn-blog-shieldBottomFloatingToolbar", () => {
  1611. return this.shieldBottomFloatingToolbar();
  1612. });
  1613. Panel.execMenuOnce("csdn-blog-shieldLeftBlogContainerAside", () => {
  1614. return this.shieldLeftBlogContainerAside();
  1615. });
  1616. Panel.execMenuOnce("csdn-blog-shieldRightDirectoryInformation", () => {
  1617. return this.shieldRightDirectoryInformation();
  1618. });
  1619. Panel.execMenuOnce("csdn-blog-shieldArticleSearchTip", () => {
  1620. return this.shieldArticleSearchTip();
  1621. });
  1622. Panel.execMenuOnce("csdn-blog-allowSelectContent", () => {
  1623. return this.allowSelectContent();
  1624. });
  1625. domUtils.ready(() => {
  1626. Panel.execMenuOnce("csdn-blog-identityCSDNDownload", () => {
  1627. this.identityCSDNDownload();
  1628. });
  1629. Panel.execMenuOnce("csdn-blog-clickPreCodeAutomatically", () => {
  1630. this.clickPreCodeAutomatically();
  1631. });
  1632. Panel.execMenuOnce("csdn-blog-restoreComments", () => {
  1633. this.restoreComments();
  1634. });
  1635. });
  1636. },
  1637. /**
  1638. * 点击代码块自动展开
  1639. */
  1640. clickPreCodeAutomatically() {
  1641. log.info("点击代码块自动展开");
  1642. document.addEventListener("click", function(event) {
  1643. let $click = event.target;
  1644. if ($click.localName !== "pre") {
  1645. return;
  1646. }
  1647. $click.style.setProperty("height", "auto");
  1648. $click.querySelector(".hide-preCode-box")?.remove();
  1649. });
  1650. },
  1651. /**
  1652. * 恢复评论到正确位置
  1653. */
  1654. restoreComments() {
  1655. log.info("恢复评论到正确位置-第一条评论");
  1656. utils.waitNode(".first-recommend-box").then(($firstRecommendBox) => {
  1657. let recommendBoxElement = document.querySelector(
  1658. ".recommend-box.insert-baidu-box.recommend-box-style"
  1659. );
  1660. recommendBoxElement.insertBefore(
  1661. $firstRecommendBox,
  1662. recommendBoxElement.firstChild
  1663. );
  1664. });
  1665. log.info("恢复评论到正确位置-第二条评论");
  1666. utils.waitNode(".second-recommend-box").then(($secondRecommendBox) => {
  1667. let recommendBoxElement = document.querySelector(
  1668. ".recommend-box.insert-baidu-box.recommend-box-style"
  1669. );
  1670. recommendBoxElement.insertBefore(
  1671. $secondRecommendBox,
  1672. recommendBoxElement.firstChild
  1673. );
  1674. });
  1675. },
  1676. /**
  1677. * 标识CSDN下载的链接
  1678. */
  1679. identityCSDNDownload() {
  1680. log.info("标识CSDN下载的链接");
  1681. document.querySelectorAll(
  1682. ".recommend-item-box[data-url*='https://download.csdn.net/']"
  1683. ).forEach((item) => {
  1684. if (Panel.getValue("csdn-blog-removeResourceDownloadArticle")) {
  1685. item.remove();
  1686. } else {
  1687. item.querySelector(".content-box").style.setProperty("border", "2px solid red");
  1688. }
  1689. });
  1690. },
  1691. /**
  1692. * 全文居中
  1693. */
  1694. articleCenter() {
  1695. log.info("全文居中");
  1696. return addStyle(BlogArticleCenterCSS);
  1697. },
  1698. /**
  1699. * 屏蔽登录(不可用)弹窗
  1700. */
  1701. shieldLoginDialog() {
  1702. log.info("屏蔽登录(不可用)弹窗");
  1703. return CommonUtil.addBlockCSS(`.passport-login-container`);
  1704. },
  1705. /**
  1706. * 自动展开代码块
  1707. */
  1708. autoExpandCodeContent() {
  1709. log.info("自动展开代码块");
  1710. return [
  1711. CommonUtil.addBlockCSS("pre.set-code-hide .hide-preCode-box"),
  1712. addStyle(
  1713. /*css*/
  1714. `
  1715. pre.set-code-hide{
  1716. height: auto !important;
  1717. }
  1718. /* 自动展开代码块 */
  1719. .comment-list-box,
  1720. main div.blog-content-box pre {
  1721. max-height: none !important;
  1722. }
  1723. `
  1724. )
  1725. ];
  1726. },
  1727. /**
  1728. * 自动展开全文
  1729. */
  1730. autoExpandContent() {
  1731. log.info("自动展开全文");
  1732. return addStyle(
  1733. /*css*/
  1734. `
  1735. /* 自动展开全文 */
  1736. #article_content,
  1737. .user-article.user-article-hide {
  1738. height: auto !important;
  1739. overflow: auto !important;
  1740. }
  1741. `
  1742. );
  1743. },
  1744. /**
  1745. * 屏蔽评论区
  1746. */
  1747. blockComment() {
  1748. log.info("屏蔽评论区");
  1749. return CommonUtil.addBlockCSS(`#pcCommentBox`);
  1750. },
  1751. /**
  1752. * 屏蔽底部推荐文章
  1753. */
  1754. shieldBottomRecommendArticle() {
  1755. log.info("屏蔽底部推荐文章");
  1756. return CommonUtil.addBlockCSS(`main > div.recommend-box`);
  1757. },
  1758. /**
  1759. * 屏蔽底部xx技能树
  1760. */
  1761. shieldBottomSkillTree() {
  1762. log.info("屏蔽底部xx技能树");
  1763. return CommonUtil.addBlockCSS(`#treeSkill`);
  1764. },
  1765. /**
  1766. * 屏蔽底部悬浮工具栏
  1767. */
  1768. shieldBottomFloatingToolbar() {
  1769. log.info("屏蔽底部悬浮工具栏");
  1770. return CommonUtil.addBlockCSS(`#toolBarBox`);
  1771. },
  1772. /**
  1773. * 屏蔽左侧博客信息
  1774. */
  1775. shieldLeftBlogContainerAside() {
  1776. log.info("【屏蔽】左侧博客信息");
  1777. return CommonUtil.addBlockCSS(`aside.blog_container_aside`);
  1778. },
  1779. /**
  1780. * 【屏蔽】右侧目录信息
  1781. */
  1782. shieldRightDirectoryInformation() {
  1783. log.info("【屏蔽】右侧目录信息");
  1784. return CommonUtil.addBlockCSS("#rightAsideConcision", "#rightAside");
  1785. },
  1786. /**
  1787. * 屏蔽文章内的选中搜索悬浮提示
  1788. */
  1789. shieldArticleSearchTip() {
  1790. log.info("屏蔽文章内的选中搜索悬浮提示");
  1791. return CommonUtil.addBlockCSS(`#articleSearchTip`);
  1792. },
  1793. /**
  1794. * 允许选择内容
  1795. */
  1796. allowSelectContent() {
  1797. log.info("允许选择内容");
  1798. return addStyle(
  1799. /*css*/
  1800. `
  1801. #content_views,
  1802. #content_views pre,
  1803. #content_views pre code {
  1804. user-select: text !important;
  1805. }
  1806. `
  1807. );
  1808. }
  1809. };
  1810. const WenkuCSS = "#chatgpt-article-detail\r\n > div.layout-center\r\n > div.main\r\n > div.article-box\r\n > div.cont.first-show.forbid {\r\n max-height: unset !important;\r\n height: auto !important;\r\n overflow: auto !important;\r\n}\r\n\r\n.forbid {\r\n user-select: text !important;\r\n}\r\n";
  1811. const ShieldCSS$3 = "/* wenku顶部横幅 */\r\n#app > div > div.main.pb-32 > div > div.top-bar,\r\n/* 底部展开全文 */\r\n#chatgpt-article-detail > div.layout-center > div.main > div.article-box > div.cont.first-show.forbid > div.open {\r\n display: none !important;\r\n}";
  1812. const CSDNWenKu = {
  1813. init() {
  1814. addStyle(WenkuCSS);
  1815. addStyle(ShieldCSS$3);
  1816. Panel.execMenuOnce("csdn-wenku-shieldResourceRecommend", () => {
  1817. return this.shieldResourceRecommend();
  1818. });
  1819. Panel.execMenuOnce("csdn-wenku-shieldRightUserInfo", () => {
  1820. return this.shieldRightUserInfo();
  1821. });
  1822. Panel.execMenuOnce("csdn-wenku-shieldRightToolBar", () => {
  1823. return this.shieldRightToolBar();
  1824. });
  1825. },
  1826. /**
  1827. * 【屏蔽】资源推荐
  1828. */
  1829. shieldResourceRecommend() {
  1830. log.info("【屏蔽】资源推荐");
  1831. return CommonUtil.addBlockCSS("#recommend");
  1832. },
  1833. /**
  1834. * 【屏蔽】右侧用户信息
  1835. */
  1836. shieldRightUserInfo() {
  1837. log.info("【屏蔽】右侧用户信息");
  1838. return CommonUtil.addBlockCSS(".layout-right");
  1839. },
  1840. /**
  1841. * 【屏蔽】右侧悬浮工具栏
  1842. */
  1843. shieldRightToolBar() {
  1844. log.info("【屏蔽】右侧悬浮工具栏");
  1845. return CommonUtil.addBlockCSS(".csdn-side-toolbar");
  1846. }
  1847. };
  1848. const CSDNLink = {
  1849. init() {
  1850. Panel.execMenuOnce("csdn-link-jumpRedirect", () => {
  1851. this.jumpRedirect();
  1852. });
  1853. },
  1854. /**
  1855. * 去除CSDN拦截其它网址的url并自动跳转
  1856. * @example
  1857. * https://link.csdn.net/?target=https%3A%2F%2Fjaist.dl.sourceforge.net%2Fproject%2Fportecle%2Fv1.11%2Fportecle-1.11.zip
  1858. */
  1859. jumpRedirect() {
  1860. try {
  1861. let urlSearchParams = new URLSearchParams(window.location.search);
  1862. const URL_KEY = "target";
  1863. if (urlSearchParams.has(URL_KEY)) {
  1864. let target = urlSearchParams.get(URL_KEY);
  1865. let jumpUrl = decodeURIComponent(target);
  1866. log.success(`跳转链接:${jumpUrl}`);
  1867. window.location.href = jumpUrl;
  1868. } else {
  1869. log.error("解析跳转的链接失败,原因:搜索参数中没有target参数");
  1870. }
  1871. } catch (error) {
  1872. Qmsg.error("跳转链接失败:" + error.message);
  1873. }
  1874. }
  1875. };
  1876. const BlogShieldCSS = ".ecommend-item-box.recommend-recommend-box,\r\n.login-mark,\r\n.opt-box.text-center,\r\n.leftPop,\r\n#csdn-shop-window,\r\n.toolbar-advert,\r\n.hide-article-box,\r\n.user-desc.user-desc-fix,\r\n.recommend-card-box,\r\n.more-article,\r\n.article-show-more,\r\n#csdn-toolbar-profile-nologin,\r\n.guide-rr-first,\r\n#recommend-item-box-tow,\r\n/* 发文章得原力分图片提示 */\r\ndiv.csdn-toolbar-creative-mp,\r\n/* 阅读终点,创作起航,您可以撰写心得或摘录文章要点写篇博文。 */\r\n#toolBarBox div.write-guide-buttom-box,\r\n/* 觉得还不错? 一键收藏 */\r\nul.toolbox-list div.tool-active-list,\r\n/* 右边按钮组的最上面的创作话题 */\r\ndiv.csdn-side-toolbar .activity-swiper-box,\r\n.sidetool-writeguide-box .tip-box,\r\n/* 右下角的登录(不可用)提示 */\r\n.passport-login-tip-container,\r\n/* 全屏双十一红包 */\r\n.csdn-reapck-select,\r\n/* 侧栏的618会员开通 */\r\n.csdn-side-toolbar .sidecolumn-vip {\r\n display: none !important;\r\n}\r\n";
  1877. const BlogCSS = "/*.blog_container_aside,\r\n#nav {\r\n margin-left: -45px;\r\n}\r\n.recommend-right.align-items-stretch.clearfix,\r\n.dl_right_fixed {\r\n margin-left: 45px;\r\n}*/\r\n";
  1878. const CSDNBlog = {
  1879. init() {
  1880. this.addCSS();
  1881. Panel.execMenuOnce("csdn-blog-shieldTopToolbar", () => {
  1882. return this.shieldTopToolbar();
  1883. });
  1884. domUtils.ready(() => {
  1885. Panel.execMenuOnce("csdn-blog-removeClipboardHijacking", () => {
  1886. this.removeClipboardHijacking();
  1887. });
  1888. Panel.execMenuOnce("csdn-blog-unBlockCopy", () => {
  1889. this.unBlockCopy();
  1890. });
  1891. });
  1892. },
  1893. /**
  1894. * 添加屏蔽CSS和功能CSS
  1895. */
  1896. addCSS() {
  1897. log.info("添加屏蔽CSS和功能CSS");
  1898. return [addStyle(BlogShieldCSS), addStyle(BlogCSS)];
  1899. },
  1900. /**
  1901. * 去除剪贴板劫持
  1902. */
  1903. removeClipboardHijacking() {
  1904. log.info("去除剪贴板劫持");
  1905. let $article_copyright = document.querySelector(".article-copyright");
  1906. if ($article_copyright) {
  1907. $article_copyright.remove();
  1908. }
  1909. if (_unsafeWindow.articleType) {
  1910. _unsafeWindow.articleType = 0;
  1911. }
  1912. if (_unsafeWindow.csdn && _unsafeWindow.csdn.copyright && _unsafeWindow.csdn.copyright.textData) {
  1913. _unsafeWindow.csdn.copyright.textData = "";
  1914. }
  1915. if (_unsafeWindow.csdn && _unsafeWindow.csdn.copyright && _unsafeWindow.csdn.copyright.htmlData) {
  1916. _unsafeWindow.csdn.copyright.htmlData = "";
  1917. }
  1918. },
  1919. /**
  1920. * 取消禁止复制
  1921. */
  1922. unBlockCopy() {
  1923. log.info("取消禁止复制");
  1924. domUtils.on(
  1925. document,
  1926. "click",
  1927. function(event) {
  1928. let $click = event.target;
  1929. let $parent = $click.parentElement;
  1930. if (!$click.classList.contains("hljs-button")) {
  1931. return;
  1932. }
  1933. let $code = $parent.querySelector("code");
  1934. $code = $code || $parent;
  1935. utils.preventEvent(event);
  1936. let copyText = $code.innerText;
  1937. log.info(
  1938. "点击复制按钮复制内容:" + (copyText.length > 8 ? copyText.substring(0, 8) + "..." : copyText)
  1939. );
  1940. utils.setClip(copyText);
  1941. $click.setAttribute("data-title", "复制成功");
  1942. },
  1943. {
  1944. capture: true
  1945. }
  1946. );
  1947. let changeDataTitle = new utils.LockFunction(function(event) {
  1948. let $mouse = event.target;
  1949. if ($mouse.localName !== "pre") {
  1950. return;
  1951. }
  1952. let $hljsBtn = $mouse.querySelector(".hljs-button");
  1953. if ($hljsBtn) {
  1954. $hljsBtn.setAttribute("data-title", "复制");
  1955. }
  1956. });
  1957. domUtils.on(
  1958. document,
  1959. ["mouseenter", "mouseleave"],
  1960. function(event) {
  1961. changeDataTitle.run(event);
  1962. },
  1963. {
  1964. capture: true
  1965. }
  1966. );
  1967. utils.waitNode("#content_views").then(($content_views) => {
  1968. if (_unsafeWindow.$) {
  1969. _unsafeWindow.$("#content_views")?.unbind("copy");
  1970. }
  1971. domUtils.on(
  1972. $content_views,
  1973. "copy",
  1974. function(event) {
  1975. utils.preventEvent(event);
  1976. let selectText = _unsafeWindow.getSelection();
  1977. let copyText = selectText?.toString();
  1978. log.info(
  1979. "Ctrl+C复制内容:" + (copyText.length > 8 ? copyText.substring(0, 8) + "..." : copyText)
  1980. );
  1981. utils.setClip(copyText);
  1982. return false;
  1983. },
  1984. {
  1985. capture: true
  1986. }
  1987. );
  1988. });
  1989. utils.waitNode(".hljs-button").then(() => {
  1990. setTimeout(() => {
  1991. $$(".hljs-button").forEach(($el) => {
  1992. $el.removeAttribute("onclick");
  1993. $el.removeAttribute("data-report-click");
  1994. $el.setAttribute("data-title", "复制");
  1995. });
  1996. }, 250);
  1997. });
  1998. },
  1999. /**
  2000. * 屏蔽顶部Toolbar
  2001. */
  2002. shieldTopToolbar() {
  2003. log.info("屏蔽顶部Toolbar");
  2004. return CommonUtil.addBlockCSS("#toolbarBox", "#csdn-toolbar");
  2005. }
  2006. };
  2007. const CSDN = {
  2008. init() {
  2009. if (CSDNRouter.isLink()) {
  2010. log.info("Router: 中转链接");
  2011. CSDNLink.init();
  2012. } else if (CSDNRouter.isHuaWeiCloudBlog()) {
  2013. log.info("Router: 华为云联盟");
  2014. CSDNHuaWeiCloud.init();
  2015. } else if (CSDNRouter.isBlog()) {
  2016. log.info("Router: 博客");
  2017. CSDNBlog.init();
  2018. if (CSDNRouter.isBlogArticle()) {
  2019. log.info("Router: 帖子");
  2020. CSDNBlogArticle.init();
  2021. }
  2022. } else if (CSDNRouter.isWenKu()) {
  2023. log.info("Router: 文库");
  2024. CSDNWenKu.init();
  2025. } else {
  2026. log.error("暂未适配,请反馈开发者:" + globalThis.location.href);
  2027. }
  2028. }
  2029. };
  2030. const M_CSDNLink = {
  2031. init() {
  2032. Panel.execMenuOnce("m-csdn-link-jumpRedirect", () => {
  2033. CSDNLink.jumpRedirect();
  2034. });
  2035. }
  2036. };
  2037. const ShieldCSS$2 = "/* 右下角的 免费赢华为平板xxxx */\r\n.org-main-content .siderbar-box {\r\n display: none !important;\r\n}\r\n";
  2038. const M_CSDNHuaWeiCloud = {
  2039. init() {
  2040. addStyle(ShieldCSS$2);
  2041. Panel.execMenuOnce("m-csdn-hua-wei-cloud-autoExpandContent", () => {
  2042. return CSDNHuaWeiCloud.autoExpandContent();
  2043. });
  2044. Panel.execMenuOnce(
  2045. "m-csdn-hua-wei-cloud-blockBottomJoinTheCommunity",
  2046. () => {
  2047. return this.blockBottomJoinTheCommunity();
  2048. }
  2049. );
  2050. },
  2051. /**
  2052. * 【屏蔽】底部加入社区
  2053. */
  2054. blockBottomJoinTheCommunity() {
  2055. log.info("【屏蔽】底部加入社区");
  2056. return CommonUtil.addBlockCSS(".user-desc");
  2057. }
  2058. };
  2059. const ApiResponseCheck = {
  2060. isSuccessResponse(data) {
  2061. if (data == null) {
  2062. return false;
  2063. }
  2064. if (typeof data === "string") {
  2065. data = utils.toJSON(data);
  2066. }
  2067. return data?.code === 200;
  2068. }
  2069. };
  2070. const CSDNFavoriteApi = {
  2071. /**
  2072. * 获取收藏夹信息
  2073. * @param url 当前url
  2074. */
  2075. async folderListWithCheck(url) {
  2076. let response = await httpx.get(
  2077. `https://mp-action.csdn.net/interact/wrapper/pc/favorite/v1/api/folderListWithCheck`,
  2078. {
  2079. data: {
  2080. url
  2081. },
  2082. fetch: true,
  2083. allowInterceptConfig: false,
  2084. headers: {
  2085. "User-Agent": utils.getRandomPCUA()
  2086. }
  2087. }
  2088. );
  2089. log.info(response);
  2090. let data = utils.toJSON(response.data.responseText);
  2091. if (!response.status || !ApiResponseCheck.isSuccessResponse(response.data.responseText)) {
  2092. log.error("获取收藏夹信息失败,请求异常");
  2093. if (typeof data.msg === "string") {
  2094. Qmsg.error(data.msg);
  2095. } else {
  2096. Qmsg.error("获取收藏夹信息失败");
  2097. }
  2098. return;
  2099. }
  2100. return data.data.result;
  2101. },
  2102. /**
  2103. * 添加到某个收藏夹
  2104. */
  2105. async addFavoriteInFolds(requestData) {
  2106. let response = await httpx.post(
  2107. "https://mp-action.csdn.net/interact/wrapper/pc/favorite/v1/api/addFavoriteInFolds",
  2108. {
  2109. fetch: true,
  2110. data: requestData,
  2111. headers: {
  2112. "Content-Type": "application/json",
  2113. "User-Agent": utils.getRandomPCUA()
  2114. },
  2115. allowInterceptConfig: false
  2116. }
  2117. );
  2118. log.info(response);
  2119. if (!response.status || !ApiResponseCheck.isSuccessResponse(response.data.responseText)) {
  2120. log.error("添加收藏失败,请求异常", response);
  2121. Qmsg.error("添加收藏失败,请求异常");
  2122. return;
  2123. }
  2124. return true;
  2125. },
  2126. /**
  2127. * 检查收藏夹信息
  2128. * @param url
  2129. * @returns
  2130. * + true 已收藏
  2131. * + false 未收藏
  2132. */
  2133. async checkFavoriteByUrl(url) {
  2134. debugger;
  2135. let response = await httpx.get(
  2136. `https://mp-action.csdn.net/interact/wrapper/pc/favorite/v1/api/checkFavoriteByUrl`,
  2137. {
  2138. data: {
  2139. url
  2140. },
  2141. fetch: true,
  2142. allowInterceptConfig: false,
  2143. headers: {
  2144. "User-Agent": utils.getRandomPCUA()
  2145. }
  2146. }
  2147. );
  2148. log.info(response);
  2149. if (!response.status || !ApiResponseCheck.isSuccessResponse(response.data.responseText)) {
  2150. log.error("检查收藏夹状态失败,请求异常");
  2151. Qmsg.error("检查收藏夹状态失败,请求异常");
  2152. return;
  2153. }
  2154. let data = utils.toJSON(response.data.responseText);
  2155. return data.data;
  2156. },
  2157. /**
  2158. * 创建收藏夹
  2159. */
  2160. async createFolder(config) {
  2161. let response = await httpx.post(
  2162. `https://mp-action.csdn.net/interact/wrapper/pc/favorite/v1/api/createFolder`,
  2163. {
  2164. data: config,
  2165. fetch: true,
  2166. headers: {
  2167. Accept: "application/json, text/javascript, */*; q=0.01",
  2168. "Content-Type": "application/json",
  2169. "User-Agent": utils.getRandomPCUA()
  2170. },
  2171. allowInterceptConfig: false
  2172. }
  2173. );
  2174. log.info(response);
  2175. if (!response.status || !ApiResponseCheck.isSuccessResponse(response.data.responseText)) {
  2176. Qmsg.error("创建收藏夹失败");
  2177. return;
  2178. }
  2179. let data = utils.toJSON(response.data.responseText);
  2180. return data.data;
  2181. }
  2182. };
  2183. const M_CSDNBlogArticle = {
  2184. init() {
  2185. Panel.exec(
  2186. "m-csdn-blog-shieldTopToolbar",
  2187. () => {
  2188. return this.shieldTopToolbar();
  2189. },
  2190. (keyList) => {
  2191. return !Panel.getValue(keyList[0]);
  2192. },
  2193. true
  2194. );
  2195. Panel.execMenuOnce("m-csdn-blog-notLimitCodePreMaxHeight", () => {
  2196. return this.notLimitCodePreMaxHeight();
  2197. });
  2198. Panel.execMenuOnce("m-csdn-blog-notLimitCommentMaxHeight", () => {
  2199. return this.notLimitCommentMaxHeight();
  2200. });
  2201. Panel.execMenuOnce("m-csdn-blog-allowSelectText", () => {
  2202. return this.allowSelectText();
  2203. });
  2204. Panel.execMenuOnce("m-csdn-blog-autoExpandContent", () => {
  2205. return this.autoExpandContent();
  2206. });
  2207. Panel.exec(
  2208. "m-csdn-blog-bottomArticleEnable",
  2209. () => {
  2210. return this.blockBottomArticle();
  2211. },
  2212. (keyList) => {
  2213. return !Panel.getValue(keyList[0]);
  2214. },
  2215. true
  2216. );
  2217. Panel.exec(
  2218. "m-csdn-blog-comment-enable",
  2219. () => {
  2220. return this.blockComment();
  2221. },
  2222. (keyList) => {
  2223. return !Panel.getValue(keyList[0]);
  2224. },
  2225. true
  2226. );
  2227. Panel.exec(
  2228. "m-csdn-blog-bottom-toolbar-enable",
  2229. () => {
  2230. return this.blockBottomToolBar();
  2231. },
  2232. (keyList) => {
  2233. return !Panel.getValue(keyList[0]);
  2234. },
  2235. true
  2236. );
  2237. Panel.execMenuOnce("m-csdn-blog-bottom-toolbar-always-bottom", () => {
  2238. return this.bottomToolBarAlwaysShow();
  2239. });
  2240. domUtils.ready(() => {
  2241. Panel.execMenuOnce("m-csdn-blog-removeAds", () => {
  2242. return this.removeAds();
  2243. });
  2244. Panel.execMenuOnce("m-csdn-blog-refactoringRecommendation", () => {
  2245. this.refactoringRecommendation();
  2246. });
  2247. Panel.execMenuOnce("m-csdn-blog-unBlockCopy", () => {
  2248. CSDNBlog.unBlockCopy();
  2249. });
  2250. Panel.execMenuOnce(
  2251. "m-csdn-blog-bottom-toolbar-optimizationCollectButton",
  2252. () => {
  2253. this.optimizationCollectButton();
  2254. }
  2255. );
  2256. });
  2257. },
  2258. /**
  2259. * 屏蔽顶部Toolbar
  2260. */
  2261. shieldTopToolbar() {
  2262. log.info("屏蔽顶部Toolbar");
  2263. return [
  2264. CommonUtil.addBlockCSS("#csdn-toolbar"),
  2265. addStyle(
  2266. /*css*/
  2267. `
  2268. /* 内容顶部要归位 */
  2269. body #main,
  2270. .margin_sides{
  2271. margin-top: unset !important;
  2272. padding-top: unset !important;
  2273. }
  2274. #article .article_title{
  2275. margin-top: .32rem !important;
  2276. padding-top: unset !important;
  2277. }
  2278. `
  2279. )
  2280. ];
  2281. },
  2282. /**
  2283. * 重构底部推荐
  2284. */
  2285. refactoringRecommendation() {
  2286. function refactoring() {
  2287. document.querySelectorAll(".container-fluid").forEach((item) => {
  2288. let url = "";
  2289. let title = "";
  2290. let content = "";
  2291. let img = "";
  2292. let isCSDNDownload = false;
  2293. let isCSDNEduDownload = false;
  2294. if (item.hasAttribute("data-url")) {
  2295. url = item.getAttribute("data-url");
  2296. title = item.querySelector(".recommend_title div.left")?.innerHTML;
  2297. if (!item.querySelector(".text")) {
  2298. return;
  2299. }
  2300. content = item.querySelector(".text")?.innerHTML;
  2301. if (item.querySelectorAll(".recommend-img").length) {
  2302. item.querySelectorAll(".recommend-img").forEach((item2) => {
  2303. img += item2.innerHTML;
  2304. });
  2305. }
  2306. } else {
  2307. url = item.querySelector("a[data-type]").getAttribute("href");
  2308. title = item.querySelector(".recommend_title div.left").innerHTML;
  2309. content = item.querySelector(".text").innerHTML;
  2310. }
  2311. var _URL_ = new URL(url);
  2312. if (_URL_.host === "download.csdn.net" || _URL_.host === "www.iteye.com" && _URL_.pathname.match(/^\/resource/gi)) {
  2313. isCSDNDownload = true;
  2314. title = `<div class="component-box"><a class="praise" href="javascript:;">CSDN下载</a></div>` + title;
  2315. } else if (_URL_.origin.match(/edu.csdn.net/gi)) {
  2316. isCSDNEduDownload = true;
  2317. title = `<div class="component-box"><a class="csdn-edu-title" href="javascript:;">CSDN学院</a></div>` + title;
  2318. }
  2319. item.setAttribute("class", "GM-csdn-dl");
  2320. item.setAttribute("data-url", url);
  2321. item.innerHTML = `<div class="GM-csdn-title"><div class="left">${title}</div></div><div class="GM-csdn-content">${content}</div><div class="GM-csdn-img">${img}</div>`;
  2322. item.addEventListener("click", function() {
  2323. if (Panel.getValue("m-csdn-blog-openNewTab")) {
  2324. window.open(url, "_blank");
  2325. } else {
  2326. window.location.href = url;
  2327. }
  2328. });
  2329. if ((isCSDNDownload || isCSDNEduDownload) && Panel.getValue("m-csdn-blog-removeResourceArticle")) {
  2330. item.remove();
  2331. }
  2332. });
  2333. }
  2334. let lockFunction = new utils.LockFunction(refactoring, 50);
  2335. utils.waitNode("#recommend").then(($recommend) => {
  2336. log.info("重构底部推荐");
  2337. lockFunction.run();
  2338. utils.mutationObserver($recommend, {
  2339. callback: () => {
  2340. lockFunction.run();
  2341. },
  2342. config: { childList: true, subtree: true, attributes: true }
  2343. });
  2344. });
  2345. },
  2346. /**
  2347. * 屏蔽底部文章
  2348. */
  2349. blockBottomArticle() {
  2350. log.info("屏蔽底部文章");
  2351. return CommonUtil.addBlockCSS("#recommend");
  2352. },
  2353. /**
  2354. * 屏蔽评论
  2355. */
  2356. blockComment() {
  2357. log.info("屏蔽评论");
  2358. return CommonUtil.addBlockCSS("#comment");
  2359. },
  2360. /**
  2361. * 去除广告
  2362. */
  2363. removeAds() {
  2364. log.info("去除广告");
  2365. return [
  2366. /* 登录(不可用)窗口 */
  2367. CommonUtil.waitRemove(".passport-login-container"),
  2368. /* 打开APP */
  2369. CommonUtil.waitRemove(".btn_open_app_prompt_box.detail-open-removed"),
  2370. /* 广告 */
  2371. CommonUtil.waitRemove(".add-firstAd"),
  2372. /* 打开CSDN APP 小程序看全文 */
  2373. CommonUtil.waitRemove("div.feed-Sign-weixin"),
  2374. /* ios版本提示 */
  2375. CommonUtil.waitRemove("div.ios-shadowbox")
  2376. ];
  2377. },
  2378. /**
  2379. * 不限制代码块最大高度
  2380. */
  2381. notLimitCodePreMaxHeight() {
  2382. log.info("不限制代码块最大高度");
  2383. return addStyle(
  2384. /*css*/
  2385. `
  2386. pre{
  2387. max-height: unset !important;
  2388. }
  2389. `
  2390. );
  2391. },
  2392. /**
  2393. * 不限制评论区最大高度
  2394. */
  2395. notLimitCommentMaxHeight() {
  2396. log.info("不限制评论区最大高度");
  2397. return addStyle(
  2398. /*css*/
  2399. `
  2400. #comment{
  2401. max-height: none !important;
  2402. }
  2403. `
  2404. );
  2405. },
  2406. /**
  2407. * 允许选择文字
  2408. */
  2409. allowSelectText() {
  2410. log.info("允许选择文字");
  2411. return addStyle(
  2412. /*css*/
  2413. `
  2414. #content_views,
  2415. #content_views pre,
  2416. #content_views pre code{
  2417. webkit-touch-callout: text !important;
  2418. -webkit-user-select: text !important;
  2419. -khtml-user-select: text !important;
  2420. -moz-user-select: text !important;
  2421. -ms-user-select: text !important;
  2422. user-select: text !important;
  2423. }
  2424. `
  2425. );
  2426. },
  2427. /**
  2428. * 自动展开内容
  2429. */
  2430. autoExpandContent() {
  2431. log.info("自动展开内容");
  2432. return addStyle(
  2433. /*css*/
  2434. `
  2435. #content_views pre.set-code-hide,
  2436. .article_content{
  2437. height: 100% !important;
  2438. overflow: auto !important;
  2439. }
  2440. `
  2441. );
  2442. },
  2443. /**
  2444. * 屏蔽底部工具栏
  2445. */
  2446. blockBottomToolBar() {
  2447. log.info(`屏蔽底部工具栏`);
  2448. return CommonUtil.addBlockCSS("#operate");
  2449. },
  2450. /**
  2451. * 底部工具栏常驻
  2452. */
  2453. bottomToolBarAlwaysShow() {
  2454. log.info(`底部工具栏常驻`);
  2455. return addStyle(
  2456. /*css*/
  2457. `
  2458. /* 底部工具栏 */
  2459. #operate {
  2460. bottom: 0 !important;
  2461. }
  2462. `
  2463. );
  2464. },
  2465. /**
  2466. * 优化收藏按钮
  2467. */
  2468. optimizationCollectButton() {
  2469. log.info(`优化收藏按钮`);
  2470. utils.waitNode("#operate .collect-btn", 1e4).then(($collectBtn) => {
  2471. if (!$collectBtn) {
  2472. return;
  2473. }
  2474. domUtils.on(
  2475. $collectBtn,
  2476. "click",
  2477. async (event) => {
  2478. utils.preventEvent(event);
  2479. let $isCollect = $collectBtn.querySelector(".collect");
  2480. let $unCollect = $collectBtn.querySelector(".uncollect");
  2481. let folderInfo = await CSDNFavoriteApi.folderListWithCheck(
  2482. window.location.origin + window.location.pathname
  2483. );
  2484. if (!folderInfo) {
  2485. return;
  2486. }
  2487. let isFavoriteFolderIdList = [];
  2488. folderInfo.forEach((item) => {
  2489. if (item.IsFavorite) {
  2490. isFavoriteFolderIdList.push(item.ID);
  2491. }
  2492. });
  2493. let createCollectItem = (data) => {
  2494. let folderId = data.ID;
  2495. let $item = domUtils.createElement(
  2496. "li",
  2497. {
  2498. className: "csdn-collection-item",
  2499. innerHTML: (
  2500. /*html*/
  2501. `
  2502. <div class="csdn-collection-item_left">
  2503. <div class="csdn-collection-item_title">
  2504. <span class="title-m">${data.Name}</span>
  2505. </div>
  2506. <span class="csdn-collection-item_ext">
  2507. <span class="csdn-collection-item_length">${data.FavoriteNum}条内容</span>
  2508. <span class="dot">・</span>
  2509. <span class="csdn-collection-controls">${data.IsPrivate ? "私密" : "公开"}</span>
  2510. </span>
  2511. </div>
  2512. <span class="collect-btn">${data.IsFavorite ? "已收藏" : "收藏"}</span>
  2513. `
  2514. )
  2515. },
  2516. {
  2517. "data-is-collect": data.IsFavorite
  2518. }
  2519. );
  2520. $item.querySelector(".title-m");
  2521. let $contentLength = $item.querySelector(
  2522. ".csdn-collection-item_length"
  2523. );
  2524. $item.querySelector(
  2525. ".csdn-collection-controls"
  2526. );
  2527. let $collectBtn2 = $item.querySelector(".collect-btn");
  2528. domUtils.on($collectBtn2, "click", async (event2) => {
  2529. let articleDetailUrl = _unsafeWindow.articleDetailUrl;
  2530. if (articleDetailUrl == null) {
  2531. articleDetailUrl = window.location.origin + window.location.pathname;
  2532. }
  2533. let articleId = _unsafeWindow.articleId;
  2534. if (articleId == null) {
  2535. log.error("获取文章ID失败");
  2536. Qmsg.error("获取文章ID失败");
  2537. return;
  2538. }
  2539. let username = _unsafeWindow.username;
  2540. if (username == null) {
  2541. log.error("获取文章作者失败");
  2542. Qmsg.error("获取文章作者失败");
  2543. return;
  2544. }
  2545. let articleTitle = _unsafeWindow.articleTitle;
  2546. if (articleTitle == null) {
  2547. articleTitle = document.title.replace(/-CSDN博客$/, "");
  2548. }
  2549. if (articleTitle == null) {
  2550. log.error("获取文章标题失败");
  2551. Qmsg.error("获取文章标题失败");
  2552. return;
  2553. }
  2554. let articleDesc = _unsafeWindow.articleDesc;
  2555. if (articleDesc == null) {
  2556. let $meta = $("meta[name='description']");
  2557. if ($meta) {
  2558. articleDesc = $meta.getAttribute("content");
  2559. }
  2560. }
  2561. if (articleDesc == null) {
  2562. log.error("获取文章描述失败");
  2563. Qmsg.error("获取文章描述失败");
  2564. return;
  2565. }
  2566. let folderIdList = [...isFavoriteFolderIdList];
  2567. let $loading = Qmsg.loading("处理中...");
  2568. try {
  2569. let checkResponse = await CSDNFavoriteApi.checkFavoriteByUrl(
  2570. articleDetailUrl
  2571. );
  2572. if (checkResponse == null) {
  2573. return;
  2574. }
  2575. log.info(folderId, checkResponse);
  2576. let toCollect = !checkResponse[folderId];
  2577. if (toCollect) {
  2578. log.info(`添加收藏`);
  2579. folderIdList.push(folderId);
  2580. } else {
  2581. log.info(`取消收藏`);
  2582. folderIdList.splice(folderIdList.indexOf(folderId), 1);
  2583. }
  2584. let response = await CSDNFavoriteApi.addFavoriteInFolds({
  2585. author: username,
  2586. url: articleDetailUrl,
  2587. source: "blog",
  2588. sourceId: articleId,
  2589. title: articleTitle,
  2590. description: articleDesc,
  2591. fromType: "PC",
  2592. username: data.Username,
  2593. folderIdList
  2594. });
  2595. if (!response) {
  2596. return;
  2597. }
  2598. let check_isCollect = await CSDNFavoriteApi.checkFavoriteByUrl(articleDetailUrl);
  2599. if (check_isCollect == null) {
  2600. return;
  2601. }
  2602. log.info(folderId, check_isCollect);
  2603. $item.setAttribute(
  2604. "data-is-collect",
  2605. (!!check_isCollect[folderId]).toString()
  2606. );
  2607. if (toCollect) {
  2608. if (!check_isCollect[folderId]) {
  2609. log.error("收藏失败", check_isCollect, folderId);
  2610. Qmsg.error("收藏失败");
  2611. } else {
  2612. log.success("收藏成功");
  2613. Qmsg.success("收藏成功");
  2614. domUtils.text($collectBtn2, "已收藏");
  2615. if (!isFavoriteFolderIdList.includes(folderId)) {
  2616. isFavoriteFolderIdList.push(folderId);
  2617. }
  2618. data.FavoriteNum++;
  2619. }
  2620. } else {
  2621. if (!check_isCollect[folderId]) {
  2622. log.success("取消收藏成功");
  2623. Qmsg.success("取消收藏成功");
  2624. domUtils.text($collectBtn2, "收藏");
  2625. if (isFavoriteFolderIdList.includes(folderId)) {
  2626. isFavoriteFolderIdList.splice(
  2627. isFavoriteFolderIdList.indexOf(folderId),
  2628. 1
  2629. );
  2630. }
  2631. data.FavoriteNum--;
  2632. } else {
  2633. log.error("取消收藏失败", check_isCollect, folderId);
  2634. Qmsg.error("取消收藏失败");
  2635. }
  2636. }
  2637. domUtils.text($contentLength, `${data.FavoriteNum}条内容`);
  2638. let findValue = Object.values(check_isCollect).find(
  2639. (item) => item
  2640. );
  2641. if (findValue) {
  2642. domUtils.show($isCollect, false);
  2643. domUtils.hide($unCollect, false);
  2644. } else {
  2645. domUtils.show($unCollect, false);
  2646. domUtils.hide($isCollect, false);
  2647. }
  2648. $loading.close();
  2649. } catch (error) {
  2650. log.error(error);
  2651. } finally {
  2652. $loading.close();
  2653. }
  2654. });
  2655. return $item;
  2656. };
  2657. let $alert = __pops.alert({
  2658. title: {
  2659. text: "添加收藏夹",
  2660. position: "center"
  2661. },
  2662. content: {
  2663. text: (
  2664. /*html*/
  2665. `
  2666. <ul class="csdn-collection-items"></ul>
  2667. `
  2668. ),
  2669. html: true
  2670. },
  2671. btn: {
  2672. ok: {
  2673. enable: false
  2674. }
  2675. },
  2676. width: PanelUISize.setting.width,
  2677. height: PanelUISize.setting.height,
  2678. drag: true,
  2679. mask: {
  2680. enable: true
  2681. },
  2682. style: (
  2683. /*css*/
  2684. `
  2685. .csdn-collection-items{
  2686. --font-size: 16px;
  2687. }
  2688. .csdn-collection-items{
  2689. font-size: var(--font-size);
  2690. font-weight: 400;
  2691. padding: 0 20px 0;
  2692. margin: 24px 0;
  2693. overflow: auto;
  2694. -ms-scroll-chaining: none;
  2695. overscroll-behavior: contain;
  2696. }
  2697. .csdn-collection-item{
  2698. width: 100%;
  2699. height: 62px;
  2700. line-height: normal;
  2701. position: relative;
  2702. padding: 8px 12px;
  2703. cursor: pointer;
  2704. display: -webkit-box;
  2705. display: -ms-flexbox;
  2706. display: flex;
  2707. -webkit-box-align: center;
  2708. -ms-flex-align: center;
  2709. align-items: center;
  2710. -webkit-box-pack: justify;
  2711. -ms-flex-pack: justify;
  2712. justify-content: space-between;
  2713. border-bottom: 1px solid #f0f0f5;
  2714. }
  2715. .csdn-collection-item_left{
  2716. line-height: normal;
  2717. flex: 1;
  2718. overflow: hidden;
  2719. }
  2720. .csdn-collection-item_title{
  2721. overflow: hidden;
  2722. text-overflow: ellipsis;
  2723. white-space: nowrap;
  2724. width: 100%;
  2725. }
  2726. .csdn-collection-item_ext{
  2727. font-weight: 400;
  2728. color: #999aaa;
  2729. line-height: 17px;
  2730. margin-top: 8px;
  2731. font-size: .8em;
  2732. overflow: hidden;
  2733. text-overflow: ellipsis;
  2734. white-space: nowrap;
  2735. width: 100%;
  2736. display: inline-flex;
  2737. align-items: center;
  2738. }
  2739. .collect-btn{
  2740. color: #555666;
  2741. font-size: var(--font-size);
  2742. width: 64px;
  2743. height: 30px;
  2744. line-height: 30px;
  2745. border-radius: 20px;
  2746. text-align: center;
  2747. -webkit-transition: all .2s;
  2748. transition: all .2s;
  2749. border: 1px solid #ccccd8;
  2750. }
  2751. .csdn-collection-item[data-is-collect="true"] .collect-btn{
  2752. color: #999aaa;
  2753. background: rgba(232, 232, 237, .3);
  2754. border: 1px solid #e8e8ed;
  2755. }
  2756. /* .csdn-collection-item:hover{
  2757. background: #f5f6f7;
  2758. }
  2759. .csdn-collection-item:hover .collect-btn{
  2760. border: 1px solid #555666;
  2761. } */
  2762. `
  2763. )
  2764. });
  2765. let $collectionContainer = $alert.$shadowRoot.querySelector(
  2766. ".csdn-collection-items"
  2767. );
  2768. folderInfo.forEach((folderInfoItem) => {
  2769. let $item = createCollectItem(folderInfoItem);
  2770. $collectionContainer.appendChild($item);
  2771. });
  2772. },
  2773. { capture: true }
  2774. );
  2775. });
  2776. }
  2777. };
  2778. const ShieldCSS$1 = "/* 右下角的买一年送3个月的广告图标 */\r\n.blind_box {\r\n display: none !important;\r\n}\r\n";
  2779. const M_CSDNWenKu = {
  2780. init() {
  2781. addStyle(ShieldCSS$1);
  2782. Panel.execMenuOnce("m-csdn-wenku-shieldBottomToolbar", () => {
  2783. return this.shieldBottomToolbar();
  2784. });
  2785. },
  2786. /**
  2787. * 【屏蔽】底部工具栏
  2788. */
  2789. shieldBottomToolbar() {
  2790. log.info("【屏蔽】底部工具栏");
  2791. return CommonUtil.addBlockCSS(`.page-container > div.btn`);
  2792. }
  2793. };
  2794. const CSDNBlockCSS = "/* 右下角悬浮图标 买1年送3个月 */\r\n.page-container .blind_box,\r\n/* 底部工具栏右边的 开会员按钮(低至xx元/次) */\r\n.page-container .btn .ml-12,\r\n/* 登录(不可用)弹窗 */\r\n.passport-login-container,\r\n/* 通用广告className匹配 */\r\n.ads {\r\n display: none !important;\r\n}\r\n";
  2795. const M_CSDNDownload = {
  2796. init() {
  2797. Panel.execMenuOnce("m-csdn-download-removeAds", () => {
  2798. return addStyle(CSDNBlockCSS);
  2799. });
  2800. Panel.execMenuOnce(
  2801. "m-csdn-download-automaticallyExpandResourceIntroduction",
  2802. () => {
  2803. return this.automaticallyExpandResourceIntroduction();
  2804. }
  2805. );
  2806. },
  2807. /**
  2808. * 自动展开资源介绍
  2809. */
  2810. automaticallyExpandResourceIntroduction() {
  2811. log.info("自动展开资源介绍");
  2812. return [
  2813. CommonUtil.addBlockCSS("label.unfold-font"),
  2814. addStyle(
  2815. /*css*/
  2816. `
  2817. .resource-desc{
  2818. max-height: unset !important;
  2819. overflow: unset !important;
  2820. }
  2821. `
  2822. )
  2823. ];
  2824. }
  2825. };
  2826. const ShieldCSS = ".view_comment_box,\r\n.weixin-shadowbox.wap-shadowbox,\r\n.feed-Sign-span,\r\n.user-desc.user-desc-fix,\r\n.comment_read_more_box,\r\n#content_views pre.set-code-hide .hide-preCode-box,\r\n/* 登录(不可用)弹窗 */\r\n.passport-login-container,\r\n.hljs-button[data-title='登录(不可用)后复制'],\r\n.article-show-more,\r\n#treeSkill,\r\ndiv.btn_open_app_prompt_div,\r\ndiv.readall_box,\r\ndiv.aside-header-fixed,\r\ndiv.feed-Sign-weixin,\r\ndiv.ios-shadowbox,\r\n/* 底部评论工具栏的抢沙发图片 */\r\n.comment-sofa-flag {\r\n display: none !important;\r\n}\r\n";
  2827. const MBlogCSS = "#mainBox {\r\n width: auto;\r\n}\r\n.user-desc.user-desc-fix {\r\n height: auto !important;\r\n overflow: auto !important;\r\n}\r\n.component-box .praise {\r\n background: #ff5722;\r\n border-radius: 5px;\r\n padding: 0px 8px;\r\n height: auto;\r\n}\r\n.component-box .praise,\r\n.component-box .share {\r\n color: #fff;\r\n}\r\n.component-box a {\r\n display: inline-block;\r\n font-size: xx-small;\r\n}\r\n.component-box {\r\n display: inline;\r\n margin: 0;\r\n position: relative;\r\n white-space: nowrap;\r\n}\r\n.csdn-edu-title {\r\n background: #4d6de1;\r\n border-radius: 5px;\r\n padding: 0px 8px;\r\n height: auto;\r\n color: #fff !important;\r\n}\r\n\r\n.GM-csdn-dl {\r\n padding: 0.24rem 0.32rem;\r\n width: 100%;\r\n justify-content: space-between;\r\n -webkit-box-pack: justify;\r\n border-bottom: 1px solid #f5f6f7 !important;\r\n}\r\n.GM-csdn-title {\r\n font-size: 0.3rem;\r\n color: #222226;\r\n letter-spacing: 0;\r\n line-height: 0.44rem;\r\n font-weight: 600;\r\n /*max-height: .88rem;*/\r\n word-break: break-all;\r\n overflow: hidden;\r\n display: -webkit-box;\r\n -webkit-box-orient: vertical;\r\n -webkit-line-clamp: 2;\r\n}\r\n.GM-csdn-title a {\r\n word-break: break-all;\r\n color: #222226;\r\n font-weight: 600;\r\n}\r\n.GM-csdn-title em,\r\n.GM-csdn-content em {\r\n font-style: normal;\r\n color: #fc5531;\r\n}\r\n.GM-csdn-content {\r\n /*max-width: 5.58rem;*/\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n display: -webkit-box;\r\n -webkit-line-clamp: 1;\r\n -webkit-box-orient: vertical;\r\n color: #555666;\r\n font-size: 0.24rem;\r\n line-height: 0.34rem;\r\n max-height: 0.34rem;\r\n word-break: break-all;\r\n -webkit-box-flex: 1;\r\n -ms-flex: 1;\r\n flex: 1;\r\n margin-top: 0.16rem;\r\n}\r\n.GM-csdn-img img {\r\n width: 2.18rem;\r\n height: 1.58rem;\r\n /*margin-left: .16rem*/\r\n}\r\n";
  2828. const M_CSDNBlog = {
  2829. init() {
  2830. this.addCSS();
  2831. },
  2832. /**
  2833. * 添加屏蔽CSS
  2834. */
  2835. addCSS() {
  2836. return [addStyle(ShieldCSS), addStyle(MBlogCSS)];
  2837. }
  2838. };
  2839. const M_CSDN = {
  2840. init() {
  2841. if (CSDNRouter.isLink()) {
  2842. log.info("Router: 中转链接");
  2843. M_CSDNLink.init();
  2844. } else if (CSDNRouter.isHuaWeiCloudBlog()) {
  2845. log.info("Router: 华为云联盟");
  2846. M_CSDNHuaWeiCloud.init();
  2847. } else if (CSDNRouter.isBlog()) {
  2848. log.info("Router: 博客");
  2849. M_CSDNBlog.init();
  2850. if (CSDNRouter.isBlogArticle()) {
  2851. log.info("Router: 文章");
  2852. M_CSDNBlogArticle.init();
  2853. }
  2854. } else if (CSDNRouter.isWenKu()) {
  2855. log.info("Router: 文库");
  2856. M_CSDNWenKu.init();
  2857. } else if (CSDNRouter.isDownload()) {
  2858. log.info("Router: 资源下载");
  2859. M_CSDNDownload.init();
  2860. } else {
  2861. log.error("暂未适配,请反馈开发者:" + globalThis.location.href);
  2862. }
  2863. }
  2864. };
  2865. const PanelComponents = {
  2866. $data: {
  2867. __storeApiFn: null,
  2868. get storeApiValue() {
  2869. if (!this.__storeApiFn) {
  2870. this.__storeApiFn = new Utils.Dictionary();
  2871. }
  2872. return this.__storeApiFn;
  2873. }
  2874. },
  2875. /**
  2876. * 获取自定义的存储接口
  2877. * @param type 组件类型
  2878. */
  2879. getStorageApi(type) {
  2880. if (!this.hasStorageApi(type)) {
  2881. return;
  2882. }
  2883. return this.$data.storeApiValue.get(type);
  2884. },
  2885. /**
  2886. * 判断是否存在自定义的存储接口
  2887. * @param type 组件类型
  2888. */
  2889. hasStorageApi(type) {
  2890. return this.$data.storeApiValue.has(type);
  2891. },
  2892. /**
  2893. * 设置自定义的存储接口
  2894. * @param type 组件类型
  2895. * @param storageApiValue 存储接口
  2896. */
  2897. setStorageApi(type, storageApiValue) {
  2898. this.$data.storeApiValue.set(type, storageApiValue);
  2899. },
  2900. /**
  2901. * 初始化组件的存储接口属性
  2902. *
  2903. * @param type 组件类型
  2904. * @param config 组件配置,必须包含prop属性
  2905. * @param storageApiValue 存储接口
  2906. */
  2907. initComponentsStorageApi(type, config, storageApiValue) {
  2908. let propsStorageApi;
  2909. if (this.hasStorageApi(type)) {
  2910. propsStorageApi = this.getStorageApi(type);
  2911. } else {
  2912. propsStorageApi = storageApiValue;
  2913. }
  2914. this.setComponentsStorageApiProperty(config, propsStorageApi);
  2915. },
  2916. /**
  2917. * 设置组件的存储接口属性
  2918. * @param config 组件配置,必须包含prop属性
  2919. * @param storageApiValue 存储接口
  2920. */
  2921. setComponentsStorageApiProperty(config, storageApiValue) {
  2922. Reflect.set(config.props, PROPS_STORAGE_API, storageApiValue);
  2923. }
  2924. };
  2925. const UISwitch = function(text, key, defaultValue, clickCallback, description, afterAddToUListCallBack) {
  2926. let result = {
  2927. text,
  2928. type: "switch",
  2929. description,
  2930. attributes: {},
  2931. props: {},
  2932. getValue() {
  2933. let storageApiValue = this.props[PROPS_STORAGE_API];
  2934. return Boolean(storageApiValue.get(key, defaultValue));
  2935. },
  2936. callback(event, __value) {
  2937. let value = Boolean(__value);
  2938. log.success(`${value ? "开启" : "关闭"} ${text}`);
  2939. if (typeof clickCallback === "function") {
  2940. let result2 = clickCallback(event, value);
  2941. if (result2) {
  2942. return;
  2943. }
  2944. }
  2945. let storageApiValue = this.props[PROPS_STORAGE_API];
  2946. storageApiValue.set(key, value);
  2947. },
  2948. afterAddToUListCallBack
  2949. };
  2950. Reflect.set(result.attributes, ATTRIBUTE_KEY, key);
  2951. Reflect.set(result.attributes, ATTRIBUTE_DEFAULT_VALUE, defaultValue);
  2952. PanelComponents.initComponentsStorageApi(
  2953. "switch",
  2954. result,
  2955. {
  2956. get(key2, defaultValue2) {
  2957. return Panel.getValue(key2, defaultValue2);
  2958. },
  2959. set(key2, value) {
  2960. Panel.setValue(key2, value);
  2961. }
  2962. }
  2963. );
  2964. return result;
  2965. };
  2966. const UISelect = function(text, key, defaultValue, data, changeCallback, description) {
  2967. let selectData = [];
  2968. if (typeof data === "function") {
  2969. selectData = data();
  2970. } else {
  2971. selectData = data;
  2972. }
  2973. let result = {
  2974. text,
  2975. type: "select",
  2976. description,
  2977. attributes: {},
  2978. props: {},
  2979. getValue() {
  2980. let storageApiValue = this.props[PROPS_STORAGE_API];
  2981. return storageApiValue.get(key, defaultValue);
  2982. },
  2983. callback(event, isSelectedValue, isSelectedText) {
  2984. let value = isSelectedValue;
  2985. log.info(`选择:${isSelectedText}`);
  2986. if (typeof changeCallback === "function") {
  2987. let result2 = changeCallback(event, value, isSelectedText);
  2988. if (result2) {
  2989. return;
  2990. }
  2991. }
  2992. let storageApiValue = this.props[PROPS_STORAGE_API];
  2993. storageApiValue.set(key, value);
  2994. },
  2995. data: selectData
  2996. };
  2997. Reflect.set(result.attributes, ATTRIBUTE_KEY, key);
  2998. Reflect.set(result.attributes, ATTRIBUTE_DEFAULT_VALUE, defaultValue);
  2999. PanelComponents.initComponentsStorageApi(
  3000. "select",
  3001. result,
  3002. {
  3003. get(key2, defaultValue2) {
  3004. return Panel.getValue(key2, defaultValue2);
  3005. },
  3006. set(key2, value) {
  3007. Panel.setValue(key2, value);
  3008. }
  3009. }
  3010. );
  3011. return result;
  3012. };
  3013. const SettingUICommon = {
  3014. id: "component-common",
  3015. title: "通用",
  3016. forms: [
  3017. {
  3018. text: "Toast配置",
  3019. type: "forms",
  3020. forms: [
  3021. UISelect(
  3022. "Toast位置",
  3023. "qmsg-config-position",
  3024. "bottom",
  3025. [
  3026. {
  3027. value: "topleft",
  3028. text: "左上角"
  3029. },
  3030. {
  3031. value: "top",
  3032. text: "顶部"
  3033. },
  3034. {
  3035. value: "topright",
  3036. text: "右上角"
  3037. },
  3038. {
  3039. value: "left",
  3040. text: "左边"
  3041. },
  3042. {
  3043. value: "center",
  3044. text: "中间"
  3045. },
  3046. {
  3047. value: "right",
  3048. text: "右边"
  3049. },
  3050. {
  3051. value: "bottomleft",
  3052. text: "左下角"
  3053. },
  3054. {
  3055. value: "bottom",
  3056. text: "底部"
  3057. },
  3058. {
  3059. value: "bottomright",
  3060. text: "右下角"
  3061. }
  3062. ],
  3063. (event, isSelectValue, isSelectText) => {
  3064. log.info("设置当前Qmsg弹出位置" + isSelectText);
  3065. },
  3066. "Toast显示在页面九宫格的位置"
  3067. ),
  3068. UISelect(
  3069. "最多显示的数量",
  3070. "qmsg-config-maxnums",
  3071. 3,
  3072. [
  3073. {
  3074. value: 1,
  3075. text: "1"
  3076. },
  3077. {
  3078. value: 2,
  3079. text: "2"
  3080. },
  3081. {
  3082. value: 3,
  3083. text: "3"
  3084. },
  3085. {
  3086. value: 4,
  3087. text: "4"
  3088. },
  3089. {
  3090. value: 5,
  3091. text: "5"
  3092. }
  3093. ],
  3094. void 0,
  3095. "限制Toast显示的数量"
  3096. ),
  3097. UISwitch(
  3098. "逆序弹出",
  3099. "qmsg-config-showreverse",
  3100. false,
  3101. void 0,
  3102. "修改Toast弹出的顺序"
  3103. )
  3104. ]
  3105. }
  3106. // {
  3107. // text: "Cookie配置",
  3108. // type: "forms",
  3109. // forms: [
  3110. // UISwitch(
  3111. // "启用",
  3112. // "httpx-use-cookie-enable",
  3113. // false,
  3114. // void 0,
  3115. // "启用后,将根据下面的配置进行添加cookie"
  3116. // ),
  3117. // UISwitch(
  3118. // "使用document.cookie",
  3119. // "httpx-use-document-cookie",
  3120. // false,
  3121. // void 0,
  3122. // "自动根据请求的域名来设置对应的cookie"
  3123. // ),
  3124. // UITextArea(
  3125. // "tieba.baidu.com",
  3126. // "httpx-cookie-tieba.baidu.com",
  3127. // "",
  3128. // void 0,
  3129. // void 0,
  3130. // "Cookie格式:xxx=xxxx;xxx=xxxx"
  3131. // ),
  3132. // ],
  3133. // },
  3134. ]
  3135. };
  3136. const UISlider = function(text, key, defaultValue, min, max, changeCallback, getToolTipContent, description, step) {
  3137. let result = {
  3138. text,
  3139. type: "slider",
  3140. description,
  3141. attributes: {},
  3142. props: {},
  3143. getValue() {
  3144. let storageApiValue = this.props[PROPS_STORAGE_API];
  3145. return storageApiValue.get(key, defaultValue);
  3146. },
  3147. getToolTipContent(value) {
  3148. if (typeof getToolTipContent === "function") {
  3149. return getToolTipContent(value);
  3150. } else {
  3151. return `${value}`;
  3152. }
  3153. },
  3154. callback(event, value) {
  3155. if (typeof changeCallback === "function") {
  3156. let result2 = changeCallback(event, value);
  3157. if (result2) {
  3158. return;
  3159. }
  3160. }
  3161. let storageApiValue = this.props[PROPS_STORAGE_API];
  3162. storageApiValue.set(key, value);
  3163. },
  3164. min,
  3165. max,
  3166. step
  3167. };
  3168. Reflect.set(result.attributes, ATTRIBUTE_KEY, key);
  3169. Reflect.set(result.attributes, ATTRIBUTE_DEFAULT_VALUE, defaultValue);
  3170. PanelComponents.initComponentsStorageApi(
  3171. "slider",
  3172. result,
  3173. {
  3174. get(key2, defaultValue2) {
  3175. return Panel.getValue(key2, defaultValue2);
  3176. },
  3177. set(key2, value) {
  3178. Panel.setValue(key2, value);
  3179. }
  3180. }
  3181. );
  3182. return result;
  3183. };
  3184. const SettingUIBlog = {
  3185. id: "panel-blog",
  3186. title: "博客",
  3187. isDefault() {
  3188. return CSDNRouter.isBlog();
  3189. },
  3190. forms: [
  3191. {
  3192. type: "forms",
  3193. text: "",
  3194. forms: [
  3195. {
  3196. type: "deepMenu",
  3197. text: "文章",
  3198. forms: [
  3199. {
  3200. type: "forms",
  3201. text: "",
  3202. forms: [
  3203. {
  3204. text: "布局屏蔽",
  3205. type: "deepMenu",
  3206. forms: [
  3207. {
  3208. text: "",
  3209. type: "forms",
  3210. forms: [
  3211. UISwitch(
  3212. "【屏蔽】登录(不可用)弹窗",
  3213. "csdn-blog-shieldLoginDialog",
  3214. true
  3215. ),
  3216. UISwitch(
  3217. "【屏蔽】左侧博客信息",
  3218. "csdn-blog-shieldLeftBlogContainerAside",
  3219. false
  3220. ),
  3221. UISwitch(
  3222. "【屏蔽】右侧目录信息",
  3223. "csdn-blog-shieldRightDirectoryInformation",
  3224. false
  3225. ),
  3226. UISwitch(
  3227. "【屏蔽】底部的悬浮工具栏",
  3228. "csdn-blog-shieldBottomFloatingToolbar",
  3229. false
  3230. )
  3231. ]
  3232. }
  3233. ]
  3234. },
  3235. {
  3236. text: "右侧悬浮工具栏",
  3237. type: "deepMenu",
  3238. forms: [
  3239. {
  3240. text: "功能",
  3241. type: "forms",
  3242. forms: [
  3243. UISwitch(
  3244. "启用",
  3245. "csdn-blog-rightToolbarEnable",
  3246. true,
  3247. void 0,
  3248. "创作中心,隐藏/显示侧栏,新手引导,客服、举报..."
  3249. ),
  3250. UISwitch(
  3251. "【添加按钮】前往评论",
  3252. "csdn-blog-addGotoRecommandButton",
  3253. true,
  3254. void 0,
  3255. "在悬浮工具栏最后面添加"
  3256. ),
  3257. UISlider(
  3258. "right偏移",
  3259. "csdn-blog-rightToolbarRightOffset",
  3260. 90,
  3261. 0,
  3262. document.documentElement.clientWidth,
  3263. (event, value) => {
  3264. let csdnSideToolbar = document.querySelector(
  3265. ".csdn-side-toolbar"
  3266. );
  3267. domUtils.css(csdnSideToolbar, {
  3268. right: value + "px"
  3269. });
  3270. },
  3271. (value) => {
  3272. return `当前:${value}px,默认:90px`;
  3273. }
  3274. ),
  3275. UISlider(
  3276. "top偏移",
  3277. "csdn-blog-rightToolbarTopOffset",
  3278. 140,
  3279. 0,
  3280. document.documentElement.clientHeight,
  3281. (event, value) => {
  3282. let csdnSideToolbar = document.querySelector(
  3283. ".csdn-side-toolbar"
  3284. );
  3285. domUtils.css(csdnSideToolbar, {
  3286. top: value + "px"
  3287. });
  3288. },
  3289. (value) => {
  3290. return `当前:${value}px,默认:90px`;
  3291. }
  3292. )
  3293. ]
  3294. },
  3295. {
  3296. text: "屏蔽",
  3297. type: "forms",
  3298. forms: [
  3299. UISwitch(
  3300. "【屏蔽】创作中心",
  3301. "csdn-blog-rightToolbarCreativeCenter",
  3302. false
  3303. ),
  3304. UISwitch(
  3305. "【屏蔽】显示/隐藏侧栏",
  3306. "csdn-blog-rightToolbarShowOrSidebar",
  3307. false
  3308. ),
  3309. UISwitch(
  3310. "【屏蔽】新手引导",
  3311. "csdn-blog-rightToolbarBeginnerGuidance",
  3312. false
  3313. ),
  3314. UISwitch(
  3315. "【屏蔽】客服",
  3316. "csdn-blog-rightToolbarCustomerService",
  3317. false
  3318. ),
  3319. UISwitch(
  3320. "【屏蔽】举报",
  3321. "csdn-blog-rightToolbarReport",
  3322. false
  3323. ),
  3324. UISwitch(
  3325. "【屏蔽】返回顶部",
  3326. "csdn-blog-rightToolbarBackToTop",
  3327. false
  3328. )
  3329. ]
  3330. }
  3331. ]
  3332. },
  3333. {
  3334. text: "内容",
  3335. type: "deepMenu",
  3336. forms: [
  3337. {
  3338. text: "功能",
  3339. type: "forms",
  3340. forms: [
  3341. UISwitch(
  3342. "点击代码块自动展开",
  3343. "csdn-blog-clickPreCodeAutomatically",
  3344. true,
  3345. void 0,
  3346. "当鼠标点击代码块区域时,将自动展开内容"
  3347. ),
  3348. UISwitch(
  3349. "自动展开代码块",
  3350. "csdn-blog-autoExpandCodeContent",
  3351. true,
  3352. void 0,
  3353. "懒人操作,免手动点击展开"
  3354. ),
  3355. UISwitch(
  3356. "自动展开内容",
  3357. "csdn-blog-autoExpandContent",
  3358. true,
  3359. void 0,
  3360. "懒人操作,免手动点击展开"
  3361. ),
  3362. UISwitch(
  3363. "全文居中",
  3364. "csdn-blog-articleCenter",
  3365. true,
  3366. function(event, enable) {
  3367. if (enable) {
  3368. alert(
  3369. "为了更好的呈现效果,请开启功能:【屏蔽】左侧博客信息、【屏蔽】右侧目录信息"
  3370. );
  3371. }
  3372. },
  3373. "自动屏蔽左侧和右侧的信息,且将文章居中"
  3374. ),
  3375. UISwitch(
  3376. "允许选择内容",
  3377. "csdn-blog-allowSelectContent",
  3378. true,
  3379. void 0
  3380. )
  3381. ]
  3382. },
  3383. {
  3384. text: "屏蔽",
  3385. type: "forms",
  3386. forms: [
  3387. UISwitch(
  3388. "【屏蔽】底部xx技能树",
  3389. "csdn-blog-shieldBottomSkillTree",
  3390. false
  3391. ),
  3392. UISwitch(
  3393. "【屏蔽】选中文字悬浮栏",
  3394. "csdn-blog-shieldArticleSearchTip",
  3395. false,
  3396. void 0,
  3397. "选中文字弹出的,例如:搜索、评论、笔记"
  3398. )
  3399. ]
  3400. }
  3401. ]
  3402. },
  3403. {
  3404. text: "评论区",
  3405. type: "deepMenu",
  3406. forms: [
  3407. {
  3408. text: "",
  3409. type: "forms",
  3410. forms: [
  3411. UISwitch(
  3412. "启用",
  3413. "csdn-blog-blockComment",
  3414. true,
  3415. void 0,
  3416. "关闭是屏蔽评论区"
  3417. ),
  3418. UISwitch(
  3419. "优化评论区的位置",
  3420. "csdn-blog-restoreComments",
  3421. true
  3422. )
  3423. ]
  3424. }
  3425. ]
  3426. },
  3427. {
  3428. text: "底部文章",
  3429. type: "deepMenu",
  3430. forms: [
  3431. {
  3432. text: "",
  3433. type: "forms",
  3434. forms: [
  3435. UISwitch(
  3436. "启用",
  3437. "csdn-blog-bottomRecommendArticleEnable",
  3438. true,
  3439. void 0,
  3440. "关闭是屏蔽底部文章"
  3441. ),
  3442. UISwitch(
  3443. "标识CSDN下载",
  3444. "csdn-blog-identityCSDNDownload",
  3445. true,
  3446. void 0,
  3447. "使用红框标识"
  3448. ),
  3449. UISwitch(
  3450. "移除资源下载的文章",
  3451. "csdn-blog-removeResourceDownloadArticle",
  3452. false,
  3453. void 0,
  3454. "download.csdn.net<br>www.iteye.com<br>edu.csdn.net"
  3455. )
  3456. ]
  3457. }
  3458. ]
  3459. }
  3460. ]
  3461. }
  3462. ]
  3463. }
  3464. ]
  3465. },
  3466. {
  3467. text: "",
  3468. type: "forms",
  3469. forms: [
  3470. {
  3471. text: "全局布局屏蔽",
  3472. type: "deepMenu",
  3473. forms: [
  3474. {
  3475. text: "",
  3476. type: "forms",
  3477. forms: [
  3478. UISwitch(
  3479. "【屏蔽】顶部工具栏",
  3480. "csdn-blog-shieldTopToolbar",
  3481. false
  3482. )
  3483. ]
  3484. }
  3485. ]
  3486. },
  3487. {
  3488. text: "劫持/拦截",
  3489. type: "deepMenu",
  3490. forms: [
  3491. {
  3492. text: "",
  3493. type: "forms",
  3494. forms: [
  3495. UISwitch(
  3496. "拦截-复制的小尾巴",
  3497. "csdn-blog-removeClipboardHijacking",
  3498. true
  3499. ),
  3500. UISwitch(
  3501. "劫持-禁止复制",
  3502. "csdn-blog-unBlockCopy",
  3503. true,
  3504. void 0,
  3505. "允许点击复制按钮进行复制"
  3506. )
  3507. ]
  3508. }
  3509. ]
  3510. }
  3511. ]
  3512. }
  3513. ]
  3514. };
  3515. const SettingUILink = {
  3516. id: "panel-link",
  3517. title: "链接",
  3518. isDefault() {
  3519. return CSDNRouter.isLink();
  3520. },
  3521. forms: [
  3522. {
  3523. text: "功能",
  3524. type: "forms",
  3525. forms: [
  3526. UISwitch(
  3527. "重定向链接",
  3528. "csdn-link-jumpRedirect",
  3529. true,
  3530. void 0,
  3531. "自动跳转至被拦截的Url链接"
  3532. )
  3533. ]
  3534. }
  3535. ]
  3536. };
  3537. const SettingUIHuaWeiCloud = {
  3538. id: "panel-hua-wei-cloud",
  3539. title: "华为云开发者联盟",
  3540. isDefault() {
  3541. return CSDNRouter.isHuaWeiCloudBlog();
  3542. },
  3543. forms: [
  3544. {
  3545. text: "功能",
  3546. type: "forms",
  3547. forms: [
  3548. UISwitch("自动展开全文", "csdn-hua-wei-cloud-autoExpandContent", true)
  3549. ]
  3550. },
  3551. {
  3552. text: "屏蔽",
  3553. type: "forms",
  3554. forms: [
  3555. UISwitch(
  3556. "【屏蔽】云开发者任务挑战活动",
  3557. "csdn-hua-wei-cloud-shieldCloudDeveloperTaskChallengeEvent",
  3558. true
  3559. ),
  3560. UISwitch(
  3561. "【屏蔽】左侧悬浮按钮",
  3562. "csdn-hua-wei-cloud-shieldLeftFloatingButton",
  3563. false,
  3564. function(event, enable) {
  3565. if (enable) {
  3566. alert(
  3567. "开启后将屏蔽【当前阅读量】、【点赞按钮】、【评论按钮】、【分享按钮】"
  3568. );
  3569. }
  3570. }
  3571. ),
  3572. UISwitch(
  3573. "【屏蔽】右侧栏",
  3574. "csdn-hua-wei-cloud-blockRightColumn",
  3575. false,
  3576. function(event, enable) {
  3577. if (enable) {
  3578. alert(
  3579. "开启后将屏蔽【相关产品】-【活动日历】-【运营活动】-【热门标签】"
  3580. );
  3581. }
  3582. }
  3583. ),
  3584. UISwitch(
  3585. "【屏蔽】底部推荐内容",
  3586. "csdn-hua-wei-cloud-blockRecommendedContentAtTheBottom",
  3587. false
  3588. ),
  3589. UISwitch(
  3590. "【屏蔽】底部更多推荐",
  3591. "csdn-hua-wei-cloud-shieldTheBottomForMoreRecommendations",
  3592. false
  3593. )
  3594. ]
  3595. }
  3596. ]
  3597. };
  3598. const SettingUIWenKu = {
  3599. id: "panel-wenku",
  3600. title: "资源",
  3601. isDefault() {
  3602. return CSDNRouter.isLink();
  3603. },
  3604. forms: [
  3605. {
  3606. text: "屏蔽",
  3607. type: "forms",
  3608. forms: [
  3609. UISwitch(
  3610. "【屏蔽】资源推荐",
  3611. "csdn-wenku-shieldResourceRecommend",
  3612. false
  3613. ),
  3614. UISwitch(
  3615. "【屏蔽】右侧用户信息",
  3616. "csdn-wenku-shieldRightUserInfo",
  3617. false
  3618. ),
  3619. UISwitch(
  3620. "【屏蔽】右侧悬浮工具栏",
  3621. "csdn-wenku-shieldRightToolBar",
  3622. false
  3623. )
  3624. ]
  3625. }
  3626. ]
  3627. };
  3628. const SettingUISo = {
  3629. id: "panel-so",
  3630. title: "搜索",
  3631. isDefault() {
  3632. return CSDNRouter.isSo();
  3633. },
  3634. forms: [
  3635. {
  3636. text: "C知道-功能",
  3637. type: "forms",
  3638. forms: [UISwitch("去除水印", "csdn-so-cknow-removeMaskCover", true)]
  3639. }
  3640. ]
  3641. };
  3642. const MSettingUICommon = {
  3643. id: "component-common",
  3644. title: "通用",
  3645. forms: [
  3646. {
  3647. text: "Toast配置",
  3648. type: "forms",
  3649. forms: [
  3650. UISelect(
  3651. "Toast位置",
  3652. "qmsg-config-position",
  3653. "bottom",
  3654. [
  3655. {
  3656. value: "topleft",
  3657. text: "左上角"
  3658. },
  3659. {
  3660. value: "top",
  3661. text: "顶部"
  3662. },
  3663. {
  3664. value: "topright",
  3665. text: "右上角"
  3666. },
  3667. {
  3668. value: "left",
  3669. text: "左边"
  3670. },
  3671. {
  3672. value: "center",
  3673. text: "中间"
  3674. },
  3675. {
  3676. value: "right",
  3677. text: "右边"
  3678. },
  3679. {
  3680. value: "bottomleft",
  3681. text: "左下角"
  3682. },
  3683. {
  3684. value: "bottom",
  3685. text: "底部"
  3686. },
  3687. {
  3688. value: "bottomright",
  3689. text: "右下角"
  3690. }
  3691. ],
  3692. (event, isSelectValue, isSelectText) => {
  3693. log.info("设置当前Qmsg弹出位置" + isSelectText);
  3694. },
  3695. "Toast显示在页面九宫格的位置"
  3696. ),
  3697. UISelect(
  3698. "最多显示的数量",
  3699. "qmsg-config-maxnums",
  3700. 3,
  3701. [
  3702. {
  3703. value: 1,
  3704. text: "1"
  3705. },
  3706. {
  3707. value: 2,
  3708. text: "2"
  3709. },
  3710. {
  3711. value: 3,
  3712. text: "3"
  3713. },
  3714. {
  3715. value: 4,
  3716. text: "4"
  3717. },
  3718. {
  3719. value: 5,
  3720. text: "5"
  3721. }
  3722. ],
  3723. void 0,
  3724. "限制Toast显示的数量"
  3725. ),
  3726. UISwitch(
  3727. "逆序弹出",
  3728. "qmsg-config-showreverse",
  3729. false,
  3730. void 0,
  3731. "修改Toast弹出的顺序"
  3732. )
  3733. ]
  3734. }
  3735. // {
  3736. // text: "Cookie配置",
  3737. // type: "forms",
  3738. // forms: [
  3739. // UISwitch(
  3740. // "启用",
  3741. // "httpx-use-cookie-enable",
  3742. // false,
  3743. // void 0,
  3744. // "启用后,将根据下面的配置进行添加cookie"
  3745. // ),
  3746. // UISwitch(
  3747. // "使用document.cookie",
  3748. // "httpx-use-document-cookie",
  3749. // false,
  3750. // void 0,
  3751. // "自动根据请求的域名来设置对应的cookie"
  3752. // ),
  3753. // UITextArea(
  3754. // "tieba.baidu.com",
  3755. // "httpx-cookie-tieba.baidu.com",
  3756. // "",
  3757. // void 0,
  3758. // void 0,
  3759. // "Cookie格式:xxx=xxxx;xxx=xxxx"
  3760. // ),
  3761. // ],
  3762. // },
  3763. ]
  3764. };
  3765. const MSettingUIBlog = {
  3766. id: "m-panel-blog",
  3767. title: "博客",
  3768. isDefault() {
  3769. return CSDNRouter.isBlog();
  3770. },
  3771. forms: [
  3772. {
  3773. type: "forms",
  3774. text: "",
  3775. forms: [
  3776. {
  3777. type: "deepMenu",
  3778. text: "文章",
  3779. forms: [
  3780. {
  3781. text: "",
  3782. type: "forms",
  3783. forms: [
  3784. {
  3785. type: "deepMenu",
  3786. text: "顶部工具栏",
  3787. forms: [
  3788. {
  3789. type: "forms",
  3790. text: "",
  3791. forms: [
  3792. UISwitch(
  3793. "启用",
  3794. "m-csdn-blog-shieldTopToolbar",
  3795. false,
  3796. void 0,
  3797. "关闭是屏蔽顶部工具栏"
  3798. )
  3799. ]
  3800. }
  3801. ]
  3802. },
  3803. {
  3804. text: "内容",
  3805. type: "deepMenu",
  3806. forms: [
  3807. {
  3808. text: "",
  3809. type: "forms",
  3810. forms: [
  3811. UISwitch(
  3812. "允许选中文字",
  3813. "m-csdn-blog-allowSelectText",
  3814. true,
  3815. void 0,
  3816. "设置user-select: text;"
  3817. ),
  3818. UISwitch(
  3819. "自动展开",
  3820. "m-csdn-blog-autoExpandContent",
  3821. true,
  3822. void 0,
  3823. "包括内容、代码块"
  3824. ),
  3825. UISwitch(
  3826. "不限制代码块的最大高度",
  3827. "m-csdn-blog-notLimitCodePreMaxHeight",
  3828. false,
  3829. void 0,
  3830. "让代码块的高度直接被撑开"
  3831. )
  3832. ]
  3833. }
  3834. ]
  3835. },
  3836. {
  3837. text: "评论",
  3838. type: "deepMenu",
  3839. forms: [
  3840. {
  3841. text: "",
  3842. type: "forms",
  3843. forms: [
  3844. UISwitch(
  3845. "启用",
  3846. "m-csdn-blog-comment-enable",
  3847. true,
  3848. void 0,
  3849. "关闭是屏蔽评论区"
  3850. ),
  3851. UISwitch(
  3852. "不限制评论区的最大高度",
  3853. "m-csdn-blog-notLimitCommentMaxHeight",
  3854. true,
  3855. void 0,
  3856. "让评论区高度直接被撑开"
  3857. )
  3858. ]
  3859. }
  3860. ]
  3861. },
  3862. {
  3863. text: "底部文章",
  3864. type: "deepMenu",
  3865. forms: [
  3866. {
  3867. text: "",
  3868. type: "forms",
  3869. forms: [
  3870. UISwitch(
  3871. "启用",
  3872. "m-csdn-blog-bottomArticleEnable",
  3873. true,
  3874. void 0,
  3875. "关闭是屏蔽底部文章"
  3876. ),
  3877. UISwitch(
  3878. "移除资源下载",
  3879. "m-csdn-blog-removeResourceArticle",
  3880. false,
  3881. void 0,
  3882. "download.csdn.net<br>www.iteye.com<br>edu.csdn.net"
  3883. ),
  3884. UISwitch(
  3885. "重构",
  3886. "m-csdn-blog-refactoringRecommendation",
  3887. true,
  3888. void 0,
  3889. "文章的样式统一"
  3890. ),
  3891. UISwitch(
  3892. "新标签页打开",
  3893. "m-csdn-blog-openNewTab",
  3894. true,
  3895. void 0,
  3896. "新标签页打开文章"
  3897. )
  3898. ]
  3899. }
  3900. ]
  3901. },
  3902. {
  3903. type: "deepMenu",
  3904. text: "底部工具栏",
  3905. forms: [
  3906. {
  3907. type: "forms",
  3908. text: "",
  3909. forms: [
  3910. UISwitch(
  3911. "启用",
  3912. "m-csdn-blog-bottom-toolbar-enable",
  3913. false,
  3914. void 0,
  3915. "关闭是屏蔽底部工具栏"
  3916. ),
  3917. UISwitch(
  3918. "常驻底部",
  3919. "m-csdn-blog-bottom-toolbar-always-bottom",
  3920. false,
  3921. void 0,
  3922. "开启后底部工具栏不随下滑滚动而隐藏"
  3923. ),
  3924. UISwitch(
  3925. "优化收藏按钮",
  3926. "m-csdn-blog-bottom-toolbar-optimizationCollectButton",
  3927. false,
  3928. void 0,
  3929. "可以自行选择收藏夹"
  3930. )
  3931. ]
  3932. }
  3933. ]
  3934. }
  3935. ]
  3936. }
  3937. ]
  3938. }
  3939. ]
  3940. },
  3941. {
  3942. type: "forms",
  3943. text: "",
  3944. forms: [
  3945. {
  3946. text: "功能",
  3947. type: "deepMenu",
  3948. forms: [
  3949. {
  3950. text: "",
  3951. type: "forms",
  3952. forms: [
  3953. UISwitch(
  3954. "【屏蔽】广告",
  3955. "m-csdn-blog-removeAds",
  3956. true,
  3957. void 0,
  3958. "包括:登录(不可用)弹窗、打开APP、ios版本提示等"
  3959. ),
  3960. UISwitch(
  3961. "允许复制",
  3962. "m-csdn-blog-unBlockCopy",
  3963. true,
  3964. void 0,
  3965. "允许点击复制按钮进行复制"
  3966. )
  3967. ]
  3968. }
  3969. ]
  3970. }
  3971. ]
  3972. }
  3973. ]
  3974. };
  3975. const MSettingUILink = {
  3976. id: "m-panel-link",
  3977. title: "链接",
  3978. isDefault() {
  3979. return CSDNRouter.isLink();
  3980. },
  3981. forms: [
  3982. {
  3983. text: "功能",
  3984. type: "forms",
  3985. forms: [
  3986. UISwitch(
  3987. "重定向链接",
  3988. "m-csdn-link-jumpRedirect",
  3989. true,
  3990. void 0,
  3991. "自动跳转至被拦截的Url链接"
  3992. )
  3993. ]
  3994. }
  3995. ]
  3996. };
  3997. const MSettingUIHuaWeiCloud = {
  3998. id: "m-panel-hua-wei-cloud",
  3999. title: "华为云开发者联盟",
  4000. isDefault() {
  4001. return CSDNRouter.isHuaWeiCloudBlog();
  4002. },
  4003. forms: [
  4004. {
  4005. text: "功能",
  4006. type: "forms",
  4007. forms: [
  4008. UISwitch(
  4009. "自动展开全文",
  4010. "m-csdn-hua-wei-cloud-autoExpandContent",
  4011. true
  4012. )
  4013. ]
  4014. },
  4015. {
  4016. text: "屏蔽",
  4017. type: "forms",
  4018. forms: [
  4019. UISwitch(
  4020. "【屏蔽】底部加入社区",
  4021. "m-csdn-hua-wei-cloud-blockBottomJoinTheCommunity",
  4022. true
  4023. )
  4024. ]
  4025. }
  4026. ]
  4027. };
  4028. const MSettingUIWenKu = {
  4029. id: "m-panel-wenku",
  4030. title: "文库",
  4031. isDefault() {
  4032. return CSDNRouter.isWenKu();
  4033. },
  4034. forms: [
  4035. {
  4036. text: "屏蔽",
  4037. type: "forms",
  4038. forms: [
  4039. UISwitch(
  4040. "【屏蔽】底部工具栏",
  4041. "m-csdn-wenku-shieldBottomToolbar",
  4042. false
  4043. )
  4044. ]
  4045. }
  4046. ]
  4047. };
  4048. const MSettingUISo = {
  4049. id: "panel-so",
  4050. title: "搜索",
  4051. isDefault() {
  4052. return CSDNRouter.isSo();
  4053. },
  4054. forms: [
  4055. {
  4056. text: "C知道-功能",
  4057. type: "forms",
  4058. forms: [UISwitch("去除水印", "m-csdn-so-cknow-removeMaskCover", true)]
  4059. }
  4060. ]
  4061. };
  4062. const MSettingUIDownload = {
  4063. id: "m-panel-download",
  4064. title: "资源",
  4065. isDefault() {
  4066. return CSDNRouter.isDownload();
  4067. },
  4068. forms: [
  4069. {
  4070. text: "功能",
  4071. type: "forms",
  4072. forms: [
  4073. UISwitch(
  4074. "自动展开资源介绍",
  4075. "m-csdn-download-automaticallyExpandResourceIntroduction",
  4076. true,
  4077. void 0,
  4078. "屏蔽资源介绍【展开全部】按钮并展开资源介绍"
  4079. )
  4080. ]
  4081. },
  4082. {
  4083. text: "屏蔽",
  4084. type: "forms",
  4085. forms: [
  4086. UISwitch(
  4087. "【屏蔽】广告",
  4088. "m-csdn-download-removeAds",
  4089. true,
  4090. void 0,
  4091. "包括:登录(不可用)弹窗、会员降价等"
  4092. )
  4093. ]
  4094. }
  4095. ]
  4096. };
  4097. PanelMenu.deleteMenuOption(0);
  4098. PanelMenu.addMenuOption([
  4099. {
  4100. key: "show_pops_panel_setting",
  4101. text: "⚙ PC端设置",
  4102. autoReload: false,
  4103. isStoreValue: false,
  4104. showText(text) {
  4105. return text;
  4106. },
  4107. callback: () => {
  4108. Panel.showPanel(PanelContent.getConfig(0));
  4109. }
  4110. },
  4111. {
  4112. key: "m_show_pops_panel_setting",
  4113. text: "⚙ 移动端端设置",
  4114. autoReload: false,
  4115. isStoreValue: false,
  4116. showText(text) {
  4117. return text;
  4118. },
  4119. callback: () => {
  4120. Panel.showPanel(PanelContent.getConfig(1));
  4121. }
  4122. },
  4123. {
  4124. key: "gotoCSDNCKnow",
  4125. text: "⚙ 前往C知道",
  4126. isStoreValue: false,
  4127. autoReload: false,
  4128. showText(text) {
  4129. return text;
  4130. },
  4131. callback() {
  4132. window.open("https://so.csdn.net/chat", "_blank");
  4133. }
  4134. }
  4135. ]);
  4136. PanelContent.addContentConfig([
  4137. SettingUICommon,
  4138. SettingUIBlog,
  4139. SettingUILink,
  4140. SettingUIHuaWeiCloud,
  4141. SettingUIWenKu,
  4142. SettingUISo
  4143. ]);
  4144. PanelContent.addContentConfig([
  4145. MSettingUICommon,
  4146. MSettingUIBlog,
  4147. MSettingUILink,
  4148. MSettingUIHuaWeiCloud,
  4149. MSettingUIWenKu,
  4150. MSettingUISo,
  4151. MSettingUIDownload
  4152. ]);
  4153. Panel.init();
  4154. let isMobile = utils.isPhone();
  4155. let CHANGE_ENV_SET_KEY = "change_env_set";
  4156. let chooseMode = _GM_getValue(CHANGE_ENV_SET_KEY);
  4157. GM_Menu.add({
  4158. key: CHANGE_ENV_SET_KEY,
  4159. text: `⚙ 自动: ${isMobile ? "移动端" : "PC端"}`,
  4160. autoReload: false,
  4161. isStoreValue: false,
  4162. showText(text) {
  4163. if (chooseMode == null) {
  4164. return text;
  4165. }
  4166. return text + ` 手动: ${chooseMode == 1 ? "移动端" : chooseMode == 2 ? "PC端" : "未知"}`;
  4167. },
  4168. callback: () => {
  4169. let allowValue = [0, 1, 2];
  4170. let chooseText = window.prompt(
  4171. "请输入当前脚本环境判定\n\n自动判断: 0\n移动端: 1\nPC端: 2",
  4172. "0"
  4173. );
  4174. if (!chooseText) {
  4175. return;
  4176. }
  4177. let chooseMode2 = parseInt(chooseText);
  4178. if (isNaN(chooseMode2)) {
  4179. Qmsg.error("输入的不是规范的数字");
  4180. return;
  4181. }
  4182. if (!allowValue.includes(chooseMode2)) {
  4183. Qmsg.error("输入的值必须是0或1或2");
  4184. return;
  4185. }
  4186. if (chooseMode2 == 0) {
  4187. _GM_deleteValue(CHANGE_ENV_SET_KEY);
  4188. } else {
  4189. _GM_setValue(CHANGE_ENV_SET_KEY, chooseMode2);
  4190. }
  4191. }
  4192. });
  4193. if (chooseMode != null) {
  4194. log.info(`手动判定为${chooseMode === 1 ? "移动端" : "PC端"}`);
  4195. if (chooseMode == 1) {
  4196. M_CSDN.init();
  4197. } else if (chooseMode == 2) {
  4198. CSDN.init();
  4199. } else {
  4200. Qmsg.error("意外,手动判定的值不在范围内");
  4201. _GM_deleteValue(CHANGE_ENV_SET_KEY);
  4202. }
  4203. } else {
  4204. if (isMobile) {
  4205. log.info("自动判定为移动端");
  4206. M_CSDN.init();
  4207. } else {
  4208. log.info("自动判定为PC端");
  4209. CSDN.init();
  4210. }
  4211. }
  4212.  
  4213. })(Qmsg, DOMUtils, Utils, pops);

QingJ © 2025

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