CSDN优化

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

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

QingJ © 2025

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