apply and fix ESlint

This commit is contained in:
MathMan05 2024-09-02 15:59:56 -05:00
parent f8b80b65fe
commit 19f08a6408
57 changed files with 8070 additions and 7943 deletions

View file

@ -9,7 +9,7 @@ class Voice {
source;
constructor(wave, freq, volume = 1) {
this.audioCtx = new (window.AudioContext)();
this.info = { wave: wave, freq: freq };
this.info = { wave, freq };
this.playing = false;
this.myArrayBuffer = this.audioCtx.createBuffer(1, this.audioCtx.sampleRate, this.audioCtx.sampleRate);
this.gainNode = this.audioCtx.createGain();
@ -43,7 +43,7 @@ class Voice {
}
}
waveFunction() {
if (typeof this.wave === 'function') {
if (typeof this.wave === "function") {
return this.wave;
}
switch (this.wave) {
@ -92,9 +92,15 @@ class Voice {
case "three": {
const voicy = new Voice("sin", 800);
voicy.play();
setTimeout(_ => { voicy.freq = 1000; }, 50);
setTimeout(_ => { voicy.freq = 1300; }, 100);
setTimeout(_ => { voicy.stop(); }, 150);
setTimeout(_ => {
voicy.freq = 1000;
}, 50);
setTimeout(_ => {
voicy.freq = 1300;
}, 100);
setTimeout(_ => {
voicy.stop();
}, 150);
break;
}
case "zip": {
@ -102,23 +108,37 @@ class Voice {
return Math.sin(((t + 2) ** (Math.cos(t * 4))) * Math.PI * 2 * freq);
}, 700);
voicy.play();
setTimeout(_ => { voicy.stop(); }, 150);
setTimeout(_ => {
voicy.stop();
}, 150);
break;
}
case "square": {
const voicy = new Voice("square", 600, .4);
const voicy = new Voice("square", 600, 0.4);
voicy.play();
setTimeout(_ => { voicy.freq = 800; }, 50);
setTimeout(_ => { voicy.freq = 1000; }, 100);
setTimeout(_ => { voicy.stop(); }, 150);
setTimeout(_ => {
voicy.freq = 800;
}, 50);
setTimeout(_ => {
voicy.freq = 1000;
}, 100);
setTimeout(_ => {
voicy.stop();
}, 150);
break;
}
case "beep": {
const voicy = new Voice("sin", 800);
voicy.play();
setTimeout(_ => { voicy.stop(); }, 50);
setTimeout(_ => { voicy.play(); }, 100);
setTimeout(_ => { voicy.stop(); }, 150);
setTimeout(_ => {
voicy.stop();
}, 50);
setTimeout(_ => {
voicy.play();
}, 100);
setTimeout(_ => {
voicy.stop();
}, 150);
break;
}
}
@ -127,13 +147,13 @@ class Voice {
return ["three", "zip", "square", "beep"];
}
static setNotificationSound(sound) {
let userinfos = getBulkInfo();
const userinfos = getBulkInfo();
userinfos.preferences.notisound = sound;
localStorage.setItem("userinfos", JSON.stringify(userinfos));
}
static getNotificationSound() {
let userinfos = getBulkInfo();
const userinfos = getBulkInfo();
return userinfos.preferences.notisound;
}
}
export { Voice as Voice };
export { Voice };

View file

@ -58,10 +58,14 @@ class Channel {
this.contextmenu.addbutton("Delete channel", function () {
console.log(this);
this.deleteChannel();
}, null, function () { return this.isAdmin(); });
}, null, function () {
return this.isAdmin();
});
this.contextmenu.addbutton("Edit channel", function () {
this.editChannel();
}, null, function () { return this.isAdmin(); });
}, null, function () {
return this.isAdmin();
});
this.contextmenu.addbutton("Make invite", function () {
this.createInvite();
}, null, function () {
@ -148,12 +152,11 @@ class Channel {
}
sortPerms() {
this.permission_overwritesar.sort((a, b) => {
const order = this.guild.roles.findIndex(_ => _.snowflake === a[0]) - this.guild.roles.findIndex(_ => _.snowflake === b[0]);
return order;
return this.guild.roles.findIndex(_ => _.snowflake === a[0]) - this.guild.roles.findIndex(_ => _.snowflake === b[0]);
});
}
setUpInfiniteScroller() {
this.infinite = new InfiniteScroller(async function (id, offset) {
this.infinite = new InfiniteScroller((async (id, offset) => {
const snowflake = id;
if (offset === 1) {
if (this.idToPrev.has(snowflake)) {
@ -176,13 +179,12 @@ class Channel {
console.log("at bottom");
}
}
}.bind(this), async function (id) {
}), (async (id) => {
//await new Promise(_=>{setTimeout(_,Math.random()*10)})
const messgage = this.messages.get(id);
try {
if (messgage) {
const html = messgage.buildhtml();
return html;
return messgage.buildhtml();
}
else {
console.error(id + " not found");
@ -191,18 +193,21 @@ class Channel {
catch (e) {
console.error(e);
}
}.bind(this), async function (id) {
return document.createElement("div");
}), (async (id) => {
const message = this.messages.get(id);
try {
if (message) {
message.deleteDiv();
return true;
}
}
catch (e) {
console.error(e);
}
finally { }
}.bind(this), this.readbottom.bind(this));
return false;
}), this.readbottom.bind(this));
}
constructor(json, owner) {
if (json === -1) {
@ -227,7 +232,6 @@ class Channel {
if (thing.id === "1182819038095799904" || thing.id === "1182820803700625444") {
continue;
}
;
this.permission_overwrites.set(thing.id, new Permissions(thing.allow, thing.deny));
const permission = this.permission_overwrites.get(thing.id);
if (permission) {
@ -268,7 +272,7 @@ class Channel {
if (!this.hasPermission("VIEW_CHANNEL")) {
return false;
}
return this.lastmessageid !== this.lastreadmessageid && this.type !== 4 && !!this.lastmessageid;
return this.lastmessageid !== this.lastreadmessageid && this.type !== 4 && Boolean(this.lastmessageid);
}
hasPermission(name, member = this.guild.member) {
if (member.isAdmin()) {
@ -277,7 +281,7 @@ class Channel {
for (const thing of member.roles) {
const premission = this.permission_overwrites.get(thing.id);
if (premission) {
let perm = premission.getPermission(name);
const perm = premission.getPermission(name);
if (perm) {
return perm === 1;
}
@ -289,7 +293,7 @@ class Channel {
return false;
}
get canMessage() {
if ((0 === this.permission_overwritesar.length) && this.hasPermission("MANAGE_CHANNELS")) {
if ((this.permission_overwritesar.length === 0) && this.hasPermission("MANAGE_CHANNELS")) {
const role = this.guild.roles.find(_ => _.name === "@everyone");
if (role) {
this.addRoleToPerms(role);
@ -298,7 +302,9 @@ class Channel {
return this.hasPermission("SEND_MESSAGES");
}
sortchildren() {
this.children.sort((a, b) => { return a.position - b.position; });
this.children.sort((a, b) => {
return a.position - b.position;
});
}
resolveparent(guild) {
const parentid = this.parent_id?.id;
@ -313,7 +319,7 @@ class Channel {
}
calculateReorder() {
let position = -1;
let build = [];
const build = [];
for (const thing of this.children) {
const thisthing = { id: thing.snowflake, position: undefined, parent_id: undefined };
if (thing.position < position) {
@ -348,8 +354,13 @@ class Channel {
}
div["all"] = this;
div.draggable = admin;
div.addEventListener("dragstart", (e) => { Channel.dragged = [this, div]; e.stopImmediatePropagation(); });
div.addEventListener("dragend", () => { Channel.dragged = []; });
div.addEventListener("dragstart", e => {
Channel.dragged = [this, div];
e.stopImmediatePropagation();
});
div.addEventListener("dragend", () => {
Channel.dragged = [];
});
if (this.type === 4) {
this.sortchildren();
const caps = document.createElement("div");
@ -383,17 +394,19 @@ class Channel {
childrendiv.appendChild(channel.createguildHTML(admin));
}
childrendiv.classList.add("channels");
setTimeout(_ => { childrendiv.style.height = childrendiv.scrollHeight + 'px'; }, 100);
setTimeout(_ => {
childrendiv.style.height = childrendiv.scrollHeight + "px";
}, 100);
decdiv.onclick = function () {
if (childrendiv.style.height !== '0px') {
if (childrendiv.style.height !== "0px") {
decoration.classList.add("hiddencat");
//childrendiv.classList.add("colapsediv");
childrendiv.style.height = '0px';
childrendiv.style.height = "0px";
}
else {
decoration.classList.remove("hiddencat");
//childrendiv.classList.remove("colapsediv")
childrendiv.style.height = childrendiv.scrollHeight + 'px';
childrendiv.style.height = childrendiv.scrollHeight + "px";
}
};
div.appendChild(childrendiv);
@ -479,14 +492,14 @@ class Channel {
}
}
coatDropDiv(div, container = false) {
div.addEventListener("dragenter", (event) => {
div.addEventListener("dragenter", event => {
console.log("enter");
event.preventDefault();
});
div.addEventListener("dragover", (event) => {
div.addEventListener("dragover", event => {
event.preventDefault();
});
div.addEventListener("drop", (event) => {
div.addEventListener("drop", event => {
const that = Channel.dragged[0];
if (!that)
return;
@ -543,8 +556,8 @@ class Channel {
method: "POST",
headers: this.headers,
body: JSON.stringify({
name: name,
type: type,
name,
type,
parent_id: this.snowflake,
permission_overwrites: [],
})
@ -558,22 +571,28 @@ class Channel {
const thistype = this.type;
const full = new Dialog(["hdiv",
["vdiv",
["textbox", "Channel name:", this.name, function () { name = this.value; }],
["mdbox", "Channel topic:", this.topic, function () { topic = this.value; }],
["checkbox", "NSFW Channel", this.nsfw, function () { nsfw = this.checked; }],
["textbox", "Channel name:", this.name, function () {
name = this.value;
}],
["mdbox", "Channel topic:", this.topic, function () {
topic = this.value;
}],
["checkbox", "NSFW Channel", this.nsfw, function () {
nsfw = this.checked;
}],
["button", "", "submit", () => {
fetch(this.info.api + "/channels/" + thisid, {
method: "PATCH",
headers: this.headers,
body: JSON.stringify({
"name": name,
"type": thistype,
"topic": topic,
"bitrate": 64000,
"user_limit": 0,
"nsfw": nsfw,
"flags": 0,
"rate_limit_per_user": 0
name,
type: thistype,
topic,
bitrate: 64000,
user_limit: 0,
nsfw,
flags: 0,
rate_limit_per_user: 0
})
});
console.log(full);
@ -683,7 +702,7 @@ class Channel {
for (let i = 0; i < 15; i++) {
const div = document.createElement("div");
div.classList.add("loadingmessage");
if (Math.random() < .5) {
if (Math.random() < 0.5) {
const pfp = document.createElement("div");
pfp.classList.add("loadingpfp");
const username = document.createElement("div");
@ -704,7 +723,6 @@ class Channel {
if (this.allthewayup) {
return;
}
;
if (this.lastreadmessageid && this.messages.has(this.lastreadmessageid)) {
return;
}
@ -715,7 +733,7 @@ class Channel {
if (response.length !== 100) {
this.allthewayup = true;
}
let prev = undefined;
let prev;
for (const thing of response) {
const message = new Message(thing, this);
if (prev) {
@ -747,7 +765,9 @@ class Channel {
}
await fetch(this.info.api + "/channels/" + this.id + "/messages?limit=100&after=" + id, {
headers: this.headers
}).then((j) => { return j.json(); }).then(response => {
}).then(j => {
return j.json();
}).then(response => {
let previd = id;
for (const i in response) {
let messager;
@ -769,7 +789,6 @@ class Channel {
}
//out.buildmessages();
});
return;
}
topid;
async grabBefore(id) {
@ -778,7 +797,9 @@ class Channel {
}
await fetch(this.info.api + "/channels/" + this.id + "/messages?before=" + id + "&limit=100", {
headers: this.headers
}).then((j) => { return j.json(); }).then((response) => {
}).then(j => {
return j.json();
}).then((response) => {
if (response.length < 100) {
this.allthewayup = true;
if (response.length === 0) {
@ -801,7 +822,7 @@ class Channel {
this.idToPrev.set(previd, messager.id);
previd = messager.id;
this.messageids.set(messager.snowflake, messager);
if (+i === response.length - 1 && response.length < 100) {
if (Number(i) === response.length - 1 && response.length < 100) {
this.topid = previd;
}
if (willbreak) {
@ -809,7 +830,6 @@ class Channel {
}
}
});
return;
}
/**
* Please dont use this, its not implemented.
@ -900,7 +920,7 @@ class Channel {
while (flake && time < flaketime) {
flake = this.idToPrev.get(flake);
if (!flake) {
return undefined;
return;
}
flaketime = Number((BigInt(flake) >> 22n) + 1420070400000n);
}
@ -919,7 +939,6 @@ class Channel {
if (thing.id === "1182819038095799904" || thing.id === "1182820803700625444") {
continue;
}
;
this.permission_overwrites.set(thing.id, new Permissions(thing.allow, thing.deny));
const permisions = this.permission_overwrites.get(thing.id);
if (permisions) {
@ -930,10 +949,10 @@ class Channel {
this.nsfw = json.nsfw;
}
typingstart() {
if (this.typing > new Date().getTime()) {
if (this.typing > Date.now()) {
return;
}
this.typing = new Date().getTime() + 6000;
this.typing = Date.now() + 6000;
fetch(this.info.api + "/channels/" + this.snowflake + "/typing", {
method: "POST",
headers: this.headers
@ -941,11 +960,11 @@ class Channel {
}
get notification() {
let notinumber = this.message_notifications;
if (+notinumber === 3) {
if (Number(notinumber) === 3) {
notinumber = null;
}
notinumber ??= this.guild.message_notifications;
switch (+notinumber) {
switch (Number(notinumber)) {
case 0:
return "all";
case 1:
@ -961,15 +980,14 @@ class Channel {
if (replyingto) {
replyjson =
{
"guild_id": replyingto.guild.id,
"channel_id": replyingto.channel.id,
"message_id": replyingto.id,
guild_id: replyingto.guild.id,
channel_id: replyingto.channel.id,
message_id: replyingto.id,
};
}
;
if (attachments.length === 0) {
const body = {
content: content,
content,
nonce: Math.floor(Math.random() * 1000000000),
message_reference: undefined
};
@ -985,21 +1003,21 @@ class Channel {
else {
const formData = new FormData();
const body = {
content: content,
content,
nonce: Math.floor(Math.random() * 1000000000),
message_reference: undefined
};
if (replyjson) {
body.message_reference = replyjson;
}
formData.append('payload_json', JSON.stringify(body));
formData.append("payload_json", JSON.stringify(body));
for (const i in attachments) {
formData.append("files[" + i + "]", attachments[i]);
}
return await fetch(this.info.api + "/channels/" + this.snowflake + "/messages", {
method: 'POST',
method: "POST",
body: formData,
headers: { "Authorization": this.headers.Authorization }
headers: { Authorization: this.headers.Authorization }
});
}
}
@ -1084,7 +1102,6 @@ class Channel {
if (deep === 3) {
return;
}
;
this.notify(message, deep + 1);
});
}
@ -1115,7 +1132,7 @@ class Channel {
body: JSON.stringify({
allow: permission.allow.toString(),
deny: permission.deny.toString(),
id: id,
id,
type: 0
})
});

View file

@ -5,7 +5,7 @@ class Contextmenu {
div;
static setup() {
Contextmenu.currentmenu = "";
document.addEventListener('click', function (event) {
document.addEventListener("click", event => {
if (Contextmenu.currentmenu == "") {
return;
}
@ -50,8 +50,8 @@ class Contextmenu {
if (Contextmenu.currentmenu != "") {
Contextmenu.currentmenu.remove();
}
div.style.top = y + 'px';
div.style.left = x + 'px';
div.style.top = y + "px";
div.style.left = x + "px";
document.body.appendChild(div);
Contextmenu.keepOnScreen(div);
console.log(div);
@ -59,7 +59,7 @@ class Contextmenu {
return this.div;
}
bindContextmenu(obj, addinfo, other) {
const func = (event) => {
const func = event => {
event.preventDefault();
event.stopImmediatePropagation();
this.makemenu(event.clientX, event.clientY, addinfo, other);
@ -75,12 +75,12 @@ class Contextmenu {
console.log(box, docheight, docwidth);
if (box.right > docwidth) {
console.log("test");
obj.style.left = docwidth - box.width + 'px';
obj.style.left = docwidth - box.width + "px";
}
if (box.bottom > docheight) {
obj.style.top = docheight - box.height + 'px';
obj.style.top = docheight - box.height + "px";
}
}
}
Contextmenu.setup();
export { Contextmenu as Contextmenu };
export { Contextmenu };

View file

@ -58,7 +58,7 @@ class Dialog {
case "checkbox":
{
const div = document.createElement("div");
const checkbox = document.createElement('input');
const checkbox = document.createElement("input");
div.appendChild(checkbox);
const label = document.createElement("span");
checkbox.checked = array[2];
@ -71,7 +71,7 @@ class Dialog {
case "button":
{
const div = document.createElement("div");
const input = document.createElement('button');
const input = document.createElement("button");
const label = document.createElement("span");
input.textContent = array[2];
label.textContent = array[1];
@ -134,7 +134,7 @@ class Dialog {
case "radio": {
const div = document.createElement("div");
const fieldset = document.createElement("fieldset");
fieldset.addEventListener("change", function () {
fieldset.addEventListener("change", () => {
let i = -1;
for (const thing of fieldset.children) {
i++;
@ -173,9 +173,8 @@ class Dialog {
div.appendChild(fieldset);
return div;
}
case "html": {
case "html":
return array[1];
}
case "select": {
const div = document.createElement("div");
const label = document.createElement("label");
@ -227,7 +226,6 @@ class Dialog {
}
default:
console.error("can't find element:" + array[0], " full element:" + array);
return;
}
}
show() {
@ -237,7 +235,9 @@ class Dialog {
this.background.classList.add("background");
document.body.appendChild(this.background);
document.body.appendChild(this.html);
this.background.onclick = _ => { this.hide(); };
this.background.onclick = _ => {
this.hide();
};
}
hide() {
document.body.removeChild(this.background);

View file

@ -96,7 +96,6 @@ class Group extends Channel {
this.messageids = new Map();
this.permission_overwrites = new Map();
this.lastmessageid = json.last_message_id;
this.lastmessageid ??= null;
this.mentions = 0;
this.setUpInfiniteScroller();
if (this.lastmessageid) {
@ -140,8 +139,8 @@ class Group extends Channel {
const messagez = new Message(messagep.d, this);
if (this.lastmessageid) {
this.idToNext.set(this.lastmessageid, messagez.id);
this.idToPrev.set(messagez.id, this.lastmessageid);
}
this.idToPrev.set(messagez.id, this.lastmessageid);
this.lastmessageid = messagez.id;
this.messageids.set(messagez.snowflake, messagez);
if (messagez.author === this.localuser.user) {

View file

@ -119,7 +119,6 @@ class Embed {
if (this.json?.timestamp) {
const span = document.createElement("span");
span.textContent = new Date(this.json.timestamp).toLocaleString();
;
footer.append(span);
}
embed.append(footer);

View file

@ -62,10 +62,8 @@ class Emoji {
for (let i = 0; i < length; i++) {
array[i] = read8();
}
const decoded = new TextDecoder("utf-8").decode(array.buffer);
;
//console.log(array);
return decoded;
return new TextDecoder("utf-8").decode(array.buffer);
}
const build = [];
let cats = read16();
@ -78,7 +76,7 @@ class Emoji {
const name = readString8();
const len = read8();
const skin_tone_support = len > 127;
const emoji = readStringNo(len - (+skin_tone_support * 128));
const emoji = readStringNo(len - (Number(skin_tone_support) * 128));
emojis.push({
name,
skin_tone_support,
@ -102,7 +100,9 @@ class Emoji {
}
static async emojiPicker(x, y, localuser) {
let res;
const promise = new Promise((r) => { res = r; });
const promise = new Promise(r => {
res = r;
});
const menu = document.createElement("div");
menu.classList.add("flextttb", "emojiPicker");
menu.style.top = y + "px";

View file

@ -31,7 +31,7 @@ class File {
this.width /= scale;
this.height /= scale;
}
if (this.content_type.startsWith('image/')) {
if (this.content_type.startsWith("image/")) {
const div = document.createElement("div");
const img = document.createElement("img");
img.classList.add("messageimg");
@ -50,7 +50,7 @@ class File {
console.log(this.width, this.height);
return div;
}
else if (this.content_type.startsWith('video/')) {
else if (this.content_type.startsWith("video/")) {
const video = document.createElement("video");
const source = document.createElement("source");
source.src = src;
@ -63,7 +63,7 @@ class File {
}
return video;
}
else if (this.content_type.startsWith('audio/')) {
else if (this.content_type.startsWith("audio/")) {
const audio = document.createElement("audio");
const source = document.createElement("source");
source.src = src;
@ -138,8 +138,8 @@ class File {
return div;
}
static filesizehuman(fsize) {
var i = fsize == 0 ? 0 : Math.floor(Math.log(fsize) / Math.log(1024));
return +((fsize / Math.pow(1024, i)).toFixed(2)) * 1 + ' ' + ['Bytes', 'Kilobytes', 'Megabytes', 'Gigabytes', 'Terabytes'][i];
const i = fsize == 0 ? 0 : Math.floor(Math.log(fsize) / Math.log(1024));
return Number((fsize / Math.pow(1024, i)).toFixed(2)) * 1 + " " + ["Bytes", "Kilobytes", "Megabytes", "Gigabytes", "Terabytes"][i];
}
}
export { File };

View file

@ -149,7 +149,7 @@ class Guild {
method: "PATCH",
headers: this.headers,
body: JSON.stringify({
"message_notifications": noti
message_notifications: noti
})
});
this.message_notifications = noti;
@ -202,7 +202,7 @@ class Guild {
}
calculateReorder() {
let position = -1;
let build = [];
const build = [];
for (const thing of this.headchannels) {
const thisthing = { id: thing.snowflake, position: undefined, parent_id: undefined };
if (thing.position <= position) {
@ -256,7 +256,9 @@ class Guild {
return this.owner.info;
}
sortchannels() {
this.headchannels.sort((a, b) => { return a.position - b.position; });
this.headchannels.sort((a, b) => {
return a.position - b.position;
});
}
generateGuildIcon() {
const divy = document.createElement("div");
@ -279,7 +281,7 @@ class Guild {
}
else {
const div = document.createElement("div");
let build = this.properties.name.replace(/'s /g, " ").replace(/\w+/g, word => word[0]).replace(/\s/g, "");
const build = this.properties.name.replace(/'s /g, " ").replace(/\w+/g, word => word[0]).replace(/\s/g, "");
div.textContent = build;
div.classList.add("blankserver", "servericon");
divy.appendChild(div);
@ -336,7 +338,7 @@ class Guild {
headers: this.headers,
});
}
unreads(html = undefined) {
unreads(html) {
if (html) {
this.html = html;
}
@ -399,7 +401,7 @@ class Guild {
}
return this.member.hasRole(r);
}
loadChannel(ID = undefined) {
loadChannel(ID) {
if (ID && this.channelids[ID]) {
this.channelids[ID].getHTML();
return;
@ -451,7 +453,7 @@ class Guild {
["voice", "text", "announcement"],
function (e) {
console.log(e);
category = { "text": 0, "voice": 2, "announcement": 5, "category": 4 }[e];
category = { text: 0, voice: 2, announcement: 5, category: 4 }[e];
},
1
],
@ -463,12 +465,12 @@ class Guild {
console.log(name, category);
func(name, category);
channelselect.hide();
}.bind(this)]]);
}]]);
channelselect.show();
}
createcategory() {
let name = "";
let category = 4;
const category = 4;
const channelselect = new Dialog(["vdiv",
["textbox", "Name of category", "", function () {
console.log(this);
@ -510,7 +512,7 @@ class Guild {
fetch(this.info.api + "/guilds/" + this.snowflake + "/channels", {
method: "POST",
headers: this.headers,
body: JSON.stringify({ name: name, type: type })
body: JSON.stringify({ name, type })
});
}
async createRole(name) {
@ -518,7 +520,7 @@ class Guild {
method: "POST",
headers: this.headers,
body: JSON.stringify({
name: name,
name,
color: 0,
permissions: "0"
})

View file

@ -33,7 +33,7 @@ fetch("/instances.json").then(_ => _.json()).then((json) => {
if (instance.descriptionLong) {
p.innerText = instance.descriptionLong;
}
else {
else if (instance.description) {
p.innerText = instance.description;
}
textbox.append(p);

View file

@ -6,8 +6,10 @@ import { File } from "./file.js";
(async () => {
async function waitforload() {
let res;
new Promise(r => { res = r; });
document.addEventListener("DOMContentLoaded", function () {
new Promise(r => {
res = r;
});
document.addEventListener("DOMContentLoaded", () => {
res();
});
await res;
@ -15,7 +17,7 @@ import { File } from "./file.js";
await waitforload();
const users = getBulkUsers();
if (!users.currentuser) {
window.location.href = '/login.html';
window.location.href = "/login.html";
}
function showAccountSwitcher() {
const table = document.createElement("div");
@ -45,7 +47,7 @@ import { File } from "./file.js";
loading.classList.remove("doneloading");
loading.classList.add("loading");
thisuser = new Localuser(specialuser);
users["currentuser"] = specialuser.uid;
users.currentuser = specialuser.uid;
localStorage.setItem("userinfos", JSON.stringify(users));
thisuser.initwebsocket().then(_ => {
thisuser.loaduser();
@ -107,16 +109,20 @@ import { File } from "./file.js";
}
{
const menu = new Contextmenu("create rightclick"); //Really should go into the localuser class, but that's a later thing
menu.addbutton("Create channel", function () {
menu.addbutton("Create channel", () => {
if (thisuser.lookingguild) {
thisuser.lookingguild.createchannels();
}
}, null, _ => { return thisuser.isAdmin(); });
menu.addbutton("Create category", function () {
}, null, _ => {
return thisuser.isAdmin();
});
menu.addbutton("Create category", () => {
if (thisuser.lookingguild) {
thisuser.lookingguild.createcategory();
}
}, null, _ => { return thisuser.isAdmin(); });
}, null, _ => {
return thisuser.isAdmin();
});
menu.bindContextmenu(document.getElementById("channels"), 0, 0);
}
const pasteimage = document.getElementById("pasteimage");
@ -134,7 +140,7 @@ import { File } from "./file.js";
}
else {
replyingto = thisuser.channelfocus.replyingto;
let replying = replyingto;
const replying = replyingto;
if (replyingto?.div) {
replyingto.div.classList.remove("replying");
}
@ -151,7 +157,6 @@ import { File } from "./file.js";
pasteimage.removeChild(imageshtml.pop());
}
typebox.innerHTML = "";
return;
}
}
const typebox = document.getElementById("typebox");
@ -174,7 +179,7 @@ import { File } from "./file.js";
*/
const images = [];
const imageshtml = [];
document.addEventListener('paste', async (e) => {
document.addEventListener("paste", async (e) => {
if (!e.clipboardData)
return;
Array.from(e.clipboardData.files).forEach(async (f) => {

View file

@ -32,7 +32,7 @@ class InfiniteScroller {
this.watchForChange();
});
this.scroll.addEventListener("scroll", _ => {
if (null === this.timeout) {
if (this.timeout === null) {
this.timeout = setTimeout(this.updatestuff.bind(this), 300);
}
this.watchForChange();
@ -71,10 +71,8 @@ class InfiniteScroller {
this.averageheight = 60;
}
this.scrollTop = this.scroll.scrollTop;
if (!this.scrollBottom) {
if (!await this.watchForChange()) {
this.reachesBottom();
}
if (!this.scrollBottom && !await this.watchForChange()) {
this.reachesBottom();
}
if (!this.scrollTop) {
await this.watchForChange();
@ -129,7 +127,6 @@ class InfiniteScroller {
this.HTMLElements.unshift([html, nextid]);
this.scrollTop += this.averageheight;
}
;
}
if (this.scrollTop > this.maxDist) {
const html = this.HTMLElements.shift();
@ -176,7 +173,6 @@ class InfiniteScroller {
this.HTMLElements.push([html, nextid]);
this.scrollBottom += this.averageheight;
}
;
}
if (scrollBottom > this.maxDist) {
const html = this.HTMLElements.pop();
@ -225,14 +221,14 @@ class InfiniteScroller {
}
const out = await Promise.allSettled([this.watchForTop(), this.watchForBottom()]);
const changed = (out[0].value || out[1].value);
if (null === this.timeout && changed) {
if (this.timeout === null && changed) {
this.timeout = setTimeout(this.updatestuff.bind(this), 300);
}
if (!this.currrunning) {
console.error("something really bad happened");
}
res(!!changed);
return !!changed;
res(Boolean(changed));
return Boolean(changed);
}
catch (e) {
console.error(e);

View file

@ -11,7 +11,7 @@ import { getBulkUsers, getapiurls } from "./login.js";
console.log(users.users[thing]);
}
let urls;
if (!joinable.length) {
if (!joinable.length && well) {
const out = await getapiurls(well);
if (out) {
urls = out;
@ -24,7 +24,7 @@ import { getBulkUsers, getapiurls } from "./login.js";
}
}
else {
throw Error("someone needs to handle the case where the servers don't exist");
throw new Error("someone needs to handle the case where the servers don't exist");
}
}
else {
@ -86,7 +86,7 @@ import { getBulkUsers, getapiurls } from "./login.js";
Authorization: thing.token
}
}).then(_ => {
users["currentuser"] = specialuser.uid;
users.currentuser = specialuser.uid;
localStorage.setItem("userinfos", JSON.stringify(users));
window.location.href = "/channels/" + guildinfo.id;
});

View file

@ -22,13 +22,13 @@ function trimswitcher() {
for (const thing in json.users) {
const user = json.users[thing];
let wellknown = user.serverurls.wellknown;
if (wellknown[wellknown.length - 1] !== "/") {
if (wellknown.at(-1) !== "/") {
wellknown += "/";
}
wellknown += user.username;
if (map.has(wellknown)) {
const otheruser = map.get(wellknown);
if (otheruser[1].serverurls.wellknown[otheruser[1].serverurls.wellknown.length - 1] === "/") {
if (otheruser[1].serverurls.wellknown.at(-1) === "/") {
delete json.users[otheruser[0]];
map.set(wellknown, [thing, user]);
}
@ -41,7 +41,7 @@ function trimswitcher() {
}
}
for (const thing in json.users) {
if (thing[thing.length - 1] === "/") {
if (thing.at(-1) === "/") {
const user = json.users[thing];
delete json.users[thing];
json.users[thing.slice(0, -1)] = user;
@ -73,7 +73,7 @@ function setDefaults() {
if (userinfos.accent_color === undefined) {
userinfos.accent_color = "#242443";
}
document.documentElement.style.setProperty('--accent-color', userinfos.accent_color);
document.documentElement.style.setProperty("--accent-color", userinfos.accent_color);
if (userinfos.preferences === undefined) {
userinfos.preferences = {
theme: "Dark",
@ -103,11 +103,8 @@ class Specialuser {
this.serverurls.api = apistring;
this.serverurls.cdn = new URL(json.serverurls.cdn).toString().replace(/\/$/, "");
this.serverurls.gateway = new URL(json.serverurls.gateway).toString().replace(/\/$/, "");
;
this.serverurls.wellknown = new URL(json.serverurls.wellknown).toString().replace(/\/$/, "");
;
this.serverurls.login = new URL(json.serverurls.login).toString().replace(/\/$/, "");
;
this.email = json.email;
this.token = json.token;
this.loggedin = json.loggedin;
@ -178,12 +175,12 @@ async function getapiurls(str) {
}
}
}
if (str[str.length - 1] !== "/") {
if (str.at(-1) !== "/") {
str += "/";
}
let api;
try {
const info = await fetch(`${str}/.well-known/spacebar`).then((x) => x.json());
const info = await fetch(`${str}/.well-known/spacebar`).then(x => x.json());
api = info.api;
}
catch {
@ -191,7 +188,7 @@ async function getapiurls(str) {
}
const url = new URL(api);
try {
const info = await fetch(`${api}${url.pathname.includes("api") ? "" : "api"}/policies/instance/domains`).then((x) => x.json());
const info = await fetch(`${api}${url.pathname.includes("api") ? "" : "api"}/policies/instance/domains`).then(x => x.json());
return {
api: info.apiEndpoint,
gateway: info.gateway,
@ -226,8 +223,8 @@ async function checkInstance(e) {
instanceinfo.value = instancein.value;
localStorage.setItem("instanceinfo", JSON.stringify(instanceinfo));
verify.textContent = "Instance is all good";
if (checkInstance["alt"]) {
checkInstance["alt"]();
if (checkInstance.alt) {
checkInstance.alt();
}
setTimeout(_ => {
console.log(verify.textContent);
@ -238,7 +235,7 @@ async function checkInstance(e) {
verify.textContent = "Invalid Instance, try again";
}
}
catch (e) {
catch {
console.log("catch");
verify.textContent = "Invalid Instance, try again";
}
@ -271,10 +268,10 @@ async function login(username, password, captcha) {
const options = {
method: "POST",
body: JSON.stringify({
"login": username,
"password": password,
"undelete": false,
"captcha_key": captcha
login: username,
password,
undelete: false,
captcha_key: captcha
}),
headers: {
"Content-type": "application/json; charset=UTF-8",
@ -283,10 +280,10 @@ async function login(username, password, captcha) {
try {
const info = JSON.parse(localStorage.getItem("instanceinfo"));
const api = info.login + (info.login.startsWith("/") ? "/" : "");
return await fetch(api + '/auth/login', options).then(response => response.json())
.then((response) => {
return await fetch(api + "/auth/login", options).then(response => response.json())
.then(response => {
console.log(response, response.message);
if ("Invalid Form Body" === response.message) {
if (response.message === "Invalid Form Body") {
return response.errors.login._errors[0].message;
console.log("test");
}
@ -306,13 +303,14 @@ async function login(username, password, captcha) {
else {
eval("hcaptcha.reset()");
}
return;
}
else {
console.log(response);
if (response.ticket) {
let onetimecode = "";
new Dialog(["vdiv", ["title", "2FA code:"], ["textbox", "", "", function () { onetimecode = this.value; }], ["button", "", "Submit", function () {
new Dialog(["vdiv", ["title", "2FA code:"], ["textbox", "", "", function () {
onetimecode = this.value;
}], ["button", "", "Submit", function () {
fetch(api + "/auth/mfa/totp", {
method: "POST",
headers: {
@ -336,7 +334,7 @@ async function login(username, password, captcha) {
window.location.href = redir;
}
else {
window.location.href = '/channels/@me';
window.location.href = "/channels/@me";
}
}
});
@ -352,7 +350,7 @@ async function login(username, password, captcha) {
window.location.href = redir;
}
else {
window.location.href = '/channels/@me';
window.location.href = "/channels/@me";
}
return "";
}
@ -360,13 +358,12 @@ async function login(username, password, captcha) {
});
}
catch (error) {
console.error('Error:', error);
console.error("Error:", error);
}
;
}
async function check(e) {
e.preventDefault();
let h = await login(e.srcElement[1].value, e.srcElement[2].value, e.srcElement[3].value);
const h = await login(e.srcElement[1].value, e.srcElement[2].value, e.srcElement[3].value);
document.getElementById("wrong").textContent = h;
console.log(h);
}

View file

@ -28,7 +28,7 @@ class MarkDown {
return this.makeHTML().textContent;
}
makeHTML({ keep = this.keep, stdsize = this.stdsize } = {}) {
return this.markdown(this.txt, { keep: keep, stdsize: stdsize });
return this.markdown(this.txt, { keep, stdsize });
}
markdown(text, { keep = false, stdsize = false } = {}) {
let txt;
@ -105,7 +105,7 @@ class MarkDown {
if (keep) {
element.append(keepys);
}
element.appendChild(this.markdown(build, { keep: keep, stdsize: stdsize }));
element.appendChild(this.markdown(build, { keep, stdsize }));
span.append(element);
}
finally {
@ -179,7 +179,7 @@ class MarkDown {
}
else {
const pre = document.createElement("pre");
if (build[build.length - 1] === "\n") {
if (build.at(-1) === "\n") {
build = build.substring(0, build.length - 1);
}
if (txt[i] === "\n") {
@ -224,7 +224,7 @@ class MarkDown {
if (keep) {
i.append(stars);
}
i.appendChild(this.markdown(build, { keep: keep, stdsize: stdsize }));
i.appendChild(this.markdown(build, { keep, stdsize }));
if (keep) {
i.append(stars);
}
@ -235,7 +235,7 @@ class MarkDown {
if (keep) {
b.append(stars);
}
b.appendChild(this.markdown(build, { keep: keep, stdsize: stdsize }));
b.appendChild(this.markdown(build, { keep, stdsize }));
if (keep) {
b.append(stars);
}
@ -247,7 +247,7 @@ class MarkDown {
if (keep) {
b.append(stars);
}
b.appendChild(this.markdown(build, { keep: keep, stdsize: stdsize }));
b.appendChild(this.markdown(build, { keep, stdsize }));
if (keep) {
b.append(stars);
}
@ -290,7 +290,7 @@ class MarkDown {
if (keep) {
i.append(underscores);
}
i.appendChild(this.markdown(build, { keep: keep, stdsize: stdsize }));
i.appendChild(this.markdown(build, { keep, stdsize }));
if (keep) {
i.append(underscores);
}
@ -301,7 +301,7 @@ class MarkDown {
if (keep) {
u.append(underscores);
}
u.appendChild(this.markdown(build, { keep: keep, stdsize: stdsize }));
u.appendChild(this.markdown(build, { keep, stdsize }));
if (keep) {
u.append(underscores);
}
@ -313,7 +313,7 @@ class MarkDown {
if (keep) {
i.append(underscores);
}
i.appendChild(this.markdown(build, { keep: keep, stdsize: stdsize }));
i.appendChild(this.markdown(build, { keep, stdsize }));
if (keep) {
i.append(underscores);
}
@ -325,7 +325,7 @@ class MarkDown {
}
}
if (txt[i] === "~" && txt[i + 1] === "~") {
let count = 2;
const count = 2;
let build = [];
let find = 0;
let j = i + 2;
@ -350,7 +350,7 @@ class MarkDown {
if (keep) {
s.append(tildes);
}
s.appendChild(this.markdown(build, { keep: keep, stdsize: stdsize }));
s.appendChild(this.markdown(build, { keep, stdsize }));
if (keep) {
s.append(tildes);
}
@ -360,7 +360,7 @@ class MarkDown {
}
}
if (txt[i] === "|" && txt[i + 1] === "|") {
let count = 2;
const count = 2;
let build = [];
let find = 0;
let j = i + 2;
@ -385,7 +385,7 @@ class MarkDown {
if (keep) {
j.append(pipes);
}
j.appendChild(this.markdown(build, { keep: keep, stdsize: stdsize }));
j.appendChild(this.markdown(build, { keep, stdsize }));
j.classList.add("spoiler");
j.onclick = MarkDown.unspoil;
if (keep) {
@ -459,7 +459,7 @@ class MarkDown {
i = j;
const isEmojiOnly = txt.join("").trim() === buildjoin.trim();
const owner = (this.owner instanceof Channel) ? this.owner.guild : this.owner;
const emoji = new Emoji({ name: buildjoin, id: parts[2], animated: !!parts[1] }, owner);
const emoji = new Emoji({ name: buildjoin, id: parts[2], animated: Boolean(parts[1]) }, owner);
span.appendChild(emoji.getHTML(isEmojiOnly));
continue;
}
@ -479,7 +479,6 @@ class MarkDown {
else {
break;
}
;
}
else if (partsFound === 1 && txt[j] === ")") {
partsFound++;
@ -529,7 +528,7 @@ class MarkDown {
return;
console.log(_.clipboardData.types);
const data = _.clipboardData.getData("text");
document.execCommand('insertHTML', false, data);
document.execCommand("insertHTML", false, data);
_.preventDefault();
if (!box.onkeyup)
return;
@ -568,25 +567,25 @@ class MarkDown {
}
//solution from https://stackoverflow.com/questions/4576694/saving-and-restoring-caret-position-for-contenteditable-div
function saveCaretPosition(context) {
var selection = window.getSelection();
const selection = window.getSelection();
if (!selection)
return;
var range = selection.getRangeAt(0);
const range = selection.getRangeAt(0);
range.setStart(context, 0);
var len = range.toString().length;
const len = range.toString().length;
return function restore() {
if (!selection)
return;
var pos = getTextNodeAtPosition(context, len);
const pos = getTextNodeAtPosition(context, len);
selection.removeAllRanges();
var range = new Range();
const range = new Range();
range.setStart(pos.node, pos.position);
selection.addRange(range);
};
}
function getTextNodeAtPosition(root, index) {
const NODE_TYPE = NodeFilter.SHOW_TEXT;
var treeWalker = document.createTreeWalker(root, NODE_TYPE, function next(elem) {
const treeWalker = document.createTreeWalker(root, NODE_TYPE, elem => {
if (!elem.textContent)
return 0;
if (index > elem.textContent.length) {
@ -595,7 +594,7 @@ function getTextNodeAtPosition(root, index) {
}
return NodeFilter.FILTER_ACCEPT;
});
var c = treeWalker.nextNode();
const c = treeWalker.nextNode();
return {
node: c ? c : root,
position: index

View file

@ -28,7 +28,7 @@ class Member {
continue;
}
if (thing === "roles") {
for (const strrole of memberjson["roles"]) {
for (const strrole of memberjson.roles) {
const role = SnowFlake.getSnowFlakeFromID(strrole, Role).getObject();
this.roles.push(role);
}
@ -38,7 +38,6 @@ class Member {
}
if (this.localuser.userMap.has(this?.id)) {
this.user = this.localuser.userMap.get(this?.id);
return;
}
}
get guild() {
@ -89,7 +88,6 @@ class Member {
const membjson = await membpromise;
if (membjson === undefined) {
res(undefined);
return undefined;
}
else {
const member = new Member(membjson, guild);
@ -149,7 +147,6 @@ class Member {
if (!this) {
return;
}
;
/*
if(this.error){

View file

@ -42,7 +42,9 @@ class Message {
return this.snowflake.id;
}
static setup() {
this.del = new Promise(_ => { this.resolve = _; });
this.del = new Promise(_ => {
this.resolve = _;
});
Message.setupcmenu();
}
static setupcmenu() {
@ -63,7 +65,7 @@ class Message {
Message.contextmenu.addbutton("Edit", function () {
this.channel.editing = this;
const markdown = document.getElementById("typebox")["markdown"];
markdown.txt = this.content.rawString.split('');
markdown.txt = this.content.rawString.split("");
markdown.boxupdate(document.getElementById("typebox"));
}, null, function () {
return this.author.id === this.localuser.user.id;
@ -199,7 +201,7 @@ class Message {
getimages() {
const build = [];
for (const thing of this.attachments) {
if (thing.content_type.startsWith('image/')) {
if (thing.content_type.startsWith("image/")) {
build.push(thing);
}
}
@ -209,7 +211,7 @@ class Message {
return await fetch(this.info.api + "/channels/" + this.channel.snowflake + "/messages/" + this.id, {
method: "PATCH",
headers: this.headers,
body: JSON.stringify({ content: content })
body: JSON.stringify({ content })
});
}
delete() {
@ -266,7 +268,7 @@ class Message {
this.generateMessage();
}
}
generateMessage(premessage = undefined, ignoredblock = false) {
generateMessage(premessage, ignoredblock = false) {
if (!this.div)
return;
if (!premessage) {
@ -277,21 +279,21 @@ class Message {
div.classList.add("replying");
}
div.innerHTML = "";
const build = document.createElement('div');
const build = document.createElement("div");
build.classList.add("flexltr", "message");
div.classList.remove("zeroheight");
if (this.author.relationshipType === 2) {
if (ignoredblock) {
if (premessage?.author !== this.author) {
const span = document.createElement("span");
span.textContent = `You have this user blocked, click to hide these messages.`;
span.textContent = "You have this user blocked, click to hide these messages.";
div.append(span);
span.classList.add("blocked");
span.onclick = _ => {
const scroll = this.channel.infinite.scrollTop;
let next = this;
while (next?.author === this.author) {
next.generateMessage(undefined);
next.generateMessage();
next = this.channel.messages.get(this.channel.idToNext.get(next.id));
}
if (this.channel.infinite.scroll && scroll) {
@ -375,7 +377,7 @@ class Message {
}
div.appendChild(build);
if ({ 0: true, 19: true }[this.type] || this.attachments.length !== 0) {
const pfpRow = document.createElement('div');
const pfpRow = document.createElement("div");
pfpRow.classList.add("flexltr");
let pfpparent, current;
if (premessage != null) {
@ -545,7 +547,7 @@ class Message {
if (thing.emoji.name === data.name) {
thing.count--;
if (thing.count === 0) {
this.reactions.splice(+i, 1);
this.reactions.splice(Number(i), 1);
this.updateReactions();
return;
}
@ -565,16 +567,16 @@ class Message {
for (const i in this.reactions) {
const reaction = this.reactions[i];
if ((reaction.emoji.id && reaction.emoji.id == emoji.id) || (!reaction.emoji.id && reaction.emoji.name == emoji.name)) {
this.reactions.splice(+i, 1);
this.reactions.splice(Number(i), 1);
this.updateReactions();
break;
}
}
}
buildhtml(premessage = undefined) {
buildhtml(premessage) {
if (this.div) {
console.error(`HTML for ${this.snowflake} already exists, aborting`);
return;
return this.div;
}
try {
const div = document.createElement("div");
@ -585,15 +587,16 @@ class Message {
catch (e) {
console.error(e);
}
return this.div;
}
}
let now = new Date().toLocaleDateString();
const now = new Date().toLocaleDateString();
const yesterday = new Date(now);
yesterday.setDate(new Date().getDate() - 1);
let yesterdayStr = yesterday.toLocaleDateString();
const yesterdayStr = yesterday.toLocaleDateString();
function formatTime(date) {
const datestring = date.toLocaleDateString();
const formatTime = (date) => date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
const formatTime = (date) => date.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
if (datestring === now) {
return `Today at ${formatTime(date)}`;
}

View file

@ -3,12 +3,12 @@ class Permissions {
deny;
hasDeny;
constructor(allow, deny = "") {
this.hasDeny = !!deny;
this.hasDeny = Boolean(deny);
try {
this.allow = BigInt(allow);
this.deny = BigInt(deny);
}
catch (e) {
catch {
this.allow = 0n;
this.deny = 0n;
console.error(`Something really stupid happened with a permission with allow being ${allow} and deny being, ${deny}, execution will still happen, but something really stupid happened, please report if you know what caused this.`);

View file

@ -17,9 +17,9 @@ async function registertry(e) {
await fetch(apiurl + "/auth/register", {
body: JSON.stringify({
date_of_birth: dateofbirth,
email: email,
username: username,
password: password,
email,
username,
password,
consent: elements[6].checked,
captcha_key: elements[7]?.value
}),
@ -67,14 +67,14 @@ async function registertry(e) {
}
}
else {
adduser({ serverurls: JSON.parse(localStorage.getItem("instanceinfo")), email: email, token: e.token }).username = username;
adduser({ serverurls: JSON.parse(localStorage.getItem("instanceinfo")), email, token: e.token }).username = username;
localStorage.setItem("token", e.token);
const redir = new URLSearchParams(window.location.search).get("goback");
if (redir) {
window.location.href = redir;
}
else {
window.location.href = '/channels/@me';
window.location.href = "/channels/@me";
}
}
});
@ -93,7 +93,9 @@ function error(e, message) {
}
else {
element.classList.remove("suberror");
setTimeout(_ => { element.classList.add("suberror"); }, 100);
setTimeout(_ => {
element.classList.add("suberror");
}, 100);
}
element.textContent = message;
}

View file

@ -38,7 +38,6 @@ class Role {
if (this.color === 0) {
return null;
}
;
return `#${this.color.toString(16)}`;
}
}
@ -55,7 +54,6 @@ class PermissionToggle {
this.owner = owner;
}
watchForChange() { }
;
generateHTML() {
const div = document.createElement("div");
div.classList.add("setting");
@ -80,7 +78,6 @@ class PermissionToggle {
if (state === 1) {
on.checked = true;
}
;
on.onclick = _ => {
this.permissions.setPermission(this.rolejson.name, 1);
this.owner.changed();
@ -92,7 +89,6 @@ class PermissionToggle {
if (state === 0) {
no.checked = true;
}
;
no.onclick = _ => {
this.permissions.setPermission(this.rolejson.name, 0);
this.owner.changed();
@ -105,7 +101,6 @@ class PermissionToggle {
if (state === -1) {
off.checked = true;
}
;
off.onclick = _ => {
this.permissions.setPermission(this.rolejson.name, -1);
this.owner.changed();

View file

@ -4,7 +4,7 @@ function deleteoldcache() {
}
async function putInCache(request, response) {
console.log(request, response);
const cache = await caches.open('cache');
const cache = await caches.open("cache");
console.log("Grabbed");
try {
console.log(await cache.put(request, response));
@ -13,7 +13,6 @@ async function putInCache(request, response) {
console.error(error);
}
}
;
console.log("test");
let lastcache;
self.addEventListener("activate", async (event) => {
@ -37,7 +36,9 @@ async function checkCache() {
putInCache("/getupdates", data.clone());
}
checkedrecently = true;
setTimeout(_ => { checkedrecently = false; }, 1000 * 60 * 30);
setTimeout(_ => {
checkedrecently = false;
}, 1000 * 60 * 30);
});
}
var checkedrecently = false;
@ -83,7 +84,7 @@ async function getfile(event) {
console.error(e);
}
}
self.addEventListener('fetch', (event) => {
self.addEventListener("fetch", (event) => {
try {
event.respondWith(getfile(event));
}

View file

@ -9,7 +9,7 @@ class Buttons {
this.buttons = [];
this.name = name;
}
add(name, thing = undefined) {
add(name, thing) {
if (!thing) {
thing = new Options(name, this);
}
@ -380,7 +380,6 @@ class FileInput {
if (this.onchange) {
this.onchange(null);
}
;
this.value = null;
this.owner.changed();
};
@ -424,7 +423,6 @@ class HtmlArea {
}
}
watchForChange() { }
;
}
class Options {
name;
@ -451,7 +449,6 @@ class Options {
}
}
watchForChange() { }
;
addOptions(name, { ltr = false } = {}) {
const options = new Options(name, this, { ltr });
this.options.push(options);
@ -467,7 +464,7 @@ class Options {
this.generateContainter();
}
else {
throw Error("Tried to make a subOptions when the options weren't rendered");
throw new Error("Tried to make a subOptions when the options weren't rendered");
}
return options;
}
@ -479,7 +476,7 @@ class Options {
this.generateContainter();
}
else {
throw Error("Tried to make a subForm when the options weren't rendered");
throw new Error("Tried to make a subForm when the options weren't rendered");
}
return options;
}
@ -758,7 +755,6 @@ class Form {
watchForChange(func) {
this.onSubmit = func;
}
;
changed() {
if (this.traditionalSubmit) {
this.owner.changed();
@ -819,7 +815,6 @@ class Form {
if (!(errors instanceof Object)) {
return;
}
;
for (const error of Object.keys(errors)) {
const elm = this.names.get(error);
if (elm) {
@ -858,7 +853,9 @@ class Form {
}
else {
element.classList.remove("suberror");
setTimeout(_ => { element.classList.add("suberror"); }, 100);
setTimeout(_ => {
element.classList.add("suberror");
}, 100);
}
element.textContent = message;
}
@ -887,7 +884,9 @@ class Settings extends Buttons {
exit.textContent = "✖";
exit.classList.add("exitsettings");
background.append(exit);
exit.onclick = _ => { this.hide(); };
exit.onclick = _ => {
this.hide();
};
document.body.append(background);
this.html = background;
}

View file

@ -72,7 +72,7 @@ class User {
});
this.contextmenu.addbutton("Message user", function () {
fetch(this.info.api + "/users/@me/channels", { method: "POST",
body: JSON.stringify({ "recipients": [this.id] }),
body: JSON.stringify({ recipients: [this.id] }),
headers: this.localuser.headers
});
});
@ -97,7 +97,7 @@ class User {
});
this.contextmenu.addbutton("Kick member", function (member) {
member.kick();
}, null, function (member) {
}, null, member => {
if (!member)
return false;
const us = member.guild.member;
@ -111,7 +111,7 @@ class User {
});
this.contextmenu.addbutton("Ban member", function (member) {
member.ban();
}, null, function (member) {
}, null, member => {
if (!member)
return false;
const us = member.guild.member;
@ -192,7 +192,7 @@ class User {
}
}
buildpfp() {
const pfp = document.createElement('img');
const pfp = document.createElement("img");
pfp.loading = "lazy";
pfp.src = this.getpfpsrc();
pfp.classList.add("pfp");
@ -419,7 +419,7 @@ class User {
}
return div;
}
profileclick(obj, guild = undefined) {
profileclick(obj, guild) {
obj.onclick = e => {
this.buildprofile(e.clientX, e.clientY, guild);
e.stopPropagation();

View file

@ -5,111 +5,109 @@ const buffer=new ArrayBuffer(2**26);
const view = new DataView(buffer, 0);
let i=0;
function write16(numb){
view.setUint16(i,numb);
i+=2;
view.setUint16(i,numb);
i+=2;
}
function write8(numb){
view.setUint8(i,numb);
i+=1;
view.setUint8(i,numb);
i+=1;
}
function writeString8(str){
const encode=new TextEncoder("utf-8").encode(str);
write8(encode.length);
for(const thing of encode){
write8(thing);
}
const encode=new TextEncoder("utf-8").encode(str);
write8(encode.length);
for(const thing of encode){
write8(thing);
}
}
function writeString16(str){
const encode=new TextEncoder("utf-8").encode(str);
write16(encode.length);
for(const thing of encode){
write8(thing);
}
const encode=new TextEncoder("utf-8").encode(str);
write16(encode.length);
for(const thing of encode){
write8(thing);
}
}
function writeStringNo(str){
const encode=new TextEncoder("utf-8").encode(str);
for(const thing of encode){
write8(thing);
}
const encode=new TextEncoder("utf-8").encode(str);
for(const thing of encode){
write8(thing);
}
}
write16(emojilist.length);
for(const thing of emojilist){
writeString16(thing.name);
write16(thing.emojis.length);
for(const emoji of thing.emojis){
writeString8(emoji.name);
write8(new TextEncoder("utf-8").encode(emoji.emoji).length+128*emoji.skin_tone_support);
writeStringNo(emoji.emoji);
}
writeString16(thing.name);
write16(thing.emojis.length);
for(const emoji of thing.emojis){
writeString8(emoji.name);
write8(new TextEncoder("utf-8").encode(emoji.emoji).length+128*emoji.skin_tone_support);
writeStringNo(emoji.emoji);
}
}
const out=new ArrayBuffer(i);
const ar=new Uint8Array(out);
const br=new Uint8Array(buffer)
const br=new Uint8Array(buffer);
for(const thing in ar){
ar[thing]=br[thing];
ar[thing]=br[thing];
}
console.log(i,ar);
function decodeEmojiList(buffer){
const view = new DataView(buffer, 0);
let i=0;
function read16(){
const int=view.getUint16(i);
i+=2;
return int;
}
function read8(){
const int=view.getUint8(i);
i+=1;
return int;
}
function readString8(){
return readStringNo(read8());
}
function readString16(){
return readStringNo(read16());
}
function readStringNo(length){
const array=new Uint8Array(length);
const view = new DataView(buffer, 0);
let i=0;
function read16(){
const int=view.getUint16(i);
i+=2;
return int;
}
function read8(){
const int=view.getUint8(i);
i+=1;
return int;
}
function readString8(){
return readStringNo(read8());
}
function readString16(){
return readStringNo(read16());
}
function readStringNo(length){
const array=new Uint8Array(length);
for(let i=0;i<length;i++){
array[i]=read8();
}
const decoded=new TextDecoder("utf-8").decode(array.buffer);;
for(let i=0;i<length;i++){
array[i]=read8();
}
//console.log(array);
return new TextDecoder("utf-8").decode(array.buffer);
}
const build=[];
let cats=read16();
//console.log(array);
return decoded;
}
const build=[];
let cats=read16();
for(;cats!==0;cats--){
const name=readString16();
const emojis=[];
let emojinumber=read16();
for(;emojinumber!==0;emojinumber--){
//console.log(emojis);
const name=readString8();
const len=read8();
const skin_tone_support=len>127;
const emoji=readStringNo(len-skin_tone_support*128);
emojis.push({
name,
skin_tone_support,
emoji
})
}
build.push({
name,
emojis
})
}
return build;
for(;cats!==0;cats--){
const name=readString16();
const emojis=[];
let emojinumber=read16();
for(;emojinumber!==0;emojinumber--){
//console.log(emojis);
const name=readString8();
const len=read8();
const skin_tone_support=len>127;
const emoji=readStringNo(len-skin_tone_support*128);
emojis.push({
name,
skin_tone_support,
emoji
});
}
build.push({
name,
emojis
});
}
return build;
}
console.log(JSON.stringify(decodeEmojiList(out)));
const fs = require('fs');
const fs = require("node:fs");
fs.writeFile("./webpage/emoji.bin",new Uint8Array(out),_=>{
});

View file

@ -10,10 +10,10 @@ const tsParser = require("@typescript-eslint/parser");
const linterOptions = {
reportUnusedDisableDirectives: "error"
}
};
const global = {
...globals.browser
}
};
const rules = {
"array-callback-return": 2,
@ -227,7 +227,7 @@ const rules = {
"sonarjs/prefer-while": 2,
"sonarjs/no-gratuitous-expressions": 2,
"sonarjs/no-duplicated-branches": 2
}
};
module.exports = [
{
@ -307,4 +307,4 @@ module.exports = [
"@html-eslint/require-img-alt": 1
}
}
]
];

293
index.js
View file

@ -1,119 +1,118 @@
#! /usr/bin/env node
const compression = require('compression')
const compression = require("compression");
const express = require('express');
const fs = require('fs');
const express = require("express");
const fs = require("node:fs");
const app = express();
const instances=require("./webpage/instances.json");
const stats=require("./stats.js");
const instancenames=new Map();
for(const instance of instances){
instancenames.set(instance.name,instance);
instancenames.set(instance.name,instance);
}
app.use(compression())
app.use(compression());
fetch("https://raw.githubusercontent.com/spacebarchat/spacebarchat/master/instances/instances.json").then(_=>_.json()).then(json=>{
for(const instance of json){
if(!instancenames.has(instance.name)){
instances.push(instance);
}else{
const ofinst=instancenames.get(instance.name)
for(const key of Object.keys(instance)){
if(!ofinst[key]){
ofinst[key]=instance[key];
}
}
}
}
stats.observe(instances)
})
app.use("/getupdates",(req, res) => {
const out=fs.statSync(`${__dirname}/webpage`);
res.send(out.mtimeMs+"");
for(const instance of json){
if(!instancenames.has(instance.name)){
instances.push(instance);
}else{
const ofinst=instancenames.get(instance.name);
for(const key of Object.keys(instance)){
if(!ofinst[key]){
ofinst[key]=instance[key];
}
}
}
}
stats.observe(instances);
});
let debugging=true;//Do not turn this off, the service worker is all kinds of jank as is, it'll really mess your day up if you disable this
app.use("/getupdates",(req, res)=>{
const out=fs.statSync(`${__dirname}/webpage`);
res.send(out.mtimeMs+"");
});
const debugging=true;//Do not turn this off, the service worker is all kinds of jank as is, it'll really mess your day up if you disable this
function isembed(str){
return str.includes("discord")||str.includes("Spacebar");
return str.includes("discord")||str.includes("Spacebar");
}
async function getapiurls(str){
if(str[str.length-1]!=="/"){
str+="/"
}
let api;
try{
const info=await fetch(`${str}/.well-known/spacebar`).then((x) => x.json());
api=info.api;
}catch{
return false
}
const url = new URL(api);
try{
const info=await fetch(`${api}${url.pathname.includes("api") ? "" : "api"}/policies/instance/domains`).then((x) => x.json());
return {
api: info.apiEndpoint,
gateway: info.gateway,
cdn: info.cdn,
wellknown: str,
};
}catch{
return false;
}
if(str.at(-1)!=="/"){
str+="/";
}
let api;
try{
const info=await fetch(`${str}/.well-known/spacebar`).then(x=>x.json());
api=info.api;
}catch{
return false;
}
const url = new URL(api);
try{
const info=await fetch(`${api}${url.pathname.includes("api") ? "" : "api"}/policies/instance/domains`).then(x=>x.json());
return{
api: info.apiEndpoint,
gateway: info.gateway,
cdn: info.cdn,
wellknown: str,
};
}catch{
return false;
}
}
async function inviteres(req,res){
let url;
if(URL.canParse(req.query.url)){
url=new URL(req.query.url);
}else{
const scheme = req.secure ? 'https' : 'http';
const host=`${scheme}://${req.get("Host")}`;
url=new URL(host);
}
try{
if(url.pathname.startsWith("invite")){
throw -1;
}
const code=url.pathname.split("/")[2];
let title="";
let description="";
let thumbnail="";
const urls=await getapiurls(url.searchParams.get("instance"));
await fetch(`${urls.api}/invites/${code}`,{
method:"GET"
}).then(_=>_.json()).then(json=>{
title=json.guild.name;
if(json.inviter){
description=json.inviter.username+" Has invited you to "+json.guild.name+(json.guild.description?json.guild.description+"\n":"");
}else{
description="you've been invited to "+json.guild.name+(json.guild.description?json.guild.description+"\n":"");
}
if(json.guild.icon){
thumbnail=`${urls.cdn}/icons/${json.guild.id}/${json.guild.icon}.png`;
}
});
const json={
type:"link",
version:"1.0",
title,
thumbnail,
description,
};
res.send(JSON.stringify(json));
}catch(e){
console.error(e);
const json={
type:"link",
version:"1.0",
title:"Jank Client",
thumbnail:"/logo.webp",
description:"A spacebar client that has DMs, replying and more",
url:url.toString()
};
res.send(JSON.stringify(json));
}
let url;
if(URL.canParse(req.query.url)){
url=new URL(req.query.url);
}else{
const scheme = req.secure ? "https" : "http";
const host=`${scheme}://${req.get("Host")}`;
url=new URL(host);
}
try{
if(url.pathname.startsWith("invite")){
throw-1;
}
const code=url.pathname.split("/")[2];
let title="";
let description="";
let thumbnail="";
const urls=await getapiurls(url.searchParams.get("instance"));
await fetch(`${urls.api}/invites/${code}`,{
method: "GET"
}).then(_=>_.json()).then(json=>{
title=json.guild.name;
if(json.inviter){
description=json.inviter.username+" Has invited you to "+json.guild.name+(json.guild.description?json.guild.description+"\n":"");
}else{
description="you've been invited to "+json.guild.name+(json.guild.description?json.guild.description+"\n":"");
}
if(json.guild.icon){
thumbnail=`${urls.cdn}/icons/${json.guild.id}/${json.guild.icon}.png`;
}
});
const json={
type: "link",
version: "1.0",
title,
thumbnail,
description,
};
res.send(JSON.stringify(json));
}catch(e){
console.error(e);
const json={
type: "link",
version: "1.0",
title: "Jank Client",
thumbnail: "/logo.webp",
description: "A spacebar client that has DMs, replying and more",
url: url.toString()
};
res.send(JSON.stringify(json));
}
}
/*
/*
function htmlEnc(s) {//https://stackoverflow.com/a/11561642
return s.replaceAll(/&/g, '&amp;')
.replaceAll(/</g, '&lt;')
@ -143,60 +142,56 @@ async function inviteres(req,res){
return false;
*/
app.use('/services/oembed', (req, res) => {
inviteres(req, res);
})
app.use("/services/oembed", (req, res)=>{
inviteres(req, res);
});
app.use("/uptime",(req,res)=>{
console.log(req.query.name)
const uptime=stats.uptime[req.query.name];
console.log(req.query.name,uptime,stats.uptime)
res.send(uptime);
})
app.use('/', async (req, res) => {
const scheme = req.secure ? 'https' : 'http';
const host=`${scheme}://${req.get("Host")}`;
const ref=host+req.originalUrl;
if(host&&ref){
const link=`${host}/services/oembed?url=${encodeURIComponent(ref)}`;
res.set("Link",`<${link}>; rel="alternate"; type="application/json+oembed"; title="Jank Client oEmbed format"`);
}else{
console.log(req);
}
if(req.path==="/"){
res.sendFile(`./webpage/home.html`, {root: __dirname});
return;
}
if(debugging&&req.path.startsWith("/service.js")){
res.send("dud");
return;
}
if(req.path.startsWith("/instances.json")){
res.send(JSON.stringify(instances));
return;
}
if(req.path.startsWith("/invite/")){
res.sendFile(`./webpage/invite.html`, {root: __dirname});
return;
}
if(fs.existsSync(`${__dirname}/webpage${req.path}`)) {
res.sendFile(`./webpage${req.path}`, {root: __dirname});
}else if(req.path.endsWith(".js") && fs.existsSync(`${__dirname}/.dist${req.path}`)){
const dir=`./.dist${req.path}`;
res.sendFile(dir, {root: __dirname});
return;
}
else if(fs.existsSync(`${__dirname}/webpage${req.path}.html`)) {
res.sendFile(`./webpage${req.path}.html`, {root: __dirname});
}
else {
res.sendFile("./webpage/index.html", {root: __dirname});
}
console.log(req.query.name);
const uptime=stats.uptime[req.query.name];
console.log(req.query.name,uptime,stats.uptime);
res.send(uptime);
});
app.use("/", async (req, res)=>{
const scheme = req.secure ? "https" : "http";
const host=`${scheme}://${req.get("Host")}`;
const ref=host+req.originalUrl;
if(host&&ref){
const link=`${host}/services/oembed?url=${encodeURIComponent(ref)}`;
res.set("Link",`<${link}>; rel="alternate"; type="application/json+oembed"; title="Jank Client oEmbed format"`);
}else{
console.log(req);
}
if(req.path==="/"){
res.sendFile("./webpage/home.html", {root: __dirname});
return;
}
if(debugging&&req.path.startsWith("/service.js")){
res.send("dud");
return;
}
if(req.path.startsWith("/instances.json")){
res.send(JSON.stringify(instances));
return;
}
if(req.path.startsWith("/invite/")){
res.sendFile("./webpage/invite.html", {root: __dirname});
return;
}
if(fs.existsSync(`${__dirname}/webpage${req.path}`)){
res.sendFile(`./webpage${req.path}`, {root: __dirname});
}else if(req.path.endsWith(".js") && fs.existsSync(`${__dirname}/.dist${req.path}`)){
const dir=`./.dist${req.path}`;
res.sendFile(dir, {root: __dirname});
}else if(fs.existsSync(`${__dirname}/webpage${req.path}.html`)){
res.sendFile(`./webpage${req.path}.html`, {root: __dirname});
}else{
res.sendFile("./webpage/index.html", {root: __dirname});
}
});
const PORT = process.env.PORT || +process.argv[1] || 8080;
app.listen(PORT, () => {});
const PORT = process.env.PORT || Number(process.argv[1]) || 8080;
app.listen(PORT, ()=>{});
console.log("this ran :P");
exports.getapiurls=getapiurls;

284
stats.js
View file

@ -1,160 +1,164 @@
const index = require('./index.js');
const fs=require("fs");
const index = require("./index.js");
const fs=require("node:fs");
let uptimeObject={};
if(fs.existsSync("./uptime.json")){
try{
uptimeObject=JSON.parse(fs.readFileSync('./uptime.json', 'utf8'));
}catch{
uptimeObject={};
}
try{
uptimeObject=JSON.parse(fs.readFileSync("./uptime.json", "utf8"));
}catch{
uptimeObject={};
}
}
if(uptimeObject["undefined"]){
delete uptimeObject["undefined"];
updatejson();
if(uptimeObject.undefined){
delete uptimeObject.undefined;
updatejson();
}
async function observe(instances){
const active=new Set();
async function resolveinstance(instance){
try{calcStats(instance)}catch(e){console.error(e)}
let api;
if(instance.urls){
api=instance.urls.api;
}else if(instance.url){
const urls=await index.getapiurls(instance.url);
if(urls){
api=urls.api;
}
}
if(!api||api===""){
setStatus(instance,false);
console.warn(instance.name+" does not resolve api URL");
setTimeout(_=>{resolveinstance(instance)},1000*60*30,);
return
}
active.add(instance.name);
api+=api.endsWith("/")?"":"/"
function check(){
fetch(api+"ping",{method:"HEAD"}).then(_=>{
setStatus(instance,_.ok);
})
}
setTimeout(
_=>{
check();
setInterval(_=>{
check();
},1000*60*30)
},Math.random()*1000*60*10
)
}
const promlist=[];
for(const instance of instances){
promlist.push(resolveinstance(instance));
}
await Promise.allSettled(promlist);
for(const key of Object.keys(uptimeObject)){
if(!active.has(key)){
setStatus(key,false);
}
}
const active=new Set();
async function resolveinstance(instance){
try{
calcStats(instance);
}catch(e){
console.error(e);
}
let api;
if(instance.urls){
api=instance.urls.api;
}else if(instance.url){
const urls=await index.getapiurls(instance.url);
if(urls){
api=urls.api;
}
}
if(!api||api===""){
setStatus(instance,false);
console.warn(instance.name+" does not resolve api URL");
setTimeout(_=>{
resolveinstance(instance);
},1000*60*30,);
return;
}
active.add(instance.name);
api+=api.endsWith("/")?"":"/";
function check(){
fetch(api+"ping",{method: "HEAD"}).then(_=>{
setStatus(instance,_.ok);
});
}
setTimeout(
_=>{
check();
setInterval(_=>{
check();
},1000*60*30);
},Math.random()*1000*60*10
);
}
const promlist=[];
for(const instance of instances){
promlist.push(resolveinstance(instance));
}
await Promise.allSettled(promlist);
for(const key of Object.keys(uptimeObject)){
if(!active.has(key)){
setStatus(key,false);
}
}
}
function calcStats(instance){
let obj=uptimeObject[instance.name];
if(!obj) return;
const day=Date.now()-1000*60*60*24;
const week=Date.now()-1000*60*60*24*7;
let alltime=-1;
let totalTimePassed=0;
let daytime=-1;
let weektime=-1;
let online=false;
let i=0;
for(const thing of obj){
online=thing.online;
const stamp=thing.time;
if(alltime===-1){
alltime=0;
}
let timepassed;
if(obj[i+1]){
timepassed=obj[i+1].time-stamp;
}else{
timepassed=Date.now()-stamp;
}
totalTimePassed+=timepassed;
alltime+=online*timepassed;
if(stamp>week){
if(weektime===-1){
weektime=online*(stamp-week);
}else{
weektime+=online*timepassed;
}
if(stamp>day){
if(daytime===-1){
daytime=online*(stamp-day);
}else{
daytime+=online*timepassed;
}
}
}
const obj=uptimeObject[instance.name];
if(!obj)return;
const day=Date.now()-1000*60*60*24;
const week=Date.now()-1000*60*60*24*7;
let alltime=-1;
let totalTimePassed=0;
let daytime=-1;
let weektime=-1;
let online=false;
let i=0;
for(const thing of obj){
online=thing.online;
const stamp=thing.time;
if(alltime===-1){
alltime=0;
}
let timepassed;
if(obj[i+1]){
timepassed=obj[i+1].time-stamp;
}else{
timepassed=Date.now()-stamp;
}
totalTimePassed+=timepassed;
alltime+=online*timepassed;
if(stamp>week){
if(weektime===-1){
weektime=online*(stamp-week);
}else{
weektime+=online*timepassed;
}
if(stamp>day){
if(daytime===-1){
daytime=online*(stamp-day);
}else{
daytime+=online*timepassed;
}
}
}
i++;
}
console.log(daytime);
instance.online=online;
alltime/=totalTimePassed;
if(totalTimePassed>1000*60*60*24){
if(daytime===-1){
daytime=online*1000*60*60*24;
}
daytime/=1000*60*60*24;
if(totalTimePassed>1000*60*60*24*7){
if(weektime===-1){
weektime=online*1000*60*60*24*7;
}
weektime/=1000*60*60*24*7;
}else{
weektime=alltime;
}
}else{
weektime=alltime
daytime=alltime;
}
instance.uptime={daytime,weektime,alltime}
i++;
}
console.log(daytime);
instance.online=online;
alltime/=totalTimePassed;
if(totalTimePassed>1000*60*60*24){
if(daytime===-1){
daytime=online*1000*60*60*24;
}
daytime/=1000*60*60*24;
if(totalTimePassed>1000*60*60*24*7){
if(weektime===-1){
weektime=online*1000*60*60*24*7;
}
weektime/=1000*60*60*24*7;
}else{
weektime=alltime;
}
}else{
weektime=alltime;
daytime=alltime;
}
instance.uptime={daytime,weektime,alltime};
}
/**
* @param {string|Object} instance
* @param {boolean} status
*/
function setStatus(instance,status){
let name=instance.name;
if(typeof instance==="string" ){
name=instance;
}
let name=instance.name;
if(typeof instance==="string"){
name=instance;
}
let obj=uptimeObject[name];
let needSetting=false;
if(!obj){
obj=[];
uptimeObject[name]=obj;
needSetting=true;
}else{
if(obj[obj.length-1].online!==status){
needSetting=true;
}
}
if(needSetting){
obj.push({time:Date.now(),online:status});
updatejson();
}
if(typeof instance!=="string" ){
calcStats(instance);
}
let obj=uptimeObject[name];
let needSetting=false;
if(!obj){
obj=[];
uptimeObject[name]=obj;
needSetting=true;
}else{
if(obj.at(-1).online!==status){
needSetting=true;
}
}
if(needSetting){
obj.push({time: Date.now(),online: status});
updatejson();
}
if(typeof instance!=="string"){
calcStats(instance);
}
}
function updatejson(){
fs.writeFile('./uptime.json',JSON.stringify(uptimeObject),_=>{});
fs.writeFile("./uptime.json",JSON.stringify(uptimeObject),_=>{});
}
exports.observe=observe;
exports.uptime=uptimeObject;

View file

@ -1,146 +1,164 @@
import {getBulkInfo} from "./login.js";
import{getBulkInfo}from"./login.js";
class Voice{
audioCtx:AudioContext;
info:{wave:string|Function,freq:number};
playing:boolean;
myArrayBuffer:AudioBuffer;
gainNode:GainNode;
buffer:Float32Array;
source:AudioBufferSourceNode;
constructor(wave:string|Function,freq:number,volume=1){
this.audioCtx = new (window.AudioContext)();
this.info={wave:wave,freq:freq}
this.playing=false;
this.myArrayBuffer=this.audioCtx.createBuffer(
1,
this.audioCtx.sampleRate,
this.audioCtx.sampleRate,
);
this.gainNode = this.audioCtx.createGain();
this.gainNode.gain.value=volume;
this.gainNode.connect(this.audioCtx.destination);
this.buffer=this.myArrayBuffer.getChannelData(0);
this.source = this.audioCtx.createBufferSource();
this.source.buffer = this.myArrayBuffer;
this.source.loop=true;
this.source.start();
this.updateWave();
}
get wave():string|Function{
return this.info.wave;
}
get freq():number{
return this.info.freq;
}
set wave(wave:string|Function){
this.info.wave=wave;
this.updateWave();
}
set freq(freq:number){
this.info.freq=freq;
this.updateWave();
}
updateWave():void{
const func=this.waveFunction();
for (let i = 0; i < this.buffer.length; i++) {
this.buffer[i]=func(i/this.audioCtx.sampleRate,this.freq);
}
}
waveFunction():Function{
if(typeof this.wave === 'function'){
return this.wave;
}
switch(this.wave){
case "sin":
return (t:number,freq:number)=>{
return Math.sin(t*Math.PI*2*freq);
}
case "triangle":
return (t:number,freq:number)=>{
return Math.abs((4*t*freq)%4-2)-1;
}
case "sawtooth":
return (t:number,freq:number)=>{
return ((t*freq)%1)*2-1;
}
case "square":
return (t:number,freq:number)=>{
return (t*freq)%2<1?1:-1;
}
case "white":
return (_t:number,_freq:number)=>{
return Math.random()*2-1;
}
case "noise":
return (_t:number,_freq:number)=>{
return 0;
}
}
return new Function();
}
play():void{
if(this.playing){
return;
}
this.source.connect(this.gainNode);
this.playing=true;
}
stop():void{
if(this.playing){
this.source.disconnect();
this.playing=false;
}
}
static noises(noise:string):void{
switch(noise){
case "three":{
const voicy=new Voice("sin",800);
voicy.play();
setTimeout(_=>{voicy.freq=1000},50);
setTimeout(_=>{voicy.freq=1300},100);
setTimeout(_=>{voicy.stop()},150);
break;
}
case "zip":{
const voicy=new Voice((t:number,freq:number)=>{
return Math.sin(((t+2)**(Math.cos(t*4)))*Math.PI*2*freq);
},700);
voicy.play();
setTimeout(_=>{voicy.stop()},150);
break;
}
case "square":{
const voicy=new Voice("square",600,.4);
voicy.play()
setTimeout(_=>{voicy.freq=800},50);
setTimeout(_=>{voicy.freq=1000},100);
setTimeout(_=>{voicy.stop()},150);
break;
}
case "beep":{
const voicy=new Voice("sin",800);
voicy.play();
setTimeout(_=>{voicy.stop()},50);
setTimeout(_=>{voicy.play();},100);
setTimeout(_=>{voicy.stop()},150);
break;
}
}
}
static get sounds(){
return ["three","zip","square","beep"];
}
static setNotificationSound(sound:string){
let userinfos=getBulkInfo();
userinfos.preferences.notisound=sound;
localStorage.setItem("userinfos",JSON.stringify(userinfos));
}
static getNotificationSound(){
let userinfos=getBulkInfo();
return userinfos.preferences.notisound;
}
audioCtx:AudioContext;
info:{wave:string|Function,freq:number};
playing:boolean;
myArrayBuffer:AudioBuffer;
gainNode:GainNode;
buffer:Float32Array;
source:AudioBufferSourceNode;
constructor(wave:string|Function,freq:number,volume=1){
this.audioCtx = new (window.AudioContext)();
this.info={wave,freq};
this.playing=false;
this.myArrayBuffer=this.audioCtx.createBuffer(
1,
this.audioCtx.sampleRate,
this.audioCtx.sampleRate,
);
this.gainNode = this.audioCtx.createGain();
this.gainNode.gain.value=volume;
this.gainNode.connect(this.audioCtx.destination);
this.buffer=this.myArrayBuffer.getChannelData(0);
this.source = this.audioCtx.createBufferSource();
this.source.buffer = this.myArrayBuffer;
this.source.loop=true;
this.source.start();
this.updateWave();
}
get wave():string|Function{
return this.info.wave;
}
get freq():number{
return this.info.freq;
}
set wave(wave:string|Function){
this.info.wave=wave;
this.updateWave();
}
set freq(freq:number){
this.info.freq=freq;
this.updateWave();
}
updateWave():void{
const func=this.waveFunction();
for(let i = 0; i < this.buffer.length; i++){
this.buffer[i]=func(i/this.audioCtx.sampleRate,this.freq);
}
}
waveFunction():Function{
if(typeof this.wave === "function"){
return this.wave;
}
switch(this.wave){
case"sin":
return(t:number,freq:number)=>{
return Math.sin(t*Math.PI*2*freq);
};
case"triangle":
return(t:number,freq:number)=>{
return Math.abs((4*t*freq)%4-2)-1;
};
case"sawtooth":
return(t:number,freq:number)=>{
return((t*freq)%1)*2-1;
};
case"square":
return(t:number,freq:number)=>{
return(t*freq)%2<1?1:-1;
};
case"white":
return(_t:number,_freq:number)=>{
return Math.random()*2-1;
};
case"noise":
return(_t:number,_freq:number)=>{
return 0;
};
}
return new Function();
}
play():void{
if(this.playing){
return;
}
this.source.connect(this.gainNode);
this.playing=true;
}
stop():void{
if(this.playing){
this.source.disconnect();
this.playing=false;
}
}
static noises(noise:string):void{
switch(noise){
case"three":{
const voicy=new Voice("sin",800);
voicy.play();
setTimeout(_=>{
voicy.freq=1000;
},50);
setTimeout(_=>{
voicy.freq=1300;
},100);
setTimeout(_=>{
voicy.stop();
},150);
break;
}
case"zip":{
const voicy=new Voice((t:number,freq:number)=>{
return Math.sin(((t+2)**(Math.cos(t*4)))*Math.PI*2*freq);
},700);
voicy.play();
setTimeout(_=>{
voicy.stop();
},150);
break;
}
case"square":{
const voicy=new Voice("square",600,0.4);
voicy.play();
setTimeout(_=>{
voicy.freq=800;
},50);
setTimeout(_=>{
voicy.freq=1000;
},100);
setTimeout(_=>{
voicy.stop();
},150);
break;
}
case"beep":{
const voicy=new Voice("sin",800);
voicy.play();
setTimeout(_=>{
voicy.stop();
},50);
setTimeout(_=>{
voicy.play();
},100);
setTimeout(_=>{
voicy.stop();
},150);
break;
}
}
}
static get sounds(){
return["three","zip","square","beep"];
}
static setNotificationSound(sound:string){
const userinfos=getBulkInfo();
userinfos.preferences.notisound=sound;
localStorage.setItem("userinfos",JSON.stringify(userinfos));
}
static getNotificationSound(){
const userinfos=getBulkInfo();
return userinfos.preferences.notisound;
}
}
export {Voice as Voice};
export{Voice};

File diff suppressed because it is too large Load diff

View file

@ -1,88 +1,88 @@
class Contextmenu<x,y>{
static currentmenu;
name:string;
buttons:[string,(this:x,arg:y,e:MouseEvent)=>void,string|null,(this:x,arg:y)=>boolean,(this:x,arg:y)=>boolean,string][];
div:HTMLDivElement;
static setup(){
Contextmenu.currentmenu="";
document.addEventListener('click', function(event) {
if(Contextmenu.currentmenu==""){
return;
}
if (!Contextmenu.currentmenu.contains(event.target)) {
Contextmenu.currentmenu.remove();
Contextmenu.currentmenu="";
}
});
}
constructor(name:string){
this.name=name;
this.buttons=[]
}
addbutton(text:string,onclick:(this:x,arg:y,e:MouseEvent)=>void,img:null|string=null,shown:(this:x,arg:y)=>boolean=_=>true,enabled:(this:x,arg:y)=>boolean=_=>true){
this.buttons.push([text,onclick,img,shown,enabled,"button"]);
return {};
}
addsubmenu(text:string,onclick:(this:x,arg:y,e:MouseEvent)=>void,img=null,shown:(this:x,arg:y)=>boolean=_=>true,enabled:(this:x,arg:y)=>boolean=_=>true){
this.buttons.push([text,onclick,img,shown,enabled,"submenu"])
return {};
}
makemenu(x:number,y:number,addinfo:any,other:y){
const div=document.createElement("div");
div.classList.add("contextmenu","flexttb");
static currentmenu;
name:string;
buttons:[string,(this:x,arg:y,e:MouseEvent)=>void,string|null,(this:x,arg:y)=>boolean,(this:x,arg:y)=>boolean,string][];
div:HTMLDivElement;
static setup(){
Contextmenu.currentmenu="";
document.addEventListener("click", event=>{
if(Contextmenu.currentmenu==""){
return;
}
if(!Contextmenu.currentmenu.contains(event.target)){
Contextmenu.currentmenu.remove();
Contextmenu.currentmenu="";
}
});
}
constructor(name:string){
this.name=name;
this.buttons=[];
}
addbutton(text:string,onclick:(this:x,arg:y,e:MouseEvent)=>void,img:null|string=null,shown:(this:x,arg:y)=>boolean=_=>true,enabled:(this:x,arg:y)=>boolean=_=>true){
this.buttons.push([text,onclick,img,shown,enabled,"button"]);
return{};
}
addsubmenu(text:string,onclick:(this:x,arg:y,e:MouseEvent)=>void,img=null,shown:(this:x,arg:y)=>boolean=_=>true,enabled:(this:x,arg:y)=>boolean=_=>true){
this.buttons.push([text,onclick,img,shown,enabled,"submenu"]);
return{};
}
makemenu(x:number,y:number,addinfo:any,other:y){
const div=document.createElement("div");
div.classList.add("contextmenu","flexttb");
let visibleButtons=0;
for(const thing of this.buttons){
if(!thing[3].bind(addinfo)(other))continue;
visibleButtons++;
let visibleButtons=0;
for(const thing of this.buttons){
if(!thing[3].bind(addinfo)(other))continue;
visibleButtons++;
const intext=document.createElement("button")
intext.disabled=!thing[4].bind(addinfo)(other);
intext.classList.add("contextbutton")
intext.textContent=thing[0]
console.log(thing)
if(thing[5]==="button"||thing[5]==="submenu"){
intext.onclick=thing[1].bind(addinfo,other);
}
const intext=document.createElement("button");
intext.disabled=!thing[4].bind(addinfo)(other);
intext.classList.add("contextbutton");
intext.textContent=thing[0];
console.log(thing);
if(thing[5]==="button"||thing[5]==="submenu"){
intext.onclick=thing[1].bind(addinfo,other);
}
div.appendChild(intext);
}
if (visibleButtons == 0) return;
div.appendChild(intext);
}
if(visibleButtons == 0)return;
if(Contextmenu.currentmenu!=""){
Contextmenu.currentmenu.remove();
}
div.style.top = y+'px';
div.style.left = x+'px';
document.body.appendChild(div);
Contextmenu.keepOnScreen(div);
console.log(div)
Contextmenu.currentmenu=div;
return this.div;
}
bindContextmenu(obj:HTMLElement,addinfo:x,other:y){
const func=(event) => {
event.preventDefault();
event.stopImmediatePropagation();
this.makemenu(event.clientX,event.clientY,addinfo,other);
}
obj.addEventListener("contextmenu", func);
return func;
}
static keepOnScreen(obj:HTMLElement){
const html = document.documentElement.getBoundingClientRect();
const docheight=html.height
const docwidth=html.width
const box=obj.getBoundingClientRect();
console.log(box,docheight,docwidth);
if(box.right>docwidth){
console.log("test")
obj.style.left = docwidth-box.width+'px';
}
if(box.bottom>docheight){
obj.style.top = docheight-box.height+'px';
}
}
if(Contextmenu.currentmenu!=""){
Contextmenu.currentmenu.remove();
}
div.style.top = y+"px";
div.style.left = x+"px";
document.body.appendChild(div);
Contextmenu.keepOnScreen(div);
console.log(div);
Contextmenu.currentmenu=div;
return this.div;
}
bindContextmenu(obj:HTMLElement,addinfo:x,other:y){
const func=event=>{
event.preventDefault();
event.stopImmediatePropagation();
this.makemenu(event.clientX,event.clientY,addinfo,other);
};
obj.addEventListener("contextmenu", func);
return func;
}
static keepOnScreen(obj:HTMLElement){
const html = document.documentElement.getBoundingClientRect();
const docheight=html.height;
const docwidth=html.width;
const box=obj.getBoundingClientRect();
console.log(box,docheight,docwidth);
if(box.right>docwidth){
console.log("test");
obj.style.left = docwidth-box.width+"px";
}
if(box.bottom>docheight){
obj.style.top = docheight-box.height+"px";
}
}
}
Contextmenu.setup();
export {Contextmenu as Contextmenu}
export{Contextmenu};

View file

@ -1,251 +1,254 @@
class Dialog{
layout;
onclose: Function;
onopen: Function;
html:HTMLDivElement;
background: HTMLDivElement;
constructor(layout,onclose=_=>{},onopen=_=>{}){
this.layout=layout;
this.onclose=onclose;
this.onopen=onopen;
const div=document.createElement("div");
div.appendChild(this.tohtml(layout))
this.html=div;
this.html.classList.add("centeritem");
if(!(layout[0]==="img")){
layout;
onclose: Function;
onopen: Function;
html:HTMLDivElement;
background: HTMLDivElement;
constructor(layout,onclose=_=>{},onopen=_=>{}){
this.layout=layout;
this.onclose=onclose;
this.onopen=onopen;
const div=document.createElement("div");
div.appendChild(this.tohtml(layout));
this.html=div;
this.html.classList.add("centeritem");
if(!(layout[0]==="img")){
this.html.classList.add("nonimagecenter");
}
}
tohtml(array:any[]){
switch(array[0]){
case"img":
const img=document.createElement("img");
img.src=array[1];
if(array[2]!=undefined){
if(array[2].length==2){
img.width=array[2][0];
img.height=array[2][1];
}else if(array[2][0]=="fit"){
img.classList.add("imgfit");
}
}
return img;
case"hdiv":
const hdiv=document.createElement("table");
const tr=document.createElement("tr");
hdiv.appendChild(tr);
this.html.classList.add("nonimagecenter");
}
}
tohtml(array:any[]){
switch(array[0]){
case "img":
const img=document.createElement("img");
img.src=array[1];
if(array[2]!=undefined){
if(array[2].length==2){
img.width=array[2][0];
img.height=array[2][1];
}else if(array[2][0]=="fit"){
img.classList.add("imgfit")
}
}
return img;
case "hdiv":
const hdiv=document.createElement("table");
const tr=document.createElement("tr");
hdiv.appendChild(tr)
for(const thing of array){
if(thing==="hdiv"){
continue;
}
const td=document.createElement("td");
td.appendChild(this.tohtml(thing));
tr.appendChild(td);
}
return hdiv;
case"vdiv":
const vdiv=document.createElement("table");
for(const thing of array){
if(thing==="vdiv"){
continue;
}
const tr=document.createElement("tr");
tr.appendChild(this.tohtml(thing));
vdiv.appendChild(tr);
}
return vdiv;
case"checkbox":
{
const div=document.createElement("div");
const checkbox = document.createElement("input");
div.appendChild(checkbox);
const label=document.createElement("span");
checkbox.checked=array[2];
label.textContent=array[1];
div.appendChild(label);
checkbox.addEventListener("change",array[3]);
checkbox.type = "checkbox";
return div;
}
case"button":
{
const div=document.createElement("div");
const input = document.createElement("button");
for(const thing of array){
if(thing==="hdiv"){continue;}
const td=document.createElement("td");
td.appendChild(this.tohtml(thing));
tr.appendChild(td);
}
return hdiv;
case "vdiv":
const vdiv=document.createElement("table");
for(const thing of array){
if(thing==="vdiv"){continue;}
const tr=document.createElement("tr");
tr.appendChild(this.tohtml(thing));
vdiv.appendChild(tr);
}
return vdiv;
case "checkbox":
{
const div=document.createElement("div");
const checkbox = document.createElement('input');
div.appendChild(checkbox)
const label=document.createElement("span");
checkbox.checked=array[2];
label.textContent=array[1];
div.appendChild(label);
checkbox.addEventListener("change",array[3]);
checkbox.type = "checkbox";
return div;
}
case "button":
{
const div=document.createElement("div");
const input = document.createElement('button');
const label=document.createElement("span");
input.textContent=array[2];
label.textContent=array[1];
div.appendChild(label);
div.appendChild(input);
input.addEventListener("click",array[3]);
return div;
}
case"mdbox":
{
const div=document.createElement("div");
const input=document.createElement("textarea");
input.value=array[2];
const label=document.createElement("span");
label.textContent=array[1];
input.addEventListener("input",array[3]);
div.appendChild(label);
div.appendChild(document.createElement("br"));
div.appendChild(input);
return div;
}
case"textbox":
{
const div=document.createElement("div");
const input=document.createElement("input");
input.value=array[2];
input.type="text";
const label=document.createElement("span");
label.textContent=array[1];
console.log(array[3]);
input.addEventListener("input",array[3]);
div.appendChild(label);
div.appendChild(input);
return div;
}
case"fileupload":
{
const div=document.createElement("div");
const input=document.createElement("input");
input.type="file";
const label=document.createElement("span");
label.textContent=array[1];
div.appendChild(label);
div.appendChild(input);
input.addEventListener("change",array[2]);
console.log(array);
return div;
}
case"text":{
const span =document.createElement("span");
span.textContent=array[1];
return span;
}
case"title":{
const span =document.createElement("span");
span.classList.add("title");
span.textContent=array[1];
return span;
}
case"radio":{
const div=document.createElement("div");
const fieldset=document.createElement("fieldset");
fieldset.addEventListener("change",()=>{
let i=-1;
for(const thing of fieldset.children){
i++;
if(i===0){
continue;
}
const checkbox = thing.children[0].children[0] as HTMLInputElement;
if(checkbox.checked){
array[3](checkbox.value);
}
}
});
const legend=document.createElement("legend");
legend.textContent=array[1];
fieldset.appendChild(legend);
let i=0;
for(const thing of array[2]){
const div=document.createElement("div");
const input=document.createElement("input");
input.classList.add("radio");
input.type="radio";
input.name=array[1];
input.value=thing;
if(i===array[4]){
input.checked=true;
}
const label=document.createElement("label");
const label=document.createElement("span");
input.textContent=array[2];
label.textContent=array[1];
div.appendChild(label);
div.appendChild(input)
input.addEventListener("click",array[3]);
return div;
}
case "mdbox":
{
const div=document.createElement("div");
const input=document.createElement("textarea");
input.value=array[2];
const label=document.createElement("span");
label.textContent=array[1];
input.addEventListener("input",array[3]);
div.appendChild(label);
div.appendChild(document.createElement("br"));
div.appendChild(input);
return div;
}
case "textbox":
{
const div=document.createElement("div");
const input=document.createElement("input");
input.value=array[2];
input.type="text";
const label=document.createElement("span");
label.textContent=array[1];
console.log(array[3])
input.addEventListener("input",array[3]);
div.appendChild(label);
div.appendChild(input);
return div;
}
case "fileupload":
{
const div=document.createElement("div");
const input=document.createElement("input");
input.type="file";
const label=document.createElement("span");
label.textContent=array[1];
div.appendChild(label);
div.appendChild(input);
input.addEventListener("change",array[2]);
console.log(array)
return div;
}
case "text":{
const span =document.createElement("span");
span.textContent=array[1];
return span;
}
case "title":{
const span =document.createElement("span");
span.classList.add("title")
span.textContent=array[1];
return span;
}
case "radio":{
const div=document.createElement("div");
const fieldset=document.createElement("fieldset");
fieldset.addEventListener("change",function(){
let i=-1;
for(const thing of fieldset.children){
i++;
if(i===0){
continue;
}
const checkbox = thing.children[0].children[0] as HTMLInputElement;
if(checkbox.checked){
array[3](checkbox.value);
}
}
});
const legend=document.createElement("legend");
legend.textContent=array[1];
fieldset.appendChild(legend);
let i=0;
for(const thing of array[2]){
const div=document.createElement("div");
const input=document.createElement("input");
input.classList.add("radio")
input.type="radio";
input.name=array[1];
input.value=thing;
if(i===array[4]){
input.checked=true;
}
const label=document.createElement("label");
label.appendChild(input);
const span=document.createElement("span");
span.textContent=thing;
label.appendChild(span);
div.appendChild(label);
fieldset.appendChild(div);
i++;
}
div.appendChild(fieldset);
return div;
}
case"html":
return array[1];
label.appendChild(input);
const span=document.createElement("span");
span.textContent=thing;
label.appendChild(span);
div.appendChild(label);
fieldset.appendChild(div);
i++
}
div.appendChild(fieldset);
return div;
}
case "html":{
return array[1];
}
case "select":{
const div=document.createElement("div");
const label=document.createElement("label");
const select=document.createElement("select");
case"select":{
const div=document.createElement("div");
const label=document.createElement("label");
const select=document.createElement("select");
label.textContent=array[1];
div.append(label);
div.appendChild(select);
for(const thing of array[2]){
const option = document.createElement("option");
option.textContent=thing;
select.appendChild(option);
}
select.selectedIndex=array[4];
select.addEventListener("change",array[3]);
return div;
}
case "tabs":{
const table=document.createElement("table");
const tabs=document.createElement("tr");
tabs.classList.add("tabbed-head");
table.appendChild(tabs);
const td=document.createElement("td");
tabs.appendChild(td);
const content=document.createElement("tr");
content.classList.add("tabbed-content");
table.appendChild(content);
label.textContent=array[1];
div.append(label);
div.appendChild(select);
for(const thing of array[2]){
const option = document.createElement("option");
option.textContent=thing;
select.appendChild(option);
}
select.selectedIndex=array[4];
select.addEventListener("change",array[3]);
return div;
}
case"tabs":{
const table=document.createElement("table");
const tabs=document.createElement("tr");
tabs.classList.add("tabbed-head");
table.appendChild(tabs);
const td=document.createElement("td");
tabs.appendChild(td);
const content=document.createElement("tr");
content.classList.add("tabbed-content");
table.appendChild(content);
let shown;
for(const thing of array[1]){
const button=document.createElement("button");
button.textContent=thing[0];
td.appendChild(button);
let shown;
for(const thing of array[1]){
const button=document.createElement("button");
button.textContent=thing[0];
td.appendChild(button);
const tdcontent=document.createElement("td");
tdcontent.colSpan=array[1].length;
tdcontent.appendChild(this.tohtml(thing[1]));
content.appendChild(tdcontent);
if(!shown){
shown=tdcontent;
}else{
tdcontent.hidden=true;
}
button.addEventListener("click",_=>{
shown.hidden=true;
tdcontent.hidden=false;
shown=tdcontent;
})
}
return table;
}
default:
console.error("can't find element:"+array[0]," full element:"+array)
return;
}
}
show(){
this.onopen();
console.log("fullscreen")
this.background=document.createElement("div");
this.background.classList.add("background");
document.body.appendChild(this.background);
document.body.appendChild(this.html);
this.background.onclick = _=>{this.hide()};
}
hide(){
document.body.removeChild(this.background);
document.body.removeChild(this.html);
}
const tdcontent=document.createElement("td");
tdcontent.colSpan=array[1].length;
tdcontent.appendChild(this.tohtml(thing[1]));
content.appendChild(tdcontent);
if(!shown){
shown=tdcontent;
}else{
tdcontent.hidden=true;
}
button.addEventListener("click",_=>{
shown.hidden=true;
tdcontent.hidden=false;
shown=tdcontent;
});
}
return table;
}
default:
console.error("can't find element:"+array[0]," full element:"+array);
}
}
show(){
this.onopen();
console.log("fullscreen");
this.background=document.createElement("div");
this.background.classList.add("background");
document.body.appendChild(this.background);
document.body.appendChild(this.html);
this.background.onclick = _=>{
this.hide();
};
}
hide(){
document.body.removeChild(this.background);
document.body.removeChild(this.html);
}
}
export {Dialog};
export{Dialog};

View file

@ -1,61 +1,61 @@
import {Guild} from "./guild.js";
import { Channel } from "./channel.js";
import { Message } from "./message.js";
import { Localuser } from "./localuser.js";
import {User} from "./user.js";
import { Member } from "./member.js";
import { SnowFlake } from "./snowflake.js";
import { dirrectjson, memberjson } from "./jsontypes.js";
import { Permissions } from "./permissions.js";
import{Guild}from"./guild.js";
import{ Channel }from"./channel.js";
import{ Message }from"./message.js";
import{ Localuser }from"./localuser.js";
import{User}from"./user.js";
import{ Member }from"./member.js";
import{ SnowFlake }from"./snowflake.js";
import{ dirrectjson, memberjson }from"./jsontypes.js";
import{ Permissions }from"./permissions.js";
class Direct extends Guild{
constructor(json:dirrectjson[],owner:Localuser){
super(-1,owner,null);
this.message_notifications=0;
this.owner=owner;
if(!this.localuser){
console.error("Owner was not included, please fix")
}
this.headers=this.localuser.headers;
this.channels=[];
this.channelids={};
this.snowflake=new SnowFlake("@me",this);
this.properties={};
this.roles=[];
this.roleids=new Map();
this.prevchannel=undefined;
this.properties.name="Direct Messages";
for(const thing of json){
const temp=new Group(thing,this);
this.channels.push(temp);
this.channelids[temp.id]=temp;
}
this.headchannels=this.channels;
}
createChannelpac(json){
const thischannel=new Group(json,this);
this.channelids[json.id]=thischannel;
this.channels.push(thischannel);
this.calculateReorder();
this.printServers();
}
giveMember(_member:memberjson){
console.error("not a real guild, can't give member object")
}
getRole(ID:string){
return null;
}
hasRole(r:string){
return false;
}
isAdmin(){
return false;
}
unreaddms(){
for(const thing of this.channels){
(thing as Group).unreads();
}
}
constructor(json:dirrectjson[],owner:Localuser){
super(-1,owner,null);
this.message_notifications=0;
this.owner=owner;
if(!this.localuser){
console.error("Owner was not included, please fix");
}
this.headers=this.localuser.headers;
this.channels=[];
this.channelids={};
this.snowflake=new SnowFlake("@me",this);
this.properties={};
this.roles=[];
this.roleids=new Map();
this.prevchannel=undefined;
this.properties.name="Direct Messages";
for(const thing of json){
const temp=new Group(thing,this);
this.channels.push(temp);
this.channelids[temp.id]=temp;
}
this.headchannels=this.channels;
}
createChannelpac(json){
const thischannel=new Group(json,this);
this.channelids[json.id]=thischannel;
this.channels.push(thischannel);
this.calculateReorder();
this.printServers();
}
giveMember(_member:memberjson){
console.error("not a real guild, can't give member object");
}
getRole(ID:string){
return null;
}
hasRole(r:string){
return false;
}
isAdmin(){
return false;
}
unreaddms(){
for(const thing of this.channels){
(thing as Group).unreads();
}
}
}
const dmPermissions = new Permissions("0");
@ -82,139 +82,139 @@ dmPermissions.setPermission("STREAM",1);
dmPermissions.setPermission("USE_VAD",1);
class Group extends Channel{
user:User;
constructor(json:dirrectjson,owner:Direct){
super(-1,owner);
this.owner=owner;
this.headers=this.guild.headers;
this.name=json.recipients[0]?.username;
if(json.recipients[0]){
this.user=new User(json.recipients[0],this.localuser);
}else{
this.user=this.localuser.user;
}
this.name??=this.localuser.user.username;
this.snowflake=new SnowFlake(json.id,this);
this.parent_id=null;
this.parent=null;
this.children=[];
this.guild_id="@me";
this.messageids=new Map();
this.permission_overwrites=new Map();
this.lastmessageid=json.last_message_id;
this.lastmessageid??=null;
this.mentions=0;
this.setUpInfiniteScroller();
if(this.lastmessageid){
this.position=Number((BigInt(this.lastmessageid)>>22n)+1420070400000n);
}
this.position=-Math.max(this.position,this.snowflake.getUnixTime());
}
createguildHTML(){
const div=document.createElement("div")
div.classList.add("channeleffects");
const myhtml=document.createElement("span");
myhtml.textContent=this.name;
div.appendChild(this.user.buildpfp());
div.appendChild(myhtml);
div["myinfo"]=this;
div.onclick=_=>{
this.getHTML();
}
return div;
}
async getHTML(){
const id=++Channel.genid;
if(this.guild!==this.localuser.lookingguild){
this.guild.loadGuild();
}
this.guild.prevchannel=this;
this.localuser.channelfocus=this;
const prom=this.infinite.delete();
await this.putmessages();
await prom;
if(id!==Channel.genid){
return;
}
this.buildmessages();
history.pushState(null, "","/channels/"+this.guild_id+"/"+this.id);
this.localuser.pageTitle("@"+this.name);
(document.getElementById("channelTopic") as HTMLElement).setAttribute("hidden","");
(document.getElementById("typebox") as HTMLDivElement).contentEditable=""+true;
}
messageCreate(messagep){
const messagez=new Message(messagep.d,this);
if(this.lastmessageid){
this.idToNext.set(this.lastmessageid,messagez.id);
}
this.idToPrev.set(messagez.id,this.lastmessageid);
this.lastmessageid=messagez.id;
this.messageids.set(messagez.snowflake,messagez);
if(messagez.author===this.localuser.user){
this.lastreadmessageid=messagez.id;
if(this.myhtml){
this.myhtml.classList.remove("cunread");
}
}else{
if(this.myhtml){
this.myhtml.classList.add("cunread");
}
}
this.unreads();
this.infinite.addedBottom();
if(messagez.author===this.localuser.user){
return;
}
if(this.localuser.lookingguild?.prevchannel===this&&document.hasFocus()){
return;
}
if(this.notification==="all"){
this.notify(messagez);
}else if(this.notification==="mentions"&&messagez.mentionsuser(this.localuser.user)){
this.notify(messagez);
}
}
notititle(message){
return message.author.username;
}
unreads(){
const sentdms=document.getElementById("sentdms") as HTMLDivElement;//Need to change sometime
let current:HTMLElement|null=null;
for(const thing of sentdms.children){
if(thing["all"]===this){
current=thing as HTMLElement;
}
}
if(this.hasunreads){
if(current){current["noti"].textContent=this.mentions;return;}
const div=document.createElement("div");
div.classList.add("servernoti");
const noti=document.createElement("div");
noti.classList.add("unread","notiunread","pinged");
noti.textContent=""+this.mentions;
div["noti"]=noti;
div.append(noti)
const buildpfp=this.user.buildpfp();
div["all"]=this;
buildpfp.classList.add("mentioned");
div.append(buildpfp)
sentdms.append(div);
div.onclick=_=>{
this.guild.loadGuild();
this.getHTML();
}
}else if(current){
user:User;
constructor(json:dirrectjson,owner:Direct){
super(-1,owner);
this.owner=owner;
this.headers=this.guild.headers;
this.name=json.recipients[0]?.username;
if(json.recipients[0]){
this.user=new User(json.recipients[0],this.localuser);
}else{
this.user=this.localuser.user;
}
this.name??=this.localuser.user.username;
this.snowflake=new SnowFlake(json.id,this);
this.parent_id=null;
this.parent=null;
this.children=[];
this.guild_id="@me";
this.messageids=new Map();
this.permission_overwrites=new Map();
this.lastmessageid=json.last_message_id;
this.mentions=0;
this.setUpInfiniteScroller();
if(this.lastmessageid){
this.position=Number((BigInt(this.lastmessageid)>>22n)+1420070400000n);
}
this.position=-Math.max(this.position,this.snowflake.getUnixTime());
}
createguildHTML(){
const div=document.createElement("div");
div.classList.add("channeleffects");
const myhtml=document.createElement("span");
myhtml.textContent=this.name;
div.appendChild(this.user.buildpfp());
div.appendChild(myhtml);
div["myinfo"]=this;
div.onclick=_=>{
this.getHTML();
};
return div;
}
async getHTML(){
const id=++Channel.genid;
if(this.guild!==this.localuser.lookingguild){
this.guild.loadGuild();
}
this.guild.prevchannel=this;
this.localuser.channelfocus=this;
const prom=this.infinite.delete();
await this.putmessages();
await prom;
if(id!==Channel.genid){
return;
}
this.buildmessages();
history.pushState(null, "","/channels/"+this.guild_id+"/"+this.id);
this.localuser.pageTitle("@"+this.name);
(document.getElementById("channelTopic") as HTMLElement).setAttribute("hidden","");
(document.getElementById("typebox") as HTMLDivElement).contentEditable=""+true;
}
messageCreate(messagep){
const messagez=new Message(messagep.d,this);
if(this.lastmessageid){
this.idToNext.set(this.lastmessageid,messagez.id);
this.idToPrev.set(messagez.id,this.lastmessageid);
}
this.lastmessageid=messagez.id;
this.messageids.set(messagez.snowflake,messagez);
if(messagez.author===this.localuser.user){
this.lastreadmessageid=messagez.id;
if(this.myhtml){
this.myhtml.classList.remove("cunread");
}
}else{
if(this.myhtml){
this.myhtml.classList.add("cunread");
}
}
this.unreads();
this.infinite.addedBottom();
if(messagez.author===this.localuser.user){
return;
}
if(this.localuser.lookingguild?.prevchannel===this&&document.hasFocus()){
return;
}
if(this.notification==="all"){
this.notify(messagez);
}else if(this.notification==="mentions"&&messagez.mentionsuser(this.localuser.user)){
this.notify(messagez);
}
}
notititle(message){
return message.author.username;
}
unreads(){
const sentdms=document.getElementById("sentdms") as HTMLDivElement;//Need to change sometime
let current:HTMLElement|null=null;
for(const thing of sentdms.children){
if(thing["all"]===this){
current=thing as HTMLElement;
}
}
if(this.hasunreads){
if(current){
current["noti"].textContent=this.mentions;return;
}
const div=document.createElement("div");
div.classList.add("servernoti");
const noti=document.createElement("div");
noti.classList.add("unread","notiunread","pinged");
noti.textContent=""+this.mentions;
div["noti"]=noti;
div.append(noti);
const buildpfp=this.user.buildpfp();
div["all"]=this;
buildpfp.classList.add("mentioned");
div.append(buildpfp);
sentdms.append(div);
div.onclick=_=>{
this.guild.loadGuild();
this.getHTML();
};
}else if(current){
current.remove();
}else{
current.remove();
}else{
}
}
isAdmin(): boolean {
return false;
}
hasPermission(name: string): boolean {
return dmPermissions.hasPermission(name);
}
}
}
isAdmin(): boolean{
return false;
}
hasPermission(name: string): boolean{
return dmPermissions.hasPermission(name);
}
}
export {Direct, Group};
export{Direct, Group};

View file

@ -1,230 +1,232 @@
import {Dialog} from "./dialog.js";
import {Message} from "./message.js";
import {MarkDown} from "./markdown.js";
import { embedjson } from "./jsontypes.js";
import{Dialog}from"./dialog.js";
import{Message}from"./message.js";
import{MarkDown}from"./markdown.js";
import{ embedjson }from"./jsontypes.js";
class Embed{
type:string;
owner:Message;
json:embedjson;
constructor(json:embedjson, owner:Message){
this.type=this.getType(json);
this.owner=owner;
this.json=json;
}
getType(json:embedjson){
return json.type||"rich";
}
generateHTML(){
switch(this.type){
case "rich":
return this.generateRich();
case "image":
return this.generateImage();
case "link":
return this.generateLink();
case "article":
return this.generateArticle();
default:
console.warn(`unsupported embed type ${this.type}, please add support dev :3`,this.json);
return document.createElement("div");//prevent errors by giving blank div
}
}
get message(){
return this.owner;
}
get channel(){
return this.message.channel;
}
get guild(){
return this.channel.guild;
}
get localuser(){
return this.guild.localuser;
}
generateRich(){
const div=document.createElement("div");
if(this.json.color){
div.style.backgroundColor="#"+this.json.color.toString(16);
}
div.classList.add("embed-color");
type:string;
owner:Message;
json:embedjson;
constructor(json:embedjson, owner:Message){
this.type=this.getType(json);
this.owner=owner;
this.json=json;
}
getType(json:embedjson){
return json.type||"rich";
}
generateHTML(){
switch(this.type){
case"rich":
return this.generateRich();
case"image":
return this.generateImage();
case"link":
return this.generateLink();
case"article":
return this.generateArticle();
default:
console.warn(`unsupported embed type ${this.type}, please add support dev :3`,this.json);
return document.createElement("div");//prevent errors by giving blank div
}
}
get message(){
return this.owner;
}
get channel(){
return this.message.channel;
}
get guild(){
return this.channel.guild;
}
get localuser(){
return this.guild.localuser;
}
generateRich(){
const div=document.createElement("div");
if(this.json.color){
div.style.backgroundColor="#"+this.json.color.toString(16);
}
div.classList.add("embed-color");
const embed=document.createElement("div");
embed.classList.add("embed");
div.append(embed);
const embed=document.createElement("div");
embed.classList.add("embed");
div.append(embed);
if(this.json.author){
const authorline=document.createElement("div");
if(this.json.author.icon_url){
const img=document.createElement("img");
img.classList.add("embedimg");
img.src=this.json.author.icon_url;
authorline.append(img);
}
const a=document.createElement("a");
a.textContent=this.json.author.name as string;
if(this.json.author.url){
a.href=this.json.author.url
}
a.classList.add("username")
authorline.append(a);
embed.append(authorline);
}
if(this.json.title){
const title=document.createElement("a");
title.append(new MarkDown(this.json.title,this.channel).makeHTML());
if(this.json.url){
title.href=this.json.url;
}
title.classList.add("embedtitle");
embed.append(title);
}
if(this.json.description){
const p=document.createElement("p");
p.append(new MarkDown(this.json.description,this.channel).makeHTML());
embed.append(p);
}
if(this.json.author){
const authorline=document.createElement("div");
if(this.json.author.icon_url){
const img=document.createElement("img");
img.classList.add("embedimg");
img.src=this.json.author.icon_url;
authorline.append(img);
}
const a=document.createElement("a");
a.textContent=this.json.author.name as string;
if(this.json.author.url){
a.href=this.json.author.url;
}
a.classList.add("username");
authorline.append(a);
embed.append(authorline);
}
if(this.json.title){
const title=document.createElement("a");
title.append(new MarkDown(this.json.title,this.channel).makeHTML());
if(this.json.url){
title.href=this.json.url;
}
title.classList.add("embedtitle");
embed.append(title);
}
if(this.json.description){
const p=document.createElement("p");
p.append(new MarkDown(this.json.description,this.channel).makeHTML());
embed.append(p);
}
embed.append(document.createElement("br"));
if(this.json.fields){
for(const thing of this.json.fields){
const div=document.createElement("div");
const b=document.createElement("b");
b.textContent=thing.name;
div.append(b);
const p=document.createElement("p")
p.append(new MarkDown(thing.value,this.channel).makeHTML());
p.classList.add("embedp");
div.append(p);
embed.append(document.createElement("br"));
if(this.json.fields){
for(const thing of this.json.fields){
const div=document.createElement("div");
const b=document.createElement("b");
b.textContent=thing.name;
div.append(b);
const p=document.createElement("p");
p.append(new MarkDown(thing.value,this.channel).makeHTML());
p.classList.add("embedp");
div.append(p);
if(thing.inline){div.classList.add("inline");}
embed.append(div);
}
}
if(this.json.footer||this.json.timestamp){
const footer=document.createElement("div");
if(this.json?.footer?.icon_url){
const img=document.createElement("img");
img.src=this.json.footer.icon_url;
img.classList.add("embedicon");
footer.append(img);
}
if(this.json?.footer?.text){
const span=document.createElement("span");
span.textContent=this.json.footer.text;
span.classList.add("spaceright");
footer.append(span);
}
if(this.json?.footer&&this.json?.timestamp){
const span=document.createElement("span");
span.textContent="•";
span.classList.add("spaceright");
footer.append(span);
}
if(this.json?.timestamp){
const span=document.createElement("span")
span.textContent=new Date(this.json.timestamp).toLocaleString();;
footer.append(span);
}
embed.append(footer);
}
return div;
}
generateImage(){
const img=document.createElement("img");
img.classList.add("messageimg")
img.onclick=function(){
const full=new Dialog(["img",img.src,["fit"]]);
full.show();
}
img.src=this.json.thumbnail.proxy_url;
if(this.json.thumbnail.width){
let scale=1;
const max=96*3;
scale=Math.max(scale,this.json.thumbnail.width/max);
scale=Math.max(scale,this.json.thumbnail.height/max);
this.json.thumbnail.width/=scale;
this.json.thumbnail.height/=scale;
}
img.style.width=this.json.thumbnail.width+"px";
img.style.height=this.json.thumbnail.height+"px";
console.log(this.json,"Image fix");
return img;
}
generateLink(){
const table=document.createElement("table");
table.classList.add("embed","linkembed");
const trtop=document.createElement("tr");
table.append(trtop);
if(this.json.url&&this.json.title){
const td=document.createElement("td");
const a=document.createElement("a");
a.href=this.json.url;
a.textContent=this.json.title;
td.append(a);
trtop.append(td);
}
{
const td=document.createElement("td");
const img=document.createElement("img");
if(this.json.thumbnail){
img.classList.add("embedimg");
img.onclick=function(){
const full=new Dialog(["img",img.src,["fit"]]);
full.show();
}
img.src=this.json.thumbnail.proxy_url;
td.append(img);
}
trtop.append(td);
}
const bottomtr=document.createElement("tr");
const td=document.createElement("td");
if(this.json.description){
const span=document.createElement("span");
span.textContent=this.json.description;
td.append(span);
}
bottomtr.append(td);
table.append(bottomtr)
return table;
}
generateArticle(){
const colordiv=document.createElement("div");
colordiv.style.backgroundColor="#000000";
colordiv.classList.add("embed-color");
if(thing.inline){
div.classList.add("inline");
}
embed.append(div);
}
}
if(this.json.footer||this.json.timestamp){
const footer=document.createElement("div");
if(this.json?.footer?.icon_url){
const img=document.createElement("img");
img.src=this.json.footer.icon_url;
img.classList.add("embedicon");
footer.append(img);
}
if(this.json?.footer?.text){
const span=document.createElement("span");
span.textContent=this.json.footer.text;
span.classList.add("spaceright");
footer.append(span);
}
if(this.json?.footer&&this.json?.timestamp){
const span=document.createElement("span");
span.textContent="•";
span.classList.add("spaceright");
footer.append(span);
}
if(this.json?.timestamp){
const span=document.createElement("span");
span.textContent=new Date(this.json.timestamp).toLocaleString();
footer.append(span);
}
embed.append(footer);
}
return div;
}
generateImage(){
const img=document.createElement("img");
img.classList.add("messageimg");
img.onclick=function(){
const full=new Dialog(["img",img.src,["fit"]]);
full.show();
};
img.src=this.json.thumbnail.proxy_url;
if(this.json.thumbnail.width){
let scale=1;
const max=96*3;
scale=Math.max(scale,this.json.thumbnail.width/max);
scale=Math.max(scale,this.json.thumbnail.height/max);
this.json.thumbnail.width/=scale;
this.json.thumbnail.height/=scale;
}
img.style.width=this.json.thumbnail.width+"px";
img.style.height=this.json.thumbnail.height+"px";
console.log(this.json,"Image fix");
return img;
}
generateLink(){
const table=document.createElement("table");
table.classList.add("embed","linkembed");
const trtop=document.createElement("tr");
table.append(trtop);
if(this.json.url&&this.json.title){
const td=document.createElement("td");
const a=document.createElement("a");
a.href=this.json.url;
a.textContent=this.json.title;
td.append(a);
trtop.append(td);
}
{
const td=document.createElement("td");
const img=document.createElement("img");
if(this.json.thumbnail){
img.classList.add("embedimg");
img.onclick=function(){
const full=new Dialog(["img",img.src,["fit"]]);
full.show();
};
img.src=this.json.thumbnail.proxy_url;
td.append(img);
}
trtop.append(td);
}
const bottomtr=document.createElement("tr");
const td=document.createElement("td");
if(this.json.description){
const span=document.createElement("span");
span.textContent=this.json.description;
td.append(span);
}
bottomtr.append(td);
table.append(bottomtr);
return table;
}
generateArticle(){
const colordiv=document.createElement("div");
colordiv.style.backgroundColor="#000000";
colordiv.classList.add("embed-color");
const div=document.createElement("div");
div.classList.add("embed");
if(this.json.provider){
const provider=document.createElement("p");
provider.classList.add("provider");
provider.textContent=this.json.provider.name;
div.append(provider);
}
const a=document.createElement("a");
if(this.json.url&&this.json.url){
a.href=this.json.url;
a.textContent=this.json.url;
div.append(a);
}
if(this.json.description){
const description=document.createElement("p");
description.textContent=this.json.description;
div.append(description);
}
if(this.json.thumbnail){
const img=document.createElement("img");
img.classList.add("bigembedimg");
img.onclick=function(){
const full=new Dialog(["img",img.src,["fit"]]);
full.show();
}
img.src=this.json.thumbnail.proxy_url||this.json.thumbnail.url;
div.append(img);
}
colordiv.append(div);
return colordiv;
}
const div=document.createElement("div");
div.classList.add("embed");
if(this.json.provider){
const provider=document.createElement("p");
provider.classList.add("provider");
provider.textContent=this.json.provider.name;
div.append(provider);
}
const a=document.createElement("a");
if(this.json.url&&this.json.url){
a.href=this.json.url;
a.textContent=this.json.url;
div.append(a);
}
if(this.json.description){
const description=document.createElement("p");
description.textContent=this.json.description;
div.append(description);
}
if(this.json.thumbnail){
const img=document.createElement("img");
img.classList.add("bigembedimg");
img.onclick=function(){
const full=new Dialog(["img",img.src,["fit"]]);
full.show();
};
img.src=this.json.thumbnail.proxy_url||this.json.thumbnail.url;
div.append(img);
}
colordiv.append(div);
return colordiv;
}
}
export {Embed};
export{Embed};

View file

@ -1,226 +1,226 @@
import { Contextmenu } from "./contextmenu.js";
import { Guild } from "./guild.js";
import { emojijson } from "./jsontypes.js";
import { Localuser } from "./localuser.js";
import{ Contextmenu }from"./contextmenu.js";
import{ Guild }from"./guild.js";
import{ emojijson }from"./jsontypes.js";
import{ Localuser }from"./localuser.js";
class Emoji{
static emojis:{
static emojis:{
name:string,
emojis:{
name:string,
emoji:string,
}[]
}[];
name:string;
id:string;
animated:boolean;
owner:Guild|Localuser;
get guild(){
if(this.owner instanceof Guild){
return this.owner;
}
}
get localuser(){
if(this.owner instanceof Guild){
return this.owner.localuser;
}else{
return this.owner;
}
}
get info(){
return this.owner.info;
}
constructor(json:{name:string,id:string,animated:boolean},owner:Guild|Localuser){
this.name=json.name;
this.id=json.id;
this.animated=json.animated
this.owner=owner;
}
getHTML(bigemoji:boolean=false){
const emojiElem=document.createElement("img");
emojiElem.classList.add("md-emoji");
emojiElem.classList.add(bigemoji ? "bigemoji" : "smallemoji");
emojiElem.crossOrigin="anonymous";
emojiElem.src=this.info.cdn + "/emojis/" + this.id + "." + (this.animated ? "gif" : "png") + "?size=32";
name:string;
id:string;
animated:boolean;
owner:Guild|Localuser;
get guild(){
if(this.owner instanceof Guild){
return this.owner;
}
}
get localuser(){
if(this.owner instanceof Guild){
return this.owner.localuser;
}else{
return this.owner;
}
}
get info(){
return this.owner.info;
}
constructor(json:{name:string,id:string,animated:boolean},owner:Guild|Localuser){
this.name=json.name;
this.id=json.id;
this.animated=json.animated;
this.owner=owner;
}
getHTML(bigemoji:boolean=false){
const emojiElem=document.createElement("img");
emojiElem.classList.add("md-emoji");
emojiElem.classList.add(bigemoji ? "bigemoji" : "smallemoji");
emojiElem.crossOrigin="anonymous";
emojiElem.src=this.info.cdn + "/emojis/" + this.id + "." + (this.animated ? "gif" : "png") + "?size=32";
emojiElem.alt=this.name;
emojiElem.loading="lazy";
return emojiElem;
}
static decodeEmojiList(buffer:ArrayBuffer){
const view = new DataView(buffer, 0);
let i=0;
function read16(){
const int=view.getUint16(i);
i+=2;
return int;
}
function read8(){
const int=view.getUint8(i);
i+=1;
return int;
}
function readString8(){
return readStringNo(read8());
}
function readString16(){
return readStringNo(read16());
}
function readStringNo(length:number){
const array=new Uint8Array(length);
emojiElem.alt=this.name;
emojiElem.loading="lazy";
return emojiElem;
}
static decodeEmojiList(buffer:ArrayBuffer){
const view = new DataView(buffer, 0);
let i=0;
function read16(){
const int=view.getUint16(i);
i+=2;
return int;
}
function read8(){
const int=view.getUint8(i);
i+=1;
return int;
}
function readString8(){
return readStringNo(read8());
}
function readString16(){
return readStringNo(read16());
}
function readStringNo(length:number){
const array=new Uint8Array(length);
for(let i=0;i<length;i++){
array[i]=read8();
}
const decoded=new TextDecoder("utf-8").decode(array.buffer);;
for(let i=0;i<length;i++){
array[i]=read8();
}
//console.log(array);
return new TextDecoder("utf-8").decode(array.buffer);
}
const build:{name:string,emojis:{name:string,emoji:string}[]}[]=[];
let cats=read16();
//console.log(array);
return decoded;
}
const build:{name:string,emojis:{name:string,emoji:string}[]}[]=[];
let cats=read16();
for(;cats!==0;cats--){
const name=readString16();
const emojis:{
for(;cats!==0;cats--){
const name=readString16();
const emojis:{
name:string,
skin_tone_support:boolean,
emoji:string
}[]=[];
let emojinumber=read16();
for(;emojinumber!==0;emojinumber--){
//console.log(emojis);
const name=readString8();
const len=read8();
const skin_tone_support=len>127;
const emoji=readStringNo(len-(+skin_tone_support*128));
emojis.push({
name,
skin_tone_support,
emoji
})
}
build.push({
name,
emojis
})
}
this.emojis=build;
console.log(build);
}
static grabEmoji(){
fetch("/emoji.bin").then(e=>{
return e.arrayBuffer()
}).then(e=>{
Emoji.decodeEmojiList(e);
})
}
static async emojiPicker(x:number,y:number, localuser:Localuser):Promise<Emoji|string>{
let res:(r:Emoji|string)=>void;
const promise:Promise<Emoji|string>=new Promise((r)=>{res=r;})
const menu=document.createElement("div");
menu.classList.add("flextttb", "emojiPicker")
menu.style.top=y+"px";
menu.style.left=x+"px";
let emojinumber=read16();
for(;emojinumber!==0;emojinumber--){
//console.log(emojis);
const name=readString8();
const len=read8();
const skin_tone_support=len>127;
const emoji=readStringNo(len-(Number(skin_tone_support)*128));
emojis.push({
name,
skin_tone_support,
emoji
});
}
build.push({
name,
emojis
});
}
this.emojis=build;
console.log(build);
}
static grabEmoji(){
fetch("/emoji.bin").then(e=>{
return e.arrayBuffer();
}).then(e=>{
Emoji.decodeEmojiList(e);
});
}
static async emojiPicker(x:number,y:number, localuser:Localuser):Promise<Emoji|string>{
let res:(r:Emoji|string)=>void;
const promise:Promise<Emoji|string>=new Promise(r=>{
res=r;
});
const menu=document.createElement("div");
menu.classList.add("flextttb", "emojiPicker");
menu.style.top=y+"px";
menu.style.left=x+"px";
const title=document.createElement("h2");
title.textContent=Emoji.emojis[0].name;
title.classList.add("emojiTitle");
menu.append(title);
const selection=document.createElement("div");
selection.classList.add("flexltr","dontshrink","emojirow");
const body=document.createElement("div");
body.classList.add("emojiBody");
const title=document.createElement("h2");
title.textContent=Emoji.emojis[0].name;
title.classList.add("emojiTitle");
menu.append(title);
const selection=document.createElement("div");
selection.classList.add("flexltr","dontshrink","emojirow");
const body=document.createElement("div");
body.classList.add("emojiBody");
let isFirst = true;
localuser.guilds.filter(guild => guild.id != "@me" && guild.emojis.length > 0).forEach(guild => {
const select = document.createElement("div")
select.classList.add("emojiSelect")
let isFirst = true;
localuser.guilds.filter(guild=>guild.id != "@me" && guild.emojis.length > 0).forEach(guild=>{
const select = document.createElement("div");
select.classList.add("emojiSelect");
if (guild.properties.icon) {
const img = document.createElement("img")
img.classList.add("pfp", "servericon", "emoji-server")
img.crossOrigin = "anonymous"
img.src = localuser.info.cdn + "/icons/" + guild.properties.id + "/" + guild.properties.icon + ".png?size=48"
img.alt = "Server: " + guild.properties.name
select.appendChild(img)
} else {
const div = document.createElement("span")
div.textContent = guild.properties.name.replace(/'s /g, " ").replace(/\w+/g, word => word[0]).replace(/\s/g, "")
select.append(div)
}
if(guild.properties.icon){
const img = document.createElement("img");
img.classList.add("pfp", "servericon", "emoji-server");
img.crossOrigin = "anonymous";
img.src = localuser.info.cdn + "/icons/" + guild.properties.id + "/" + guild.properties.icon + ".png?size=48";
img.alt = "Server: " + guild.properties.name;
select.appendChild(img);
}else{
const div = document.createElement("span");
div.textContent = guild.properties.name.replace(/'s /g, " ").replace(/\w+/g, word=>word[0]).replace(/\s/g, "");
select.append(div);
}
selection.append(select)
selection.append(select);
const clickEvent = () => {
title.textContent = guild.properties.name
body.innerHTML = ""
for (const emojit of guild.emojis) {
const emojiElem = document.createElement("div")
emojiElem.classList.add("emojiSelect")
const clickEvent = ()=>{
title.textContent = guild.properties.name;
body.innerHTML = "";
for(const emojit of guild.emojis){
const emojiElem = document.createElement("div");
emojiElem.classList.add("emojiSelect");
const emojiClass = new Emoji({
id: emojit.id as string,
name: emojit.name,
animated: emojit.animated as boolean
},localuser)
emojiElem.append(emojiClass.getHTML())
body.append(emojiElem)
const emojiClass = new Emoji({
id: emojit.id as string,
name: emojit.name,
animated: emojit.animated as boolean
},localuser);
emojiElem.append(emojiClass.getHTML());
body.append(emojiElem);
emojiElem.addEventListener("click", () => {
res(emojiClass)
Contextmenu.currentmenu.remove()
})
}
}
emojiElem.addEventListener("click", ()=>{
res(emojiClass);
Contextmenu.currentmenu.remove();
});
}
};
select.addEventListener("click", clickEvent)
if (isFirst) {
clickEvent()
isFirst = false
}
})
select.addEventListener("click", clickEvent);
if(isFirst){
clickEvent();
isFirst = false;
}
});
setTimeout(()=>{
if(Contextmenu.currentmenu!=""){
Contextmenu.currentmenu.remove();
}
document.body.append(menu);
Contextmenu.currentmenu=menu;
Contextmenu.keepOnScreen(menu);
},10)
setTimeout(()=>{
if(Contextmenu.currentmenu!=""){
Contextmenu.currentmenu.remove();
}
document.body.append(menu);
Contextmenu.currentmenu=menu;
Contextmenu.keepOnScreen(menu);
},10);
let i=0;
for(const thing of Emoji.emojis){
const select=document.createElement("div");
select.textContent=thing.emojis[0].emoji;
select.classList.add("emojiSelect");
selection.append(select);
const clickEvent=()=>{
title.textContent=thing.name;
body.innerHTML="";
for(const emojit of thing.emojis){
const emoji=document.createElement("div");
emoji.classList.add("emojiSelect");
emoji.textContent=emojit.emoji;
body.append(emoji);
emoji.onclick=_=>{
res(emojit.emoji);
Contextmenu.currentmenu.remove();
}
}
};
select.onclick=clickEvent
if(i===0){
clickEvent();
}
i++;
}
menu.append(selection);
menu.append(body);
return promise;
}
let i=0;
for(const thing of Emoji.emojis){
const select=document.createElement("div");
select.textContent=thing.emojis[0].emoji;
select.classList.add("emojiSelect");
selection.append(select);
const clickEvent=()=>{
title.textContent=thing.name;
body.innerHTML="";
for(const emojit of thing.emojis){
const emoji=document.createElement("div");
emoji.classList.add("emojiSelect");
emoji.textContent=emojit.emoji;
body.append(emoji);
emoji.onclick=_=>{
res(emojit.emoji);
Contextmenu.currentmenu.remove();
};
}
};
select.onclick=clickEvent;
if(i===0){
clickEvent();
}
i++;
}
menu.append(selection);
menu.append(body);
return promise;
}
}
Emoji.grabEmoji();
export {Emoji};
export{Emoji};

View file

@ -1,145 +1,145 @@
import { Message } from "./message.js";
import { Dialog } from "./dialog.js";
import { filejson } from "./jsontypes.js";
import{ Message }from"./message.js";
import{ Dialog }from"./dialog.js";
import{ filejson }from"./jsontypes.js";
class File{
owner:Message|null;
id:string;
filename:string;
content_type:string;
width:number|undefined;
height:number|undefined;
proxy_url:string|undefined;
url:string;
size:number;
constructor(fileJSON:filejson,owner:Message|null){
this.owner=owner;
this.id=fileJSON.id;
this.filename=fileJSON.filename;
this.content_type=fileJSON.content_type;
this.width=fileJSON.width;
this.height=fileJSON.height;
this.url=fileJSON.url;
this.proxy_url=fileJSON.proxy_url;
this.content_type=fileJSON.content_type;
this.size=fileJSON.size;
}
getHTML(temp:boolean=false):HTMLElement{
const src=this.proxy_url||this.url;
if(this.width&&this.height){
let scale=1;
const max=96*3;
scale=Math.max(scale,this.width/max);
scale=Math.max(scale,this.height/max);
this.width/=scale;
this.height/=scale;
}
if(this.content_type.startsWith('image/')){
const div=document.createElement("div")
const img=document.createElement("img");
img.classList.add("messageimg");
div.classList.add("messageimgdiv")
img.onclick=function(){
const full=new Dialog(["img",img.src,["fit"]]);
full.show();
}
img.src=src;
div.append(img)
if(this.width){
div.style.width=this.width+"px";
div.style.height=this.height+"px";
}
console.log(img);
console.log(this.width,this.height)
return div;
}else if(this.content_type.startsWith('video/')){
const video=document.createElement("video");
const source=document.createElement("source");
source.src=src;
video.append(source);
source.type=this.content_type;
video.controls=!temp;
if(this.width&&this.height){
video.width=this.width;
video.height=this.height;
}
return video;
}else if(this.content_type.startsWith('audio/')){
const audio=document.createElement("audio");
const source=document.createElement("source");
source.src=src;
audio.append(source);
source.type=this.content_type;
audio.controls=!temp;
return audio;
}else{
return this.createunknown();
}
}
upHTML(files:Blob[],file:globalThis.File):HTMLElement{
const div=document.createElement("div");
const contained=this.getHTML(true);
div.classList.add("containedFile");
div.append(contained);
const controls=document.createElement("div");
const garbage=document.createElement("button");
garbage.textContent="🗑";
garbage.onclick=_=>{
div.remove();
files.splice(files.indexOf(file),1);
}
controls.classList.add("controls");
div.append(controls);
controls.append(garbage);
return div;
}
static initFromBlob(file:globalThis.File){
return new File({
filename:file.name,
size:file.size,
id:"null",
content_type:file.type,
width:undefined,
height:undefined,
url:URL.createObjectURL(file),
proxy_url:undefined
},null)
}
createunknown():HTMLElement{
console.log("🗎")
const src=this.proxy_url||this.url;
const div=document.createElement("table");
div.classList.add("unknownfile");
const nametr=document.createElement("tr");
div.append(nametr);
const fileicon=document.createElement("td");
nametr.append(fileicon);
fileicon.append("🗎");
fileicon.classList.add("fileicon");
fileicon.rowSpan=2;
const nametd=document.createElement("td");
if(src){
const a=document.createElement("a");
a.href=src;
a.textContent=this.filename;
nametd.append(a);
}else{
nametd.textContent=this.filename;
}
owner:Message|null;
id:string;
filename:string;
content_type:string;
width:number|undefined;
height:number|undefined;
proxy_url:string|undefined;
url:string;
size:number;
constructor(fileJSON:filejson,owner:Message|null){
this.owner=owner;
this.id=fileJSON.id;
this.filename=fileJSON.filename;
this.content_type=fileJSON.content_type;
this.width=fileJSON.width;
this.height=fileJSON.height;
this.url=fileJSON.url;
this.proxy_url=fileJSON.proxy_url;
this.content_type=fileJSON.content_type;
this.size=fileJSON.size;
}
getHTML(temp:boolean=false):HTMLElement{
const src=this.proxy_url||this.url;
if(this.width&&this.height){
let scale=1;
const max=96*3;
scale=Math.max(scale,this.width/max);
scale=Math.max(scale,this.height/max);
this.width/=scale;
this.height/=scale;
}
if(this.content_type.startsWith("image/")){
const div=document.createElement("div");
const img=document.createElement("img");
img.classList.add("messageimg");
div.classList.add("messageimgdiv");
img.onclick=function(){
const full=new Dialog(["img",img.src,["fit"]]);
full.show();
};
img.src=src;
div.append(img);
if(this.width){
div.style.width=this.width+"px";
div.style.height=this.height+"px";
}
console.log(img);
console.log(this.width,this.height);
return div;
}else if(this.content_type.startsWith("video/")){
const video=document.createElement("video");
const source=document.createElement("source");
source.src=src;
video.append(source);
source.type=this.content_type;
video.controls=!temp;
if(this.width&&this.height){
video.width=this.width;
video.height=this.height;
}
return video;
}else if(this.content_type.startsWith("audio/")){
const audio=document.createElement("audio");
const source=document.createElement("source");
source.src=src;
audio.append(source);
source.type=this.content_type;
audio.controls=!temp;
return audio;
}else{
return this.createunknown();
}
}
upHTML(files:Blob[],file:globalThis.File):HTMLElement{
const div=document.createElement("div");
const contained=this.getHTML(true);
div.classList.add("containedFile");
div.append(contained);
const controls=document.createElement("div");
const garbage=document.createElement("button");
garbage.textContent="🗑";
garbage.onclick=_=>{
div.remove();
files.splice(files.indexOf(file),1);
};
controls.classList.add("controls");
div.append(controls);
controls.append(garbage);
return div;
}
static initFromBlob(file:globalThis.File){
return new File({
filename: file.name,
size: file.size,
id: "null",
content_type: file.type,
width: undefined,
height: undefined,
url: URL.createObjectURL(file),
proxy_url: undefined
},null);
}
createunknown():HTMLElement{
console.log("🗎");
const src=this.proxy_url||this.url;
const div=document.createElement("table");
div.classList.add("unknownfile");
const nametr=document.createElement("tr");
div.append(nametr);
const fileicon=document.createElement("td");
nametr.append(fileicon);
fileicon.append("🗎");
fileicon.classList.add("fileicon");
fileicon.rowSpan=2;
const nametd=document.createElement("td");
if(src){
const a=document.createElement("a");
a.href=src;
a.textContent=this.filename;
nametd.append(a);
}else{
nametd.textContent=this.filename;
}
nametd.classList.add("filename");
nametr.append(nametd);
const sizetr=document.createElement("tr");
const size=document.createElement("td");
sizetr.append(size);
size.textContent="Size:"+File.filesizehuman(this.size);
size.classList.add("filesize");
div.appendChild(sizetr);
return div;
}
static filesizehuman(fsize:number){
var i = fsize == 0 ? 0 : Math.floor(Math.log(fsize) / Math.log(1024));
return +((fsize / Math.pow(1024, i)).toFixed(2)) * 1 + ' ' + ['Bytes', 'Kilobytes', 'Megabytes', 'Gigabytes', 'Terabytes'][i];
}
nametd.classList.add("filename");
nametr.append(nametd);
const sizetr=document.createElement("tr");
const size=document.createElement("td");
sizetr.append(size);
size.textContent="Size:"+File.filesizehuman(this.size);
size.classList.add("filesize");
div.appendChild(sizetr);
return div;
}
static filesizehuman(fsize:number){
const i = fsize == 0 ? 0 : Math.floor(Math.log(fsize) / Math.log(1024));
return Number((fsize / Math.pow(1024, i)).toFixed(2)) * 1 + " " + ["Bytes", "Kilobytes", "Megabytes", "Gigabytes", "Terabytes"][i];
}
}
export{File}
export{File};

File diff suppressed because it is too large Load diff

View file

@ -1,48 +1,48 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Jank Client</title>
<meta content="Jank Client" property="og:title" />
<meta content="A spacebar client that has DMs, replying and more" property="og:description" />
<meta content="/logo.webp" property="og:image" />
<meta content="#4b458c" data-react-helmet="true" name="theme-color" />
<link href="/style.css" rel="stylesheet" type="text/css" />
<link href="/themes.css" rel="stylesheet" type="text/css" id="lightcss"/>
</head>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Jank Client</title>
<meta content="Jank Client" property="og:title">
<meta content="A spacebar client that has DMs, replying and more" property="og:description">
<meta content="/logo.webp" property="og:image">
<meta content="#4b458c" data-react-helmet="true" name="theme-color">
<link href="/style.css" rel="stylesheet">
<link href="/themes.css" rel="stylesheet" id="lightcss">
</head>
<body class="Dark-theme">
<div id="titleDiv">
<img src="/logo.svg" width="40">
<h1 id="pageTitle">Jank Client</h1>
<a href="https://sb-jankclient.vanillaminigames.net/invite/USgYJo?instance=https%3A%2F%2Fspacebar.chat" class="TitleButtons"><h1>Spacebar Guild</h1></a>
<a href="https://github.com/MathMan05/JankClient" class="TitleButtons"><h1>Github</h1></a>
</div>
<div class="flexttb">
<body class="Dark-theme">
<div id="titleDiv">
<img src="/logo.svg" width="40">
<h1 id="pageTitle">Jank Client</h1>
<a href="https://sb-jankclient.vanillaminigames.net/invite/USgYJo?instance=https%3A%2F%2Fspacebar.chat" class="TitleButtons"><h1>Spacebar Guild</h1></a>
<a href="https://github.com/MathMan05/JankClient" class="TitleButtons"><h1>Github</h1></a>
</div>
<div class="flexttb">
<div class="flexttb pagehead"><h1>Welcome to Jank Client</h1></div>
<div class="pagebox">
<p>Jank Client is a spacebar compatible client seeking to be as good as it can be with many features including:</p>
<ul>
<li>Direct Messaging</li>
<li>Reactions support</li>
<li>Invites</li>
<li>Account switching</li>
<li>User settings</li>
</ul>
</div>
<div class="pagebox">
<h2>Spacebar compatible Instances:</h2>
<div id="instancebox">
</div>
</div>
<div class="pagebox">
<h2>Contribute to Jank Client</h2>
<p>We always appreciate some help, wether that be in the form of bug reports, or code, or even just pointing out some typos.</p><br>
<div class="flexttb pagehead"><h1>Welcome to Jank Client</h1></div>
<div class="pagebox">
<p>Jank Client is a spacebar compatible client seeking to be as good as it can be with many features including:</p>
<ul>
<li>Direct Messaging</li>
<li>Reactions support</li>
<li>Invites</li>
<li>Account switching</li>
<li>User settings</li>
</ul>
</div>
<div class="pagebox">
<h2>Spacebar compatible Instances:</h2>
<div id="instancebox">
</div>
</div>
<div class="pagebox">
<h2>Contribute to Jank Client</h2>
<p>We always appreciate some help, wether that be in the form of bug reports, or code, or even just pointing out some typos.</p><br>
</a><a href="https://github.com/MathMan05/JankClient" class="TitleButtons"><h1>Github</h1></a>
</div>
</div>
</body>
<script src="/home.js" type="module" ></script>
</div>
</div>
</body>
<script src="/home.js" type="module"></script>
</html>

View file

@ -1,64 +1,64 @@
import {mobile} from "./login.js";
import{mobile}from"./login.js";
console.log(mobile);
const serverbox=document.getElementById("instancebox") as HTMLDivElement;
fetch("/instances.json").then(_=>_.json()).then((json:{name:string,description?:string,descriptionLong?:string,image?:string,url?:string,display?:boolean,online?:boolean,
uptime:{alltime:number,daytime:number,weektime:number},
urls:{wellknown:string,api:string,cdn:string,gateway:string,login?:string}}[])=>{
console.warn(json);
for(const instance of json){
if(instance.display===false){
continue;
}
const div=document.createElement("div");
div.classList.add("flexltr","instance");
if(instance.image){
const img=document.createElement("img");
img.src=instance.image;
div.append(img);
}
const statbox=document.createElement("div");
statbox.classList.add("flexttb");
console.warn(json);
for(const instance of json){
if(instance.display===false){
continue;
}
const div=document.createElement("div");
div.classList.add("flexltr","instance");
if(instance.image){
const img=document.createElement("img");
img.src=instance.image;
div.append(img);
}
const statbox=document.createElement("div");
statbox.classList.add("flexttb");
{
const textbox=document.createElement("div");
textbox.classList.add("flexttb","instatancetextbox");
const title=document.createElement("h2");
title.innerText=instance.name;
if(instance.online!==undefined){
const status=document.createElement("span");
status.innerText=instance.online?"Online":"Offline";
status.classList.add("instanceStatus");
title.append(status);
}
textbox.append(title);
if(instance.description||instance.descriptionLong){
const p=document.createElement("p");
if(instance.descriptionLong){
p.innerText=instance.descriptionLong;
}else{
p.innerText=instance.description;
}
textbox.append(p);
}
statbox.append(textbox)
}
if(instance.uptime){
const stats=document.createElement("div");
stats.classList.add("flexltr");
const span=document.createElement("span");
span.innerText=`Uptime: All time: ${Math.floor(instance.uptime.alltime*100)}% This week: ${Math.floor(instance.uptime.weektime*100)}% Today: ${Math.floor(instance.uptime.daytime*100)}%`
stats.append(span);
statbox.append(stats);
}
div.append(statbox);
div.onclick=_=>{
if(instance.online){
window.location.href="/register.html?instance="+encodeURI(instance.name);
}else{
alert("Instance is offline, can't connect");
}
}
serverbox.append(div);
}
})
{
const textbox=document.createElement("div");
textbox.classList.add("flexttb","instatancetextbox");
const title=document.createElement("h2");
title.innerText=instance.name;
if(instance.online!==undefined){
const status=document.createElement("span");
status.innerText=instance.online?"Online":"Offline";
status.classList.add("instanceStatus");
title.append(status);
}
textbox.append(title);
if(instance.description||instance.descriptionLong){
const p=document.createElement("p");
if(instance.descriptionLong){
p.innerText=instance.descriptionLong;
}else if(instance.description){
p.innerText=instance.description;
}
textbox.append(p);
}
statbox.append(textbox);
}
if(instance.uptime){
const stats=document.createElement("div");
stats.classList.add("flexltr");
const span=document.createElement("span");
span.innerText=`Uptime: All time: ${Math.floor(instance.uptime.alltime*100)}% This week: ${Math.floor(instance.uptime.weektime*100)}% Today: ${Math.floor(instance.uptime.daytime*100)}%`;
stats.append(span);
statbox.append(stats);
}
div.append(statbox);
div.onclick=_=>{
if(instance.online){
window.location.href="/register.html?instance="+encodeURI(instance.name);
}else{
alert("Instance is offline, can't connect");
}
};
serverbox.append(div);
}
});

View file

@ -1,80 +1,80 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>Jank Client</title>
<meta content="Jank Client" property="og:title" />
<meta content="A spacebar client that has DMs, replying and more" property="og:description" />
<meta content="/logo.webp" property="og:image" />
<meta content="#4b458c" data-react-helmet="true" name="theme-color" />
<link href="/style.css" rel="stylesheet" type="text/css" />
<link href="/themes.css" rel="stylesheet" type="text/css" id="lightcss"/>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>Jank Client</title>
<meta content="Jank Client" property="og:title">
<meta content="A spacebar client that has DMs, replying and more" property="og:description">
<meta content="/logo.webp" property="og:image">
<meta content="#4b458c" data-react-helmet="true" name="theme-color">
<link href="/style.css" rel="stylesheet">
<link href="/themes.css" rel="stylesheet" id="lightcss">
<link rel="manifest" href="/manifest.json" />
</head>
<link rel="manifest" href="/manifest.json">
</head>
<body class="Dark-theme">
<script src="/index.js" type="module"></script>
<body class="Dark-theme">
<script src="/index.js" type="module"></script>
<div id="loading" class="loading">
<div id="centerdiv">
<img src="/logo.svg" style="width:3in;height:3in;">
<h1>Jank Client is loading</h1>
<h2 id="load-desc">This shouldn't take long</h2>
<h1 id="switchaccounts">Switch Accounts</h1>
</div>
</div>
<div class="flexltr" id="page">
<div id="neunence">
<div id="servers"></div>
</div>
<div class="flexttb channelflex">
<div class="servertd" id="servertd">
<h2 id="serverName">Server Name</h2>
</div>
<div id="channels"></div>
<div class="flexltr" id="userdock">
<div class="flexltr" id="userinfo">
<img id="userpfp" class="pfp">
<div id="loading" class="loading">
<div id="centerdiv">
<img src="/logo.svg" style="width:3in;height:3in;">
<h1>Jank Client is loading</h1>
<h2 id="load-desc">This shouldn't take long</h2>
<h1 id="switchaccounts">Switch Accounts</h1>
</div>
</div>
<div class="flexltr" id="page">
<div id="neunence">
<div id="servers"></div>
</div>
<div class="flexttb channelflex">
<div class="servertd" id="servertd">
<h2 id="serverName">Server Name</h2>
</div>
<div id="channels"></div>
<div class="flexltr" id="userdock">
<div class="flexltr" id="userinfo">
<img id="userpfp" class="pfp">
<div class="userflex">
<p id="username">USERNAME</p>
<p id="status">STATUS</p>
</div>
</div>
<div class="userflex">
<p id="username">USERNAME</p>
<p id="status">STATUS</p>
</div>
</div>
<div id="user-actions">
<img id="settings" class="svgtheme" src="/icons/settings.svg"></img>
</div>
</div>
</div>
<div class="flexttb messageflex">
<div class="servertd channelnamediv">
<span id="mobileback" hidden></span>
<span id="channelname">Channel name</span>
<span id="channelTopic" hidden>Channel topic</span>
</div>
<div id="channelw">
<div id="loadingdiv">
</div>
</div>
<div id="pasteimage"></div>
<div id="replybox" class="hideReplyBox"></div>
<div id="typediv">
<div id="realbox">
<div id="typebox" contentEditable="true"></div>
</div>
<div id="typing" class="hidden">
<p id="typingtext">typing</p>
<div class="loading-indicator">
<div class="dot"></div>
<div class="dot"></div>
<div class="dot"></div>
</div>
</div>
</div>
</div>
</div>
</body>
<div id="user-actions">
<img id="settings" class="svgtheme" src="/icons/settings.svg"></img>
</div>
</div>
</div>
<div class="flexttb messageflex">
<div class="servertd channelnamediv">
<span id="mobileback" hidden></span>
<span id="channelname">Channel name</span>
<span id="channelTopic" hidden>Channel topic</span>
</div>
<div id="channelw">
<div id="loadingdiv">
</div>
</div>
<div id="pasteimage"></div>
<div id="replybox" class="hideReplyBox"></div>
<div id="typediv">
<div id="realbox">
<div id="typebox" contentEditable="true"></div>
</div>
<div id="typing" class="hidden">
<p id="typingtext">typing</p>
<div class="loading-indicator">
<div class="dot"></div>
<div class="dot"></div>
<div class="dot"></div>
</div>
</div>
</div>
</div>
</div>
</body>
</html>

View file

@ -1,183 +1,187 @@
import { Localuser } from "./localuser.js";
import {Contextmenu} from "./contextmenu.js";
import {mobile, getBulkUsers,setTheme, Specialuser} from "./login.js";
import { MarkDown } from "./markdown.js";
import { Message } from "./message.js";
import { File } from "./file.js";
(async()=>{
async function waitforload(){
let res;
new Promise(r=>{res=r});
document.addEventListener("DOMContentLoaded", function(){
res();
});
await res;
}
await waitforload();
import{ Localuser }from"./localuser.js";
import{Contextmenu}from"./contextmenu.js";
import{mobile, getBulkUsers,setTheme, Specialuser}from"./login.js";
import{ MarkDown }from"./markdown.js";
import{ Message }from"./message.js";
import{ File }from"./file.js";
(async ()=>{
async function waitforload(){
let res;
new Promise(r=>{
res=r;
});
document.addEventListener("DOMContentLoaded", ()=>{
res();
});
await res;
}
await waitforload();
const users=getBulkUsers();
if(!users.currentuser){
window.location.href = '/login.html';
}
const users=getBulkUsers();
if(!users.currentuser){
window.location.href = "/login.html";
}
function showAccountSwitcher(){
const table=document.createElement("div");
for(const thing of Object.values(users.users)){
const specialuser=thing as Specialuser;
console.log(specialuser.pfpsrc)
function showAccountSwitcher(){
const table=document.createElement("div");
for(const thing of Object.values(users.users)){
const specialuser=thing as Specialuser;
console.log(specialuser.pfpsrc);
const userinfo=document.createElement("div");
userinfo.classList.add("flexltr","switchtable");
const pfp=document.createElement("img");
userinfo.append(pfp);
const userinfo=document.createElement("div");
userinfo.classList.add("flexltr","switchtable");
const pfp=document.createElement("img");
userinfo.append(pfp);
const user=document.createElement("div");
userinfo.append(user);
user.append(specialuser.username);
user.append(document.createElement("br"));
const span=document.createElement("span");
span.textContent=specialuser.serverurls.wellknown.replace("https://","").replace("http://","");
user.append(span);
user.classList.add("userinfo")
span.classList.add("serverURL")
const user=document.createElement("div");
userinfo.append(user);
user.append(specialuser.username);
user.append(document.createElement("br"));
const span=document.createElement("span");
span.textContent=specialuser.serverurls.wellknown.replace("https://","").replace("http://","");
user.append(span);
user.classList.add("userinfo");
span.classList.add("serverURL");
pfp.src=specialuser.pfpsrc;
pfp.classList.add("pfp");
table.append(userinfo);
userinfo.addEventListener("click",_=>{
thisuser.unload();
thisuser.swapped=true;
const loading=document.getElementById("loading") as HTMLDivElement;
loading.classList.remove("doneloading");
loading.classList.add("loading");
thisuser=new Localuser(specialuser);
users["currentuser"]=specialuser.uid;
localStorage.setItem("userinfos",JSON.stringify(users));
thisuser.initwebsocket().then(_=>{
thisuser.loaduser();
thisuser.init();
loading.classList.add("doneloading");
loading.classList.remove("loading");
console.log("done loading")
});
userinfo.remove();
})
}
{
const td=document.createElement("div");
td.classList.add("switchtable")
td.append("Switch accounts ⇌");
td.addEventListener("click",_=>{
window.location.href="/login.html";
})
table.append(td);
}
table.classList.add("accountSwitcher");
if(Contextmenu.currentmenu!=""){
Contextmenu.currentmenu.remove();
}
Contextmenu.currentmenu=table;
console.log(table);
document.body.append(table);
}
{
const userinfo=document.getElementById("userinfo") as HTMLDivElement;
userinfo.addEventListener("click",_=>{
_.stopImmediatePropagation();
showAccountSwitcher();
})
const switchaccounts=document.getElementById("switchaccounts") as HTMLDivElement;
switchaccounts.addEventListener("click",_=>{
_.stopImmediatePropagation();
showAccountSwitcher();
})
console.log("this ran")
}
let thisuser:Localuser;
try{
console.log(users.users,users.currentuser)
thisuser=new Localuser(users.users[users.currentuser]);
thisuser.initwebsocket().then(_=>{
thisuser.loaduser();
thisuser.init();
const loading=document.getElementById("loading") as HTMLDivElement;
loading.classList.add("doneloading");
loading.classList.remove("loading");
console.log("done loading")
});
}catch(e){
console.error(e);
(document.getElementById("load-desc") as HTMLSpanElement).textContent="Account unable to start";
thisuser=new Localuser(-1);
}
pfp.src=specialuser.pfpsrc;
pfp.classList.add("pfp");
table.append(userinfo);
userinfo.addEventListener("click",_=>{
thisuser.unload();
thisuser.swapped=true;
const loading=document.getElementById("loading") as HTMLDivElement;
loading.classList.remove("doneloading");
loading.classList.add("loading");
thisuser=new Localuser(specialuser);
users.currentuser=specialuser.uid;
localStorage.setItem("userinfos",JSON.stringify(users));
thisuser.initwebsocket().then(_=>{
thisuser.loaduser();
thisuser.init();
loading.classList.add("doneloading");
loading.classList.remove("loading");
console.log("done loading");
});
userinfo.remove();
});
}
{
const td=document.createElement("div");
td.classList.add("switchtable");
td.append("Switch accounts ⇌");
td.addEventListener("click",_=>{
window.location.href="/login.html";
});
table.append(td);
}
table.classList.add("accountSwitcher");
if(Contextmenu.currentmenu!=""){
Contextmenu.currentmenu.remove();
}
Contextmenu.currentmenu=table;
console.log(table);
document.body.append(table);
}
{
const userinfo=document.getElementById("userinfo") as HTMLDivElement;
userinfo.addEventListener("click",_=>{
_.stopImmediatePropagation();
showAccountSwitcher();
});
const switchaccounts=document.getElementById("switchaccounts") as HTMLDivElement;
switchaccounts.addEventListener("click",_=>{
_.stopImmediatePropagation();
showAccountSwitcher();
});
console.log("this ran");
}
let thisuser:Localuser;
try{
console.log(users.users,users.currentuser);
thisuser=new Localuser(users.users[users.currentuser]);
thisuser.initwebsocket().then(_=>{
thisuser.loaduser();
thisuser.init();
const loading=document.getElementById("loading") as HTMLDivElement;
loading.classList.add("doneloading");
loading.classList.remove("loading");
console.log("done loading");
});
}catch(e){
console.error(e);
(document.getElementById("load-desc") as HTMLSpanElement).textContent="Account unable to start";
thisuser=new Localuser(-1);
}
{
const menu=new Contextmenu("create rightclick");//Really should go into the localuser class, but that's a later thing
menu.addbutton("Create channel",function(){
if(thisuser.lookingguild){
thisuser.lookingguild.createchannels();
}
},null,_=>{return thisuser.isAdmin()})
{
const menu=new Contextmenu("create rightclick");//Really should go into the localuser class, but that's a later thing
menu.addbutton("Create channel",()=>{
if(thisuser.lookingguild){
thisuser.lookingguild.createchannels();
}
},null,_=>{
return thisuser.isAdmin();
});
menu.addbutton("Create category",function(){
if(thisuser.lookingguild){
thisuser.lookingguild.createcategory();
}
},null,_=>{return thisuser.isAdmin()})
menu.bindContextmenu(document.getElementById("channels") as HTMLDivElement,0,0)
}
menu.addbutton("Create category",()=>{
if(thisuser.lookingguild){
thisuser.lookingguild.createcategory();
}
},null,_=>{
return thisuser.isAdmin();
});
menu.bindContextmenu(document.getElementById("channels") as HTMLDivElement,0,0);
}
const pasteimage=document.getElementById("pasteimage") as HTMLDivElement;
let replyingto:Message|null=null;
async function enter(event){
const channel=thisuser.channelfocus
if(!channel||!thisuser.channelfocus) return;
channel.typingstart();
if(event.key === "Enter"&&!event.shiftKey){
event.preventDefault();
if(channel.editing){
channel.editing.edit(markdown.rawString);
channel.editing=null;
}else{
replyingto= thisuser.channelfocus.replyingto;
let replying=replyingto;
if(replyingto?.div){
replyingto.div.classList.remove("replying");
}
thisuser.channelfocus.replyingto=null;
channel.sendMessage(markdown.rawString,{
attachments:images,
embeds:[],
replyingto:replying
})
thisuser.channelfocus.makereplybox();
}
while(images.length!=0){
images.pop();
pasteimage.removeChild(imageshtml.pop() as HTMLElement);
}
typebox.innerHTML="";
return;
}
}
const pasteimage=document.getElementById("pasteimage") as HTMLDivElement;
let replyingto:Message|null=null;
async function enter(event){
const channel=thisuser.channelfocus;
if(!channel||!thisuser.channelfocus)return;
channel.typingstart();
if(event.key === "Enter"&&!event.shiftKey){
event.preventDefault();
if(channel.editing){
channel.editing.edit(markdown.rawString);
channel.editing=null;
}else{
replyingto= thisuser.channelfocus.replyingto;
const replying=replyingto;
if(replyingto?.div){
replyingto.div.classList.remove("replying");
}
thisuser.channelfocus.replyingto=null;
channel.sendMessage(markdown.rawString,{
attachments: images,
embeds: [],
replyingto: replying
});
thisuser.channelfocus.makereplybox();
}
while(images.length!=0){
images.pop();
pasteimage.removeChild(imageshtml.pop() as HTMLElement);
}
typebox.innerHTML="";
}
}
const typebox=document.getElementById("typebox") as HTMLDivElement;
const markdown=new MarkDown("",thisuser);
markdown.giveBox(typebox);
typebox["markdown"]=markdown;
typebox.addEventListener("keyup",enter);
typebox.addEventListener("keydown",event=>{
if(event.key === "Enter"&&!event.shiftKey) event.preventDefault();
});
console.log(typebox)
typebox.onclick=console.log;
const typebox=document.getElementById("typebox") as HTMLDivElement;
const markdown=new MarkDown("",thisuser);
markdown.giveBox(typebox);
typebox["markdown"]=markdown;
typebox.addEventListener("keyup",enter);
typebox.addEventListener("keydown",event=>{
if(event.key === "Enter"&&!event.shiftKey) event.preventDefault();
});
console.log(typebox);
typebox.onclick=console.log;
/*
/*
function getguildinfo(){
const path=window.location.pathname.split("/");
const channel=path[3];
@ -185,41 +189,41 @@ import { File } from "./file.js";
}
*/
const images:Blob[]=[];
const imageshtml:HTMLElement[]=[];
const images:Blob[]=[];
const imageshtml:HTMLElement[]=[];
document.addEventListener('paste', async (e) => {
if(!e.clipboardData) return;
Array.from(e.clipboardData.files).forEach(async (f) => {
const file=File.initFromBlob(f);
e.preventDefault();
const html=file.upHTML(images,f);
pasteimage.appendChild(html);
images.push(f)
imageshtml.push(html);
});
});
document.addEventListener("paste", async e=>{
if(!e.clipboardData)return;
Array.from(e.clipboardData.files).forEach(async f=>{
const file=File.initFromBlob(f);
e.preventDefault();
const html=file.upHTML(images,f);
pasteimage.appendChild(html);
images.push(f);
imageshtml.push(html);
});
});
setTheme();
setTheme();
function userSettings(){
thisuser.showusersettings();
}
(document.getElementById("settings") as HTMLImageElement).onclick=userSettings;
function userSettings(){
thisuser.showusersettings();
}
(document.getElementById("settings") as HTMLImageElement).onclick=userSettings;
if(mobile){
(document.getElementById("channelw") as HTMLDivElement).onclick=()=>{
((document.getElementById("channels") as HTMLDivElement).parentNode as HTMLElement).classList.add("collapse");
(document.getElementById("servertd") as HTMLDivElement).classList.add("collapse");
(document.getElementById("servers") as HTMLDivElement).classList.add("collapse");
}
(document.getElementById("mobileback") as HTMLDivElement).textContent="#";
(document.getElementById("mobileback") as HTMLDivElement).onclick=()=>{
((document.getElementById("channels") as HTMLDivElement).parentNode as HTMLElement).classList.remove("collapse");
(document.getElementById("servertd") as HTMLDivElement).classList.remove("collapse");
(document.getElementById("servers") as HTMLDivElement).classList.remove("collapse");
}
}
})()
if(mobile){
(document.getElementById("channelw") as HTMLDivElement).onclick=()=>{
((document.getElementById("channels") as HTMLDivElement).parentNode as HTMLElement).classList.add("collapse");
(document.getElementById("servertd") as HTMLDivElement).classList.add("collapse");
(document.getElementById("servers") as HTMLDivElement).classList.add("collapse");
};
(document.getElementById("mobileback") as HTMLDivElement).textContent="#";
(document.getElementById("mobileback") as HTMLDivElement).onclick=()=>{
((document.getElementById("channels") as HTMLDivElement).parentNode as HTMLElement).classList.remove("collapse");
(document.getElementById("servertd") as HTMLDivElement).classList.remove("collapse");
(document.getElementById("servers") as HTMLDivElement).classList.remove("collapse");
};
}
})();

View file

@ -1,305 +1,294 @@
class InfiniteScroller{
readonly getIDFromOffset:(ID:string,offset:number)=>Promise<string|undefined>;
readonly getHTMLFromID:(ID:string)=>Promise<HTMLElement>;
readonly destroyFromID:(ID:string)=>Promise<boolean>;
readonly reachesBottom:()=>void;
private readonly minDist=2000;
private readonly fillDist=3000;
private readonly maxDist=6000;
HTMLElements:[HTMLElement,string][]=[];
div:HTMLDivElement|null;
scroll:HTMLDivElement|null;
constructor(getIDFromOffset:InfiniteScroller["getIDFromOffset"],getHTMLFromID:InfiniteScroller["getHTMLFromID"],destroyFromID:InfiniteScroller["destroyFromID"],reachesBottom:InfiniteScroller["reachesBottom"]=()=>{}){
this.getIDFromOffset=getIDFromOffset;
this.getHTMLFromID=getHTMLFromID;
this.destroyFromID=destroyFromID;
this.reachesBottom=reachesBottom;
}
timeout:NodeJS.Timeout|null;
async getDiv(initialId:string,bottom=true):Promise<HTMLDivElement>{
const div=document.createElement("div");
div.classList.add("messagecontainer");
//div.classList.add("flexttb")
const scroll=document.createElement("div");
scroll.classList.add("flexttb","scroller")
div.appendChild(scroll);
this.div=div;
//this.interval=setInterval(this.updatestuff.bind(this,true),100);
readonly getIDFromOffset:(ID:string,offset:number)=>Promise<string|undefined>;
readonly getHTMLFromID:(ID:string)=>Promise<HTMLElement>;
readonly destroyFromID:(ID:string)=>Promise<boolean>;
readonly reachesBottom:()=>void;
private readonly minDist=2000;
private readonly fillDist=3000;
private readonly maxDist=6000;
HTMLElements:[HTMLElement,string][]=[];
div:HTMLDivElement|null;
scroll:HTMLDivElement|null;
constructor(getIDFromOffset:InfiniteScroller["getIDFromOffset"],getHTMLFromID:InfiniteScroller["getHTMLFromID"],destroyFromID:InfiniteScroller["destroyFromID"],reachesBottom:InfiniteScroller["reachesBottom"]=()=>{}){
this.getIDFromOffset=getIDFromOffset;
this.getHTMLFromID=getHTMLFromID;
this.destroyFromID=destroyFromID;
this.reachesBottom=reachesBottom;
}
timeout:NodeJS.Timeout|null;
async getDiv(initialId:string,bottom=true):Promise<HTMLDivElement>{
const div=document.createElement("div");
div.classList.add("messagecontainer");
//div.classList.add("flexttb")
const scroll=document.createElement("div");
scroll.classList.add("flexttb","scroller");
div.appendChild(scroll);
this.div=div;
//this.interval=setInterval(this.updatestuff.bind(this,true),100);
this.scroll=scroll;
this.div.addEventListener("scroll",_=>{
if(this.scroll) this.scrollTop=this.scroll.scrollTop;
this.watchForChange()
});
this.scroll.addEventListener("scroll",_=>{
if(null===this.timeout){
this.timeout=setTimeout(this.updatestuff.bind(this),300);
}
this.scroll=scroll;
this.div.addEventListener("scroll",_=>{
if(this.scroll)this.scrollTop=this.scroll.scrollTop;
this.watchForChange();
});
this.scroll.addEventListener("scroll",_=>{
if(this.timeout===null){
this.timeout=setTimeout(this.updatestuff.bind(this),300);
}
this.watchForChange()
});
{
let oldheight=0;
new ResizeObserver(_=>{
this.updatestuff();
const change=oldheight-div.offsetHeight;
if(change>0&&this.scroll){
this.scroll.scrollTop+=change;
}
oldheight=div.offsetHeight;
this.watchForChange();
}).observe(div);
}
new ResizeObserver(this.watchForChange.bind(this)).observe(scroll);
this.watchForChange();
});
{
let oldheight=0;
new ResizeObserver(_=>{
this.updatestuff();
const change=oldheight-div.offsetHeight;
if(change>0&&this.scroll){
this.scroll.scrollTop+=change;
}
oldheight=div.offsetHeight;
this.watchForChange();
}).observe(div);
}
new ResizeObserver(this.watchForChange.bind(this)).observe(scroll);
await this.firstElement(initialId)
this.updatestuff();
await this.watchForChange().then(_=>{
this.updatestuff();
})
return div;
}
scrollBottom:number;
scrollTop:number;
needsupdate=true;
averageheight:number=60;
async updatestuff(){
this.timeout=null;
if(!this.scroll) return;
this.scrollBottom = this.scroll.scrollHeight - this.scroll.scrollTop - this.scroll.clientHeight;
this.averageheight=this.scroll.scrollHeight/this.HTMLElements.length;
if(this.averageheight<10){
this.averageheight=60;
}
this.scrollTop=this.scroll.scrollTop;
if(!this.scrollBottom){
if(!await this.watchForChange()){
this.reachesBottom();
}
}
if(!this.scrollTop){
await this.watchForChange()
}
this.needsupdate=false;
//this.watchForChange();
}
async firstElement(id:string){
if(!this.scroll) return;
const html=await this.getHTMLFromID(id);
this.scroll.appendChild(html);
this.HTMLElements.push([html,id]);
}
currrunning:boolean=false;
async addedBottom(){
this.updatestuff();
const func=this.snapBottom();
await this.watchForChange();
func();
}
snapBottom(){
const scrollBottom=this.scrollBottom;
return ()=>{
if(this.scroll&&scrollBottom<30){
this.scroll.scrollTop=this.scroll.scrollHeight+20;
}
}
}
private async watchForTop(already=false,fragement=new DocumentFragment()):Promise<boolean>{
if(!this.scroll) return false;
try{
let again=false;
if(this.scrollTop<(already?this.fillDist:this.minDist)){
let nextid:string|undefined;
const firstelm=this.HTMLElements.at(0);
if(firstelm){
const previd=firstelm[1];
nextid=await this.getIDFromOffset(previd,1);
}
await this.firstElement(initialId);
this.updatestuff();
await this.watchForChange().then(_=>{
this.updatestuff();
});
return div;
}
scrollBottom:number;
scrollTop:number;
needsupdate=true;
averageheight:number=60;
async updatestuff(){
this.timeout=null;
if(!this.scroll)return;
this.scrollBottom = this.scroll.scrollHeight - this.scroll.scrollTop - this.scroll.clientHeight;
this.averageheight=this.scroll.scrollHeight/this.HTMLElements.length;
if(this.averageheight<10){
this.averageheight=60;
}
this.scrollTop=this.scroll.scrollTop;
if(!this.scrollBottom && !await this.watchForChange()){
this.reachesBottom();
}
if(!this.scrollTop){
await this.watchForChange();
}
this.needsupdate=false;
//this.watchForChange();
}
async firstElement(id:string){
if(!this.scroll)return;
const html=await this.getHTMLFromID(id);
this.scroll.appendChild(html);
this.HTMLElements.push([html,id]);
}
currrunning:boolean=false;
async addedBottom(){
this.updatestuff();
const func=this.snapBottom();
await this.watchForChange();
func();
}
snapBottom(){
const scrollBottom=this.scrollBottom;
return()=>{
if(this.scroll&&scrollBottom<30){
this.scroll.scrollTop=this.scroll.scrollHeight+20;
}
};
}
private async watchForTop(already=false,fragement=new DocumentFragment()):Promise<boolean>{
if(!this.scroll)return false;
try{
let again=false;
if(this.scrollTop<(already?this.fillDist:this.minDist)){
let nextid:string|undefined;
const firstelm=this.HTMLElements.at(0);
if(firstelm){
const previd=firstelm[1];
nextid=await this.getIDFromOffset(previd,1);
}
if(!nextid){
if(!nextid){
}else{
const html=await this.getHTMLFromID(nextid);
if(!html){
this.destroyFromID(nextid);
return false;
}
again=true;
fragement.prepend(html);
this.HTMLElements.unshift([html,nextid]);
this.scrollTop+=this.averageheight;
};
}
if(this.scrollTop>this.maxDist){
}else{
const html=await this.getHTMLFromID(nextid);
if(!html){
this.destroyFromID(nextid);
return false;
}
again=true;
fragement.prepend(html);
this.HTMLElements.unshift([html,nextid]);
this.scrollTop+=this.averageheight;
}
}
if(this.scrollTop>this.maxDist){
const html=this.HTMLElements.shift();
if(html){
again=true;
await this.destroyFromID(html[1]);
this.scrollTop-=this.averageheight;
}
}
if(again){
await this.watchForTop(true,fragement);
}
return again;
}finally{
if(!already){
if(this.scroll.scrollTop===0){
this.scrollTop=1;
this.scroll.scrollTop=10;
}
this.scroll.prepend(fragement,fragement);
}
}
}
async watchForBottom(already=false,fragement=new DocumentFragment()):Promise<boolean>{
if(!this.scroll)return false;
try{
let again=false;
const scrollBottom = this.scrollBottom;
if(scrollBottom<(already?this.fillDist:this.minDist)){
let nextid:string|undefined;
const lastelm=this.HTMLElements.at(-1);
if(lastelm){
const previd=lastelm[1];
nextid=await this.getIDFromOffset(previd,-1);
}
if(!nextid){
}else{
again=true;
const html=await this.getHTMLFromID(nextid);
fragement.appendChild(html);
this.HTMLElements.push([html,nextid]);
this.scrollBottom+=this.averageheight;
}
}
if(scrollBottom>this.maxDist){
const html=this.HTMLElements.pop();
if(html){
await this.destroyFromID(html[1]);
this.scrollBottom-=this.averageheight;
again=true;
}
}
if(again){
await this.watchForBottom(true,fragement);
}
return again;
}finally{
if(!already){
this.scroll.append(fragement);
if(this.scrollBottom<30){
this.scroll.scrollTop=this.scroll.scrollHeight;
}
}
}
}
watchtime:boolean=false;
changePromise:Promise<boolean>|undefined;
async watchForChange():Promise<boolean>{
if(this.currrunning){
this.watchtime=true;
if(this.changePromise){
return await this.changePromise;
}else{
return false;
}
}else{
this.watchtime=false;
this.currrunning=true;
}
this.changePromise=new Promise<boolean>(async res=>{
try{
try{
if(!this.div){
res(false);return false;
}
const out=await Promise.allSettled([this.watchForTop(),this.watchForBottom()]) as {value:boolean}[];
const changed=(out[0].value||out[1].value);
if(this.timeout===null&&changed){
this.timeout=setTimeout(this.updatestuff.bind(this),300);
}
if(!this.currrunning){
console.error("something really bad happened");
}
const html=this.HTMLElements.shift();
if(html){
again=true;
await this.destroyFromID(html[1]);
this.scrollTop-=this.averageheight;
}
}
if(again){
await this.watchForTop(true,fragement);
}
return again;
}finally{
if(!already){
if(this.scroll.scrollTop===0){
this.scrollTop=1;
this.scroll.scrollTop=10;
}
this.scroll.prepend(fragement,fragement);
}
}
}
async watchForBottom(already=false,fragement=new DocumentFragment()):Promise<boolean>{
if(!this.scroll) return false;
try{
let again=false;
const scrollBottom = this.scrollBottom;
if(scrollBottom<(already?this.fillDist:this.minDist)){
let nextid:string|undefined;
const lastelm=this.HTMLElements.at(-1);
if(lastelm){
const previd=lastelm[1];
nextid=await this.getIDFromOffset(previd,-1);
}
if(!nextid){
}else{
again=true;
const html=await this.getHTMLFromID(nextid);
fragement.appendChild(html);
this.HTMLElements.push([html,nextid]);
this.scrollBottom+=this.averageheight;
};
}
if(scrollBottom>this.maxDist){
const html=this.HTMLElements.pop();
if(html){
await this.destroyFromID(html[1]);
this.scrollBottom-=this.averageheight;
again=true;
}
}
if(again){
await this.watchForBottom(true,fragement);
}
return again;
}finally{
if(!already){
this.scroll.append(fragement);
if(this.scrollBottom<30){
this.scroll.scrollTop=this.scroll.scrollHeight;
}
}
}
}
watchtime:boolean=false;
changePromise:Promise<boolean>|undefined;
async watchForChange():Promise<boolean>{
if(this.currrunning){
this.watchtime=true;
if(this.changePromise){
return await this.changePromise;
}else{
return false;
}
}else{
this.watchtime=false;
this.currrunning=true;
}
this.changePromise=new Promise<boolean>(async res=>{
try{
try{
if(!this.div){res(false);return false}
const out=await Promise.allSettled([this.watchForTop(),this.watchForBottom()]) as {value:boolean}[];
const changed=(out[0].value||out[1].value);
if(null===this.timeout&&changed){
this.timeout=setTimeout(this.updatestuff.bind(this),300);
}
if(!this.currrunning){console.error("something really bad happened")}
res(!!changed);
return !!changed;
}catch(e){
console.error(e);
}
res(false);
return false;
}catch(e){
throw e;
}finally{
setTimeout(_=>{
this.changePromise=undefined;
this.currrunning=false;
if(this.watchtime){
this.watchForChange();
}
},300)
}
})
return await this.changePromise;
}
async focus(id:string,flash=true){
let element:HTMLElement|undefined;
for(const thing of this.HTMLElements){
if(thing[1]===id){
element=thing[0];
}
}
console.log(id,element,this.HTMLElements.length,":3");
if(element){
if(flash){
element.scrollIntoView({
behavior:"smooth",
block:"center"
});
await new Promise(resolve => setTimeout(resolve, 1000));
element.classList.remove("jumped");
await new Promise(resolve => setTimeout(resolve, 100));
element.classList.add("jumped");
}else{
element.scrollIntoView();
}
}else{
for(const thing of this.HTMLElements){
await this.destroyFromID(thing[1]);
}
this.HTMLElements=[];
await this.firstElement(id);
this.updatestuff();
await this.watchForChange();
await new Promise(resolve => setTimeout(resolve, 100));
await this.focus(id,true);
}
}
async delete():Promise<void>{
for(const thing of this.HTMLElements){
await this.destroyFromID(thing[1]);
}
this.HTMLElements=[];
if(this.timeout){
clearTimeout(this.timeout);
}
if(this.div){
this.div.remove();
}
this.scroll=null;
this.div=null;
}
res(Boolean(changed));
return Boolean(changed);
}catch(e){
console.error(e);
}
res(false);
return false;
}catch(e){
throw e;
}finally{
setTimeout(_=>{
this.changePromise=undefined;
this.currrunning=false;
if(this.watchtime){
this.watchForChange();
}
},300);
}
});
return await this.changePromise;
}
async focus(id:string,flash=true){
let element:HTMLElement|undefined;
for(const thing of this.HTMLElements){
if(thing[1]===id){
element=thing[0];
}
}
console.log(id,element,this.HTMLElements.length,":3");
if(element){
if(flash){
element.scrollIntoView({
behavior: "smooth",
block: "center"
});
await new Promise(resolve=>setTimeout(resolve, 1000));
element.classList.remove("jumped");
await new Promise(resolve=>setTimeout(resolve, 100));
element.classList.add("jumped");
}else{
element.scrollIntoView();
}
}else{
for(const thing of this.HTMLElements){
await this.destroyFromID(thing[1]);
}
this.HTMLElements=[];
await this.firstElement(id);
this.updatestuff();
await this.watchForChange();
await new Promise(resolve=>setTimeout(resolve, 100));
await this.focus(id,true);
}
}
async delete():Promise<void>{
for(const thing of this.HTMLElements){
await this.destroyFromID(thing[1]);
}
this.HTMLElements=[];
if(this.timeout){
clearTimeout(this.timeout);
}
if(this.div){
this.div.remove();
}
this.scroll=null;
this.div=null;
}
}
export {InfiniteScroller};
export{InfiniteScroller};

View file

@ -1,22 +1,22 @@
<body class="Dark-theme">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Jank Client</title>
<meta content="Invite" property="og:title" />
<meta content="You shouldn't see this, but this is an invite URL" property="og:description" />
<meta content="/logo.webp" property="og:image" />
<meta content="#4b458c" data-react-helmet="true" name="theme-color" />
<link href="/style.css" rel="stylesheet" type="text/css" />
<link href="/themes.css" rel="stylesheet" type="text/css" id="lightcss"/>
</head>
<div>
<div id="invitebody">
<div id="inviteimg"></div>
<h1 id="invitename">Server Name</h1>
<p id="invitedescription">Someone invited you to Server Name</p>
<button id="AcceptInvite">Accept Invite</button>
</div>
</div>
<script type="module" src="/invite.js"></script>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Jank Client</title>
<meta content="Invite" property="og:title">
<meta content="You shouldn't see this, but this is an invite URL" property="og:description">
<meta content="/logo.webp" property="og:image">
<meta content="#4b458c" data-react-helmet="true" name="theme-color">
<link href="/style.css" rel="stylesheet">
<link href="/themes.css" rel="stylesheet" id="lightcss">
</head>
<div>
<div id="invitebody">
<div id="inviteimg"></div>
<h1 id="invitename">Server Name</h1>
<p id="invitedescription">Someone invited you to Server Name</p>
<button id="AcceptInvite">Accept Invite</button>
</div>
</div>
<script type="module" src="/invite.js"></script>
</body>

View file

@ -1,120 +1,118 @@
import {getBulkUsers, Specialuser, getapiurls} from "./login.js";
(async()=>{
const users=getBulkUsers();
const well=new URLSearchParams(window.location.search).get("instance");
const joinable:Specialuser[]=[];
for(const thing in users.users){
const user:Specialuser = users.users[thing]
if(user.serverurls.wellknown.includes(well)){
joinable.push(user);
}
console.log(users.users[thing]);
}
let urls:{api:string,cdn:string};
if(!joinable.length){
const out=await getapiurls(well);
if(out){
urls=out;
for(const thing in users.users){
const user:Specialuser = users.users[thing]
if(user.serverurls.api.includes(out.api)){
joinable.push(user);
}
console.log(users.users[thing]);
}
}else{
throw Error("someone needs to handle the case where the servers don't exist")
}
}else{
urls=joinable[0].serverurls;
}
if(!joinable.length){
document.getElementById("AcceptInvite").textContent="Create an account to accept the invite"
}
const code=window.location.pathname.split("/")[2];
let guildinfo;
fetch(`${urls.api}/invites/${code}`,{
method:"GET"
}).then(_=>_.json()).then(json=>{
const guildjson=json.guild;
guildinfo=guildjson;
document.getElementById("invitename").textContent=guildjson.name;
document.getElementById("invitedescription").textContent=
import{getBulkUsers, Specialuser, getapiurls}from"./login.js";
(async ()=>{
const users=getBulkUsers();
const well=new URLSearchParams(window.location.search).get("instance");
const joinable:Specialuser[]=[];
for(const thing in users.users){
const user:Specialuser = users.users[thing];
if(user.serverurls.wellknown.includes(well)){
joinable.push(user);
}
console.log(users.users[thing]);
}
let urls:{api:string,cdn:string};
if(!joinable.length&&well){
const out=await getapiurls(well);
if(out){
urls=out;
for(const thing in users.users){
const user:Specialuser = users.users[thing];
if(user.serverurls.api.includes(out.api)){
joinable.push(user);
}
console.log(users.users[thing]);
}
}else{
throw new Error("someone needs to handle the case where the servers don't exist");
}
}else{
urls=joinable[0].serverurls;
}
if(!joinable.length){
document.getElementById("AcceptInvite").textContent="Create an account to accept the invite";
}
const code=window.location.pathname.split("/")[2];
let guildinfo;
fetch(`${urls.api}/invites/${code}`,{
method: "GET"
}).then(_=>_.json()).then(json=>{
const guildjson=json.guild;
guildinfo=guildjson;
document.getElementById("invitename").textContent=guildjson.name;
document.getElementById("invitedescription").textContent=
`${json.inviter.username} invited you to join ${guildjson.name}`;
if(guildjson.icon){
const img=document.createElement("img");
img.src=`${urls.cdn}/icons/${guildjson.id}/${guildjson.icon}.png`;
img.classList.add("inviteGuild");
document.getElementById("inviteimg").append(img);
}else{
const txt=guildjson.name.replace(/'s /g, " ").replace(/\w+/g, word => word[0]).replace(/\s/g, "");
const div=document.createElement("div");
div.textContent=txt;
div.classList.add("inviteGuild");
document.getElementById("inviteimg").append(div);
}
if(guildjson.icon){
const img=document.createElement("img");
img.src=`${urls.cdn}/icons/${guildjson.id}/${guildjson.icon}.png`;
img.classList.add("inviteGuild");
document.getElementById("inviteimg").append(img);
}else{
const txt=guildjson.name.replace(/'s /g, " ").replace(/\w+/g, word=>word[0]).replace(/\s/g, "");
const div=document.createElement("div");
div.textContent=txt;
div.classList.add("inviteGuild");
document.getElementById("inviteimg").append(div);
}
});
function showAccounts(){
const table=document.createElement("dialog");
for(const thing of Object.values(joinable)){
const specialuser=thing as Specialuser;
console.log(specialuser.pfpsrc);
})
function showAccounts(){
const table=document.createElement("dialog");
for(const thing of Object.values(joinable)){
const specialuser=thing as Specialuser;
console.log(specialuser.pfpsrc)
const userinfo=document.createElement("div");
userinfo.classList.add("flexltr","switchtable");
const pfp=document.createElement("img");
userinfo.append(pfp);
const userinfo=document.createElement("div");
userinfo.classList.add("flexltr","switchtable");
const pfp=document.createElement("img");
userinfo.append(pfp);
const user=document.createElement("div");
userinfo.append(user);
user.append(specialuser.username);
user.append(document.createElement("br"));
const span=document.createElement("span");
span.textContent=specialuser.serverurls.wellknown.replace("https://","").replace("http://","");
user.append(span);
user.classList.add("userinfo");
span.classList.add("serverURL");
const user=document.createElement("div");
userinfo.append(user);
user.append(specialuser.username);
user.append(document.createElement("br"));
const span=document.createElement("span");
span.textContent=specialuser.serverurls.wellknown.replace("https://","").replace("http://","");
user.append(span);
user.classList.add("userinfo")
span.classList.add("serverURL")
pfp.src=specialuser.pfpsrc;
pfp.classList.add("pfp");
table.append(userinfo);
userinfo.addEventListener("click",_=>{
console.log(thing);
fetch(`${urls.api}/invites/${code}`,{
method:"POST",
headers:{
Authorization:thing.token
}
}).then(_=>{
users["currentuser"]=specialuser.uid;
localStorage.setItem("userinfos",JSON.stringify(users));
window.location.href="/channels/"+guildinfo.id;
})
})
}
{
const td=document.createElement("div");
td.classList.add("switchtable")
td.append("Login or create an account ⇌");
td.addEventListener("click",_=>{
const l=new URLSearchParams("?")
l.set("goback",window.location.href);
l.set("instance",well);
window.location.href="/login?"+l.toString();
})
if(!joinable.length){
const l=new URLSearchParams("?")
l.set("goback",window.location.href);
l.set("instance",well);
window.location.href="/login?"+l.toString();
}
table.append(td);
}
table.classList.add("accountSwitcher");
console.log(table);
document.body.append(table);
}
document.getElementById("AcceptInvite").addEventListener("click",showAccounts);
pfp.src=specialuser.pfpsrc;
pfp.classList.add("pfp");
table.append(userinfo);
userinfo.addEventListener("click",_=>{
console.log(thing);
fetch(`${urls.api}/invites/${code}`,{
method: "POST",
headers: {
Authorization: thing.token
}
}).then(_=>{
users.currentuser=specialuser.uid;
localStorage.setItem("userinfos",JSON.stringify(users));
window.location.href="/channels/"+guildinfo.id;
});
});
}
{
const td=document.createElement("div");
td.classList.add("switchtable");
td.append("Login or create an account ⇌");
td.addEventListener("click",_=>{
const l=new URLSearchParams("?");
l.set("goback",window.location.href);
l.set("instance",well);
window.location.href="/login?"+l.toString();
});
if(!joinable.length){
const l=new URLSearchParams("?");
l.set("goback",window.location.href);
l.set("instance",well);
window.location.href="/login?"+l.toString();
}
table.append(td);
}
table.classList.add("accountSwitcher");
console.log(table);
document.body.append(table);
}
document.getElementById("AcceptInvite").addEventListener("click",showAccounts);
})();

View file

@ -396,4 +396,4 @@ type memberChunk={
chunk_count: number,
not_found: string[]
}
export {readyjson,dirrectjson,channeljson,guildjson,rolesjson,userjson,memberjson,mainuserjson,messagejson,filejson,embedjson,emojijson,presencejson,wsjson,messageCreateJson,memberChunk};
export{readyjson,dirrectjson,channeljson,guildjson,rolesjson,userjson,memberjson,mainuserjson,messagejson,filejson,embedjson,emojijson,presencejson,wsjson,messageCreateJson,memberChunk};

View file

@ -1,36 +1,36 @@
<body class="Dark-theme">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Jank Client</title>
<meta content="Jank Client" property="og:title" />
<meta content="A spacebar client that has DMs, replying and more" property="og:description" />
<meta content="/logo.webp" property="og:image" />
<meta content="#4b458c" data-react-helmet="true" name="theme-color" />
<link href="/style.css" rel="stylesheet" type="text/css" />
<link href="/themes.css" rel="stylesheet" type="text/css" id="lightcss"/>
</head>
<div id="logindiv">
<h1>Login</h1><br>
<form id="form" submit="check(e)">
<label for="instance"><b>Instance:</b></label><br>
<p id="verify"></p>
<input type="search" list="instances" placeholder="Instance URL" name="instance" id="instancein" value="" id="instancein" required><br><br>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Jank Client</title>
<meta content="Jank Client" property="og:title">
<meta content="A spacebar client that has DMs, replying and more" property="og:description">
<meta content="/logo.webp" property="og:image">
<meta content="#4b458c" data-react-helmet="true" name="theme-color">
<link href="/style.css" rel="stylesheet">
<link href="/themes.css" rel="stylesheet" id="lightcss">
</head>
<div id="logindiv">
<h1>Login</h1><br>
<form id="form" submit="check(e)">
<label for="instance"><b>Instance:</b></label><br>
<p id="verify"></p>
<input type="search" list="instances" placeholder="Instance URL" name="instance" id="instancein" value="" id="instancein" required><br><br>
<label for="uname"><b>Email:</b></label><br>
<input type="text" placeholder="Enter email address" name="uname" id="uname" required><br><br>
<label for="uname"><b>Email:</b></label><br>
<input type="text" placeholder="Enter email address" name="uname" id="uname" required><br><br>
<label for="psw"><b>Password:</b></label><br>
<input type="password" placeholder="Enter Password" name="psw" id="psw" required><br><br><br><br>
<p class="wrongred" id="wrong"></p>
<label for="psw"><b>Password:</b></label><br>
<input type="password" placeholder="Enter Password" name="psw" id="psw" required><br><br><br><br>
<p class="wrongred" id="wrong"></p>
<div id="h-captcha">
<div id="h-captcha">
</div>
<button type="submit">Login</button>
</form>
<a href="/register.html" id="switch">Don't have an account?</a>
</div>
<datalist id="instances"></datalist>
<script src="/login.js" type="module" ></script>
</div>
<button type="submit">Login</button>
</form>
<a href="/register.html" id="switch">Don't have an account?</a>
</div>
<datalist id="instances"></datalist>
<script src="/login.js" type="module"></script>
</body>

View file

@ -1,366 +1,362 @@
import { Dialog } from "./dialog.js";
import{ Dialog }from"./dialog.js";
const mobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
function setTheme(){
let name=localStorage.getItem("theme");
if(!name){
localStorage.setItem("theme","Dark");
name="Dark";
}
document.body.className=name+"-theme";
let name=localStorage.getItem("theme");
if(!name){
localStorage.setItem("theme","Dark");
name="Dark";
}
document.body.className=name+"-theme";
}
setTheme();
function getBulkUsers(){
const json=getBulkInfo()
for(const thing in json.users){
json.users[thing]=new Specialuser(json.users[thing]);
}
return json;
const json=getBulkInfo();
for(const thing in json.users){
json.users[thing]=new Specialuser(json.users[thing]);
}
return json;
}
function trimswitcher(){
const json=getBulkInfo()
const map=new Map();
for(const thing in json.users){
const user=json.users[thing];
let wellknown=user.serverurls.wellknown;
if(wellknown[wellknown.length-1]!=="/"){
wellknown+="/";
}
wellknown+=user.username;
if(map.has(wellknown)){
const otheruser=map.get(wellknown);
if(otheruser[1].serverurls.wellknown[otheruser[1].serverurls.wellknown.length-1]==="/"){
delete json.users[otheruser[0]];
map.set(wellknown,[thing,user]);
}else{
delete json.users[thing];
}
}else{
map.set(wellknown,[thing,user]);
}
}
for(const thing in json.users){
if(thing[thing.length-1]==="/"){
const user=json.users[thing];
delete json.users[thing];
json.users[thing.slice(0, -1)]=user;
}
}
localStorage.setItem("userinfos",JSON.stringify(json));
console.log(json);
const json=getBulkInfo();
const map=new Map();
for(const thing in json.users){
const user=json.users[thing];
let wellknown=user.serverurls.wellknown;
if(wellknown.at(-1)!=="/"){
wellknown+="/";
}
wellknown+=user.username;
if(map.has(wellknown)){
const otheruser=map.get(wellknown);
if(otheruser[1].serverurls.wellknown.at(-1)==="/"){
delete json.users[otheruser[0]];
map.set(wellknown,[thing,user]);
}else{
delete json.users[thing];
}
}else{
map.set(wellknown,[thing,user]);
}
}
for(const thing in json.users){
if(thing.at(-1)==="/"){
const user=json.users[thing];
delete json.users[thing];
json.users[thing.slice(0, -1)]=user;
}
}
localStorage.setItem("userinfos",JSON.stringify(json));
console.log(json);
}
function getBulkInfo(){
return JSON.parse(localStorage.getItem("userinfos"));
return JSON.parse(localStorage.getItem("userinfos"));
}
function setDefaults(){
let userinfos=getBulkInfo();
if(!userinfos){
localStorage.setItem("userinfos",JSON.stringify({
currentuser:null,
users:{},
preferences:
let userinfos=getBulkInfo();
if(!userinfos){
localStorage.setItem("userinfos",JSON.stringify({
currentuser: null,
users: {},
preferences:
{
theme:"Dark",
notifications:false,
notisound:"three",
theme: "Dark",
notifications: false,
notisound: "three",
},
}));
userinfos=getBulkInfo();
}
if(userinfos.users===undefined){
userinfos.users={};
}
if(userinfos.accent_color===undefined){
userinfos.accent_color="#242443";
}
document.documentElement.style.setProperty('--accent-color', userinfos.accent_color);
if(userinfos.preferences===undefined){
userinfos.preferences={
theme:"Dark",
notifications:false,
notisound:"three",
}
}
if(userinfos.preferences&&(userinfos.preferences.notisound===undefined)){
userinfos.preferences.notisound="three";
}
localStorage.setItem("userinfos",JSON.stringify(userinfos))
}));
userinfos=getBulkInfo();
}
if(userinfos.users===undefined){
userinfos.users={};
}
if(userinfos.accent_color===undefined){
userinfos.accent_color="#242443";
}
document.documentElement.style.setProperty("--accent-color", userinfos.accent_color);
if(userinfos.preferences===undefined){
userinfos.preferences={
theme: "Dark",
notifications: false,
notisound: "three",
};
}
if(userinfos.preferences&&(userinfos.preferences.notisound===undefined)){
userinfos.preferences.notisound="three";
}
localStorage.setItem("userinfos",JSON.stringify(userinfos));
}
setDefaults();
class Specialuser{
serverurls:{api:string,cdn:string,gateway:string,wellknown:string,login:string};
email:string;
token:string;
loggedin;
json;
constructor(json){
if(json instanceof Specialuser){
console.error("specialuser can't construct from another specialuser");
}
this.serverurls=json.serverurls;
let apistring=new URL(json.serverurls.api).toString();
apistring=apistring.replace(/\/(v\d+\/?)?$/, "")+"/v9";
this.serverurls.api=apistring;
this.serverurls.cdn=new URL(json.serverurls.cdn).toString().replace(/\/$/,"");
this.serverurls.gateway=new URL(json.serverurls.gateway).toString().replace(/\/$/,"");;
this.serverurls.wellknown=new URL(json.serverurls.wellknown).toString().replace(/\/$/,"");;
this.serverurls.login=new URL(json.serverurls.login).toString().replace(/\/$/,"");;
this.email=json.email;
this.token=json.token;
this.loggedin=json.loggedin;
this.json=json;
if(!this.serverurls||!this.email||!this.token){
console.error("There are fundamentally missing pieces of info missing from this user");
}
}
set pfpsrc(e){
console.log("this ran fr")
this.json.pfpsrc=e;
this.updateLocal();
}
get pfpsrc(){
return this.json.pfpsrc;
}
set username(e){
this.json.username=e;
this.updateLocal();
}
get username(){
return this.json.username;
}
get uid(){
return this.email+this.serverurls.wellknown;
}
toJSON(){
return this.json;
}
updateLocal(){
const info=getBulkInfo();
info.users[this.uid]=this.toJSON();
localStorage.setItem("userinfos",JSON.stringify(info));
}
serverurls:{api:string,cdn:string,gateway:string,wellknown:string,login:string};
email:string;
token:string;
loggedin;
json;
constructor(json){
if(json instanceof Specialuser){
console.error("specialuser can't construct from another specialuser");
}
this.serverurls=json.serverurls;
let apistring=new URL(json.serverurls.api).toString();
apistring=apistring.replace(/\/(v\d+\/?)?$/, "")+"/v9";
this.serverurls.api=apistring;
this.serverurls.cdn=new URL(json.serverurls.cdn).toString().replace(/\/$/,"");
this.serverurls.gateway=new URL(json.serverurls.gateway).toString().replace(/\/$/,"");
this.serverurls.wellknown=new URL(json.serverurls.wellknown).toString().replace(/\/$/,"");
this.serverurls.login=new URL(json.serverurls.login).toString().replace(/\/$/,"");
this.email=json.email;
this.token=json.token;
this.loggedin=json.loggedin;
this.json=json;
if(!this.serverurls||!this.email||!this.token){
console.error("There are fundamentally missing pieces of info missing from this user");
}
}
set pfpsrc(e){
console.log("this ran fr");
this.json.pfpsrc=e;
this.updateLocal();
}
get pfpsrc(){
return this.json.pfpsrc;
}
set username(e){
this.json.username=e;
this.updateLocal();
}
get username(){
return this.json.username;
}
get uid(){
return this.email+this.serverurls.wellknown;
}
toJSON(){
return this.json;
}
updateLocal(){
const info=getBulkInfo();
info.users[this.uid]=this.toJSON();
localStorage.setItem("userinfos",JSON.stringify(info));
}
}
function adduser(user){
user=new Specialuser(user);
const info=getBulkInfo();
info.users[user.uid]=user;
info.currentuser=user.uid;
localStorage.setItem("userinfos",JSON.stringify(info));
return user;
user=new Specialuser(user);
const info=getBulkInfo();
info.users[user.uid]=user;
info.currentuser=user.uid;
localStorage.setItem("userinfos",JSON.stringify(info));
return user;
}
const instancein=document.getElementById("instancein") as HTMLInputElement;
const instancein=document.getElementById("instancein") as HTMLInputElement;
let timeout;
let instanceinfo;
const stringURLMap=new Map<string,string>();
const stringURLsMap=new Map<string,{wellknown:string,api:string,cdn:string,gateway:string,login?:string}>();
async function getapiurls(str:string):Promise<{api:string,cdn:string,gateway:string,wellknown:string,login:string}|false>{
if(!URL.canParse(str)){
const val=stringURLMap.get(str);
if(val){
str=val;
}else{
const val=stringURLsMap.get(str)
if(val){
const responce=await fetch(val.api+val.api.endsWith("/")?"":"/"+"ping");
if(responce.ok){
if(val.login){
return val as {wellknown:string,api:string,cdn:string,gateway:string,login:string};
}else{
val.login=val.api;
return val as {wellknown:string,api:string,cdn:string,gateway:string,login:string};
}
}
}
}
}
if(str[str.length-1]!=="/"){
str+="/"
}
let api:string;
try{
const info=await fetch(`${str}/.well-known/spacebar`).then((x) => x.json());
api=info.api;
}catch{
return false
}
const url = new URL(api);
try{
const info=await fetch(`${api}${url.pathname.includes("api") ? "" : "api"}/policies/instance/domains`).then((x) => x.json());
return {
api: info.apiEndpoint,
gateway: info.gateway,
cdn: info.cdn,
wellknown: str,
login:url.toString()
};
}catch{
const val=stringURLsMap.get(str)
if(val){
const responce=await fetch(val.api+val.api.endsWith("/")?"":"/"+"ping");
if(responce.ok){
if(val.login){
return val as {wellknown:string,api:string,cdn:string,gateway:string,login:string};
}else{
val.login=val.api;
return val as {wellknown:string,api:string,cdn:string,gateway:string,login:string};
}
}
}
return false;
}
if(!URL.canParse(str)){
const val=stringURLMap.get(str);
if(val){
str=val;
}else{
const val=stringURLsMap.get(str);
if(val){
const responce=await fetch(val.api+val.api.endsWith("/")?"":"/"+"ping");
if(responce.ok){
if(val.login){
return val as {wellknown:string,api:string,cdn:string,gateway:string,login:string};
}else{
val.login=val.api;
return val as {wellknown:string,api:string,cdn:string,gateway:string,login:string};
}
}
}
}
}
if(str.at(-1)!=="/"){
str+="/";
}
let api:string;
try{
const info=await fetch(`${str}/.well-known/spacebar`).then(x=>x.json());
api=info.api;
}catch{
return false;
}
const url = new URL(api);
try{
const info=await fetch(`${api}${url.pathname.includes("api") ? "" : "api"}/policies/instance/domains`).then(x=>x.json());
return{
api: info.apiEndpoint,
gateway: info.gateway,
cdn: info.cdn,
wellknown: str,
login: url.toString()
};
}catch{
const val=stringURLsMap.get(str);
if(val){
const responce=await fetch(val.api+val.api.endsWith("/")?"":"/"+"ping");
if(responce.ok){
if(val.login){
return val as {wellknown:string,api:string,cdn:string,gateway:string,login:string};
}else{
val.login=val.api;
return val as {wellknown:string,api:string,cdn:string,gateway:string,login:string};
}
}
}
return false;
}
}
async function checkInstance(e:string){
const verify=document.getElementById("verify");
try{
verify.textContent="Checking Instance";
const instanceinfo=await getapiurls((instancein as HTMLInputElement).value) as {wellknown:string,api:string,cdn:string,gateway:string,login:string, value:string};
if(instanceinfo){
instanceinfo.value=(instancein as HTMLInputElement).value;
localStorage.setItem("instanceinfo",JSON.stringify(instanceinfo));
verify.textContent="Instance is all good"
if(checkInstance["alt"]){checkInstance["alt"]();}
setTimeout(_=>{
console.log(verify.textContent)
verify.textContent="";
},3000);
}else{
verify.textContent="Invalid Instance, try again"
}
}catch(e){
console.log("catch")
verify.textContent="Invalid Instance, try again"
}
const verify=document.getElementById("verify");
try{
verify.textContent="Checking Instance";
const instanceinfo=await getapiurls((instancein as HTMLInputElement).value) as {wellknown:string,api:string,cdn:string,gateway:string,login:string, value:string};
if(instanceinfo){
instanceinfo.value=(instancein as HTMLInputElement).value;
localStorage.setItem("instanceinfo",JSON.stringify(instanceinfo));
verify.textContent="Instance is all good";
if(checkInstance.alt){
checkInstance.alt();
}
setTimeout(_=>{
console.log(verify.textContent);
verify.textContent="";
},3000);
}else{
verify.textContent="Invalid Instance, try again";
}
}catch{
console.log("catch");
verify.textContent="Invalid Instance, try again";
}
}
if(instancein){
console.log(instancein)
instancein.addEventListener("keydown",e=>{
const verify=document.getElementById("verify");
verify.textContent="Waiting to check Instance"
clearTimeout(timeout);
timeout=setTimeout(checkInstance,1000);
});
if(localStorage.getItem("instanceinfo")){
const json=JSON.parse(localStorage.getItem("instanceinfo"));
if(json.value){
(instancein as HTMLInputElement).value=json.value
}else{
(instancein as HTMLInputElement).value=json.wellknown
}
}else{
checkInstance("https://spacebar.chat/");
}
console.log(instancein);
instancein.addEventListener("keydown",e=>{
const verify=document.getElementById("verify");
verify.textContent="Waiting to check Instance";
clearTimeout(timeout);
timeout=setTimeout(checkInstance,1000);
});
if(localStorage.getItem("instanceinfo")){
const json=JSON.parse(localStorage.getItem("instanceinfo"));
if(json.value){
(instancein as HTMLInputElement).value=json.value;
}else{
(instancein as HTMLInputElement).value=json.wellknown;
}
}else{
checkInstance("https://spacebar.chat/");
}
}
async function login(username:string, password:string, captcha:string){
if(captcha===""){
captcha=undefined;
}
const options={
method: "POST",
body:JSON.stringify({
"login": username,
"password": password,
"undelete":false,
"captcha_key":captcha
}),
headers: {
"Content-type": "application/json; charset=UTF-8",
}}
try{
const info=JSON.parse(localStorage.getItem("instanceinfo"));
const api=info.login+(info.login.startsWith("/")?"/":"");
return await fetch(api+'/auth/login',options).then(response=>response.json())
.then((response) => {
console.log(response,response.message)
if("Invalid Form Body"===response.message){
return response.errors.login._errors[0].message;
console.log("test")
}
//this.serverurls||!this.email||!this.token
console.log(response);
if(captcha===""){
captcha=undefined;
}
const options={
method: "POST",
body: JSON.stringify({
login: username,
password,
undelete: false,
captcha_key: captcha
}),
headers: {
"Content-type": "application/json; charset=UTF-8",
}};
try{
const info=JSON.parse(localStorage.getItem("instanceinfo"));
const api=info.login+(info.login.startsWith("/")?"/":"");
return await fetch(api+"/auth/login",options).then(response=>response.json())
.then(response=>{
console.log(response,response.message);
if(response.message==="Invalid Form Body"){
return response.errors.login._errors[0].message;
console.log("test");
}
//this.serverurls||!this.email||!this.token
console.log(response);
if(response.captcha_sitekey){
if(response.captcha_sitekey){
const capt=document.getElementById("h-captcha");
if(!capt.children.length){
const capty=document.createElement("div");
capty.classList.add("h-captcha");
const capt=document.getElementById("h-captcha");
if(!capt.children.length){
const capty=document.createElement("div");
capty.classList.add("h-captcha");
capty.setAttribute("data-sitekey", response.captcha_sitekey);
const script=document.createElement("script");
script.src="https://js.hcaptcha.com/1/api.js";
capt.append(script);
capt.append(capty);
}else{
eval("hcaptcha.reset()");
}
return;
}else{
console.log(response);
if(response.ticket){
let onetimecode="";
new Dialog(["vdiv",["title","2FA code:"],["textbox","","",function(this:HTMLInputElement){onetimecode=this.value}],["button","","Submit",function(){
fetch(api+"/auth/mfa/totp",{
method:"POST",
headers:{
"Content-Type": "application/json"
},
body:JSON.stringify({
code:onetimecode,
ticket:response.ticket,
})
}).then(r=>r.json()).then(response=>{
if(response.message){
alert(response.message)
}else{
console.warn(response);
if(!response.token) return;
adduser({serverurls:JSON.parse(localStorage.getItem("instanceinfo")),email:username,token:response.token}).username=username;
const redir=new URLSearchParams(window.location.search).get("goback");
if(redir){
window.location.href = redir;
}else{
window.location.href = '/channels/@me';
}
}
})
}]]).show();
}else{
console.warn(response);
if(!response.token) return;
adduser({serverurls:JSON.parse(localStorage.getItem("instanceinfo")),email:username,token:response.token}).username=username;
const redir=new URLSearchParams(window.location.search).get("goback");
if(redir){
window.location.href = redir;
}else{
window.location.href = '/channels/@me';
}
return "";
}
}
})
}catch(error){
console.error('Error:', error);
};
capty.setAttribute("data-sitekey", response.captcha_sitekey);
const script=document.createElement("script");
script.src="https://js.hcaptcha.com/1/api.js";
capt.append(script);
capt.append(capty);
}else{
eval("hcaptcha.reset()");
}
}else{
console.log(response);
if(response.ticket){
let onetimecode="";
new Dialog(["vdiv",["title","2FA code:"],["textbox","","",function(this:HTMLInputElement){
onetimecode=this.value;
}],["button","","Submit",function(){
fetch(api+"/auth/mfa/totp",{
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
code: onetimecode,
ticket: response.ticket,
})
}).then(r=>r.json()).then(response=>{
if(response.message){
alert(response.message);
}else{
console.warn(response);
if(!response.token)return;
adduser({serverurls: JSON.parse(localStorage.getItem("instanceinfo")),email: username,token: response.token}).username=username;
const redir=new URLSearchParams(window.location.search).get("goback");
if(redir){
window.location.href = redir;
}else{
window.location.href = "/channels/@me";
}
}
});
}]]).show();
}else{
console.warn(response);
if(!response.token)return;
adduser({serverurls: JSON.parse(localStorage.getItem("instanceinfo")),email: username,token: response.token}).username=username;
const redir=new URLSearchParams(window.location.search).get("goback");
if(redir){
window.location.href = redir;
}else{
window.location.href = "/channels/@me";
}
return"";
}
}
});
}catch(error){
console.error("Error:", error);
}
}
async function check(e){
e.preventDefault();
let h=await login(e.srcElement[1].value,e.srcElement[2].value,e.srcElement[3].value);
document.getElementById("wrong").textContent=h;
console.log(h);
e.preventDefault();
const h=await login(e.srcElement[1].value,e.srcElement[2].value,e.srcElement[3].value);
document.getElementById("wrong").textContent=h;
console.log(h);
}
if(document.getElementById("form")){
document.getElementById("form").addEventListener("submit", check);
document.getElementById("form").addEventListener("submit", check);
}
//this currently does not work, and need to be implemented better at some time.
/*
@ -390,50 +386,50 @@ if ("serviceWorker" in navigator){
*/
const switchurl=document.getElementById("switch") as HTMLAreaElement;
if(switchurl){
switchurl.href+=window.location.search;
const instance=new URLSearchParams(window.location.search).get("instance");
console.log(instance);
if(instance){
instancein.value=instance;
checkInstance("");
}
switchurl.href+=window.location.search;
const instance=new URLSearchParams(window.location.search).get("instance");
console.log(instance);
if(instance){
instancein.value=instance;
checkInstance("");
}
}
export {checkInstance};
export{checkInstance};
trimswitcher();
export {mobile, getBulkUsers,getBulkInfo,setTheme,Specialuser,getapiurls,adduser}
export{mobile, getBulkUsers,getBulkInfo,setTheme,Specialuser,getapiurls,adduser};
const datalist=document.getElementById("instances");
console.warn(datalist);
if(datalist){
fetch("/instances.json").then(_=>_.json()).then((json:{name:string,online:boolean,description?:string,src?:string,url?:string,display?:boolean,urls:{wellknown:string,api:string,cdn:string,gateway:string,login?:string}}[])=>{
console.warn(json);
if(instancein&&instancein.value===""){
instancein.value=json[0].name;
}
for(const instance of json){
if(instance.display===false){
continue;
}
const option=document.createElement("option");
option.disabled=!instance.online;
option.value=instance.name;
if(instance.url){
stringURLMap.set(option.value,instance.url);
if(instance.urls){
stringURLsMap.set(instance.url,instance.urls);
}
}else if(instance.urls){
stringURLsMap.set(option.value,instance.urls);
}else{
option.disabled=true;
}
if(instance.description){
option.label=instance.description;
}else{
option.label=instance.name;
}
datalist.append(option);
}
checkInstance("");
})
fetch("/instances.json").then(_=>_.json()).then((json:{name:string,online:boolean,description?:string,src?:string,url?:string,display?:boolean,urls:{wellknown:string,api:string,cdn:string,gateway:string,login?:string}}[])=>{
console.warn(json);
if(instancein&&instancein.value===""){
instancein.value=json[0].name;
}
for(const instance of json){
if(instance.display===false){
continue;
}
const option=document.createElement("option");
option.disabled=!instance.online;
option.value=instance.name;
if(instance.url){
stringURLMap.set(option.value,instance.url);
if(instance.urls){
stringURLsMap.set(instance.url,instance.urls);
}
}else if(instance.urls){
stringURLsMap.set(option.value,instance.urls);
}else{
option.disabled=true;
}
if(instance.description){
option.label=instance.description;
}else{
option.label=instance.name;
}
datalist.append(option);
}
checkInstance("");
});
}

File diff suppressed because it is too large Load diff

View file

@ -1,218 +1,219 @@
import {User} from "./user.js";
import {Role} from "./role.js";
import {Guild} from "./guild.js";
import { SnowFlake } from "./snowflake.js";
import { memberjson, presencejson, userjson } from "./jsontypes.js";
import { Dialog } from "./dialog.js";
import{User}from"./user.js";
import{Role}from"./role.js";
import{Guild}from"./guild.js";
import{ SnowFlake }from"./snowflake.js";
import{ memberjson, presencejson, userjson }from"./jsontypes.js";
import{ Dialog }from"./dialog.js";
class Member{
static already={};
owner:Guild;
user:User;
roles:Role[]=[];
id:string;
nick:string;
private constructor(memberjson:memberjson,owner:Guild){
this.owner=owner;
if(this.localuser.userMap.has(memberjson.id)){
this.user=this.localuser.userMap.get(memberjson.id) as User;
}else if(memberjson.user){
this.user=new User(memberjson.user,owner.localuser);
}else{
throw new Error("Missing user object of this member");
}
static already={};
owner:Guild;
user:User;
roles:Role[]=[];
id:string;
nick:string;
private constructor(memberjson:memberjson,owner:Guild){
this.owner=owner;
if(this.localuser.userMap.has(memberjson.id)){
this.user=this.localuser.userMap.get(memberjson.id) as User;
}else if(memberjson.user){
this.user=new User(memberjson.user,owner.localuser);
}else{
throw new Error("Missing user object of this member");
}
for(const thing of Object.keys(memberjson)){
if(thing==="guild"){continue}
if(thing==="owner"){continue}
if(thing==="roles"){
for(const strrole of memberjson["roles"]){
const role=SnowFlake.getSnowFlakeFromID(strrole,Role).getObject();
this.roles.push(role);
}
continue;
}
this[thing]=memberjson[thing];
}
if(this.localuser.userMap.has(this?.id)){
this.user=this.localuser.userMap.get(this?.id) as User;
return;
}
}
get guild(){
return this.owner;
}
get localuser(){
return this.guild.localuser;
}
get info(){
return this.owner.info;
}
static async new(memberjson:memberjson,owner:Guild):Promise<Member|undefined>{
let user:User;
if(owner.localuser.userMap.has(memberjson.id)){
user=owner.localuser.userMap.get(memberjson.id);
}else if(memberjson.user){
user=new User(memberjson.user,owner.localuser);
}else{
throw new Error("missing user object of this member");
}
if(user.members.has(owner)){
let memb=user.members.get(owner)
if(memb===undefined){
memb=new Member(memberjson,owner);
user.members.set(owner,memb);
return memb
}else if(memb instanceof Promise){
return await memb;//I should do something else, though for now this is "good enough"
}else{
return memb;
}
}else{
const memb=new Member(memberjson,owner);
user.members.set(owner,memb);
return memb;
}
}
static async resolveMember(user:User,guild:Guild):Promise<Member|undefined>{
const maybe=user.members.get(guild);
if(!user.members.has(guild)){
const membpromise=guild.localuser.resolvemember(user.id,guild.id);
const promise=new Promise<Member|undefined>( async res=>{
const membjson=await membpromise;
if(membjson===undefined){
res(undefined);
return undefined;
}else{
const member=new Member(membjson,guild);
const map=guild.localuser.presences;
member.getPresence(map.get(member.id));
map.delete(member.id);
res(member);
return member;
}
})
user.members.set(guild,promise);
}
if(maybe instanceof Promise){
return await maybe;
}else{
return maybe
}
}
public getPresence(presence:presencejson|undefined){
this.user.getPresence(presence);
}
/**
for(const thing of Object.keys(memberjson)){
if(thing==="guild"){
continue;
}
if(thing==="owner"){
continue;
}
if(thing==="roles"){
for(const strrole of memberjson.roles){
const role=SnowFlake.getSnowFlakeFromID(strrole,Role).getObject();
this.roles.push(role);
}
continue;
}
this[thing]=memberjson[thing];
}
if(this.localuser.userMap.has(this?.id)){
this.user=this.localuser.userMap.get(this?.id) as User;
}
}
get guild(){
return this.owner;
}
get localuser(){
return this.guild.localuser;
}
get info(){
return this.owner.info;
}
static async new(memberjson:memberjson,owner:Guild):Promise<Member|undefined>{
let user:User;
if(owner.localuser.userMap.has(memberjson.id)){
user=owner.localuser.userMap.get(memberjson.id) as User;
}else if(memberjson.user){
user=new User(memberjson.user,owner.localuser);
}else{
throw new Error("missing user object of this member");
}
if(user.members.has(owner)){
let memb=user.members.get(owner);
if(memb===undefined){
memb=new Member(memberjson,owner);
user.members.set(owner,memb);
return memb;
}else if(memb instanceof Promise){
return await memb;//I should do something else, though for now this is "good enough"
}else{
return memb;
}
}else{
const memb=new Member(memberjson,owner);
user.members.set(owner,memb);
return memb;
}
}
static async resolveMember(user:User,guild:Guild):Promise<Member|undefined>{
const maybe=user.members.get(guild);
if(!user.members.has(guild)){
const membpromise=guild.localuser.resolvemember(user.id,guild.id);
const promise=new Promise<Member|undefined>(async res=>{
const membjson=await membpromise;
if(membjson===undefined){
res(undefined);
}else{
const member=new Member(membjson,guild);
const map=guild.localuser.presences;
member.getPresence(map.get(member.id));
map.delete(member.id);
res(member);
return member;
}
});
user.members.set(guild,promise);
}
if(maybe instanceof Promise){
return await maybe;
}else{
return maybe;
}
}
public getPresence(presence:presencejson|undefined){
this.user.getPresence(presence);
}
/**
* @todo
*/
highInfo(){
fetch(this.info.api+"/users/"+this.id+"/profile?with_mutual_guilds=true&with_mutual_friends_count=true&guild_id="+this.guild.id,{headers:this.guild.headers})
}
hasRole(ID:string){
console.log(this.roles,ID);
for(const thing of this.roles){
if(thing.id===ID){
return true;
}
}
return false;
}
getColor(){
for(const thing of this.roles){
const color=thing.getColor();
if(color){
return color;
}
}
return "";
}
isAdmin(){
for(const role of this.roles){
if(role.permissions.getPermission("ADMINISTRATOR")){
return true;
}
}
return this.guild.properties.owner_id===this.user.id;
}
bind(html:HTMLElement){
if(html.tagName==="SPAN"){
if(!this) {return};
/*
highInfo(){
fetch(this.info.api+"/users/"+this.id+"/profile?with_mutual_guilds=true&with_mutual_friends_count=true&guild_id="+this.guild.id,{headers: this.guild.headers});
}
hasRole(ID:string){
console.log(this.roles,ID);
for(const thing of this.roles){
if(thing.id===ID){
return true;
}
}
return false;
}
getColor(){
for(const thing of this.roles){
const color=thing.getColor();
if(color){
return color;
}
}
return"";
}
isAdmin(){
for(const role of this.roles){
if(role.permissions.getPermission("ADMINISTRATOR")){
return true;
}
}
return this.guild.properties.owner_id===this.user.id;
}
bind(html:HTMLElement){
if(html.tagName==="SPAN"){
if(!this){
return;
}
/*
if(this.error){
}
*/
html.style.color=this.getColor();
}
html.style.color=this.getColor();
}
//this.profileclick(html);
}
profileclick(html:HTMLElement){
//to be implemented
}
get name(){
return this.nick||this.user.username;
}
kick(){
let reason=""
const menu=new Dialog(["vdiv",
["title","Kick "+this.name+" from "+this.guild.properties.name],
["textbox","Reason:","",function(e:Event){
reason=(e.target as HTMLInputElement).value;
}],
["button","","submit",()=>{
this.kickAPI(reason);
menu.hide();
}]
]);
menu.show();
}
kickAPI(reason:string){
const headers=structuredClone(this.guild.headers);
headers["x-audit-log-reason"]=reason
fetch(`${this.info.api}/guilds/${this.guild.id}/members/${this.id}`,{
method:"DELETE",
headers,
//this.profileclick(html);
}
profileclick(html:HTMLElement){
//to be implemented
}
get name(){
return this.nick||this.user.username;
}
kick(){
let reason="";
const menu=new Dialog(["vdiv",
["title","Kick "+this.name+" from "+this.guild.properties.name],
["textbox","Reason:","",function(e:Event){
reason=(e.target as HTMLInputElement).value;
}],
["button","","submit",()=>{
this.kickAPI(reason);
menu.hide();
}]
]);
menu.show();
}
kickAPI(reason:string){
const headers=structuredClone(this.guild.headers);
headers["x-audit-log-reason"]=reason;
fetch(`${this.info.api}/guilds/${this.guild.id}/members/${this.id}`,{
method: "DELETE",
headers,
})
}
ban(){
let reason=""
const menu=new Dialog(["vdiv",
["title","Ban "+this.name+" from "+this.guild.properties.name],
["textbox","Reason:","",function(e:Event){
reason=(e.target as HTMLInputElement).value;
}],
["button","","submit",()=>{
this.banAPI(reason);
menu.hide();
}]
]);
menu.show();
}
banAPI(reason:string){
const headers=structuredClone(this.guild.headers);
headers["x-audit-log-reason"]=reason
fetch(`${this.info.api}/guilds/${this.guild.id}/bans/${this.id}`,{
method:"PUT",
headers
});
}
ban(){
let reason="";
const menu=new Dialog(["vdiv",
["title","Ban "+this.name+" from "+this.guild.properties.name],
["textbox","Reason:","",function(e:Event){
reason=(e.target as HTMLInputElement).value;
}],
["button","","submit",()=>{
this.banAPI(reason);
menu.hide();
}]
]);
menu.show();
}
banAPI(reason:string){
const headers=structuredClone(this.guild.headers);
headers["x-audit-log-reason"]=reason;
fetch(`${this.info.api}/guilds/${this.guild.id}/bans/${this.id}`,{
method: "PUT",
headers
})
}
hasPermission(name:string):boolean{
if(this.isAdmin()){
return true;
}
for(const thing of this.roles){
if(thing.permissions.getPermission(name)){
return true;
}
}
return false;
}
});
}
hasPermission(name:string):boolean{
if(this.isAdmin()){
return true;
}
for(const thing of this.roles){
if(thing.permissions.getPermission(name)){
return true;
}
}
return false;
}
}
export {Member};
export{Member};

File diff suppressed because it is too large Load diff

View file

@ -1,323 +1,323 @@
class Permissions{
allow:bigint;
deny:bigint;
readonly hasDeny:boolean;
constructor(allow:string,deny:string=""){
this.hasDeny=!!deny;
try{
this.allow = BigInt(allow);
this.deny = BigInt(deny);
}catch(e){
this.allow = 0n;
this.deny = 0n;
console.error(`Something really stupid happened with a permission with allow being ${allow} and deny being, ${deny}, execution will still happen, but something really stupid happened, please report if you know what caused this.`)
}
}
getPermissionbit(b:number,big:bigint) : boolean{
return Boolean((big>>BigInt(b))&1n);
}
setPermissionbit(b:number,state:boolean,big:bigint) : bigint{
const bit=1n<<BigInt(b);
return (big & ~bit) | (BigInt(state) << BigInt(b));//thanks to geotale for this code :3
}
static map:{
allow:bigint;
deny:bigint;
readonly hasDeny:boolean;
constructor(allow:string,deny:string=""){
this.hasDeny=Boolean(deny);
try{
this.allow = BigInt(allow);
this.deny = BigInt(deny);
}catch{
this.allow = 0n;
this.deny = 0n;
console.error(`Something really stupid happened with a permission with allow being ${allow} and deny being, ${deny}, execution will still happen, but something really stupid happened, please report if you know what caused this.`);
}
}
getPermissionbit(b:number,big:bigint) : boolean{
return Boolean((big>>BigInt(b))&1n);
}
setPermissionbit(b:number,state:boolean,big:bigint) : bigint{
const bit=1n<<BigInt(b);
return(big & ~bit) | (BigInt(state) << BigInt(b));//thanks to geotale for this code :3
}
static map:{
[key:number|string]:{"name":string,"readableName":string,"description":string}|number,
}
static info:{"name":string,"readableName":string,"description":string}[];
static makeMap(){
Permissions.info=[//for people in the future, do not reorder these, the creation of the map realize on the order
{
name:"CREATE_INSTANT_INVITE",
readableName:"Create invite",
description:"Allows the user to create invites for the guild"
},
{
name:"KICK_MEMBERS",
readableName:"Kick members",
description:"Allows the user to kick members from the guild"
},
{
name:"BAN_MEMBERS",
readableName:"Ban members",
description:"Allows the user to ban members from the guild"
},
{
name:"ADMINISTRATOR",
readableName:"Administrator",
description:"Allows all permissions and bypasses channel permission overwrites. This is a dangerous permission!"
},
{
name:"MANAGE_CHANNELS",
readableName:"Manage channels",
description:"Allows the user to manage and edit channels"
},
{
name:"MANAGE_GUILD",
readableName:"Manage guild",
description:"Allows management and editing of the guild"
},
{
name:"ADD_REACTIONS",
readableName:"Add reactions",
description:"Allows user to add reactions to messages"
},
{
name:"VIEW_AUDIT_LOG",
readableName:"View audit log",
description:"Allows the user to view the audit log"
},
{
name:"PRIORITY_SPEAKER",
readableName:"Priority speaker",
description:"Allows for using priority speaker in a voice channel"
},
{
name:"STREAM",
readableName:"Video",
description:"Allows the user to stream"
},
{
name:"VIEW_CHANNEL",
readableName:"View channels",
description:"Allows the user to view the channel"
},
{
name:"SEND_MESSAGES",
readableName:"Send messages",
description:"Allows user to send messages"
},
{
name:"SEND_TTS_MESSAGES",
readableName:"Send text-to-speech messages",
description:"Allows the user to send text-to-speech messages"
},
{
name:"MANAGE_MESSAGES",
readableName:"Manage messages",
description:"Allows the user to delete messages that aren't their own"
},
{
name:"EMBED_LINKS",
readableName:"Embed links",
description:"Allow links sent by this user to auto-embed"
},
{
name:"ATTACH_FILES",
readableName:"Attach files",
description:"Allows the user to attach files"
},
{
name:"READ_MESSAGE_HISTORY",
readableName:"Read message history",
description:"Allows user to read the message history"
},
{
name:"MENTION_EVERYONE",
readableName:"Mention @everyone, @here and all roles",
description:"Allows the user to mention everyone"
},
{
name:"USE_EXTERNAL_EMOJIS",
readableName:"Use external emojis",
description:"Allows the user to use external emojis"
},
{
name:"VIEW_GUILD_INSIGHTS",
readableName:"View guild insights",
description:"Allows the user to see guild insights"
},
{
name:"CONNECT",
readableName:"Connect",
description:"Allows the user to connect to a voice channel"
},
{
name:"SPEAK",
readableName:"Speak",
description:"Allows the user to speak in a voice channel"
},
{
name:"MUTE_MEMBERS",
readableName:"Mute members",
description:"Allows user to mute other members"
},
{
name:"DEAFEN_MEMBERS",
readableName:"Deafen members",
description:"Allows user to deafen other members"
},
{
name:"MOVE_MEMBERS",
readableName:"Move members",
description:"Allows the user to move members between voice channels"
},
{
name:"USE_VAD",
readableName:"Use voice activity detection",
description:"Allows users to speak in a voice channel by simply talking"
},
{
name:"CHANGE_NICKNAME",
readableName:"Change nickname",
description:"Allows the user to change their own nickname"
},
{
name:"MANAGE_NICKNAMES",
readableName:"Manage nicknames",
description:"Allows user to change nicknames of other members"
},
{
name:"MANAGE_ROLES",
readableName:"Manage roles",
description:"Allows user to edit and manage roles"
},
{
name:"MANAGE_WEBHOOKS",
readableName:"Manage webhooks",
description:"Allows management and editing of webhooks"
},
{
name:"MANAGE_GUILD_EXPRESSIONS",
readableName:"Manage expressions",
description:"Allows for managing emoji, stickers, and soundboards"
},
{
name:"USE_APPLICATION_COMMANDS",
readableName:"Use application commands",
description:"Allows the user to use application commands"
},
{
name:"REQUEST_TO_SPEAK",
readableName:"Request to speak",
description:"Allows user to request to speak in stage channel"
},
{
name:"MANAGE_EVENTS",
readableName:"Manage events",
description:"Allows user to edit and manage events"
},
{
name:"MANAGE_THREADS",
readableName:"Manage threads",
description:"Allows the user to delete and archive threads and view all private threads"
},
{
name:"CREATE_PUBLIC_THREADS",
readableName:"Create public threads",
description:"Allows the user to create public threads"
},
{
name:"CREATE_PRIVATE_THREADS",
readableName:"Create private threads",
description:"Allows the user to create private threads"
},
{
name:"USE_EXTERNAL_STICKERS",
readableName:"Use external stickers",
description:"Allows user to use external stickers"
},
{
name:"SEND_MESSAGES_IN_THREADS",
readableName:"Send messages in threads",
description:"Allows the user to send messages in threads"
},
{
name:"USE_EMBEDDED_ACTIVITIES",
readableName:"Use activities",
description:"Allows the user to use embedded activities"
},
{
name:"MODERATE_MEMBERS",
readableName:"Timeout members",
description:"Allows the user to time out other users to prevent them from sending or reacting to messages in chat and threads, and from speaking in voice and stage channels"
},
{
name:"VIEW_CREATOR_MONETIZATION_ANALYTICS",
readableName:"View creator monetization analytics",
description:"Allows for viewing role subscription insights"
},
{
name:"USE_SOUNDBOARD",
readableName:"Use soundboard",
description:"Allows for using soundboard in a voice channel"
},
{
name:"CREATE_GUILD_EXPRESSIONS",
readableName:"Create expressions",
description:"Allows for creating emojis, stickers, and soundboard sounds, and editing and deleting those created by the current user."
},
{
name:"CREATE_EVENTS",
readableName:"Create events",
description:"Allows for creating scheduled events, and editing and deleting those created by the current user."
},
{
name:"USE_EXTERNAL_SOUNDS",
readableName:"Use external sounds",
description:"Allows the usage of custom soundboard sounds from other servers"
},
{
name:"SEND_VOICE_MESSAGES",
readableName:"Send voice messages",
description:"Allows sending voice messages"
},
{
name:"SEND_POLLS",
readableName:"Create polls",
description:"Allows sending polls"
},
{
name:"USE_EXTERNAL_APPS",
readableName:"Use external apps",
description:"Allows user-installed apps to send public responses. " +
};
static info:{"name":string,"readableName":string,"description":string}[];
static makeMap(){
Permissions.info=[//for people in the future, do not reorder these, the creation of the map realize on the order
{
name: "CREATE_INSTANT_INVITE",
readableName: "Create invite",
description: "Allows the user to create invites for the guild"
},
{
name: "KICK_MEMBERS",
readableName: "Kick members",
description: "Allows the user to kick members from the guild"
},
{
name: "BAN_MEMBERS",
readableName: "Ban members",
description: "Allows the user to ban members from the guild"
},
{
name: "ADMINISTRATOR",
readableName: "Administrator",
description: "Allows all permissions and bypasses channel permission overwrites. This is a dangerous permission!"
},
{
name: "MANAGE_CHANNELS",
readableName: "Manage channels",
description: "Allows the user to manage and edit channels"
},
{
name: "MANAGE_GUILD",
readableName: "Manage guild",
description: "Allows management and editing of the guild"
},
{
name: "ADD_REACTIONS",
readableName: "Add reactions",
description: "Allows user to add reactions to messages"
},
{
name: "VIEW_AUDIT_LOG",
readableName: "View audit log",
description: "Allows the user to view the audit log"
},
{
name: "PRIORITY_SPEAKER",
readableName: "Priority speaker",
description: "Allows for using priority speaker in a voice channel"
},
{
name: "STREAM",
readableName: "Video",
description: "Allows the user to stream"
},
{
name: "VIEW_CHANNEL",
readableName: "View channels",
description: "Allows the user to view the channel"
},
{
name: "SEND_MESSAGES",
readableName: "Send messages",
description: "Allows user to send messages"
},
{
name: "SEND_TTS_MESSAGES",
readableName: "Send text-to-speech messages",
description: "Allows the user to send text-to-speech messages"
},
{
name: "MANAGE_MESSAGES",
readableName: "Manage messages",
description: "Allows the user to delete messages that aren't their own"
},
{
name: "EMBED_LINKS",
readableName: "Embed links",
description: "Allow links sent by this user to auto-embed"
},
{
name: "ATTACH_FILES",
readableName: "Attach files",
description: "Allows the user to attach files"
},
{
name: "READ_MESSAGE_HISTORY",
readableName: "Read message history",
description: "Allows user to read the message history"
},
{
name: "MENTION_EVERYONE",
readableName: "Mention @everyone, @here and all roles",
description: "Allows the user to mention everyone"
},
{
name: "USE_EXTERNAL_EMOJIS",
readableName: "Use external emojis",
description: "Allows the user to use external emojis"
},
{
name: "VIEW_GUILD_INSIGHTS",
readableName: "View guild insights",
description: "Allows the user to see guild insights"
},
{
name: "CONNECT",
readableName: "Connect",
description: "Allows the user to connect to a voice channel"
},
{
name: "SPEAK",
readableName: "Speak",
description: "Allows the user to speak in a voice channel"
},
{
name: "MUTE_MEMBERS",
readableName: "Mute members",
description: "Allows user to mute other members"
},
{
name: "DEAFEN_MEMBERS",
readableName: "Deafen members",
description: "Allows user to deafen other members"
},
{
name: "MOVE_MEMBERS",
readableName: "Move members",
description: "Allows the user to move members between voice channels"
},
{
name: "USE_VAD",
readableName: "Use voice activity detection",
description: "Allows users to speak in a voice channel by simply talking"
},
{
name: "CHANGE_NICKNAME",
readableName: "Change nickname",
description: "Allows the user to change their own nickname"
},
{
name: "MANAGE_NICKNAMES",
readableName: "Manage nicknames",
description: "Allows user to change nicknames of other members"
},
{
name: "MANAGE_ROLES",
readableName: "Manage roles",
description: "Allows user to edit and manage roles"
},
{
name: "MANAGE_WEBHOOKS",
readableName: "Manage webhooks",
description: "Allows management and editing of webhooks"
},
{
name: "MANAGE_GUILD_EXPRESSIONS",
readableName: "Manage expressions",
description: "Allows for managing emoji, stickers, and soundboards"
},
{
name: "USE_APPLICATION_COMMANDS",
readableName: "Use application commands",
description: "Allows the user to use application commands"
},
{
name: "REQUEST_TO_SPEAK",
readableName: "Request to speak",
description: "Allows user to request to speak in stage channel"
},
{
name: "MANAGE_EVENTS",
readableName: "Manage events",
description: "Allows user to edit and manage events"
},
{
name: "MANAGE_THREADS",
readableName: "Manage threads",
description: "Allows the user to delete and archive threads and view all private threads"
},
{
name: "CREATE_PUBLIC_THREADS",
readableName: "Create public threads",
description: "Allows the user to create public threads"
},
{
name: "CREATE_PRIVATE_THREADS",
readableName: "Create private threads",
description: "Allows the user to create private threads"
},
{
name: "USE_EXTERNAL_STICKERS",
readableName: "Use external stickers",
description: "Allows user to use external stickers"
},
{
name: "SEND_MESSAGES_IN_THREADS",
readableName: "Send messages in threads",
description: "Allows the user to send messages in threads"
},
{
name: "USE_EMBEDDED_ACTIVITIES",
readableName: "Use activities",
description: "Allows the user to use embedded activities"
},
{
name: "MODERATE_MEMBERS",
readableName: "Timeout members",
description: "Allows the user to time out other users to prevent them from sending or reacting to messages in chat and threads, and from speaking in voice and stage channels"
},
{
name: "VIEW_CREATOR_MONETIZATION_ANALYTICS",
readableName: "View creator monetization analytics",
description: "Allows for viewing role subscription insights"
},
{
name: "USE_SOUNDBOARD",
readableName: "Use soundboard",
description: "Allows for using soundboard in a voice channel"
},
{
name: "CREATE_GUILD_EXPRESSIONS",
readableName: "Create expressions",
description: "Allows for creating emojis, stickers, and soundboard sounds, and editing and deleting those created by the current user."
},
{
name: "CREATE_EVENTS",
readableName: "Create events",
description: "Allows for creating scheduled events, and editing and deleting those created by the current user."
},
{
name: "USE_EXTERNAL_SOUNDS",
readableName: "Use external sounds",
description: "Allows the usage of custom soundboard sounds from other servers"
},
{
name: "SEND_VOICE_MESSAGES",
readableName: "Send voice messages",
description: "Allows sending voice messages"
},
{
name: "SEND_POLLS",
readableName: "Create polls",
description: "Allows sending polls"
},
{
name: "USE_EXTERNAL_APPS",
readableName: "Use external apps",
description: "Allows user-installed apps to send public responses. " +
"When disabled, users will still be allowed to use their apps but the responses will be ephemeral. " +
"This only applies to apps not also installed to the server."
},
];
Permissions.map={};
let i=0;
for(const thing of Permissions.info){
Permissions.map[i]=thing;
Permissions.map[thing.name]=i;
i++;
}
}
getPermission(name:string):number{
if(this.getPermissionbit(Permissions.map[name] as number,this.allow)){
return 1;
}else if(this.getPermissionbit(Permissions.map[name] as number,this.deny)){
return -1;
}else{
return 0;
}
}
hasPermission(name:string):boolean{
if(this.deny){console.warn("This function may of been used in error, think about using getPermision instead")}
if (this.getPermissionbit(Permissions.map[name] as number,this.allow)) return true;
if (name != "ADMINISTRATOR") return this.hasPermission("ADMINISTRATOR");
return false;
}
setPermission(name:string,setto:number):void{
const bit=Permissions.map[name] as number;
if (!bit) {
return console.error("Tried to set permission to " + setto + " for " + name + " but it doesn't exist");
}
},
];
Permissions.map={};
let i=0;
for(const thing of Permissions.info){
Permissions.map[i]=thing;
Permissions.map[thing.name]=i;
i++;
}
}
getPermission(name:string):number{
if(this.getPermissionbit(Permissions.map[name] as number,this.allow)){
return 1;
}else if(this.getPermissionbit(Permissions.map[name] as number,this.deny)){
return-1;
}else{
return 0;
}
}
hasPermission(name:string):boolean{
if(this.deny){
console.warn("This function may of been used in error, think about using getPermision instead");
}
if(this.getPermissionbit(Permissions.map[name] as number,this.allow))return true;
if(name != "ADMINISTRATOR")return this.hasPermission("ADMINISTRATOR");
return false;
}
setPermission(name:string,setto:number):void{
const bit=Permissions.map[name] as number;
if(!bit){
return console.error("Tried to set permission to " + setto + " for " + name + " but it doesn't exist");
}
if(setto===0){
this.deny=this.setPermissionbit(bit,false,this.deny);
this.allow=this.setPermissionbit(bit,false,this.allow);
}else if(setto===1){
this.deny=this.setPermissionbit(bit,false,this.deny);
this.allow=this.setPermissionbit(bit,true,this.allow);
}else if(setto===-1){
this.deny=this.setPermissionbit(bit,true,this.deny);
this.allow=this.setPermissionbit(bit,false,this.allow);
}else{
console.error("invalid number entered:"+setto);
}
}
if(setto===0){
this.deny=this.setPermissionbit(bit,false,this.deny);
this.allow=this.setPermissionbit(bit,false,this.allow);
}else if(setto===1){
this.deny=this.setPermissionbit(bit,false,this.deny);
this.allow=this.setPermissionbit(bit,true,this.allow);
}else if(setto===-1){
this.deny=this.setPermissionbit(bit,true,this.deny);
this.allow=this.setPermissionbit(bit,false,this.allow);
}else{
console.error("invalid number entered:"+setto);
}
}
}
Permissions.makeMap();
export {Permissions};
export{Permissions};

View file

@ -1,60 +1,60 @@
<body class="Dark-theme">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Jank Client</title>
<meta content="Jank Client" property="og:title" />
<meta content="A spacebar client that has DMs, replying and more" property="og:description" />
<meta content="/logo.webp" property="og:image" />
<meta content="#4b458c" data-react-helmet="true" name="theme-color" />
<link href="/style.css" rel="stylesheet" type="text/css" />
<link href="/themes.css" rel="stylesheet" type="text/css" id="lightcss"/>
</head>
<div id="logindiv">
<h1>Create an account</h1><br>
<form id="register" submit="registertry(e)">
<div>
<label for="instance"><b>Instance:</b></label><br>
<p id="verify"></p>
<input type="search" list="instances" placeholder="Instance URL" id="instancein" name="instance" value="" id="instancein" required>
</div>
<div>
<label for="uname"><b>Email:</b></label><br>
<input type="text" placeholder="Enter Email" name="uname" id="uname" required>
</div>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Jank Client</title>
<meta content="Jank Client" property="og:title">
<meta content="A spacebar client that has DMs, replying and more" property="og:description">
<meta content="/logo.webp" property="og:image">
<meta content="#4b458c" data-react-helmet="true" name="theme-color">
<link href="/style.css" rel="stylesheet">
<link href="/themes.css" rel="stylesheet" id="lightcss">
</head>
<div id="logindiv">
<h1>Create an account</h1><br>
<form id="register" submit="registertry(e)">
<div>
<label for="instance"><b>Instance:</b></label><br>
<p id="verify"></p>
<input type="search" list="instances" placeholder="Instance URL" id="instancein" name="instance" value="" id="instancein" required>
</div>
<div>
<label for="uname"><b>Email:</b></label><br>
<input type="text" placeholder="Enter Email" name="uname" id="uname" required>
</div>
<div>
<label for="uname"><b>Username:</b></label><br>
<input type="text" placeholder="Enter Username" name="username" id="username" required>
</div>
<div>
<label for="psw"><b>Password:</b></label><br>
<input type="password" placeholder="Enter Password" name="psw" id="psw" required>
</div>
<div>
<label for="uname"><b>Username:</b></label><br>
<input type="text" placeholder="Enter Username" name="username" id="username" required>
</div>
<div>
<label for="psw"><b>Password:</b></label><br>
<input type="password" placeholder="Enter Password" name="psw" id="psw" required>
</div>
<div>
<label for="psw2"><b>Enter password again:</b></label><br>
<input type="password" placeholder="Enter Password Again" name="psw2" id="psw2" required>
</div>
<div>
<label for="psw2"><b>Enter password again:</b></label><br>
<input type="password" placeholder="Enter Password Again" name="psw2" id="psw2" required>
</div>
<div>
<label for="date"><b>Date of birth:</b></label><br>
<input type="date" id="date" name="date"/>
</div>
<div>
<label for="date"><b>Date of birth:</b></label><br>
<input type="date" id="date" name="date">
</div>
<div>
<b id="TOSbox">I agree to the <a href="" id="TOSa">Terms of Service</a>:</b>
<input type="checkbox" id="TOS" name="TOS"/>
</div>
<div>
<b id="TOSbox">I agree to the <a href="" id="TOSa">Terms of Service</a>:</b>
<input type="checkbox" id="TOS" name="TOS">
</div>
<p class="wrongred" id="wrong"></p>
<div id="h-captcha">
<p class="wrongred" id="wrong"></p>
<div id="h-captcha">
</div>
<button type="submit" class="dontgrow">Create account</button>
</form>
<a href="/login.html" id="switch">Already have an account?</a>
</div>
<datalist id="instances"></datalist>
<script src="/register.js" type="module"></script>
</div>
<button type="submit" class="dontgrow">Create account</button>
</form>
<a href="/login.html" id="switch">Already have an account?</a>
</div>
<datalist id="instances"></datalist>
<script src="/register.js" type="module"></script>
</body>

View file

@ -1,109 +1,110 @@
import {checkInstance, adduser} from "./login.js";
import{checkInstance, adduser}from"./login.js";
if(document.getElementById("register")){
document.getElementById("register").addEventListener("submit", registertry);
document.getElementById("register").addEventListener("submit", registertry);
}
async function registertry(e){
e.preventDefault();
const elements=e.srcElement;
const email=elements[1].value;
const username=elements[2].value;
if(elements[3].value!==elements[4].value){
document.getElementById("wrong").textContent="Passwords don't match";
return;
}
const password=elements[3].value;
const dateofbirth=elements[5].value;
const apiurl=new URL(JSON.parse(localStorage.getItem("instanceinfo")).api);
e.preventDefault();
const elements=e.srcElement;
const email=elements[1].value;
const username=elements[2].value;
if(elements[3].value!==elements[4].value){
document.getElementById("wrong").textContent="Passwords don't match";
return;
}
const password=elements[3].value;
const dateofbirth=elements[5].value;
const apiurl=new URL(JSON.parse(localStorage.getItem("instanceinfo")).api)
await fetch(apiurl+"/auth/register",{
body: JSON.stringify({
date_of_birth: dateofbirth,
email,
username,
password,
consent: elements[6].checked,
captcha_key: elements[7]?.value
}),
headers: {
"content-type": "application/json"
},
method: "POST"
}).then(e=>{
e.json().then(e=>{
if(e.captcha_sitekey){
const capt=document.getElementById("h-captcha");
if(!capt.children.length){
const capty=document.createElement("div");
capty.classList.add("h-captcha");
await fetch(apiurl+"/auth/register",{
body:JSON.stringify({
date_of_birth:dateofbirth,
email:email,
username:username,
password:password,
consent:elements[6].checked,
captcha_key:elements[7]?.value
}),
headers:{
"content-type": "application/json"
},
method:"POST"
}).then(e=>{
e.json().then(e=>{
if(e.captcha_sitekey){
const capt=document.getElementById("h-captcha");
if(!capt.children.length){
const capty=document.createElement("div");
capty.classList.add("h-captcha");
capty.setAttribute("data-sitekey", e.captcha_sitekey);
const script=document.createElement("script");
script.src="https://js.hcaptcha.com/1/api.js";
capt.append(script);
capt.append(capty);
}else{
eval("hcaptcha.reset()");
}
return;
}
if(!e.token){
console.log(e);
if(e.errors.consent){
error(elements[6],e.errors.consent._errors[0].message);
}else if(e.errors.password){
error(elements[3],"Password: "+e.errors.password._errors[0].message);
}else if(e.errors.username){
error(elements[2],"Username: "+e.errors.username._errors[0].message);
}else if(e.errors.email){
error(elements[1],"Email: "+e.errors.email._errors[0].message);
}else if(e.errors.date_of_birth){
error(elements[5],"Date of Birth: "+e.errors.date_of_birth._errors[0].message);
}else{
document.getElementById("wrong").textContent=e.errors[Object.keys(e.errors)[0]]._errors[0].message;
}
}else{
adduser({serverurls:JSON.parse(localStorage.getItem("instanceinfo")),email:email,token:e.token}).username=username;
localStorage.setItem("token",e.token);
const redir=new URLSearchParams(window.location.search).get("goback");
if(redir){
window.location.href = redir;
}else{
window.location.href = '/channels/@me';
}
}
})
})
//document.getElementById("wrong").textContent=h;
// console.log(h);
capty.setAttribute("data-sitekey", e.captcha_sitekey);
const script=document.createElement("script");
script.src="https://js.hcaptcha.com/1/api.js";
capt.append(script);
capt.append(capty);
}else{
eval("hcaptcha.reset()");
}
return;
}
if(!e.token){
console.log(e);
if(e.errors.consent){
error(elements[6],e.errors.consent._errors[0].message);
}else if(e.errors.password){
error(elements[3],"Password: "+e.errors.password._errors[0].message);
}else if(e.errors.username){
error(elements[2],"Username: "+e.errors.username._errors[0].message);
}else if(e.errors.email){
error(elements[1],"Email: "+e.errors.email._errors[0].message);
}else if(e.errors.date_of_birth){
error(elements[5],"Date of Birth: "+e.errors.date_of_birth._errors[0].message);
}else{
document.getElementById("wrong").textContent=e.errors[Object.keys(e.errors)[0]]._errors[0].message;
}
}else{
adduser({serverurls: JSON.parse(localStorage.getItem("instanceinfo")),email,token: e.token}).username=username;
localStorage.setItem("token",e.token);
const redir=new URLSearchParams(window.location.search).get("goback");
if(redir){
window.location.href = redir;
}else{
window.location.href = "/channels/@me";
}
}
});
});
//document.getElementById("wrong").textContent=h;
// console.log(h);
}
function error(e:HTMLFormElement,message:string){
const p=e.parentElement;
let element=p.getElementsByClassName("suberror")[0] as HTMLElement;
if(!element){
const div=document.createElement("div");
div.classList.add("suberror","suberrora");
p.append(div);
element=div;
}else{
element.classList.remove("suberror");
setTimeout(_=>{element.classList.add("suberror")},100);
}
element.textContent=message;
const p=e.parentElement;
let element=p.getElementsByClassName("suberror")[0] as HTMLElement;
if(!element){
const div=document.createElement("div");
div.classList.add("suberror","suberrora");
p.append(div);
element=div;
}else{
element.classList.remove("suberror");
setTimeout(_=>{
element.classList.add("suberror");
},100);
}
element.textContent=message;
}
let TOSa=document.getElementById("TOSa");
async function tosLogic(){
const apiurl=new URL(JSON.parse(localStorage.getItem("instanceinfo")).api)
const tosPage=(await (await fetch(apiurl.toString()+"/ping")).json()).instance.tosPage;
if(tosPage){
document.getElementById("TOSbox").innerHTML="I agree to the <a href=\"\" id=\"TOSa\">Terms of Service</a>:";
TOSa=document.getElementById("TOSa");
(TOSa as HTMLAnchorElement).href=tosPage;
}else{
document.getElementById("TOSbox").textContent="This instance has no Terms of Service, accept ToS anyways:";
TOSa=null;
}
console.log(tosPage);
const apiurl=new URL(JSON.parse(localStorage.getItem("instanceinfo")).api);
const tosPage=(await (await fetch(apiurl.toString()+"/ping")).json()).instance.tosPage;
if(tosPage){
document.getElementById("TOSbox").innerHTML="I agree to the <a href=\"\" id=\"TOSa\">Terms of Service</a>:";
TOSa=document.getElementById("TOSa");
(TOSa as HTMLAnchorElement).href=tosPage;
}else{
document.getElementById("TOSbox").textContent="This instance has no Terms of Service, accept ToS anyways:";
TOSa=null;
}
console.log(tosPage);
}
tosLogic();

View file

@ -1,162 +1,170 @@
import {Permissions} from "./permissions.js";
import {Localuser} from "./localuser.js";
import {Guild} from "./guild.js";
import { SnowFlake } from "./snowflake.js";
import { rolesjson } from "./jsontypes.js";
import{Permissions}from"./permissions.js";
import{Localuser}from"./localuser.js";
import{Guild}from"./guild.js";
import{ SnowFlake }from"./snowflake.js";
import{ rolesjson }from"./jsontypes.js";
class Role{
permissions:Permissions;
owner:Guild;
color:number;
readonly snowflake:SnowFlake<Role>;
name:string;
info:Guild["info"];
hoist:boolean;
icon:string;
mentionable:boolean;
unicode_emoji:string;
headers:Guild["headers"];
get id(){
return this.snowflake.id;
}
constructor(json:rolesjson, owner:Guild){
this.headers=owner.headers;
this.info=owner.info;
for(const thing of Object.keys(json)){
if(thing==="id"){
this.snowflake=new SnowFlake(json.id,this);
continue;
}
this[thing]=json[thing];
}
this.permissions=new Permissions(json.permissions);
this.owner=owner;
}
get guild():Guild{
return this.owner;
}
get localuser():Localuser{
return this.guild.localuser;
}
getColor():string|null{
if(this.color===0){return null};
return `#${this.color.toString(16)}`;
}
permissions:Permissions;
owner:Guild;
color:number;
readonly snowflake:SnowFlake<Role>;
name:string;
info:Guild["info"];
hoist:boolean;
icon:string;
mentionable:boolean;
unicode_emoji:string;
headers:Guild["headers"];
get id(){
return this.snowflake.id;
}
constructor(json:rolesjson, owner:Guild){
this.headers=owner.headers;
this.info=owner.info;
for(const thing of Object.keys(json)){
if(thing==="id"){
this.snowflake=new SnowFlake(json.id,this);
continue;
}
this[thing]=json[thing];
}
this.permissions=new Permissions(json.permissions);
this.owner=owner;
}
get guild():Guild{
return this.owner;
}
get localuser():Localuser{
return this.guild.localuser;
}
getColor():string|null{
if(this.color===0){
return null;
}
return`#${this.color.toString(16)}`;
}
}
export {Role};
import {Options} from "./settings.js";
export{Role};
import{Options}from"./settings.js";
class PermissionToggle implements OptionsElement<number>{
readonly rolejson:{name:string,readableName:string,description:string};
permissions:Permissions;
owner:Options;
value:number;
constructor(roleJSON:PermissionToggle["rolejson"],permissions:Permissions,owner:Options){
this.rolejson=roleJSON;
this.permissions=permissions;
this.owner=owner;
}
watchForChange(){};
generateHTML():HTMLElement{
const div=document.createElement("div");
div.classList.add("setting");
const name=document.createElement("span");
name.textContent=this.rolejson.readableName;
name.classList.add("settingsname");
div.append(name);
readonly rolejson:{name:string,readableName:string,description:string};
permissions:Permissions;
owner:Options;
value:number;
constructor(roleJSON:PermissionToggle["rolejson"],permissions:Permissions,owner:Options){
this.rolejson=roleJSON;
this.permissions=permissions;
this.owner=owner;
}
watchForChange(){}
generateHTML():HTMLElement{
const div=document.createElement("div");
div.classList.add("setting");
const name=document.createElement("span");
name.textContent=this.rolejson.readableName;
name.classList.add("settingsname");
div.append(name);
div.append(this.generateCheckbox());
const p=document.createElement("p");
p.textContent=this.rolejson.description;
div.appendChild(p);
return div;
}
generateCheckbox():HTMLElement{
const div=document.createElement("div");
div.classList.add("tritoggle");
const state=this.permissions.getPermission(this.rolejson.name);
div.append(this.generateCheckbox());
const p=document.createElement("p");
p.textContent=this.rolejson.description;
div.appendChild(p);
return div;
}
generateCheckbox():HTMLElement{
const div=document.createElement("div");
div.classList.add("tritoggle");
const state=this.permissions.getPermission(this.rolejson.name);
const on=document.createElement("input");
on.type="radio";
on.name=this.rolejson.name;
div.append(on);
if(state===1){on.checked=true;};
on.onclick=_=>{
this.permissions.setPermission(this.rolejson.name,1);
this.owner.changed();
}
const on=document.createElement("input");
on.type="radio";
on.name=this.rolejson.name;
div.append(on);
if(state===1){
on.checked=true;
}
on.onclick=_=>{
this.permissions.setPermission(this.rolejson.name,1);
this.owner.changed();
};
const no=document.createElement("input");
no.type="radio";
no.name=this.rolejson.name;
div.append(no);
if(state===0){no.checked=true;};
no.onclick=_=>{
this.permissions.setPermission(this.rolejson.name,0);
this.owner.changed();
}
if(this.permissions.hasDeny){
const off=document.createElement("input");
off.type="radio";
off.name=this.rolejson.name;
div.append(off);
if(state===-1){off.checked=true;};
off.onclick=_=>{
this.permissions.setPermission(this.rolejson.name,-1);
this.owner.changed();
}
}
return div;
}
submit(){
const no=document.createElement("input");
no.type="radio";
no.name=this.rolejson.name;
div.append(no);
if(state===0){
no.checked=true;
}
no.onclick=_=>{
this.permissions.setPermission(this.rolejson.name,0);
this.owner.changed();
};
if(this.permissions.hasDeny){
const off=document.createElement("input");
off.type="radio";
off.name=this.rolejson.name;
div.append(off);
if(state===-1){
off.checked=true;
}
off.onclick=_=>{
this.permissions.setPermission(this.rolejson.name,-1);
this.owner.changed();
};
}
return div;
}
submit(){
}
}
}
import { OptionsElement,Buttons } from "./settings.js";
import{ OptionsElement,Buttons }from"./settings.js";
class RoleList extends Buttons{
readonly permissions:[SnowFlake<Role>,Permissions][];
permission:Permissions;
readonly guild:Guild;
readonly channel:boolean;
readonly declare buttons:[string,string][];
readonly options:Options;
onchange:Function;
curid:string;
constructor(permissions:[SnowFlake<Role>,Permissions][],guild:Guild,onchange:Function,channel=false){
super("Roles");
this.guild=guild;
this.permissions=permissions;
this.channel=channel;
this.onchange=onchange;
const options=new Options("",this);
if(channel){
this.permission=new Permissions("0","0");
}else{
this.permission=new Permissions("0");
}
for(const thing of Permissions.info){
options.options.push(new PermissionToggle(thing,this.permission,options));
}
for(const i of permissions){
console.log(i);
this.buttons.push([i[0].getObject().name,i[0].id])//
}
this.options=options;
}
handleString(str:string):HTMLElement{
this.curid=str;
const arr=this.permissions.find(_=>_[0].id===str);
if(arr){
const perm=arr[1];
this.permission.deny=perm.deny;
this.permission.allow=perm.allow;
this.options.name=SnowFlake.getSnowFlakeFromID(str,Role).getObject().name;
this.options.haschanged=false;
}
return this.options.generateHTML();
}
save(){
this.onchange(this.curid,this.permission);
}
readonly permissions:[SnowFlake<Role>,Permissions][];
permission:Permissions;
readonly guild:Guild;
readonly channel:boolean;
readonly declare buttons:[string,string][];
readonly options:Options;
onchange:Function;
curid:string;
constructor(permissions:[SnowFlake<Role>,Permissions][],guild:Guild,onchange:Function,channel=false){
super("Roles");
this.guild=guild;
this.permissions=permissions;
this.channel=channel;
this.onchange=onchange;
const options=new Options("",this);
if(channel){
this.permission=new Permissions("0","0");
}else{
this.permission=new Permissions("0");
}
for(const thing of Permissions.info){
options.options.push(new PermissionToggle(thing,this.permission,options));
}
for(const i of permissions){
console.log(i);
this.buttons.push([i[0].getObject().name,i[0].id]);//
}
this.options=options;
}
handleString(str:string):HTMLElement{
this.curid=str;
const arr=this.permissions.find(_=>_[0].id===str);
if(arr){
const perm=arr[1];
this.permission.deny=perm.deny;
this.permission.allow=perm.allow;
this.options.name=SnowFlake.getSnowFlakeFromID(str,Role).getObject().name;
this.options.haschanged=false;
}
return this.options.generateHTML();
}
save(){
this.onchange(this.curid,this.permission);
}
}
export {RoleList}
export{RoleList};

View file

@ -1,87 +1,93 @@
function deleteoldcache(){
caches.delete("cache");
console.log("this ran :P")
caches.delete("cache");
console.log("this ran :P");
}
async function putInCache(request, response){
console.log(request,response);
const cache = await caches.open('cache');
console.log("Grabbed")
try{
console.log(await cache.put(request, response));
}catch(error){
console.error(error);
}
};
console.log(request,response);
const cache = await caches.open("cache");
console.log("Grabbed");
try{
console.log(await cache.put(request, response));
}catch(error){
console.error(error);
}
}
console.log("test");
let lastcache
self.addEventListener("activate", async (event) => {
console.log("test2");
checkCache();
})
let lastcache;
self.addEventListener("activate", async event=>{
console.log("test2");
checkCache();
});
async function checkCache(){
if(checkedrecently){
return;
}
const promise=await caches.match("/getupdates");
if(promise){
lastcache= await promise.text();
}
console.log(lastcache);
fetch("/getupdates").then(async data=>{
const text=await data.clone().text();
console.log(text,lastcache)
if(lastcache!==text){
deleteoldcache();
putInCache("/getupdates",data.clone());
}
checkedrecently=true;
setTimeout(_=>{checkedrecently=false},1000*60*30);
})
if(checkedrecently){
return;
}
const promise=await caches.match("/getupdates");
if(promise){
lastcache= await promise.text();
}
console.log(lastcache);
fetch("/getupdates").then(async data=>{
const text=await data.clone().text();
console.log(text,lastcache);
if(lastcache!==text){
deleteoldcache();
putInCache("/getupdates",data.clone());
}
checkedrecently=true;
setTimeout(_=>{
checkedrecently=false;
},1000*60*30);
});
}
var checkedrecently=false;
function samedomain(url){
return new URL(url).origin===self.origin;
return new URL(url).origin===self.origin;
}
function isindexhtml(url){
console.log(url);
if(new URL(url).pathname.startsWith("/channels")){
return true;
}
return false;
console.log(url);
if(new URL(url).pathname.startsWith("/channels")){
return true;
}
return false;
}
async function getfile(event){
checkCache();
if(!samedomain(event.request.url)){
return await fetch(event.request.clone());
}
const responseFromCache = await caches.match(event.request.url);
console.log(responseFromCache,caches);
if (responseFromCache) {
console.log("cache hit")
return responseFromCache;
}
if(isindexhtml(event.request.url)){
console.log("is index.html")
const responseFromCache = await caches.match("/index.html");
if (responseFromCache) {
console.log("cache hit")
return responseFromCache;
}
const responseFromNetwork = await fetch("/index.html");
await putInCache("/index.html",responseFromNetwork.clone());
return responseFromNetwork;
}
const responseFromNetwork = await fetch(event.request.clone());
console.log(event.request.clone());
await putInCache(event.request.clone(),responseFromNetwork.clone());
try{
return responseFromNetwork;
}catch(e){console.error(e)}
checkCache();
if(!samedomain(event.request.url)){
return await fetch(event.request.clone());
}
const responseFromCache = await caches.match(event.request.url);
console.log(responseFromCache,caches);
if(responseFromCache){
console.log("cache hit");
return responseFromCache;
}
if(isindexhtml(event.request.url)){
console.log("is index.html");
const responseFromCache = await caches.match("/index.html");
if(responseFromCache){
console.log("cache hit");
return responseFromCache;
}
const responseFromNetwork = await fetch("/index.html");
await putInCache("/index.html",responseFromNetwork.clone());
return responseFromNetwork;
}
const responseFromNetwork = await fetch(event.request.clone());
console.log(event.request.clone());
await putInCache(event.request.clone(),responseFromNetwork.clone());
try{
return responseFromNetwork;
}catch(e){
console.error(e);
}
}
self.addEventListener('fetch', (event:any) => {
try{
event.respondWith(getfile(event));
}catch(e){console.error(e)}
})
self.addEventListener("fetch", (event:any)=>{
try{
event.respondWith(getfile(event));
}catch(e){
console.error(e);
}
});

File diff suppressed because it is too large Load diff

View file

@ -1,95 +1,95 @@
class SnowFlake<x extends WeakKey>{
public readonly id:string;
private static SnowFlakes:Map<any,Map<string,WeakRef<SnowFlake<any>>>>=new Map();
private static readonly FinalizationRegistry=new FinalizationRegistry((a:[string,WeakKey])=>{
SnowFlake.SnowFlakes.get(a[1]).delete(a[0]);
});
private obj:x;
constructor(id:string,obj:x){
if(!obj){
this.id=id;
return;
}
if(!SnowFlake.SnowFlakes.get(obj.constructor)){
SnowFlake.SnowFlakes.set(obj.constructor,new Map());
}
if(SnowFlake.SnowFlakes.get(obj.constructor).get(id)){
const snowflake=SnowFlake.SnowFlakes.get(obj.constructor).get(id).deref();
if(snowflake){
snowflake.obj=obj;
return snowflake;
}else{
SnowFlake.SnowFlakes.get(obj.constructor).delete(id);
}
}
this.id=id;
SnowFlake.SnowFlakes.get(obj.constructor).set(id,new WeakRef(this));
SnowFlake.FinalizationRegistry.register(this,[id,obj.constructor]);
this.obj=obj;
}
static clear(){//this is kinda a temp solution, it should be fixed, though its not that easy to do so
this.SnowFlakes=new Map();
}
/**
public readonly id:string;
private static SnowFlakes:Map<any,Map<string,WeakRef<SnowFlake<any>>>>=new Map();
private static readonly FinalizationRegistry=new FinalizationRegistry((a:[string,WeakKey])=>{
SnowFlake.SnowFlakes.get(a[1]).delete(a[0]);
});
private obj:x;
constructor(id:string,obj:x){
if(!obj){
this.id=id;
return;
}
if(!SnowFlake.SnowFlakes.get(obj.constructor)){
SnowFlake.SnowFlakes.set(obj.constructor,new Map());
}
if(SnowFlake.SnowFlakes.get(obj.constructor).get(id)){
const snowflake=SnowFlake.SnowFlakes.get(obj.constructor).get(id).deref();
if(snowflake){
snowflake.obj=obj;
return snowflake;
}else{
SnowFlake.SnowFlakes.get(obj.constructor).delete(id);
}
}
this.id=id;
SnowFlake.SnowFlakes.get(obj.constructor).set(id,new WeakRef(this));
SnowFlake.FinalizationRegistry.register(this,[id,obj.constructor]);
this.obj=obj;
}
static clear(){//this is kinda a temp solution, it should be fixed, though its not that easy to do so
this.SnowFlakes=new Map();
}
/**
* Just to clarify bc TS, it returns a SnowFlake\<type> which is what you entered with the type parameter
* @deprecated
**/
static getSnowFlakeFromID<T extends {}>(id:string,type: abstract new(...args: never) => T): SnowFlake<T>{
if(!SnowFlake.SnowFlakes.get(type)){
SnowFlake.SnowFlakes.set(type,new Map());
}
const snowflake=SnowFlake.SnowFlakes.get(type).get(id);
if(snowflake){
const obj=snowflake.deref();
if(obj){
return obj;
}else{
SnowFlake.SnowFlakes.get(type).delete(id);
}
}
{
const snowflake=new SnowFlake(id,undefined);
static getSnowFlakeFromID<T extends {}>(id:string,type: abstract new(...args: never) => T): SnowFlake<T>{
if(!SnowFlake.SnowFlakes.get(type)){
SnowFlake.SnowFlakes.set(type,new Map());
}
const snowflake=SnowFlake.SnowFlakes.get(type).get(id);
if(snowflake){
const obj=snowflake.deref();
if(obj){
return obj;
}else{
SnowFlake.SnowFlakes.get(type).delete(id);
}
}
{
const snowflake=new SnowFlake(id,undefined);
SnowFlake.SnowFlakes.get(type).set(id,new WeakRef(snowflake));
SnowFlake.FinalizationRegistry.register(this,[id,type]);
SnowFlake.SnowFlakes.get(type).set(id,new WeakRef(snowflake));
SnowFlake.FinalizationRegistry.register(this,[id,type]);
return snowflake;
}
}
/**
return snowflake;
}
}
/**
* @deprecated
*
*
*/
static hasSnowFlakeFromID(id:string,type:any){
if(!SnowFlake.SnowFlakes.get(type)){
return false;
}
const flake=SnowFlake.SnowFlakes.get(type).get(id);
if(flake){
const flake2=flake.deref()?.getObject();
if(flake2){
return true;
}else{
return false;
}
}else{
return false;
}
}
getUnixTime():number{
try{
return Number((BigInt(this.id)>>22n)+1420070400000n);
}catch{
console.error(`The ID is corrupted, it's ${this.id} when it should be some number.`)
return 0;
}
}
toString(){
return this.id;
}
getObject():x{
return this.obj;
}
static hasSnowFlakeFromID(id:string,type:any){
if(!SnowFlake.SnowFlakes.get(type)){
return false;
}
const flake=SnowFlake.SnowFlakes.get(type).get(id);
if(flake){
const flake2=flake.deref()?.getObject();
if(flake2){
return true;
}else{
return false;
}
}else{
return false;
}
}
getUnixTime():number{
try{
return Number((BigInt(this.id)>>22n)+1420070400000n);
}catch{
console.error(`The ID is corrupted, it's ${this.id} when it should be some number.`);
return 0;
}
}
toString(){
return this.id;
}
getObject():x{
return this.obj;
}
}
export {SnowFlake};
export{SnowFlake};

View file

@ -1,431 +1,432 @@
//const usercache={};
import {Member} from "./member.js";
import {MarkDown} from "./markdown.js";
import {Contextmenu} from "./contextmenu.js";
import {Localuser} from "./localuser.js";
import {Guild} from "./guild.js";
import { SnowFlake } from "./snowflake.js";
import { presencejson, userjson } from "./jsontypes.js";
import{Member}from"./member.js";
import{MarkDown}from"./markdown.js";
import{Contextmenu}from"./contextmenu.js";
import{Localuser}from"./localuser.js";
import{Guild}from"./guild.js";
import{ SnowFlake }from"./snowflake.js";
import{ presencejson, userjson }from"./jsontypes.js";
class User{
owner:Localuser;
hypotheticalpfp:boolean;
snowflake:SnowFlake<User>;
avatar:string|null;
username:string;
nickname:string|null=null;
relationshipType:0|1|2|3|4=0;
bio:MarkDown;
discriminator:string;
pronouns:string;
bot:boolean;
public_flags: number;
accent_color: number;
banner: string|undefined;
hypotheticalbanner:boolean;
premium_since: string;
premium_type: number;
theme_colors: string;
badge_ids: string[];
members: WeakMap<Guild, Member|undefined|Promise<Member|undefined>>=new WeakMap();
private status:string;
clone(){
return new User({
username:this.username,
id:this.id+"#clone",
public_flags:this.public_flags,
discriminator:this.discriminator,
avatar:this.avatar,
accent_color:this.accent_color,
banner:this.banner,
bio:this.bio.rawString,
premium_since:this.premium_since,
premium_type:this.premium_type,
bot:this.bot,
theme_colors:this.theme_colors,
pronouns:this.pronouns,
badge_ids:this.badge_ids
},this.owner)
}
public getPresence(presence:presencejson|undefined){
if(presence){
this.setstatus(presence.status);
}else{
this.setstatus("offline");
}
}
setstatus(status:string){
this.status=status;
}
async getStatus(){
if(this.status){
return this.status;
}else{
return "offline";
}
}
get id(){
return this.snowflake.id;
}
static contextmenu=new Contextmenu<User,Member|undefined>("User Menu");
static setUpContextMenu(){
this.contextmenu.addbutton("Copy user id",function(this:User){
navigator.clipboard.writeText(this.id);
});
this.contextmenu.addbutton("Message user",function(this:User){
fetch(this.info.api+"/users/@me/channels",
{method:"POST",
body:JSON.stringify({"recipients":[this.id]}),
headers: this.localuser.headers
});
});
this.contextmenu.addbutton("Block user",function(this:User){
this.block();
},null,function(){
return this.relationshipType!==2
});
owner:Localuser;
hypotheticalpfp:boolean;
snowflake:SnowFlake<User>;
avatar:string|null;
username:string;
nickname:string|null=null;
relationshipType:0|1|2|3|4=0;
bio:MarkDown;
discriminator:string;
pronouns:string;
bot:boolean;
public_flags: number;
accent_color: number;
banner: string|undefined;
hypotheticalbanner:boolean;
premium_since: string;
premium_type: number;
theme_colors: string;
badge_ids: string[];
members: WeakMap<Guild, Member|undefined|Promise<Member|undefined>>=new WeakMap();
private status:string;
clone(){
return new User({
username: this.username,
id: this.id+"#clone",
public_flags: this.public_flags,
discriminator: this.discriminator,
avatar: this.avatar,
accent_color: this.accent_color,
banner: this.banner,
bio: this.bio.rawString,
premium_since: this.premium_since,
premium_type: this.premium_type,
bot: this.bot,
theme_colors: this.theme_colors,
pronouns: this.pronouns,
badge_ids: this.badge_ids
},this.owner);
}
public getPresence(presence:presencejson|undefined){
if(presence){
this.setstatus(presence.status);
}else{
this.setstatus("offline");
}
}
setstatus(status:string){
this.status=status;
}
async getStatus(){
if(this.status){
return this.status;
}else{
return"offline";
}
}
get id(){
return this.snowflake.id;
}
static contextmenu=new Contextmenu<User,Member|undefined>("User Menu");
static setUpContextMenu(){
this.contextmenu.addbutton("Copy user id",function(this:User){
navigator.clipboard.writeText(this.id);
});
this.contextmenu.addbutton("Message user",function(this:User){
fetch(this.info.api+"/users/@me/channels",
{method: "POST",
body: JSON.stringify({recipients: [this.id]}),
headers: this.localuser.headers
});
});
this.contextmenu.addbutton("Block user",function(this:User){
this.block();
},null,function(){
return this.relationshipType!==2;
});
this.contextmenu.addbutton("Unblock user",function(this:User){
this.unblock();
},null,function(){
return this.relationshipType===2
});
this.contextmenu.addbutton("Friend request",function(this:User){
fetch(`${this.info.api}/users/@me/relationships/${this.id}`,{
method:"PUT",
headers:this.owner.headers,
body:JSON.stringify({
type:1
})
})
});
this.contextmenu.addbutton("Kick member",function(this:User,member:Member){
member.kick();
},null,function(member){
if(!member) return false;
const us=member.guild.member;
if(member.id===us.id){
return false;
}
if(member.id===member.guild.properties.owner_id){
return false;
}
return (us.hasPermission("KICK_MEMBERS"))||false;
});
this.contextmenu.addbutton("Ban member",function(this:User,member:Member){
member.ban();
},null,function(member){
if(!member) return false;
const us=member.guild.member;
if(member.id===us.id){
return false;
}
if(member.id===member.guild.properties.owner_id){
return false;
}
return (us.hasPermission("BAN_MEMBERS"))||false;
});
}
static checkuser(user:User|userjson,owner:Localuser):User{
if(owner.userMap.has(user.id)){
return owner.userMap.get(user.id) as User;
}else{
const tempuser=new User(user as userjson,owner,true)
owner.userMap.set(user.id,tempuser);
return tempuser;
}
}
get info(){
return this.owner.info;
}
get localuser(){
return this.owner;
}
constructor(userjson:userjson,owner:Localuser,dontclone=false){
this.owner=owner;
if(!owner){console.error("missing localuser")}
if(dontclone){
for(const thing of Object.keys(userjson)){
if(thing==="bio"){
this.bio=new MarkDown(userjson[thing],this.localuser);
continue;
}
if(thing === "id"){
this.snowflake=new SnowFlake(userjson[thing],this);
continue;
}
this[thing]=userjson[thing];
}
this.hypotheticalpfp=false;
}else{
return User.checkuser(userjson,owner);
}
}
async resolvemember(guild:Guild){
return await Member.resolveMember(this,guild);
}
this.contextmenu.addbutton("Unblock user",function(this:User){
this.unblock();
},null,function(){
return this.relationshipType===2;
});
this.contextmenu.addbutton("Friend request",function(this:User){
fetch(`${this.info.api}/users/@me/relationships/${this.id}`,{
method: "PUT",
headers: this.owner.headers,
body: JSON.stringify({
type: 1
})
});
});
this.contextmenu.addbutton("Kick member",function(this:User,member:Member){
member.kick();
},null,member=>{
if(!member)return false;
const us=member.guild.member;
if(member.id===us.id){
return false;
}
if(member.id===member.guild.properties.owner_id){
return false;
}
return(us.hasPermission("KICK_MEMBERS"))||false;
});
this.contextmenu.addbutton("Ban member",function(this:User,member:Member){
member.ban();
},null,member=>{
if(!member)return false;
const us=member.guild.member;
if(member.id===us.id){
return false;
}
if(member.id===member.guild.properties.owner_id){
return false;
}
return(us.hasPermission("BAN_MEMBERS"))||false;
});
}
static checkuser(user:User|userjson,owner:Localuser):User{
if(owner.userMap.has(user.id)){
return owner.userMap.get(user.id) as User;
}else{
const tempuser=new User(user as userjson,owner,true);
owner.userMap.set(user.id,tempuser);
return tempuser;
}
}
get info(){
return this.owner.info;
}
get localuser(){
return this.owner;
}
constructor(userjson:userjson,owner:Localuser,dontclone=false){
this.owner=owner;
if(!owner){
console.error("missing localuser");
}
if(dontclone){
for(const thing of Object.keys(userjson)){
if(thing==="bio"){
this.bio=new MarkDown(userjson[thing],this.localuser);
continue;
}
if(thing === "id"){
this.snowflake=new SnowFlake(userjson[thing],this);
continue;
}
this[thing]=userjson[thing];
}
this.hypotheticalpfp=false;
}else{
return User.checkuser(userjson,owner);
}
}
async resolvemember(guild:Guild){
return await Member.resolveMember(this,guild);
}
async getUserProfile(){
return (await fetch(`${this.info.api}/users/${this.id.replace("#clone","")}/profile?with_mutual_guilds=true&with_mutual_friends=true`,{
headers:this.localuser.headers
})).json()
}
resolving:false|Promise<any>=false;
async getBadge(id:string){
if(this.localuser.badges.has(id)){
return this.localuser.badges.get(id);
}else{
if(this.resolving)
{
await this.resolving;
return this.localuser.badges.get(id);
}
async getUserProfile(){
return(await fetch(`${this.info.api}/users/${this.id.replace("#clone","")}/profile?with_mutual_guilds=true&with_mutual_friends=true`,{
headers: this.localuser.headers
})).json();
}
resolving:false|Promise<any>=false;
async getBadge(id:string){
if(this.localuser.badges.has(id)){
return this.localuser.badges.get(id);
}else{
if(this.resolving){
await this.resolving;
return this.localuser.badges.get(id);
}
const prom=await this.getUserProfile();
this.resolving=prom;
const badges=prom.badges;
this.resolving=false;
for(const thing of badges){
this.localuser.badges.set(thing.id,thing);
}
return this.localuser.badges.get(id);
}
}
buildpfp(){
const pfp=document.createElement('img');
pfp.loading="lazy";
pfp.src=this.getpfpsrc();
pfp.classList.add("pfp");
pfp.classList.add("userid:"+this.id);
return pfp;
}
async buildstatuspfp(){
const div=document.createElement("div");
div.style.position="relative";
const pfp=this.buildpfp();
div.append(pfp);
{
const status=document.createElement("div");
status.classList.add("statusDiv");
switch(await this.getStatus()){
case "offline":
status.classList.add("offlinestatus");
break;
case "online":
default:
status.classList.add("onlinestatus");
break;
}
div.append(status);
}
return div;
}
userupdate(json:userjson){
if(json.avatar!==this.avatar){
console.log
this.changepfp(json.avatar);
}
}
bind(html:HTMLElement,guild:Guild|null=null,error=true){
if(guild&&guild.id!=="@me"){
Member.resolveMember(this,guild).then(_=>{
User.contextmenu.bindContextmenu(html,this,_);
if(_===undefined&&error){
const error=document.createElement("span");
error.textContent="!";
error.classList.add("membererror");
html.after(error);
return;
}
if(_){
_.bind(html);
}
}).catch(_=>{
console.log(_)
});
}
if(guild){
this.profileclick(html,guild);
}else{
this.profileclick(html);
}
}
static async resolve(id:string,localuser:Localuser){
const json=await fetch(localuser.info.api.toString()+"/users/"+id+"/profile",
{headers:localuser.headers}
).then(_=>_.json());
return new User(json,localuser);
}
changepfp(update:string|null){
this.avatar=update;
this.hypotheticalpfp=false;
const src=this.getpfpsrc();
console.log(src)
for(const thing of document.getElementsByClassName("userid:"+this.id)){
(thing as HTMLImageElement).src=src;
}
}
block(){
fetch(`${this.info.api}/users/@me/relationships/${this.id}`,{
method:"PUT",
headers:this.owner.headers,
body:JSON.stringify({
type:2
})
})
this.relationshipType=2;
const channel=this.localuser.channelfocus;
if(channel){
for(const thing of channel.messages){
thing[1].generateMessage();
}
}
}
unblock(){
fetch(`${this.info.api}/users/@me/relationships/${this.id}`,{
method:"DELETE",
headers:this.owner.headers,
})
this.relationshipType=0;
const channel=this.localuser.channelfocus;
if(channel){
for(const thing of channel.messages){
thing[1].generateMessage();
}
}
}
getpfpsrc(){
if(this.hypotheticalpfp&&this.avatar){
return this.avatar;
}
if(this.avatar!=null){
return this.info.cdn+"/avatars/"+this.id.replace("#clone","")+"/"+this.avatar+".png";
}else{
const int=new Number((BigInt(this.id.replace("#clone","")) >> 22n) % 6n);
return this.info.cdn+`/embed/avatars/${int}.png`;
}
}
createjankpromises(){
new Promise(_=>{})
}
async buildprofile(x:number,y:number,guild:Guild|null=null){
if(Contextmenu.currentmenu!=""){
Contextmenu.currentmenu.remove();
}
const prom=await this.getUserProfile();
this.resolving=prom;
const badges=prom.badges;
this.resolving=false;
for(const thing of badges){
this.localuser.badges.set(thing.id,thing);
}
return this.localuser.badges.get(id);
}
}
buildpfp(){
const pfp=document.createElement("img");
pfp.loading="lazy";
pfp.src=this.getpfpsrc();
pfp.classList.add("pfp");
pfp.classList.add("userid:"+this.id);
return pfp;
}
async buildstatuspfp(){
const div=document.createElement("div");
div.style.position="relative";
const pfp=this.buildpfp();
div.append(pfp);
{
const status=document.createElement("div");
status.classList.add("statusDiv");
switch(await this.getStatus()){
case"offline":
status.classList.add("offlinestatus");
break;
case"online":
default:
status.classList.add("onlinestatus");
break;
}
div.append(status);
}
return div;
}
userupdate(json:userjson){
if(json.avatar!==this.avatar){
console.log;
this.changepfp(json.avatar);
}
}
bind(html:HTMLElement,guild:Guild|null=null,error=true){
if(guild&&guild.id!=="@me"){
Member.resolveMember(this,guild).then(_=>{
User.contextmenu.bindContextmenu(html,this,_);
if(_===undefined&&error){
const error=document.createElement("span");
error.textContent="!";
error.classList.add("membererror");
html.after(error);
return;
}
if(_){
_.bind(html);
}
}).catch(_=>{
console.log(_);
});
}
if(guild){
this.profileclick(html,guild);
}else{
this.profileclick(html);
}
}
static async resolve(id:string,localuser:Localuser){
const json=await fetch(localuser.info.api.toString()+"/users/"+id+"/profile",
{headers: localuser.headers}
).then(_=>_.json());
return new User(json,localuser);
}
changepfp(update:string|null){
this.avatar=update;
this.hypotheticalpfp=false;
const src=this.getpfpsrc();
console.log(src);
for(const thing of document.getElementsByClassName("userid:"+this.id)){
(thing as HTMLImageElement).src=src;
}
}
block(){
fetch(`${this.info.api}/users/@me/relationships/${this.id}`,{
method: "PUT",
headers: this.owner.headers,
body: JSON.stringify({
type: 2
})
});
this.relationshipType=2;
const channel=this.localuser.channelfocus;
if(channel){
for(const thing of channel.messages){
thing[1].generateMessage();
}
}
}
unblock(){
fetch(`${this.info.api}/users/@me/relationships/${this.id}`,{
method: "DELETE",
headers: this.owner.headers,
});
this.relationshipType=0;
const channel=this.localuser.channelfocus;
if(channel){
for(const thing of channel.messages){
thing[1].generateMessage();
}
}
}
getpfpsrc(){
if(this.hypotheticalpfp&&this.avatar){
return this.avatar;
}
if(this.avatar!=null){
return this.info.cdn+"/avatars/"+this.id.replace("#clone","")+"/"+this.avatar+".png";
}else{
const int=new Number((BigInt(this.id.replace("#clone","")) >> 22n) % 6n);
return this.info.cdn+`/embed/avatars/${int}.png`;
}
}
createjankpromises(){
new Promise(_=>{});
}
async buildprofile(x:number,y:number,guild:Guild|null=null){
if(Contextmenu.currentmenu!=""){
Contextmenu.currentmenu.remove();
}
const div=document.createElement("div");
const div=document.createElement("div");
if(this.accent_color){
div.style.setProperty("--accent_color","#"+this.accent_color.toString(16).padStart(6,"0"));
}else{
div.style.setProperty("--accent_color","transparent");
}
if(this.banner){
const banner=document.createElement("img")
let src:string;
if(!this.hypotheticalbanner){
src=this.info.cdn+"/avatars/"+this.id.replace("#clone","")+"/"+this.banner+".png";
}else{
src=this.banner;
}
console.log(src,this.banner);
banner.src=src;
banner.classList.add("banner");
div.append(banner);
}
if(x!==-1){
div.style.left=x+"px";
div.style.top=y+"px";
div.classList.add("profile","flexttb");
}else{
this.setstatus("online");
div.classList.add("hypoprofile","flexttb");
}
const badgediv=document.createElement("div");
badgediv.classList.add("badges");
(async ()=>{
if(!this.badge_ids) return;
for(const id of this.badge_ids){
const badgejson=await this.getBadge(id);
if(badgejson){
const badge=document.createElement(badgejson.link?"a":"div");
badge.classList.add("badge")
const img=document.createElement("img");
img.src=badgejson.icon;
badge.append(img);
const span=document.createElement("span");
span.textContent=badgejson.description;
badge.append(span);
if(badge instanceof HTMLAnchorElement){
badge.href=badgejson.link;
}
badgediv.append(badge);
}
}
})()
{
const pfp=await this.buildstatuspfp();
div.appendChild(pfp);
}
{
const userbody=document.createElement("div");
userbody.classList.add("infosection");
div.appendChild(userbody);
const usernamehtml=document.createElement("h2");
usernamehtml.textContent=this.username;
userbody.appendChild(usernamehtml);
userbody.appendChild(badgediv);
const discrimatorhtml=document.createElement("h3");
discrimatorhtml.classList.add("tag");
discrimatorhtml.textContent=this.username+"#"+this.discriminator;
userbody.appendChild(discrimatorhtml)
if(this.accent_color){
div.style.setProperty("--accent_color","#"+this.accent_color.toString(16).padStart(6,"0"));
}else{
div.style.setProperty("--accent_color","transparent");
}
if(this.banner){
const banner=document.createElement("img");
let src:string;
if(!this.hypotheticalbanner){
src=this.info.cdn+"/avatars/"+this.id.replace("#clone","")+"/"+this.banner+".png";
}else{
src=this.banner;
}
console.log(src,this.banner);
banner.src=src;
banner.classList.add("banner");
div.append(banner);
}
if(x!==-1){
div.style.left=x+"px";
div.style.top=y+"px";
div.classList.add("profile","flexttb");
}else{
this.setstatus("online");
div.classList.add("hypoprofile","flexttb");
}
const badgediv=document.createElement("div");
badgediv.classList.add("badges");
(async ()=>{
if(!this.badge_ids)return;
for(const id of this.badge_ids){
const badgejson=await this.getBadge(id);
if(badgejson){
const badge=document.createElement(badgejson.link?"a":"div");
badge.classList.add("badge");
const img=document.createElement("img");
img.src=badgejson.icon;
badge.append(img);
const span=document.createElement("span");
span.textContent=badgejson.description;
badge.append(span);
if(badge instanceof HTMLAnchorElement){
badge.href=badgejson.link;
}
badgediv.append(badge);
}
}
})();
{
const pfp=await this.buildstatuspfp();
div.appendChild(pfp);
}
{
const userbody=document.createElement("div");
userbody.classList.add("infosection");
div.appendChild(userbody);
const usernamehtml=document.createElement("h2");
usernamehtml.textContent=this.username;
userbody.appendChild(usernamehtml);
userbody.appendChild(badgediv);
const discrimatorhtml=document.createElement("h3");
discrimatorhtml.classList.add("tag");
discrimatorhtml.textContent=this.username+"#"+this.discriminator;
userbody.appendChild(discrimatorhtml);
const pronounshtml=document.createElement("p");
pronounshtml.textContent=this.pronouns;
pronounshtml.classList.add("pronouns");
userbody.appendChild(pronounshtml)
const pronounshtml=document.createElement("p");
pronounshtml.textContent=this.pronouns;
pronounshtml.classList.add("pronouns");
userbody.appendChild(pronounshtml);
const rule=document.createElement("hr");
userbody.appendChild(rule);
const biohtml=this.bio.makeHTML();
userbody.appendChild(biohtml);
if(guild){
Member.resolveMember(this,guild).then(member=>{
if(!member) return;
const roles=document.createElement("div");
roles.classList.add("rolesbox");
for(const role of member.roles){
const div=document.createElement("div");
div.classList.add("rolediv");
const color=document.createElement("div");
div.append(color);
color.style.setProperty("--role-color","#"+role.color.toString(16).padStart(6,"0"))
color.classList.add("colorrolediv");
const span=document.createElement("span");
div.append(span);
span.textContent=role.name;
roles.append(div);
}
userbody.append(roles);
});
}
}
console.log(div);
const rule=document.createElement("hr");
userbody.appendChild(rule);
const biohtml=this.bio.makeHTML();
userbody.appendChild(biohtml);
if(guild){
Member.resolveMember(this,guild).then(member=>{
if(!member)return;
const roles=document.createElement("div");
roles.classList.add("rolesbox");
for(const role of member.roles){
const div=document.createElement("div");
div.classList.add("rolediv");
const color=document.createElement("div");
div.append(color);
color.style.setProperty("--role-color","#"+role.color.toString(16).padStart(6,"0"));
color.classList.add("colorrolediv");
const span=document.createElement("span");
div.append(span);
span.textContent=role.name;
roles.append(div);
}
userbody.append(roles);
});
}
}
console.log(div);
if(x!==-1){
Contextmenu.currentmenu=div;
document.body.appendChild(div)
Contextmenu.keepOnScreen(div);
}
return div;
}
profileclick(obj:HTMLElement,guild:Guild|undefined=undefined){
obj.onclick=e=>{
this.buildprofile(e.clientX,e.clientY,guild);
e.stopPropagation();
}
}
if(x!==-1){
Contextmenu.currentmenu=div;
document.body.appendChild(div);
Contextmenu.keepOnScreen(div);
}
return div;
}
profileclick(obj:HTMLElement,guild?:Guild|undefined){
obj.onclick=e=>{
this.buildprofile(e.clientX,e.clientY,guild);
e.stopPropagation();
};
}
}
User.setUpContextMenu();
export {User};
export{User};