CleanReader

启用后,自动进入简洁阅读模式。

  1. // ==UserScript==
  2. // @name CleanReader
  3. // @namespace https://greasyfork.org/zh-CN/users/141921
  4. // @version 0.4.1
  5. // @description 启用后,自动进入简洁阅读模式。
  6. // @author Vinx
  7. // @match
  8. // @require https://cdn.bootcss.com/jquery/2.2.4/jquery.min.js
  9. // @require https://greasyfork.org/scripts/38955-jquery-print/code/jQueryprint.js?version=254772
  10. // @include *
  11. // @grant GM_addStyle
  12. // @run-at document-end
  13. // @note 2021.05.08-V0.4.1 修正开源中国显示
  14. // @note 2021.05.08-V0.4.0 修复了CSDN显示问题,整理代码
  15. // @note 2018.11.07-V0.3.3 修复了CSDN打印的问题,添加OSChina等网站
  16. // @note 2018.03.18-V0.3.2 添加放大缩小按钮,并支持按键缩放(+/= :放大,- :缩小)
  17. // @note 2018.03.18-V0.3.1 添加ESC退出阅读模式功能
  18. // @note 2018.03.15-V0.3.0 添加对Discuz论坛的支持,修改标题和内容居中显示
  19. // @note 2018.02.28-V0.2.0 不在自动进入,添加了阅读及打印按钮
  20. // @note 2017.07.19-V0.1.0 首次发布,启用后,自动进入简洁阅读模式。
  21. // ==/UserScript==
  22.  
  23. var contents = {
  24. 'blog.csdn.net': {
  25. 'title': ".title-article|.csdn_top",
  26. 'content': ".article_content"
  27. },
  28. 'www.cnblogs.com': {
  29. 'title': "#cb_post_title_url",
  30. 'content': "#cnblogs_post_body"
  31. },
  32. 'blog.sina.com.cn': {
  33. 'title': ".articalTitle",
  34. 'content': ".articalContent"
  35. },
  36. 'bbs.fishc.com': {
  37. 'title': "#thread_subject",
  38. 'content': "[id^='postmessage_']:first"
  39. },
  40. 'www.liaoxuefeng.com': {
  41. 'title': ".x-content h4",
  42. 'content': ".x-main-content"
  43. },
  44. 'blog.163.com': {
  45. 'title': ".title.pre.fs1",
  46. 'content': ".bct.fc05.fc11.nbw-blog.ztag"
  47. },
  48. 'www.runoob.com': {
  49. 'title': ".article-intro h1:first",
  50. 'content': ".article-body"
  51. },
  52. 'lib.uml.com.cn': {
  53. 'title': ".arttitle",
  54. 'content': ".artcontent"
  55. },
  56. 'bbs.pediy.com': {
  57. 'title': ".break-all.subject",
  58. 'content': ".message.break-all:first"
  59. },
  60. 'oschina.net': {
  61. 'title': ".article-box__title",
  62. 'content': ".article-detail .content"
  63. },
  64. 'www.oschina.net': {
  65. 'title': ".article-box__title",
  66. 'content': ".article-detail .content"
  67. },
  68. 'my.oschina.net': {
  69. 'title': ".article-box__title",
  70. 'content': ".article-detail .content"
  71. },
  72. 'kns.cnki.net': {
  73. 'title': "",
  74. 'content': ".content"
  75. }
  76.  
  77. };
  78.  
  79. (function () {
  80. 'use strict';
  81. var J;
  82. if (typeof jQuery != 'undefined') { //避免与原网页中的Jquery冲突
  83. J = jQuery.noConflict(true);
  84. }
  85.  
  86. AddTemplateSite();
  87.  
  88. if (!IsContentPage()) return;
  89.  
  90. // 添加样式表
  91. addStyle();
  92.  
  93. // 保存样式
  94. var html_height = J("html").css("height");
  95. var body_width = J("body").css("width");
  96. var body_height = J("body").css("height");
  97. var body_min_width = J("body").css("min_width");
  98. var body_min_height = J("body").css("min_height");
  99. var body_background = J("body").css("background");
  100. var body_text_align = J("body").css("text-align");
  101. var body_margin = J("body").css("margin");
  102. var body_padding = J("body").css("padding");
  103. var body_overflow_y = J("body").css("overflow-y");
  104.  
  105. // 创建阅读按钮
  106. createReadButton();
  107. // 创建打印按钮
  108. createPrintButton();
  109. // 创建缩小按钮
  110. createZoomOutButton();
  111. // 创建放大按钮
  112. createZoomInButton();
  113.  
  114. keyEventHandler();
  115.  
  116. function addStyle() {
  117. J("head").append('<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.css">');
  118.  
  119. GM_addStyle(`
  120. #read_button, #print_button, #zoom_out_button, #zoom_in_button{
  121. width: 24px;
  122. height: 24px;
  123. z-index: 999999;
  124. cursor: pointer;
  125. position: fixed;
  126. margin: 0 auto;
  127. color: #0593d3;
  128. font-size: 24px;
  129. }
  130.  
  131. #read_button{
  132.  
  133. right: 32px;
  134. bottom: 32px;
  135. }
  136.  
  137. #print_button {
  138.  
  139. right: 32px;
  140. bottom: 64px;
  141. }
  142.  
  143. #zoom_out_button {
  144.  
  145. right: 32px;
  146. bottom: 96px;
  147. }
  148.  
  149. #zoom_in_button {
  150.  
  151. right: 32px;
  152. bottom: 128px;
  153. }
  154.  
  155. @media print{
  156. body{
  157. min-width:1000px;
  158. }
  159.  
  160. pre{
  161. word-break: break-word;
  162. white-space: pre-wrap;
  163. }
  164.  
  165. #read_button, #print_button, #zoom_in_button ,#zoom_out_button{
  166. display: none;
  167. }
  168. }
  169. `);
  170. }
  171.  
  172. // 创建阅读按钮
  173. function createReadButton() {
  174. var read_button = document.createElement("div");
  175. J(read_button).attr("class", "fa fa-book fa-2");
  176. J(read_button).attr("id", "read_button");
  177. J("body").prepend(read_button);
  178.  
  179. J("#read_button").click(function () {
  180. var isShow = J(read_button).attr("userdata");
  181. if (isShow == "true") {
  182. exitCleanRead();
  183. J("#print_button").hide();
  184. J("#zoom_in_button").hide();
  185. J("#zoom_out_button").hide();
  186. }
  187. else {
  188. enterCleanRead();
  189. J("#read_button").show();
  190. J("#print_button").show();
  191. J("#zoom_in_button").show();
  192. J("#zoom_out_button").show();
  193. }
  194. });
  195. }
  196.  
  197. // 创建打印按钮
  198. function createPrintButton() {
  199. var print_button = document.createElement("div");
  200. J(print_button).attr("class", "fa fa-print fa-2");
  201. J(print_button).attr("id", "print_button");
  202. J("body").prepend(print_button);
  203. J("#print_button").hide();
  204.  
  205. J("#print_button").click(function () {
  206. J("#CleanReader").print({
  207. globalStyles: true,
  208. mediaPrint: false,
  209. stylesheet: null,
  210. noPrintSelector: ".no-print",
  211. iframe: true,
  212. append: null,
  213. prepend: null,
  214. manuallyCopyFormValues: true,
  215. deferred: J.Deferred()
  216. });
  217. });
  218. }
  219.  
  220. // 创建缩小按钮
  221. function createZoomOutButton() {
  222. var zoom_out_button = document.createElement("div");
  223. J(zoom_out_button).attr("class", "fa fa-search-minus fa-2");
  224. J(zoom_out_button).attr("id", "zoom_out_button");
  225. J("body").prepend(zoom_out_button);
  226. J("#zoom_out_button").hide();
  227.  
  228. J("#zoom_out_button").click(function () {
  229. var zoom = J("#CleanReader").css("zoom");
  230. zoom = parseFloat(zoom);
  231. zoom = zoom - 0.1;
  232. if (zoom <= 0) zoom = 0.1;
  233. J("#CleanReader").css("zoom", zoom);
  234. });
  235.  
  236. }
  237.  
  238. // 创建放大按钮
  239. function createZoomInButton() {
  240. var zoom_in_button = document.createElement("div");
  241. J(zoom_in_button).attr("class", "fa fa-search-plus fa-2");
  242. J(zoom_in_button).attr("id", "zoom_in_button");
  243. J("body").prepend(zoom_in_button);
  244. J("#zoom_in_button").hide();
  245.  
  246. J("#zoom_in_button").click(function () {
  247. var zoom = J("#CleanReader").css("zoom");
  248. zoom = parseFloat(zoom);
  249. zoom = zoom + 0.1;
  250. J("#CleanReader").css("zoom", zoom);
  251. });
  252.  
  253. }
  254.  
  255. // 按键事件处理
  256. function keyEventHandler() {
  257. //ESC退出
  258. J(document).keyup(function () {
  259. var isShow = J("#read_button").attr("userdata");
  260. if (event.keyCode == 27 && isShow == "true") {
  261. exitCleanRead();
  262. J("#print_button").hide();
  263. J("#zoom_in_button").hide();
  264. J("#zoom_out_button").hide();
  265. }
  266. });
  267.  
  268. // + - 放大缩小
  269. J(document).keydown(function () {
  270. var zoom = 1;
  271. var isShow = J("#read_button").attr("userdata");
  272. if (event.keyCode == 107 || event.keyCode == 187) {
  273. zoom = J("#CleanReader").css("zoom");
  274. zoom = parseFloat(zoom);
  275. zoom = zoom + 0.1;
  276. J("#CleanReader").css("zoom", zoom);
  277. }
  278. else if (event.keyCode == 109 || event.keyCode == 189) {
  279. zoom = J("#CleanReader").css("zoom");
  280. zoom = parseFloat(zoom);
  281. zoom = zoom - 0.1;
  282. if (zoom <= 0) zoom = 0.1;
  283. J("#CleanReader").css("zoom", zoom);
  284. }
  285. });
  286. }
  287.  
  288. // 判断是否为内容页
  289. function IsContentPage() {
  290. if (!contents.hasOwnProperty(window.location.host)) return false;
  291.  
  292. var isContentPage = false;
  293. var tmp1 = contents[window.location.host].content.split("|");
  294. for (var i = 0; i < tmp1.length; i++) {
  295. if (J(tmp1[i]).length > 0) {
  296. isContentPage = true;
  297. break;
  298. }
  299. }
  300.  
  301. if (!isContentPage) return false;
  302.  
  303. return true;
  304. }
  305.  
  306. function AddTemplateSite() {
  307. var jsonstr = {};
  308. //Discuz
  309. if (J("meta[name='generator'][content]").length > 0 && J("meta[name='generator'][content]").attr("content").indexOf("Discuz") >= 0) {
  310. jsonstr.title = "#thread_subject";
  311. jsonstr.content = "[id^='postmessage_']:first|.pattl";
  312. contents[window.location.host] = jsonstr;
  313. }
  314. }
  315.  
  316. // 进入阅读模式
  317. function enterCleanRead() {
  318. var hostName = window.location.host;
  319.  
  320. J("html").css("height", "100%");
  321. J("body").css("height", "100%");
  322. J("body").css("background", "#ffffff");
  323. J("body").css("text-align", "left");
  324. J("body").css("margin", "0 0");
  325. J("body").css("padding", "0 0");
  326. J("body").css("overflow-y", "hidden");
  327.  
  328. var CleanReader = document.createElement("div");
  329. J(CleanReader).attr("style", "background-color: rgb(255, 255, 255);"
  330. + "width: 1000px;"
  331. + "margin: 0px auto;"
  332. + "padding: 0px 20px;")
  333. J(CleanReader).attr("id", "CleanReader");
  334.  
  335. // 标题节点
  336. var titleSelectors = contents[hostName].title.split("|");
  337. var titleText;
  338. for (var i = 0; i < titleSelectors.length; i++) {
  339. if (J(titleSelectors[i]).length > 0) {
  340. titleText = J(titleSelectors[i]).text();
  341. break;
  342. }
  343. }
  344.  
  345. var title = document.createElement("div");
  346. J(title).html(titleText);
  347. J(title).attr("style", "font-size: 32px;"
  348. + "margin: 0px auto;"
  349. + "padding: 10px 0px;"
  350. + "text-align: center;")
  351.  
  352. J(CleanReader).append(title);
  353. J(CleanReader).append("<hr/>");
  354.  
  355. // 正文节点
  356. var textSelectors = contents[hostName].content.split("|");
  357. for (var j = 0; j < textSelectors.length; j++) {
  358. J(CleanReader).append(J(textSelectors[j]).clone());
  359. }
  360.  
  361. J(CleanReader).children().css("margin", "0px auto");
  362.  
  363. // 黑色遮罩
  364. var mask = document.createElement("div");
  365. J(mask).attr("style", "height: 100%;"
  366. + "width: 100%;"
  367. + "background-color: rgba(0, 0, 0, 0.9);"
  368. + "z-index: 999997;"
  369. + "position: fixed;"
  370. + "left: 0;"
  371. + "top: 0;"
  372. + "margin: 0px auto;"
  373. + "overflow-y: auto;");
  374. J(mask).attr("id", "CleanReadermask");
  375. J(mask).append(CleanReader);
  376.  
  377. J("body").css("overflow-y", "hidden");
  378. J("body").prepend(mask);
  379.  
  380. var image = J(CleanReader).find("img");
  381. for (var k = 0; k < image.length; k++) {
  382. J(image[k]).css("max-width", "90%");
  383. J(image[k]).css("height", "auto");
  384. }
  385.  
  386. J("#read_button").attr("userdata", "true");
  387. }
  388.  
  389. // 退出阅读模式
  390. function exitCleanRead() {
  391. J("#CleanReadermask").remove();
  392.  
  393. J("html").css("height", html_height);
  394. J("body").css("width", body_width);
  395. J("body").css("height", body_height);
  396. J("body").css("min_width", body_min_width);
  397. J("body").css("min_height", body_min_height);
  398. J("body").css("background", body_background);
  399. J("body").css("text-align", body_text_align);
  400. J("body").css("margin", body_margin);
  401. J("body").css("padding", body_padding);
  402. J("body").css("overflow-y", body_overflow_y);
  403.  
  404. J("#read_button").attr("userdata", "");
  405. }
  406. })();
  407.