CSDN优化

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

当前为 2024-11-08 提交的版本,查看 最新版本

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