CSDN优化

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

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

QingJ © 2025

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