KStats = function(input){ const self = this, default_settings = { autostart : true, nulls : false, default_vaalue : null, ajax_timeout : 2000, settings_overwrite : false, kstats_url : "https://kstats.k3y.pw/api/uCDY3brWxEJrJywm2sFcKo1d8oaUdmxTTrv3VGuhpyRDpPYXyKeHWeknh/{session}/ecma/set", frames_per_second : 1, milliseconds_per_connection : 2000, session_cookie_name : "kstats_session_id", session_timeout : 60000, data_key : "kstats_data" }, custom = {}, threads = []; let started = false, thread = null, last_connection = 0, milliseconds_per_connection = null, register_thread = null, id = null, url = null, session = null, data_key = null; const default_value = this.default_value = (_default, nulls) => _default !== undefined && (_default !== null || (typeof nulls == "boolean" ? nulls : settings("nulls", null, false, false))) ? _default : settings(["default_value", "default"], null, null, true); const settings = this.settings = (names, inputs, _default, nulls) => { if(!names) return default_value(_default, nulls); const l = (names.push ? names : names = [names]).length, m = (inputs = (inputs ? inputs.push ? inputs : [inputs] : []).concat([input, custom, default_settings])).length; typeof nulls != "boolean" && (nulls = settings("nulls", null, false, false)); for(let j = 0; j < m; j ++) if(typeof inputs[j] == "object") for(let i = 0; i < l; i ++) if(names[i] && inputs[j][names[i]] !== undefined && (nulls || inputs[j][names[i]] !== null)) return inputs[j][names[i]]; return default_value(_default, nulls); }; const load = this.load = (url, callback) => { let ended = false; const ajax = new XMLHttpRequest(), timeout = settings(["ajax_timeout", "load_timeout", "timeout"]), date = Date.now(), end = message => { if(ended) return; ended = true; typeof callback == "function" && callback(ajax.responseText, ajax.status, ajax.readyState, message == "OK", message); }; ajax.open("get", url, true); ajax.timeout = timeout; ajax.onreadystatechange = () => { if(ended) return; if(ajax.readyState == 4) end((ajax.status >= 200 && ajax.status < 300) || [301, 302, 304].includes(ajax.status) ? "OK" : "HTTP_ERROR"); else if(Date.now() - date > timeout) end("FORCED_TIMEOUT"); }; ajax.send(null); ajax.onabort = () => end("ABORTED"), ajax.onerror = () => end("ERROR"); ajax.ontimeout = () => end("TIMEOUT"); return ajax; }; const settings_add_i = (items, overwrite, callback, i) => { if(i >= items.length){ typeof callback == "function" && callback(); return; }; if(items[i]){ if(typeof items[i] == "string"){ let json; try{ json = JSON.parse(items[i]); }catch(exception){}; if(json) settings_add_i(json.push ? json : [json], overwrite, end, 0); else load(json, response => { try{ json = JSON.parse(response); }catch(exception){}; if(json) settings_add_i(json.push ? json : [json], overwrite, end, 0); else end(); }); return; }; if(typeof items[i] == "object"){ if(items[i].push){ settings_add_i(items[i], overwrite, end, 0); return; }; for(const key in items[i]) (overwrite || custom[key] === undefined) && (custom[key] = items[i][key]); }; }; end(); }; const settings_add = this.settings_add = (items, overwrite, callback) => settings_add_i(items ? items.push ? items : [items] : [], typeof overwrite == "boolean" ? overwrite : settings(["settings_overwrite", "overwrite"]), callback, 0); const threads_method = () => threads.forEach(thread => thread && thread()); const threads_start = this.threads_start = () => thread === null && (thread = setInterval(threads_method, 1000 / settings(["frames_per_second", "fps"]))); this.threads_stop = () => { if(thread === null) return; clearInterval(thread); thread = null; }; const threads_add = this.threads_add = method => { if(typeof method != "function") return null; let i = 0; const l = threads.length; for(; i < l; i ++) if(!threads[i]) break; threads[i] = method; return i; }; const threads_remove = this.threads_remove = i => !isNaN(i) && threads[i] && (threads[i] = null); const string_variables = this.string_variables = (string, variables, _default) => { if(!variables) variables = []; else if(!variables.push) variables = [variables]; for(let i = variables.length - 1; i >= 0; i --) typeof variables[i] != "object" && (variables = variables.splice(i, 1)); const l = variables.length; return string.replace(/\{([^\{\}]+)\}/g, (...arguments) => { for(let i = 0; i < l; i ++) if(variables[i][arguments[1]] !== undefined) return variables[i][arguments[1]]; return _default !== undefined ? _default : arguments[0]; }); }; const update = () => load(tmp = string_variables(url || (url = settings(["kstats_url", "url"])), { session : session }) + (id ? "/" + id : "") + "?" + (data_key || (data_key = settings("data_key"))) + "=" + btoa(JSON.stringify({ url : window.location.href.replace(/^([^#]+)(\#.*)?$/, "$1") })), (...arguments) => { if(!arguments[0]) return; const data = JSON.parse(arguments[0]), current_session = Number(data.data.variables.current_session); session != current_session && (document.cookie = settings("session_cookie_name") + "=" + (session = current_session) + ";expires=" + new Date(Date.now() + settings("session_timeout")).toUTCString() + ";path=/;SameSite=Lax"); if(id === null){ id = data.data.variables.id; addEventListener("beforeunload", update); }; }); const register_event = () => { const date = Date.now(); if(date - last_connection > milliseconds_per_connection){ last_connection = date; update(); }; }; this.start = () => { if(started) return; started = true; settings_add(settings("settings_files"), true, () => { const session_pattern = new RegExp("^" + settings("session_cookie_name") + "=(.+)$"); milliseconds_per_connection = settings("milliseconds_per_connection"); !decodeURIComponent(document.cookie).split(";").some(cookie => { const matches = cookie.trim().match(session_pattern); if(matches){ session = Number(matches[1]); return true; }; }) && (session = 0); threads_start(); register_thread = threads_add(register_event); }); }; const construct = () => { settings("autostart") && self.start(); }; construct(); };