简书优化

支持手机端和PC端,屏蔽广告,优化浏览体验,自动跳转拦截的URL

目前为 2024-06-23 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name 简书优化
  3. // @namespace https://github.com/WhiteSevs/TamperMonkeyScript
  4. // @version 2024.6.23
  5. // @author WhiteSevs
  6. // @description 支持手机端和PC端,屏蔽广告,优化浏览体验,自动跳转拦截的URL
  7. // @license GPL-3.0-only
  8. // @icon https://www.jianshu.com/favicon.ico
  9. // @supportURL https://github.com/WhiteSevs/TamperMonkeyScript/issues
  10. // @match *://*.jianshu.com/*
  11. // @match *://*.jianshu.io/*
  12. // @require https://update.gf.qytechs.cn/scripts/494167/1376186/CoverUMD.js
  13. // @require https://update.gf.qytechs.cn/scripts/456485/1398647/pops.js
  14. // @require https://fastly.jsdelivr.net/npm/qmsg@1.1.2/dist/index.umd.js
  15. // @require https://fastly.jsdelivr.net/npm/@whitesev/utils@1.5.8/dist/index.umd.js
  16. // @require https://fastly.jsdelivr.net/npm/@whitesev/domutils@1.1.2/dist/index.umd.js
  17. // @grant GM_addStyle
  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) {
  31. 'use strict';
  32.  
  33. var _a;
  34. var _GM_addStyle = /* @__PURE__ */ (() => typeof GM_addStyle != "undefined" ? GM_addStyle : void 0)();
  35. var _GM_deleteValue = /* @__PURE__ */ (() => typeof GM_deleteValue != "undefined" ? GM_deleteValue : void 0)();
  36. var _GM_getValue = /* @__PURE__ */ (() => typeof GM_getValue != "undefined" ? GM_getValue : void 0)();
  37. var _GM_info = /* @__PURE__ */ (() => typeof GM_info != "undefined" ? GM_info : void 0)();
  38. var _GM_registerMenuCommand = /* @__PURE__ */ (() => typeof GM_registerMenuCommand != "undefined" ? GM_registerMenuCommand : void 0)();
  39. var _GM_setValue = /* @__PURE__ */ (() => typeof GM_setValue != "undefined" ? GM_setValue : void 0)();
  40. var _GM_unregisterMenuCommand = /* @__PURE__ */ (() => typeof GM_unregisterMenuCommand != "undefined" ? GM_unregisterMenuCommand : void 0)();
  41. var _GM_xmlhttpRequest = /* @__PURE__ */ (() => typeof GM_xmlhttpRequest != "undefined" ? GM_xmlhttpRequest : void 0)();
  42. var _unsafeWindow = /* @__PURE__ */ (() => typeof unsafeWindow != "undefined" ? unsafeWindow : void 0)();
  43. var _monkeyWindow = /* @__PURE__ */ (() => window)();
  44. const _SCRIPT_NAME_ = "简书优化";
  45. const utils = Utils.noConflict();
  46. DOMUtils.noConflict();
  47. const pops = _monkeyWindow.pops || _unsafeWindow.pops;
  48. const log = new utils.Log(
  49. _GM_info,
  50. _unsafeWindow.console || _monkeyWindow.console
  51. );
  52. const SCRIPT_NAME = ((_a = _GM_info == null ? void 0 : _GM_info.script) == null ? void 0 : _a.name) || _SCRIPT_NAME_;
  53. const DEBUG = false;
  54. log.config({
  55. debug: DEBUG,
  56. logMaxCount: 1e3,
  57. autoClearConsole: true,
  58. tag: true
  59. });
  60. Qmsg.config(
  61. Object.defineProperties(
  62. {
  63. html: true,
  64. autoClose: true,
  65. showClose: false
  66. },
  67. {
  68. position: {
  69. get() {
  70. return PopsPanel.getValue("qmsg-config-position", "bottom");
  71. }
  72. },
  73. maxNums: {
  74. get() {
  75. return PopsPanel.getValue("qmsg-config-maxnums", 5);
  76. }
  77. },
  78. showReverse: {
  79. get() {
  80. return PopsPanel.getValue("qmsg-config-showreverse", true);
  81. }
  82. },
  83. zIndex: {
  84. get() {
  85. let maxZIndex = Utils.getMaxZIndex(10);
  86. let popsMaxZIndex = pops.config.Utils.getPopsMaxZIndex(10).zIndex;
  87. return Utils.getMaxValue(maxZIndex, popsMaxZIndex);
  88. }
  89. }
  90. }
  91. )
  92. );
  93. const GM_Menu = new utils.GM_Menu({
  94. GM_getValue: _GM_getValue,
  95. GM_setValue: _GM_setValue,
  96. GM_registerMenuCommand: _GM_registerMenuCommand,
  97. GM_unregisterMenuCommand: _GM_unregisterMenuCommand
  98. });
  99. const httpx = new utils.Httpx(_GM_xmlhttpRequest);
  100. httpx.interceptors.response.use(void 0, (data) => {
  101. log.error(["拦截器-请求错误", data]);
  102. if (data.type === "onabort") {
  103. Qmsg.warning("请求取消");
  104. } else if (data.type === "onerror") {
  105. Qmsg.error("请求异常");
  106. } else if (data.type === "ontimeout") {
  107. Qmsg.error("请求超时");
  108. } else {
  109. Qmsg.error("其它错误");
  110. }
  111. return data;
  112. });
  113. httpx.config({
  114. logDetails: DEBUG
  115. });
  116. ({
  117. Object: {
  118. defineProperty: _unsafeWindow.Object.defineProperty
  119. },
  120. Function: {
  121. apply: _unsafeWindow.Function.prototype.apply,
  122. call: _unsafeWindow.Function.prototype.call
  123. },
  124. Element: {
  125. appendChild: _unsafeWindow.Element.prototype.appendChild
  126. },
  127. setTimeout: _unsafeWindow.setTimeout
  128. });
  129. const KEY = "GM_Panel";
  130. const ATTRIBUTE_KEY = "data-key";
  131. const ATTRIBUTE_DEFAULT_VALUE = "data-default-value";
  132. const UISwitch = function(text, key, defaultValue, clickCallBack, description) {
  133. let result = {
  134. text,
  135. type: "switch",
  136. description,
  137. attributes: {},
  138. getValue() {
  139. return Boolean(PopsPanel.getValue(key, defaultValue));
  140. },
  141. callback(event, value) {
  142. log.success(`${value ? "开启" : "关闭"} ${text}`);
  143. PopsPanel.setValue(key, Boolean(value));
  144. },
  145. afterAddToUListCallBack: void 0
  146. };
  147. if (result.attributes) {
  148. result.attributes[ATTRIBUTE_KEY] = key;
  149. result.attributes[ATTRIBUTE_DEFAULT_VALUE] = Boolean(defaultValue);
  150. }
  151. return result;
  152. };
  153. const SettingUIPC = {
  154. id: "jianshu-panel-config-pc",
  155. title: "桌面端",
  156. forms: [
  157. {
  158. text: "屏蔽",
  159. type: "forms",
  160. forms: [
  161. UISwitch(
  162. "【屏蔽】底部推荐阅读",
  163. "JianShuShieldRecommendedReading",
  164. false
  165. ),
  166. UISwitch("【屏蔽】评论区", "JianShuShieldUserComments", false),
  167. UISwitch("【屏蔽】相关文章", "JianShuShieldRelatedArticles", false),
  168. UISwitch(
  169. "【屏蔽】客户端弹窗",
  170. "jianshu-shieldClientDialog",
  171. true,
  172. void 0,
  173. "弹出的【扫码安装简书客户端 畅享全文阅读体验】"
  174. ),
  175. UISwitch("【屏蔽】顶部导航栏", "jianshu-shieldTopNav", false),
  176. UISwitch(
  177. "【屏蔽】底部工具栏",
  178. "jianshu-shieldBottomToolbar",
  179. false,
  180. void 0,
  181. "屏蔽掉底部悬浮的评论输入框、评论、点赞..."
  182. )
  183. ]
  184. },
  185. {
  186. text: "功能",
  187. type: "forms",
  188. forms: [
  189. UISwitch("全文居中", "JianShuArticleCenter", true),
  190. UISwitch("自动展开全文", "JianShuAutoExpandFullText", true),
  191. UISwitch(
  192. "重定向链接",
  193. "JianShuAutoJumpRedirect_PC",
  194. true,
  195. void 0,
  196. "自动跳转简书拦截的Url链接"
  197. )
  198. ]
  199. },
  200. {
  201. text: "劫持/拦截",
  202. type: "forms",
  203. forms: [
  204. UISwitch(
  205. "拦截-剪贴板",
  206. "JianShuRemoveClipboardHijacking",
  207. true,
  208. void 0,
  209. "去除禁止复制"
  210. )
  211. ]
  212. }
  213. ]
  214. };
  215. const SettingUIMobile = {
  216. id: "jianshu-panel-config-mobile",
  217. title: "移动端",
  218. forms: [
  219. {
  220. text: "屏蔽",
  221. type: "forms",
  222. forms: [
  223. UISwitch(
  224. "【屏蔽】底部推荐阅读",
  225. "JianShuremoveFooterRecommendRead",
  226. false
  227. ),
  228. UISwitch("【屏蔽】评论区", "JianShuShieldUserCommentsMobile", false)
  229. ]
  230. },
  231. {
  232. text: "功能",
  233. type: "forms",
  234. forms: [
  235. UISwitch("自动展开全文", "JianShuAutoExpandFullText_Mobile", true),
  236. UISwitch(
  237. "重定向链接",
  238. "JianShuAutoJumpRedirect_Mobile",
  239. true,
  240. void 0,
  241. "自动跳转简书拦截的Url链接"
  242. )
  243. ]
  244. },
  245. {
  246. text: "劫持/拦截",
  247. type: "forms",
  248. forms: [
  249. UISwitch(
  250. "拦截-剪贴板",
  251. "JianShuRemoveClipboardHijacking_Mobile",
  252. true,
  253. void 0,
  254. "去除禁止复制"
  255. ),
  256. UISwitch(
  257. "劫持-唤醒/跳转App",
  258. "JianShuHijackSchemeScriptLabel_Mobile",
  259. true,
  260. void 0,
  261. "去除简书唤醒调用App"
  262. )
  263. ]
  264. }
  265. ]
  266. };
  267. const UISelect = function(text, key, defaultValue, data, callback, description) {
  268. let selectData = [];
  269. if (typeof data === "function") {
  270. selectData = data();
  271. } else {
  272. selectData = data;
  273. }
  274. let result = {
  275. text,
  276. type: "select",
  277. description,
  278. attributes: {},
  279. getValue() {
  280. return PopsPanel.getValue(key, defaultValue);
  281. },
  282. callback(event, isSelectedValue, isSelectedText) {
  283. PopsPanel.setValue(key, isSelectedValue);
  284. if (typeof callback === "function") {
  285. callback(event, isSelectedValue, isSelectedText);
  286. }
  287. },
  288. data: selectData
  289. };
  290. if (result.attributes) {
  291. result.attributes[ATTRIBUTE_KEY] = key;
  292. result.attributes[ATTRIBUTE_DEFAULT_VALUE] = defaultValue;
  293. }
  294. return result;
  295. };
  296. const SettingUICommon = {
  297. id: "jianshu-panel-common",
  298. title: "通用",
  299. forms: [
  300. {
  301. text: "Toast配置",
  302. type: "forms",
  303. forms: [
  304. UISelect(
  305. "Toast位置",
  306. "qmsg-config-position",
  307. "bottom",
  308. [
  309. {
  310. value: "topleft",
  311. text: "左上角"
  312. },
  313. {
  314. value: "top",
  315. text: "顶部"
  316. },
  317. {
  318. value: "topright",
  319. text: "右上角"
  320. },
  321. {
  322. value: "left",
  323. text: "左边"
  324. },
  325. {
  326. value: "center",
  327. text: "中间"
  328. },
  329. {
  330. value: "right",
  331. text: "右边"
  332. },
  333. {
  334. value: "bottomleft",
  335. text: "左下角"
  336. },
  337. {
  338. value: "bottom",
  339. text: "底部"
  340. },
  341. {
  342. value: "bottomright",
  343. text: "右下角"
  344. }
  345. ],
  346. (event, isSelectValue, isSelectText) => {
  347. log.info("设置当前Qmsg弹出位置" + isSelectText);
  348. },
  349. "Toast显示在页面九宫格的位置"
  350. ),
  351. UISelect(
  352. "最多显示的数量",
  353. "qmsg-config-maxnums",
  354. 3,
  355. [
  356. {
  357. value: 1,
  358. text: "1"
  359. },
  360. {
  361. value: 2,
  362. text: "2"
  363. },
  364. {
  365. value: 3,
  366. text: "3"
  367. },
  368. {
  369. value: 4,
  370. text: "4"
  371. },
  372. {
  373. value: 5,
  374. text: "5"
  375. }
  376. ],
  377. void 0,
  378. "限制Toast显示的数量"
  379. ),
  380. UISwitch(
  381. "逆序弹出",
  382. "qmsg-config-showreverse",
  383. false,
  384. void 0,
  385. "修改Toast弹出的顺序"
  386. )
  387. ]
  388. }
  389. ]
  390. };
  391. const __PopsPanel__ = {
  392. data: null,
  393. oneSuccessExecMenu: null,
  394. onceExec: null,
  395. listenData: null
  396. };
  397. const PopsPanel = {
  398. /** 数据 */
  399. $data: {
  400. /**
  401. * 菜单项的默认值
  402. */
  403. get data() {
  404. if (__PopsPanel__.data == null) {
  405. __PopsPanel__.data = new utils.Dictionary();
  406. }
  407. return __PopsPanel__.data;
  408. },
  409. /**
  410. * 成功只执行了一次的项
  411. */
  412. get oneSuccessExecMenu() {
  413. if (__PopsPanel__.oneSuccessExecMenu == null) {
  414. __PopsPanel__.oneSuccessExecMenu = new utils.Dictionary();
  415. }
  416. return __PopsPanel__.oneSuccessExecMenu;
  417. },
  418. /**
  419. * 成功只执行了一次的项
  420. */
  421. get onceExec() {
  422. if (__PopsPanel__.onceExec == null) {
  423. __PopsPanel__.onceExec = new utils.Dictionary();
  424. }
  425. return __PopsPanel__.onceExec;
  426. },
  427. /** 脚本名,一般用在设置的标题上 */
  428. get scriptName() {
  429. return SCRIPT_NAME;
  430. },
  431. /** 菜单项的总值在本地数据配置的键名 */
  432. key: KEY,
  433. /** 菜单项在attributes上配置的菜单键 */
  434. attributeKeyName: ATTRIBUTE_KEY,
  435. /** 菜单项在attributes上配置的菜单默认值 */
  436. attributeDefaultValueName: ATTRIBUTE_DEFAULT_VALUE
  437. },
  438. /** 监听器 */
  439. $listener: {
  440. /**
  441. * 值改变的监听器
  442. */
  443. get listenData() {
  444. if (__PopsPanel__.listenData == null) {
  445. __PopsPanel__.listenData = new utils.Dictionary();
  446. }
  447. return __PopsPanel__.listenData;
  448. }
  449. },
  450. init() {
  451. this.initPanelDefaultValue();
  452. this.initExtensionsMenu();
  453. },
  454. initExtensionsMenu() {
  455. if (_unsafeWindow.top !== _unsafeWindow.self) {
  456. return;
  457. }
  458. GM_Menu.add([
  459. {
  460. key: "show_pops_panel_setting",
  461. text: "⚙ 设置",
  462. autoReload: false,
  463. isStoreValue: false,
  464. showText(text) {
  465. return text;
  466. },
  467. callback: () => {
  468. this.showPanel();
  469. }
  470. }
  471. ]);
  472. },
  473. /** 初始化本地设置默认的值 */
  474. initPanelDefaultValue() {
  475. let that = this;
  476. function initDefaultValue(config) {
  477. if (!config["attributes"]) {
  478. return;
  479. }
  480. let key = config.attributes[ATTRIBUTE_KEY];
  481. let defaultValue = config["attributes"][ATTRIBUTE_DEFAULT_VALUE];
  482. if (key == null) {
  483. log.warn(["请先配置键", config]);
  484. return;
  485. }
  486. if (that.$data.data.has(key)) {
  487. log.warn("请检查该key(已存在): " + key);
  488. }
  489. that.$data.data.set(key, defaultValue);
  490. }
  491. let contentConfigList = this.getPanelContentConfig();
  492. for (let index = 0; index < contentConfigList.length; index++) {
  493. let leftContentConfigItem = contentConfigList[index];
  494. if (!leftContentConfigItem.forms) {
  495. continue;
  496. }
  497. let rightContentConfigList = leftContentConfigItem.forms;
  498. for (let formItemIndex = 0; formItemIndex < rightContentConfigList.length; formItemIndex++) {
  499. let rightContentConfigItem = rightContentConfigList[formItemIndex];
  500. if (rightContentConfigItem.forms) {
  501. let childFormConfigList = rightContentConfigItem.forms;
  502. for (let formChildConfigIndex = 0; formChildConfigIndex < childFormConfigList.length; formChildConfigIndex++) {
  503. initDefaultValue(childFormConfigList[formChildConfigIndex]);
  504. }
  505. } else {
  506. initDefaultValue(rightContentConfigItem);
  507. }
  508. }
  509. }
  510. },
  511. /**
  512. * 设置值
  513. * @param key 键
  514. * @param value 值
  515. */
  516. setValue(key, value) {
  517. let locaData = _GM_getValue(KEY, {});
  518. let oldValue = locaData[key];
  519. locaData[key] = value;
  520. _GM_setValue(KEY, locaData);
  521. if (this.$listener.listenData.has(key)) {
  522. this.$listener.listenData.get(key).callback(key, oldValue, value);
  523. }
  524. },
  525. /**
  526. * 获取值
  527. * @param key 键
  528. * @param defaultValue 默认值
  529. */
  530. getValue(key, defaultValue) {
  531. let locaData = _GM_getValue(KEY, {});
  532. let localValue = locaData[key];
  533. if (localValue == null) {
  534. if (this.$data.data.has(key)) {
  535. return this.$data.data.get(key);
  536. }
  537. return defaultValue;
  538. }
  539. return localValue;
  540. },
  541. /**
  542. * 删除值
  543. * @param key 键
  544. */
  545. deleteValue(key) {
  546. let locaData = _GM_getValue(KEY, {});
  547. let oldValue = locaData[key];
  548. Reflect.deleteProperty(locaData, key);
  549. _GM_setValue(KEY, locaData);
  550. if (this.$listener.listenData.has(key)) {
  551. this.$listener.listenData.get(key).callback(key, oldValue, void 0);
  552. }
  553. },
  554. /**
  555. * 监听调用setValue、deleteValue
  556. * @param key 需要监听的键
  557. * @param callback
  558. */
  559. addValueChangeListener(key, callback) {
  560. let listenerId = Math.random();
  561. this.$listener.listenData.set(key, {
  562. id: listenerId,
  563. key,
  564. callback
  565. });
  566. return listenerId;
  567. },
  568. /**
  569. * 移除监听
  570. * @param listenerId 监听的id
  571. */
  572. removeValueChangeListener(listenerId) {
  573. let deleteKey = null;
  574. for (const [key, value] of this.$listener.listenData.entries()) {
  575. if (value.id === listenerId) {
  576. deleteKey = key;
  577. break;
  578. }
  579. }
  580. if (typeof deleteKey === "string") {
  581. this.$listener.listenData.delete(deleteKey);
  582. } else {
  583. console.warn("没有找到对应的监听器");
  584. }
  585. },
  586. /**
  587. * 判断该键是否存在
  588. * @param key 键
  589. */
  590. hasKey(key) {
  591. let locaData = _GM_getValue(KEY, {});
  592. return key in locaData;
  593. },
  594. /**
  595. * 自动判断菜单是否启用,然后执行回调
  596. * @param key
  597. * @param callback 回调
  598. */
  599. execMenu(key, callback) {
  600. if (typeof key !== "string") {
  601. throw new TypeError("key 必须是字符串");
  602. }
  603. if (!this.$data.data.has(key)) {
  604. log.warn(`${key} 键不存在`);
  605. return;
  606. }
  607. let value = PopsPanel.getValue(key);
  608. if (value) {
  609. callback(value);
  610. }
  611. },
  612. /**
  613. * 自动判断菜单是否启用,然后执行回调,只会执行一次
  614. * @param key
  615. * @param callback 回调
  616. */
  617. execMenuOnce(key, callback) {
  618. if (typeof key !== "string") {
  619. throw new TypeError("key 必须是字符串");
  620. }
  621. if (!this.$data.data.has(key)) {
  622. log.warn(`${key} 键不存在`);
  623. return;
  624. }
  625. let value = PopsPanel.getValue(key);
  626. if (value) {
  627. if (this.$data.oneSuccessExecMenu.has(key)) {
  628. return;
  629. }
  630. callback(value);
  631. this.$data.oneSuccessExecMenu.set(key, 1);
  632. }
  633. },
  634. /**
  635. * 根据key执行一次
  636. * @param key
  637. */
  638. onceExec(key, callback) {
  639. if (typeof key !== "string") {
  640. throw new TypeError("key 必须是字符串");
  641. }
  642. if (this.$data.onceExec.has(key)) {
  643. return;
  644. }
  645. callback();
  646. this.$data.onceExec.set(key, 1);
  647. },
  648. /**
  649. * 显示设置面板
  650. */
  651. showPanel() {
  652. pops.panel({
  653. title: {
  654. text: `${SCRIPT_NAME}-设置`,
  655. position: "center",
  656. html: false,
  657. style: ""
  658. },
  659. content: this.getPanelContentConfig(),
  660. mask: {
  661. enable: true,
  662. clickEvent: {
  663. toClose: true,
  664. toHide: false
  665. }
  666. },
  667. isMobile: this.isMobile(),
  668. width: this.getWidth(),
  669. height: this.getHeight(),
  670. drag: true,
  671. only: true
  672. });
  673. },
  674. isMobile() {
  675. return window.outerWidth < 550;
  676. },
  677. /**
  678. * 获取设置面板的宽度
  679. */
  680. getWidth() {
  681. if (window.outerWidth < 550) {
  682. return "92dvw";
  683. } else {
  684. return "550px";
  685. }
  686. },
  687. /**
  688. * 获取设置面板的高度
  689. */
  690. getHeight() {
  691. if (window.outerHeight > 450) {
  692. return "80dvh";
  693. } else {
  694. return "450px";
  695. }
  696. },
  697. /**
  698. * 获取配置内容
  699. */
  700. getPanelContentConfig() {
  701. let configList = [
  702. SettingUICommon,
  703. SettingUIPC,
  704. SettingUIMobile
  705. ];
  706. return configList;
  707. }
  708. };
  709. const ShieldCSS = `.download-app-guidance,\r
  710. .call-app-btn,\r
  711. .collapse-tips,\r
  712. .note-graceful-button,\r
  713. .app-open,\r
  714. .header-wrap,\r
  715. .recommend-wrap.recommend-ad,\r
  716. .call-app-Ad-bottom,\r
  717. #recommended-notes p.top-title span.more,\r
  718. #homepage .modal,\r
  719. button.index_call-app-btn,\r
  720. span.note__flow__download,\r
  721. .download-guide,\r
  722. #footer,\r
  723. .comment-open-app-btn-wrap,\r
  724. .nav.navbar-nav + div,\r
  725. .self-flow-ad,\r
  726. #free-reward-panel,\r
  727. div[id*='AdFive'],\r
  728. #index-aside-download-qrbox,\r
  729. .baidu-app-download-2eIkf_1,\r
  730. /* 底部的"小礼物走一走,来简书关注我"、赞赏支持和更多精彩内容,就在简书APP */\r
  731. div[role="main"] > div > section:first-child > div:nth-last-child(2),\r
  732. /* 它的内部是script标签,可能影响部分评论之间的高度问题 */\r
  733. div.adad_container ,\r
  734. /* 顶部导航栏的【下载App】 */\r
  735. #__next nav a[href*="navbar-app"] {\r
  736. display: none !important;\r
  737. }\r
  738. body.reader-day-mode.normal-size {\r
  739. overflow: auto !important;\r
  740. }\r
  741. .collapse-free-content {\r
  742. height: auto !important;\r
  743. }\r
  744. .copyright {\r
  745. color: #000 !important;\r
  746. }\r
  747. #note-show .content .show-content-free .collapse-free-content:after {\r
  748. background-image: none !important;\r
  749. }\r
  750. footer > div > div {\r
  751. justify-content: center;\r
  752. }\r
  753. /* 修复底部最后编辑于:。。。在某些套壳浏览器上的错位问题 */\r
  754. #note-show .content .show-content-free .note-meta-time {\r
  755. margin-top: 0px !important;\r
  756. }\r
  757. `;
  758. const JianshuRouter = {
  759. /**
  760. * 简书拦截跳转的网址
  761. */
  762. isGoWild() {
  763. return window.location.pathname === "/go-wild";
  764. }
  765. };
  766. const waitForElementToRemove = function(selectorText = "") {
  767. utils.waitNodeList(selectorText).then((nodeList) => {
  768. nodeList.forEach((item) => item.remove());
  769. });
  770. };
  771. const Jianshu = {
  772. init() {
  773. this.addCSS();
  774. PopsPanel.execMenu("JianShuAutoJumpRedirect_PC", () => {
  775. this.jumpRedirect();
  776. });
  777. PopsPanel.execMenu("JianShuRemoveClipboardHijacking", () => {
  778. this.removeClipboardHijacking();
  779. });
  780. PopsPanel.execMenu("JianShuAutoExpandFullText", () => {
  781. this.autoExpandFullText();
  782. });
  783. PopsPanel.execMenu("JianShuArticleCenter", () => {
  784. this.articleCenter();
  785. });
  786. PopsPanel.execMenu("JianShuShieldRelatedArticles", () => {
  787. this.shieldRelatedArticles();
  788. });
  789. PopsPanel.execMenu("jianshu-shieldClientDialog", () => {
  790. this.shieldClientDialog();
  791. });
  792. PopsPanel.execMenu("JianShuShieldUserComments", () => {
  793. this.shieldUserComments();
  794. });
  795. PopsPanel.execMenu("JianShuShieldRecommendedReading", () => {
  796. this.shieldRecommendedReading();
  797. });
  798. PopsPanel.execMenu("jianshu-shieldTopNav", () => {
  799. this.shieldTopNav();
  800. });
  801. PopsPanel.execMenu("jianshu-shieldBottomToolbar", () => {
  802. this.shieldBottomToolbar();
  803. });
  804. },
  805. /**
  806. * 添加屏蔽CSS
  807. */
  808. addCSS() {
  809. log.info("添加屏蔽CSS");
  810. _GM_addStyle(ShieldCSS);
  811. },
  812. /**
  813. * 全文居中
  814. */
  815. articleCenter() {
  816. log.info("全文居中");
  817. _GM_addStyle(`
  818. div[role=main] aside,
  819. div._3Pnjry{
  820. display: none !important;
  821. }
  822. div._gp-ck{
  823. width: 100% !important;
  824. }`);
  825. waitForElementToRemove("div[role=main] aside");
  826. waitForElementToRemove("div._3Pnjry");
  827. utils.waitNodeList("div._gp-ck").then((nodeList) => {
  828. nodeList.forEach((item) => {
  829. item.style["width"] = "100%";
  830. });
  831. });
  832. },
  833. /**
  834. * 去除剪贴板劫持
  835. */
  836. removeClipboardHijacking() {
  837. log.info("去除剪贴板劫持");
  838. const stopNativePropagation = (event) => {
  839. event.stopPropagation();
  840. };
  841. window.addEventListener("copy", stopNativePropagation, true);
  842. document.addEventListener("copy", stopNativePropagation, true);
  843. },
  844. /**
  845. * 自动展开全文
  846. */
  847. autoExpandFullText() {
  848. utils.waitNode(`div#homepage div[class*="dialog-"]`).then((element) => {
  849. element.style["visibility"] = "hidden";
  850. log.info("自动展开全文");
  851. utils.mutationObserver(element, {
  852. callback: (mutations) => {
  853. if (mutations.length == 0) {
  854. return;
  855. }
  856. mutations.forEach((mutationItem) => {
  857. var _a2;
  858. if (mutationItem.target.style["display"] != "none") {
  859. log.success("自动展开全文-自动点击");
  860. (_a2 = document.querySelector(
  861. 'div#homepage div[class*="dialog-"] .cancel'
  862. )) == null ? void 0 : _a2.click();
  863. }
  864. });
  865. },
  866. config: {
  867. /* 子节点的变动(新增、删除或者更改) */
  868. childList: false,
  869. /* 属性的变动 */
  870. attributes: true,
  871. /* 节点内容或节点文本的变动 */
  872. characterData: true,
  873. /* 是否将观察器应用于该节点的所有后代节点 */
  874. subtree: true
  875. }
  876. });
  877. });
  878. },
  879. /**
  880. * 去除简书拦截其它网址的url并自动跳转
  881. */
  882. jumpRedirect() {
  883. if (JianshuRouter.isGoWild()) {
  884. log.success("去除简书拦截其它网址的url并自动跳转");
  885. window.stop();
  886. let search = window.location.href.replace(
  887. window.location.origin + "/",
  888. ""
  889. );
  890. search = decodeURIComponent(search);
  891. let newURL = search.replace(/^go-wild\?ac=2&url=/gi, "").replace(/^https:\/\/link.zhihu.com\/\?target\=/gi, "");
  892. window.location.href = newURL;
  893. }
  894. },
  895. /**
  896. * 屏蔽相关文章
  897. */
  898. shieldRelatedArticles() {
  899. log.info("屏蔽相关文章");
  900. _GM_addStyle(`
  901. div[role="main"] > div > section:nth-child(2){
  902. display: none !important;
  903. }
  904. `);
  905. },
  906. /**
  907. * 【屏蔽】客户端弹窗
  908. */
  909. shieldClientDialog() {
  910. log.info("【屏蔽】客户端弹窗");
  911. _GM_addStyle(`
  912. div:has(>div[class*="-mask"]:not([class*="-mask-hidden"]) + div[tabindex="-1"][role="dialog"]){
  913. display: none !important;
  914. }`);
  915. utils.waitNode(
  916. `div[class*="-mask"]:not([class*="-mask-hidden"]) + div[tabindex="-1"][role="dialog"]`
  917. ).then((element) => {
  918. log.success("弹窗出现");
  919. utils.waitPropertyByInterval(
  920. element,
  921. () => {
  922. var _a2, _b, _c, _d;
  923. let react = utils.getReactObj(element);
  924. return (_d = (_c = (_b = (_a2 = react == null ? void 0 : react.reactInternalInstance) == null ? void 0 : _a2.return) == null ? void 0 : _b.return) == null ? void 0 : _c.memoizedProps) == null ? void 0 : _d.onClose;
  925. },
  926. 250,
  927. 1e4
  928. ).then(() => {
  929. let react = utils.getReactObj(element);
  930. react.reactInternalInstance.return.return.memoizedProps.onClose(
  931. new Event("click")
  932. );
  933. log.success("调用函数关闭弹窗");
  934. });
  935. });
  936. },
  937. /**
  938. * 屏蔽评论区
  939. */
  940. shieldUserComments() {
  941. log.info("屏蔽评论区");
  942. _GM_addStyle(`
  943. div#note-page-comment{
  944. display: none !important;
  945. }
  946. `);
  947. },
  948. /**
  949. * 屏蔽底部推荐阅读
  950. */
  951. shieldRecommendedReading() {
  952. log.info("屏蔽底部推荐阅读");
  953. _GM_addStyle(`
  954. div[role="main"] > div > section:last-child{
  955. display: none !important;
  956. }
  957. `);
  958. },
  959. /**
  960. * 【屏蔽】顶部导航栏
  961. */
  962. shieldTopNav() {
  963. log.info("【屏蔽】顶部导航栏");
  964. _GM_addStyle(`
  965. header{
  966. display: none !important;
  967. }
  968. `);
  969. },
  970. /**
  971. * 【屏蔽】底部工具栏
  972. */
  973. shieldBottomToolbar() {
  974. log.info("【屏蔽】底部工具栏");
  975. _GM_addStyle(`
  976. footer{
  977. display: none !important;
  978. }
  979. `);
  980. }
  981. };
  982. const M_Jianshu = {
  983. init() {
  984. this.addCSS();
  985. PopsPanel.execMenu("JianShuAutoJumpRedirect_Mobile", () => {
  986. Jianshu.jumpRedirect();
  987. });
  988. PopsPanel.execMenu("JianShuHijackSchemeScriptLabel_Mobile", () => {
  989. this.handlePrototype();
  990. });
  991. PopsPanel.execMenu("JianShuRemoveClipboardHijacking_Mobile", () => {
  992. Jianshu.removeClipboardHijacking();
  993. });
  994. PopsPanel.execMenu("JianShuAutoExpandFullText_Mobile", () => {
  995. Jianshu.autoExpandFullText();
  996. });
  997. PopsPanel.execMenu("JianShuremoveFooterRecommendRead", () => {
  998. this.removeFooterRecommendRead();
  999. });
  1000. PopsPanel.execMenu("JianShuShieldUserCommentsMobile", () => {
  1001. this.shieldUserComments();
  1002. });
  1003. },
  1004. /**
  1005. * 添加屏蔽CSS
  1006. */
  1007. addCSS() {
  1008. Jianshu.addCSS();
  1009. },
  1010. /**
  1011. * 手机-屏蔽底部推荐阅读
  1012. */
  1013. removeFooterRecommendRead() {
  1014. log.info("屏蔽底部推荐阅读");
  1015. _GM_addStyle(`
  1016. #recommended-notes{
  1017. display: none !important;
  1018. }`);
  1019. },
  1020. /**
  1021. * 处理原型
  1022. */
  1023. handlePrototype() {
  1024. log.info("处理原型添加script标签");
  1025. let originalAppendChild = Node.prototype.appendChild;
  1026. _unsafeWindow.Node.prototype.appendChild = function(element) {
  1027. let allowElementLocalNameList = ["img"];
  1028. if (element.src && !element.src.includes("jianshu.io") && !allowElementLocalNameList.includes(element.localName)) {
  1029. log.success(["禁止添加的元素", element]);
  1030. return null;
  1031. } else {
  1032. return originalAppendChild.call(this, element);
  1033. }
  1034. };
  1035. },
  1036. /**
  1037. * 屏蔽评论区
  1038. */
  1039. shieldUserComments() {
  1040. log.info("屏蔽评论区");
  1041. _GM_addStyle(`
  1042. #comment-main{
  1043. display: none !important;
  1044. }
  1045. `);
  1046. }
  1047. };
  1048. PopsPanel.init();
  1049. let isMobile = utils.isPhone();
  1050. let CHANGE_ENV_SET_KEY = "change_env_set";
  1051. let chooseMode = _GM_getValue(CHANGE_ENV_SET_KEY);
  1052. GM_Menu.add({
  1053. key: CHANGE_ENV_SET_KEY,
  1054. text: `⚙ 自动: ${isMobile ? "移动端" : "PC端"}`,
  1055. autoReload: false,
  1056. isStoreValue: false,
  1057. showText(text) {
  1058. if (chooseMode == null) {
  1059. return text;
  1060. }
  1061. return text + ` 手动: ${chooseMode == 1 ? "移动端" : chooseMode == 2 ? "PC端" : "未知"}`;
  1062. },
  1063. callback: () => {
  1064. let allowValue = [0, 1, 2];
  1065. let chooseText = window.prompt(
  1066. "请输入当前脚本环境判定\n\n自动判断: 0\n移动端: 1\nPC端: 2",
  1067. "0"
  1068. );
  1069. if (!chooseText) {
  1070. return;
  1071. }
  1072. let chooseMode2 = parseInt(chooseText);
  1073. if (isNaN(chooseMode2)) {
  1074. Qmsg.error("输入的不是规范的数字");
  1075. return;
  1076. }
  1077. if (!allowValue.includes(chooseMode2)) {
  1078. Qmsg.error("输入的值必须是0或1或2");
  1079. return;
  1080. }
  1081. if (chooseMode2 == 0) {
  1082. _GM_deleteValue(CHANGE_ENV_SET_KEY);
  1083. } else {
  1084. _GM_setValue(CHANGE_ENV_SET_KEY, chooseMode2);
  1085. }
  1086. }
  1087. });
  1088. if (chooseMode != null) {
  1089. log.info(`手动判定为${chooseMode === 1 ? "移动端" : "PC端"}`);
  1090. if (chooseMode == 1) {
  1091. M_Jianshu.init();
  1092. } else if (chooseMode == 2) {
  1093. Jianshu.init();
  1094. } else {
  1095. Qmsg.error("意外,手动判定的值不在范围内");
  1096. _GM_deleteValue(CHANGE_ENV_SET_KEY);
  1097. }
  1098. } else {
  1099. if (isMobile) {
  1100. log.info("自动判定为移动端");
  1101. M_Jianshu.init();
  1102. } else {
  1103. log.info("自动判定为PC端");
  1104. Jianshu.init();
  1105. }
  1106. }
  1107.  
  1108. })(Qmsg, DOMUtils, Utils);

QingJ © 2025

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