wenku8+ loader

load wenku8+ temporarily

当前为 2021-12-23 提交的版本,查看 最新版本

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