wenku8+ loader entry

load wenku8+ temporarily (entry)

目前为 2021-12-23 提交的版本。查看 最新版本

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

  1. (function () {
  2. const KEY_LOCALCDN = 'LOCAL-CDN';
  3. const KEY_LOCALCDN_VERSION = 'version';
  4. const VALUE_LOCALCDN_VERSION = '0.1';
  5. const KEY_POLYFILL = 'wenku8-plus-injecter';
  6. GM_PolyFill(KEY_POLYFILL);
  7. DoLog();
  8. const loader_url = 'https://cdn.jsdelivr.net/gh/PYUDNG/wenku8-injector@main/wenku8_plus_loader.js'; // 'https://tinyurl.com/y63x2xb4'
  9.  
  10. const a = document.querySelector('a[href="#wenkuplus"]');
  11. a.setAttribute('tiptitle', '点击这里即可运行脚本,关闭浏览器后脚本就会自动消失');
  12. a.addEventListener('click', function(e) {
  13. destroyEvent(e);
  14. !window.wenku8plus_loader_loaded && confirm('是否运行 [轻小说文库8+]?') && loadJSPlus(loader_url, function(success) {
  15. if (success) {
  16. DoLog(['wenku8+ loader loaded for', oDom]);
  17. window.wenku8plus_loader_loaded = true;
  18. a.setAttribute('tiptitle', '[轻小说文库8+]已经在运行啦!点击这里打开文库首页,开始浏览文库8~');
  19. a.href = 'https://www.wenku8.net/index.php';
  20. } else {
  21. DoLog(['wenku8+ loader load failed on', oDom]);
  22. }
  23. });
  24. });
  25.  
  26. // Just stopPropagation and preventDefault
  27. function destroyEvent(e) {
  28. if (!e) {return false;};
  29. if (!e instanceof Event) {return false;};
  30. e.stopPropagation();
  31. e.preventDefault();
  32. }
  33.  
  34. // Load/Read and Save javascript from given url
  35. // Auto reties when xhr fails.
  36. // If load success then callback(true), else callback(false)
  37. function loadJSPlus(url, callback, oDoc = document, maxRetry = 3, retried = 0) {
  38. const fn = callback || function () {};
  39. const localCDN = GM_getValue(KEY_LOCALCDN, {});
  40. if (localCDN[url]) {
  41. DoLog(LogLevel.Info, 'Loading js from localCDN: ' + url);
  42. const js = localCDN[url];
  43. appendScript(js);
  44. fn(true);
  45. return;
  46. }
  47. DoLog(LogLevel.Info, 'Loading js from web: ' + url);
  48. GM_xmlhttpRequest({
  49. method: 'GET',
  50. url: url,
  51. responseType: 'text',
  52. onload: function (e) {
  53. if (e.status === 200) {
  54. const js = e.responseText;
  55. localCDN[url] = js;
  56. localCDN[KEY_LOCALCDN_VERSION] = VALUE_LOCALCDN_VERSION;
  57. GM_setValue(KEY_LOCALCDN, localCDN);
  58. appendScript(js);
  59. fn(true);
  60. } else {
  61. retry();
  62. }
  63. },
  64. onerror: retry
  65. })
  66.  
  67. function appendScript(code) {
  68. const script = oDoc.createElement('script');
  69. script.type = 'text/javascript';
  70. script.innerHTML = code;
  71. oDoc.head.appendChild(script);
  72. }
  73.  
  74. function retry() {
  75. retried++;
  76. if (retried <= maxRetry) {
  77. loadJSPlus(url, callback, oDoc, maxRetry, retried);
  78. } else {
  79. fn(false);
  80. }
  81. }
  82. }
  83.  
  84. // GM_Polyfill By PY-DNG
  85. // 2021.07.18
  86. // Simply provides the following GM_functions using localStorage, XMLHttpRequest and window.open:
  87. // Returns object GM_POLYFILLED which has the following properties that shows you which GM_functions are actually polyfilled:
  88. // GM_setValue, GM_getValue, GM_deleteValue, GM_listValues, GM_xmlhttpRequest, GM_openInTab, GM_setClipboard
  89. function GM_PolyFill(name = 'default') {
  90. const GM_POLYFILL_KEY_STORAGE = 'GM_STORAGE_POLYFILL';
  91. const GM_POLYFILL_storage = GM_POLYFILL_getStorage();
  92. const GM_POLYFILLED = {
  93. GM_setValue: true,
  94. GM_getValue: true,
  95. GM_deleteValue: true,
  96. GM_listValues: true,
  97. GM_xmlhttpRequest: true,
  98. GM_openInTab: true,
  99. GM_setClipboard: true,
  100. };
  101. GM_setValue_polyfill();
  102. GM_getValue_polyfill();
  103. GM_deleteValue_polyfill();
  104. GM_listValues_polyfill();
  105. GM_xmlhttpRequest_polyfill();
  106. GM_openInTab_polyfill();
  107. GM_setClipboard_polyfill();
  108.  
  109. function GM_POLYFILL_getStorage() {
  110. let gstorage = localStorage.getItem(GM_POLYFILL_KEY_STORAGE);
  111. gstorage = gstorage ? JSON.parse(gstorage) : {};
  112. let storage = gstorage[name] ? gstorage[name] : {};
  113. return storage;
  114. };
  115.  
  116. function GM_POLYFILL_saveStorage() {
  117. let gstorage = localStorage.getItem(GM_POLYFILL_KEY_STORAGE);
  118. gstorage = gstorage ? JSON.parse(gstorage) : {};
  119. gstorage[name] = GM_POLYFILL_storage;
  120. localStorage.setItem(GM_POLYFILL_KEY_STORAGE, JSON.stringify(gstorage));
  121. };
  122. // GM_setValue
  123. function GM_setValue_polyfill() {
  124. typeof (GM_setValue) === 'function' ? GM_POLYFILLED.GM_setValue = false: window.GM_setValue = PF_GM_setValue;;
  125.  
  126. function PF_GM_setValue(name, value) {
  127. name = String(name);
  128. GM_POLYFILL_storage[name] = value;
  129. GM_POLYFILL_saveStorage();
  130. };
  131. };
  132. // GM_getValue
  133. function GM_getValue_polyfill() {
  134. typeof (GM_getValue) === 'function' ? GM_POLYFILLED.GM_getValue = false: window.GM_getValue = PF_GM_getValue;
  135.  
  136. function PF_GM_getValue(name, defaultValue) {
  137. name = String(name);
  138. if (GM_POLYFILL_storage.hasOwnProperty(name)) {
  139. return GM_POLYFILL_storage[name];
  140. } else {
  141. return defaultValue;
  142. };
  143. };
  144. };
  145. // GM_deleteValue
  146. function GM_deleteValue_polyfill() {
  147. typeof (GM_deleteValue) === 'function' ? GM_POLYFILLED.GM_deleteValue = false: window.GM_deleteValue = PF_GM_deleteValue;
  148.  
  149. function PF_GM_deleteValue(name) {
  150. name = String(name);
  151. if (GM_POLYFILL_storage.hasOwnProperty(name)) {
  152. delete GM_POLYFILL_storage[name];
  153. GM_POLYFILL_saveStorage();
  154. };
  155. };
  156. };
  157. // GM_listValues
  158. function GM_listValues_polyfill() {
  159. typeof (GM_listValues) === 'function' ? GM_POLYFILLED.GM_listValues = false: window.GM_listValues = PF_GM_listValues;
  160.  
  161. function PF_GM_listValues() {
  162. return Object.keys(GM_POLYFILL_storage);
  163. };
  164. };
  165. // GM_xmlhttpRequest
  166. // not supported properties of details: synchronous binary nocache revalidate context fetch
  167. // not supported properties of response(onload arguments[0]): finalUrl
  168. // ---!IMPORTANT!--- DOES NOT SUPPORT CROSS-ORIGIN REQUESTS!!!!! ---!IMPORTANT!---
  169. function GM_xmlhttpRequest_polyfill() {
  170. typeof (GM_xmlhttpRequest) === 'function' ? GM_POLYFILLED.GM_xmlhttpRequest = false: window.GM_xmlhttpRequest = PF_GM_xmlhttpRequest;
  171. // details.synchronous is not supported as Tempermonkey
  172. function PF_GM_xmlhttpRequest(details) {
  173. const xhr = new XMLHttpRequest();
  174. // open request
  175. const openArgs = [details.method, details.url, true];
  176. if (details.user && details.password) {
  177. openArgs.push(details.user);
  178. openArgs.push(details.password);
  179. }
  180. xhr.open.apply(xhr, openArgs);
  181. // set headers
  182. if (details.headers) {
  183. for (const key of Object.keys(details.headers)) {
  184. xhr.setRequestHeader(key, details.headers[key]);
  185. };
  186. };
  187. details.cookie ? xhr.setRequestHeader('cookie', details.cookie) : function () {};
  188. details.anonymous ? xhr.setRequestHeader('cookie', '') : function () {};
  189. // properties
  190. xhr.timeout = details.timeout;
  191. xhr.responseType = details.responseType;
  192. details.overrideMimeType ? xhr.overrideMimeType(details.overrideMimeType) : function () {};
  193. // events
  194. xhr.onabort = details.onabort;
  195. xhr.onerror = details.onerror;
  196. xhr.onloadstart = details.onloadstart;
  197. xhr.onprogress = details.onprogress;
  198. xhr.onreadystatechange = details.onreadystatechange;
  199. xhr.ontimeout = details.ontimeout;
  200. xhr.onload = function (e) {
  201. const response = {
  202. readyState: xhr.readyState,
  203. status: xhr.status,
  204. statusText: xhr.statusText,
  205. responseHeaders: xhr.getAllResponseHeaders(),
  206. response: xhr.response
  207. };
  208. (details.responseType === '' || details.responseType === 'text') ? (response.responseText = xhr.responseText) : function () {};
  209. (details.responseType === '' || details.responseType === 'document') ? (response.responseXML = xhr.responseXML) : function () {};
  210. details.onload(response);
  211. };
  212. // send request
  213. details.data ? xhr.send(details.data) : xhr.send();
  214. return {
  215. abort: xhr.abort
  216. };
  217. };
  218. };
  219. // NOTE: options(arg2) is NOT SUPPORTED! if provided, then will just be skipped.
  220. function GM_openInTab_polyfill() {
  221. typeof (GM_openInTab) === 'function' ? GM_POLYFILLED.GM_openInTab = false: window.GM_openInTab = PF_GM_openInTab;
  222.  
  223. function PF_GM_openInTab(url) {
  224. window.open(url);
  225. };
  226. };
  227. // NOTE: needs to be called in an event handler function, and info(arg2) is NOT SUPPORTED!
  228. function GM_setClipboard_polyfill() {
  229. typeof (GM_setClipboard) === 'function' ? GM_POLYFILLED.GM_setClipboard = false: window.GM_setClipboard = PF_GM_setClipboard;
  230.  
  231. function PF_GM_setClipboard(text) {
  232. // Create a new textarea for copying
  233. const newInput = document.createElement('textarea');
  234. document.body.appendChild(newInput);
  235. newInput.value = text;
  236. newInput.select();
  237. document.execCommand('copy');
  238. document.body.removeChild(newInput);
  239. };
  240. };
  241. return GM_POLYFILLED;
  242. };
  243. // Arguments: level=LogLevel.Info, logContent, asObject=false
  244. // Needs one call "DoLog();" to get it initialized before using it!
  245. function DoLog() {
  246. // Global log levels set
  247. window.LogLevel = {
  248. None: 0,
  249. Error: 1,
  250. Success: 2,
  251. Warning: 3,
  252. Info: 4,
  253. }
  254. window.LogLevelMap = {};
  255. window.LogLevelMap[LogLevel.None] = {
  256. prefix: '',
  257. color: 'color:#ffffff'
  258. }
  259. window.LogLevelMap[LogLevel.Error] = {
  260. prefix: '[Error]',
  261. color: 'color:#ff0000'
  262. }
  263. window.LogLevelMap[LogLevel.Success] = {
  264. prefix: '[Success]',
  265. color: 'color:#00aa00'
  266. }
  267. window.LogLevelMap[LogLevel.Warning] = {
  268. prefix: '[Warning]',
  269. color: 'color:#ffa500'
  270. }
  271. window.LogLevelMap[LogLevel.Info] = {
  272. prefix: '[Info]',
  273. color: 'color:#888888'
  274. }
  275. window.LogLevelMap[LogLevel.Elements] = {
  276. prefix: '[Elements]',
  277. color: 'color:#000000'
  278. }
  279. // Current log level
  280. DoLog.logLevel = window.isPY_DNG ? LogLevel.Info : LogLevel.Warning; // Info Warning Success Error
  281. // Log counter
  282. DoLog.logCount === undefined && (DoLog.logCount = 0);
  283. if (++DoLog.logCount > 512) {
  284. console.clear();
  285. DoLog.logCount = 0;
  286. }
  287. // Get args
  288. let level, logContent, asObject;
  289. switch (arguments.length) {
  290. case 1:
  291. level = LogLevel.Info;
  292. logContent = arguments[0];
  293. asObject = false;
  294. break;
  295. case 2:
  296. level = arguments[0];
  297. logContent = arguments[1];
  298. asObject = false;
  299. break;
  300. case 3:
  301. level = arguments[0];
  302. logContent = arguments[1];
  303. asObject = arguments[2];
  304. break;
  305. default:
  306. level = LogLevel.Info;
  307. logContent = 'DoLog initialized.';
  308. asObject = false;
  309. break;
  310. }
  311. // Log when log level permits
  312. if (level <= DoLog.logLevel) {
  313. let msg = '%c' + LogLevelMap[level].prefix;
  314. let subst = LogLevelMap[level].color;
  315. if (asObject) {
  316. msg += ' %o';
  317. } else {
  318. switch (typeof (logContent)) {
  319. case 'string':
  320. msg += ' %s';
  321. break;
  322. case 'number':
  323. msg += ' %d';
  324. break;
  325. case 'object':
  326. msg += ' %o';
  327. break;
  328. }
  329. }
  330. console.log(msg, subst, logContent);
  331. }
  332. }
  333. })();