CSDN优化

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

当前为 2024-12-17 提交的版本,查看 最新版本

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