常用函数

自用函数 For wenku8++

当前为 2022-08-26 提交的版本,查看 最新版本

此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.gf.qytechs.cn/scripts/449412/1085783/Basic%20Functions.js

  1. /* eslint-disable no-multi-spaces */
  2.  
  3. // ==UserScript==
  4. // @name Basic Functions
  5. // @name:zh-CN 常用函数
  6. // @name:en Basic Functions
  7. // @namespace Wenku8++
  8. // @version 0.4
  9. // @description 自用函数 For wenku8++
  10. // @description:zh-CN 自用函数 For wenku8++
  11. // @description:en Useful functions for myself
  12. // @author PY-DNG
  13. // @license GPL-license
  14. // @grant GM_info
  15. // @grant GM_addStyle
  16. // @grant GM_addElement
  17. // @grant GM_deleteValue
  18. // @grant GM_listValues
  19. // @grant GM_addValueChangeListener
  20. // @grant GM_removeValueChangeListener
  21. // @grant GM_setValue
  22. // @grant GM_getValue
  23. // @grant GM_log
  24. // @grant GM_getResourceText
  25. // @grant GM_getResourceURL
  26. // @grant GM_registerMenuCommand
  27. // @grant GM_unregisterMenuCommand
  28. // @grant GM_openInTab
  29. // @grant GM_xmlhttpRequest
  30. // @grant GM_download
  31. // @grant GM_getTab
  32. // @grant GM_saveTab
  33. // @grant GM_getTabs
  34. // @grant GM_notification
  35. // @grant GM_setClipboard
  36. // @grant GM_info
  37. // @grant unsafeWindow
  38. // ==/UserScript==
  39.  
  40. const LogLevel = {
  41. None: 0,
  42. Error: 1,
  43. Success: 2,
  44. Warning: 3,
  45. Info: 4,
  46. }
  47.  
  48. // Arguments: level=LogLevel.Info, logContent, asObject=false
  49. // Needs one call "DoLog();" to get it initialized before using it!
  50. function DoLog() {
  51. // Get window
  52. const win = (typeof(unsafeWindow) === 'object' && unsafeWindow !== null) ? unsafeWindow : window;
  53.  
  54. const LogLevelMap = {};
  55. LogLevelMap[LogLevel.None] = {
  56. prefix: '',
  57. color: 'color:#ffffff'
  58. }
  59. LogLevelMap[LogLevel.Error] = {
  60. prefix: '[Error]',
  61. color: 'color:#ff0000'
  62. }
  63. LogLevelMap[LogLevel.Success] = {
  64. prefix: '[Success]',
  65. color: 'color:#00aa00'
  66. }
  67. LogLevelMap[LogLevel.Warning] = {
  68. prefix: '[Warning]',
  69. color: 'color:#ffa500'
  70. }
  71. LogLevelMap[LogLevel.Info] = {
  72. prefix: '[Info]',
  73. color: 'color:#888888'
  74. }
  75. LogLevelMap[LogLevel.Elements] = {
  76. prefix: '[Elements]',
  77. color: 'color:#000000'
  78. }
  79.  
  80. // Current log level
  81. DoLog.logLevel = win.isPY_DNG ? LogLevel.Info : LogLevel.Warning; // Info Warning Success Error
  82.  
  83. // Log counter
  84. DoLog.logCount === undefined && (DoLog.logCount = 0);
  85.  
  86. // Get args
  87. let level, logContent, asObject;
  88. switch (arguments.length) {
  89. case 1:
  90. level = LogLevel.Info;
  91. logContent = arguments[0];
  92. asObject = false;
  93. break;
  94. case 2:
  95. level = arguments[0];
  96. logContent = arguments[1];
  97. asObject = false;
  98. break;
  99. case 3:
  100. level = arguments[0];
  101. logContent = arguments[1];
  102. asObject = arguments[2];
  103. break;
  104. default:
  105. level = LogLevel.Info;
  106. logContent = 'DoLog initialized.';
  107. asObject = false;
  108. break;
  109. }
  110.  
  111. // Log when log level permits
  112. if (level <= DoLog.logLevel) {
  113. let msg = '%c' + LogLevelMap[level].prefix + (typeof MODULE_DATA === 'object' ? '[' + MODULE_DATA.name + ']' : '');
  114. let subst = LogLevelMap[level].color;
  115.  
  116. if (asObject) {
  117. msg += ' %o';
  118. } else {
  119. switch (typeof(logContent)) {
  120. case 'string':
  121. msg += ' %s';
  122. break;
  123. case 'number':
  124. msg += ' %d';
  125. break;
  126. case 'object':
  127. msg += ' %o';
  128. break;
  129. }
  130. }
  131.  
  132. if (++DoLog.logCount > 512) {
  133. console.clear();
  134. DoLog.logCount = 0;
  135. }
  136. console.log(msg, subst, logContent);
  137. }
  138. }
  139. DoLog();
  140.  
  141. // Basic functions
  142. // querySelector
  143. function $() {
  144. switch (arguments.length) {
  145. case 2:
  146. return arguments[0].querySelector(arguments[1]);
  147. break;
  148. default:
  149. return document.querySelector(arguments[0]);
  150. }
  151. }
  152. // querySelectorAll
  153. function $All() {
  154. switch (arguments.length) {
  155. case 2:
  156. return arguments[0].querySelectorAll(arguments[1]);
  157. break;
  158. default:
  159. return document.querySelectorAll(arguments[0]);
  160. }
  161. }
  162. // createElement
  163. function $CrE() {
  164. switch (arguments.length) {
  165. case 2:
  166. return arguments[0].createElement(arguments[1]);
  167. break;
  168. default:
  169. return document.createElement(arguments[0]);
  170. }
  171. }
  172. // Object1[prop] ==> Object2[prop]
  173. function copyProp(obj1, obj2, prop) {
  174. obj1.hasOwnProperty(prop) && (obj2[prop] = obj1[prop]);
  175. }
  176. function copyProps(obj1, obj2, props) {
  177. props.forEach((prop) => (copyProp(obj1, obj2, prop)));
  178. }
  179.  
  180. function clearChildNodes(elm) {
  181. for (const el of elm.childNodes) {
  182. elm.removeChild(el);
  183. }
  184. }
  185.  
  186. // Just stopPropagation and preventDefault
  187. function destroyEvent(e) {
  188. if (!e) {
  189. return false;
  190. };
  191. if (!e instanceof Event) {
  192. return false;
  193. };
  194. e.stopPropagation();
  195. e.preventDefault();
  196. }
  197.  
  198. // GM_XHR HOOK: The number of running GM_XHRs in a time must under maxXHR
  199. // Returns the abort function to stop the request anyway(no matter it's still waiting, or requesting)
  200. // (If the request is invalid, such as url === '', will return false and will NOT make this request)
  201. // If the abort function called on a request that is not running(still waiting or finished), there will be NO onabort event
  202. // Requires: function delItem(){...} & function uniqueIDMaker(){...}
  203. function GMXHRHook(maxXHR = 5) {
  204. const GM_XHR = GM_xmlhttpRequest;
  205. const getID = uniqueIDMaker();
  206. let todoList = [],
  207. ongoingList = [];
  208. GM_xmlhttpRequest = safeGMxhr;
  209.  
  210. function safeGMxhr() {
  211. // Get an id for this request, arrange a request object for it.
  212. const id = getID();
  213. const request = {
  214. id: id,
  215. args: arguments,
  216. aborter: null
  217. };
  218.  
  219. // Deal onload function first
  220. dealEndingEvents(request);
  221.  
  222. /* DO NOT DO THIS! KEEP ITS ORIGINAL PROPERTIES!
  223. // Stop invalid requests
  224. if (!validCheck(request)) {
  225. return false;
  226. }
  227. */
  228.  
  229. // Judge if we could start the request now or later?
  230. todoList.push(request);
  231. checkXHR();
  232. return makeAbortFunc(id);
  233.  
  234. // Decrease activeXHRCount while GM_XHR onload;
  235. function dealEndingEvents(request) {
  236. const e = request.args[0];
  237.  
  238. // onload event
  239. const oriOnload = e.onload;
  240. e.onload = function() {
  241. reqFinish(request.id);
  242. checkXHR();
  243. oriOnload ? oriOnload.apply(null, arguments) : function() {};
  244. }
  245.  
  246. // onerror event
  247. const oriOnerror = e.onerror;
  248. e.onerror = function() {
  249. reqFinish(request.id);
  250. checkXHR();
  251. oriOnerror ? oriOnerror.apply(null, arguments) : function() {};
  252. }
  253.  
  254. // ontimeout event
  255. const oriOntimeout = e.ontimeout;
  256. e.ontimeout = function() {
  257. reqFinish(request.id);
  258. checkXHR();
  259. oriOntimeout ? oriOntimeout.apply(null, arguments) : function() {};
  260. }
  261.  
  262. // onabort event
  263. const oriOnabort = e.onabort;
  264. e.onabort = function() {
  265. reqFinish(request.id);
  266. checkXHR();
  267. oriOnabort ? oriOnabort.apply(null, arguments) : function() {};
  268. }
  269. }
  270.  
  271. // Check if the request is invalid
  272. function validCheck(request) {
  273. const e = request.args[0];
  274.  
  275. if (!e.url) {
  276. return false;
  277. }
  278.  
  279. return true;
  280. }
  281.  
  282. // Call a XHR from todoList and push the request object to ongoingList if called
  283. function checkXHR() {
  284. if (ongoingList.length >= maxXHR) {
  285. return false;
  286. };
  287. if (todoList.length === 0) {
  288. return false;
  289. };
  290. const req = todoList.shift();
  291. const reqArgs = req.args;
  292. const aborter = GM_XHR.apply(null, reqArgs);
  293. req.aborter = aborter;
  294. ongoingList.push(req);
  295. return req;
  296. }
  297.  
  298. // Make a function that aborts a certain request
  299. function makeAbortFunc(id) {
  300. return function() {
  301. let i;
  302.  
  303. // Check if the request haven't been called
  304. for (i = 0; i < todoList.length; i++) {
  305. const req = todoList[i];
  306. if (req.id === id) {
  307. // found this request: haven't been called
  308. delItem(todoList, i);
  309. return true;
  310. }
  311. }
  312.  
  313. // Check if the request is running now
  314. for (i = 0; i < ongoingList.length; i++) {
  315. const req = todoList[i];
  316. if (req.id === id) {
  317. // found this request: running now
  318. req.aborter();
  319. reqFinish(id);
  320. checkXHR();
  321. }
  322. }
  323.  
  324. // Oh no, this request is already finished...
  325. return false;
  326. }
  327. }
  328.  
  329. // Remove a certain request from ongoingList
  330. function reqFinish(id) {
  331. let i;
  332. for (i = 0; i < ongoingList.length; i++) {
  333. const req = ongoingList[i];
  334. if (req.id === id) {
  335. ongoingList = delItem(ongoingList, i);
  336. return true;
  337. }
  338. }
  339. return false;
  340. }
  341. }
  342. }
  343.  
  344. // Get a url argument from lacation.href
  345. // also recieve a function to deal the matched string
  346. // returns defaultValue if name not found
  347. // Args: {url=location.href, name, dealFunc=((a)=>{return a;}), defaultValue=null} or 'name'
  348. function getUrlArgv(details) {
  349. typeof(details) === 'string' && (details = {
  350. name: details
  351. });
  352. typeof(details) === 'undefined' && (details = {});
  353. if (!details.name) {
  354. return null;
  355. };
  356.  
  357. const url = details.url ? details.url : location.href;
  358. const name = details.name ? details.name : '';
  359. const dealFunc = details.dealFunc ? details.dealFunc : ((a) => {
  360. return a;
  361. });
  362. const defaultValue = details.defaultValue ? details.defaultValue : null;
  363. const matcher = new RegExp('[\\?&]' + name + '=([^&#]+)');
  364. const result = url.match(matcher);
  365. const argv = result ? dealFunc(result[1]) : defaultValue;
  366.  
  367. return argv;
  368. }
  369.  
  370. // Append a style text to document(<head>) with a <style> element
  371. function addStyle(css, id) {
  372. const style = document.createElement("style");
  373. id && (style.id = id);
  374. style.textContent = css;
  375. for (const elm of document.querySelectorAll('#' + id)) {
  376. elm.parentElement && elm.parentElement.removeChild(elm);
  377. }
  378. document.head.appendChild(style);
  379. }
  380.  
  381. // Save dataURL to file
  382. function saveFile(dataURL, filename) {
  383. const a = document.createElement('a');
  384. a.href = dataURL;
  385. a.download = filename;
  386. a.click();
  387. }
  388.  
  389. // File download function
  390. // details looks like the detail of GM_xmlhttpRequest
  391. // onload function will be called after file saved to disk
  392. function downloadFile(details) {
  393. if (!details.url || !details.name) {
  394. return false;
  395. };
  396.  
  397. // Configure request object
  398. const requestObj = {
  399. url: details.url,
  400. responseType: 'blob',
  401. onload: function(e) {
  402. // Save file
  403. saveFile(URL.createObjectURL(e.response), details.name);
  404.  
  405. // onload callback
  406. details.onload ? details.onload(e) : function() {};
  407. }
  408. }
  409. if (details.onloadstart) {
  410. requestObj.onloadstart = details.onloadstart;
  411. };
  412. if (details.onprogress) {
  413. requestObj.onprogress = details.onprogress;
  414. };
  415. if (details.onerror) {
  416. requestObj.onerror = details.onerror;
  417. };
  418. if (details.onabort) {
  419. requestObj.onabort = details.onabort;
  420. };
  421. if (details.onreadystatechange) {
  422. requestObj.onreadystatechange = details.onreadystatechange;
  423. };
  424. if (details.ontimeout) {
  425. requestObj.ontimeout = details.ontimeout;
  426. };
  427.  
  428. // Send request
  429. GM_xmlhttpRequest(requestObj);
  430. }
  431.  
  432. // get '/' splited API array from a url
  433. function getAPI(url = location.href) {
  434. return url.replace(/https?:\/\/(.*?\.){1,2}.*?\//, '').replace(/\?.*/, '').match(/[^\/]+?(?=(\/|$))/g);
  435. }
  436.  
  437. // get host part from a url(includes '^https://', '/$')
  438. function getHost(url = location.href) {
  439. const match = location.href.match(/https?:\/\/[^\/]+\//);
  440. return match ? match[0] : match;
  441. }
  442.  
  443. function AsyncManager() {
  444. const AM = this;
  445.  
  446. // Ongoing xhr count
  447. this.taskCount = 0;
  448.  
  449. // Whether generate finish events
  450. let finishEvent = false;
  451. Object.defineProperty(this, 'finishEvent', {
  452. configurable: true,
  453. enumerable: true,
  454. get: () => (finishEvent),
  455. set: (b) => {
  456. finishEvent = b;
  457. b && AM.taskCount === 0 && AM.onfinish && AM.onfinish();
  458. }
  459. });
  460.  
  461. // Add one task
  462. this.add = () => (++AM.taskCount);
  463.  
  464. // Finish one task
  465. this.finish = () => ((--AM.taskCount === 0 && AM.finishEvent && AM.onfinish && AM.onfinish(), AM.taskCount));
  466. }
  467.  
  468. // Polyfill String.prototype.replaceAll
  469. // replaceValue does NOT support regexp match groups($1, $2, etc.)
  470. function polyfill_replaceAll() {
  471. String.prototype.replaceAll = String.prototype.replaceAll ? String.prototype.replaceAll : PF_replaceAll;
  472.  
  473. function PF_replaceAll(searchValue, replaceValue) {
  474. const str = String(this);
  475.  
  476. if (searchValue instanceof RegExp) {
  477. const global = RegExp(searchValue, 'g');
  478. if (/\$/.test(replaceValue)) {
  479. console.error('Error: Polyfilled String.protopype.replaceAll does support regexp groups');
  480. };
  481. return str.replace(global, replaceValue);
  482. } else {
  483. return str.split(searchValue).join(replaceValue);
  484. }
  485. }
  486. }
  487.  
  488. function randint(min, max) {
  489. return Math.floor(Math.random() * (max - min + 1)) + min;
  490. }
  491.  
  492. // Del a item from an array using its index. Returns the array but can NOT modify the original array directly!!
  493. function delItem(arr, delIndex) {
  494. arr = arr.slice(0, delIndex).concat(arr.slice(delIndex + 1));
  495. return arr;
  496. }
  497.  
  498. // type: [Error, TypeError]
  499. function Err(msg, type=0) {
  500. throw new [Error, TypeError][type](msg);
  501. }

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址