248 lines
8.2 KiB
JavaScript
Executable File
248 lines
8.2 KiB
JavaScript
Executable File
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();
|
|
|
|
};
|