beautifier

自定义页面背景图,布局优化

此脚本不应直接安装,它是一个供其他脚本使用的外部库。如果您需要使用该库,请在脚本元属性加入:// @require https://update.cn-greasyfork.org/scripts/450211/1091805/beautifier.js

  1. /* eslint-disable no-multi-spaces */
  2. /* eslint-disable no-implicit-globals */
  3. /* eslint-disable userscripts/no-invalid-headers */
  4. /* eslint-disable userscripts/no-invalid-grant */
  5.  
  6. // ==UserScript==
  7. // @name beautifier
  8. // @displayname 页面美化
  9. // @namespace Wenku8++
  10. // @version 0.3.1
  11. // @description 自定义页面背景图,布局优化
  12. // @author PY-DNG
  13. // @license GPL-v3
  14. // @regurl https?://www\.wenku8\.net/.*
  15. // @require https://greasyfork.org/scripts/449412-basic-functions/code/Basic%20Functions.js?version=1085783
  16. // @require https://greasyfork.org/scripts/449583-configmanager/code/ConfigManager.js?version=1085902
  17. // @grant GM_getValue
  18. // @grant GM_setValue
  19. // @grant GM_listValues
  20. // @grant GM_deleteValue
  21. // ==/UserScript==
  22.  
  23. (function __MAIN__() {
  24. const ASSETS = require('assets');
  25. const alertify = require('alertify');
  26. const settings = require('settings');
  27. const SettingPanel = require('SettingPanel');
  28.  
  29. const CONST = {
  30. Text: {
  31. CommonBeautify: '通用页面美化',
  32. NovelBeautify: '阅读页面美化',
  33. ReviewBeautify: '书评页面美化',
  34. DefaultBeautify: '默认页面美化图片',
  35. Enable: '启用',
  36. BackgroundImage: '背景图片',
  37. AlertTitle: '页面美化设置',
  38. InvalidImageUrl: '图片链接格式错误</br>仅仅接受http/https/data链接',
  39. textScale: '文字大小缩放'
  40. },
  41. ClassName: {
  42. BgImage: 'plus_cbty_image',
  43. BgCover: 'plus_cbty_cover',
  44. CSS: 'plus_beautifier'
  45. },
  46. CSS: {
  47. Common: '.plus_cbty_image {position: fixed;top: 0;left: 0;z-index: -2;}.plus_cbty_cover {position: fixed;top: 0;left: calc((100vw - 960px) / 2);z-Index: -1;background-color: rgba(255,255,255,0.7);width: 960px;height: 100vh;}body {overflow: auto;}body>.main {position: relative;margin-left: 0;margin-right: 0;left: calc((100vw - 960px) / 2);}body.plus_cbty table.grid td, body.plus_cbty .odd, body.plus_cbty .even, body.plus_cbty .blockcontent {background-color: rgba(255,255,255,0) !important;}.textarea, .text {background-color: rgba(255,255,255,0.9);}#headlink{background-color: rgba(255,255,255,0.7);}',
  48. Novel: 'html{background-image: url({BGI});}body {width: 100vw;height: 100vh;overflow: overlay;margin: 0px;background-color: rgba(255,255,255,0.7);}#contentmain {overflow-y: auto;height: calc(100vh - {H});max-width: 100%;min-width: 0px;max-width: 100vw;}#adv1, #adtop, #headlink, #footlink, #adbottom {overflow: overlay;min-width: 0px;max-width: 100vw;}#adv900, #adv5 {max-width: 100vw;}',
  49. Review: 'body {overflow: auto;background-image: url({BGI});}#content > table > tbody > tr > td, tr td.odd, tr td.even {background-color: rgba(255,255,255,0.7) !important;overflow: auto;}body.plus_cbty #content > table > tbody > tr > td {background-color: rgba(255,255,255,0) !important;overflow: auto;}#content {height: 100vh;overflow: auto;}.m_top, .m_head, .main.nav, .m_foot {display: none;}.main {margin-top: 0px;}#content table div[style*="width:100%"], #content table div[style*="width:60%"] strong{font-size: calc(1em * {S}/ 100);line-height: calc(120% * {S}/ 100);}.jieqiQuote, .jieqiCode, .jieqiNote {font-size: inherit;}'
  50. },
  51. Config_Ruleset: {
  52. 'version-key': 'config-version',
  53. 'ignores': ["LOCAL-CDN"],
  54. 'defaultValues': {
  55. //'config-key': {},
  56. common: {
  57. enable: false,
  58. image: null
  59. },
  60. novel: {
  61. enable: false,
  62. image: null
  63. },
  64. review: {
  65. enable: false,
  66. image: null,
  67. textScale: 100
  68. },
  69. image: null
  70. },
  71. 'updaters': {
  72. /*'config-key': [
  73. function() {
  74. // This function contains updater for config['config-key'] from v0 to v1
  75. },
  76. function() {
  77. // This function contains updater for config['config-key'] from v1 to v2
  78. }
  79. ]*/
  80. }
  81. }
  82. };
  83.  
  84. // Config
  85. const CM = new ConfigManager(CONST.Config_Ruleset);
  86. const CONFIG = CM.Config;
  87. CM.setDefaults();
  88.  
  89. // Settings
  90. settings.registerSettings(MODULE_IDENTIFIER, setter);
  91.  
  92. // Beautifier pages
  93. const API = getAPI();
  94. if (API[0] === 'novel' && [...API].pop() !== 'index.htm') {
  95. CONFIG.novel.enable && novel();
  96. } else if (API.join('/') === 'modules/article/reviewshow.php') {
  97. CONFIG.review.enable && review();
  98. } else {
  99. CONFIG.common.enable && common();
  100. }
  101.  
  102. exports = {
  103. //
  104. };
  105.  
  106. // Beautifier for all wenku pages
  107. function common() {
  108. const src = CONFIG.common.image || CONFIG.image;
  109.  
  110. const img = $CrE('img');
  111. img.src = src;
  112. img.classList.add(CONST.ClassName.BgImage);
  113. document.body.appendChild(img);
  114.  
  115. const cover = $CrE('div');
  116. cover.classList.add(CONST.ClassName.BgCover);
  117. document.body.appendChild(cover);
  118.  
  119. document.body.classList.add('plus_cbty');
  120. addStyle(CONST.CSS.Common, CONST.ClassName.CSS);
  121. return true;
  122. }
  123.  
  124. // Novel reading page
  125. function novel() {
  126. const src = CONFIG.novel.image || CONFIG.image;
  127. const usedHeight = getRestHeight();
  128.  
  129. addStyle(CONST.CSS.Novel
  130. .replaceAll('{BGI}', src)
  131. .replaceAll('{H}', usedHeight), CONST.ClassName.CSS
  132. );
  133.  
  134. unsafeWindow.scrolling = beautiful_scrolling;
  135.  
  136. // Get rest height without #contentmain
  137. function getRestHeight() {
  138. let usedHeight = 0;
  139. ['adv1', 'adtop', 'headlink', 'footlink', 'adbottom'].forEach((id) => {
  140. const node = $('#'+id);
  141. if (node instanceof Element && node.id !== 'contentmain') {
  142. const cs = getComputedStyle(node);
  143. ['height', 'marginTop', 'marginBottom', 'paddingTop', 'paddingBottom', 'borderTop', 'borderBottom'].forEach((style) => {
  144. const reg = cs[style].match(/([\.\d]+)px/);
  145. reg && (usedHeight += Number(reg[1]));
  146. });
  147. };
  148. });
  149. usedHeight = usedHeight.toString() + 'px';
  150. return usedHeight;
  151. }
  152.  
  153. // Mouse dblclick scroll with beautifier applied
  154. function beautiful_scrolling() {
  155. var contentmain = pageResource.elements.contentmain;
  156. var currentpos = contentmain.scrollTop || 0;
  157. contentmain.scrollTo(0, ++currentpos);
  158. var nowpos = contentmain.scrollTop || 0;
  159. if(currentpos != nowpos) unsafeWindow.clearInterval(timer);
  160. }
  161. }
  162.  
  163. // Review reading page
  164. function review() {
  165. const src = CONFIG.review.image || CONFIG.image;
  166. const textScale = CONFIG.review.textScale;
  167. const main = $('#content');
  168. addStyle(CONST.CSS.Review
  169. .replaceAll('{BGI}', src)
  170. .replaceAll('{S}', textScale.toString())
  171. , CONST.ClassName.CSS);
  172. scaleimgs();
  173. hookPosition();
  174.  
  175. function scaleimgs() {
  176. const w = main.clientWidth * 0.8 - 3; // td.width = "80%", .even {padding: 3px;}
  177. Array.from($All('.divimage>img')).forEach((img) => {
  178. img.width = img.width < w ? img.width : w;
  179. });
  180. }
  181.  
  182. function hookPosition() {
  183. if (typeof UBBEditor !== 'object') {
  184. hookPosition.wait = hookPosition.wait ? hookPosition.wait : 0;
  185. if (++hookPosition.wait > 50) {return false;}
  186. hookPosition.wait % 10 === 0 && DoLog('hookPosition: UBBEditor not loaded, waiting...');
  187. setTimeout(hookPosition, ASSETS.Number.Interval);
  188. return false;
  189. }
  190. UBBEditor.GetPosition = function (obj) {
  191. var r = new Array();
  192. r.x = obj.offsetLeft;
  193. r.y = obj.offsetTop;
  194. while (obj = obj.offsetParent) {
  195. if (unsafeWindow.$(obj).getStyle('position') == 'absolute' || unsafeWindow.$(obj).getStyle('position') == 'relative') break;
  196. r.x += obj.offsetLeft;
  197. r.y += obj.offsetTop;
  198. }
  199. r.x -= main.scrollLeft;
  200. r.y -= main.scrollTop;
  201. return r;
  202. }
  203. }
  204. }
  205.  
  206. // Settings
  207. function setter() {
  208. const storage = {
  209. GM_getValue: GM_getValue,
  210. GM_setValue: GM_setValue,
  211. GM_listValues: GM_listValues,
  212. GM_deleteValue: GM_deleteValue
  213. };
  214. const Panel = SettingPanel.SettingPanel;
  215. const SetPanel = new Panel({
  216. buttons: 'saver',
  217. header: CONST.Text.AlertTitle,
  218. tables: [{
  219. rows: [{
  220. blocks: [{
  221. isHeader: true,
  222. colSpan: 2,
  223. innerText: CONST.Text.CommonBeautify
  224. }]
  225. },{
  226. blocks: [{
  227. innerText: CONST.Text.Enable
  228. },{
  229. options: [{
  230. path: 'common/enable',
  231. type: 'boolean'
  232. }]
  233. }]
  234. },{
  235. blocks: [{
  236. innerText: CONST.Text.BackgroundImage
  237. },{
  238. options: [{
  239. path: 'common/image',
  240. type: ['image', 'string'],
  241. checker: imageUrlChecker,
  242. }]
  243. }]
  244. }]
  245. },{
  246. rows: [{
  247. blocks: [{
  248. isHeader: true,
  249. colSpan: 2,
  250. innerText: CONST.Text.NovelBeautify
  251. }]
  252. },{
  253. blocks: [{
  254. innerText: CONST.Text.Enable
  255. },{
  256. options: [{
  257. path: 'novel/enable',
  258. type: 'boolean'
  259. }]
  260. }]
  261. },{
  262. blocks: [{
  263. innerText: CONST.Text.BackgroundImage
  264. },{
  265. options: [{
  266. path: 'novel/image',
  267. type: ['image', 'string'],
  268. checker: imageUrlChecker,
  269. }]
  270. }]
  271. }]
  272. },{
  273. rows: [{
  274. blocks: [{
  275. isHeader: true,
  276. colSpan: 2,
  277. innerText: CONST.Text.ReviewBeautify
  278. }]
  279. },{
  280. blocks: [{
  281. innerText: CONST.Text.Enable
  282. },{
  283. options: [{
  284. path: 'review/enable',
  285. type: 'boolean'
  286. }]
  287. }]
  288. },{
  289. blocks: [{
  290. innerText: CONST.Text.BackgroundImage
  291. },{
  292. options: [{
  293. path: 'review/image',
  294. type: ['image', 'string'],
  295. checker: imageUrlChecker,
  296. }]
  297. }]
  298. },{
  299. blocks: [{
  300. innerText: CONST.Text.textScale
  301. },{
  302. options: [{
  303. path: 'review/textScale',
  304. type: 'number'
  305. }],
  306. children: [(() => {
  307. const span = $CrE('span');
  308. span.innerText = '%';
  309. return span;
  310. }) ()]
  311. }]
  312. }]
  313. },{
  314. rows: [{
  315. blocks: [{
  316. isHeader: true,
  317. colSpan: 2,
  318. innerText: CONST.Text.DefaultBeautify
  319. }]
  320. },{
  321. blocks: [{
  322. innerText: CONST.Text.BackgroundImage
  323. },{
  324. options: [{
  325. path: 'image',
  326. type: ['image', 'string'],
  327. checker: imageUrlChecker,
  328. }]
  329. }]
  330. }]
  331. }]
  332. }, storage);
  333.  
  334. function imageUrlChecker(e, value) {
  335. if (!value.match(/.+:/)) {
  336. alertify.alert(CONST.Text.AlertTitle, CONST.Text.InvalidImageUrl);
  337. return false;
  338. }
  339. e.target.value = value || null;
  340. return true;
  341. }
  342. }
  343. })();