dev-stuff

7/29/2024, 8:57:58 PM

Dieses Skript sollte nicht direkt installiert werden. Es handelt sich hier um eine Bibliothek für andere Skripte, welche über folgenden Befehl in den Metadaten eines Skriptes eingebunden wird // @require https://update.greatest.deepsurf.us/scripts/502146/1435298/dev-stuff.js

  1. // ==UserScript==
  2. // @name Dev stuff
  3. // @namespace Violentmonkey Scripts
  4. // @match https://*/*
  5. // @grant none
  6. // @version 0.3
  7. // @author Shaun Mitchell
  8. // @description 7/29/2024, 8:57:58 PM
  9. // ==/UserScript==
  10.  
  11. var circularCounter = 10000;
  12. unsafeWindow.circularCounter = circularCounter;
  13.  
  14. function mapToStyle(styles) {
  15. return Object.entries(styles)
  16. .map(([key, value]) => `${key}: ${value};`)
  17. .join(' ');
  18. }
  19.  
  20. class Debugger {
  21. // Set the default name to the name of the loaded script
  22. static name = GM.info.script.name;
  23. static level = "debug";
  24. static levels = ["debug", "info", "warn", "error"];
  25. static styles = {
  26. debug: {
  27. "color": "grey",
  28. },
  29. info: {
  30. "color": "cyan"
  31. },
  32. warn: {
  33. "color": "yellow",
  34. "font-weight": "bold"
  35. },
  36. error: {
  37. "background-color": "red",
  38. "color": "black",
  39. "font-weight": "bold",
  40. "font-size": "1.1em"
  41. }
  42. }
  43.  
  44. static setLevel(level) {
  45. this.level = level;
  46. }
  47.  
  48. static isValidLevel(level) {
  49. let cur_index = -1;
  50. let lvl_index = -1;
  51.  
  52. for (let i = 0; i < this.levels.length; i++) {
  53. let l = this.levels[i];
  54. if (l == this.level) {
  55. cur_index = i;
  56. }
  57. if (l == level) {
  58. lvl_index = i;
  59. }
  60. if (cur_index > -1 && lvl_index > -1) {
  61. break;
  62. }
  63. }
  64.  
  65. return lvl_index >= cur_index;
  66. }
  67.  
  68. static log(level, ...args) {
  69. if (this.isValidLevel(level)) {
  70. const timestamp = new Date().toISOString().replace("T", " ").replace(/\..*/, "")
  71. const style = mapToStyle(this.styles[level]);
  72. console[level](`%c[${this.name}.${level} | ${timestamp}]`, style, ...args);
  73. }
  74. }
  75.  
  76. static debug(...args) { this.log("debug", ...args) }
  77. static info(...args) { this.log("info", ...args) }
  78. static warn(...args) { this.log("warn", ...args) }
  79. static error(...args) { this.log("error", ...args) }
  80. }
  81.  
  82. function debug(...args) { Debugger.debug(...args) }
  83. function info(...args) { Debugger.info(...args) }
  84. function warn(...args) { Debugger.warn(...args) }
  85. function error(...args) { Debugger.error(...args) }
  86.  
  87. function getFunctionArgs(func) {
  88. return func.toString().match(/[^)]+(\([^)]+\))/);
  89. }
  90.  
  91. // attribution:
  92. // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Cyclic_object_value
  93. function getCircularReplacer() {
  94. const ancestors = [];
  95. let i = 0;
  96. Debugger.info("returning circular replacer");
  97. return function(key, value) {
  98. unsafeWindow.lastKey = key;
  99. unsafeWindow.lastValue = value;
  100.  
  101. i += 1;
  102. if (i % unsafeWindow.circularCounter === (unsafeWindow.circularCounter-1)) {
  103. Debugger.info(`processing key ${i}`, key);
  104. }
  105. const valueType = typeof value;
  106.  
  107. if (valueType !== "object" || value === null) {
  108. return value;
  109. }
  110. if (valueType === "function") {
  111. console.info(`skipping function ${key}`);
  112. return `[Function ${key}]`;
  113. }
  114. if (valueType === "object") {
  115. const protoName = value.__proto__.constructor.name;
  116. // Skip anything except arrays and associative arrays and ''
  117. if (protoName !== "Object" && protoName !== "Array" && protoName !== '') {
  118. console.info(`skipping object ${key} ${value.__proto__}`);
  119. return `[${value.__proto__.constructor.name} ${key}]`
  120. }
  121. }
  122.  
  123. // `this` is the object that value is contained in,
  124. // i.e., its direct parent.
  125. while (ancestors.length > 0 && ancestors.at(-1) !== this) {
  126. ancestors.pop();
  127. }
  128.  
  129. if (ancestors.includes(value)) {
  130. return "[Circular]";
  131. }
  132.  
  133. ancestors.push(value);
  134. return value;
  135. };
  136. }
  137.  
  138. function asyncStringify(str, indent) {
  139. return new Promise((resolve, reject) => {
  140. resolve(JSON.stringify(str, getCircularReplacer(), " ".repeat(indent)));
  141. });
  142. }
  143.  
  144. function downloadObjectJSON(object, filename, indent) {
  145. const default_filename = "object-blob.js.json";
  146. const default_indent = 4;
  147.  
  148. // Allow for intelligent argument parsing for, e.g.: `downloadObjectBlob(foo, indent=4)`
  149. args = [object, filename, indent];
  150. filename = undefined;
  151. indent = undefined;
  152. for (const arg of args) {
  153. switch (typeof arg) {
  154. case 'number':
  155. indent = arg;
  156. break;
  157. case 'string':
  158. filename = arg;
  159. break;
  160. case 'object':
  161. object = arg;
  162. break;
  163. case 'undefined':
  164. break;
  165. default:
  166. error(`error: unexpected type for ${arg}`);
  167. return null
  168. }
  169. }
  170. if (filename === undefined) { filename = default_filename }
  171. if (indent === undefined) { indent = default_indent }
  172. asyncStringify(object, indent)
  173. .then(function (text) {
  174. downloadBlob(text, filename)
  175. })
  176. .catch(function (reason) {
  177. unsafeWindow.reason = reason;
  178. Debugger.error("download failed", reason);
  179. })
  180. }
  181.  
  182. function downloadBlob(text, filename) {
  183. info(`downloading text=${text.length} bytes, filename=${filename}`)
  184. blob = new Blob([text]);
  185. debug(`blob=<Blob size=${blob.size}>, filename=${filename}`);
  186. var a = window.document.createElement('a');
  187. a.href = window.URL.createObjectURL(blob, {type: 'text/json'});
  188. a.download = filename;
  189.  
  190. // Append anchor to body.
  191. document.body.appendChild(a);
  192. a.dispatchEvent(new MouseEvent('click'));
  193.  
  194. // Remove anchor from body
  195. document.body.removeChild(a);
  196. }
  197.  
  198. /* Export stuff */
  199.  
  200. unsafeWindow.mapToStyle = mapToStyle;
  201. unsafeWindow.Debugger = Debugger;
  202. unsafeWindow.getCircularReplacer = getCircularReplacer;
  203. unsafeWindow.downloadObjectJSON = downloadObjectJSON;
  204. unsafeWindow.asyncStringify = asyncStringify;
  205. unsafeWindow.downloadBlob = downloadBlob;
  206. unsafeWindow.debug = debug;
  207. unsafeWindow.info = info;
  208. unsafeWindow.warn = warn;
  209. unsafeWindow.error = error;
  210. unsafeWindow.getFunctionArgs = getFunctionArgs;