DKK Torn Utilities DEVELOPMENT

Commonly used functions in my Torn scripts.

当前为 2019-11-18 提交的版本,查看 最新版本

此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.cn-greasyfork.org/scripts/392610/750713/DKK%20Torn%20Utilities%20DEVELOPMENT.js

  1. // ==UserScript==
  2. // @name DKK Torn Utilities DEVELOPMENT
  3. // @description Commonly used functions in my Torn scripts.
  4. // @version 1.8.7
  5. // @exclude *
  6. // @namespace https://openuserjs.org/users/DeKleineKobini
  7. // @homepageURL https://www.torn.com/forums.php#/p=threads&t=16110079
  8. // ==/UserScript==
  9.  
  10. /* Script Setup */
  11. addFunctions();
  12.  
  13. function addFunctions() {
  14. // formating for numbers
  15. Number.prototype.format = function(n, x) {
  16. var re = '\\d(?=(\\d{' + (x || 3) + '})+' + (n > 0 ? '\\.' : '$') + ')';
  17. return this.toFixed(Math.max(0, ~~n)).replace(new RegExp(re, 'g'), '$&,');
  18. };
  19. }
  20.  
  21. function initScript2(options) {
  22. // some easy name conversion
  23. if (options.name) {
  24. if (!options.id) options.id = replaceAll(options.name, " ", "").toLowerCase();
  25. if (!options.abbr) options.abbr = "";
  26. }
  27. if (!options.logging) {
  28. options.logging = "ERROR";
  29. }
  30. // actually init
  31. if (options.api){
  32. if (options.apikey) _setAPI(options.apikey);
  33. requireAPI();
  34. }
  35. if (!window.scripts) window.scripts = { ids: [] };
  36. window.scripts.ids.push(options.id);
  37. window.scripts[options.id] = {
  38. name: options.name,
  39. abbr: options.short,
  40. api: options.api ? true : false
  41. };
  42. loggingLevel = LOGGING_LEVELS[options.logging];
  43. }
  44.  
  45. function initScript(id, name, short, api, key) {
  46. if (!window.scripts) window.scripts = {
  47. ids: []
  48. };
  49. window.scripts.ids.push(id);
  50. window.scripts[id] = {
  51. name: name,
  52. short: short,
  53. api: api ? true : false
  54. };
  55.  
  56. prefix = "[" + short + "]";
  57.  
  58. if (api) {
  59. if (key) _setAPI(key);
  60. requireAPI();
  61. }
  62. }
  63.  
  64. /* Messaging */
  65. var prefix = "[DKK]";
  66.  
  67. const LOGGING_LEVELS = {
  68. FATAL: 0,
  69. ERROR: 1,
  70. WARN: 2,
  71. INFO: 3,
  72. DEBUG: 4,
  73. TRACE: 5,
  74. ALL: 6,
  75. OFF: 7
  76. }
  77. var loggingLevel = LOGGING_LEVELS.ERROR;
  78.  
  79. function log(level, message) {
  80. if (loggingLevel < LOGGING_LEVELS[level]) return false;
  81. let msg = `${prefix}[${level}] ${message}`;
  82. console.log(msg, arguments);
  83. return true;
  84. }
  85. function debug(message) {
  86. log("DEBUG", message);
  87. }
  88.  
  89. /* Torn API */
  90. var apiKey;
  91.  
  92. const API_LENGTH = 16;
  93. const STORAGE_LOCATION = "dkkutils_apikey";
  94.  
  95. function requireAPI() {
  96. debug("Require API")
  97. apiKey = getAPI();
  98. if (!_isValidAPIKey(apiKey)) {
  99. debug("Require API - no valid api found")
  100. let response = prompt("Please enter your API key: ");
  101. if (_isValidAPIKey(response)) {
  102. setAPI(response);
  103. } else {
  104. debug("Require API - no valid api given")
  105. alert("Given API key was not valid, script might not work.\nRefresh to try again.")
  106. }
  107. }
  108. }
  109.  
  110. function _isValidAPIKey(key) {
  111. if (!key) key = apiKey;
  112.  
  113. if (!key || key === undefined || key == "undefined" || key === null || key == "null" || key === "") return false;
  114. if (key.length != API_LENGTH) return false;
  115.  
  116. return true;
  117. }
  118.  
  119. function sendAPIRequest(part, id, selections, key) {
  120. debug(`Sending API request to ${part}/${selections} on id ${id}`);
  121. if (!key) key = apiKey;
  122.  
  123. return new Promise((resolve, reject) => {
  124. GM_xmlhttpRequest({
  125. method: "GET",
  126. url: `https://api.torn.com/${part}/${id}?selections=${selections}&key=${key}`,
  127. onreadystatechange: function(res) {
  128. if (res.readyState > 3 && res.status === 200) {
  129. debug("API response received.")
  130. if (!isJsonString(res.responseText)) {
  131. reject("JSON Error", res.responseText);
  132. return;
  133. }
  134.  
  135. var json = JSON.parse(res.responseText);
  136. resolve(json);
  137.  
  138. if (json.error) {
  139. var code = json.error.code;
  140. debug("API Error: " + code)
  141. if (code == 2) setAPI(null);
  142. }
  143. }
  144. },
  145. onerror: function(err) {
  146. console.log(err);
  147. reject('XHR error.');
  148. }
  149. })
  150. }).catch(e => {
  151. console.log(e);
  152. });
  153. }
  154.  
  155. function setAPI(key) {
  156. if (!_isValidAPIKey(key)) {
  157. localStorage.removeItem(STORAGE_LOCATION);
  158. debug("Removed key from storage.")
  159. } else {
  160. localStorage.setItem(STORAGE_LOCATION, key);
  161. debug(`Added key '${key}' to storage. '${localStorage.getItem(STORAGE_LOCATION)}'`)
  162. }
  163.  
  164. apiKey = key;
  165. }
  166.  
  167. function _setAPI(key) {
  168. if (!_isValidAPIKey(key)) localStorage.removeItem(STORAGE_LOCATION);
  169. else localStorage.setItem(STORAGE_LOCATION, key);
  170.  
  171. apiKey = key;
  172. }
  173.  
  174. function getAPI() {
  175. let utilsKey = localStorage.getItem(STORAGE_LOCATION);
  176. if (_isValidAPIKey(utilsKey)) {
  177. debug("getAPI - from own storage '" + utilsKey.length + "'");
  178. return utilsKey;
  179. } else {
  180. debug("getAPI - from own storage is invalid '" + utilsKey + "'");
  181. }
  182.  
  183. let key = localStorage.getItem("_apiKey") || localStorage.getItem("x_apikey") || localStorage.getItem("jebster.torn") || localStorage.getItem("TornApiKey");
  184.  
  185. if (_isValidAPIKey(key)) {
  186. debug("getAPI - from other storage");
  187. setAPI(key);
  188. } else if (localStorage.getItem("_apiKey")) {
  189. debug("getAPI - removed 1");
  190. localStorage.removeItem("_apiKey");
  191. } else if (localStorage.getItem("x_apikey")) {
  192. debug("getAPI - removed 2");
  193. localStorage.removeItem("x_apikey");
  194. } else if (localStorage.getItem("jebster.torn")) {
  195. debug("getAPI - removed 3");
  196. localStorage.removeItem("jebster.torn");
  197. } else if (localStorage.getItem("_apiKey")) {
  198. debug("getAPI - removed 4");
  199. localStorage.removeItem("TornApiKey");
  200. }
  201.  
  202. return utilsKey;
  203. }
  204.  
  205. /* Torn General*/
  206. function getScriptUser() {
  207. let body = $("body")
  208. let contentWrapper = $("#mainContainer > .content-wrapper");
  209.  
  210. return {
  211. isJailed: body.hasClass("jail"),
  212. isHospitalized: body.hasClass("hospital"),
  213. isTravelling: contentWrapper.hasClass("travelling")
  214. }
  215. }
  216.  
  217. /* Networking */
  218.  
  219. function xhrIntercept(callback) {
  220. let oldXHROpen = window.XMLHttpRequest.prototype.open;
  221. window.XMLHttpRequest.prototype.open = function() {
  222. this.addEventListener('readystatechange', function() {
  223. if (this.readyState > 3 && this.status == 200) {
  224. var page = this.responseURL.substring(this.responseURL.indexOf("torn.com/") + "torn.com/".length, this.responseURL.indexOf(".php"));
  225.  
  226. var json, uri;
  227. if (isJsonString(this.response)) json = JSON.parse(this.response);
  228. else uri = getUrlParams(this.responseURL);
  229.  
  230. callback(page, json, uri, this);
  231. }
  232. });
  233.  
  234. return oldXHROpen.apply(this, arguments);
  235. }
  236. }
  237.  
  238. function ajax(callback) {
  239. $(document).ajaxComplete((event, xhr, settings) => {
  240. if (xhr.readyState > 3 && xhr.status == 200) {
  241. if (settings.url.indexOf("torn.com/") < 0) settings.url = "torn.com" + (settings.url.startsWith("/") ? "" : "/") + settings.url;
  242. var page = settings.url.substring(settings.url.indexOf("torn.com/") + "torn.com/".length, settings.url.indexOf(".php"));
  243.  
  244. var json, uri;
  245. if (isJsonString(xhr.responseText)) json = JSON.parse(xhr.responseText);
  246. else uri = getUrlParams(settings.url);
  247.  
  248. callback(page, json, uri, xhr, settings);
  249. }
  250. });
  251. }
  252.  
  253. function interceptFetch(url, callback) {
  254. unsafeWindow.fetch = async (input, options) => {
  255. const response = await fetch(input, options)
  256.  
  257. if (response.url.startsWith("https://www.torn.com/" + url)) {
  258. let res = response.clone();
  259.  
  260. Promise.resolve(res.json().then((json) => callback(json, res.url)));
  261. }
  262.  
  263. return response;
  264. }
  265. }
  266.  
  267. /* DOM */
  268. function observeMutationsFull(root, callback, options) {
  269. if (!options) options = {
  270. childList: true
  271. };
  272.  
  273. new MutationObserver(callback).observe(root, options);
  274. }
  275.  
  276. function observeMutations(root, selector, runOnce, callback, options, callbackRemoval) {
  277. var ran = false;
  278. observeMutationsFull(root, function(mutations, me) {
  279. var check = $(selector);
  280.  
  281. if (check.length) {
  282. if (runOnce) me.disconnect();
  283.  
  284. ran = true;
  285. callback(mutations, me);
  286. } else if (ran) {
  287. ran = false;
  288. if (callbackRemoval) callbackRemoval(mutations, me);
  289. }
  290. }, options);
  291. }
  292.  
  293. /* Caching */
  294. function setCache(key, value, time, sub) {
  295. var end = time == -1 ? -1 : Date.now() + time;
  296.  
  297. var obj = sub ? value : {
  298. value: value,
  299. end: Date.now() + time
  300. };
  301.  
  302. GM_setValue(key, JSON.stringify(obj));
  303. }
  304.  
  305. async function getCache(key, subbed) {
  306. let _obj = await GM_getValue(key, subbed ? "{}" : "{\"end\":0}");
  307. let obj = JSON.parse(_obj);
  308.  
  309. var end = obj.end;
  310. if (!end || end == -1 || end > Date.now())
  311. return subbed ? obj : obj.value;
  312.  
  313. return undefined;
  314. }
  315.  
  316. function getSubCache(cache, id) {
  317. if (cache[id]) {
  318. var end = cache[id].end;
  319. if (end == -1 || end > Date.now())
  320. return cache[id].value;
  321. }
  322.  
  323. return undefined;
  324. }
  325.  
  326. /* General Utilities */
  327.  
  328. function isJsonString(str) {
  329. if (!str || str == "") return false;
  330. try {
  331. JSON.parse(str);
  332. } catch (e) {
  333. return false;
  334. }
  335. return true;
  336. }
  337.  
  338. /**
  339. * JavaScript Get URL Parameter (https://www.kevinleary.net/javascript-get-url-parameters/)
  340. *
  341. * @param String prop The specific URL parameter you want to retreive the value for
  342. * @return String|Object If prop is provided a string value is returned, otherwise an object of all properties is returned
  343. */
  344. function getUrlParams(url, prop) {
  345. var params = {};
  346. var search = decodeURIComponent(((url) ? url : window.location.href).slice(window.location.href.indexOf('?') + 1));
  347. var definitions = search.split('&');
  348.  
  349. definitions.forEach(function(val, key) {
  350. var parts = val.split('=', 2);
  351. params[parts[0]] = parts[1];
  352. });
  353.  
  354. return (prop && prop in params) ? params[prop] : params;
  355. }
  356.  
  357. function getSpecialSearch() {
  358. let hash = window.location.hash;
  359.  
  360. hash = hash.replace("#/", "?");
  361. hash = hash.replace("#!", "?");
  362.  
  363. return hash;
  364. }
  365.  
  366. function replaceAll(str, text, replace) {
  367. while (contains(str, text)) {
  368. str = str.replace(text, replace);
  369. }
  370. return str;
  371. }
  372.  
  373. function contains(str, text) {
  374. return str.indexOf(text) >= 0;
  375. }
  376.  
  377. function stripHtml(html) {
  378. var tmp = document.createElement("DIV");
  379. tmp.innerHTML = html;
  380. var stripped = tmp.textContent || tmp.innerText || "";
  381.  
  382. stripped = replaceAll(stripped, "\n", "");
  383. stripped = replaceAll(stripped, "\t", "");
  384. stripped = replaceAll(stripped, " ", " ");
  385.  
  386. return stripped;
  387. }
  388.  
  389. function getNewDay() {
  390. let now = new Date();
  391. let newDay = new Date(Date.UTC(now.getFullYear(), now.getMonth(), now.getDate() - 1, 0, 0, 0));
  392.  
  393. if (Date.now() >= newDay.getTime()) newDay.setUTCDate(newDay.getUTCDate() + 1);
  394. if (Date.now() >= newDay.getTime()) newDay.setUTCDate(newDay.getUTCDate() + 1);
  395.  
  396. return newDay;
  397. }
  398.  
  399. function getMillisUntilNewDay() {
  400. return getNewDay().getTime() - Date.now();
  401. }
  402.  
  403. function _isMobile() {
  404. return navigator.userAgent.match(/Android/i) ||
  405. navigator.userAgent.match(/webOS/i) ||
  406. navigator.userAgent.match(/iPhone/i) ||
  407. navigator.userAgent.match(/iPad/i) ||
  408. navigator.userAgent.match(/iPod/i) ||
  409. navigator.userAgent.match(/BlackBerry/i) ||
  410. navigator.userAgent.match(/Windows Phone/i);
  411. }
  412.  
  413. function runOnEvent(funct, event, runBefore) {
  414. if (runBefore) funct();
  415.  
  416. $(window).bind(event, function() {
  417. funct();
  418. });
  419. }
  420.  
  421. function timeSince(timeStamp) {
  422. let now = new Date();
  423. let secondsPast = (now.getTime() - timeStamp.getTime()) / 1000;
  424.  
  425. if (secondsPast < 60) return parseInt(secondsPast) + 's';
  426. else if (secondsPast < 3600) return parseInt(secondsPast / 60) + 'm';
  427. else if (secondsPast <= 86400) return parseInt(secondsPast / 3600) + 'h';
  428. else if (secondsPast > 86400) {
  429. let day = timeStamp.getDate();
  430. let month = timeStamp.toDateString().match(/ [a-zA-Z]*/)[0].replace(" ", "");
  431. let year = timeStamp.getFullYear() == now.getFullYear() ? "" : " " + timeStamp.getFullYear();
  432. return day + " " + month + year;
  433. }
  434. }