CSDN优化

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

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

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