常用函数

自用函数 For wenku8++

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

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

QingJ © 2025

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