wenku8+ loader

load wenku8+ temporarily

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

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

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