您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Some small QOL improvements to ninja.io!
// ==UserScript== // @name Ninja.io Utils // @namespace https://itsmeow.cat // @version 1.32 // @description Some small QOL improvements to ninja.io! // @author Meow // @match https://ninja.io/* // @match https://ninja.io // @match http://ninja.io/* // @match http://ninja.io // @match https://*.ninja.io/* // @match http://*.ninja.io/* // @match https://*.ninja.io // @match http://*.ninja.io // @icon https://www.google.com/s2/favicons?domain=ninja.io // @require https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.5.1/socket.io.min.js // @homepageURL https://nutils.itsmeow.cat // @supportURL https://github.com/itzTheMeow/NinjaIOUtils/issues // @grant none // ==/UserScript== /* This file was generated automatically by a build script! If you want to see the source code, view it on github: > https://github.com/itzTheMeow/NinjaIOUtils */ (() => { var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, { get: (a, b) => (typeof require !== "undefined" ? require : a)[b] }) : x)(function(x) { if (typeof require !== "undefined") return require.apply(this, arguments); throw new Error('Dynamic require of "' + x + '" is not supported'); }); var __commonJS = (cb, mod) => function __require2() { return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod)); // node_modules/localforage/dist/localforage.js var require_localforage = __commonJS({ "node_modules/localforage/dist/localforage.js"(exports, module) { (function(f) { if (typeof exports === "object" && typeof module !== "undefined") { module.exports = f(); } else if (typeof define === "function" && define.amd) { define([], f); } else { var g; if (typeof window !== "undefined") { g = window; } else if (typeof global !== "undefined") { g = global; } else if (typeof self !== "undefined") { g = self; } else { g = this; } g.localforage = f(); } })(function() { var define2, module2, exports2; return function e(t, n, r) { function s(o2, u) { if (!n[o2]) { if (!t[o2]) { var a = typeof __require == "function" && __require; if (!u && a) return a(o2, true); if (i) return i(o2, true); var f = new Error("Cannot find module '" + o2 + "'"); throw f.code = "MODULE_NOT_FOUND", f; } var l = n[o2] = { exports: {} }; t[o2][0].call(l.exports, function(e2) { var n2 = t[o2][1][e2]; return s(n2 ? n2 : e2); }, l, l.exports, e, t, n, r); } return n[o2].exports; } var i = typeof __require == "function" && __require; for (var o = 0; o < r.length; o++) s(r[o]); return s; }({ 1: [function(_dereq_, module3, exports3) { (function(global2) { "use strict"; var Mutation = global2.MutationObserver || global2.WebKitMutationObserver; var scheduleDrain; { if (Mutation) { var called = 0; var observer = new Mutation(nextTick); var element = global2.document.createTextNode(""); observer.observe(element, { characterData: true }); scheduleDrain = function() { element.data = called = ++called % 2; }; } else if (!global2.setImmediate && typeof global2.MessageChannel !== "undefined") { var channel = new global2.MessageChannel(); channel.port1.onmessage = nextTick; scheduleDrain = function() { channel.port2.postMessage(0); }; } else if ("document" in global2 && "onreadystatechange" in global2.document.createElement("script")) { scheduleDrain = function() { var scriptEl = global2.document.createElement("script"); scriptEl.onreadystatechange = function() { nextTick(); scriptEl.onreadystatechange = null; scriptEl.parentNode.removeChild(scriptEl); scriptEl = null; }; global2.document.documentElement.appendChild(scriptEl); }; } else { scheduleDrain = function() { setTimeout(nextTick, 0); }; } } var draining; var queue = []; function nextTick() { draining = true; var i, oldQueue; var len = queue.length; while (len) { oldQueue = queue; queue = []; i = -1; while (++i < len) { oldQueue[i](); } len = queue.length; } draining = false; } module3.exports = immediate; function immediate(task) { if (queue.push(task) === 1 && !draining) { scheduleDrain(); } } }).call(this, typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}); }, {}], 2: [function(_dereq_, module3, exports3) { "use strict"; var immediate = _dereq_(1); function INTERNAL() { } var handlers = {}; var REJECTED = ["REJECTED"]; var FULFILLED = ["FULFILLED"]; var PENDING = ["PENDING"]; module3.exports = Promise2; function Promise2(resolver) { if (typeof resolver !== "function") { throw new TypeError("resolver must be a function"); } this.state = PENDING; this.queue = []; this.outcome = void 0; if (resolver !== INTERNAL) { safelyResolveThenable(this, resolver); } } Promise2.prototype["catch"] = function(onRejected) { return this.then(null, onRejected); }; Promise2.prototype.then = function(onFulfilled, onRejected) { if (typeof onFulfilled !== "function" && this.state === FULFILLED || typeof onRejected !== "function" && this.state === REJECTED) { return this; } var promise = new this.constructor(INTERNAL); if (this.state !== PENDING) { var resolver = this.state === FULFILLED ? onFulfilled : onRejected; unwrap(promise, resolver, this.outcome); } else { this.queue.push(new QueueItem(promise, onFulfilled, onRejected)); } return promise; }; function QueueItem(promise, onFulfilled, onRejected) { this.promise = promise; if (typeof onFulfilled === "function") { this.onFulfilled = onFulfilled; this.callFulfilled = this.otherCallFulfilled; } if (typeof onRejected === "function") { this.onRejected = onRejected; this.callRejected = this.otherCallRejected; } } QueueItem.prototype.callFulfilled = function(value) { handlers.resolve(this.promise, value); }; QueueItem.prototype.otherCallFulfilled = function(value) { unwrap(this.promise, this.onFulfilled, value); }; QueueItem.prototype.callRejected = function(value) { handlers.reject(this.promise, value); }; QueueItem.prototype.otherCallRejected = function(value) { unwrap(this.promise, this.onRejected, value); }; function unwrap(promise, func, value) { immediate(function() { var returnValue; try { returnValue = func(value); } catch (e) { return handlers.reject(promise, e); } if (returnValue === promise) { handlers.reject(promise, new TypeError("Cannot resolve promise with itself")); } else { handlers.resolve(promise, returnValue); } }); } handlers.resolve = function(self2, value) { var result = tryCatch(getThen, value); if (result.status === "error") { return handlers.reject(self2, result.value); } var thenable = result.value; if (thenable) { safelyResolveThenable(self2, thenable); } else { self2.state = FULFILLED; self2.outcome = value; var i = -1; var len = self2.queue.length; while (++i < len) { self2.queue[i].callFulfilled(value); } } return self2; }; handlers.reject = function(self2, error) { self2.state = REJECTED; self2.outcome = error; var i = -1; var len = self2.queue.length; while (++i < len) { self2.queue[i].callRejected(error); } return self2; }; function getThen(obj) { var then = obj && obj.then; if (obj && (typeof obj === "object" || typeof obj === "function") && typeof then === "function") { return function appyThen() { then.apply(obj, arguments); }; } } function safelyResolveThenable(self2, thenable) { var called = false; function onError(value) { if (called) { return; } called = true; handlers.reject(self2, value); } function onSuccess(value) { if (called) { return; } called = true; handlers.resolve(self2, value); } function tryToUnwrap() { thenable(onSuccess, onError); } var result = tryCatch(tryToUnwrap); if (result.status === "error") { onError(result.value); } } function tryCatch(func, value) { var out = {}; try { out.value = func(value); out.status = "success"; } catch (e) { out.status = "error"; out.value = e; } return out; } Promise2.resolve = resolve; function resolve(value) { if (value instanceof this) { return value; } return handlers.resolve(new this(INTERNAL), value); } Promise2.reject = reject; function reject(reason) { var promise = new this(INTERNAL); return handlers.reject(promise, reason); } Promise2.all = all; function all(iterable) { var self2 = this; if (Object.prototype.toString.call(iterable) !== "[object Array]") { return this.reject(new TypeError("must be an array")); } var len = iterable.length; var called = false; if (!len) { return this.resolve([]); } var values = new Array(len); var resolved = 0; var i = -1; var promise = new this(INTERNAL); while (++i < len) { allResolver(iterable[i], i); } return promise; function allResolver(value, i2) { self2.resolve(value).then(resolveFromAll, function(error) { if (!called) { called = true; handlers.reject(promise, error); } }); function resolveFromAll(outValue) { values[i2] = outValue; if (++resolved === len && !called) { called = true; handlers.resolve(promise, values); } } } } Promise2.race = race; function race(iterable) { var self2 = this; if (Object.prototype.toString.call(iterable) !== "[object Array]") { return this.reject(new TypeError("must be an array")); } var len = iterable.length; var called = false; if (!len) { return this.resolve([]); } var i = -1; var promise = new this(INTERNAL); while (++i < len) { resolver(iterable[i]); } return promise; function resolver(value) { self2.resolve(value).then(function(response) { if (!called) { called = true; handlers.resolve(promise, response); } }, function(error) { if (!called) { called = true; handlers.reject(promise, error); } }); } } }, { "1": 1 }], 3: [function(_dereq_, module3, exports3) { (function(global2) { "use strict"; if (typeof global2.Promise !== "function") { global2.Promise = _dereq_(2); } }).call(this, typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}); }, { "2": 2 }], 4: [function(_dereq_, module3, exports3) { "use strict"; var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function(obj) { return typeof obj; } : function(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function getIDB() { try { if (typeof indexedDB !== "undefined") { return indexedDB; } if (typeof webkitIndexedDB !== "undefined") { return webkitIndexedDB; } if (typeof mozIndexedDB !== "undefined") { return mozIndexedDB; } if (typeof OIndexedDB !== "undefined") { return OIndexedDB; } if (typeof msIndexedDB !== "undefined") { return msIndexedDB; } } catch (e) { return; } } var idb = getIDB(); function isIndexedDBValid() { try { if (!idb || !idb.open) { return false; } var isSafari = typeof openDatabase !== "undefined" && /(Safari|iPhone|iPad|iPod)/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent) && !/BlackBerry/.test(navigator.platform); var hasFetch = typeof fetch === "function" && fetch.toString().indexOf("[native code") !== -1; return (!isSafari || hasFetch) && typeof indexedDB !== "undefined" && typeof IDBKeyRange !== "undefined"; } catch (e) { return false; } } function createBlob(parts, properties) { parts = parts || []; properties = properties || {}; try { return new Blob(parts, properties); } catch (e) { if (e.name !== "TypeError") { throw e; } var Builder = typeof BlobBuilder !== "undefined" ? BlobBuilder : typeof MSBlobBuilder !== "undefined" ? MSBlobBuilder : typeof MozBlobBuilder !== "undefined" ? MozBlobBuilder : WebKitBlobBuilder; var builder = new Builder(); for (var i = 0; i < parts.length; i += 1) { builder.append(parts[i]); } return builder.getBlob(properties.type); } } if (typeof Promise === "undefined") { _dereq_(3); } var Promise$1 = Promise; function executeCallback(promise, callback) { if (callback) { promise.then(function(result) { callback(null, result); }, function(error) { callback(error); }); } } function executeTwoCallbacks(promise, callback, errorCallback) { if (typeof callback === "function") { promise.then(callback); } if (typeof errorCallback === "function") { promise["catch"](errorCallback); } } function normalizeKey(key2) { if (typeof key2 !== "string") { console.warn(key2 + " used as a key, but it is not a string."); key2 = String(key2); } return key2; } function getCallback() { if (arguments.length && typeof arguments[arguments.length - 1] === "function") { return arguments[arguments.length - 1]; } } var DETECT_BLOB_SUPPORT_STORE = "local-forage-detect-blob-support"; var supportsBlobs = void 0; var dbContexts = {}; var toString = Object.prototype.toString; var READ_ONLY = "readonly"; var READ_WRITE = "readwrite"; function _binStringToArrayBuffer(bin) { var length2 = bin.length; var buf = new ArrayBuffer(length2); var arr = new Uint8Array(buf); for (var i = 0; i < length2; i++) { arr[i] = bin.charCodeAt(i); } return buf; } function _checkBlobSupportWithoutCaching(idb2) { return new Promise$1(function(resolve) { var txn = idb2.transaction(DETECT_BLOB_SUPPORT_STORE, READ_WRITE); var blob = createBlob([""]); txn.objectStore(DETECT_BLOB_SUPPORT_STORE).put(blob, "key"); txn.onabort = function(e) { e.preventDefault(); e.stopPropagation(); resolve(false); }; txn.oncomplete = function() { var matchedChrome = navigator.userAgent.match(/Chrome\/(\d+)/); var matchedEdge = navigator.userAgent.match(/Edge\//); resolve(matchedEdge || !matchedChrome || parseInt(matchedChrome[1], 10) >= 43); }; })["catch"](function() { return false; }); } function _checkBlobSupport(idb2) { if (typeof supportsBlobs === "boolean") { return Promise$1.resolve(supportsBlobs); } return _checkBlobSupportWithoutCaching(idb2).then(function(value) { supportsBlobs = value; return supportsBlobs; }); } function _deferReadiness(dbInfo) { var dbContext = dbContexts[dbInfo.name]; var deferredOperation = {}; deferredOperation.promise = new Promise$1(function(resolve, reject) { deferredOperation.resolve = resolve; deferredOperation.reject = reject; }); dbContext.deferredOperations.push(deferredOperation); if (!dbContext.dbReady) { dbContext.dbReady = deferredOperation.promise; } else { dbContext.dbReady = dbContext.dbReady.then(function() { return deferredOperation.promise; }); } } function _advanceReadiness(dbInfo) { var dbContext = dbContexts[dbInfo.name]; var deferredOperation = dbContext.deferredOperations.pop(); if (deferredOperation) { deferredOperation.resolve(); return deferredOperation.promise; } } function _rejectReadiness(dbInfo, err) { var dbContext = dbContexts[dbInfo.name]; var deferredOperation = dbContext.deferredOperations.pop(); if (deferredOperation) { deferredOperation.reject(err); return deferredOperation.promise; } } function _getConnection(dbInfo, upgradeNeeded) { return new Promise$1(function(resolve, reject) { dbContexts[dbInfo.name] = dbContexts[dbInfo.name] || createDbContext(); if (dbInfo.db) { if (upgradeNeeded) { _deferReadiness(dbInfo); dbInfo.db.close(); } else { return resolve(dbInfo.db); } } var dbArgs = [dbInfo.name]; if (upgradeNeeded) { dbArgs.push(dbInfo.version); } var openreq = idb.open.apply(idb, dbArgs); if (upgradeNeeded) { openreq.onupgradeneeded = function(e) { var db = openreq.result; try { db.createObjectStore(dbInfo.storeName); if (e.oldVersion <= 1) { db.createObjectStore(DETECT_BLOB_SUPPORT_STORE); } } catch (ex) { if (ex.name === "ConstraintError") { console.warn('The database "' + dbInfo.name + '" has been upgraded from version ' + e.oldVersion + " to version " + e.newVersion + ', but the storage "' + dbInfo.storeName + '" already exists.'); } else { throw ex; } } }; } openreq.onerror = function(e) { e.preventDefault(); reject(openreq.error); }; openreq.onsuccess = function() { var db = openreq.result; db.onversionchange = function(e) { e.target.close(); }; resolve(db); _advanceReadiness(dbInfo); }; }); } function _getOriginalConnection(dbInfo) { return _getConnection(dbInfo, false); } function _getUpgradedConnection(dbInfo) { return _getConnection(dbInfo, true); } function _isUpgradeNeeded(dbInfo, defaultVersion) { if (!dbInfo.db) { return true; } var isNewStore = !dbInfo.db.objectStoreNames.contains(dbInfo.storeName); var isDowngrade = dbInfo.version < dbInfo.db.version; var isUpgrade = dbInfo.version > dbInfo.db.version; if (isDowngrade) { if (dbInfo.version !== defaultVersion) { console.warn('The database "' + dbInfo.name + `" can't be downgraded from version ` + dbInfo.db.version + " to version " + dbInfo.version + "."); } dbInfo.version = dbInfo.db.version; } if (isUpgrade || isNewStore) { if (isNewStore) { var incVersion = dbInfo.db.version + 1; if (incVersion > dbInfo.version) { dbInfo.version = incVersion; } } return true; } return false; } function _encodeBlob(blob) { return new Promise$1(function(resolve, reject) { var reader = new FileReader(); reader.onerror = reject; reader.onloadend = function(e) { var base64 = btoa(e.target.result || ""); resolve({ __local_forage_encoded_blob: true, data: base64, type: blob.type }); }; reader.readAsBinaryString(blob); }); } function _decodeBlob(encodedBlob) { var arrayBuff = _binStringToArrayBuffer(atob(encodedBlob.data)); return createBlob([arrayBuff], { type: encodedBlob.type }); } function _isEncodedBlob(value) { return value && value.__local_forage_encoded_blob; } function _fullyReady(callback) { var self2 = this; var promise = self2._initReady().then(function() { var dbContext = dbContexts[self2._dbInfo.name]; if (dbContext && dbContext.dbReady) { return dbContext.dbReady; } }); executeTwoCallbacks(promise, callback, callback); return promise; } function _tryReconnect(dbInfo) { _deferReadiness(dbInfo); var dbContext = dbContexts[dbInfo.name]; var forages = dbContext.forages; for (var i = 0; i < forages.length; i++) { var forage = forages[i]; if (forage._dbInfo.db) { forage._dbInfo.db.close(); forage._dbInfo.db = null; } } dbInfo.db = null; return _getOriginalConnection(dbInfo).then(function(db) { dbInfo.db = db; if (_isUpgradeNeeded(dbInfo)) { return _getUpgradedConnection(dbInfo); } return db; }).then(function(db) { dbInfo.db = dbContext.db = db; for (var i2 = 0; i2 < forages.length; i2++) { forages[i2]._dbInfo.db = db; } })["catch"](function(err) { _rejectReadiness(dbInfo, err); throw err; }); } function createTransaction(dbInfo, mode, callback, retries) { if (retries === void 0) { retries = 1; } try { var tx = dbInfo.db.transaction(dbInfo.storeName, mode); callback(null, tx); } catch (err) { if (retries > 0 && (!dbInfo.db || err.name === "InvalidStateError" || err.name === "NotFoundError")) { return Promise$1.resolve().then(function() { if (!dbInfo.db || err.name === "NotFoundError" && !dbInfo.db.objectStoreNames.contains(dbInfo.storeName) && dbInfo.version <= dbInfo.db.version) { if (dbInfo.db) { dbInfo.version = dbInfo.db.version + 1; } return _getUpgradedConnection(dbInfo); } }).then(function() { return _tryReconnect(dbInfo).then(function() { createTransaction(dbInfo, mode, callback, retries - 1); }); })["catch"](callback); } callback(err); } } function createDbContext() { return { forages: [], db: null, dbReady: null, deferredOperations: [] }; } function _initStorage(options) { var self2 = this; var dbInfo = { db: null }; if (options) { for (var i in options) { dbInfo[i] = options[i]; } } var dbContext = dbContexts[dbInfo.name]; if (!dbContext) { dbContext = createDbContext(); dbContexts[dbInfo.name] = dbContext; } dbContext.forages.push(self2); if (!self2._initReady) { self2._initReady = self2.ready; self2.ready = _fullyReady; } var initPromises = []; function ignoreErrors() { return Promise$1.resolve(); } for (var j = 0; j < dbContext.forages.length; j++) { var forage = dbContext.forages[j]; if (forage !== self2) { initPromises.push(forage._initReady()["catch"](ignoreErrors)); } } var forages = dbContext.forages.slice(0); return Promise$1.all(initPromises).then(function() { dbInfo.db = dbContext.db; return _getOriginalConnection(dbInfo); }).then(function(db) { dbInfo.db = db; if (_isUpgradeNeeded(dbInfo, self2._defaultConfig.version)) { return _getUpgradedConnection(dbInfo); } return db; }).then(function(db) { dbInfo.db = dbContext.db = db; self2._dbInfo = dbInfo; for (var k = 0; k < forages.length; k++) { var forage2 = forages[k]; if (forage2 !== self2) { forage2._dbInfo.db = dbInfo.db; forage2._dbInfo.version = dbInfo.version; } } }); } function getItem(key2, callback) { var self2 = this; key2 = normalizeKey(key2); var promise = new Promise$1(function(resolve, reject) { self2.ready().then(function() { createTransaction(self2._dbInfo, READ_ONLY, function(err, transaction) { if (err) { return reject(err); } try { var store = transaction.objectStore(self2._dbInfo.storeName); var req = store.get(key2); req.onsuccess = function() { var value = req.result; if (value === void 0) { value = null; } if (_isEncodedBlob(value)) { value = _decodeBlob(value); } resolve(value); }; req.onerror = function() { reject(req.error); }; } catch (e) { reject(e); } }); })["catch"](reject); }); executeCallback(promise, callback); return promise; } function iterate(iterator, callback) { var self2 = this; var promise = new Promise$1(function(resolve, reject) { self2.ready().then(function() { createTransaction(self2._dbInfo, READ_ONLY, function(err, transaction) { if (err) { return reject(err); } try { var store = transaction.objectStore(self2._dbInfo.storeName); var req = store.openCursor(); var iterationNumber = 1; req.onsuccess = function() { var cursor = req.result; if (cursor) { var value = cursor.value; if (_isEncodedBlob(value)) { value = _decodeBlob(value); } var result = iterator(value, cursor.key, iterationNumber++); if (result !== void 0) { resolve(result); } else { cursor["continue"](); } } else { resolve(); } }; req.onerror = function() { reject(req.error); }; } catch (e) { reject(e); } }); })["catch"](reject); }); executeCallback(promise, callback); return promise; } function setItem(key2, value, callback) { var self2 = this; key2 = normalizeKey(key2); var promise = new Promise$1(function(resolve, reject) { var dbInfo; self2.ready().then(function() { dbInfo = self2._dbInfo; if (toString.call(value) === "[object Blob]") { return _checkBlobSupport(dbInfo.db).then(function(blobSupport) { if (blobSupport) { return value; } return _encodeBlob(value); }); } return value; }).then(function(value2) { createTransaction(self2._dbInfo, READ_WRITE, function(err, transaction) { if (err) { return reject(err); } try { var store = transaction.objectStore(self2._dbInfo.storeName); if (value2 === null) { value2 = void 0; } var req = store.put(value2, key2); transaction.oncomplete = function() { if (value2 === void 0) { value2 = null; } resolve(value2); }; transaction.onabort = transaction.onerror = function() { var err2 = req.error ? req.error : req.transaction.error; reject(err2); }; } catch (e) { reject(e); } }); })["catch"](reject); }); executeCallback(promise, callback); return promise; } function removeItem(key2, callback) { var self2 = this; key2 = normalizeKey(key2); var promise = new Promise$1(function(resolve, reject) { self2.ready().then(function() { createTransaction(self2._dbInfo, READ_WRITE, function(err, transaction) { if (err) { return reject(err); } try { var store = transaction.objectStore(self2._dbInfo.storeName); var req = store["delete"](key2); transaction.oncomplete = function() { resolve(); }; transaction.onerror = function() { reject(req.error); }; transaction.onabort = function() { var err2 = req.error ? req.error : req.transaction.error; reject(err2); }; } catch (e) { reject(e); } }); })["catch"](reject); }); executeCallback(promise, callback); return promise; } function clear(callback) { var self2 = this; var promise = new Promise$1(function(resolve, reject) { self2.ready().then(function() { createTransaction(self2._dbInfo, READ_WRITE, function(err, transaction) { if (err) { return reject(err); } try { var store = transaction.objectStore(self2._dbInfo.storeName); var req = store.clear(); transaction.oncomplete = function() { resolve(); }; transaction.onabort = transaction.onerror = function() { var err2 = req.error ? req.error : req.transaction.error; reject(err2); }; } catch (e) { reject(e); } }); })["catch"](reject); }); executeCallback(promise, callback); return promise; } function length(callback) { var self2 = this; var promise = new Promise$1(function(resolve, reject) { self2.ready().then(function() { createTransaction(self2._dbInfo, READ_ONLY, function(err, transaction) { if (err) { return reject(err); } try { var store = transaction.objectStore(self2._dbInfo.storeName); var req = store.count(); req.onsuccess = function() { resolve(req.result); }; req.onerror = function() { reject(req.error); }; } catch (e) { reject(e); } }); })["catch"](reject); }); executeCallback(promise, callback); return promise; } function key(n, callback) { var self2 = this; var promise = new Promise$1(function(resolve, reject) { if (n < 0) { resolve(null); return; } self2.ready().then(function() { createTransaction(self2._dbInfo, READ_ONLY, function(err, transaction) { if (err) { return reject(err); } try { var store = transaction.objectStore(self2._dbInfo.storeName); var advanced = false; var req = store.openKeyCursor(); req.onsuccess = function() { var cursor = req.result; if (!cursor) { resolve(null); return; } if (n === 0) { resolve(cursor.key); } else { if (!advanced) { advanced = true; cursor.advance(n); } else { resolve(cursor.key); } } }; req.onerror = function() { reject(req.error); }; } catch (e) { reject(e); } }); })["catch"](reject); }); executeCallback(promise, callback); return promise; } function keys(callback) { var self2 = this; var promise = new Promise$1(function(resolve, reject) { self2.ready().then(function() { createTransaction(self2._dbInfo, READ_ONLY, function(err, transaction) { if (err) { return reject(err); } try { var store = transaction.objectStore(self2._dbInfo.storeName); var req = store.openKeyCursor(); var keys2 = []; req.onsuccess = function() { var cursor = req.result; if (!cursor) { resolve(keys2); return; } keys2.push(cursor.key); cursor["continue"](); }; req.onerror = function() { reject(req.error); }; } catch (e) { reject(e); } }); })["catch"](reject); }); executeCallback(promise, callback); return promise; } function dropInstance(options, callback) { callback = getCallback.apply(this, arguments); var currentConfig = this.config(); options = typeof options !== "function" && options || {}; if (!options.name) { options.name = options.name || currentConfig.name; options.storeName = options.storeName || currentConfig.storeName; } var self2 = this; var promise; if (!options.name) { promise = Promise$1.reject("Invalid arguments"); } else { var isCurrentDb = options.name === currentConfig.name && self2._dbInfo.db; var dbPromise = isCurrentDb ? Promise$1.resolve(self2._dbInfo.db) : _getOriginalConnection(options).then(function(db) { var dbContext = dbContexts[options.name]; var forages = dbContext.forages; dbContext.db = db; for (var i = 0; i < forages.length; i++) { forages[i]._dbInfo.db = db; } return db; }); if (!options.storeName) { promise = dbPromise.then(function(db) { _deferReadiness(options); var dbContext = dbContexts[options.name]; var forages = dbContext.forages; db.close(); for (var i = 0; i < forages.length; i++) { var forage = forages[i]; forage._dbInfo.db = null; } var dropDBPromise = new Promise$1(function(resolve, reject) { var req = idb.deleteDatabase(options.name); req.onerror = function() { var db2 = req.result; if (db2) { db2.close(); } reject(req.error); }; req.onblocked = function() { console.warn('dropInstance blocked for database "' + options.name + '" until all open connections are closed'); }; req.onsuccess = function() { var db2 = req.result; if (db2) { db2.close(); } resolve(db2); }; }); return dropDBPromise.then(function(db2) { dbContext.db = db2; for (var i2 = 0; i2 < forages.length; i2++) { var _forage = forages[i2]; _advanceReadiness(_forage._dbInfo); } })["catch"](function(err) { (_rejectReadiness(options, err) || Promise$1.resolve())["catch"](function() { }); throw err; }); }); } else { promise = dbPromise.then(function(db) { if (!db.objectStoreNames.contains(options.storeName)) { return; } var newVersion = db.version + 1; _deferReadiness(options); var dbContext = dbContexts[options.name]; var forages = dbContext.forages; db.close(); for (var i = 0; i < forages.length; i++) { var forage = forages[i]; forage._dbInfo.db = null; forage._dbInfo.version = newVersion; } var dropObjectPromise = new Promise$1(function(resolve, reject) { var req = idb.open(options.name, newVersion); req.onerror = function(err) { var db2 = req.result; db2.close(); reject(err); }; req.onupgradeneeded = function() { var db2 = req.result; db2.deleteObjectStore(options.storeName); }; req.onsuccess = function() { var db2 = req.result; db2.close(); resolve(db2); }; }); return dropObjectPromise.then(function(db2) { dbContext.db = db2; for (var j = 0; j < forages.length; j++) { var _forage2 = forages[j]; _forage2._dbInfo.db = db2; _advanceReadiness(_forage2._dbInfo); } })["catch"](function(err) { (_rejectReadiness(options, err) || Promise$1.resolve())["catch"](function() { }); throw err; }); }); } } executeCallback(promise, callback); return promise; } var asyncStorage = { _driver: "asyncStorage", _initStorage, _support: isIndexedDBValid(), iterate, getItem, setItem, removeItem, clear, length, key, keys, dropInstance }; function isWebSQLValid() { return typeof openDatabase === "function"; } var BASE_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; var BLOB_TYPE_PREFIX = "~~local_forage_type~"; var BLOB_TYPE_PREFIX_REGEX = /^~~local_forage_type~([^~]+)~/; var SERIALIZED_MARKER = "__lfsc__:"; var SERIALIZED_MARKER_LENGTH = SERIALIZED_MARKER.length; var TYPE_ARRAYBUFFER = "arbf"; var TYPE_BLOB = "blob"; var TYPE_INT8ARRAY = "si08"; var TYPE_UINT8ARRAY = "ui08"; var TYPE_UINT8CLAMPEDARRAY = "uic8"; var TYPE_INT16ARRAY = "si16"; var TYPE_INT32ARRAY = "si32"; var TYPE_UINT16ARRAY = "ur16"; var TYPE_UINT32ARRAY = "ui32"; var TYPE_FLOAT32ARRAY = "fl32"; var TYPE_FLOAT64ARRAY = "fl64"; var TYPE_SERIALIZED_MARKER_LENGTH = SERIALIZED_MARKER_LENGTH + TYPE_ARRAYBUFFER.length; var toString$1 = Object.prototype.toString; function stringToBuffer(serializedString) { var bufferLength = serializedString.length * 0.75; var len = serializedString.length; var i; var p = 0; var encoded1, encoded2, encoded3, encoded4; if (serializedString[serializedString.length - 1] === "=") { bufferLength--; if (serializedString[serializedString.length - 2] === "=") { bufferLength--; } } var buffer = new ArrayBuffer(bufferLength); var bytes = new Uint8Array(buffer); for (i = 0; i < len; i += 4) { encoded1 = BASE_CHARS.indexOf(serializedString[i]); encoded2 = BASE_CHARS.indexOf(serializedString[i + 1]); encoded3 = BASE_CHARS.indexOf(serializedString[i + 2]); encoded4 = BASE_CHARS.indexOf(serializedString[i + 3]); bytes[p++] = encoded1 << 2 | encoded2 >> 4; bytes[p++] = (encoded2 & 15) << 4 | encoded3 >> 2; bytes[p++] = (encoded3 & 3) << 6 | encoded4 & 63; } return buffer; } function bufferToString(buffer) { var bytes = new Uint8Array(buffer); var base64String = ""; var i; for (i = 0; i < bytes.length; i += 3) { base64String += BASE_CHARS[bytes[i] >> 2]; base64String += BASE_CHARS[(bytes[i] & 3) << 4 | bytes[i + 1] >> 4]; base64String += BASE_CHARS[(bytes[i + 1] & 15) << 2 | bytes[i + 2] >> 6]; base64String += BASE_CHARS[bytes[i + 2] & 63]; } if (bytes.length % 3 === 2) { base64String = base64String.substring(0, base64String.length - 1) + "="; } else if (bytes.length % 3 === 1) { base64String = base64String.substring(0, base64String.length - 2) + "=="; } return base64String; } function serialize(value, callback) { var valueType = ""; if (value) { valueType = toString$1.call(value); } if (value && (valueType === "[object ArrayBuffer]" || value.buffer && toString$1.call(value.buffer) === "[object ArrayBuffer]")) { var buffer; var marker = SERIALIZED_MARKER; if (value instanceof ArrayBuffer) { buffer = value; marker += TYPE_ARRAYBUFFER; } else { buffer = value.buffer; if (valueType === "[object Int8Array]") { marker += TYPE_INT8ARRAY; } else if (valueType === "[object Uint8Array]") { marker += TYPE_UINT8ARRAY; } else if (valueType === "[object Uint8ClampedArray]") { marker += TYPE_UINT8CLAMPEDARRAY; } else if (valueType === "[object Int16Array]") { marker += TYPE_INT16ARRAY; } else if (valueType === "[object Uint16Array]") { marker += TYPE_UINT16ARRAY; } else if (valueType === "[object Int32Array]") { marker += TYPE_INT32ARRAY; } else if (valueType === "[object Uint32Array]") { marker += TYPE_UINT32ARRAY; } else if (valueType === "[object Float32Array]") { marker += TYPE_FLOAT32ARRAY; } else if (valueType === "[object Float64Array]") { marker += TYPE_FLOAT64ARRAY; } else { callback(new Error("Failed to get type for BinaryArray")); } } callback(marker + bufferToString(buffer)); } else if (valueType === "[object Blob]") { var fileReader = new FileReader(); fileReader.onload = function() { var str = BLOB_TYPE_PREFIX + value.type + "~" + bufferToString(this.result); callback(SERIALIZED_MARKER + TYPE_BLOB + str); }; fileReader.readAsArrayBuffer(value); } else { try { callback(JSON.stringify(value)); } catch (e) { console.error("Couldn't convert value into a JSON string: ", value); callback(null, e); } } } function deserialize(value) { if (value.substring(0, SERIALIZED_MARKER_LENGTH) !== SERIALIZED_MARKER) { return JSON.parse(value); } var serializedString = value.substring(TYPE_SERIALIZED_MARKER_LENGTH); var type = value.substring(SERIALIZED_MARKER_LENGTH, TYPE_SERIALIZED_MARKER_LENGTH); var blobType; if (type === TYPE_BLOB && BLOB_TYPE_PREFIX_REGEX.test(serializedString)) { var matcher = serializedString.match(BLOB_TYPE_PREFIX_REGEX); blobType = matcher[1]; serializedString = serializedString.substring(matcher[0].length); } var buffer = stringToBuffer(serializedString); switch (type) { case TYPE_ARRAYBUFFER: return buffer; case TYPE_BLOB: return createBlob([buffer], { type: blobType }); case TYPE_INT8ARRAY: return new Int8Array(buffer); case TYPE_UINT8ARRAY: return new Uint8Array(buffer); case TYPE_UINT8CLAMPEDARRAY: return new Uint8ClampedArray(buffer); case TYPE_INT16ARRAY: return new Int16Array(buffer); case TYPE_UINT16ARRAY: return new Uint16Array(buffer); case TYPE_INT32ARRAY: return new Int32Array(buffer); case TYPE_UINT32ARRAY: return new Uint32Array(buffer); case TYPE_FLOAT32ARRAY: return new Float32Array(buffer); case TYPE_FLOAT64ARRAY: return new Float64Array(buffer); default: throw new Error("Unkown type: " + type); } } var localforageSerializer = { serialize, deserialize, stringToBuffer, bufferToString }; function createDbTable(t, dbInfo, callback, errorCallback) { t.executeSql("CREATE TABLE IF NOT EXISTS " + dbInfo.storeName + " (id INTEGER PRIMARY KEY, key unique, value)", [], callback, errorCallback); } function _initStorage$1(options) { var self2 = this; var dbInfo = { db: null }; if (options) { for (var i in options) { dbInfo[i] = typeof options[i] !== "string" ? options[i].toString() : options[i]; } } var dbInfoPromise = new Promise$1(function(resolve, reject) { try { dbInfo.db = openDatabase(dbInfo.name, String(dbInfo.version), dbInfo.description, dbInfo.size); } catch (e) { return reject(e); } dbInfo.db.transaction(function(t) { createDbTable(t, dbInfo, function() { self2._dbInfo = dbInfo; resolve(); }, function(t2, error) { reject(error); }); }, reject); }); dbInfo.serializer = localforageSerializer; return dbInfoPromise; } function tryExecuteSql(t, dbInfo, sqlStatement, args, callback, errorCallback) { t.executeSql(sqlStatement, args, callback, function(t2, error) { if (error.code === error.SYNTAX_ERR) { t2.executeSql("SELECT name FROM sqlite_master WHERE type='table' AND name = ?", [dbInfo.storeName], function(t3, results) { if (!results.rows.length) { createDbTable(t3, dbInfo, function() { t3.executeSql(sqlStatement, args, callback, errorCallback); }, errorCallback); } else { errorCallback(t3, error); } }, errorCallback); } else { errorCallback(t2, error); } }, errorCallback); } function getItem$1(key2, callback) { var self2 = this; key2 = normalizeKey(key2); var promise = new Promise$1(function(resolve, reject) { self2.ready().then(function() { var dbInfo = self2._dbInfo; dbInfo.db.transaction(function(t) { tryExecuteSql(t, dbInfo, "SELECT * FROM " + dbInfo.storeName + " WHERE key = ? LIMIT 1", [key2], function(t2, results) { var result = results.rows.length ? results.rows.item(0).value : null; if (result) { result = dbInfo.serializer.deserialize(result); } resolve(result); }, function(t2, error) { reject(error); }); }); })["catch"](reject); }); executeCallback(promise, callback); return promise; } function iterate$1(iterator, callback) { var self2 = this; var promise = new Promise$1(function(resolve, reject) { self2.ready().then(function() { var dbInfo = self2._dbInfo; dbInfo.db.transaction(function(t) { tryExecuteSql(t, dbInfo, "SELECT * FROM " + dbInfo.storeName, [], function(t2, results) { var rows = results.rows; var length2 = rows.length; for (var i = 0; i < length2; i++) { var item = rows.item(i); var result = item.value; if (result) { result = dbInfo.serializer.deserialize(result); } result = iterator(result, item.key, i + 1); if (result !== void 0) { resolve(result); return; } } resolve(); }, function(t2, error) { reject(error); }); }); })["catch"](reject); }); executeCallback(promise, callback); return promise; } function _setItem(key2, value, callback, retriesLeft) { var self2 = this; key2 = normalizeKey(key2); var promise = new Promise$1(function(resolve, reject) { self2.ready().then(function() { if (value === void 0) { value = null; } var originalValue = value; var dbInfo = self2._dbInfo; dbInfo.serializer.serialize(value, function(value2, error) { if (error) { reject(error); } else { dbInfo.db.transaction(function(t) { tryExecuteSql(t, dbInfo, "INSERT OR REPLACE INTO " + dbInfo.storeName + " (key, value) VALUES (?, ?)", [key2, value2], function() { resolve(originalValue); }, function(t2, error2) { reject(error2); }); }, function(sqlError) { if (sqlError.code === sqlError.QUOTA_ERR) { if (retriesLeft > 0) { resolve(_setItem.apply(self2, [key2, originalValue, callback, retriesLeft - 1])); return; } reject(sqlError); } }); } }); })["catch"](reject); }); executeCallback(promise, callback); return promise; } function setItem$1(key2, value, callback) { return _setItem.apply(this, [key2, value, callback, 1]); } function removeItem$1(key2, callback) { var self2 = this; key2 = normalizeKey(key2); var promise = new Promise$1(function(resolve, reject) { self2.ready().then(function() { var dbInfo = self2._dbInfo; dbInfo.db.transaction(function(t) { tryExecuteSql(t, dbInfo, "DELETE FROM " + dbInfo.storeName + " WHERE key = ?", [key2], function() { resolve(); }, function(t2, error) { reject(error); }); }); })["catch"](reject); }); executeCallback(promise, callback); return promise; } function clear$1(callback) { var self2 = this; var promise = new Promise$1(function(resolve, reject) { self2.ready().then(function() { var dbInfo = self2._dbInfo; dbInfo.db.transaction(function(t) { tryExecuteSql(t, dbInfo, "DELETE FROM " + dbInfo.storeName, [], function() { resolve(); }, function(t2, error) { reject(error); }); }); })["catch"](reject); }); executeCallback(promise, callback); return promise; } function length$1(callback) { var self2 = this; var promise = new Promise$1(function(resolve, reject) { self2.ready().then(function() { var dbInfo = self2._dbInfo; dbInfo.db.transaction(function(t) { tryExecuteSql(t, dbInfo, "SELECT COUNT(key) as c FROM " + dbInfo.storeName, [], function(t2, results) { var result = results.rows.item(0).c; resolve(result); }, function(t2, error) { reject(error); }); }); })["catch"](reject); }); executeCallback(promise, callback); return promise; } function key$1(n, callback) { var self2 = this; var promise = new Promise$1(function(resolve, reject) { self2.ready().then(function() { var dbInfo = self2._dbInfo; dbInfo.db.transaction(function(t) { tryExecuteSql(t, dbInfo, "SELECT key FROM " + dbInfo.storeName + " WHERE id = ? LIMIT 1", [n + 1], function(t2, results) { var result = results.rows.length ? results.rows.item(0).key : null; resolve(result); }, function(t2, error) { reject(error); }); }); })["catch"](reject); }); executeCallback(promise, callback); return promise; } function keys$1(callback) { var self2 = this; var promise = new Promise$1(function(resolve, reject) { self2.ready().then(function() { var dbInfo = self2._dbInfo; dbInfo.db.transaction(function(t) { tryExecuteSql(t, dbInfo, "SELECT key FROM " + dbInfo.storeName, [], function(t2, results) { var keys2 = []; for (var i = 0; i < results.rows.length; i++) { keys2.push(results.rows.item(i).key); } resolve(keys2); }, function(t2, error) { reject(error); }); }); })["catch"](reject); }); executeCallback(promise, callback); return promise; } function getAllStoreNames(db) { return new Promise$1(function(resolve, reject) { db.transaction(function(t) { t.executeSql("SELECT name FROM sqlite_master WHERE type='table' AND name <> '__WebKitDatabaseInfoTable__'", [], function(t2, results) { var storeNames = []; for (var i = 0; i < results.rows.length; i++) { storeNames.push(results.rows.item(i).name); } resolve({ db, storeNames }); }, function(t2, error) { reject(error); }); }, function(sqlError) { reject(sqlError); }); }); } function dropInstance$1(options, callback) { callback = getCallback.apply(this, arguments); var currentConfig = this.config(); options = typeof options !== "function" && options || {}; if (!options.name) { options.name = options.name || currentConfig.name; options.storeName = options.storeName || currentConfig.storeName; } var self2 = this; var promise; if (!options.name) { promise = Promise$1.reject("Invalid arguments"); } else { promise = new Promise$1(function(resolve) { var db; if (options.name === currentConfig.name) { db = self2._dbInfo.db; } else { db = openDatabase(options.name, "", "", 0); } if (!options.storeName) { resolve(getAllStoreNames(db)); } else { resolve({ db, storeNames: [options.storeName] }); } }).then(function(operationInfo) { return new Promise$1(function(resolve, reject) { operationInfo.db.transaction(function(t) { function dropTable(storeName) { return new Promise$1(function(resolve2, reject2) { t.executeSql("DROP TABLE IF EXISTS " + storeName, [], function() { resolve2(); }, function(t2, error) { reject2(error); }); }); } var operations = []; for (var i = 0, len = operationInfo.storeNames.length; i < len; i++) { operations.push(dropTable(operationInfo.storeNames[i])); } Promise$1.all(operations).then(function() { resolve(); })["catch"](function(e) { reject(e); }); }, function(sqlError) { reject(sqlError); }); }); }); } executeCallback(promise, callback); return promise; } var webSQLStorage = { _driver: "webSQLStorage", _initStorage: _initStorage$1, _support: isWebSQLValid(), iterate: iterate$1, getItem: getItem$1, setItem: setItem$1, removeItem: removeItem$1, clear: clear$1, length: length$1, key: key$1, keys: keys$1, dropInstance: dropInstance$1 }; function isLocalStorageValid() { try { return typeof localStorage !== "undefined" && "setItem" in localStorage && !!localStorage.setItem; } catch (e) { return false; } } function _getKeyPrefix(options, defaultConfig) { var keyPrefix = options.name + "/"; if (options.storeName !== defaultConfig.storeName) { keyPrefix += options.storeName + "/"; } return keyPrefix; } function checkIfLocalStorageThrows() { var localStorageTestKey = "_localforage_support_test"; try { localStorage.setItem(localStorageTestKey, true); localStorage.removeItem(localStorageTestKey); return false; } catch (e) { return true; } } function _isLocalStorageUsable() { return !checkIfLocalStorageThrows() || localStorage.length > 0; } function _initStorage$2(options) { var self2 = this; var dbInfo = {}; if (options) { for (var i in options) { dbInfo[i] = options[i]; } } dbInfo.keyPrefix = _getKeyPrefix(options, self2._defaultConfig); if (!_isLocalStorageUsable()) { return Promise$1.reject(); } self2._dbInfo = dbInfo; dbInfo.serializer = localforageSerializer; return Promise$1.resolve(); } function clear$2(callback) { var self2 = this; var promise = self2.ready().then(function() { var keyPrefix = self2._dbInfo.keyPrefix; for (var i = localStorage.length - 1; i >= 0; i--) { var key2 = localStorage.key(i); if (key2.indexOf(keyPrefix) === 0) { localStorage.removeItem(key2); } } }); executeCallback(promise, callback); return promise; } function getItem$2(key2, callback) { var self2 = this; key2 = normalizeKey(key2); var promise = self2.ready().then(function() { var dbInfo = self2._dbInfo; var result = localStorage.getItem(dbInfo.keyPrefix + key2); if (result) { result = dbInfo.serializer.deserialize(result); } return result; }); executeCallback(promise, callback); return promise; } function iterate$2(iterator, callback) { var self2 = this; var promise = self2.ready().then(function() { var dbInfo = self2._dbInfo; var keyPrefix = dbInfo.keyPrefix; var keyPrefixLength = keyPrefix.length; var length2 = localStorage.length; var iterationNumber = 1; for (var i = 0; i < length2; i++) { var key2 = localStorage.key(i); if (key2.indexOf(keyPrefix) !== 0) { continue; } var value = localStorage.getItem(key2); if (value) { value = dbInfo.serializer.deserialize(value); } value = iterator(value, key2.substring(keyPrefixLength), iterationNumber++); if (value !== void 0) { return value; } } }); executeCallback(promise, callback); return promise; } function key$2(n, callback) { var self2 = this; var promise = self2.ready().then(function() { var dbInfo = self2._dbInfo; var result; try { result = localStorage.key(n); } catch (error) { result = null; } if (result) { result = result.substring(dbInfo.keyPrefix.length); } return result; }); executeCallback(promise, callback); return promise; } function keys$2(callback) { var self2 = this; var promise = self2.ready().then(function() { var dbInfo = self2._dbInfo; var length2 = localStorage.length; var keys2 = []; for (var i = 0; i < length2; i++) { var itemKey = localStorage.key(i); if (itemKey.indexOf(dbInfo.keyPrefix) === 0) { keys2.push(itemKey.substring(dbInfo.keyPrefix.length)); } } return keys2; }); executeCallback(promise, callback); return promise; } function length$2(callback) { var self2 = this; var promise = self2.keys().then(function(keys2) { return keys2.length; }); executeCallback(promise, callback); return promise; } function removeItem$2(key2, callback) { var self2 = this; key2 = normalizeKey(key2); var promise = self2.ready().then(function() { var dbInfo = self2._dbInfo; localStorage.removeItem(dbInfo.keyPrefix + key2); }); executeCallback(promise, callback); return promise; } function setItem$2(key2, value, callback) { var self2 = this; key2 = normalizeKey(key2); var promise = self2.ready().then(function() { if (value === void 0) { value = null; } var originalValue = value; return new Promise$1(function(resolve, reject) { var dbInfo = self2._dbInfo; dbInfo.serializer.serialize(value, function(value2, error) { if (error) { reject(error); } else { try { localStorage.setItem(dbInfo.keyPrefix + key2, value2); resolve(originalValue); } catch (e) { if (e.name === "QuotaExceededError" || e.name === "NS_ERROR_DOM_QUOTA_REACHED") { reject(e); } reject(e); } } }); }); }); executeCallback(promise, callback); return promise; } function dropInstance$2(options, callback) { callback = getCallback.apply(this, arguments); options = typeof options !== "function" && options || {}; if (!options.name) { var currentConfig = this.config(); options.name = options.name || currentConfig.name; options.storeName = options.storeName || currentConfig.storeName; } var self2 = this; var promise; if (!options.name) { promise = Promise$1.reject("Invalid arguments"); } else { promise = new Promise$1(function(resolve) { if (!options.storeName) { resolve(options.name + "/"); } else { resolve(_getKeyPrefix(options, self2._defaultConfig)); } }).then(function(keyPrefix) { for (var i = localStorage.length - 1; i >= 0; i--) { var key2 = localStorage.key(i); if (key2.indexOf(keyPrefix) === 0) { localStorage.removeItem(key2); } } }); } executeCallback(promise, callback); return promise; } var localStorageWrapper = { _driver: "localStorageWrapper", _initStorage: _initStorage$2, _support: isLocalStorageValid(), iterate: iterate$2, getItem: getItem$2, setItem: setItem$2, removeItem: removeItem$2, clear: clear$2, length: length$2, key: key$2, keys: keys$2, dropInstance: dropInstance$2 }; var sameValue = function sameValue2(x, y) { return x === y || typeof x === "number" && typeof y === "number" && isNaN(x) && isNaN(y); }; var includes = function includes2(array, searchElement) { var len = array.length; var i = 0; while (i < len) { if (sameValue(array[i], searchElement)) { return true; } i++; } return false; }; var isArray = Array.isArray || function(arg) { return Object.prototype.toString.call(arg) === "[object Array]"; }; var DefinedDrivers = {}; var DriverSupport = {}; var DefaultDrivers = { INDEXEDDB: asyncStorage, WEBSQL: webSQLStorage, LOCALSTORAGE: localStorageWrapper }; var DefaultDriverOrder = [DefaultDrivers.INDEXEDDB._driver, DefaultDrivers.WEBSQL._driver, DefaultDrivers.LOCALSTORAGE._driver]; var OptionalDriverMethods = ["dropInstance"]; var LibraryMethods = ["clear", "getItem", "iterate", "key", "keys", "length", "removeItem", "setItem"].concat(OptionalDriverMethods); var DefaultConfig = { description: "", driver: DefaultDriverOrder.slice(), name: "localforage", size: 4980736, storeName: "keyvaluepairs", version: 1 }; function callWhenReady(localForageInstance, libraryMethod) { localForageInstance[libraryMethod] = function() { var _args = arguments; return localForageInstance.ready().then(function() { return localForageInstance[libraryMethod].apply(localForageInstance, _args); }); }; } function extend() { for (var i = 1; i < arguments.length; i++) { var arg = arguments[i]; if (arg) { for (var _key in arg) { if (arg.hasOwnProperty(_key)) { if (isArray(arg[_key])) { arguments[0][_key] = arg[_key].slice(); } else { arguments[0][_key] = arg[_key]; } } } } } return arguments[0]; } var LocalForage = function() { function LocalForage2(options) { _classCallCheck(this, LocalForage2); for (var driverTypeKey in DefaultDrivers) { if (DefaultDrivers.hasOwnProperty(driverTypeKey)) { var driver = DefaultDrivers[driverTypeKey]; var driverName = driver._driver; this[driverTypeKey] = driverName; if (!DefinedDrivers[driverName]) { this.defineDriver(driver); } } } this._defaultConfig = extend({}, DefaultConfig); this._config = extend({}, this._defaultConfig, options); this._driverSet = null; this._initDriver = null; this._ready = false; this._dbInfo = null; this._wrapLibraryMethodsWithReady(); this.setDriver(this._config.driver)["catch"](function() { }); } LocalForage2.prototype.config = function config(options) { if ((typeof options === "undefined" ? "undefined" : _typeof(options)) === "object") { if (this._ready) { return new Error("Can't call config() after localforage has been used."); } for (var i in options) { if (i === "storeName") { options[i] = options[i].replace(/\W/g, "_"); } if (i === "version" && typeof options[i] !== "number") { return new Error("Database version must be a number."); } this._config[i] = options[i]; } if ("driver" in options && options.driver) { return this.setDriver(this._config.driver); } return true; } else if (typeof options === "string") { return this._config[options]; } else { return this._config; } }; LocalForage2.prototype.defineDriver = function defineDriver(driverObject, callback, errorCallback) { var promise = new Promise$1(function(resolve, reject) { try { var driverName = driverObject._driver; var complianceError = new Error("Custom driver not compliant; see https://mozilla.github.io/localForage/#definedriver"); if (!driverObject._driver) { reject(complianceError); return; } var driverMethods = LibraryMethods.concat("_initStorage"); for (var i = 0, len = driverMethods.length; i < len; i++) { var driverMethodName = driverMethods[i]; var isRequired = !includes(OptionalDriverMethods, driverMethodName); if ((isRequired || driverObject[driverMethodName]) && typeof driverObject[driverMethodName] !== "function") { reject(complianceError); return; } } var configureMissingMethods = function configureMissingMethods2() { var methodNotImplementedFactory = function methodNotImplementedFactory2(methodName) { return function() { var error = new Error("Method " + methodName + " is not implemented by the current driver"); var promise2 = Promise$1.reject(error); executeCallback(promise2, arguments[arguments.length - 1]); return promise2; }; }; for (var _i = 0, _len = OptionalDriverMethods.length; _i < _len; _i++) { var optionalDriverMethod = OptionalDriverMethods[_i]; if (!driverObject[optionalDriverMethod]) { driverObject[optionalDriverMethod] = methodNotImplementedFactory(optionalDriverMethod); } } }; configureMissingMethods(); var setDriverSupport = function setDriverSupport2(support) { if (DefinedDrivers[driverName]) { console.info("Redefining LocalForage driver: " + driverName); } DefinedDrivers[driverName] = driverObject; DriverSupport[driverName] = support; resolve(); }; if ("_support" in driverObject) { if (driverObject._support && typeof driverObject._support === "function") { driverObject._support().then(setDriverSupport, reject); } else { setDriverSupport(!!driverObject._support); } } else { setDriverSupport(true); } } catch (e) { reject(e); } }); executeTwoCallbacks(promise, callback, errorCallback); return promise; }; LocalForage2.prototype.driver = function driver() { return this._driver || null; }; LocalForage2.prototype.getDriver = function getDriver(driverName, callback, errorCallback) { var getDriverPromise = DefinedDrivers[driverName] ? Promise$1.resolve(DefinedDrivers[driverName]) : Promise$1.reject(new Error("Driver not found.")); executeTwoCallbacks(getDriverPromise, callback, errorCallback); return getDriverPromise; }; LocalForage2.prototype.getSerializer = function getSerializer(callback) { var serializerPromise = Promise$1.resolve(localforageSerializer); executeTwoCallbacks(serializerPromise, callback); return serializerPromise; }; LocalForage2.prototype.ready = function ready(callback) { var self2 = this; var promise = self2._driverSet.then(function() { if (self2._ready === null) { self2._ready = self2._initDriver(); } return self2._ready; }); executeTwoCallbacks(promise, callback, callback); return promise; }; LocalForage2.prototype.setDriver = function setDriver(drivers, callback, errorCallback) { var self2 = this; if (!isArray(drivers)) { drivers = [drivers]; } var supportedDrivers = this._getSupportedDrivers(drivers); function setDriverToConfig() { self2._config.driver = self2.driver(); } function extendSelfWithDriver(driver) { self2._extend(driver); setDriverToConfig(); self2._ready = self2._initStorage(self2._config); return self2._ready; } function initDriver(supportedDrivers2) { return function() { var currentDriverIndex = 0; function driverPromiseLoop() { while (currentDriverIndex < supportedDrivers2.length) { var driverName = supportedDrivers2[currentDriverIndex]; currentDriverIndex++; self2._dbInfo = null; self2._ready = null; return self2.getDriver(driverName).then(extendSelfWithDriver)["catch"](driverPromiseLoop); } setDriverToConfig(); var error = new Error("No available storage method found."); self2._driverSet = Promise$1.reject(error); return self2._driverSet; } return driverPromiseLoop(); }; } var oldDriverSetDone = this._driverSet !== null ? this._driverSet["catch"](function() { return Promise$1.resolve(); }) : Promise$1.resolve(); this._driverSet = oldDriverSetDone.then(function() { var driverName = supportedDrivers[0]; self2._dbInfo = null; self2._ready = null; return self2.getDriver(driverName).then(function(driver) { self2._driver = driver._driver; setDriverToConfig(); self2._wrapLibraryMethodsWithReady(); self2._initDriver = initDriver(supportedDrivers); }); })["catch"](function() { setDriverToConfig(); var error = new Error("No available storage method found."); self2._driverSet = Promise$1.reject(error); return self2._driverSet; }); executeTwoCallbacks(this._driverSet, callback, errorCallback); return this._driverSet; }; LocalForage2.prototype.supports = function supports(driverName) { return !!DriverSupport[driverName]; }; LocalForage2.prototype._extend = function _extend(libraryMethodsAndProperties) { extend(this, libraryMethodsAndProperties); }; LocalForage2.prototype._getSupportedDrivers = function _getSupportedDrivers(drivers) { var supportedDrivers = []; for (var i = 0, len = drivers.length; i < len; i++) { var driverName = drivers[i]; if (this.supports(driverName)) { supportedDrivers.push(driverName); } } return supportedDrivers; }; LocalForage2.prototype._wrapLibraryMethodsWithReady = function _wrapLibraryMethodsWithReady() { for (var i = 0, len = LibraryMethods.length; i < len; i++) { callWhenReady(this, LibraryMethods[i]); } }; LocalForage2.prototype.createInstance = function createInstance(options) { return new LocalForage2(options); }; return LocalForage2; }(); var localforage_js = new LocalForage(); module3.exports = localforage_js; }, { "3": 3 }] }, {}, [4])(4); }); } }); // src/applySettingsHook.ts function applySettingsHook() { Manager.prototype._applySettings = Manager.prototype.applySettings; Manager.prototype.applySettings = function(s) { this.isRanked = s.ranked; return this._applySettings(s); }; } // src/config.ts var config_default = { ver: "1.32", api: "https://nutils.itsmeow.cat", customDelimiter: "__custom", actualGameVersion: document.querySelector(`script[src*="game.js"]`)?.src.split("/").pop()?.split("?v=")?.[1] || (() => { try { return App.ClientVersion; } catch { return "unknown"; } })(), PacketTypeMap: { systemMessage: "w", chatSend: "x", findMatch: "h", joinMatch: "j", data: "d", data2: "p", joinedMessage: "i" }, Colors: { dotGreen: 65280, dotGrey: 8947848, dotOrange: 16757012, green: 8978312, grey: 16777215, red: 12603201, white: 13421772, yellow: 16763904 }, MapIDs: { Hull: 1, Igloo: 2, Temple: 3, DragonsDen: 4, dm_Arena1: 5, Elysium: 6, Tobruk: 7, ColdFusion: 8, TwinFaces: 9, KodysIsland: 10, Canyon: 11, Hill364: 12, Stasis: 13, ctf_Evening: 14, ArcticDusk: 15, Cathedral: 16, ctf_Lambda: 17, Aerial: 18, ctf_FacingWorlds: 19, ctf_Ash: 20, ctf_Naom: 21, dm_Hunter: 22, Tribal: 23, Kiwi: 24, Webb: 26, dm_Sleet: 27, SpaceStation: 28, Sinkhole: 29, LonelyIsland: 30, dm_Nexus: 31 }, WeaponReloadTimes: { Fists: 0, Shotgun: 0, SubmachineGun: 0, NadeLauncher: 2e3, Barrett: 2500, ShockRifle: 0, PulseGun: 0, FlameThrower: 0, RPG: 4e3, Rifle: 2e3, LaserGun: 0, LinkGun: 0, AK47: 0, Chainsaw: 0, DesertEagle: 0, Minigun: 0, X75: 0, MAC10: 0, Bow: 300, RocketLauncher: 0, Carbine: 0, BoomerangGun: 0, M60: 4e3, Uzi: 0, Bouncyball: 0 } }; // src/settings/settings.ts var settingsKey = "ninjaioutils"; var SETTINGS = { ...{ uiScale: 0, showFPS: true, texturePack: null, typewriter: false, apiKey: "", appearOnline: true, enableHotkeyMessages: true, hotkeyMessages: [], helpfulUI: true }, ...JSON.parse(localStorage.getItem(settingsKey) || "{}") }; var saveSettings = () => { localStorage.setItem(settingsKey, JSON.stringify(SETTINGS)); }; // src/settings/settingsTabGraphics.ts function hookGraphicsSettingsTab() { app.menu.settingsPanel.graphicsTab.addChild(app.menu.settingsPanel.graphicsTab.fpsDisplay = new Checkbox("showFPS", "Show FPS Display", true)); app.menu.settingsPanel.graphicsTab.fpsDisplay.x = app.menu.settingsPanel.graphicsTab.enableAA.x; app.menu.settingsPanel.graphicsTab.fpsDisplay.y = app.menu.settingsPanel.graphicsTab.enableAA.y + app.menu.settingsPanel.graphicsTab.enableAA.height + 12; app.menu.settingsPanel.graphicsTab.fpsDisplay.on(Checkbox.CHANGE, function(b) { SETTINGS.showFPS = b; saveSettings(); if (frameDisplay.style.display == "none" && SETTINGS.showFPS) showFPS(); }); app.menu.settingsPanel.graphicsTab.fpsDisplay.setChecked(SETTINGS.showFPS); app.menu.settingsPanel.graphicsTab.addChild(app.menu.settingsPanel.graphicsTab.uiScaler = new Slider("chatOpacity", "UI scale", SETTINGS.uiScale || 0.4, 2, 0.4)); if (!SETTINGS.uiScale) app.menu.settingsPanel.graphicsTab.uiScaler.valueLabel.text = "default"; app.menu.settingsPanel.graphicsTab.uiScaler.x = app.menu.settingsPanel.graphicsTab.fpsDisplay.x; app.menu.settingsPanel.graphicsTab.uiScaler.y = app.menu.settingsPanel.graphicsTab.fpsDisplay.y + app.menu.settingsPanel.graphicsTab.fpsDisplay.height + 10; app.menu.settingsPanel.graphicsTab.uiScaler.on(Slider.CHANGE, (b) => { b = Math.round(b * 10) / 10; if (b == 0.4) { app.menu.settingsPanel.graphicsTab.uiScaler.valueLabel.text = "default"; b = 0; } if (b == SETTINGS.uiScale) return; App.NUIScale = SETTINGS.uiScale = b; saveSettings(); app.onResize(); const conf = document.createElement("div"); const confy = document.createElement("button"); confy.innerHTML = "OK"; confy.onclick = () => conf.remove(); conf.appendChild(confy); const confn = document.createElement("button"); confn.innerHTML = "Undo"; confn.onclick = () => { App.NUIScale = SETTINGS.uiScale = 0; saveSettings(); app.onResize(); conf.remove(); }; confn.style.marginLeft = "0.3rem"; conf.appendChild(confn); conf.style.position = "absolute"; conf.style.top = "50%"; conf.style.left = "50%"; conf.style.transform = "translate(-50%,-50%)"; conf.style.backgroundColor = "rgba(0,0,0,0.8)"; conf.style.padding = "0.5rem"; document.body.appendChild(conf); }); app.menu.settingsPanel.graphicsTab.addChild(app.menu.settingsPanel.graphicsTab.helpfulBox = new Checkbox("helpful", "Enable Helpful UI (healthbars)", true)); app.menu.settingsPanel.graphicsTab.helpfulBox.x = app.menu.settingsPanel.graphicsTab.fpsDisplay.x; app.menu.settingsPanel.graphicsTab.helpfulBox.y = app.menu.settingsPanel.graphicsTab.uiScaler.y + app.menu.settingsPanel.graphicsTab.uiScaler.height + 12; app.menu.settingsPanel.graphicsTab.helpfulBox.on(Checkbox.CHANGE, function(b) { SETTINGS.helpfulUI = b; saveSettings(); }); app.menu.settingsPanel.graphicsTab.helpfulBox.setChecked(SETTINGS.helpfulUI); } // src/hotkeyMessages.ts var isRateLimited = false; var registeredHotkeyMessages = new Map(SETTINGS.hotkeyMessages); async function handleKeyDown(e) { if (e.repeat) return; const isAltPressed = UserInput.pressed[18]; const message = registeredHotkeyMessages.get(e.key); if (message && isAltPressed && SETTINGS.enableHotkeyMessages) sendChatMessage(message); } async function sendChatMessage(message) { if (!app.client.socket || isRateLimited) return; const binaryChatMessage = Client.compress({ t: config_default.PacketTypeMap.chatSend, msg: message }); app.client.socket.send(binaryChatMessage); isRateLimited = true; setTimeout(() => isRateLimited = false, 1e3 * 1.4); } // src/settings/settingsTabHotkeyMsgs.ts function getHotkeyMsgsTab() { class HotkeyMessagesTab extends PIXI.Container { constructor() { super(); const tab = this; EventDispatcher.call(this); this.marginLeft = 40; this.marginTop = 52; this.off = this.marginTop + 6; this.hkmTitle = new PIXI.Text("Hotkey Messages", { fontName: "Arial", fontSize: 18, lineHeight: 18, fill: config_default.Colors.yellow, strokeThickness: 3, lineJoin: "round" }); this.hkmTitle.x = this.marginLeft - 5; this.hkmTitle.y = this.off; this.addChild(this.hkmTitle); this.hkmHint = new PIXI.Text("(Use fullscreen to avoid conflics with browser hotkeys)", { fontName: "Arial", fontSize: 14, fill: config_default.Colors.white, strokeThickness: 2, lineJoin: "round" }); this.hkmHint.x = this.hkmTitle.x + this.hkmTitle.width + 3; this.hkmHint.y = this.off + 2; this.addChild(this.hkmHint); this.enableHKM = new Checkbox("enableHKM", "Enable Hotkey Messages", true); this.enableHKM.x = this.marginLeft; this.enableHKM.y = this.off += 34; this.enableHKM.on(Checkbox.CHANGE, function(b) { SETTINGS.enableHotkeyMessages = b; saveSettings(); }); this.addChild(this.enableHKM); this.enableHKM.setChecked(SETTINGS.enableHotkeyMessages); const doHotkey = (key) => { const keyLabel = new PIXI.Text(`ALT + ${key.toUpperCase()}`, { fontName: "Arial", fontSize: 16, fill: config_default.Colors.yellow, strokeThickness: 3, lineJoin: "round" }); keyLabel.x = this.marginLeft; keyLabel.y = this.off += 55; this.addChild(keyLabel); const keyText = new InputField(`${key}_hotkey`, false, 24); keyText.setDimensions(370, 35); keyText.forceLowerCase = false; keyText.setMaxChars(40); if (registeredHotkeyMessages.get(key)) keyText.setText(registeredHotkeyMessages.get(key)); keyText.x = 125; keyText.y = this.off - 6; keyText.setFilter("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890 <>?!@#$%^&*()-_+=[]{}:~|/."); keyText.addListener(InputField.CHANGE, function(d) { const message = d.data.value || ""; registeredHotkeyMessages.set(key, message); SETTINGS.hotkeyMessages = [...registeredHotkeyMessages]; saveSettings(); }); this.addChild(keyText); }; ["a", "s", "d", "q", "w", "e"].forEach((k) => doHotkey(k)); } } return HotkeyMessagesTab; } // src/settings/settingsTabSound.ts function hookSoundSettingsTab() { app.menu.settingsPanel.soundTab.addChild(app.menu.settingsPanel.soundTab.typewriter = new Checkbox("typewriter", "Enable Typing Noise", true)); app.menu.settingsPanel.soundTab.typewriter.x = app.menu.settingsPanel.soundTab.volumeSlider.x; app.menu.settingsPanel.soundTab.typewriter.y = app.menu.settingsPanel.soundTab.volumeSlider.y + app.menu.settingsPanel.soundTab.volumeSlider.height + 14; app.menu.settingsPanel.soundTab.typewriter.on(Checkbox.CHANGE, function(b) { SETTINGS.typewriter = b; saveSettings(); }); app.menu.settingsPanel.soundTab.typewriter.setChecked(SETTINGS.typewriter); } // src/settings/settingsTabTex.ts var import_localforage = __toESM(require_localforage()); // src/Scrollbar.ts function getScrollbar() { class _Scrollbar extends PIXI.Container { constructor(h, start = 0) { super(); this.h = h; this.start = start; this.scrolling = false; this.oy = 0; this.scrollBar = new PIXI.Graphics(); this.scrollBar.lineStyle(1, 16777215, 0.4, 0); this.scrollBar.drawRoundedRect(0, -5, 20, this.h, 4); this.scrollBar.endFill(); this.scrollBar.x = 0; this.scrollBar.y = 0; this.scrollBar.interactive = true; this.scrollBar.alpha = 0.5; this.addChild(this.scrollBar); this.scrollBar.hitArea = new PIXI.Rectangle(-4, -4, 32, this.h + 8); this.scrollButton = new PIXI.Graphics(); this.scrollButton.lineStyle(1, 16777215, 0.4, 0); this.scrollButton.beginFill(16777215, 0.2); this.scrollButton.drawRoundedRect(0, 0, 16, 32, 4); this.scrollButton.endFill(); this.scrollButton.hitArea = new PIXI.Rectangle(0, 0, 0, 0); this.addChild(this.scrollButton); this.scrollButton.x = 2; this.scrollButton.y = -3 + (this.h - 39) * this.start; this.scrollBar.on("mouseover", () => { this.scrollBar.alpha = 1; }); this.scrollBar.on("mouseout", () => { this.scrollBar.alpha = 0.5; }); this.scrollBar.on("mouseupoutside", () => { this.scrolling = false; }); this.scrollBar.on("mousedown", (c) => { c.stopPropagation(); this.scrolling = true; this.oy = c.data.global.y / App.Scale; }); this.scrollBar.on("mouseup", (c) => { c.stopPropagation(); this.scrolling = false; }); this.scrollBar.on("mousemove", (c) => { this.scroll(c.data.global.y / App.Scale); }); this.scrollBar.on("pointerup", () => { this.scrolling = false; }); this.scrollBar.on("pointerupoutside", () => { this.scrolling = false; }); this.scrollBar.on("pointerdown", (c) => { this.scrolling = true; this.oy = c.data.global.y / App.Scale; this.scroll(c.data.global.y / App.Scale); }); this.scrollBar.on("pointermove", (c) => this.scroll(c.data.global.y / App.Scale)); this.wheelListener = (c) => { this.scrolling = true; this.scroll(this.oy + 0.2 * c.data.delta); this.scrolling = false; }; } scrolling = false; oy = 0; scrollBar; scrollButton; wheelListener; enableWheel() { UserInput.hasListener(UserInput.WHEEL, this.wheelListener) || UserInput.addListener(UserInput.WHEEL, this.wheelListener); } disableWheel() { UserInput.removeListener(UserInput.WHEEL, this.wheelListener); } scroll(a) { if (this.scrolling) { let b = this.scrollButton.y + (a - this.oy); -3 > b ? b = -3 : b > this.h - 39 && (b = this.h - 39); let c = this.h / (this.h - 39); this.scrollButton.y = b; this.oy = a; this.emit(Scrollbar.SCROLL, 1 / this.h * (b + 3) * c); } } reset() { this.scrollButton.y = -3 + (this.h - 39) * this.start; } onMouseOver() { this.scrollButton.alpha = 1; } onMouseOut() { this.scrollButton.alpha = 0.5; } } const Scrollbar = _Scrollbar; Scrollbar.SCROLL = "scroll"; return Scrollbar; } // src/settings/settingsTabTex.ts function getTexTab() { const maxPacks = 6; const FileUploader = document.createElement("input"); FileUploader.type = "file"; FileUploader.accept = "application/zip"; FileUploader.multiple = false; FileUploader.style.display = "none"; document.body.appendChild(FileUploader); class TexTab extends PIXI.Container { constructor() { super(); const tab = this; EventDispatcher.call(this); this.marginLeft = 40; this.marginTop = 52; this.off = this.marginTop + 6; this.texTitle = new PIXI.Text("Texture Packs", { fontName: "Arial", fontSize: 18, lineHeight: 18, fill: config_default.Colors.yellow, strokeThickness: 3, lineJoin: "round" }); this.texTitle.x = this.marginLeft - 5; this.texTitle.y = this.off; this.addChild(this.texTitle); this.texHint = new PIXI.Text("(make sure to click save)", { fontName: "Arial", fontSize: 14, fill: config_default.Colors.white, strokeThickness: 2, lineJoin: "round" }); this.texHint.x = this.texTitle.x + this.texTitle.width + 3; this.texHint.y = this.off + 2; this.addChild(this.texHint); this.off += 30; const customTitle = new PIXI.Text("Custom Pack (read docs for tutorial)", { fontName: "Arial", fontSize: 16, fill: config_default.Colors.white, strokeThickness: 2, lineJoin: "round" }); customTitle.x = this.marginLeft; customTitle.y = this.off; this.addChild(customTitle); this.off += 20; const customDesc = new PIXI.Text("Upload a zip file containing your textures.", { fontName: "Arial", fontSize: 14, fill: config_default.Colors.yellow, strokeThickness: 2, lineJoin: "round" }); customDesc.x = this.marginLeft; customDesc.y = this.off; this.addChild(customDesc); this.off += 24; const uploader = new Button("uploader"); uploader.addListener(Button.BUTTON_RELEASED, async () => { if (await import_localforage.default.getItem("custom_pack")) { await import_localforage.default.removeItem("custom_pack"); delete this.hasCust; this.runPacks(); } else { FileUploader.click(); } }); uploader.x = this.marginLeft + 8; uploader.y = this.off; uploader.scale.x = uploader.scale.y = 0.75; this.addChild(uploader); this.off += 12; const off = this.off; this.packIndex = 0; !(this.runPacks = async () => { this.off = off; if (this.hadPacks) this.hadPacks.map((p) => p.destroy()); this.hadPacks = []; const packs = this.packList || (this.packList = await fetch(`${config_default.api}/packs`).then((r) => r.json())); const custom = this.hasCust || (this.hasCust = await import_localforage.default.getItem("custom_pack") || true); const packList = packs.sort((a, b) => a.name.toLowerCase() > b.name.toLowerCase() ? 1 : -1).slice(this.packIndex, this.packIndex + maxPacks); if (custom && custom !== true) { packList.unshift({ id: config_default.customDelimiter, name: "Custom Pack", description: "Custom texture pack: " + custom.name, author: "You!", hasCombined: false, hasSeamless: false }); uploader.setText("Delete"); uploader.setTint(config_default.Colors.red); } else { uploader.setText("Upload"); uploader.setTint(config_default.Colors.grey); } packList.forEach((pak) => { const hasPack = SETTINGS.texturePack == pak.id; const packName = new PIXI.Text(`${pak.name || "Texture Pack"} (by ${pak.author || "Unnamed"})`, { fontName: "Arial", fontSize: 16, fill: config_default.Colors.white, strokeThickness: 2, lineJoin: "round" }); packName.x = this.marginLeft; packName.y = this.off += 28; this.hadPacks.push(this.addChild(packName)); const flags = []; if (pak.hasCombined) flags.push("textures"); if (pak.hasSeamless) flags.push("terrain"); const packDescription = new PIXI.Text(`${pak.description || "No Description."}${flags.length ? ` (${flags.join(", ")})` : ""}`, { fontName: "Arial", fontSize: 14, fill: config_default.Colors.white, strokeThickness: 2, lineJoin: "round" }); packDescription.x = this.marginLeft; packDescription.y = this.off += packName.height + 2; this.hadPacks.push(this.addChild(packDescription)); const packButton = new Button(`pack_btn_${pak.id}`); packButton.x = packName.x + packName.width + 12; packButton.y = this.off - packName.height; packButton.setText(hasPack ? "Remove" : "Use"); packButton.setTint(hasPack ? config_default.Colors.red : config_default.Colors.green); packButton.scale.x = packButton.scale.y = 0.5; packButton.addListener(Button.BUTTON_RELEASED, async () => { SETTINGS.texturePack = hasPack ? null : pak.id; app.menu.settingsPanel.controlsTab.forceRefresh = true; saveSettings(); this.runPacks(); }); this.hadPacks.push(this.addChild(packButton)); }); })(); } } TexTab.prototype.onShow = function() { if (!this.scroller) { const off = this.parent.texTabButton.height + 32; this.scroller = new (getScrollbar())(this.parent.height - off - 12 - this.parent.applyButton.height); this.scroller.x = this.parent.width - this.scroller.width * 1.75; this.scroller.y = off; this.scroller.on(getScrollbar().SCROLL, (prog) => { this.packIndex = Math.round((this.packList.length - maxPacks) * prog); this.runPacks(); }); this.addChild(this.scroller); } this.scroller.enableWheel(); const tab = this; FileUploader.onchange = () => { [...FileUploader.files].forEach(async (f) => { if (f.type.startsWith("application/") && f.type.includes("zip")) { await import_localforage.default.setItem("custom_pack", f); delete tab.hasCust; tab.runPacks(); app.menu.settingsPanel.controlsTab.forceRefresh = true; } }); }; }; TexTab.prototype.onHide = function() { this.scroller.disableWheel(); }; return TexTab; } // src/settings/settingsTab.ts function settingsTab() { SettingsPanel.OPEN_TAB = "opened_settings_tab"; if (!app.menu?.settingsPanel) return setTimeout(() => settingsTab(), 500); function SettingsPanelNew(w, h) { const pan = new SettingsPanel(w, h); function newTab(title, name, x, tab) { name = `${name}Tab`; pan[name] = new tab(); pan[`${name}Button`] = new PIXI.Text(title, { fontName: "Arial", fontSize: 18, lineHeight: 18, fill: config_default.Colors.yellow, strokeThickness: 3, lineJoin: "round" }); pan[`${name}Button`].resolution = 1.5 * App.DevicePixelRatio; pan[`${name}Button`].anchor.x = pan[`${name}Button`].anchor.y = 0.5; pan[`${name}Button`].x = x + 56; pan[`${name}Button`].y = 28; pan.addChild(pan[`${name}Button`]); pan[`${name}ButtonBackground`] = new PIXI.Graphics(); pan[`${name}ButtonBackground`].beginFill(16777215, 0.1); pan[`${name}ButtonBackground`].drawRoundedRect(0, 0, 112, 30, 2); pan[`${name}ButtonBackground`].endFill(); pan[`${name}ButtonBackground`].x = x; pan[`${name}ButtonBackground`].y = 12; pan[`${name}ButtonBackground`].interactive = true; pan[`${name}ButtonBackground`].on("touchstart", pan.displayTab.bind(pan, SettingsPanel.Tabs.UTIL)); pan[`${name}ButtonBackground`].on("mousedown", pan.displayTab.bind(pan, SettingsPanel.Tabs.UTIL)); pan[`${name}ButtonBackground`].on("mouseover", function() { pan[`${name}ButtonBackground`].tint = 11184810; }); pan[`${name}ButtonBackground`].on("mouseout", function() { pan[`${name}ButtonBackground`].tint = 16777215; }); pan.addChild(pan[`${name}ButtonBackground`]); } newTab("Texture Pack", "tex", 302, getTexTab()); newTab("Messages", "hkm", 418, getHotkeyMsgsTab()); return pan; } SettingsPanel.Tabs.TEX = "tex"; SettingsPanel.Tabs.HKM = "hkm"; const oldX = app.menu.settingsPanel.x, oldY = app.menu.settingsPanel.y; app.menu.settingsPanel.destroy(); app.menu.settingsPanel = SettingsPanelNew(660, 524); app.menu.settingsPanel.x = oldX; app.menu.settingsPanel.y = oldY; app.menu.resize(); app.menu.settingsPanel.displayTab = function(name) { AudioEffects.ButtonClick.audio.play(); saveSettings(); Object.values(SettingsPanel.Tabs).filter((t2) => t2 !== name).forEach((i) => { const t2 = this[`${i}Tab`]; if (t2.parent) { if (t2.onHide) t2.onHide(); this.removeChild(t2); } this[`${i}TabButtonBackground`].alpha = 1; }); const t = this[`${name}Tab`]; this[`${name}TabButtonBackground`].alpha = 0; this.addChild(t); if (t.onShow) t.onShow(); app.menu.settingsPanel.selectedTab = name; App.Layer.memberMenu.emit(SettingsPanel.OPEN_TAB, name); }; Object.values(SettingsPanel.Tabs).forEach((d) => { const tab = app.menu.settingsPanel[`${d}TabButtonBackground`]; tab.on("mousedown", app.menu.settingsPanel.displayTab.bind(app.menu.settingsPanel, d)); tab._events.mousedown.shift(); }); hookSoundSettingsTab(); hookGraphicsSettingsTab(); } // src/shareURLs.ts var gameLinkData = { id: "", name: "", pass: "" }; function clearSaved() { window.location.hash = ""; gameLinkData.id = gameLinkData.name = gameLinkData.pass = ""; } function initShareURLHook() { App.Layer.on("join_game", (name, id, pass) => { setHash(id, name, pass); }); app.client.addListener(Protocol.DISCONNECT, () => { clearSaved(); settingsTab(); }); APIClient.realPostCreateGame = APIClient.postCreateGame; APIClient.postCreateGame = function(serverID, settings, mode, time, serverName, serverPass, customData, auth) { setHash(serverID, serverName, serverPass); return APIClient.realPostCreateGame(serverID, settings, mode, time, serverName, serverPass, customData, auth); }; } function tryJoinLink(args) { const [id, name, pass] = args || window.location.hash.substring(1)?.split("&").map(decodeURIComponent) || []; if (!id || !name) return; App.Console.log(`Attempting to join room ${name}...`); const loadingMenu = App.Layer.loadingMenu; App.Layer.addChild(loadingMenu); loadingMenu.show(); loadingMenu.setTitle(`Click to join server. ${name}`); loadingMenu.cancelCount = -1; loadingMenu.joinButton = new Button("join"); loadingMenu.joinButton.selected = true; loadingMenu.joinButton.setText("Join"); loadingMenu.joinButton.scale.x = loadingMenu.joinButton.scale.y = 0.8; loadingMenu.joinButton.addListener(Button.BUTTON_RELEASED, function() { removeJoinStuff(); loadingMenu.show(); App.Layer.emit("join_game", name, id, pass || ""); }); loadingMenu.joinButton.x = loadingMenu.title.x + 0.5 * (loadingMenu.title.width - loadingMenu.joinButton.width); loadingMenu.joinButton.y = loadingMenu.title.y + 40; loadingMenu.joinButton.setTint(config_default.Colors.green); loadingMenu.container.addChild(loadingMenu.joinButton); loadingMenu.cancelButton2 = new Button("cancel2"); loadingMenu.cancelButton2.setText("Cancel"); loadingMenu.cancelButton2.scale.x = loadingMenu.cancelButton2.scale.y = 0.8; loadingMenu.cancelButton2.addListener(Button.BUTTON_RELEASED, function() { removeJoinStuff(); clearSaved(); return loadingMenu.emit(Layer.Events.LOADING_CANCEL); }); loadingMenu.cancelButton2.x = loadingMenu.joinButton.x + loadingMenu.joinButton.width + 8; loadingMenu.cancelButton2.y = loadingMenu.title.y + 40; loadingMenu.cancelButton2.setTint(config_default.Colors.red); loadingMenu.container.addChild(loadingMenu.cancelButton2); loadingMenu.title.y -= 36; function removeJoinStuff() { loadingMenu.title.y += 36; loadingMenu.container.removeChild(loadingMenu.joinButton); loadingMenu.container.removeChild(loadingMenu.cancelButton2); } } // src/utils.ts var inGame = () => app.matchStarted && app.client.socket && app.client.socket.readyState == WebSocket.OPEN; function setHash(id, name, pass) { gameLinkData.id = id; gameLinkData.name = name; gameLinkData.pass = pass || ""; window.location.hash = pass ? `${id}&${encodeURIComponent(name)}&${encodeURIComponent(pass)}` : `${id}&${encodeURIComponent(name)}`; } function io(url) { return window.io(url); } // src/fpsCounter.ts var frameDisplay = document.createElement("div"); Object.entries({ padding: "0.3rem 0.4rem", font: "16px Arial", display: "none", position: "fixed", top: "0px", left: "50%", transform: "translateX(-50%)", backgroundColor: "rgba(0,0,0,0.5)", borderBottomLeftRadius: "6px", borderBottomRightRadius: "6px", pointerEvents: "none", userSelect: "none" }).forEach((e) => { frameDisplay.style[e[0]] = e[1]; }); frameDisplay.textContent = "..."; document.body.appendChild(frameDisplay); function showFPS() { let lastUpdate = Date.now(), frames = 0; if (SETTINGS.showFPS) frameDisplay.style.display = "block"; function updateCounter() { const now = Date.now(), elapsed = now - lastUpdate; if (elapsed < 500) { frames++; } else { let fps = `${Math.round(frames / (elapsed / 1e3))} FPS`; if (inGame()) fps += ` - ${App.Stats.ping || 0}ms`; if (frameDisplay.innerText !== fps) frameDisplay.innerText = fps; frames = 0; lastUpdate = now; frameDisplay.style.display = "block"; } if (!SETTINGS.showFPS) return frameDisplay.style.display = "none"; } app._stepCallback = app._stepCallback || app.stepCallback; app.stepCallback = function(...d) { try { updateCounter(); } catch (err) { console.error(err); } return app._stepCallback(...d); }; } // src/friendOnlineHook.ts function initFriendOnlineHook() { SocialMenu.prototype._maskFriendList = SocialMenu.prototype.maskFriendList; SocialMenu.prototype.maskFriendList = function(scrollDist) { this.friends.sort((f1, f2) => f2.seen.getTime() - f1.seen.getTime()).forEach((f, fi) => f.y = 47 + fi * SocialMenu.ItemHeight); this._maskFriendList(scrollDist); }; FriendItem = class FriendItem extends PIXI.Graphics { id; name; seen; clan; nameLabel; onlineNow; constructor(id, name, seen, clan) { super(); this.id = id; this.name = name; this.seen = new Date(Date.parse(seen) - 6e4 * new Date().getTimezoneOffset()); this.onlineNow = !!App.Layer.socialMenu.onlineFriends?.includes(this.id); if (this.onlineNow) this.seen = new Date(); this.clan = clan; this.beginFill(16777215, 0.15); this.drawRoundedRect(0, 0, 340, 26, 4); this.endFill(); this.interactive = true; this.tint = 12303291; this.on("mouseover", () => { this.tint = 16777215; }); this.on("mouseout", () => { this.tint = 12303291; }); this.on("mousedown", () => this.emit(SocialMenu.ACCESS_PROFILE, this.id)); this.on("rightdown", () => this.emit(SocialMenu.SHOW_FRIEND_DROPDOWN, this.id)); this.beginFill(this.onlineNow ? config_default.Colors.dotGreen : 30 > Math.round((Date.now() - this.seen.getTime()) / 1e3) ? config_default.Colors.dotOrange : config_default.Colors.dotGrey, 1); this.drawCircle(320, 13, 8); this.endFill(); this.nameLabel = new PIXI.BitmapText(this.name, { fontName: "Open Sans", fontSize: 22 }); this.nameLabel.x = 8; this.nameLabel.y = 2; this.addChild(this.nameLabel); } }; } async function updateFriendList(reload = true) { if (App.Layer.socialMenu.mode == "friends") { try { const friendsOnline = await fetch(`${config_default.api}/onlineplayers`).then((res) => res.json()); App.Layer.socialMenu.onlineFriends = friendsOnline?.filter((f) => App.Layer.socialMenu.friends.find((fr) => fr.id == f)) || []; } catch { App.Layer.socialMenu.onlineFriends = []; } if (reload) await App.Layer.socialMenu.loadFriends(); } } // src/friendSearch.ts function socialMenuHook() { SocialMenu.prototype.maskInvitationList = function(scrollDist) { const pl = "Type to search."; const pad = 8; if (!this.listSearch) { this.listSearch = new InputField("list_search", false, SocialMenu.ItemHeight / 1.5); this.listSearch.setDimensions(this.listContainer.width, SocialMenu.ItemHeight); this.listSearch.forceLowerCase = false; this.listSearch.setMaxChars(128); this.listSearch.setFilter("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890:/?.#-_ "); this.listSearch.x = pad; this.listSearch.y = this.height - this.listSearch.height - pad / 2 - this.infoText.height; this.listSearch.setText(pl); const ls = this; this.listSearch.addListener(InputField.CHANGE, function(d2) { d2 = d2.data.value || ""; if (d2.startsWith(pl) || d2 == pl.slice(0, pl.length - 1)) { d2 = d2.substring(pl.length); ls.listSearch.setText(d2); } ls.maskInvitationList(ls.inviteScrollRatio); }); } this.listContainer.removeChildren = () => { this.listContainer.removeChild(...this.listContainer.children); this.container.removeChild(this.listSearch); }; const searchTerm = this.listSearch.getText() || ""; const filtered = searchTerm && searchTerm !== pl ? this.invites.filter((i) => i.name.toLowerCase().includes(searchTerm.toLowerCase())) : null; this.invites.forEach((i) => { i.alpha = 1; i.isRed = false; if (!i.redReady) { i.redReady = true; i.on("mouseout", function() { i.tint = i.isRed ? config_default.Colors.red : 12303291; }); } }); if (!this.listSearch.parent) this.container.addChild(this.listSearch); const listHeight = SocialMenu.ListHeight - this.listSearch.height - pad / 2; const itemDisplayCount = Math.floor(listHeight / SocialMenu.ItemHeight); if (this.invites.length <= itemDisplayCount) { this.listContainer.y = 0; } else { this.invites.forEach((i) => this.listContainer.removeChild(i)); this.listContainer.y = -(scrollDist * (this.invites.length * SocialMenu.ItemHeight - (listHeight - SocialMenu.ItemHeight))); for (var d = Math.round(Math.abs(this.listContainer.y / SocialMenu.ItemHeight)), displayOffset = 0; displayOffset < itemDisplayCount; displayOffset++) { const inv = this.invites[d + displayOffset]; this.listContainer.addChild(inv); let g; if (0 === displayOffset) { g = d * SocialMenu.ItemHeight + this.listContainer.y; inv.alpha = 0 <= g ? 1 : 1 - 1 / (0.5 * SocialMenu.ItemHeight) * Math.abs(g); } else { displayOffset === itemDisplayCount - 1 ? (g = d * SocialMenu.ItemHeight + this.listContainer.y, inv.alpha = 0 > g ? 1 : 1 - 1 / (0.5 * SocialMenu.ItemHeight) * Math.abs(g)) : inv.alpha = 1; } } this.inviteScrollRatio = scrollDist; } this.listSearch.y = this.height - this.listSearch.height - pad / 2 - this.infoText.height; this.invites.forEach((i) => { i.tint = 12303291; if (!filtered) return; if (filtered.includes(i)) { i.tint = 12603201; i.alpha = 1; i.isRed = true; } else { i.alpha *= 0.5; } }); }; } // src/fullscreenHook.ts function hookFullscreen() { window.addEventListener("keydown", (e) => { if (e.key == "F11") { e.preventDefault(); if (document.fullscreenElement) document.exitFullscreen(); else document.querySelector("html").requestFullscreen(); } }); } // src/hookUtilsMenu.ts function hookUtilsMenu() { const menu = App.Layer.memberMenu; menu.memberButton.parent.removeChild(menu.memberButton); menu.clanButton.parent.removeChild(menu.clanButton); let menuClanState = 0; menu.memberclanButton = new MemberMenuButton("", 16763904, 18); menu.memberclanButton.x = 0; menu.memberclanButton.y = menu.rankingButton.y + 70; menu.memberclanButton.on(MemberMenuButton.BUTTON_PRESSED, () => { if (!["member", "clan"].includes(App.Layer.memberMenu.mode)) menuClanState = 0; menuClanState++; if (menuClanState == 3) menuClanState = 0; title1.tint = title2.tint = config_default.Colors.yellow; switch (menuClanState) { case 0: menu.emit(Layer.Events.MENU_ACCESS); break; case 1: menu.emit(Layer.Events.MEMBER_ACCESS); title1.tint = config_default.Colors.green; break; case 2: menu.emit(Layer.Events.CLAN_BROWSER_ACCESS); title2.tint = config_default.Colors.green; break; } }); menu.memberButton.setActive = (n) => { if (menuClanState == 1 || !menuClanState && !n) { menu.memberclanButton.setActive(n); if (!n) title1.tint = config_default.Colors.yellow; } }; menu.clanButton.setActive = (n) => { if (menuClanState == 2 || !menuClanState && !n) { menu.memberclanButton.setActive(n); if (!n) title2.tint = config_default.Colors.yellow; } }; const ico1 = new PIXI.Sprite(App.CombinedTextures["menu_icon_players"]); ico1.x = 0.25 * menu.memberclanButton.rectWidth; menu.memberclanButton.addChild(ico1); const ico2 = new PIXI.Sprite(App.CombinedTextures["menu_icon_clans"]); ico2.x = 0.75 * menu.memberclanButton.rectWidth; menu.memberclanButton.addChild(ico2); ico1.scale.x = ico1.scale.y = ico2.scale.x = ico2.scale.y = 0.25; ico1.anchor.x = ico1.anchor.y = ico2.anchor.x = ico2.anchor.y = 0.5; ico1.tint = ico2.tint = config_default.Colors.white; ico1.y = ico2.y = 0.37 * menu.memberclanButton.rectHeight; const icosep = new PIXI.Text("/", { fontSize: 16, fontName: "Arial", fill: config_default.Colors.white, lineJoin: "round", strokeThickness: 3 }); icosep.x = 0.5 * menu.memberclanButton.rectWidth; icosep.y = 0.37 * menu.memberclanButton.rectHeight; icosep.anchor.x = icosep.anchor.y = 0.5; menu.memberclanButton.addChild(icosep); const title1 = new PIXI.Text("Players", { fontSize: 11, fontName: "Arial", fill: config_default.Colors.white, lineJoin: "round", strokeThickness: 2 }); title1.x = 0.25 * menu.memberclanButton.rectWidth; menu.memberclanButton.addChild(title1); const title2 = new PIXI.Text("Clans", { fontSize: 14, fontName: "Arial", fill: config_default.Colors.white, lineJoin: "round", strokeThickness: 2 }); title2.x = 0.75 * menu.memberclanButton.rectWidth; menu.memberclanButton.addChild(title2); const titlesep = new PIXI.Text("/", { fontSize: 16, fontName: "Arial", fill: config_default.Colors.white, lineJoin: "round", strokeThickness: 3 }); titlesep.x = 0.5 * menu.memberclanButton.rectWidth; menu.memberclanButton.addChild(titlesep); title1.y = title2.y = titlesep.y = 0.7 * menu.memberclanButton.rectHeight; title1.anchor.x = title1.anchor.y = title2.anchor.x = title2.anchor.y = titlesep.anchor.x = titlesep.anchor.y = 0.5; title1.tint = title2.tint = config_default.Colors.yellow; menu.container.addChild(menu.memberclanButton); const setActive = menu.clanButton.setActive.bind(menu.clanButton); menu.clanButton.setActive = (n) => { setActive(n); if (!n) menu.utilsButton.setActive(0); }; menu.utilsButton = new MemberMenuButton("NinjaIOUtils", 16763904, 15, "gears_icon"); menu.utilsButton.x = 0; menu.utilsButton.y = menu.memberButton.y + 70; menu.utilsButton.on(MemberMenuButton.BUTTON_PRESSED, () => { if (menu.utilsButton.active) { menu.utilsButton.setActive(0); menu.emit(Layer.Events.MENU_ACCESS); return; } menu.emit(Layer.Events.MENU_ACCESS); menu.playButton.setActive(0); menu.utilsButton.setActive(1); App.Layer.utilsMenu.show(); App.Layer.addChild(App.Layer.utilsMenu); App.Layer.emit(Layer.Events.HIDE_MENU); app.onResize(); }); menu.utilsButton.icon.scale.x = menu.utilsButton.icon.scale.y = 0.7; menu.container.addChild(menu.utilsButton); class UtilsMenu extends Feature { ox = 0; oy = 0; off = 0; marginLeft = 0; background = new PIXI.Graphics(); closeButton = new ImgButton(); pmTitle = new PIXI.Text("NinjaIOUtils", { fontName: "Arial", fontSize: 19, lineHeight: 16, fill: config_default.Colors.yellow, strokeThickness: 3, lineJoin: "round" }); constructor() { super(); this.background.interactive = true; this.background.x = 0; this.background.y = 40; this.background.lineStyle(1, 16777215, 0.1, 0); this.background.beginFill(3355443, 0.9); this.background.drawRect(0, 0, 660, 524); this.background.endFill(); this.background.beginFill(0, 0.3); this.background.drawRect(10, 10, 640, 504); this.background.endFill(); this.background.drawRect(15, 42, 630, 2); this.container.addChild(this.background); this.ox = 10; this.oy = 60; this.closeButton.x = this.background.width - 40; this.closeButton.y = this.oy - 6; this.closeButton.scale.x = this.closeButton.scale.y = 0.8; this.closeButton.on(ImgButton.CLICK, () => App.Layer.memberMenu.emit(Layer.Events.MENU_ACCESS)); this.container.addChild(this.closeButton); this.pmTitle.x = 0.5 * this.width - 20; this.pmTitle.y = this.oy - 4; this.pmTitle.anchor.x = 0.5; this.container.addChild(this.pmTitle); this.container.x = 0.5 * -this.width; this.reposition(); } reposition() { this.off = 0; } show() { } } App.Layer.mainMenuHides.push(App.Layer.utilsMenu = new UtilsMenu()); [ "loginMenu", "memberBrowserMenu", "clanBrowserMenu", "registerMenu", "upResetMenu", "profileMenu", "userMenu", "rankingMenu", "newsMenu", "partnerMenu", "serverListMenu", "clanMenu", "serverCreationMenu", "renameMenu", "logoutMenu", "guestProfileMenu" ].forEach((e) => App.Layer[e].hides.push(App.Layer.utilsMenu)); App.Layer.features.push(App.Layer.utilsMenu); } // src/userCommunicationProtocol.ts var commConfig = { prefix: "$NIOU", sep: "|" }; var commPackets = { gameLink: "requestGameLink" }; function decodeUserCommunication(message) { if (!message.startsWith(commConfig.prefix)) return null; const args = message.split(commConfig.sep); if (!Object.values(commPackets).includes(args[1])) return null; return { packet: args[1], args: args.slice(2) }; } async function communicateUser(id, packetID, args) { await APIClient.postFriendMessage(id, [commConfig.prefix, packetID, ...args].join(commConfig.sep), app.credential.id); return true; } // src/joinGameHook.ts function hookJoinGameButton() { const btn = new Button("usr_join"); btn.setText("Join Game"); btn.scale.x = btn.scale.y = 0.75; const repos = () => btn.x = App.Layer.userMenu.ox + App.Layer.userMenu.w - btn.width - 30; repos(); btn.y = App.Layer.userMenu.h - 10; btn.visible = false; btn.addListener(Button.BUTTON_PRESSED, async () => { btn.setText("Requesting link..."); repos(); const rej = (msg) => { btn.setText(msg); repos(); setTimeout(() => (btn.setText("Join Game"), repos()), 4e3); }; const req = String(Date.now()); await communicateUser(App.Layer.userMenu.id, commPackets.gameLink, [req]); const res = (await fetch(`${config_default.api}/requestlink?id=${req}&userid=${App.Layer.userMenu.id}`).then((r) => r.json()))?.[0]; if (res == false) rej("User not in game."); else if (res == true) rej("User in private game."); else if (Array.isArray(res)) { btn.setText("Join Game"); repos(); App.Layer.userMenu.onCloseButtonReleased(); tryJoinLink([res[0], res[1], res[2]]); } else return rej("User not online."); }); App.Layer.userMenu.container.addChild(btn); App.Layer.userMenu._load = App.Layer.userMenu.load; App.Layer.userMenu.load = async (id, type) => { btn.visible = false; await App.Layer.userMenu._load(id, type); await updateFriendList(false); btn.visible = App.Layer.socialMenu.onlineFriends.includes(App.Layer.userMenu.id); }; } // src/mapIdentifier.ts function initMapIdentifier() { Client.prototype.onMessage = function(_a) { const a = Client.decompress(_a.data); try { if (a.type == config_default.PacketTypeMap.data && a.data.type == config_default.PacketTypeMap.joinedMessage && a.data.info.startsWith("You joined ")) { let roomName = a.data.info.substring("You joined ".length); setHash(app.client.server.id, roomName, gameLinkData.pass); } const repFail = () => App.Console.log(`# Failed to identify map. Please report to Meow.`); const repSuccess = (id, name) => App.Console.log(`# Identified map as ${name} (ID: ${id}).`); if (a.type == config_default.PacketTypeMap.data2 && a.data.t == config_default.PacketTypeMap.systemMessage && a.data.msg.startsWith("Joining ")) { const mapName = (a.data.msg.match(/(?: - )(.*)(?: by)/) || [])[1]; this.mapID = 0; if (mapName) { const mapID = config_default.MapIDs[mapName]; if (mapID) { repSuccess(mapID, mapName); this.mapID = mapID; } else repFail(); } else repFail(); } else if (a.type == config_default.PacketTypeMap.data2 && a.data.t == config_default.PacketTypeMap.systemMessage && a.data.msg.startsWith("loading map: ")) { const mapName = a.data.msg.substring("loading map: ".length); this.mapID = 0; if (mapName) { const mapID = config_default.MapIDs[mapName]; if (mapID) { repSuccess(mapID, mapName); this.mapID = mapID; } else repFail(); } else repFail(); } } catch (err) { console.error(err); } this.dispatchEvent(a); }; } // src/matchStartHook.ts var startingLevel = { l: 0 }; function matchStartHook() { App.prototype.realInitGameMode = App.prototype.initGameMode; App.prototype.initGameMode = function(data) { this.realInitGameMode(data); this.game.on(Game.MATCH_START, async function() { startingLevel.l = 0; startingLevel.l = Number((await APIClient.getUserProfile(app.credential.playerid)).experience); }); }; } // src/matchEndHook.ts function matchEndHook() { Game.prototype._endGame = Game.prototype.endGame; Game.prototype.endGame = function(data) { if (SETTINGS.apiKey) { App.Console.log("Attempting to upload match score..."); if (this.manager.isRanked) { try { const leaderIndex = data.leaderboard.id.indexOf(this.sessionId); const statModel = { id: app.credential.playerid, map: app.client.mapID, mode: this.mode, kills: data.leaderboard.kills[leaderIndex], deaths: data.leaderboard.deaths[leaderIndex], caps: data.leaderboard.points ? data.leaderboard.points[leaderIndex] : 0 }; fetch(`${config_default.api}/submit?key=${SETTINGS.apiKey}`, { method: "POST", body: JSON.stringify(statModel), headers: { "Content-Type": "application/json" } }).then((res) => res.json()).then((res) => { if (res.err) { App.Console.log(`Failed to upload match score! ERR_${res.err}`); App.Console.log(`Error: ${res.message}`); } else { App.Console.log("Successfully uploaded match score!"); } }).catch((err) => { App.Console.log("Failed to upload match score! (check console for errors)"); console.error(err); }); } catch (err) { App.Console.log("Failed to upload match score! (check console for errors)"); console.error(err); } } else { App.Console.log("Match is unranked or custom, scores not uploaded."); } } app.game.reticle.children.forEach((c) => c.visible = false); (async () => { const xp = Number((await APIClient.getUserProfile(app.credential.playerid))?.experience) || 0; if (xp && startingLevel.l) { const plevel = Math.min(Math.max(Math.floor(0.2 * Math.sqrt(startingLevel.l / 15.625)), 1), 160); const level = Math.min(Math.max(Math.floor(0.2 * Math.sqrt(xp / 15.625)), 1), 160); const xpNeeded = 15.625 * Math.pow((level + 1) / 0.2, 2) - (1 === level ? 0 : 15.625 * Math.pow(level / 0.2, 2)); const gain = xp - startingLevel.l; App.Console.log(`You gained ${gain.toLocaleString()} (${Math.round(gain / xpNeeded * 1e3) / 10}%) experience this round!`, config_default.Colors.green); if (level > plevel) App.Console.log(`You leveled up! You are now level ${level}.`, config_default.Colors.yellow); } startingLevel.l = 0; })(); return this._endGame(data); }; } // src/repositionItems.ts function reposItems() { if (!app.menu) return; try { App.Layer.partyMenu.reposition(); app.menu.joinButton.x = app.menu.backgroundImage.x + 28; app.menu.serverListButton.x = app.menu.joinButton.x + app.menu.joinButton.width + 26; app.menu.serverListButton.y = app.menu.joinButton.y; app.menu.serverCreateButton.x = app.menu.serverListButton.x - (app.menu.serverCreateButton.width - app.menu.serverListButton.width); app.menu.serverCreateButton.y = app.menu.serverListButton.y + app.menu.serverListButton.height + 14; app.menu.partyButton.x = app.menu.serverCreateButton.x - (app.menu.partyButton.backgroundEnabled.width - 10); app.menu.partyButton.y = app.menu.serverCreateButton.y - 4; app.menu.onlineOption.x = app.menu.joinButton.x + (app.menu.partyButton.x - app.menu.joinButton.x - app.menu.onlineOption.width) * 0.75; app.menu.onlineOption.y = app.menu.serverCreateButton.y + app.menu.serverCreateButton.height / 2 - app.menu.onlineOption.height / 2 + 2; } catch { } } function reindexItems() { app.menu.modeContainer.parent.addChild(app.menu.modeContainer); app.menu.serverContainer.parent.addChild(app.menu.serverContainer); } // src/onlineStatus.ts var failedOnline = false; var wentOnline = false; var onlineSocket; function goOnline() { if (app.credential.accounttype == "guest") { if (failedOnline) return; failedOnline = true; return App.Console.log("Failed to go online: You are not logged in!"); } failedOnline = false; if (onlineSocket) onlineSocket.disconnect(); onlineSocket = io(config_default.api); onlineSocket.on("connect", () => onlineSocket && onlineSocket.emit("init", 0 /* online */, app.credential.playerid)); onlineSocket.on("success", () => { App.Console.log("Successfully went online!"); wentOnline = true; }); onlineSocket.on("fail", (msg) => { wentOnline = false; failedOnline = true; App.Console.log(`Failed to go online: ${msg}`); }); onlineSocket.on("disconnect", () => { onlineSocket = null; if (wentOnline) App.Console.log("Went offline."); wentOnline = false; }); onlineSocket.on("needsLink", async (requestID) => { const messages = JSON.parse(await APIClient.getMessages(app.credential.id))?.messages; const msg = messages?.find((m) => decodeUserCommunication(m.message)?.packet == commPackets.gameLink); if (msg && decodeUserCommunication(msg.message)?.args[0] == requestID) { if (!inGame()) onlineSocket.emit("gotLink", requestID, false); else if (gameLinkData.pass) onlineSocket.emit("gotLink", requestID, true); else onlineSocket.emit("gotLink", requestID, [ gameLinkData.id, gameLinkData.name, gameLinkData.pass ]); } else onlineSocket.emit("gotLink", requestID, null); }); } function goOffline() { if (onlineSocket) { onlineSocket.emit("dc"); onlineSocket.disconnect(); } } function initOnlineOptionHook() { function doOnlineStatusOption() { app.menu.onlineOption = new Checkbox("appearOnline", "Appear Online", true); app.menu.onlineOption.setChecked(SETTINGS.appearOnline); app.menu.onlineOption.on(Checkbox.CHANGE, function(b) { SETTINGS.appearOnline = b; saveSettings(); if (SETTINGS.appearOnline) goOnline(); else goOffline(); }); app.menu.onlineOption.scale.x = app.menu.onlineOption.scale.y = 1.1; app.menu.container.addChild(app.menu.onlineOption); reindexItems(); reposItems(); } doOnlineStatusOption(); app.onShowMenu(() => doOnlineStatusOption()); if (SETTINGS.appearOnline) goOnline(); setInterval(() => SETTINGS.appearOnline && !onlineSocket && !failedOnline && goOnline(), 1e3); } // src/partyMenu.ts function initPartyMenu() { class PartyMenu extends Feature { ox = 0; oy = 0; off = 0; marginLeft = 0; memberList = []; readyState = false; socket; code = ""; background = new PIXI.Graphics(); closeButton = new ImgButton(); pmTitle = new PIXI.Text("Party Manager", { fontName: "Arial", fontSize: 19, lineHeight: 16, fill: config_default.Colors.yellow, strokeThickness: 3, lineJoin: "round" }); startContainer = new PIXI.Container(); partyCodeText = new PIXI.Text("Enter a party code:", { fontName: "Arial", fontSize: 16, fill: config_default.Colors.yellow, strokeThickness: 2, lineJoin: "round" }); codeInput = new InputField("code_input", false, 24); joinPartyButton = new Button("join_party"); loadingContainer = new PIXI.Container(); loadingText = new PIXI.Text("", { fontName: "Arial", fontSize: 16, fill: config_default.Colors.yellow, strokeThickness: 2, lineJoin: "round" }); preGameContainer = new PIXI.Container(); preGameMemberList = new PIXI.Container(); partyCodeListText = new PIXI.Text("Party Code: ", { fontName: "Arial", fontSize: 18, fill: config_default.Colors.yellow, strokeThickness: 2, lineJoin: "round" }); readyButton = new Button("join_party"); leaveButton = new Button("leave_party"); constructor() { super(); this.background.interactive = true; this.background.x = 0; this.background.y = 40; this.background.lineStyle(1, 16777215, 0.1, 0); this.background.beginFill(3355443, 0.9); this.background.drawRect(0, 0, 660, 524); this.background.endFill(); this.background.beginFill(0, 0.3); this.background.drawRect(10, 10, 640, 504); this.background.endFill(); this.background.drawRect(15, 42, 630, 2); this.container.addChild(this.background); this.ox = 10; this.oy = 60; this.closeButton.x = this.background.width - 40; this.closeButton.y = this.oy - 6; this.closeButton.scale.x = this.closeButton.scale.y = 0.8; this.closeButton.on(ImgButton.CLICK, () => App.Layer.memberMenu.emit(Layer.Events.MENU_ACCESS)); this.container.addChild(this.closeButton); this.pmTitle.x = 0.5 * this.width - 20; this.pmTitle.y = this.oy - 4; this.pmTitle.anchor.x = 0.5; this.container.addChild(this.pmTitle); this.container.x = 0.5 * -this.width; this.container.addChild(this.startContainer); this.startContainer.addChild(this.partyCodeText); this.codeInput.setDimensions(190, 35); this.codeInput.forceLowerCase = false; this.codeInput.setMaxChars(16); this.codeInput.setFilter("abcdefghijklmnopqrstuvwxyz."); this.codeInput.addListener(InputField.SUBMIT, () => this.joinParty()); this.startContainer.addChild(this.codeInput); this.joinPartyButton.setText("Join Party"); this.joinPartyButton.scale.x = this.joinPartyButton.scale.y = 0.75; this.joinPartyButton.addListener(Button.BUTTON_RELEASED, () => this.joinParty()); this.startContainer.addChild(this.joinPartyButton); this.container.addChild(this.loadingContainer); this.loadingContainer.visible = false; this.loadingContainer.addChild(this.loadingText); this.container.addChild(this.preGameContainer); this.preGameContainer.visible = false; this.preGameContainer.addChild(this.preGameMemberList); this.preGameContainer.addChild(this.partyCodeListText); this.readyButton.setText("Ready"); this.readyButton.setTint(config_default.Colors.green); this.readyButton.scale.x = this.readyButton.scale.y = 0.75; this.readyButton.addListener(Button.BUTTON_RELEASED, () => this.socket.emit("isReady", !this.readyState)); this.preGameContainer.addChild(this.readyButton); this.leaveButton.setText("Leave Party"); this.leaveButton.setTint(config_default.Colors.red); this.leaveButton.scale.x = this.leaveButton.scale.y = 0.75; this.leaveButton.addListener(Button.BUTTON_RELEASED, () => this.socket.emit("leave")); this.preGameContainer.addChild(this.leaveButton); this.reposition(); } reposition() { this.off = 0; this.startContainer.x = this.ox; this.startContainer.y = this.oy; this.partyCodeText.x = this.marginLeft = this.ox + 10; this.partyCodeText.y = this.off = this.oy; this.codeInput.x = this.marginLeft; this.codeInput.y = this.off += 24; this.joinPartyButton.x = this.marginLeft + this.codeInput.width + this.joinPartyButton.width / 6; this.joinPartyButton.y = this.off + 4; this.off = 0; this.loadingContainer.x = this.ox; this.loadingContainer.y = this.oy; this.loadingText.x = this.marginLeft = this.ox + 10; this.loadingText.y = this.off = this.oy; this.off = 0; this.preGameContainer.x = this.ox; this.preGameContainer.y = this.oy; this.partyCodeListText.x = this.marginLeft = this.ox + 10; this.partyCodeListText.y = this.off += 38; this.preGameMemberList.x = this.ox; this.preGameMemberList.y = this.off += 4; this.readyButton.x = this.width - this.readyButton.width - 34; this.readyButton.y = this.height - this.readyButton.height * 2 - 6; this.leaveButton.x = this.ox + 10; this.leaveButton.y = this.readyButton.y; } setTitle(text = "Party Manager") { this.pmTitle.setText(text); } startLoading(text) { this.hideAllWindows(); this.loadingContainer.visible = true; this.loadingText.text = text; } hideAllWindows() { this.startContainer.visible = this.loadingContainer.visible = this.preGameContainer.visible = false; } show() { } joinParty() { const code = this.codeInput.getText(); if (!code.trim()) return this.codeInput.markInvalid(), this.codeInput.setFocus(true); this.startLoading("Joining party..."); this.socket = io(config_default.api); this.socket.once("connect", () => { this.socket.emit("init", 1 /* party */, code, app.credential.username); this.socket.once("denyJoin", () => this.startLoading("Invalid party code.")); this.socket.once("joinedParty", (code2) => { this.code = code2; this.hideAllWindows(); this.preGameContainer.visible = true; this.partyCodeListText.text = `Party Code: ${this.code}`; }); this.socket.on("updateMembers", (m) => this.updateMembers(m)); }); this.socket.on("joinErr", (err) => { this.socket.disconnect(); this.startLoading(err); setTimeout(() => { this.hideAllWindows(); this.startContainer.visible = true; }, 2500); }); this.socket.on("disconnect", () => { this.socket.disconnect(); this.hideAllWindows(); this.startContainer.visible = true; this.memberList = []; }); this.socket.on("connect_error", () => { this.socket.disconnect(); this.startLoading("Error connecting to socket."); setTimeout(() => { this.hideAllWindows(); this.startContainer.visible = true; }, 2500); }); } isPartyOwner() { return this.memberList[0].me; } updateMembers(list) { this.memberList = list; this.preGameMemberList.removeChildren(); this.reposition(); this.memberList.forEach((m, i) => { if (m.me) { this.readyState = m.ready; this.readyButton.setText(this.readyState ? "Not Ready" : "Ready"); this.readyButton.setTint(this.readyState ? config_default.Colors.red : config_default.Colors.green); this.reposition(); } const text = new PIXI.Text(m.name + (m.ready ? " (Ready)" : " (Not Ready)"), { fontName: "Arial", fontSize: 16, fill: m.ready ? config_default.Colors.green : config_default.Colors.white, strokeThickness: 2, lineJoin: "round" }); text.x = this.preGameMemberList.x; text.y = this.preGameMemberList.y + 14 + 18 * (i || -0.5); this.preGameMemberList.addChild(text); if (this.isPartyOwner() && i) { const banBtn = new Button(`ban_mem`); banBtn.setText("Ban"); banBtn.setTint(config_default.Colors.red); banBtn.scale.x = banBtn.scale.y = 0.5; banBtn.x = text.x + text.width + 12; banBtn.y = text.y; banBtn.addListener(Button.BUTTON_RELEASED, () => this.socket.emit("banMem", m.name)); this.preGameMemberList.addChild(banBtn); } }); } } function doPartyButton() { App.Layer.mainMenuHides.push(App.Layer.partyMenu = new PartyMenu()); [ "loginMenu", "memberBrowserMenu", "clanBrowserMenu", "registerMenu", "upResetMenu", "profileMenu", "userMenu", "rankingMenu", "newsMenu", "partnerMenu", "serverListMenu", "clanMenu", "serverCreationMenu", "renameMenu", "logoutMenu", "guestProfileMenu" ].forEach((e) => App.Layer[e].hides.push(App.Layer.partyMenu)); App.Layer.features.push(App.Layer.partyMenu); app.menu.partyButton = new MemberMenuButton("Party", config_default.Colors.yellow, 18, "head_alpha", false); app.menu.partyButton.on(MemberMenuButton.BUTTON_PRESSED, function() { App.Layer.mainMenuHides.forEach(function(c) { return App.Layer.hideFeature(c); }); App.Layer.memberMenu.playButton.setActive(0); App.Layer.partyMenu.show(); App.Layer.addChild(App.Layer.partyMenu); App.Layer.emit(Layer.Events.HIDE_MENU); app.onResize(); }); app.menu.partyButton.scale = { x: 0.8, y: 0.8 }; app.menu.partyButton.icon.scale = { x: 0.6, y: 0.6 }; app.menu.container.addChild(app.menu.partyButton); reindexItems(); } doPartyButton(); app.onShowMenu(() => doPartyButton()); app.menu._resize = app.menu.resize; app.menu.resize = () => { app.menu._resize(); reposItems(); }; } // src/playerDataHook.ts function hookPlayerData() { Manager.prototype._createLocalPlayer = Manager.prototype.createLocalPlayer; Manager.prototype.createLocalPlayer = function(...d) { const plr = this._createLocalPlayer(...d); this.localPlayer.update(null, null, null, true); return plr; }; Player.prototype._update = Player.prototype.update; Player.prototype.update = function(...d) { const doForce = d[3] == true; const upd = !doForce ? this._update(...d) : null; if (SETTINGS.helpfulUI) { const isLocal = doForce || this.id == app.game.manager.getLocalPlayer()?.id, hideHUD = this.alive && (isLocal || !this.prone) && !app.game.gameover; const hpbar = this.hpbar || (this.hpbar = new HealthBar()); if (!hpbar.parent) { this.visual.addChild(hpbar); hpbar.scale.x = hpbar.scale.y = 0.25; hpbar.x = -hpbar.width / 2; hpbar.y = isLocal ? -65 : -50; hpbar.background.visible = false; } if (hpbar.getValue() !== this.health) hpbar.setValue(this.health); hpbar.visible = hideHUD; if (isLocal) { const jetbar = this.jetbar || (this.jetbar = new JetBar()); if (!jetbar.parent) { this.visual.addChild(jetbar); jetbar.scale.x = jetbar.scale.y = 0.25; jetbar.x = -jetbar.width / 2; jetbar.y = -50; jetbar.background.visible = false; } this.jetLeft = app.game.hud.jetBar.getValue(); if (jetbar.getValue() !== this.jetLeft) jetbar.setValue(this.jetLeft); if (!app.game.hud.jetBar._setValue) { app.game.hud.jetBar._setValue = app.game.hud.jetBar.setValue; app.game.hud.jetBar.setValue = (v) => { jetbar.maxValue = app.game.hud.jetBar.maxValue; jetbar.setValue(v); return app.game.hud.jetBar._setValue(v); }; } jetbar.visible = hideHUD; const ammobar = this.ammobar || (this.ammobar = new PIXI.Graphics()); if (!ammobar.parent) { app.game.reticle.addChild(ammobar); ammobar.x = -18; ammobar.y = 26; ammobar.scale.x = ammobar.scale.y = 0.3; ammobar.rotation = -Math.PI / 2; } this.ammoLeft = app.game.hud.ammoBar.getValue(); if (ammobar.value !== this.ammoLeft) ammobar.value = this.ammoLeft; (ammobar.update = ammobar.update || (() => { const delta = Date.now() - ammobar.lastUpdate, maxReload = config_default.WeaponReloadTimes[Object.entries(ItemList).find((e) => e[1] == ammobar.item)?.[0]] || 0; if (ammobar.reloadTime) ammobar.reloadTime = ammobar.reloadTime - delta || -1; else ammobar.reloadTime = maxReload; ammobar.lastUpdate = Date.now(); ammobar.value = app.game.hud.ammoBar.getValue(); ammobar.max = app.game.hud.ammoBar.maxValue; if (!maxReload || ammobar.value > 0) ammobar.reloadTime = 0; const w = 12, r = 30, clr = ammobar.value > 0 ? config_default.Colors.yellow : config_default.Colors.red; ammobar.clear(); ammobar.lineStyle(w, clr, 0.2); ammobar.arc(0, 0, r, 0, Math.PI * 2); ammobar.lineStyle(w, clr); ammobar.arc(0, 0, r, 0, Math.PI * 2 * (ammobar.value > 0 ? ammobar.value / ammobar.max : ammobar.reloadTime > 0 ? ammobar.reloadTime / Math.max(0, maxReload) || ammobar.reloadTime : 1)); ammobar.endFill(); if (ammobar.reloadTime > 0) setTimeout(() => ammobar.update(), 10); }))(); if (!app.game.hud.ammoBar._update) { app.game.hud.ammoBar._update = app.game.hud.ammoBar.update; app.game.hud.ammoBar.update = () => { app.game.hud.ammoBar._update(); ammobar.update(); }; } if (!app.game.hud.ammoBar._setValue) { app.game.hud.ammoBar._setValue = app.game.hud.ammoBar.setValue; app.game.hud.ammoBar.setValue = (v) => { app.game.hud.ammoBar._setValue(v); ammobar.update(); }; } if (!app.game.hud.ammoBar._setItem) { app.game.hud.ammoBar._setItem = app.game.hud.ammoBar.setItem; app.game.hud.ammoBar.setItem = (i) => { app.game.hud.ammoBar._setItem(i); ammobar.item = i ? i.t : null; }; } const beltbar = this.beltbar || (this.beltbar = new PIXI.Container()); if (!beltbar.parent) { app.game.reticle.addChild(beltbar); beltbar.x = 10; beltbar.y = 26; } (beltbar.update = beltbar.update || (() => { beltbar.removeChildren(); beltbar.value = app.game.hud.ammoBar.beltAmmoAmount; if (beltbar.value <= 0) beltbar.item = null; if (beltbar.item) { for (let i = beltbar.value; i--; i > 0) { const beltIcon = new SpriteMap[beltbar.item](); beltIcon.anchor.x = beltIcon.anchor.y = 0; beltIcon.x = i * 16; beltIcon.y = 0; beltIcon.width = beltIcon.height = 0.15; beltbar.addChild(beltIcon); } } }))(); if (!app.game.hud.ammoBar._setBeltItem) { app.game.hud.ammoBar._setBeltItem = app.game.hud.ammoBar.setBeltItem; app.game.hud.ammoBar.setBeltItem = (i) => { app.game.hud.ammoBar._setBeltItem(i); beltbar.item = i ? i.t.substring(1) : null; beltbar.update(); }; } if (!app.game.hud.ammoBar._decrementBeltValue) { app.game.hud.ammoBar._decrementBeltValue = app.game.hud.ammoBar.decrementBeltValue; app.game.hud.ammoBar.decrementBeltValue = () => { app.game.hud.ammoBar._decrementBeltValue(); beltbar.update(); }; } app.game.reticle.children.forEach((c) => c.visible = doForce || hideHUD); } } return upd; }; } // src/preloaderHook.ts function hookPreloader() { const preloader = document.getElementById("preloader"); if (!preloader) return; const tst = setInterval(function() { try { App.RemovePreloader = function() { let b = 1; preloader.style.pointerEvents = "none"; const c = setInterval(() => { 0 < b - 0.05 ? (preloader.style.opacity = String(b), b -= 0.05) : (preloader.remove(), clearInterval(c)); }, 1e3 / 60); }; clearInterval(tst); } catch { } }, 10); const st = document.createElement("style"); st.innerHTML = "#preloader{cursor:default!important;}#texresetbtn{cursor:pointer!important;}"; document.head.appendChild(st); preloader.style.top = preloader.style.left = "0px"; const reset = document.createElement("button"); reset.id = "texresetbtn"; Object.entries({ backgroundColor: "#FF0000", border: "1px solid rgba(255,255,255,0.7)", position: "fixed", bottom: "1rem", right: "1rem", opacity: "0.5", borderRadius: "6px", padding: "2px", color: "white" }).forEach((s) => { reset.style[s[0]] = s[1]; }); reset.innerText = "Skip Loading Texture Packs"; reset.onclick = () => { app.proceed(); window.SKIP_TEX_LOAD = true; reset.remove(); }; preloader.appendChild(reset); } // src/socialMenuHook.ts function hookSocialMenu() { const menu = App.Layer.socialMenu; class RecordingMenu extends Feature { mx = 0; my = 0; down = false; dragging = false; title; background; closeButton; startButton; dlWEBM; dlMP4; donedl; previd; recordingSince = 0; preContainer; postContainer; constructor() { super(); this.background = new PIXI.Graphics(); this.background.lineStyle(1, 16777215, 0.1, 0); this.background.beginFill(3355443, 0.9); this.background.drawRoundedRect(0, 0, 400, 320, 4); this.background.interactive = true; this.container.addChild(this.background); this.background.on("mousedown", (a) => { a.stopPropagation(); this.down = true; this.ox = a.data.global.x; this.oy = a.data.global.y; }); this.background.on("mouseup", (a) => { a.stopPropagation(); this.down = this.dragging = false; }); this.background.on("mousemove", (a) => { if (this.down) { let b = a.data.global.x; a = a.data.global.y; let c = b - this.ox, d = a - this.oy; this.x += c; this.y += d; this.ox = b; this.oy = a; 0 < Math.sqrt(c * c + d * d) && (this.dragging = true); } }); this.title = new PIXI.BitmapText("Screen Recorder", { fontName: "Open Sans", fontSize: 28 }); this.title.x = this.mx = 8; this.title.y = this.my = 6; this.title.tint = config_default.Colors.red; this.container.addChild(this.title); setInterval(() => this.updateTitle(), 900); this.closeButton = new PIXI.Sprite(App.CombinedTextures.exit); this.closeButton.scale.x = this.closeButton.scale.y = 0.8; this.closeButton.alpha = 0.5; this.closeButton.interactive = true; this.closeButton.x = this.width - 28; this.closeButton.y = 2; this.closeButton.on("mousedown", (a) => { this.closeButton.alpha = 0.5; this.parent.removeChild(this); AudioEffects.ButtonClick.audio.play(); }); this.closeButton.on("mouseover", (a) => { this.closeButton.alpha = 1; AudioEffects.ButtonHover.audio.play(); }); this.closeButton.on("mouseout", (a) => { this.closeButton.alpha = 0.5; }); this.container.addChild(this.closeButton); this.container.addChild(this.preContainer = new PIXI.Container()); this.container.addChild(this.postContainer = new PIXI.Container()); this.postContainer.visible = false; this.startButton = new Button("start_rec"); this.setupStartButton(); this.startButton.scale.x = this.startButton.scale.y = 0.75; this.startButton.x = this.mx + 10; this.startButton.y = this.my += this.title.height + 10; this.startButton.addListener(Button.BUTTON_RELEASED, () => { if (this.recordingSince && this.recorder) this.recorder.stop(); else this.onRecStart(); }); this.preContainer.addChild(this.startButton); this.mx = 18; this.my = this.title.height + 16; this.donedl = new Button("dl_done"); this.donedl.setText("Done"); this.donedl.setTint(config_default.Colors.yellow); this.donedl.scale.x = this.donedl.scale.y = 0.7; this.donedl.x = this.mx; this.donedl.y = this.my; this.donedl.addListener(Button.BUTTON_RELEASED, () => { this.preContainer.visible = true; this.postContainer.visible = false; this.stream = this.recorder = this.recordedChunks = null; }); this.postContainer.addChild(this.donedl); this.previd = new Button("dl_prev"); this.previd.setText("Preview"); this.previd.setTint(config_default.Colors.green); this.previd.scale.x = this.previd.scale.y = 0.7; this.previd.x = this.donedl.x + this.donedl.width + 6; this.previd.y = this.my; this.previd.addListener(Button.BUTTON_RELEASED, () => window.open(URL.createObjectURL(new Blob(this.recordedChunks, { type: "video/webm" })), "_blank")); this.postContainer.addChild(this.previd); this.dlWEBM = new Button("dl_webm"); this.dlWEBM.setText("Download WEBM"); this.dlWEBM.scale.x = this.dlWEBM.scale.y = 0.7; this.dlWEBM.x = this.mx; this.dlWEBM.y = this.my += this.donedl.height + 8; this.dlWEBM.addListener(Button.BUTTON_RELEASED, () => { this.dlBlob(new Blob(this.recordedChunks, { type: "video/webm" })); }); this.postContainer.addChild(this.dlWEBM); this.dlMP4 = new Button("dl_mp4"); this.dlMP4.setText("Convert to MP4 (slow)"); this.dlMP4.scale.x = this.dlMP4.scale.y = 0.7; this.dlMP4.x = this.dlWEBM.x + this.dlWEBM.width + 6; this.dlMP4.y = this.dlWEBM.y; this.dlMP4.addListener(Button.BUTTON_RELEASED, async () => { try { this.dlMP4.setText("Starting..."); const worker = new Worker(URL.createObjectURL(new Blob([await fetch(`${config_default.api}/ffmpeg.js`).then((r) => r.text())], { type: "application/javascript" }))); worker.onmessage = async (e) => { try { const msg = e.data; switch (msg.type) { case "ready": this.dlMP4.setText("Converting..."); console.log(`[ConversionWorker] Worker ready!`); worker.postMessage({ bypass: true, type: "run", arguments: [ "-i", "video.webm", "-vf", "crop=trunc(iw/2)*2:trunc(ih/2)*2", "-preset", "ultrafast", "vid.mp4" ], MEMFS: [ { name: "video.webm", data: await new Blob(this.recordedChunks, { type: "video/webm" }).arrayBuffer() } ] }); break; case "stdout": case "stderr": console.log(`[ConversionWorker.${msg.type}] ${msg.data}`); if (msg.data == "Conversion failed!") { this.dlMP4.setText("Error!"); } break; case "done": this.dlMP4.setText("Convert to MP4 (slow)"); if (typeof msg.data !== "string" && msg.data.MEMFS) { this.dlBlob(new Blob([msg.data.MEMFS[0].data], { type: "video/mp4" })); } break; } } catch (err) { alert(`Error with conversion: ${err}, ${err.stack}`); } }; } catch (err) { alert(`Error creating conversion worker: ${err}, ${err.stack}`); } }); this.postContainer.addChild(this.dlMP4); } updateTitle() { const timeRec = Math.floor((Date.now() - this.recordingSince) / 1e3); this.title.text = this.recordingSince ? `Recording... (${Math.floor(timeRec / 60).toString().padStart(2, "0")}:${Math.floor(timeRec % 60).toString().padStart(2, "0")})` : `Screen Recorder`; } setupStartButton() { this.startButton.setText("Start Recording"); this.startButton.setTint(config_default.Colors.green); } stream; recorder; recordedChunks; onRecStart() { this.recordingSince = Date.now(); this.updateTitle(); this.startButton.setText("Stop Recording"); this.startButton.setTint(config_default.Colors.red); try { this.stream = new MediaStream(); const howlStream = Howler.ctx.createMediaStreamDestination(); Howler.masterGain.connect(howlStream); const vid = App.Renderer.view.captureStream(); this.stream.addTrack(howlStream.stream.getTracks()[0]); this.stream.addTrack(vid.getTracks()[0]); this.recordedChunks = []; this.recorder = new MediaRecorder(this.stream, { bitsPerSecond: 4e7 }); this.recorder.ondataavailable = (e) => this.recordedChunks.push(e.data); this.recorder.onstop = () => this.onRecStop(); this.recorder.start(); } catch (err) { alert(`Error starting recording: ${err}, ${err.stack}`); } } onRecStop() { this.recordingSince = 0; this.updateTitle(); this.setupStartButton(); this.preContainer.visible = false; this.postContainer.visible = true; this.dlMP4.setText("Convert to MP4 (slow)"); } dlBlob(blob) { const link = document.createElement("a"); link.href = URL.createObjectURL(blob); link.download = `NinjaRecording-${new Date().toISOString().split(".")[0].replace("T", "-").replace(/:/g, ".")}.${blob.type.split("/").pop()}`; document.body.appendChild(link); link.click(); link.remove(); } } class SocialMenuDropdown extends PIXI.Container { background; constructor() { super(); this.background = new PIXI.Graphics(); this.background.clear(); this.background.lineStyle(1, 16777215, 0.4, 0.3); this.background.beginFill(5592405, 0.9); this.background.drawRoundedRect(0, 0, 110, 64, 4); this.background.endFill(); this.addChild(this.background); const clanChatBtn = new SocialMenuDropdownActionRow("Clan Chat", () => menu.onClanChatButtonReleased.bind(menu)(), config_default.Colors.white); const recordBtn = new SocialMenuDropdownActionRow("Record", () => { menu.addChild(menu.recMenu); menu.recMenu.x = -Math.abs(menu.width - menu.recMenu.width) / 2; menu.recMenu.y = -Math.abs(menu.height - menu.recMenu.height) / 2; }, config_default.Colors.red); clanChatBtn.x = 10; clanChatBtn.y = 4; recordBtn.x = 10; recordBtn.y = 34; this.addChild(clanChatBtn); this.addChild(recordBtn); } } class SocialMenuDropdownActionRow extends PIXI.Container { actionText; constructor(text, onclick, color) { super(); this.text = text; this.actionText = new PIXI.BitmapText(this.text, { fontName: "Open Sans", fontSize: 24 }); this.hitArea = new PIXI.Rectangle(0, 0, 100, 30); this.actionText.tint = color; this.actionText.alpha = 0.7; this.interactive = true; this.on("mouseover", () => this.actionText.alpha = 1); this.on("mouseout", () => this.actionText.alpha = 0.7); this.on("mousedown", (e) => { e.stopPropagation(); onclick(); }); this.addChild(this.actionText); } } menu.clanChatButton.destroy(); const dropdownMenu = new SocialMenuDropdown(); menu.recMenu = new RecordingMenu(); menu.dropdownButton = new Button("drop"); menu.dropdownButton.setText("V"); menu.dropdownButton.scale.x = menu.dropdownButton.scale.y = 0.7; menu.dropdownButton.addListener(Button.BUTTON_RELEASED, () => { if (dropdownMenu.parent) return menu.container.removeChild(dropdownMenu); menu.container.addChild(dropdownMenu); dropdownMenu.x = menu.dropdownButton.x - dropdownMenu.width + menu.dropdownButton.width; dropdownMenu.y = menu.dropdownButton.y + menu.dropdownButton.parent.y + menu.dropdownButton.height; }); menu.dropdownButton.x = menu.clanButton.x + menu.clanButton.width + 6; menu.dropdownButton.y = 8; menu.container.addChild(menu.dropdownButton); } // src/texturePack.ts var import_localforage2 = __toESM(require_localforage()); function hookTextureLoader() { class WorkerNew extends Worker { _postMessage; constructor(url, opts) { super(url, opts); this._postMessage = this.postMessage; this.postMessage = this.newPostMessage; } newPostMessage(data, ...args) { if (SETTINGS.texturePack && !window.SKIP_TEX_LOAD && !data?.bypass) { fetch(`${config_default.api}/packs/${SETTINGS.texturePack}`).then((r) => r.json()).then(async (pack) => { if (pack && data.id == "loadImageBitmap" && typeof data.data[0] == "string" && SETTINGS.texturePack) { const orig = data.data[0]; const isCustom = SETTINGS.texturePack == config_default.customDelimiter; if ((pack.hasCombined || isCustom) && orig.includes("ninja.io") && orig.includes("combined") && orig.endsWith(".png")) data.data[0] = `${config_default.api}/packs/${SETTINGS.texturePack}/combined.png?v=${config_default.actualGameVersion}`; if ((pack.hasSeamless || isCustom) && orig.includes("ninja.io") && orig.includes("seamless") && orig.endsWith(".png")) data.data[0] = `${config_default.api}/packs/${SETTINGS.texturePack}/seamless.png?v=${config_default.actualGameVersion}`; if (data.data[0].startsWith(config_default.api) && isCustom) { const zip = await import_localforage2.default.getItem("custom_pack"); if (zip) { const form = new FormData(); form.append("zip", zip); const res = await fetch(data.data[0], { method: "POST", body: form }).then((r) => r.blob()); data.data[0] = URL.createObjectURL(res); } else data.data[0] = orig; } } this._postMessage(data, ...args); }).catch(() => { this._postMessage(data, ...args); }); } else this._postMessage(data, ...args); } } window.Worker = Worker = WorkerNew; } // src/updateChecker.ts async function checkUpdate() { try { const newest = await fetch(`${config_default.api}/ver`).then((r) => r.text()); const num = (str) => Number(str.replace(/\./, "")); if (num(newest) > num(config_default.ver)) { App.Console.log(`Hey! A new version of NinjaIOUtils is available. (${newest})`, config_default.Colors.red); } } catch { } } // src/index.ts hookTextureLoader(); if (!navigator.clipboard.readText) { navigator.clipboard.readText = function() { return new Promise((res) => res(prompt("Paste text now.") || "")); }; } hookPreloader(); window.NIOUCheckReload = () => { if (!app.game) return "Enter a game..."; let reloadTime = 0, times = []; if (!app.game.hud.ammoBar.__setValue) app.game.hud.ammoBar.__setValue = app.game.hud.ammoBar.setValue; app.game.hud.ammoBar.setValue = (v) => { if (v <= 0 && !reloadTime) reloadTime = Date.now(); if (reloadTime && v > 0) { const t = Date.now() - reloadTime; times.push(t); const avg = (num) => { const total = times.slice(-num); return Math.round(total.reduce((t2, c) => t2 + c, 0) / total.length); }; console.log(`Time to reload: ${t}ms Last 5 avg: ${avg(5)}ms Last 10 avg: ${avg(10)}ms Last 15 avg: ${avg(15)}ms`); reloadTime = 0; } app.game.hud.ammoBar.__setValue(v); }; return "Shoot gun till reload. Reload the page to disable this logging."; }; var socialMenuDone = false; var testing = setInterval(() => { if (!socialMenuDone) { try { if (SocialMenu && FriendItem) { socialMenuHook(); initFriendOnlineHook(); socialMenuDone = true; } else return; } catch { return; } } try { if (!app || !app.menu || !app.menu.joinButton || typeof app.status.updating !== "boolean" || !APIClient || !APIClient.postCreateGame) return; } catch { return; } clearInterval(testing); App.Console.log("Loading NinjaIOUtils..."); if (app.credential.accounttype == "guest") alert(`NinjaIOUtils works best when you are logged in! No support will be provided to logged out users experiencing issues, sorry.`); app._showMenu = app.showMenu; const menuListeners = []; app.onShowMenu = (cb) => { menuListeners.push(cb); }; app.showMenu = function() { app._showMenu(); menuListeners.forEach((l) => l()); reposItems(); }; showFPS(); matchStartHook(); matchEndHook(); applySettingsHook(); initShareURLHook(); App.Stats.realSetPing = App.Stats.setPing; App.Stats.setPing = function(ping) { App.Stats.ping = ping; return App.Stats.realSetPing(ping); }; App.Console.consoleInput.addListener(InputField.CHANGE, () => { if (SETTINGS.typewriter) AudioEffects.ButtonHover.audio.play(); }); initOnlineOptionHook(); initPartyMenu(); App.Console.log("Successfully injected party menu button."); settingsTab(); App.Console.log("Successfully injected settings tab."); hookFullscreen(); reposItems(); initMapIdentifier(); hookSocialMenu(); window.addEventListener("resize", () => reposItems()); window.addEventListener("focus", () => setTimeout(() => reposItems(), 50)); setInterval(() => reposItems(), 100); document.addEventListener("keydown", handleKeyDown); updateFriendList(); hookJoinGameButton(); setTimeout(() => updateFriendList(), 2e3); setInterval(() => updateFriendList(), 6e4); checkUpdate(); hookUtilsMenu(); hookPlayerData(); app.onResize = window.eval(`(function ${app.onResize.toString().replace(`App.Scale=b`, `b=App.NUIScale||b,App.Scale=b`)})`); App.NUIScale = SETTINGS.uiScale; app.onResize(); App.Console.log(`NinjaIOUtils ${config_default.ver} Loaded Successfully!`); tryJoinLink(); }, 50); })(); /*! localForage -- Offline Storage, Improved Version 1.10.0 https://localforage.github.io/localForage (c) 2013-2017 Mozilla, Apache License 2.0 */